Merge remote-tracking branch 'upstream/master' into auth
Conflicts: mediagoblin/app.py mediagoblin/auth/forms.py mediagoblin/auth/tools.py mediagoblin/db/migrations.py mediagoblin/db/models.py mediagoblin/edit/views.py mediagoblin/plugins/basic_auth/tools.py mediagoblin/tests/test_edit.py
This commit is contained in:
commit
af4414a85f
17
AUTHORS
17
AUTHORS
@ -8,10 +8,12 @@ variety of different ways and this software wouldn't exist without them.
|
||||
Thank you!
|
||||
|
||||
* Aaron Williamson
|
||||
* Aditi Mittal
|
||||
* Aeva Ntsc
|
||||
* Alejandro Villanueva
|
||||
* Aleksandar Micovic
|
||||
* Aleksej Serdjukov
|
||||
* Alon Levy
|
||||
* Alex Camelio
|
||||
* András Veres-Szentkirályi
|
||||
* Bassam Kurdali
|
||||
@ -21,13 +23,17 @@ Thank you!
|
||||
* Corey Farwell
|
||||
* Chris Moylan
|
||||
* Christopher Allan Webber
|
||||
* David Thompson
|
||||
* Daniel Neel
|
||||
* Deb Nicholson
|
||||
* Derek Moore
|
||||
* Duncan Paterson
|
||||
* Elrond of Samba TNG
|
||||
* Emily O'Leary
|
||||
* Gabi Thume
|
||||
* Gabriel Saldana
|
||||
* Greg Grossmeier
|
||||
* Hans Lo
|
||||
* Jakob Kramer
|
||||
* Jef van Schendel
|
||||
* Jessica Tallon
|
||||
@ -36,25 +42,34 @@ Thank you!
|
||||
* Jorge Araya Navarro
|
||||
* Karen Rustad
|
||||
* Kuno Woudt
|
||||
* Laura Arjona
|
||||
* Larisa Hoffenbecker
|
||||
* Luke Slater
|
||||
* Manuel Urbano Santos
|
||||
* Mark Holmquist
|
||||
* Mats Sjöberg
|
||||
* Matt Lee
|
||||
* Michele Azzolari
|
||||
* Mike Linksvayer
|
||||
* Natalie Foust-Pilcher
|
||||
* Nathan Yergler
|
||||
* Odin Hørthe Omdal
|
||||
* Osama Khalid
|
||||
* Pablo J. Urbano Santos
|
||||
* Praveen Kumar
|
||||
* Rasmus Larsson
|
||||
* Rodney Ewing
|
||||
* Runar Petursson
|
||||
* Sacha De'Angeli
|
||||
* Sam Kleinman
|
||||
* Sam Tuke
|
||||
* Sebastian Spaeth
|
||||
* Shawn Khan
|
||||
* Simon Fondrie-Teitler
|
||||
* Stefano Zacchiroli
|
||||
* Tiberiu C. Turbureanu
|
||||
* Tran Thanh Bao
|
||||
* Tryggvi Björgvinsson
|
||||
* Shawn Khan
|
||||
* Will Kahn-Greene
|
||||
|
||||
@ -64,4 +79,4 @@ If you think your name should be on this list, let us know!
|
||||
We also are currently borrowing an image in
|
||||
mediagoblin/static/images/media_thumbs/image.png from the wonderful
|
||||
people at http://tango.freedesktop.org/ which is in the public
|
||||
domain... thanks Tango folks!
|
||||
domain... thanks Tango folks!
|
||||
|
@ -73,6 +73,7 @@ This guide covers writing new GNU MediaGoblin plugins.
|
||||
pluginwriter/quickstart
|
||||
pluginwriter/database
|
||||
pluginwriter/api
|
||||
pluginwriter/tests
|
||||
|
||||
|
||||
Part 4: Developer's Zone
|
||||
|
@ -69,6 +69,32 @@ example might look like::
|
||||
This means that when people enable your plugin in their config you'll
|
||||
be able to provide defaults as well as type validation.
|
||||
|
||||
You can access this via the app_config variables in mg_globals, or you
|
||||
can use a shortcut to get your plugin's config section::
|
||||
|
||||
>>> from mediagoblin.tools import pluginapi
|
||||
# Replace with the path to your plugin.
|
||||
# (If an external package, it won't be part of mediagoblin.plugins)
|
||||
>>> floobie_config = pluginapi.get_config('mediagoblin.plugins.floobifier')
|
||||
>>> floobie_dir = floobie_config['floobie_dir']
|
||||
# This is the same as the above
|
||||
>>> from mediagoblin import mg_globals
|
||||
>>> config = mg_globals.global_config['plugins']['mediagoblin.plugins.floobifier']
|
||||
>>> floobie_dir = floobie_config['floobie_dir']
|
||||
|
||||
A tip: you have access to the `%(here)s` variable in your config,
|
||||
which is the directory that the user's mediagoblin config is running
|
||||
out of. So for example, your plugin may need a "floobie" directory to
|
||||
store floobs in. You could give them a reasonable default that makes
|
||||
use of the default `user_dev` location, but allow users to override
|
||||
it, like so::
|
||||
|
||||
[plugin_spec]
|
||||
floobie_dir = string(default="%(here)s/user_dev/floobs/")
|
||||
|
||||
Note, this is relative to the user's mediagoblin config directory,
|
||||
*not* your plugin directory!
|
||||
|
||||
|
||||
Context Hooks
|
||||
-------------
|
||||
|
64
docs/source/pluginwriter/tests.rst
Normal file
64
docs/source/pluginwriter/tests.rst
Normal file
@ -0,0 +1,64 @@
|
||||
.. MediaGoblin Documentation
|
||||
|
||||
Written in 2013 by MediaGoblin contributors
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all
|
||||
copyright and related and neighboring rights to this software to
|
||||
the public domain worldwide. This software is distributed without
|
||||
any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain
|
||||
Dedication along with this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
==============================
|
||||
Writing unit tests for plugins
|
||||
==============================
|
||||
|
||||
Here's a brief guide to writing unit tests for plugins. However, it
|
||||
isn't really ideal. It also hasn't been well tested... yes, there's
|
||||
some irony there :)
|
||||
|
||||
Some notes: we're using py.test and webtest for unit testing stuff.
|
||||
Keep that in mind.
|
||||
|
||||
My suggestion is to mime the behavior of `mediagoblin/tests/` and put
|
||||
that in your own plugin, like `myplugin/tests/`. Copy over
|
||||
`conftest.py` and `pytest.ini` to your tests directory, but possibly
|
||||
change the `test_app` fixture to match your own tests' config needs.
|
||||
For example::
|
||||
|
||||
import pkg_resources
|
||||
# [...]
|
||||
|
||||
@pytest.fixture()
|
||||
def test_app(request):
|
||||
return get_app(
|
||||
request,
|
||||
mgoblin_config=pkg_resources.resource_filename(
|
||||
'myplugin.tests', 'myplugin_mediagoblin.ini'))
|
||||
|
||||
In any test module in your tests directory you can then do::
|
||||
|
||||
def test_somethingorother(test_app):
|
||||
# real code goes here
|
||||
pass
|
||||
|
||||
And you'll get a mediagoblin application wrapped in webtest passed in
|
||||
to your environment.
|
||||
|
||||
If your plugin needs to define multiple configuration setups, you can
|
||||
actually set up multiple fixtures very easily for this. You can just
|
||||
set up multiple fixtures with different names that point to different
|
||||
configs and pass them in as that named argument.
|
||||
|
||||
To run the tests, from mediagoblin's directory (make sure that your
|
||||
plugin has been added to your mediagoblin checkout's virtualenv!) do::
|
||||
|
||||
./runtests.sh /path/to/myplugin/tests/
|
||||
|
||||
replacing `/path/to/myplugin/` with the actual path to your plugin.
|
||||
|
||||
NOTE: again, the above is untested, but it should probably work. If
|
||||
you run into trouble, `contact us
|
||||
<http://mediagoblin.org/pages/join.html>`_, preferably on IRC!
|
@ -199,8 +199,16 @@ will be able to present them to your wide audience of admirers!
|
||||
PDF and Document
|
||||
================
|
||||
|
||||
To enable the "PDF and Document" support plugin, you need pdftocairo, pdfinfo,
|
||||
unoconv with headless support. All executables must be on your execution path.
|
||||
To enable the "PDF and Document" support plugin, you need:
|
||||
|
||||
1. pdftocairo and pdfinfo for pdf only support.
|
||||
|
||||
2. unoconv with headless support to support converting libreoffice supported
|
||||
documents as well, such as doc/ppt/xls/odf/odg/odp and more.
|
||||
For the full list see mediagoblin/media_types/pdf/processing.py,
|
||||
unoconv_supported.
|
||||
|
||||
All executables must be on your execution path.
|
||||
|
||||
To install this on Fedora:
|
||||
|
||||
@ -208,6 +216,9 @@ To install this on Fedora:
|
||||
|
||||
sudo yum install -y poppler-utils unoconv libreoffice-headless
|
||||
|
||||
Note: You can leave out unoconv and libreoffice-headless if you want only pdf
|
||||
support. This will result in a much smaller list of dependencies.
|
||||
|
||||
pdf.js relies on git submodules, so be sure you have fetched them:
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -39,6 +39,13 @@ carefully, or at least skim over it.
|
||||
alias /srv/mediagoblin.example.org/mediagoblin/user_dev/plugin_static/;
|
||||
}
|
||||
|
||||
Similarly, if you've got a modified paste config, you may want to
|
||||
borrow the app:plugin_static section from the default paste.ini
|
||||
file.
|
||||
5. We now use itsdangerous for sessions; if you had any references to
|
||||
beaker in your paste config you can remove them. Again, see the
|
||||
default paste.ini config
|
||||
|
||||
**For theme authors**
|
||||
|
||||
If you have your own theme or you have any "user modified templates",
|
||||
@ -51,7 +58,23 @@ please note the following:
|
||||
You can easily customize this to give a welcome page appropriate to
|
||||
your site.
|
||||
|
||||
|
||||
**New features**
|
||||
* PDF media type!
|
||||
* Improved plugin system. More flexible, better documented, with a
|
||||
new plugin authoring section of the docs.
|
||||
* itsdangerous based sessions. No more beaker!
|
||||
* New, experimental Piwigo-based API. This means you should be able
|
||||
to use MediaGoblin with something like Shotwell. (Again, a word of
|
||||
caution: this is *very experimental*!)
|
||||
* Human readable timestamps, and the option to display the original
|
||||
date of an image when available (available as the
|
||||
"original_date_visible" variable)
|
||||
* Moved unit testing system from nosetests to py.test so we can better
|
||||
handle issues with sqlalchemy exploding with different database
|
||||
configurations. Long story :)
|
||||
* You can now disable the ability to post comments.
|
||||
* Tags now can be up to length 255 characters by default.
|
||||
|
||||
|
||||
0.3.3
|
||||
|
@ -20,7 +20,7 @@ email_debug_mode = true
|
||||
allow_registration = true
|
||||
|
||||
## Uncomment this to turn on video or enable other media types
|
||||
## You may have to install dependencies, and will have to run ./bin/dbupdate
|
||||
## You may have to install dependencies, and will have to run ./bin/gmg dbupdate
|
||||
## See http://docs.mediagoblin.org/siteadmin/media-types.html for details.
|
||||
# media_types = mediagoblin.media_types.image, mediagoblin.media_types.video
|
||||
|
||||
|
@ -23,4 +23,4 @@
|
||||
|
||||
# see http://www.python.org/dev/peps/pep-0386/
|
||||
|
||||
__version__ = "0.4.0.dev"
|
||||
__version__ = "0.4.1.dev"
|
||||
|
@ -38,6 +38,7 @@ from mediagoblin.init import (get_jinja_loader, get_staticdirector,
|
||||
from mediagoblin.tools.pluginapi import PluginManager, hook_transform
|
||||
from mediagoblin.tools.crypto import setup_crypto
|
||||
from mediagoblin.auth.tools import check_auth_enabled, no_auth_logout
|
||||
from mediagoblin import notifications
|
||||
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
@ -195,6 +196,8 @@ class MediaGoblinApp(object):
|
||||
# Log user out if authentication_disabled
|
||||
no_auth_logout(request)
|
||||
|
||||
request.notifications = notifications
|
||||
|
||||
mg_request.setup_user_in_request(request)
|
||||
|
||||
request.controller_name = None
|
||||
|
@ -30,9 +30,6 @@ class ForgotPassForm(wtforms.Form):
|
||||
class ChangePassForm(wtforms.Form):
|
||||
password = wtforms.PasswordField(
|
||||
'Password')
|
||||
userid = wtforms.HiddenField(
|
||||
'',
|
||||
[wtforms.validators.Required()])
|
||||
token = wtforms.HiddenField(
|
||||
'',
|
||||
[wtforms.validators.Required()])
|
||||
|
@ -18,6 +18,7 @@ import logging
|
||||
import wtforms
|
||||
|
||||
from mediagoblin import mg_globals
|
||||
from mediagoblin.tools.crypto import get_timed_signer_url
|
||||
from mediagoblin.db.models import User
|
||||
from mediagoblin.tools.mail import (normalize_email, send_email,
|
||||
email_debug_message)
|
||||
@ -60,11 +61,12 @@ def normalize_user_or_email_field(allow_email=True, allow_user=True):
|
||||
|
||||
|
||||
EMAIL_VERIFICATION_TEMPLATE = (
|
||||
u"http://{host}{uri}?"
|
||||
u"userid={userid}&token={verification_key}")
|
||||
u"{uri}?"
|
||||
u"token={verification_key}")
|
||||
|
||||
|
||||
def send_verification_email(user, request):
|
||||
def send_verification_email(user, request, email=None,
|
||||
rendered_email=None):
|
||||
"""
|
||||
Send the verification email to users to activate their accounts.
|
||||
|
||||
@ -72,19 +74,24 @@ def send_verification_email(user, request):
|
||||
- user: a user object
|
||||
- request: the request
|
||||
"""
|
||||
rendered_email = render_template(
|
||||
request, 'mediagoblin/auth/verification_email.txt',
|
||||
{'username': user.username,
|
||||
'verification_url': EMAIL_VERIFICATION_TEMPLATE.format(
|
||||
host=request.host,
|
||||
uri=request.urlgen('mediagoblin.auth.verify_email'),
|
||||
userid=unicode(user.id),
|
||||
verification_key=user.verification_key)})
|
||||
if not email:
|
||||
email = user.email
|
||||
|
||||
if not rendered_email:
|
||||
verification_key = get_timed_signer_url('mail_verification_token') \
|
||||
.dumps(user.id)
|
||||
rendered_email = render_template(
|
||||
request, 'mediagoblin/auth/verification_email.txt',
|
||||
{'username': user.username,
|
||||
'verification_url': EMAIL_VERIFICATION_TEMPLATE.format(
|
||||
uri=request.urlgen('mediagoblin.auth.verify_email',
|
||||
qualified=True),
|
||||
verification_key=verification_key)})
|
||||
|
||||
# TODO: There is no error handling in place
|
||||
send_email(
|
||||
mg_globals.app_config['email_sender_address'],
|
||||
[user.email],
|
||||
[email],
|
||||
# TODO
|
||||
# Due to the distributed nature of GNU MediaGoblin, we should
|
||||
# find a way to send some additional information about the
|
||||
|
@ -15,10 +15,11 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import uuid
|
||||
import datetime
|
||||
from itsdangerous import BadSignature
|
||||
|
||||
from mediagoblin import messages, mg_globals
|
||||
from mediagoblin.db.models import User
|
||||
from mediagoblin.tools.crypto import get_timed_signer_url
|
||||
from mediagoblin.tools.response import render_to_response, redirect, render_404
|
||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
||||
from mediagoblin.tools.mail import email_debug_message
|
||||
@ -136,16 +137,28 @@ def verify_email(request):
|
||||
you are lucky :)
|
||||
"""
|
||||
# If we don't have userid and token parameters, we can't do anything; 404
|
||||
if not 'userid' in request.GET or not 'token' in request.GET:
|
||||
if not 'token' in request.GET:
|
||||
return render_404(request)
|
||||
|
||||
user = User.query.filter_by(id=request.args['userid']).first()
|
||||
# Catch error if token is faked or expired
|
||||
try:
|
||||
token = get_timed_signer_url("mail_verification_token") \
|
||||
.loads(request.GET['token'], max_age=10*24*3600)
|
||||
except BadSignature:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('The verification key or user id is incorrect.'))
|
||||
|
||||
if user and user.verification_key == unicode(request.GET['token']):
|
||||
return redirect(
|
||||
request,
|
||||
'index')
|
||||
|
||||
user = User.query.filter_by(id=int(token)).first()
|
||||
|
||||
if user and user.email_verified is False:
|
||||
user.status = u'active'
|
||||
user.email_verified = True
|
||||
user.verification_key = None
|
||||
|
||||
user.save()
|
||||
|
||||
messages.add_message(
|
||||
@ -187,9 +200,6 @@ def resend_activation(request):
|
||||
|
||||
return redirect(request, "mediagoblin.user_pages.user_home", user=request.user['username'])
|
||||
|
||||
request.user.verification_key = unicode(uuid.uuid4())
|
||||
request.user.save()
|
||||
|
||||
email_debug_message(request)
|
||||
send_verification_email(request.user, request)
|
||||
|
||||
@ -259,11 +269,6 @@ def forgot_password(request):
|
||||
|
||||
# SUCCESS. Send reminder and return to login page
|
||||
if user:
|
||||
user.fp_verification_key = unicode(uuid.uuid4())
|
||||
user.fp_token_expire = datetime.datetime.now() + \
|
||||
datetime.timedelta(days=10)
|
||||
user.save()
|
||||
|
||||
email_debug_message(request)
|
||||
send_fp_verification_email(user, request)
|
||||
|
||||
@ -278,31 +283,44 @@ def verify_forgot_password(request):
|
||||
"""
|
||||
# get form data variables, and specifically check for presence of token
|
||||
formdata = _process_for_token(request)
|
||||
if not formdata['has_userid_and_token']:
|
||||
if not formdata['has_token']:
|
||||
return render_404(request)
|
||||
|
||||
formdata_token = formdata['vars']['token']
|
||||
formdata_userid = formdata['vars']['userid']
|
||||
formdata_vars = formdata['vars']
|
||||
|
||||
# check if it's a valid user id
|
||||
user = User.query.filter_by(id=formdata_userid).first()
|
||||
if not user:
|
||||
return render_404(request)
|
||||
# Catch error if token is faked or expired
|
||||
try:
|
||||
token = get_timed_signer_url("mail_verification_token") \
|
||||
.loads(formdata_vars['token'], max_age=10*24*3600)
|
||||
except BadSignature:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('The verification key or user id is incorrect.'))
|
||||
|
||||
# check if we have a real user and correct token
|
||||
if ((user and user.fp_verification_key and
|
||||
user.fp_verification_key == unicode(formdata_token) and
|
||||
datetime.datetime.now() < user.fp_token_expire
|
||||
and user.email_verified and user.status == 'active')):
|
||||
return redirect(
|
||||
request,
|
||||
'index')
|
||||
|
||||
# check if it's a valid user id
|
||||
user = User.query.filter_by(id=int(token)).first()
|
||||
|
||||
# no user in db
|
||||
if not user:
|
||||
messages.add_message(
|
||||
request, messages.ERROR,
|
||||
_('The user id is incorrect.'))
|
||||
return redirect(
|
||||
request, 'index')
|
||||
|
||||
# check if user active and has email verified
|
||||
if user.email_verified and user.status == 'active':
|
||||
|
||||
cp_form = auth_forms.ChangePassForm(formdata_vars)
|
||||
|
||||
if request.method == 'POST' and cp_form.validate():
|
||||
user.pw_hash = auth.gen_password_hash(
|
||||
cp_form.password.data)
|
||||
user.fp_verification_key = None
|
||||
user.fp_token_expire = None
|
||||
user.save()
|
||||
|
||||
messages.add_message(
|
||||
@ -316,10 +334,20 @@ def verify_forgot_password(request):
|
||||
'mediagoblin/auth/change_fp.html',
|
||||
{'cp_form': cp_form,})
|
||||
|
||||
# in case there is a valid id but no user with that id in the db
|
||||
# or the token expired
|
||||
else:
|
||||
return render_404(request)
|
||||
if not user.email_verified:
|
||||
messages.add_message(
|
||||
request, messages.ERROR,
|
||||
_('You need to verify your email before you can reset your'
|
||||
' password.'))
|
||||
|
||||
if not user.status == 'active':
|
||||
messages.add_message(
|
||||
request, messages.ERROR,
|
||||
_('You are no longer an active user. Please contact the system'
|
||||
' admin to reactivate your accoutn.'))
|
||||
|
||||
return redirect(
|
||||
request, 'index')
|
||||
|
||||
|
||||
def _process_for_token(request):
|
||||
@ -337,7 +365,6 @@ def _process_for_token(request):
|
||||
|
||||
formdata = {
|
||||
'vars': formdata_vars,
|
||||
'has_userid_and_token':
|
||||
'userid' in formdata_vars and 'token' in formdata_vars}
|
||||
'has_token': 'token' in formdata_vars}
|
||||
|
||||
return formdata
|
||||
|
@ -22,9 +22,10 @@ direct_remote_path = string(default="/mgoblin_static/")
|
||||
|
||||
# set to false to enable sending notices
|
||||
email_debug_mode = boolean(default=True)
|
||||
email_smtp_use_ssl = boolean(default=False)
|
||||
email_sender_address = string(default="notice@mediagoblin.example.org")
|
||||
email_smtp_host = string(default='')
|
||||
email_smtp_port = integer(default=25)
|
||||
email_smtp_port = integer(default=0)
|
||||
email_smtp_user = string(default=None)
|
||||
email_smtp_pass = string(default=None)
|
||||
|
||||
|
@ -26,7 +26,7 @@ from sqlalchemy.sql import and_
|
||||
from migrate.changeset.constraint import UniqueConstraint
|
||||
|
||||
from mediagoblin.db.migration_tools import RegisterMigration, inspect_table
|
||||
from mediagoblin.db.models import MediaEntry, Collection, User
|
||||
from mediagoblin.db.models import MediaEntry, Collection, User, MediaComment
|
||||
|
||||
MIGRATIONS = {}
|
||||
|
||||
@ -288,8 +288,82 @@ def unique_collections_slug(db):
|
||||
|
||||
db.commit()
|
||||
|
||||
|
||||
@RegisterMigration(11, MIGRATIONS)
|
||||
def drop_token_related_User_columns(db):
|
||||
"""
|
||||
Drop unneeded columns from the User table after switching to using
|
||||
itsdangerous tokens for email and forgot password verification.
|
||||
"""
|
||||
metadata = MetaData(bind=db.bind)
|
||||
user_table = inspect_table(metadata, 'core__users')
|
||||
|
||||
verification_key = user_table.columns['verification_key']
|
||||
fp_verification_key = user_table.columns['fp_verification_key']
|
||||
fp_token_expire = user_table.columns['fp_token_expire']
|
||||
|
||||
verification_key.drop()
|
||||
fp_verification_key.drop()
|
||||
fp_token_expire.drop()
|
||||
|
||||
db.commit()
|
||||
|
||||
class CommentSubscription_v0(declarative_base()):
|
||||
__tablename__ = 'core__comment_subscriptions'
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||
|
||||
media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=False)
|
||||
|
||||
user_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||
|
||||
notify = Column(Boolean, nullable=False, default=True)
|
||||
send_email = Column(Boolean, nullable=False, default=True)
|
||||
|
||||
|
||||
class Notification_v0(declarative_base()):
|
||||
__tablename__ = 'core__notifications'
|
||||
id = Column(Integer, primary_key=True)
|
||||
type = Column(Unicode)
|
||||
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||
|
||||
user_id = Column(Integer, ForeignKey(User.id), nullable=False,
|
||||
index=True)
|
||||
seen = Column(Boolean, default=lambda: False, index=True)
|
||||
|
||||
|
||||
class CommentNotification_v0(Notification_v0):
|
||||
__tablename__ = 'core__comment_notifications'
|
||||
id = Column(Integer, ForeignKey(Notification_v0.id), primary_key=True)
|
||||
|
||||
subject_id = Column(Integer, ForeignKey(MediaComment.id))
|
||||
|
||||
|
||||
class ProcessingNotification_v0(Notification_v0):
|
||||
__tablename__ = 'core__processing_notifications'
|
||||
|
||||
id = Column(Integer, ForeignKey(Notification_v0.id), primary_key=True)
|
||||
|
||||
subject_id = Column(Integer, ForeignKey(MediaEntry.id))
|
||||
|
||||
|
||||
@RegisterMigration(12, MIGRATIONS)
|
||||
def add_new_notification_tables(db):
|
||||
metadata = MetaData(bind=db.bind)
|
||||
|
||||
user_table = inspect_table(metadata, 'core__users')
|
||||
mediaentry_table = inspect_table(metadata, 'core__media_entries')
|
||||
mediacomment_table = inspect_table(metadata, 'core__media_comments')
|
||||
|
||||
CommentSubscription_v0.__table__.create(db.bind)
|
||||
|
||||
Notification_v0.__table__.create(db.bind)
|
||||
CommentNotification_v0.__table__.create(db.bind)
|
||||
ProcessingNotification_v0.__table__.create(db.bind)
|
||||
|
||||
|
||||
@RegisterMigration(13, MIGRATIONS)
|
||||
def pw_hash_nullable(db):
|
||||
"""Make pw_hash column nullable"""
|
||||
metadata = MetaData(bind=db.bind)
|
||||
@ -302,3 +376,4 @@ def pw_hash_nullable(db):
|
||||
constraint.create()
|
||||
|
||||
db.commit()
|
||||
|
||||
|
@ -31,6 +31,8 @@ import uuid
|
||||
import re
|
||||
import datetime
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from werkzeug.utils import cached_property
|
||||
|
||||
from mediagoblin import mg_globals
|
||||
@ -288,6 +290,13 @@ class MediaCommentMixin(object):
|
||||
"""
|
||||
return cleaned_markdown_conversion(self.content)
|
||||
|
||||
def __repr__(self):
|
||||
return '<{klass} #{id} {author} "{comment}">'.format(
|
||||
klass=self.__class__.__name__,
|
||||
id=self.id,
|
||||
author=self.get_author,
|
||||
comment=self.content)
|
||||
|
||||
|
||||
class CollectionMixin(GenerateSlugMixin):
|
||||
def check_slug_used(self, slug):
|
||||
|
@ -24,15 +24,17 @@ import datetime
|
||||
from sqlalchemy import Column, Integer, Unicode, UnicodeText, DateTime, \
|
||||
Boolean, ForeignKey, UniqueConstraint, PrimaryKeyConstraint, \
|
||||
SmallInteger
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
from sqlalchemy.orm import relationship, backref, with_polymorphic
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
from sqlalchemy.sql.expression import desc
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy.util import memoized_property
|
||||
|
||||
|
||||
from mediagoblin.db.extratypes import PathTupleWithSlashes, JSONEncoded
|
||||
from mediagoblin.db.base import Base, DictReadAttrProxy
|
||||
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin, CollectionMixin, CollectionItemMixin
|
||||
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \
|
||||
MediaCommentMixin, CollectionMixin, CollectionItemMixin
|
||||
from mediagoblin.tools.files import delete_media_files
|
||||
from mediagoblin.tools.common import import_component
|
||||
|
||||
@ -60,20 +62,17 @@ class User(Base, UserMixin):
|
||||
# the RFC) and because it would be a mess to implement at this
|
||||
# point.
|
||||
email = Column(Unicode, nullable=False)
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||
pw_hash = Column(Unicode)
|
||||
email_verified = Column(Boolean, default=False)
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||
status = Column(Unicode, default=u"needs_email_verification", nullable=False)
|
||||
# Intented to be nullable=False, but migrations would not work for it
|
||||
# set to nullable=True implicitly.
|
||||
wants_comment_notification = Column(Boolean, default=True)
|
||||
license_preference = Column(Unicode)
|
||||
verification_key = Column(Unicode)
|
||||
is_admin = Column(Boolean, default=False, nullable=False)
|
||||
url = Column(Unicode)
|
||||
bio = Column(UnicodeText) # ??
|
||||
fp_verification_key = Column(Unicode)
|
||||
fp_token_expire = Column(DateTime)
|
||||
|
||||
## TODO
|
||||
# plugin data would be in a separate model
|
||||
@ -392,6 +391,10 @@ class MediaComment(Base, MediaCommentMixin):
|
||||
backref=backref("posted_comments",
|
||||
lazy="dynamic",
|
||||
cascade="all, delete-orphan"))
|
||||
get_entry = relationship(MediaEntry,
|
||||
backref=backref("comments",
|
||||
lazy="dynamic",
|
||||
cascade="all, delete-orphan"))
|
||||
|
||||
# Cascade: Comments are somewhat owned by their MediaEntry.
|
||||
# So do the full thing.
|
||||
@ -484,9 +487,103 @@ class ProcessingMetaData(Base):
|
||||
return DictReadAttrProxy(self)
|
||||
|
||||
|
||||
class CommentSubscription(Base):
|
||||
__tablename__ = 'core__comment_subscriptions'
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||
|
||||
media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=False)
|
||||
media_entry = relationship(MediaEntry,
|
||||
backref=backref('comment_subscriptions',
|
||||
cascade='all, delete-orphan'))
|
||||
|
||||
user_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||
user = relationship(User,
|
||||
backref=backref('comment_subscriptions',
|
||||
cascade='all, delete-orphan'))
|
||||
|
||||
notify = Column(Boolean, nullable=False, default=True)
|
||||
send_email = Column(Boolean, nullable=False, default=True)
|
||||
|
||||
def __repr__(self):
|
||||
return ('<{classname} #{id}: {user} {media} notify: '
|
||||
'{notify} email: {email}>').format(
|
||||
id=self.id,
|
||||
classname=self.__class__.__name__,
|
||||
user=self.user,
|
||||
media=self.media_entry,
|
||||
notify=self.notify,
|
||||
email=self.send_email)
|
||||
|
||||
|
||||
class Notification(Base):
|
||||
__tablename__ = 'core__notifications'
|
||||
id = Column(Integer, primary_key=True)
|
||||
type = Column(Unicode)
|
||||
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||
|
||||
user_id = Column(Integer, ForeignKey('core__users.id'), nullable=False,
|
||||
index=True)
|
||||
seen = Column(Boolean, default=lambda: False, index=True)
|
||||
user = relationship(
|
||||
User,
|
||||
backref=backref('notifications', cascade='all, delete-orphan'))
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'notification',
|
||||
'polymorphic_on': type
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return '<{klass} #{id}: {user}: {subject} ({seen})>'.format(
|
||||
id=self.id,
|
||||
klass=self.__class__.__name__,
|
||||
user=self.user,
|
||||
subject=getattr(self, 'subject', None),
|
||||
seen='unseen' if not self.seen else 'seen')
|
||||
|
||||
|
||||
class CommentNotification(Notification):
|
||||
__tablename__ = 'core__comment_notifications'
|
||||
id = Column(Integer, ForeignKey(Notification.id), primary_key=True)
|
||||
|
||||
subject_id = Column(Integer, ForeignKey(MediaComment.id))
|
||||
subject = relationship(
|
||||
MediaComment,
|
||||
backref=backref('comment_notifications', cascade='all, delete-orphan'))
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'comment_notification'
|
||||
}
|
||||
|
||||
|
||||
class ProcessingNotification(Notification):
|
||||
__tablename__ = 'core__processing_notifications'
|
||||
|
||||
id = Column(Integer, ForeignKey(Notification.id), primary_key=True)
|
||||
|
||||
subject_id = Column(Integer, ForeignKey(MediaEntry.id))
|
||||
subject = relationship(
|
||||
MediaEntry,
|
||||
backref=backref('processing_notifications',
|
||||
cascade='all, delete-orphan'))
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'processing_notification'
|
||||
}
|
||||
|
||||
|
||||
with_polymorphic(
|
||||
Notification,
|
||||
[ProcessingNotification, CommentNotification])
|
||||
|
||||
MODELS = [
|
||||
User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem, MediaFile, FileKeynames,
|
||||
MediaAttachmentFile, ProcessingMetaData]
|
||||
User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem,
|
||||
MediaFile, FileKeynames, MediaAttachmentFile, ProcessingMetaData,
|
||||
Notification, CommentNotification, ProcessingNotification,
|
||||
CommentSubscription]
|
||||
|
||||
|
||||
######################################################
|
||||
|
@ -18,6 +18,29 @@
|
||||
TODO: indexes on foreignkeys, where useful.
|
||||
"""
|
||||
|
||||
###########################################################################
|
||||
# WHAT IS THIS FILE?
|
||||
# ------------------
|
||||
#
|
||||
# Upon occasion, someone runs into this file and wonders why we have
|
||||
# both a models.py and a models_v0.py.
|
||||
#
|
||||
# The short of it is: you can ignore this file.
|
||||
#
|
||||
# The long version is, in two parts:
|
||||
#
|
||||
# - We used to use MongoDB, then we switched to SQL and SQLAlchemy.
|
||||
# We needed to convert peoples' databases; the script we had would
|
||||
# switch them to the first version right after Mongo, convert over
|
||||
# all their tables, then run any migrations that were added after.
|
||||
#
|
||||
# - That script is now removed, but there is some discussion of
|
||||
# writing a test that would set us at the first SQL migration and
|
||||
# run everything after. If we wrote that, this file would still be
|
||||
# useful. But for now, it's legacy!
|
||||
#
|
||||
###########################################################################
|
||||
|
||||
|
||||
import datetime
|
||||
import sys
|
||||
|
@ -16,9 +16,11 @@
|
||||
|
||||
import wtforms
|
||||
|
||||
from mediagoblin.tools.text import tag_length_validator, TOO_LONG_TAG_WARNING
|
||||
from mediagoblin.tools.text import tag_length_validator
|
||||
from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
|
||||
from mediagoblin.tools.licenses import licenses_as_choices
|
||||
from mediagoblin.auth.forms import normalize_user_or_email_field
|
||||
|
||||
|
||||
class EditForm(wtforms.Form):
|
||||
title = wtforms.TextField(
|
||||
@ -59,6 +61,10 @@ class EditProfileForm(wtforms.Form):
|
||||
|
||||
|
||||
class EditAccountForm(wtforms.Form):
|
||||
new_email = wtforms.TextField(
|
||||
_('New email address'),
|
||||
[wtforms.validators.Optional(),
|
||||
normalize_user_or_email_field(allow_user=False)])
|
||||
license_preference = wtforms.SelectField(
|
||||
_('License preference'),
|
||||
[
|
||||
|
@ -26,3 +26,5 @@ add_route('mediagoblin.edit.delete_account', '/edit/account/delete/',
|
||||
'mediagoblin.edit.views:delete_account')
|
||||
add_route('mediagoblin.edit.pass', '/edit/password/',
|
||||
'mediagoblin.edit.views:change_pass')
|
||||
add_route('mediagoblin.edit.verify_email', '/edit/verify_email/',
|
||||
'mediagoblin.edit.views:verify_email')
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from itsdangerous import BadSignature
|
||||
from werkzeug.exceptions import Forbidden
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
@ -26,15 +27,19 @@ from mediagoblin import auth
|
||||
from mediagoblin.edit import forms
|
||||
from mediagoblin.edit.lib import may_edit_media
|
||||
from mediagoblin.decorators import (require_active_login, active_user_from_url,
|
||||
get_media_entry_by_id,
|
||||
user_may_alter_collection, get_user_collection)
|
||||
from mediagoblin.tools.response import render_to_response, \
|
||||
redirect, redirect_obj
|
||||
get_media_entry_by_id, user_may_alter_collection,
|
||||
get_user_collection)
|
||||
from mediagoblin.tools.crypto import get_timed_signer_url
|
||||
from mediagoblin.tools.mail import email_debug_message
|
||||
from mediagoblin.tools.response import (render_to_response,
|
||||
redirect, redirect_obj, render_404)
|
||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
||||
from mediagoblin.tools.template import render_template
|
||||
from mediagoblin.tools.text import (
|
||||
convert_to_tag_list_of_dicts, media_tags_as_string)
|
||||
from mediagoblin.tools.url import slugify
|
||||
from mediagoblin.db.util import check_media_slug_used, check_collection_slug_used
|
||||
from mediagoblin.db.models import User
|
||||
|
||||
import mimetypes
|
||||
|
||||
@ -212,6 +217,10 @@ def edit_profile(request, url_user=None):
|
||||
{'user': user,
|
||||
'form': form})
|
||||
|
||||
EMAIL_VERIFICATION_TEMPLATE = (
|
||||
u'{uri}?'
|
||||
u'token={verification_key}')
|
||||
|
||||
|
||||
@require_active_login
|
||||
def edit_account(request):
|
||||
@ -220,27 +229,45 @@ def edit_account(request):
|
||||
wants_comment_notification=user.wants_comment_notification,
|
||||
license_preference=user.license_preference)
|
||||
|
||||
if request.method == 'POST':
|
||||
form_validated = form.validate()
|
||||
if request.method == 'POST' and form.validate():
|
||||
user.wants_comment_notification = form.wants_comment_notification.data
|
||||
|
||||
if form_validated and \
|
||||
form.wants_comment_notification.validate(form):
|
||||
user.wants_comment_notification = \
|
||||
form.wants_comment_notification.data
|
||||
user.license_preference = form.license_preference.data
|
||||
|
||||
if form_validated and \
|
||||
form.license_preference.validate(form):
|
||||
user.license_preference = \
|
||||
form.license_preference.data
|
||||
if form.new_email.data:
|
||||
new_email = form.new_email.data
|
||||
users_with_email = User.query.filter_by(
|
||||
email=new_email).count()
|
||||
if users_with_email:
|
||||
form.new_email.errors.append(
|
||||
_('Sorry, a user with that email address'
|
||||
' already exists.'))
|
||||
else:
|
||||
verification_key = get_timed_signer_url(
|
||||
'mail_verification_token').dumps({
|
||||
'user': user.id,
|
||||
'email': new_email})
|
||||
|
||||
if form_validated and not form.errors:
|
||||
rendered_email = render_template(
|
||||
request, 'mediagoblin/edit/verification.txt',
|
||||
{'username': user.username,
|
||||
'verification_url': EMAIL_VERIFICATION_TEMPLATE.format(
|
||||
uri=request.urlgen('mediagoblin.edit.verify_email',
|
||||
qualified=True),
|
||||
verification_key=verification_key)})
|
||||
|
||||
email_debug_message(request)
|
||||
auth_tools.send_verification_email(user, request, new_email,
|
||||
rendered_email)
|
||||
|
||||
if not form.errors:
|
||||
user.save()
|
||||
messages.add_message(request,
|
||||
messages.SUCCESS,
|
||||
_("Account settings saved"))
|
||||
messages.SUCCESS,
|
||||
_("Account settings saved"))
|
||||
return redirect(request,
|
||||
'mediagoblin.user_pages.user_home',
|
||||
user=user.username)
|
||||
'mediagoblin.user_pages.user_home',
|
||||
user=user.username)
|
||||
|
||||
return render_to_response(
|
||||
request,
|
||||
@ -369,3 +396,48 @@ def change_pass(request):
|
||||
'mediagoblin/edit/change_pass.html',
|
||||
{'form': form,
|
||||
'user': user})
|
||||
|
||||
|
||||
def verify_email(request):
|
||||
"""
|
||||
Email verification view for changing email address
|
||||
"""
|
||||
# If no token, we can't do anything
|
||||
if not 'token' in request.GET:
|
||||
return render_404(request)
|
||||
|
||||
# Catch error if token is faked or expired
|
||||
token = None
|
||||
try:
|
||||
token = get_timed_signer_url("mail_verification_token") \
|
||||
.loads(request.GET['token'], max_age=10*24*3600)
|
||||
except BadSignature:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('The verification key or user id is incorrect.'))
|
||||
|
||||
return redirect(
|
||||
request,
|
||||
'index')
|
||||
|
||||
user = User.query.filter_by(id=int(token['user'])).first()
|
||||
|
||||
if user:
|
||||
user.email = token['email']
|
||||
user.save()
|
||||
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.SUCCESS,
|
||||
_('Your email address has been verified.'))
|
||||
|
||||
else:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('The verification key or user id is incorrect.'))
|
||||
|
||||
return redirect(
|
||||
request, 'mediagoblin.user_pages.user_home',
|
||||
user=user.username)
|
||||
|
Binary file not shown.
@ -15,15 +15,15 @@
|
||||
# Elrond <elrond+mediagoblin.org@samba-tng.org>, 2011
|
||||
# Art O. Pal <artopal@fastmail.fm>, 2011
|
||||
# spaetz <sebastian@sspaeth.de>, 2012
|
||||
# Vinzenz Vietzke <vietzke@b1-systems.de>, 2012
|
||||
# Vinzenz Vietzke <vietzke@b1-systems.de>, 2011
|
||||
# Vinzenz Vietzke <vinz@vinzv.de>, 2012
|
||||
# Vinzenz Vietzke <vinz@vinzv.de>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-05-28 10:43+0000\n"
|
||||
"Last-Translator: Elrond <elrond+mediagoblin.org@samba-tng.org>\n"
|
||||
"Language-Team: German (http://www.transifex.com/projects/p/mediagoblin/language/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -47,7 +47,7 @@ msgstr "E-Mail-Adresse"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "Benutzername oder E-Mail-Adresse"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -628,7 +628,7 @@ msgid ""
|
||||
"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n"
|
||||
" or\n"
|
||||
" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>"
|
||||
msgstr ""
|
||||
msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Registriere dich auf dieser Seite</a> oder <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Installiere MediaGoblin auf deinem eigenen Server</a>"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
|
||||
@ -784,7 +784,7 @@ msgstr "Bild für %(media_title)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "PDF-Datei"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -944,11 +944,11 @@ msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "Hinzugefügt"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Originaldatum"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1136,27 +1136,27 @@ msgstr "Tut uns Leid, aber unter der angegebenen Adresse gibt es keine Seite!</p
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "Jahr"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "Monat"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "Woche"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "Tag"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "Stunde"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "Minute"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"POT-Creation-Date: 2013-06-16 20:06-0500\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -17,94 +17,94 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 0.9.6\n"
|
||||
|
||||
#: mediagoblin/auth/forms.py:26
|
||||
#: mediagoblin/auth/forms.py:25
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/forms.py:30 mediagoblin/auth/forms.py:45
|
||||
#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:44
|
||||
#: mediagoblin/tests/test_util.py:110
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/forms.py:34
|
||||
#: mediagoblin/auth/forms.py:33
|
||||
msgid "Email address"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
#: mediagoblin/auth/forms.py:40
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
#: mediagoblin/auth/forms.py:51
|
||||
msgid "Username or email"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/tools.py:31
|
||||
#: mediagoblin/auth/tools.py:42
|
||||
msgid "Invalid User name or email address."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/tools.py:32
|
||||
#: mediagoblin/auth/tools.py:43
|
||||
msgid "This field does not take email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/tools.py:33
|
||||
#: mediagoblin/auth/tools.py:44
|
||||
msgid "This field requires an email address."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:54
|
||||
msgid "Sorry, registration is disabled on this instance."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:68
|
||||
#: mediagoblin/auth/tools.py:109
|
||||
msgid "Sorry, a user with that name already exists."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:72
|
||||
#: mediagoblin/auth/tools.py:113
|
||||
msgid "Sorry, a user with that email address already exists."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:182
|
||||
#: mediagoblin/auth/views.py:43
|
||||
msgid "Sorry, registration is disabled on this instance."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:133
|
||||
msgid ""
|
||||
"Your email address has been verified. You may now login, edit your "
|
||||
"profile, and submit images!"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:188
|
||||
#: mediagoblin/auth/views.py:139
|
||||
msgid "The verification key or user id is incorrect"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:206
|
||||
#: mediagoblin/auth/views.py:157
|
||||
msgid "You must be logged in so we know who to send the email to!"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:214
|
||||
#: mediagoblin/auth/views.py:165
|
||||
msgid "You've already verified your email address!"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:227
|
||||
#: mediagoblin/auth/views.py:178
|
||||
msgid "Resent your verification email."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:258
|
||||
#: mediagoblin/auth/views.py:209
|
||||
msgid ""
|
||||
"If that email address (case sensitive!) is registered an email has been "
|
||||
"sent with instructions on how to change your password."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:269
|
||||
#: mediagoblin/auth/views.py:220
|
||||
msgid "Couldn't find someone with that username."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:272
|
||||
#: mediagoblin/auth/views.py:223
|
||||
msgid "An email has been sent with instructions on how to change your password."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:279
|
||||
#: mediagoblin/auth/views.py:230
|
||||
msgid ""
|
||||
"Could not send password recovery email as your username is inactive or "
|
||||
"your account's email address has not been verified."
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/auth/views.py:336
|
||||
#: mediagoblin/auth/views.py:287
|
||||
msgid "You can now log in using your new password."
|
||||
msgstr ""
|
||||
|
||||
@ -634,13 +634,13 @@ msgid "Editing attachments for %(media_title)s"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/attachments.html:44
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:182
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:198
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:171
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:187
|
||||
msgid "Attachments"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/attachments.html:57
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:204
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:193
|
||||
msgid "Add attachment"
|
||||
msgstr ""
|
||||
|
||||
@ -763,6 +763,17 @@ msgstr ""
|
||||
msgid "WebM file (Vorbis codec)"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/image.html:36
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/image.html:39
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:132
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:152
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:59
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:87
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:93
|
||||
@ -928,21 +939,10 @@ msgstr ""
|
||||
msgid "Add this comment"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:132
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:152
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
#, python-format
|
||||
|
Binary file not shown.
@ -12,8 +12,8 @@ msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-06-01 21:16+0000\n"
|
||||
"Last-Translator: aleksejrs <deletesoftware@yandex.ru>\n"
|
||||
"Language-Team: Esperanto (http://www.transifex.com/projects/p/mediagoblin/language/eo/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -37,7 +37,7 @@ msgstr "Retpoŝtadreso"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "Uzantonomo aŭ retpoŝtadreso"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -178,7 +178,7 @@ msgstr "Permesila prefero"
|
||||
|
||||
#: mediagoblin/edit/forms.py:69
|
||||
msgid "This will be your default license on upload forms."
|
||||
msgstr ""
|
||||
msgstr "Tiu ĉi permesilo estos antaŭelektita en la alŝutformularoj."
|
||||
|
||||
#: mediagoblin/edit/forms.py:71
|
||||
msgid "Email me when others comment on my media"
|
||||
@ -264,7 +264,7 @@ msgstr "Malĝusta pasvorto"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "Via pasvorto estas sukcese ŝanĝita"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -665,11 +665,11 @@ msgstr "Konservi ŝanĝojn"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "Ŝanĝado de pasvorto de %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Konservi"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
@ -700,7 +700,7 @@ msgstr "Ŝanĝado de kontagordoj de %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "Ŝanĝi la pasvorton"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
@ -774,7 +774,7 @@ msgstr "Bildo de «%(media_title)s»"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "PDF-dosiero"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -930,15 +930,15 @@ msgstr "Aldoni ĉi tiun komenton"
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "antaŭ %(formatted_time)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "Aldonita"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Kreita"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1126,27 +1126,27 @@ msgstr ""
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "jaro(j)"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "monato(j)"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "semajno(j)"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "tago(j)"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "horo(j)"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "minuto(j)"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
@ -1185,7 +1185,7 @@ msgstr "komentis je via afiŝo"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "Ve, komentado estas malebligita."
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
Binary file not shown.
@ -20,8 +20,8 @@ msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-06-02 21:23+0000\n"
|
||||
"Last-Translator: larjona <larjona99@gmail.com>\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/projects/p/mediagoblin/language/es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -45,7 +45,7 @@ msgstr "Dirección de correo electrónico"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "Nombre de usuario o correo electrónico"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -272,7 +272,7 @@ msgstr "Contraseña incorrecta"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "Se ha cambiado la contraseña correctamente"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -289,17 +289,17 @@ msgstr "Sin embargo, se encontró un enlace simbólico de un directorio antiguo;
|
||||
#: mediagoblin/gmg_commands/assetlink.py:112
|
||||
#, python-format
|
||||
msgid "Could not link \"%s\": %s exists and is not a symlink\n"
|
||||
msgstr ""
|
||||
msgstr "No se pudo enlazar \"%s\": %s existe y no es un enlace simbólico\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:119
|
||||
#, python-format
|
||||
msgid "Skipping \"%s\"; already set up.\n"
|
||||
msgstr ""
|
||||
msgstr "Omitiendo \"%s\"; ya está establecido.\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:124
|
||||
#, python-format
|
||||
msgid "Old link found for \"%s\"; removing.\n"
|
||||
msgstr ""
|
||||
msgstr "Se encontró un enlace antiguo para \"%s\"; se eliminará.\n"
|
||||
|
||||
#: mediagoblin/meddleware/csrf.py:134
|
||||
msgid ""
|
||||
@ -315,7 +315,7 @@ msgstr "Lo sentidos, No soportamos ese tipo de archivo :("
|
||||
|
||||
#: mediagoblin/media_types/pdf/processing.py:136
|
||||
msgid "unoconv failing to run, check log file"
|
||||
msgstr ""
|
||||
msgstr "ha fallado la ejecución de unoconv, comprueba el fichero de registro (log)"
|
||||
|
||||
#: mediagoblin/media_types/video/processing.py:37
|
||||
msgid "Video transcoding failed"
|
||||
@ -626,7 +626,7 @@ msgid ""
|
||||
"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n"
|
||||
" or\n"
|
||||
" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>"
|
||||
msgstr ""
|
||||
msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Crear una cuenta en este sitio</a>\n o\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Instalar MediaGoblin en tu propio servidor</a>"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
|
||||
@ -673,11 +673,11 @@ msgstr "Guardar cambios"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "Cambiando la contraseña de %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Guardar"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
@ -708,7 +708,7 @@ msgstr "Cambio de %(username)s la configuración de la cuenta "
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "Cambiar tu contraseña."
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
@ -782,7 +782,7 @@ msgstr "Imágenes para %(media_title)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "Fichero PDF"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -938,15 +938,15 @@ msgstr "Añade un comentario "
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "hace %(formatted_time)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "Agregado"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Creado"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1134,27 +1134,27 @@ msgstr "Parece que no hay ninguna página en esta dirección. ¡Lo siento!</p><p
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "año"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "mes"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "semana"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "día"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "hora"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "minuto"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
@ -1193,7 +1193,7 @@ msgstr "comentó tu publicación"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "Lo siento, los comentarios están desactivados."
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
Binary file not shown.
@ -11,8 +11,8 @@ msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-06-01 07:11+0000\n"
|
||||
"Last-Translator: GenghisKhan <genghiskhan@gmx.ca>\n"
|
||||
"Language-Team: Hebrew (http://www.transifex.com/projects/p/mediagoblin/language/he/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -36,7 +36,7 @@ msgstr "כתובת דוא״ל"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "שם משתמש או דוא״ל"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -263,7 +263,7 @@ msgstr "סיסמה שגויה"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "סיסמתך שונתה בהצלחה"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -280,17 +280,17 @@ msgstr "בכל אופן, קישור מדור symlink נמצא; הוסר.\n"
|
||||
#: mediagoblin/gmg_commands/assetlink.py:112
|
||||
#, python-format
|
||||
msgid "Could not link \"%s\": %s exists and is not a symlink\n"
|
||||
msgstr ""
|
||||
msgstr "לא היתה אפשרות לקשר את \"%s\": %s קיים ואינו קישור סמלי (symlink)\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:119
|
||||
#, python-format
|
||||
msgid "Skipping \"%s\"; already set up.\n"
|
||||
msgstr ""
|
||||
msgstr "מדלג על \"%s\"; כבר מוגדר.\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:124
|
||||
#, python-format
|
||||
msgid "Old link found for \"%s\"; removing.\n"
|
||||
msgstr ""
|
||||
msgstr "קישור ישן נמצא עבור \"%s\"; מסיר כעת.\n"
|
||||
|
||||
#: mediagoblin/meddleware/csrf.py:134
|
||||
msgid ""
|
||||
@ -306,7 +306,7 @@ msgstr "צר לי, אינני תומך בטיפוס קובץ זה :("
|
||||
|
||||
#: mediagoblin/media_types/pdf/processing.py:136
|
||||
msgid "unoconv failing to run, check log file"
|
||||
msgstr ""
|
||||
msgstr "unoconv נכשל לפעול, בדוק קובץ יומן"
|
||||
|
||||
#: mediagoblin/media_types/video/processing.py:37
|
||||
msgid "Video transcoding failed"
|
||||
@ -617,7 +617,7 @@ msgid ""
|
||||
"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n"
|
||||
" or\n"
|
||||
" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>"
|
||||
msgstr ""
|
||||
msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">צור חשבון באתר זה</a>\n או\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">התקן את MediaGoblin על שרתך</a>"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
|
||||
@ -664,11 +664,11 @@ msgstr "שמור שינויים"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "משנה כעת את הסיסמה של %(username)s'"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "שמור"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
@ -699,7 +699,7 @@ msgstr "שינוי הגדרות חשבון עבור %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "שנה את סיסמתך."
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
@ -773,7 +773,7 @@ msgstr "תמונה עבור %(media_title)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "קובץ PDF"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -929,15 +929,15 @@ msgstr "הוסף את תגובה זו"
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "מלפני %(formatted_time)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "התווסף"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "נוצר"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1125,27 +1125,27 @@ msgstr "לא נראה שקיים עמוד בכתובת זו. צר לי!</p><p>א
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "שנה"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "חודש"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "שבוע"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "יום"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "שעה"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "דקה"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
@ -1184,7 +1184,7 @@ msgstr "הגיב/ה על פרסומך"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "מצטערים, תגובות מנוטרלות."
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
Binary file not shown.
@ -10,8 +10,8 @@ msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-06-05 22:51+0000\n"
|
||||
"Last-Translator: tryggvib <tryggvib@fsfi.is>\n"
|
||||
"Language-Team: Icelandic (Iceland) (http://www.transifex.com/projects/p/mediagoblin/language/is_IS/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -35,7 +35,7 @@ msgstr "Netfang"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "Notandanafn eða tölvupóstur"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -262,7 +262,7 @@ msgstr "Vitlaust lykilorð"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "Það tókst að breyta lykilorðinu þínu"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -279,17 +279,17 @@ msgstr "Fann samt gamlan táknrænan tengil á möppu; fjarlægður.\n"
|
||||
#: mediagoblin/gmg_commands/assetlink.py:112
|
||||
#, python-format
|
||||
msgid "Could not link \"%s\": %s exists and is not a symlink\n"
|
||||
msgstr ""
|
||||
msgstr "Gat ekki tengt \"%s\": %s er til og er ekki sýndartengill\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:119
|
||||
#, python-format
|
||||
msgid "Skipping \"%s\"; already set up.\n"
|
||||
msgstr ""
|
||||
msgstr "Hoppa yfir \"%s\"; hefur nú þegar verið sett upp.\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:124
|
||||
#, python-format
|
||||
msgid "Old link found for \"%s\"; removing.\n"
|
||||
msgstr ""
|
||||
msgstr "Gamall tengill fannst fyrir \"%s\"; fjarlægi.\n"
|
||||
|
||||
#: mediagoblin/meddleware/csrf.py:134
|
||||
msgid ""
|
||||
@ -305,7 +305,7 @@ msgstr "Ég styð því miður ekki þessa gerð af skrám :("
|
||||
|
||||
#: mediagoblin/media_types/pdf/processing.py:136
|
||||
msgid "unoconv failing to run, check log file"
|
||||
msgstr ""
|
||||
msgstr "tekst ekki að keyra unoconv, athugaðu annálsskrá"
|
||||
|
||||
#: mediagoblin/media_types/video/processing.py:37
|
||||
msgid "Video transcoding failed"
|
||||
@ -616,7 +616,7 @@ msgid ""
|
||||
"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n"
|
||||
" or\n"
|
||||
" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>"
|
||||
msgstr ""
|
||||
msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Búa til aðgang á þessari síðu</a>\neða\n<a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Settu upp þinn eigin margmiðlunarþjón</a>"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
|
||||
@ -663,11 +663,11 @@ msgstr "Vista breytingar"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "Breyti lykilorði fyrir notandann: %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Vista"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
@ -698,7 +698,7 @@ msgstr "Breyti notandaaðgangsstillingum fyrir: %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "Breyta lykilorðinu þínu."
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
@ -772,7 +772,7 @@ msgstr "Mynd fyrir %(media_title)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "PDF skrá"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -928,15 +928,15 @@ msgstr "Senda inn þessa athugasemd"
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "Fyrir %(formatted_time)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "Bætt við"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Skapað"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1124,27 +1124,27 @@ msgstr "Því miður! Það virðist ekki vera nein síða á þessari vefslóð
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "ár"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "mánuður"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "vika"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "dagur"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "klukkustund"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "mínúta"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
@ -1183,7 +1183,7 @@ msgstr "skrifaði athugasemd við færsluna þína"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "Því miður, athugasemdir eru óvirkar."
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
Binary file not shown.
@ -10,8 +10,8 @@ msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-05-31 15:40+0000\n"
|
||||
"Last-Translator: velmont <odin.omdal@gmail.com>\n"
|
||||
"Language-Team: Norwegian Nynorsk (Norway) (http://www.transifex.com/projects/p/mediagoblin/language/nn_NO/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -35,7 +35,7 @@ msgstr "Epost"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "Brukarnamn eller epost"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -262,7 +262,7 @@ msgstr "Feil passord"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "Endra passord"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -279,17 +279,17 @@ msgstr "However, old link directory symlink found; removed.\n"
|
||||
#: mediagoblin/gmg_commands/assetlink.py:112
|
||||
#, python-format
|
||||
msgid "Could not link \"%s\": %s exists and is not a symlink\n"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikkje lenkja «%s»: %s eksisterer og er ikkje ei symlenkje\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:119
|
||||
#, python-format
|
||||
msgid "Skipping \"%s\"; already set up.\n"
|
||||
msgstr ""
|
||||
msgstr "Hopper over «%s»: allereie satt opp.\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:124
|
||||
#, python-format
|
||||
msgid "Old link found for \"%s\"; removing.\n"
|
||||
msgstr ""
|
||||
msgstr "Gamal lenkje funnen for «%s»; fjernar.\n"
|
||||
|
||||
#: mediagoblin/meddleware/csrf.py:134
|
||||
msgid ""
|
||||
@ -305,7 +305,7 @@ msgstr "Orsak, stør ikkje den filtypen :("
|
||||
|
||||
#: mediagoblin/media_types/pdf/processing.py:136
|
||||
msgid "unoconv failing to run, check log file"
|
||||
msgstr ""
|
||||
msgstr "klarte ikkje køyra unoconv, sjekk logg-fil"
|
||||
|
||||
#: mediagoblin/media_types/video/processing.py:37
|
||||
msgid "Video transcoding failed"
|
||||
@ -616,7 +616,7 @@ msgid ""
|
||||
"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n"
|
||||
" or\n"
|
||||
" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>"
|
||||
msgstr ""
|
||||
msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Opprett ein konto på denne sida</a>\n eller\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set opp din eigen MediaGoblin-server</a>"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
|
||||
@ -663,11 +663,11 @@ msgstr "Lagra"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "Endrar passordet til %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Lagra"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
@ -698,7 +698,7 @@ msgstr "Endrar kontoinnstellingane til %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "Endra passordet ditt."
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
@ -772,7 +772,7 @@ msgstr "Bilete for %(media_title)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "PDF-fil"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -928,15 +928,15 @@ msgstr "Legg til dette innspelet"
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "%(formatted_time)s sidan"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "Lagt til"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Oppretta"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1124,27 +1124,27 @@ msgstr "Ser ikkje ut til å finnast noko her. Orsak.</p>\n<p>Dersom du er sikker
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "år"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "månad"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "veke"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "dag"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "time"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "minutt"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
@ -1183,7 +1183,7 @@ msgstr "kom med innspel på innlegget ditt"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "Innspel er avslege"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
Binary file not shown.
@ -10,8 +10,8 @@ msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-05-28 13:51+0000\n"
|
||||
"Last-Translator: Sergiusz Pawlowicz <transifex@pawlowicz.name>\n"
|
||||
"Language-Team: Polish (http://www.transifex.com/projects/p/mediagoblin/language/pl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -35,7 +35,7 @@ msgstr "Adres e-mail"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "Nazwa konta lub adres poczty elektronicznej"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -262,7 +262,7 @@ msgstr "Nieprawidłowe hasło"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "Twoje hasło zostało zmienione"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -279,17 +279,17 @@ msgstr "Znaleziono stary odnośnik symboliczny do katalogu; usunięto.\n"
|
||||
#: mediagoblin/gmg_commands/assetlink.py:112
|
||||
#, python-format
|
||||
msgid "Could not link \"%s\": %s exists and is not a symlink\n"
|
||||
msgstr ""
|
||||
msgstr "Nie mogę zrobić odnośnika \"%s\": %s istnieje i nie jest odnośnikiem\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:119
|
||||
#, python-format
|
||||
msgid "Skipping \"%s\"; already set up.\n"
|
||||
msgstr ""
|
||||
msgstr "Opuszczam \"%s\"; już jest gotowe.\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:124
|
||||
#, python-format
|
||||
msgid "Old link found for \"%s\"; removing.\n"
|
||||
msgstr ""
|
||||
msgstr "Znaleziono stary odnośnik dla \"%s\"; usuwam.\n"
|
||||
|
||||
#: mediagoblin/meddleware/csrf.py:134
|
||||
msgid ""
|
||||
@ -305,7 +305,7 @@ msgstr "NIestety, nie obsługujemy tego typu plików :-("
|
||||
|
||||
#: mediagoblin/media_types/pdf/processing.py:136
|
||||
msgid "unoconv failing to run, check log file"
|
||||
msgstr ""
|
||||
msgstr "nie dało się uruchomić unoconv, sprawdź log"
|
||||
|
||||
#: mediagoblin/media_types/video/processing.py:37
|
||||
msgid "Video transcoding failed"
|
||||
@ -616,7 +616,7 @@ msgid ""
|
||||
"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n"
|
||||
" or\n"
|
||||
" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>"
|
||||
msgstr ""
|
||||
msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Załóż konto na tym serwerze</a>\n albo\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Uruchom MediaGoblin na swoim własnym serwerze</a>"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
|
||||
@ -663,11 +663,11 @@ msgstr "Zapisz zmiany"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "Zmieniam hasło użytkownika %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Zachowaj"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
@ -698,7 +698,7 @@ msgstr "Zmiana ustawień konta %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "Zmień swoje hasło."
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
@ -772,7 +772,7 @@ msgstr "Grafika dla %(media_title)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "Plik PDF"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -928,15 +928,15 @@ msgstr "Dodaj komentarz"
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "%(formatted_time)s temu"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "Dodano"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Utworzono"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1124,27 +1124,27 @@ msgstr "Wygląda na to, że nic tutaj nie ma!</p><p>Jeśli jesteś pewny, że ad
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "rok"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "miesiąc"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "tydzień"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "dzień"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "godzina"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "minuta"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
@ -1183,7 +1183,7 @@ msgstr "komentarze do twojego wpisu"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "Komentowanie jest wyłączone."
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
Binary file not shown.
@ -10,8 +10,8 @@ msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-05-27 20:40+0000\n"
|
||||
"Last-Translator: George Pop <gapop@hotmail.com>\n"
|
||||
"Language-Team: Romanian (http://www.transifex.com/projects/p/mediagoblin/language/ro/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -35,7 +35,7 @@ msgstr "Adresa de e-mail"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "Numele de utilizator sau adresa de e-mail"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -262,7 +262,7 @@ msgstr "Parolă incorectă"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "Parola a fost schimbată cu succes"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -279,17 +279,17 @@ msgstr "A fost însă găsit un symlink către vechiul folder; s-a șters.\n"
|
||||
#: mediagoblin/gmg_commands/assetlink.py:112
|
||||
#, python-format
|
||||
msgid "Could not link \"%s\": %s exists and is not a symlink\n"
|
||||
msgstr ""
|
||||
msgstr "Nu s-a putut crea link pentru \"%s\": %s există deja și nu este symlink\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:119
|
||||
#, python-format
|
||||
msgid "Skipping \"%s\"; already set up.\n"
|
||||
msgstr ""
|
||||
msgstr "S-a omis \"%s\"; configurat deja.\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:124
|
||||
#, python-format
|
||||
msgid "Old link found for \"%s\"; removing.\n"
|
||||
msgstr ""
|
||||
msgstr "Există deja un link pentru \"%s\"; va fi șters.\n"
|
||||
|
||||
#: mediagoblin/meddleware/csrf.py:134
|
||||
msgid ""
|
||||
@ -305,7 +305,7 @@ msgstr "Scuze, nu recunosc acest tip de fișier :("
|
||||
|
||||
#: mediagoblin/media_types/pdf/processing.py:136
|
||||
msgid "unoconv failing to run, check log file"
|
||||
msgstr ""
|
||||
msgstr "unoconv nu poate fi executat; verificați log-ul"
|
||||
|
||||
#: mediagoblin/media_types/video/processing.py:37
|
||||
msgid "Video transcoding failed"
|
||||
@ -616,7 +616,7 @@ msgid ""
|
||||
"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n"
|
||||
" or\n"
|
||||
" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>"
|
||||
msgstr ""
|
||||
msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Creați un cont pe acest site</a>\n sau\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Instalați MediaGoblin pe serverul dvs.</a>"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
|
||||
@ -663,11 +663,11 @@ msgstr "Salvează modificările"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "Se modifică parola pentru %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Salvează"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
@ -698,7 +698,7 @@ msgstr "Se modifică setările contului pentru userul %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "Modifică parolă."
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
@ -772,7 +772,7 @@ msgstr "Imagine pentru %(media_title)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "Fișier PDF"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -928,15 +928,15 @@ msgstr "Trimite acest comentariu"
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "în urmă cu %(formatted_time)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "Adăugat"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Creat"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1124,27 +1124,27 @@ msgstr "Nu există nicio pagină la această adresă.</p><p>Dacă sunteți sigur
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "anul"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "luna"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "săptămâna"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "ziua"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "ora"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "minutul"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
@ -1183,7 +1183,7 @@ msgstr "a făcut un comentariu la postarea ta"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "Comentariile sunt dezactivate."
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
Binary file not shown.
@ -10,8 +10,8 @@ msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-06-01 21:08+0000\n"
|
||||
"Last-Translator: aleksejrs <deletesoftware@yandex.ru>\n"
|
||||
"Language-Team: Russian (http://www.transifex.com/projects/p/mediagoblin/language/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -35,7 +35,7 @@ msgstr "Адрес электронной почты"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "Имя пользователя или адрес электронной почты"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -262,7 +262,7 @@ msgstr "Неправильный пароль"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "Ваш пароль сменён успешно"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -663,11 +663,11 @@ msgstr "Сохранить изменения"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "Смена пароля %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Сохранить"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
@ -698,7 +698,7 @@ msgstr "Настройка учётной записи %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "Сменить пароль"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
@ -772,7 +772,7 @@ msgstr "Изображение «%(media_title)s»"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "PDF-файл"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -928,15 +928,15 @@ msgstr "Добавить этот комментарий"
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "%(formatted_time)s назад"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "Добавлен"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Создан"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1144,7 +1144,7 @@ msgstr ""
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "мин"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
@ -1183,7 +1183,7 @@ msgstr "оставил комментарий к вашему файлу"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "Сожалеем: возможность комментирования отключена."
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
Binary file not shown.
@ -14,8 +14,8 @@ msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-05-28 07:47+0000\n"
|
||||
"Last-Translator: martin <zatroch.martin@gmail.com>\n"
|
||||
"Language-Team: Slovak (http://www.transifex.com/projects/p/mediagoblin/language/sk/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -39,7 +39,7 @@ msgstr "Email adresse"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "Použivateľské meno alebo e-mail"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -266,7 +266,7 @@ msgstr "Nesprávne heslo"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "Tvoje heslo bolo úspešne zmenené"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -283,17 +283,17 @@ msgstr "Odstránené; hoci bol pôvodný symbolický odkaz adresára nájdený.\
|
||||
#: mediagoblin/gmg_commands/assetlink.py:112
|
||||
#, python-format
|
||||
msgid "Could not link \"%s\": %s exists and is not a symlink\n"
|
||||
msgstr ""
|
||||
msgstr "Nemožno odkazovať na \"%s\": %s existuje a nie je symbolickým odkazom\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:119
|
||||
#, python-format
|
||||
msgid "Skipping \"%s\"; already set up.\n"
|
||||
msgstr ""
|
||||
msgstr "Preskakujem \"%s\"; opakovane nastavené.\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:124
|
||||
#, python-format
|
||||
msgid "Old link found for \"%s\"; removing.\n"
|
||||
msgstr ""
|
||||
msgstr "Nájdený starý odkaz pre \"%s\"; odstraňujem.\n"
|
||||
|
||||
#: mediagoblin/meddleware/csrf.py:134
|
||||
msgid ""
|
||||
@ -309,7 +309,7 @@ msgstr "Prepáč, nepodporujem tento typ súborov =("
|
||||
|
||||
#: mediagoblin/media_types/pdf/processing.py:136
|
||||
msgid "unoconv failing to run, check log file"
|
||||
msgstr ""
|
||||
msgstr "beh unoconv zlyhal, preskúmajte log záznam"
|
||||
|
||||
#: mediagoblin/media_types/video/processing.py:37
|
||||
msgid "Video transcoding failed"
|
||||
@ -620,7 +620,7 @@ msgid ""
|
||||
"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n"
|
||||
" or\n"
|
||||
" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>"
|
||||
msgstr ""
|
||||
msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Vytvoriť účet na tejto stránke</a>\n alebo\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Nastaviť MediaGoblin na vlastnom serveri</a>"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
|
||||
@ -667,11 +667,11 @@ msgstr "Uložiť zmeny"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "Mením heslo používateľa %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Uložiť"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
@ -702,7 +702,7 @@ msgstr "Mením nastavenia účtu používateľa %(username)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "Zmeniť svoje heslo."
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
@ -776,7 +776,7 @@ msgstr "Obrázok pre %(media_title)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "PDF súbor"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -932,15 +932,15 @@ msgstr "Pridať tento komentár"
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "pred %(formatted_time)s "
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "Pridané"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Vytvorené"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
@ -1128,27 +1128,27 @@ msgstr "Zdá sa, že na tejto adrese sa nič nenachádza. Prepáč!</p><p>Pokia
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "rok"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "mesiac"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "týždeň"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "deň"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "hodina"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "minúta"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
@ -1187,7 +1187,7 @@ msgstr "okmentoval tvoj príspevok"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "Prepáč, komentovanie je vypnuté."
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
BIN
mediagoblin/i18n/tr_TR/LC_MESSAGES/mediagoblin.mo
Normal file
BIN
mediagoblin/i18n/tr_TR/LC_MESSAGES/mediagoblin.mo
Normal file
Binary file not shown.
1252
mediagoblin/i18n/tr_TR/LC_MESSAGES/mediagoblin.po
Normal file
1252
mediagoblin/i18n/tr_TR/LC_MESSAGES/mediagoblin.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
mediagoblin/i18n/zh_CN/LC_MESSAGES/mediagoblin.mo
Normal file
BIN
mediagoblin/i18n/zh_CN/LC_MESSAGES/mediagoblin.mo
Normal file
Binary file not shown.
1256
mediagoblin/i18n/zh_CN/LC_MESSAGES/mediagoblin.po
Normal file
1256
mediagoblin/i18n/zh_CN/LC_MESSAGES/mediagoblin.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -5,14 +5,16 @@
|
||||
# Translators:
|
||||
# <chc@citi.sinica.edu.tw>, 2011
|
||||
# Harry Chen <harryhow@gmail.com>, 2011-2012
|
||||
# medicalwei <medicalwei@gmail.com>, 2013
|
||||
# medicalwei <medicalwei@gmail.com>, 2012
|
||||
# m13253 <m13253@hotmail.com>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: GNU MediaGoblin\n"
|
||||
"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n"
|
||||
"POT-Creation-Date: 2013-05-27 13:54-0500\n"
|
||||
"PO-Revision-Date: 2013-05-27 18:54+0000\n"
|
||||
"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
|
||||
"PO-Revision-Date: 2013-06-16 01:40+0000\n"
|
||||
"Last-Translator: m13253 <m13253@hotmail.com>\n"
|
||||
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/projects/p/mediagoblin/language/zh_TW/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -36,7 +38,7 @@ msgstr "Email 位址"
|
||||
|
||||
#: mediagoblin/auth/forms.py:41
|
||||
msgid "Username or Email"
|
||||
msgstr ""
|
||||
msgstr "使用者名稱或 email"
|
||||
|
||||
#: mediagoblin/auth/forms.py:52
|
||||
msgid "Username or email"
|
||||
@ -44,15 +46,15 @@ msgstr "使用者名稱或 email"
|
||||
|
||||
#: mediagoblin/auth/tools.py:31
|
||||
msgid "Invalid User name or email address."
|
||||
msgstr ""
|
||||
msgstr "無效的使用者名稱或 email 位置。"
|
||||
|
||||
#: mediagoblin/auth/tools.py:32
|
||||
msgid "This field does not take email addresses."
|
||||
msgstr ""
|
||||
msgstr "本欄位不接受 email 位置。"
|
||||
|
||||
#: mediagoblin/auth/tools.py:33
|
||||
msgid "This field requires an email address."
|
||||
msgstr ""
|
||||
msgstr "本欄位需要 email 位置。"
|
||||
|
||||
#: mediagoblin/auth/views.py:54
|
||||
msgid "Sorry, registration is disabled on this instance."
|
||||
@ -92,11 +94,11 @@ msgstr "重送認證信。"
|
||||
msgid ""
|
||||
"If that email address (case sensitive!) is registered an email has been sent"
|
||||
" with instructions on how to change your password."
|
||||
msgstr ""
|
||||
msgstr "如果那 email 位置 (請注意大小寫) 已經註冊,寫有修改密碼步驟的 email 已經送出。"
|
||||
|
||||
#: mediagoblin/auth/views.py:269
|
||||
msgid "Couldn't find someone with that username."
|
||||
msgstr ""
|
||||
msgstr "找不到相關的使用者名稱。"
|
||||
|
||||
#: mediagoblin/auth/views.py:272
|
||||
msgid ""
|
||||
@ -173,15 +175,15 @@ msgstr "本網址出錯了"
|
||||
|
||||
#: mediagoblin/edit/forms.py:63
|
||||
msgid "License preference"
|
||||
msgstr ""
|
||||
msgstr "授權偏好"
|
||||
|
||||
#: mediagoblin/edit/forms.py:69
|
||||
msgid "This will be your default license on upload forms."
|
||||
msgstr ""
|
||||
msgstr "在上傳頁面,這將會是您預設的授權模式。"
|
||||
|
||||
#: mediagoblin/edit/forms.py:71
|
||||
msgid "Email me when others comment on my media"
|
||||
msgstr "當有人對我的媒體評論時寄信給我"
|
||||
msgstr "當有人對我的媒體留言時寄信給我"
|
||||
|
||||
#: mediagoblin/edit/forms.py:83
|
||||
msgid "The title can't be empty"
|
||||
@ -225,7 +227,7 @@ msgstr "您加上了附件「%s」!"
|
||||
|
||||
#: mediagoblin/edit/views.py:182
|
||||
msgid "You can only edit your own profile."
|
||||
msgstr ""
|
||||
msgstr "您只能修改您自己的個人檔案。"
|
||||
|
||||
#: mediagoblin/edit/views.py:188
|
||||
msgid "You are editing a user's profile. Proceed with caution."
|
||||
@ -241,7 +243,7 @@ msgstr "帳號設定已儲存"
|
||||
|
||||
#: mediagoblin/edit/views.py:274
|
||||
msgid "You need to confirm the deletion of your account."
|
||||
msgstr ""
|
||||
msgstr "您必須要確認是否刪除您的帳號。"
|
||||
|
||||
#: mediagoblin/edit/views.py:310 mediagoblin/submit/views.py:138
|
||||
#: mediagoblin/user_pages/views.py:222
|
||||
@ -263,7 +265,7 @@ msgstr "密碼錯誤"
|
||||
|
||||
#: mediagoblin/edit/views.py:363
|
||||
msgid "Your password was changed successfully"
|
||||
msgstr ""
|
||||
msgstr "您的密碼已經成功修改"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:60
|
||||
msgid "Cannot link theme... no theme set\n"
|
||||
@ -280,24 +282,24 @@ msgstr "但是舊的目錄連結已經找到並移除。\n"
|
||||
#: mediagoblin/gmg_commands/assetlink.py:112
|
||||
#, python-format
|
||||
msgid "Could not link \"%s\": %s exists and is not a symlink\n"
|
||||
msgstr ""
|
||||
msgstr "無法連結「%s」:%s 存在,且不是符號連結\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:119
|
||||
#, python-format
|
||||
msgid "Skipping \"%s\"; already set up.\n"
|
||||
msgstr ""
|
||||
msgstr "跳過「%s」,已經建置完成。\n"
|
||||
|
||||
#: mediagoblin/gmg_commands/assetlink.py:124
|
||||
#, python-format
|
||||
msgid "Old link found for \"%s\"; removing.\n"
|
||||
msgstr ""
|
||||
msgstr "找到「%s」舊的連結,刪除中。\n"
|
||||
|
||||
#: mediagoblin/meddleware/csrf.py:134
|
||||
msgid ""
|
||||
"CSRF cookie not present. This is most likely the result of a cookie blocker "
|
||||
"or somesuch.<br/>Make sure to permit the settings of cookies for this "
|
||||
"domain."
|
||||
msgstr ""
|
||||
msgstr "跨網站存取 (CSRF) 的 cookie 不存在,有可能是 cookie 阻擋程式之類的程式導致的。<br/>請允許此網域的 cookie 設定。"
|
||||
|
||||
#: mediagoblin/media_types/__init__.py:111
|
||||
#: mediagoblin/media_types/__init__.py:155
|
||||
@ -306,7 +308,7 @@ msgstr "抱歉,我不支援這樣的檔案格式 :("
|
||||
|
||||
#: mediagoblin/media_types/pdf/processing.py:136
|
||||
msgid "unoconv failing to run, check log file"
|
||||
msgstr ""
|
||||
msgstr "unoconv 無法執行,請檢查紀錄檔"
|
||||
|
||||
#: mediagoblin/media_types/video/processing.py:37
|
||||
msgid "Video transcoding failed"
|
||||
@ -335,7 +337,7 @@ msgstr "名稱"
|
||||
|
||||
#: mediagoblin/plugins/oauth/forms.py:35
|
||||
msgid "The name of the OAuth client"
|
||||
msgstr "OAuth client 的名稱"
|
||||
msgstr "OAuth 用戶程式的名稱"
|
||||
|
||||
#: mediagoblin/plugins/oauth/forms.py:36
|
||||
msgid "Description"
|
||||
@ -359,7 +361,7 @@ msgid ""
|
||||
" <strong>Public</strong> - The client can't make confidential\n"
|
||||
" requests to the GNU MediaGoblin instance (e.g. client-side\n"
|
||||
" JavaScript client)."
|
||||
msgstr "<strong>秘密</strong> — OAuth client 可以對 GNU MediaGoblin 站台發送不被使用者代理攔截的請求 (例如伺服端的 client)。\n<strong>公開</strong> — OAuth client 無法對 GNU MediaGoblin 站台發送秘密的請求 (例如客戶端的 JavaScript client)。"
|
||||
msgstr "<strong>秘密</strong> — OAuth 用戶程式可以對 GNU MediaGoblin 站台發送不被使用者代理攔截的請求 (例如伺服端的用戶程式)。\n<strong>公開</strong> — OAuth 用戶程式無法對 GNU MediaGoblin 站台發送秘密的請求 (例如客戶端的 JavaScript 用戶程式)。"
|
||||
|
||||
#: mediagoblin/plugins/oauth/forms.py:52
|
||||
msgid "Redirect URI"
|
||||
@ -369,23 +371,23 @@ msgstr "重定向 URI"
|
||||
msgid ""
|
||||
"The redirect URI for the applications, this field\n"
|
||||
" is <strong>required</strong> for public clients."
|
||||
msgstr "此應用程式的重定向 URI,本欄位在公開類型的 OAuth client 為必填。"
|
||||
msgstr "此應用程式的重定向 URI,本欄位在公開類型的 OAuth 用戶程式為必填。"
|
||||
|
||||
#: mediagoblin/plugins/oauth/forms.py:66
|
||||
msgid "This field is required for public clients"
|
||||
msgstr "本欄位在公開類型的 OAuth client 為必填"
|
||||
msgstr "本欄位在公開類型的用戶程式為必填"
|
||||
|
||||
#: mediagoblin/plugins/oauth/views.py:56
|
||||
msgid "The client {0} has been registered!"
|
||||
msgstr "OAuth client {0} 註冊完成!"
|
||||
msgstr "OAuth 用戶程式 {0} 註冊完成!"
|
||||
|
||||
#: mediagoblin/plugins/oauth/templates/oauth/client/connections.html:22
|
||||
msgid "OAuth client connections"
|
||||
msgstr ""
|
||||
msgstr "OAuth 用戶程式連線"
|
||||
|
||||
#: mediagoblin/plugins/oauth/templates/oauth/client/list.html:22
|
||||
msgid "Your OAuth clients"
|
||||
msgstr ""
|
||||
msgstr "您的 OAuth 用戶程式"
|
||||
|
||||
#: mediagoblin/plugins/oauth/templates/oauth/client/register.html:29
|
||||
#: mediagoblin/templates/mediagoblin/submit/collection.html:30
|
||||
@ -450,7 +452,7 @@ msgstr "媒體處理面板"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/base.html:96
|
||||
msgid "Log out"
|
||||
msgstr ""
|
||||
msgstr "登出"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/base.html:99
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/user.html:156
|
||||
@ -577,7 +579,7 @@ msgstr "%(username)s 您好:\n\n要啟動 GNU MediaGoblin 帳號,請在您
|
||||
msgid ""
|
||||
"Powered by <a href=\"http://mediagoblin.org/\" title='Version "
|
||||
"%(version)s'>MediaGoblin</a>, a <a href=\"http://gnu.org/\">GNU</a> project."
|
||||
msgstr ""
|
||||
msgstr "本站使用 <a href=\"http://mediagoblin.org/\" title='Version %(version)s'>MediaGoblin</a>,這是一個 <a href=\"http://gnu.org/\">GNU</a> 專案。"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/base_footer.html:24
|
||||
#, python-format
|
||||
@ -617,7 +619,7 @@ msgid ""
|
||||
"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n"
|
||||
" or\n"
|
||||
" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>"
|
||||
msgstr ""
|
||||
msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">在本站建立您的帳號</a>\n 或是\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">在您自己的伺服器上安裝 MediaGoblin</a>"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
|
||||
@ -664,20 +666,20 @@ msgstr "儲存變更"
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:38
|
||||
#, python-format
|
||||
msgid "Changing %(username)s's password"
|
||||
msgstr ""
|
||||
msgstr "更改 %(username)s 的密碼"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/change_pass.html:45
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "儲存"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
|
||||
#, python-format
|
||||
msgid "Really delete user '%(user_name)s' and all related media/comments?"
|
||||
msgstr ""
|
||||
msgstr "真的要刪除使用者「%(user_name)s」以及相關的媒體與留言?"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:35
|
||||
msgid "Yes, really delete my account"
|
||||
msgstr ""
|
||||
msgstr "是的,我真的要把我的帳號刪除"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:44
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/collection_confirm_delete.html:48
|
||||
@ -699,11 +701,11 @@ msgstr "正在改變 %(username)s 的帳號設定"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:46
|
||||
msgid "Change your password."
|
||||
msgstr ""
|
||||
msgstr "更改您的密碼。"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:62
|
||||
msgid "Delete my account"
|
||||
msgstr ""
|
||||
msgstr "刪除我的帳號"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/edit/edit_collection.html:29
|
||||
#, python-format
|
||||
@ -722,7 +724,7 @@ msgstr "編輯 %(username)s 的個人檔案"
|
||||
#: mediagoblin/templates/mediagoblin/listings/tag.html:35
|
||||
#, python-format
|
||||
msgid "Media tagged with: %(tag_name)s"
|
||||
msgstr "此媒體被 tag 成:%(tag_name)s"
|
||||
msgstr "這個媒體具有以下標籤:%(tag_name)s"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/audio.html:56
|
||||
@ -773,7 +775,7 @@ msgstr " %(media_title)s 的照片"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:79
|
||||
msgid "PDF file"
|
||||
msgstr ""
|
||||
msgstr "PDF 檔"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:112
|
||||
msgid "Toggle Rotate"
|
||||
@ -781,7 +783,7 @@ msgstr "切換旋轉"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:113
|
||||
msgid "Perspective"
|
||||
msgstr "視角"
|
||||
msgstr "透視"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:116
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:117
|
||||
@ -820,14 +822,14 @@ msgid ""
|
||||
"Sorry, this video will not work because\n"
|
||||
" your web browser does not support HTML5 \n"
|
||||
" video."
|
||||
msgstr ""
|
||||
msgstr "抱歉,由於您的瀏覽器不支援 HTML5 影片,本影片無法播放"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/video.html:47
|
||||
msgid ""
|
||||
"You can get a modern web browser that \n"
|
||||
" can play this video at <a href=\"http://getfirefox.com\">\n"
|
||||
" http://getfirefox.com</a>!"
|
||||
msgstr ""
|
||||
msgstr "您可以在 <a href=\"http://getfirefox.com\">http://getfirefox.com</a> 取得可以播放此影片的先進瀏覽器。"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/media_displays/video.html:69
|
||||
msgid "WebM file (640p; VP8/Vorbis)"
|
||||
@ -880,19 +882,19 @@ msgstr "移除"
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/collection_list.html:21
|
||||
#, python-format
|
||||
msgid "%(username)s's collections"
|
||||
msgstr ""
|
||||
msgstr "%(username)s 的蒐藏"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/collection_list.html:28
|
||||
#, python-format
|
||||
msgid "<a href=\"%(user_url)s\">%(username)s</a>'s collections"
|
||||
msgstr ""
|
||||
msgstr "<a href=\"%(user_url)s\">%(username)s</a> 的蒐藏"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/comment_email.txt:19
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Hi %(username)s,\n"
|
||||
"%(comment_author)s commented on your post (%(comment_url)s) at %(instance_name)s\n"
|
||||
msgstr "%(username)s 您好:\n%(comment_author)s 在 %(instance_name)s 對您的內容 (%(comment_url)s) 張貼評論\n"
|
||||
msgstr "%(username)s 您好:\n%(comment_author)s 在 %(instance_name)s 對您的內容 (%(comment_url)s) 張貼留言\n"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
|
||||
#, python-format
|
||||
@ -904,7 +906,7 @@ msgstr "%(username)s的媒體"
|
||||
msgid ""
|
||||
"<a href=\"%(user_url)s\">%(username)s</a>'s media with tag <a "
|
||||
"href=\"%(tag_url)s\">%(tag)s</a>"
|
||||
msgstr ""
|
||||
msgstr "標籤為 <a href=\"%(tag_url)s\">%(tag)s</a> 的 <a href=\"%(user_url)s\">%(username)s</a> 的媒體"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:48
|
||||
#, python-format
|
||||
@ -918,32 +920,32 @@ msgstr "❖ 瀏覽 <a href=\"%(user_url)s\">%(username)s</a> 的媒體"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:95
|
||||
msgid "Add a comment"
|
||||
msgstr "新增評論"
|
||||
msgstr "新增留言"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:104
|
||||
msgid "Add this comment"
|
||||
msgstr "增加評論"
|
||||
msgstr "增加留言"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:132
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:152
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:164
|
||||
#, python-format
|
||||
msgid "%(formatted_time)s ago"
|
||||
msgstr ""
|
||||
msgstr "%(formatted_time)s 前"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:150
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
msgstr "新增於"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media.html:161
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "建立於"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
|
||||
#, python-format
|
||||
msgid "Add “%(media_title)s” to a collection"
|
||||
msgstr ""
|
||||
msgstr "加入 “%(media_title)s” 至蒐藏"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:54
|
||||
msgid "+"
|
||||
@ -1022,7 +1024,7 @@ msgstr "這個使用者(還)沒有填寫個人檔案。"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/user.html:124
|
||||
msgid "Browse collections"
|
||||
msgstr ""
|
||||
msgstr "瀏覽蒐藏"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/user_pages/user.html:137
|
||||
#, python-format
|
||||
@ -1047,11 +1049,11 @@ msgstr " (移除)"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/utils/collections.html:21
|
||||
msgid "Collected in"
|
||||
msgstr ""
|
||||
msgstr "蒐集了"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/utils/collections.html:40
|
||||
msgid "Add to a collection"
|
||||
msgstr ""
|
||||
msgstr "加入至蒐藏"
|
||||
|
||||
#: mediagoblin/templates/mediagoblin/utils/feed_link.html:21
|
||||
#: mediagoblin/themes/airy/templates/mediagoblin/utils/feed_link.html:21
|
||||
@ -1125,31 +1127,31 @@ msgstr "不好意思,看起來這個網址上沒有網頁。</p><p>如果您
|
||||
|
||||
#: mediagoblin/tools/timesince.py:62
|
||||
msgid "year"
|
||||
msgstr ""
|
||||
msgstr "年"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:63
|
||||
msgid "month"
|
||||
msgstr ""
|
||||
msgstr "月"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:64
|
||||
msgid "week"
|
||||
msgstr ""
|
||||
msgstr "週"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:65
|
||||
msgid "day"
|
||||
msgstr ""
|
||||
msgstr "日"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:66
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "小時"
|
||||
|
||||
#: mediagoblin/tools/timesince.py:67
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "分"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:23
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
msgstr "留言"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:25
|
||||
msgid ""
|
||||
@ -1168,7 +1170,7 @@ msgstr "我確定我要從蒐藏中移除此項目"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:39
|
||||
msgid "Collection"
|
||||
msgstr ""
|
||||
msgstr "蒐藏"
|
||||
|
||||
#: mediagoblin/user_pages/forms.py:40
|
||||
msgid "-- Select --"
|
||||
@ -1180,11 +1182,11 @@ msgstr "加註"
|
||||
|
||||
#: mediagoblin/user_pages/lib.py:58
|
||||
msgid "commented on your post"
|
||||
msgstr "在您的內容張貼評論"
|
||||
msgstr "在您的內容張貼留言"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:169
|
||||
msgid "Sorry, comments are disabled."
|
||||
msgstr ""
|
||||
msgstr "抱歉,留言被關閉。"
|
||||
|
||||
#: mediagoblin/user_pages/views.py:174
|
||||
msgid "Oops, your comment was empty."
|
||||
|
@ -16,12 +16,18 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from celery import Celery
|
||||
from mediagoblin.tools.pluginapi import hook_runall
|
||||
|
||||
|
||||
MANDATORY_CELERY_IMPORTS = ['mediagoblin.processing.task']
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
MANDATORY_CELERY_IMPORTS = [
|
||||
'mediagoblin.processing.task',
|
||||
'mediagoblin.notifications.task']
|
||||
|
||||
DEFAULT_SETTINGS_MODULE = 'mediagoblin.init.celery.dummy_settings_module'
|
||||
|
||||
@ -97,3 +103,13 @@ def setup_celery_from_config(app_config, global_config,
|
||||
|
||||
if set_environ:
|
||||
os.environ['CELERY_CONFIG_MODULE'] = settings_module
|
||||
|
||||
# Replace the default celery.current_app.conf if celery has already been
|
||||
# initiated
|
||||
from celery import current_app
|
||||
|
||||
_log.info('Setting celery configuration from object "{0}"'.format(
|
||||
settings_module))
|
||||
current_app.config_from_object(this_module)
|
||||
|
||||
_log.debug('Celery broker host: {0}'.format(current_app.conf['BROKER_HOST']))
|
||||
|
@ -46,7 +46,7 @@ def sniff_handler(media_file, **kw):
|
||||
if kw.get('media') is not None:
|
||||
name, ext = os.path.splitext(kw['media'].filename)
|
||||
clean_ext = ext[1:].lower()
|
||||
|
||||
|
||||
if clean_ext in SUPPORTED_FILETYPES:
|
||||
_log.info('Found file extension in supported filetypes')
|
||||
return True
|
||||
|
@ -22,9 +22,15 @@ import logging
|
||||
import urllib
|
||||
import multiprocessing
|
||||
import gobject
|
||||
|
||||
old_argv = sys.argv
|
||||
sys.argv = []
|
||||
|
||||
import pygst
|
||||
pygst.require('0.10')
|
||||
import gst
|
||||
|
||||
sys.argv = old_argv
|
||||
import struct
|
||||
try:
|
||||
from PIL import Image
|
||||
|
141
mediagoblin/notifications/__init__.py
Normal file
141
mediagoblin/notifications/__init__.py
Normal file
@ -0,0 +1,141 @@
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
|
||||
from mediagoblin.db.models import Notification, \
|
||||
CommentNotification, CommentSubscription
|
||||
from mediagoblin.notifications.task import email_notification_task
|
||||
from mediagoblin.notifications.tools import generate_comment_message
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
def trigger_notification(comment, media_entry, request):
|
||||
'''
|
||||
Send out notifications about a new comment.
|
||||
'''
|
||||
subscriptions = CommentSubscription.query.filter_by(
|
||||
media_entry_id=media_entry.id).all()
|
||||
|
||||
for subscription in subscriptions:
|
||||
if not subscription.notify:
|
||||
continue
|
||||
|
||||
if comment.get_author == subscription.user:
|
||||
continue
|
||||
|
||||
cn = CommentNotification(
|
||||
user_id=subscription.user_id,
|
||||
subject_id=comment.id)
|
||||
|
||||
cn.save()
|
||||
|
||||
if subscription.send_email:
|
||||
message = generate_comment_message(
|
||||
subscription.user,
|
||||
comment,
|
||||
media_entry,
|
||||
request)
|
||||
|
||||
email_notification_task.apply_async([cn.id, message])
|
||||
|
||||
|
||||
def mark_notification_seen(notification):
|
||||
if notification:
|
||||
notification.seen = True
|
||||
notification.save()
|
||||
|
||||
|
||||
def mark_comment_notification_seen(comment_id, user):
|
||||
notification = CommentNotification.query.filter_by(
|
||||
user_id=user.id,
|
||||
subject_id=comment_id).first()
|
||||
|
||||
_log.debug('Marking {0} as seen.'.format(notification))
|
||||
|
||||
mark_notification_seen(notification)
|
||||
|
||||
|
||||
def get_comment_subscription(user_id, media_entry_id):
|
||||
return CommentSubscription.query.filter_by(
|
||||
user_id=user_id,
|
||||
media_entry_id=media_entry_id).first()
|
||||
|
||||
def add_comment_subscription(user, media_entry):
|
||||
'''
|
||||
Create a comment subscription for a User on a MediaEntry.
|
||||
|
||||
Uses the User's wants_comment_notification to set email notifications for
|
||||
the subscription to enabled/disabled.
|
||||
'''
|
||||
cn = get_comment_subscription(user.id, media_entry.id)
|
||||
|
||||
if not cn:
|
||||
cn = CommentSubscription(
|
||||
user_id=user.id,
|
||||
media_entry_id=media_entry.id)
|
||||
|
||||
cn.notify = True
|
||||
|
||||
if not user.wants_comment_notification:
|
||||
cn.send_email = False
|
||||
|
||||
cn.save()
|
||||
|
||||
|
||||
def silence_comment_subscription(user, media_entry):
|
||||
'''
|
||||
Silence a subscription so that the user is never notified in any way about
|
||||
new comments on an entry
|
||||
'''
|
||||
cn = get_comment_subscription(user.id, media_entry.id)
|
||||
|
||||
if cn:
|
||||
cn.notify = False
|
||||
cn.send_email = False
|
||||
cn.save()
|
||||
|
||||
|
||||
def remove_comment_subscription(user, media_entry):
|
||||
cn = get_comment_subscription(user.id, media_entry.id)
|
||||
|
||||
if cn:
|
||||
cn.delete()
|
||||
|
||||
|
||||
NOTIFICATION_FETCH_LIMIT = 100
|
||||
|
||||
|
||||
def get_notifications(user_id, only_unseen=True):
|
||||
query = Notification.query.filter_by(user_id=user_id)
|
||||
|
||||
if only_unseen:
|
||||
query = query.filter_by(seen=False)
|
||||
|
||||
notifications = query.limit(
|
||||
NOTIFICATION_FETCH_LIMIT).all()
|
||||
|
||||
return notifications
|
||||
|
||||
def get_notification_count(user_id, only_unseen=True):
|
||||
query = Notification.query.filter_by(user_id=user_id)
|
||||
|
||||
if only_unseen:
|
||||
query = query.filter_by(seen=False)
|
||||
|
||||
count = query.count()
|
||||
|
||||
return count
|
25
mediagoblin/notifications/routing.py
Normal file
25
mediagoblin/notifications/routing.py
Normal file
@ -0,0 +1,25 @@
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from mediagoblin.tools.routing import add_route
|
||||
|
||||
add_route('mediagoblin.notifications.subscribe_comments',
|
||||
'/u/<string:user>/m/<string:media>/notifications/subscribe/comments/',
|
||||
'mediagoblin.notifications.views:subscribe_comments')
|
||||
|
||||
add_route('mediagoblin.notifications.silence_comments',
|
||||
'/u/<string:user>/m/<string:media>/notifications/silence/',
|
||||
'mediagoblin.notifications.views:silence_comments')
|
46
mediagoblin/notifications/task.py
Normal file
46
mediagoblin/notifications/task.py
Normal file
@ -0,0 +1,46 @@
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
|
||||
from celery import registry
|
||||
from celery.task import Task
|
||||
|
||||
from mediagoblin.tools.mail import send_email
|
||||
from mediagoblin.db.models import CommentNotification
|
||||
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EmailNotificationTask(Task):
|
||||
'''
|
||||
Celery notification task.
|
||||
|
||||
This task is executed by celeryd to offload long-running operations from
|
||||
the web server.
|
||||
'''
|
||||
def run(self, notification_id, message):
|
||||
cn = CommentNotification.query.filter_by(id=notification_id).first()
|
||||
_log.info('Sending notification email about {0}'.format(cn))
|
||||
|
||||
return send_email(
|
||||
message['from'],
|
||||
[message['to']],
|
||||
message['subject'],
|
||||
message['body'])
|
||||
|
||||
email_notification_task = registry.tasks[EmailNotificationTask.name]
|
55
mediagoblin/notifications/tools.py
Normal file
55
mediagoblin/notifications/tools.py
Normal file
@ -0,0 +1,55 @@
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from mediagoblin.tools.template import render_template
|
||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
||||
from mediagoblin import mg_globals
|
||||
|
||||
def generate_comment_message(user, comment, media, request):
|
||||
"""
|
||||
Sends comment email to user when a comment is made on their media.
|
||||
|
||||
Args:
|
||||
- user: the user object to whom the email is sent
|
||||
- comment: the comment object referencing user's media
|
||||
- media: the media object the comment is about
|
||||
- request: the request
|
||||
"""
|
||||
|
||||
comment_url = request.urlgen(
|
||||
'mediagoblin.user_pages.media_home.view_comment',
|
||||
comment=comment.id,
|
||||
user=media.get_uploader.username,
|
||||
media=media.slug_or_id,
|
||||
qualified=True) + '#comment'
|
||||
|
||||
comment_author = comment.get_author.username
|
||||
|
||||
rendered_email = render_template(
|
||||
request, 'mediagoblin/user_pages/comment_email.txt',
|
||||
{'username': user.username,
|
||||
'comment_author': comment_author,
|
||||
'comment_content': comment.content,
|
||||
'comment_url': comment_url})
|
||||
|
||||
return {
|
||||
'from': mg_globals.app_config['email_sender_address'],
|
||||
'to': user.email,
|
||||
'subject': '{instance_title} - {comment_author} '.format(
|
||||
comment_author=comment_author,
|
||||
instance_title=mg_globals.app_config['html_title']) \
|
||||
+ _('commented on your post'),
|
||||
'body': rendered_email}
|
54
mediagoblin/notifications/views.py
Normal file
54
mediagoblin/notifications/views.py
Normal file
@ -0,0 +1,54 @@
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from mediagoblin.tools.response import render_to_response, render_404, redirect
|
||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
||||
from mediagoblin.decorators import (uses_pagination, get_user_media_entry,
|
||||
get_media_entry_by_id,
|
||||
require_active_login, user_may_delete_media, user_may_alter_collection,
|
||||
get_user_collection, get_user_collection_item, active_user_from_url)
|
||||
|
||||
from mediagoblin import messages
|
||||
|
||||
from mediagoblin.notifications import add_comment_subscription, \
|
||||
silence_comment_subscription
|
||||
|
||||
from werkzeug.exceptions import BadRequest
|
||||
|
||||
@get_user_media_entry
|
||||
@require_active_login
|
||||
def subscribe_comments(request, media):
|
||||
|
||||
add_comment_subscription(request.user, media)
|
||||
|
||||
messages.add_message(request,
|
||||
messages.SUCCESS,
|
||||
_('Subscribed to comments on %s!')
|
||||
% media.title)
|
||||
|
||||
return redirect(request, location=media.url_for_self(request.urlgen))
|
||||
|
||||
@get_user_media_entry
|
||||
@require_active_login
|
||||
def silence_comments(request, media):
|
||||
silence_comment_subscription(request.user, media)
|
||||
|
||||
messages.add_message(request,
|
||||
messages.SUCCESS,
|
||||
_('You will not receive notifications for comments on'
|
||||
' %s.') % media.title)
|
||||
|
||||
return redirect(request, location=media.url_for_self(request.urlgen))
|
@ -13,8 +13,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import uuid
|
||||
|
||||
from mediagoblin.plugins.basic_auth import forms as auth_forms
|
||||
from mediagoblin.plugins.basic_auth import tools as auth_tools
|
||||
from mediagoblin.db.models import User
|
||||
@ -45,7 +43,6 @@ def create_user(registration_form):
|
||||
user.email = registration_form.email.data
|
||||
user.pw_hash = gen_password_hash(
|
||||
registration_form.password.data)
|
||||
user.verification_key = unicode(uuid.uuid4())
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
@ -35,6 +35,7 @@ def get_url_map():
|
||||
import mediagoblin.edit.routing
|
||||
import mediagoblin.webfinger.routing
|
||||
import mediagoblin.listings.routing
|
||||
import mediagoblin.notifications.routing
|
||||
|
||||
for route in PluginManager().get_routes():
|
||||
add_route(*route)
|
||||
|
@ -129,6 +129,7 @@ header {
|
||||
|
||||
.header_dropdown {
|
||||
margin-bottom: 20px;
|
||||
padding: 0px 10px 0px 10px;
|
||||
}
|
||||
|
||||
.header_dropdown li {
|
||||
@ -384,6 +385,12 @@ a.comment_whenlink:hover {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.comment_active {
|
||||
box-shadow: 0px 0px 15px 15px #378566;
|
||||
background: #378566;
|
||||
color: #f7f7f7;
|
||||
}
|
||||
|
||||
textarea#comment_content {
|
||||
resize: vertical;
|
||||
width: 100%;
|
||||
|
File diff suppressed because it is too large
Load Diff
36
mediagoblin/static/js/notifications.js
Normal file
36
mediagoblin/static/js/notifications.js
Normal file
@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
/**
|
||||
* GNU MediaGoblin -- federated, autonomous media hosting
|
||||
* Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var notifications = {};
|
||||
|
||||
(function (n) {
|
||||
n._base = '/';
|
||||
n._endpoint = 'notifications/json';
|
||||
|
||||
n.init = function () {
|
||||
$('.notification-gem').on('click', function () {
|
||||
$('.header_dropdown_down:visible').click();
|
||||
});
|
||||
}
|
||||
|
||||
})(notifications)
|
||||
|
||||
$(document).ready(function () {
|
||||
notifications.init();
|
||||
});
|
File diff suppressed because it is too large
Load Diff
@ -75,7 +75,7 @@ class CloudFilesStorage(StorageInterface):
|
||||
_log.debug('Container: {0}'.format(
|
||||
self.container.name))
|
||||
|
||||
self.container_uri = self.container.public_uri()
|
||||
self.container_uri = self.container.public_ssl_uri()
|
||||
|
||||
def _resolve_filepath(self, filepath):
|
||||
return '/'.join(
|
||||
|
@ -34,6 +34,8 @@ from mediagoblin.media_types import sniff_media, \
|
||||
from mediagoblin.submit.lib import check_file_field, prepare_queue_task, \
|
||||
run_process_media, new_upload_entry
|
||||
|
||||
from mediagoblin.notifications import add_comment_subscription
|
||||
|
||||
|
||||
@require_active_login
|
||||
def submit_start(request):
|
||||
@ -92,6 +94,8 @@ def submit_start(request):
|
||||
run_process_media(entry, feed_url)
|
||||
add_message(request, SUCCESS, _('Woohoo! Submitted!'))
|
||||
|
||||
add_comment_subscription(request.user, entry)
|
||||
|
||||
return redirect(request, "mediagoblin.user_pages.user_home",
|
||||
user=request.user.username)
|
||||
except Exception as e:
|
||||
|
@ -34,6 +34,8 @@
|
||||
src="{{ request.staticdirect('/js/extlib/jquery.js') }}"></script>
|
||||
<script type="text/javascript"
|
||||
src="{{ request.staticdirect('/js/header_dropdown.js') }}"></script>
|
||||
<script type="text/javascript"
|
||||
src="{{ request.staticdirect('/js/notifications.js') }}"></script>
|
||||
|
||||
{# For clarification, the difference between the extra_head.html template
|
||||
# and the head template hook is that the former should be used by
|
||||
@ -57,6 +59,12 @@
|
||||
<div class="header_right">
|
||||
{%- if request.user %}
|
||||
{% if request.user and request.user.status == 'active' %}
|
||||
|
||||
{% set notification_count = request.notifications.get_notification_count(request.user.id) %}
|
||||
{% if notification_count %}
|
||||
<a href="#notifications" class="notification-gem button_action" title="Notifications">
|
||||
{{ notification_count }}</a>
|
||||
{% endif %}
|
||||
<div class="button_action header_dropdown_down">▼</div>
|
||||
<div class="button_action header_dropdown_up">▲</div>
|
||||
{% elif request.user and request.user.status == "needs_email_verification" %}
|
||||
@ -109,6 +117,7 @@
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% include 'mediagoblin/fragments/header_notifications.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
@ -46,6 +46,7 @@
|
||||
{% trans %}Change your password.{% endtrans %}
|
||||
</a>
|
||||
</p>
|
||||
{{ wtforms_util.render_field_div(form.new_email) }}
|
||||
<div class="form_field_input">
|
||||
<p>{{ form.wants_comment_notification }}
|
||||
{{ wtforms_util.render_label(form.wants_comment_notification) }}</p>
|
||||
|
29
mediagoblin/templates/mediagoblin/edit/verification.txt
Normal file
29
mediagoblin/templates/mediagoblin/edit/verification.txt
Normal file
@ -0,0 +1,29 @@
|
||||
{#
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-#}
|
||||
|
||||
{% trans username=username, verification_url=verification_url|safe -%}
|
||||
Hi,
|
||||
|
||||
We wanted to verify that you are {{ username }}. If this is the case, then
|
||||
please follow the link below to verify your new email address.
|
||||
|
||||
{{ verification_url }}
|
||||
|
||||
If you are not {{ username }} or didn't request an email change, you can ignore
|
||||
this email.
|
||||
{%- endtrans %}
|
@ -0,0 +1,40 @@
|
||||
{% set notifications = request.notifications.get_notifications(request.user.id) %}
|
||||
{% if notifications %}
|
||||
<div class="header_notifications">
|
||||
<h3>{% trans %}New comments{% endtrans %}</h3>
|
||||
<ul>
|
||||
{% for notification in notifications %}
|
||||
{% set comment = notification.subject %}
|
||||
{% set comment_author = comment.get_author %}
|
||||
{% set media = comment.get_entry %}
|
||||
<li class="comment_wrapper">
|
||||
<div class="comment_author">
|
||||
<img src="{{ request.staticdirect('/images/icon_comment.png') }}" />
|
||||
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
|
||||
user=comment_author.username) }}"
|
||||
class="comment_authorlink">
|
||||
{{- comment_author.username -}}
|
||||
</a>
|
||||
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home.view_comment',
|
||||
comment=comment.id,
|
||||
user=media.get_uploader.username,
|
||||
media=media.slug_or_id) }}#comment"
|
||||
class="comment_whenlink">
|
||||
<span title='{{- comment.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
|
||||
{%- trans formatted_time=timesince(comment.created) -%}
|
||||
{{ formatted_time }} ago
|
||||
{%- endtrans -%}
|
||||
</span>
|
||||
</a>:
|
||||
</div>
|
||||
<div class="comment_content">
|
||||
{% autoescape False -%}
|
||||
{{ comment.content_html }}
|
||||
{%- endautoescape %}
|
||||
</div>
|
||||
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
@ -46,19 +46,21 @@
|
||||
{%- endblock %}
|
||||
|
||||
{% block mediagoblin_media %}
|
||||
{% if pdf_js %}
|
||||
<iframe width=640px height=480px
|
||||
src="{{ request.staticdirect('/extlib/pdf.js/web/viewer.html') }}?file={{ pdf_view }} ">
|
||||
</iframe>
|
||||
|
||||
{% else %}
|
||||
<a href="{{ pdf_view }}">
|
||||
<img id="medium"
|
||||
class="media_image"
|
||||
src="{{ medium_view }}"
|
||||
alt="{% trans media_title=media.title -%} Image for {{ media_title}}{% endtrans %}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if pdf_js %}
|
||||
<iframe width="640px" height="480px"
|
||||
src="{{ request.staticdirect('/extlib/pdf.js/web/viewer.html') }}?file={{ pdf_view }} ">
|
||||
</iframe>
|
||||
{% else %}
|
||||
<a href="{{ pdf_view }}">
|
||||
<img id="medium"
|
||||
class="media_image"
|
||||
src="{{ medium_view }}"
|
||||
alt="
|
||||
{%- trans media_title=media.title -%}
|
||||
Image for {{ media_title}}
|
||||
{%- endtrans %}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block mediagoblin_sidebar %}
|
||||
|
@ -81,6 +81,7 @@
|
||||
user= media.get_uploader.username,
|
||||
media_id=media.id) %}
|
||||
<a class="button_action" href="{{ delete_url }}">{% trans %}Delete{% endtrans %}</a>
|
||||
|
||||
{% endif %}
|
||||
{% autoescape False %}
|
||||
<p>{{ media.description_html }}</p>
|
||||
@ -94,6 +95,8 @@
|
||||
class="button_action" id="button_addcomment" title="Add a comment">
|
||||
{% trans %}Add a comment{% endtrans %}
|
||||
</a>
|
||||
{% include "mediagoblin/utils/comment-subscription.html" %}
|
||||
|
||||
{% endif %}
|
||||
{% if request.user %}
|
||||
<form action="{{ request.urlgen('mediagoblin.user_pages.media_post_comment',
|
||||
|
@ -0,0 +1,34 @@
|
||||
{#
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
{%- if request.user %}
|
||||
{% set subscription = request.notifications.get_comment_subscription(
|
||||
request.user.id, media.id) %}
|
||||
{% if not subscription or not subscription.notify %}
|
||||
<a type="submit" href="{{ request.urlgen('mediagoblin.notifications.subscribe_comments',
|
||||
user=media.get_uploader.username,
|
||||
media=media.slug)}}"
|
||||
class="button_action">Subscribe to comments
|
||||
</a>
|
||||
{% else %}
|
||||
<a type="submit" href="{{ request.urlgen('mediagoblin.notifications.silence_comments',
|
||||
user=media.get_uploader.username,
|
||||
media=media.slug)}}"
|
||||
class="button_action">Silence comments
|
||||
</a>
|
||||
{% endif %}
|
||||
{%- endif %}
|
@ -118,20 +118,15 @@ def test_register_views(test_app):
|
||||
assert path == u'/auth/verify_email/'
|
||||
parsed_get_params = urlparse.parse_qs(get_params)
|
||||
|
||||
### user should have these same parameters
|
||||
assert parsed_get_params['userid'] == [
|
||||
unicode(new_user.id)]
|
||||
assert parsed_get_params['token'] == [
|
||||
new_user.verification_key]
|
||||
|
||||
## Try verifying with bs verification key, shouldn't work
|
||||
template.clear_test_template_context()
|
||||
response = test_app.get(
|
||||
"/auth/verify_email/?userid=%s&token=total_bs" % unicode(
|
||||
new_user.id))
|
||||
"/auth/verify_email/?token=total_bs")
|
||||
response.follow()
|
||||
context = template.TEMPLATE_TEST_CONTEXT[
|
||||
'mediagoblin/user_pages/user.html']
|
||||
|
||||
# Correct redirect?
|
||||
assert urlparse.urlsplit(response.location)[2] == '/'
|
||||
|
||||
# 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(
|
||||
@ -195,35 +190,17 @@ def test_register_views(test_app):
|
||||
|
||||
path = urlparse.urlsplit(email_context['verification_url'])[2]
|
||||
get_params = urlparse.urlsplit(email_context['verification_url'])[3]
|
||||
assert path == u'/auth/forgot_password/verify/'
|
||||
parsed_get_params = urlparse.parse_qs(get_params)
|
||||
|
||||
# user should have matching parameters
|
||||
new_user = mg_globals.database.User.find_one({'username': u'happygirl'})
|
||||
assert parsed_get_params['userid'] == [unicode(new_user.id)]
|
||||
assert parsed_get_params['token'] == [new_user.fp_verification_key]
|
||||
|
||||
### The forgotten password token should be set to expire in ~ 10 days
|
||||
# A few ticks have expired so there are only 9 full days left...
|
||||
assert (new_user.fp_token_expire - datetime.datetime.now()).days == 9
|
||||
assert path == u'/auth/forgot_password/verify/'
|
||||
|
||||
## Try using a bs password-changing verification key, shouldn't work
|
||||
template.clear_test_template_context()
|
||||
response = test_app.get(
|
||||
"/auth/forgot_password/verify/?userid=%s&token=total_bs" % unicode(
|
||||
new_user.id), status=404)
|
||||
assert response.status.split()[0] == u'404' # status="404 NOT FOUND"
|
||||
"/auth/forgot_password/verify/?token=total_bs")
|
||||
response.follow()
|
||||
|
||||
## Try using an expired token to change password, shouldn't work
|
||||
template.clear_test_template_context()
|
||||
new_user = mg_globals.database.User.find_one({'username': u'happygirl'})
|
||||
real_token_expiration = new_user.fp_token_expire
|
||||
new_user.fp_token_expire = datetime.datetime.now()
|
||||
new_user.save()
|
||||
response = test_app.get("%s?%s" % (path, get_params), status=404)
|
||||
assert response.status.split()[0] == u'404' # status="404 NOT FOUND"
|
||||
new_user.fp_token_expire = real_token_expiration
|
||||
new_user.save()
|
||||
# Correct redirect?
|
||||
assert urlparse.urlsplit(response.location)[2] == '/'
|
||||
|
||||
## Verify step 1 of password-change works -- can see form to change password
|
||||
template.clear_test_template_context()
|
||||
@ -234,7 +211,6 @@ def test_register_views(test_app):
|
||||
template.clear_test_template_context()
|
||||
response = test_app.post(
|
||||
'/auth/forgot_password/verify/', {
|
||||
'userid': parsed_get_params['userid'],
|
||||
'password': 'iamveryveryhappy',
|
||||
'token': parsed_get_params['token']})
|
||||
response.follow()
|
||||
|
@ -48,7 +48,7 @@ def test_setup_celery_from_config():
|
||||
assert isinstance(fake_celery_module.CELERYD_ETA_SCHEDULER_PRECISION, float)
|
||||
assert fake_celery_module.CELERY_RESULT_PERSISTENT is True
|
||||
assert fake_celery_module.CELERY_IMPORTS == [
|
||||
'foo.bar.baz', 'this.is.an.import', 'mediagoblin.processing.task']
|
||||
'foo.bar.baz', 'this.is.an.import', 'mediagoblin.processing.task', 'mediagoblin.notifications.task']
|
||||
assert fake_celery_module.CELERY_RESULT_BACKEND == 'database'
|
||||
assert fake_celery_module.CELERY_RESULT_DBURI == (
|
||||
'sqlite:///' +
|
||||
|
@ -15,13 +15,12 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urlparse
|
||||
import pytest
|
||||
|
||||
from mediagoblin import mg_globals
|
||||
from mediagoblin.db.models import User
|
||||
from mediagoblin.tests.tools import fixture_add_user
|
||||
from mediagoblin.tools import template
|
||||
from mediagoblin import auth
|
||||
from mediagoblin.tools import template, mail
|
||||
|
||||
|
||||
class TestUserEdit(object):
|
||||
@ -142,4 +141,68 @@ class TestUserEdit(object):
|
||||
assert form.url.errors == [
|
||||
u'This address contains errors']
|
||||
|
||||
def test_email_change(self, test_app):
|
||||
self.login(test_app)
|
||||
|
||||
# Test email already in db
|
||||
template.clear_test_template_context()
|
||||
test_app.post(
|
||||
'/edit/account/', {
|
||||
'new_email': 'chris@example.com',
|
||||
'password': 'toast'})
|
||||
|
||||
# Check form errors
|
||||
context = template.TEMPLATE_TEST_CONTEXT[
|
||||
'mediagoblin/edit/edit_account.html']
|
||||
assert context['form'].new_email.errors == [
|
||||
u'Sorry, a user with that email address already exists.']
|
||||
|
||||
# Test successful email change
|
||||
template.clear_test_template_context()
|
||||
res = test_app.post(
|
||||
'/edit/account/', {
|
||||
'new_email': 'new@example.com',
|
||||
'password': 'toast'})
|
||||
res.follow()
|
||||
|
||||
# Correct redirect?
|
||||
assert urlparse.urlsplit(res.location)[2] == '/u/chris/'
|
||||
|
||||
# Make sure we get email verification and try verifying
|
||||
assert len(mail.EMAIL_TEST_INBOX) == 1
|
||||
message = mail.EMAIL_TEST_INBOX.pop()
|
||||
assert message['To'] == 'new@example.com'
|
||||
email_context = template.TEMPLATE_TEST_CONTEXT[
|
||||
'mediagoblin/edit/verification.txt']
|
||||
assert email_context['verification_url'] in \
|
||||
message.get_payload(decode=True)
|
||||
|
||||
path = urlparse.urlsplit(email_context['verification_url'])[2]
|
||||
assert path == u'/edit/verify_email/'
|
||||
|
||||
## Try verifying with bs verification key, shouldn't work
|
||||
template.clear_test_template_context()
|
||||
res = test_app.get(
|
||||
"/edit/verify_email/?token=total_bs")
|
||||
res.follow()
|
||||
|
||||
# Correct redirect?
|
||||
assert urlparse.urlsplit(res.location)[2] == '/'
|
||||
|
||||
# Email shouldn't be saved
|
||||
email_in_db = mg_globals.database.User.find_one(
|
||||
{'email': 'new@example.com'})
|
||||
email = User.query.filter_by(username='chris').first().email
|
||||
assert email_in_db is None
|
||||
assert email == 'chris@example.com'
|
||||
|
||||
# Verify email activation works
|
||||
template.clear_test_template_context()
|
||||
get_params = urlparse.urlsplit(email_context['verification_url'])[3]
|
||||
res = test_app.get('%s?%s' % (path, get_params))
|
||||
res.follow()
|
||||
|
||||
# New email saved?
|
||||
email = User.query.filter_by(username='chris').first().email
|
||||
assert email == 'new@example.com'
|
||||
# test changing the url inproperly
|
||||
|
@ -28,8 +28,10 @@ def test_user_deletes_other_comments(test_app):
|
||||
user_a = fixture_add_user(u"chris_a")
|
||||
user_b = fixture_add_user(u"chris_b")
|
||||
|
||||
media_a = fixture_media_entry(uploader=user_a.id, save=False)
|
||||
media_b = fixture_media_entry(uploader=user_b.id, save=False)
|
||||
media_a = fixture_media_entry(uploader=user_a.id, save=False,
|
||||
expunge=False, fake_upload=False)
|
||||
media_b = fixture_media_entry(uploader=user_b.id, save=False,
|
||||
expunge=False, fake_upload=False)
|
||||
Session.add(media_a)
|
||||
Session.add(media_b)
|
||||
Session.flush()
|
||||
@ -79,7 +81,7 @@ def test_user_deletes_other_comments(test_app):
|
||||
def test_media_deletes_broken_attachment(test_app):
|
||||
user_a = fixture_add_user(u"chris_a")
|
||||
|
||||
media = fixture_media_entry(uploader=user_a.id, save=False)
|
||||
media = fixture_media_entry(uploader=user_a.id, save=False, expunge=False)
|
||||
media.attachment_files.append(dict(
|
||||
name=u"some name",
|
||||
filepath=[u"does", u"not", u"exist"],
|
||||
|
151
mediagoblin/tests/test_notifications.py
Normal file
151
mediagoblin/tests/test_notifications.py
Normal file
@ -0,0 +1,151 @@
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
|
||||
import urlparse
|
||||
|
||||
from mediagoblin.tools import template, mail
|
||||
|
||||
from mediagoblin.db.models import Notification, CommentNotification, \
|
||||
CommentSubscription
|
||||
from mediagoblin.db.base import Session
|
||||
|
||||
from mediagoblin.notifications import mark_comment_notification_seen
|
||||
|
||||
from mediagoblin.tests.tools import fixture_add_comment, \
|
||||
fixture_media_entry, fixture_add_user, \
|
||||
fixture_comment_subscription
|
||||
|
||||
|
||||
class TestNotifications:
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self, test_app):
|
||||
self.test_app = test_app
|
||||
|
||||
# TODO: Possibly abstract into a decorator like:
|
||||
# @as_authenticated_user('chris')
|
||||
self.test_user = fixture_add_user()
|
||||
|
||||
self.current_user = None
|
||||
|
||||
self.login()
|
||||
|
||||
def login(self, username=u'chris', password=u'toast'):
|
||||
response = self.test_app.post(
|
||||
'/auth/login/', {
|
||||
'username': username,
|
||||
'password': password})
|
||||
|
||||
response.follow()
|
||||
|
||||
assert urlparse.urlsplit(response.location)[2] == '/'
|
||||
assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
|
||||
|
||||
ctx = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
|
||||
|
||||
assert Session.merge(ctx['request'].user).username == username
|
||||
|
||||
self.current_user = ctx['request'].user
|
||||
|
||||
def logout(self):
|
||||
self.test_app.get('/auth/logout/')
|
||||
self.current_user = None
|
||||
|
||||
@pytest.mark.parametrize('wants_email', [True, False])
|
||||
def test_comment_notification(self, wants_email):
|
||||
'''
|
||||
Test
|
||||
- if a notification is created when posting a comment on
|
||||
another users media entry.
|
||||
- that the comment data is consistent and exists.
|
||||
|
||||
'''
|
||||
user = fixture_add_user('otherperson', password='nosreprehto',
|
||||
wants_comment_notification=wants_email)
|
||||
|
||||
user_id = user.id
|
||||
|
||||
media_entry = fixture_media_entry(uploader=user.id, state=u'processed')
|
||||
|
||||
media_entry_id = media_entry.id
|
||||
|
||||
subscription = fixture_comment_subscription(media_entry)
|
||||
|
||||
subscription_id = subscription.id
|
||||
|
||||
media_uri_id = '/u/{0}/m/{1}/'.format(user.username,
|
||||
media_entry.id)
|
||||
media_uri_slug = '/u/{0}/m/{1}/'.format(user.username,
|
||||
media_entry.slug)
|
||||
|
||||
self.test_app.post(
|
||||
media_uri_id + 'comment/add/',
|
||||
{
|
||||
'comment_content': u'Test comment #42'
|
||||
}
|
||||
)
|
||||
|
||||
notifications = Notification.query.filter_by(
|
||||
user_id=user.id).all()
|
||||
|
||||
assert len(notifications) == 1
|
||||
|
||||
notification = notifications[0]
|
||||
|
||||
assert type(notification) == CommentNotification
|
||||
assert notification.seen == False
|
||||
assert notification.user_id == user.id
|
||||
assert notification.subject.get_author.id == self.test_user.id
|
||||
assert notification.subject.content == u'Test comment #42'
|
||||
|
||||
if wants_email == True:
|
||||
assert mail.EMAIL_TEST_MBOX_INBOX == [
|
||||
{'from': 'notice@mediagoblin.example.org',
|
||||
'message': 'Content-Type: text/plain; \
|
||||
charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: \
|
||||
base64\nSubject: GNU MediaGoblin - chris commented on your \
|
||||
post\nFrom: notice@mediagoblin.example.org\nTo: \
|
||||
otherperson@example.com\n\nSGkgb3RoZXJwZXJzb24sCmNocmlzIGNvbW1lbnRlZCBvbiB5b3VyIHBvc3QgKGh0dHA6Ly9sb2Nh\nbGhvc3Q6ODAvdS9vdGhlcnBlcnNvbi9tL3NvbWUtdGl0bGUvYy8xLyNjb21tZW50KSBhdCBHTlUg\nTWVkaWFHb2JsaW4KClRlc3QgY29tbWVudCAjNDIKCkdOVSBNZWRpYUdvYmxpbg==\n',
|
||||
'to': [u'otherperson@example.com']}]
|
||||
else:
|
||||
assert mail.EMAIL_TEST_MBOX_INBOX == []
|
||||
|
||||
# Save the ids temporarily because of DetachedInstanceError
|
||||
notification_id = notification.id
|
||||
comment_id = notification.subject.id
|
||||
|
||||
self.logout()
|
||||
self.login('otherperson', 'nosreprehto')
|
||||
|
||||
self.test_app.get(media_uri_slug + '/c/{0}/'.format(comment_id))
|
||||
|
||||
notification = Notification.query.filter_by(id=notification_id).first()
|
||||
|
||||
assert notification.seen == True
|
||||
|
||||
self.test_app.get(media_uri_slug + '/notifications/silence/')
|
||||
|
||||
subscription = CommentSubscription.query.filter_by(id=subscription_id)\
|
||||
.first()
|
||||
|
||||
assert subscription.notify == False
|
||||
|
||||
notifications = Notification.query.filter_by(
|
||||
user_id=user_id).all()
|
||||
|
||||
# User should not have been notified
|
||||
assert len(notifications) == 1
|
@ -15,18 +15,17 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
import pkg_resources
|
||||
import shutil
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from paste.deploy import loadapp
|
||||
from webtest import TestApp
|
||||
|
||||
from mediagoblin import mg_globals
|
||||
from mediagoblin.db.models import User, MediaEntry, Collection
|
||||
from mediagoblin.db.models import User, MediaEntry, Collection, MediaComment, \
|
||||
CommentSubscription, CommentNotification
|
||||
from mediagoblin.tools import testing
|
||||
from mediagoblin.init.config import read_mediagoblin_config
|
||||
from mediagoblin.db.base import Session
|
||||
@ -171,7 +170,7 @@ def assert_db_meets_expected(db, expected):
|
||||
|
||||
|
||||
def fixture_add_user(username=u'chris', password=u'toast',
|
||||
active_user=True):
|
||||
active_user=True, wants_comment_notification=True):
|
||||
# Reuse existing user or create a new one
|
||||
test_user = User.query.filter_by(username=username).first()
|
||||
if test_user is None:
|
||||
@ -184,6 +183,8 @@ def fixture_add_user(username=u'chris', password=u'toast',
|
||||
test_user.email_verified = True
|
||||
test_user.status = u'active'
|
||||
|
||||
test_user.wants_comment_notification = wants_comment_notification
|
||||
|
||||
test_user.save()
|
||||
|
||||
# Reload
|
||||
@ -195,19 +196,79 @@ def fixture_add_user(username=u'chris', password=u'toast',
|
||||
return test_user
|
||||
|
||||
|
||||
def fixture_comment_subscription(entry, notify=True, send_email=None):
|
||||
if send_email is None:
|
||||
uploader = User.query.filter_by(id=entry.uploader).first()
|
||||
send_email = uploader.wants_comment_notification
|
||||
|
||||
cs = CommentSubscription(
|
||||
media_entry_id=entry.id,
|
||||
user_id=entry.uploader,
|
||||
notify=notify,
|
||||
send_email=send_email)
|
||||
|
||||
cs.save()
|
||||
|
||||
cs = CommentSubscription.query.filter_by(id=cs.id).first()
|
||||
|
||||
Session.expunge(cs)
|
||||
|
||||
return cs
|
||||
|
||||
|
||||
def fixture_add_comment_notification(entry_id, subject_id, user_id,
|
||||
seen=False):
|
||||
cn = CommentNotification(user_id=user_id,
|
||||
seen=seen,
|
||||
subject_id=subject_id)
|
||||
cn.save()
|
||||
|
||||
cn = CommentNotification.query.filter_by(id=cn.id).first()
|
||||
|
||||
Session.expunge(cn)
|
||||
|
||||
return cn
|
||||
|
||||
|
||||
def fixture_media_entry(title=u"Some title", slug=None,
|
||||
uploader=None, save=True, gen_slug=True):
|
||||
uploader=None, save=True, gen_slug=True,
|
||||
state=u'unprocessed', fake_upload=True,
|
||||
expunge=True):
|
||||
"""
|
||||
Add a media entry for testing purposes.
|
||||
|
||||
Caution: if you're adding multiple entries with fake_upload=True,
|
||||
make sure you save between them... otherwise you'll hit an
|
||||
IntegrityError from multiple newly-added-MediaEntries adding
|
||||
FileKeynames at once. :)
|
||||
"""
|
||||
if uploader is None:
|
||||
uploader = fixture_add_user().id
|
||||
|
||||
entry = MediaEntry()
|
||||
entry.title = title
|
||||
entry.slug = slug
|
||||
entry.uploader = uploader or fixture_add_user().id
|
||||
entry.uploader = uploader
|
||||
entry.media_type = u'image'
|
||||
entry.state = state
|
||||
|
||||
if fake_upload:
|
||||
entry.media_files = {'thumb': ['a', 'b', 'c.jpg'],
|
||||
'medium': ['d', 'e', 'f.png'],
|
||||
'original': ['g', 'h', 'i.png']}
|
||||
entry.media_type = u'mediagoblin.media_types.image'
|
||||
|
||||
if gen_slug:
|
||||
entry.generate_slug()
|
||||
|
||||
if save:
|
||||
entry.save()
|
||||
|
||||
if expunge:
|
||||
entry = MediaEntry.query.filter_by(id=entry.id).first()
|
||||
|
||||
Session.expunge(entry)
|
||||
|
||||
return entry
|
||||
|
||||
|
||||
@ -231,3 +292,25 @@ def fixture_add_collection(name=u"My first Collection", user=None):
|
||||
|
||||
return coll
|
||||
|
||||
def fixture_add_comment(author=None, media_entry=None, comment=None):
|
||||
if author is None:
|
||||
author = fixture_add_user().id
|
||||
|
||||
if media_entry is None:
|
||||
media_entry = fixture_media_entry().id
|
||||
|
||||
if comment is None:
|
||||
comment = \
|
||||
'Auto-generated test comment by user #{0} on media #{0}'.format(
|
||||
author, media_entry)
|
||||
|
||||
comment = MediaComment(author=author,
|
||||
media_entry=media_entry,
|
||||
content=comment)
|
||||
|
||||
comment.save()
|
||||
|
||||
Session.expunge(comment)
|
||||
|
||||
return comment
|
||||
|
||||
|
@ -90,7 +90,12 @@ def send_email(from_addr, to_addrs, subject, message_body):
|
||||
if common.TESTS_ENABLED or mg_globals.app_config['email_debug_mode']:
|
||||
mhost = FakeMhost()
|
||||
elif not mg_globals.app_config['email_debug_mode']:
|
||||
mhost = smtplib.SMTP(
|
||||
if mg_globals.app_config['email_smtp_use_ssl']:
|
||||
smtp_init = smtplib.SMTP_SSL
|
||||
else:
|
||||
smtp_init = smtplib.SMTP
|
||||
|
||||
mhost = smtp_init(
|
||||
mg_globals.app_config['email_smtp_host'],
|
||||
mg_globals.app_config['email_smtp_port'])
|
||||
|
||||
|
@ -77,7 +77,7 @@ def render_http_exception(request, exc, description):
|
||||
elif stock_desc and exc.code == 404:
|
||||
return render_404(request)
|
||||
|
||||
return render_error(request, title=exc.args[0],
|
||||
return render_error(request, title='{0} {1}'.format(exc.code, exc.name),
|
||||
err_msg=description,
|
||||
status=exc.code)
|
||||
|
||||
|
@ -25,8 +25,9 @@ from mediagoblin.tools.response import render_to_response, render_404, \
|
||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
||||
from mediagoblin.tools.pagination import Pagination
|
||||
from mediagoblin.user_pages import forms as user_forms
|
||||
from mediagoblin.user_pages.lib import (send_comment_email,
|
||||
add_media_to_collection)
|
||||
from mediagoblin.user_pages.lib import add_media_to_collection
|
||||
from mediagoblin.notifications import trigger_notification, \
|
||||
add_comment_subscription, mark_comment_notification_seen
|
||||
|
||||
from mediagoblin.decorators import (uses_pagination, get_user_media_entry,
|
||||
get_media_entry_by_id,
|
||||
@ -34,6 +35,7 @@ from mediagoblin.decorators import (uses_pagination, get_user_media_entry,
|
||||
get_user_collection, get_user_collection_item, active_user_from_url)
|
||||
|
||||
from werkzeug.contrib.atom import AtomFeed
|
||||
from werkzeug.exceptions import MethodNotAllowed
|
||||
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
@ -110,6 +112,7 @@ def user_gallery(request, page, url_user=None):
|
||||
'media_entries': media_entries,
|
||||
'pagination': pagination})
|
||||
|
||||
|
||||
MEDIA_COMMENTS_PER_PAGE = 50
|
||||
|
||||
|
||||
@ -121,6 +124,9 @@ def media_home(request, media, page, **kwargs):
|
||||
"""
|
||||
comment_id = request.matchdict.get('comment', None)
|
||||
if comment_id:
|
||||
if request.user:
|
||||
mark_comment_notification_seen(comment_id, request.user)
|
||||
|
||||
pagination = Pagination(
|
||||
page, media.get_comments(
|
||||
mg_globals.app_config['comments_ascending']),
|
||||
@ -154,7 +160,8 @@ def media_post_comment(request, media):
|
||||
"""
|
||||
recieves POST from a MediaEntry() comment form, saves the comment.
|
||||
"""
|
||||
assert request.method == 'POST'
|
||||
if not request.method == 'POST':
|
||||
raise MethodNotAllowed()
|
||||
|
||||
comment = request.db.MediaComment()
|
||||
comment.media_entry = media.id
|
||||
@ -179,11 +186,9 @@ def media_post_comment(request, media):
|
||||
request, messages.SUCCESS,
|
||||
_('Your comment has been posted!'))
|
||||
|
||||
media_uploader = media.get_uploader
|
||||
#don't send email if you comment on your own post
|
||||
if (comment.author != media_uploader and
|
||||
media_uploader.wants_comment_notification):
|
||||
send_comment_email(media_uploader, comment, media, request)
|
||||
trigger_notification(comment, media, request)
|
||||
|
||||
add_comment_subscription(request.user, media)
|
||||
|
||||
return redirect_obj(request, media)
|
||||
|
||||
|
6
setup.py
6
setup.py
@ -14,7 +14,7 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import setup, find_packages
|
||||
import os
|
||||
import re
|
||||
|
||||
@ -36,7 +36,7 @@ def get_version():
|
||||
setup(
|
||||
name="mediagoblin",
|
||||
version=get_version(),
|
||||
packages=['mediagoblin'],
|
||||
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
|
||||
zip_safe=False,
|
||||
include_package_data = True,
|
||||
# scripts and dependencies
|
||||
@ -57,7 +57,7 @@ setup(
|
||||
'webtest<2',
|
||||
'ConfigObj',
|
||||
'Markdown',
|
||||
'sqlalchemy>=0.7.0',
|
||||
'sqlalchemy>=0.8.0',
|
||||
'sqlalchemy-migrate',
|
||||
'mock',
|
||||
'itsdangerous',
|
||||
|
Loading…
x
Reference in New Issue
Block a user