aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'mediagoblin/plugins')
-rw-r--r--mediagoblin/plugins/api/tools.py14
-rw-r--r--mediagoblin/plugins/api/views.py33
-rw-r--r--mediagoblin/plugins/archivalook/migrations/b10b5f822789_archivalook_plugin_initial_migration.py37
-rw-r--r--mediagoblin/plugins/archivalook/templates/archivalook/feature.html6
-rw-r--r--mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_head.html2
-rw-r--r--mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html8
-rw-r--r--mediagoblin/plugins/archivalook/templates/archivalook/root.html16
-rw-r--r--mediagoblin/plugins/archivalook/tools.py20
-rw-r--r--mediagoblin/plugins/basic_auth/README.rst2
-rw-r--r--mediagoblin/plugins/basic_auth/__init__.py8
-rw-r--r--mediagoblin/plugins/basic_auth/forms.py10
-rw-r--r--mediagoblin/plugins/basic_auth/tools.py4
-rw-r--r--mediagoblin/plugins/basic_auth/views.py38
-rw-r--r--mediagoblin/plugins/flatpagesfile/README.rst8
-rw-r--r--mediagoblin/plugins/geolocation/__init__.py6
-rw-r--r--mediagoblin/plugins/geolocation/templates/mediagoblin/plugins/geolocation/map.html18
-rw-r--r--mediagoblin/plugins/httpapiauth/__init__.py4
-rw-r--r--mediagoblin/plugins/ldap/README.rst18
-rw-r--r--mediagoblin/plugins/ldap/forms.py4
-rw-r--r--mediagoblin/plugins/ldap/tools.py4
-rw-r--r--mediagoblin/plugins/ldap/views.py12
-rw-r--r--mediagoblin/plugins/metadata_display/templates/mediagoblin/plugins/metadata_display/metadata_table.html2
-rw-r--r--mediagoblin/plugins/oauth/README.rst148
-rw-r--r--mediagoblin/plugins/oauth/__init__.py109
-rw-r--r--mediagoblin/plugins/oauth/forms.py69
-rw-r--r--mediagoblin/plugins/oauth/migrations.py158
-rw-r--r--mediagoblin/plugins/oauth/models.py192
-rw-r--r--mediagoblin/plugins/oauth/templates/oauth/authorize.html31
-rw-r--r--mediagoblin/plugins/oauth/templates/oauth/client/list.html45
-rw-r--r--mediagoblin/plugins/oauth/templates/oauth/client/register.html34
-rw-r--r--mediagoblin/plugins/oauth/tools.py114
-rw-r--r--mediagoblin/plugins/oauth/views.py253
-rw-r--r--mediagoblin/plugins/openid/README.rst12
-rw-r--r--mediagoblin/plugins/openid/__init__.py6
-rw-r--r--mediagoblin/plugins/openid/forms.py6
-rw-r--r--mediagoblin/plugins/openid/migrations/071abb33d1da_openid_plugin_initial_migration.py54
-rw-r--r--mediagoblin/plugins/openid/store.py6
-rw-r--r--mediagoblin/plugins/openid/views.py5
-rw-r--r--mediagoblin/plugins/persona/__init__.py6
-rw-r--r--mediagoblin/plugins/persona/forms.py6
-rw-r--r--mediagoblin/plugins/persona/migrations/c7d4840a5592_persona_plugin_initial_migration.py35
-rw-r--r--mediagoblin/plugins/persona/views.py4
-rw-r--r--mediagoblin/plugins/piwigo/forms.py18
-rw-r--r--mediagoblin/plugins/piwigo/tools.py2
-rw-r--r--mediagoblin/plugins/piwigo/views.py13
-rw-r--r--mediagoblin/plugins/processing_info/README.rst5
-rw-r--r--mediagoblin/plugins/processing_info/__init__.py51
-rw-r--r--mediagoblin/plugins/processing_info/templates/mediagoblin/processing_info/header_left.html (renamed from mediagoblin/plugins/oauth/templates/oauth/client/connections.html)32
-rw-r--r--mediagoblin/plugins/trim_whitespace/README.rst2
49 files changed, 366 insertions, 1324 deletions
diff --git a/mediagoblin/plugins/api/tools.py b/mediagoblin/plugins/api/tools.py
index d1b3ebb1..e406888e 100644
--- a/mediagoblin/plugins/api/tools.py
+++ b/mediagoblin/plugins/api/tools.py
@@ -18,9 +18,11 @@ import logging
import json
from functools import wraps
-from urlparse import urljoin
from werkzeug.exceptions import Forbidden
from werkzeug.wrappers import Response
+
+from six.moves.urllib.parse import urljoin
+
from mediagoblin import mg_globals
from mediagoblin.tools.pluginapi import PluginManager
from mediagoblin.storage.filestorage import BasicFileStorage
@@ -60,12 +62,12 @@ def get_entry_serializable(entry, urlgen):
to views.
'''
return {
- 'user': entry.get_uploader.username,
- 'user_id': entry.get_uploader.id,
- 'user_bio': entry.get_uploader.bio,
- 'user_bio_html': entry.get_uploader.bio_html,
+ 'user': entry.get_actor.username,
+ 'user_id': entry.get_actor.id,
+ 'user_bio': entry.get_actor.bio,
+ 'user_bio_html': entry.get_actor.bio_html,
'user_permalink': urlgen('mediagoblin.user_pages.user_home',
- user=entry.get_uploader.username,
+ user=entry.get_actor.username,
qualified=True),
'id': entry.id,
'created': entry.created.isoformat(),
diff --git a/mediagoblin/plugins/api/views.py b/mediagoblin/plugins/api/views.py
index e8af7988..fdd22ace 100644
--- a/mediagoblin/plugins/api/views.py
+++ b/mediagoblin/plugins/api/views.py
@@ -17,6 +17,8 @@
import json
import logging
+import six
+
from werkzeug.exceptions import BadRequest
from werkzeug.wrappers import Response
@@ -24,8 +26,7 @@ from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin.tools.response import json_response
from mediagoblin.decorators import require_active_login
from mediagoblin.meddleware.csrf import csrf_exempt
-from mediagoblin.media_types import \
- InvalidFileType, FileTypeNotSupported
+from mediagoblin.media_types import FileTypeNotSupported
from mediagoblin.plugins.api.tools import api_auth, get_entry_serializable
from mediagoblin.submit.lib import \
check_file_field, submit_media, get_upload_file_limits, \
@@ -51,21 +52,18 @@ def post_entry(request):
_log.debug('File field not found')
raise BadRequest()
- upload_limit, max_file_size = get_upload_file_limits(request.user)
-
callback_url = request.form.get('callback_url')
if callback_url:
- callback_url = unicode(callback_url)
+ callback_url = six.text_type(callback_url)
try:
entry = submit_media(
mg_app=request.app, user=request.user,
submitted_file=request.files['file'],
filename=request.files['file'].filename,
- title=unicode(request.form.get('title')),
- description=unicode(request.form.get('description')),
- license=unicode(request.form.get('license', '')),
- tags_string=unicode(request.form.get('tags', '')),
- upload_limit=upload_limit, max_file_size=max_file_size,
+ title=six.text_type(request.form.get('title')),
+ description=six.text_type(request.form.get('description')),
+ license=six.text_type(request.form.get('license', '')),
+ tags_string=six.text_type(request.form.get('tags', '')),
callback_url=callback_url)
return json_response(get_entry_serializable(entry, request.urlgen))
@@ -81,17 +79,8 @@ def post_entry(request):
except UserPastUploadLimit:
raise BadRequest(
_('Sorry, you have reached your upload limit.'))
-
- except Exception as e:
- '''
- This section is intended to catch exceptions raised in
- mediagoblin.media_types
- '''
- if isinstance(e, InvalidFileType) or \
- isinstance(e, FileTypeNotSupported):
- raise BadRequest(unicode(e))
- else:
- raise
+ except FileTypeNotSupported as e:
+ raise BadRequest(e)
@api_auth
@@ -103,7 +92,7 @@ def api_test(request):
# TODO: This is the *only* thing using Response() here, should that
# not simply use json_response()?
- return Response(json.dumps(user_data))
+ return Response(json.dumps(user_data, sort_keys=True))
def get_entries(request):
diff --git a/mediagoblin/plugins/archivalook/migrations/b10b5f822789_archivalook_plugin_initial_migration.py b/mediagoblin/plugins/archivalook/migrations/b10b5f822789_archivalook_plugin_initial_migration.py
new file mode 100644
index 00000000..1bedcf3c
--- /dev/null
+++ b/mediagoblin/plugins/archivalook/migrations/b10b5f822789_archivalook_plugin_initial_migration.py
@@ -0,0 +1,37 @@
+"""ArchivaLook plugin initial migration
+
+Revision ID: b10b5f822789
+Revises: 52bf0ccbedc1
+Create Date: 2016-03-12 23:37:51.551856
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'b10b5f822789'
+down_revision = '52bf0ccbedc1'
+branch_labels = ('archivalook_plugin',)
+depends_on = None
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ if op.get_bind().engine.has_table('archivalook__featured_media'):
+ # Skip; this has already been instantiated
+ # (probably via sqlalchemy-migrate)
+ return
+
+ op.create_table(
+ 'archivalook__featured_media',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('media_entry_id', sa.Integer(), nullable=False),
+ sa.Column('display_type', sa.Unicode(), nullable=False),
+ sa.Column('order', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['media_entry_id'],
+ ['core__media_entries.id']),
+ sa.PrimaryKeyConstraint('id'))
+
+
+def downgrade():
+ op.drop_table('archivalook__featured_media')
diff --git a/mediagoblin/plugins/archivalook/templates/archivalook/feature.html b/mediagoblin/plugins/archivalook/templates/archivalook/feature.html
index 6bcf792e..b19029c3 100644
--- a/mediagoblin/plugins/archivalook/templates/archivalook/feature.html
+++ b/mediagoblin/plugins/archivalook/templates/archivalook/feature.html
@@ -35,7 +35,7 @@
</a></strong>
<p>
{% trans %}
- Go to the page of the media entry you want to feature. Copy it's URL and
+ Go to the page of the media entry you want to feature. Copy its URL and
then paste it into a new line in the text box above. There should be only
one url per line. The url that you paste into the text box should be under
the header describing how prominent a feature it will be (whether Primary,
@@ -52,7 +52,7 @@
Yes. If you would prefer, you may go to the media homepage of the piece
of media you would like to feature or unfeature and look at the bar to
the side of the media entry. If the piece of media has not been featured
- yet you should see a button that says 'Feature'. Press that button and
+ yet you should see a button that says "Feature". Press that button and
the media will be featured as a Primary Feature at the top of the page.
All other featured media entries will remain as features, but will be
pushed further down the page.<br /><br />
@@ -86,7 +86,7 @@
featured?{% endtrans %}</a></strong>
<p>
{% trans %}
- When a media entry is featured, the entry's title, it's thumbnail and a
+ When a media entry is featured, the entry's title, its thumbnail and a
portion of its description will be displayed on your website's front page.
The number of characters displayed varies on the prominence of the feature.
Primary Features display the first 512 characters of their description,
diff --git a/mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_head.html b/mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_head.html
index a53694b3..d1ea0b9a 100644
--- a/mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_head.html
+++ b/mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_head.html
@@ -1,5 +1,3 @@
<link rel="stylesheet" type="text/css" href="{{ request.staticdirect('/css/audio.css') }}" />
<script type="text/javascript" src="{{ request.staticdirect(
- '/js/extlib/html5slider.js') }}"></script>
- <script type="text/javascript" src="{{ request.staticdirect(
'/js/audio.js') }}"></script>
diff --git a/mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html b/mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html
index c5b8a7d6..942a888b 100644
--- a/mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html
+++ b/mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html
@@ -21,7 +21,7 @@
{% if not media_feature %}
<a href="{{ request.urlgen(
'mediagoblin.user_pages.media_feature',
- user=media.get_uploader.username,
+ user=media.get_actor.username,
media=media.slug_or_id) }}"
class="button_action" id="button_featuremedia" title="{% trans %}
Feature Media {% endtrans %}">
@@ -29,7 +29,7 @@ Feature Media {% endtrans %}">
{% else %}
<a href="{{ request.urlgen(
'mediagoblin.user_pages.media_unfeature',
- user=media.get_uploader.username,
+ user=media.get_actor.username,
media=media.slug_or_id) }}"
class="button_action" id="button_unfeaturemedia" title="{% trans %}
Unfeature Media {% endtrans %}">
@@ -37,7 +37,7 @@ Unfeature Media {% endtrans %}">
{% if not media_feature.display_type == 'primary' %}
<a href="{{ request.urlgen(
'mediagoblin.user_pages.feature_promote',
- user=media.get_uploader.username,
+ user=media.get_actor.username,
media=media.slug_or_id) }}"
class="button_action" id="button_promotefeature" title="{% trans %}
Promote Feature {% endtrans %}">
@@ -45,7 +45,7 @@ Promote Feature {% endtrans %}">
{% endif %}{% if not media_feature.display_type == 'tertiary' %}
<a href="{{ request.urlgen(
'mediagoblin.user_pages.feature_demote',
- user=media.get_uploader.username,
+ user=media.get_actor.username,
media=media.slug_or_id) }}"
class="button_action" id="button_demotefeature" title="{% trans %}
Demote Feature {% endtrans %}">
diff --git a/mediagoblin/plugins/archivalook/templates/archivalook/root.html b/mediagoblin/plugins/archivalook/templates/archivalook/root.html
index 72fe8656..4dbd0f9e 100644
--- a/mediagoblin/plugins/archivalook/templates/archivalook/root.html
+++ b/mediagoblin/plugins/archivalook/templates/archivalook/root.html
@@ -59,17 +59,19 @@
{%- elif request.user and request.user.has_privilege('featurer') %}
<div>
<h2>{% trans %}Nothing is currently featured.{% endtrans %}</h2>
- {% trans %}If you would like to feature a
+ {% trans -%}
+ If you would like to feature a
piece of media, go to that media entry's homepage and click the button
- that says{% endtrans %} <a class="button_action">Feature</a>.
- {% trans %}You're seeing this page because you are a user capable of
+ that says <a class="button_action">Feature</a>.
+ {%- endtrans %}
+ {% trans featured_media_url=request.urlgen('manage-featured-media') -%}
+ You're seeing this page because you are a user capable of
featuring media, a regular user would see a blank page, so be sure to
have media featured as long as your instance has the 'archivalook'
plugin enabled. A more advanced tool to manage features can be found
- in the{% endtrans %}
- <a href="{{ request.urlgen(
- 'manage-featured-media') }}">
- {% trans %}feature management panel.{% endtrans %}</a>
+ in the <a href="{{ featured_media_url }}">feature management panel.</a>
+ {%- endtrans %}
+ </a>
</div>
</div>
{%- endif %}
diff --git a/mediagoblin/plugins/archivalook/tools.py b/mediagoblin/plugins/archivalook/tools.py
index 9c715c9b..ad2eee5f 100644
--- a/mediagoblin/plugins/archivalook/tools.py
+++ b/mediagoblin/plugins/archivalook/tools.py
@@ -13,7 +13,9 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from mediagoblin.db.models import MediaEntry, User
+import six
+
+from mediagoblin.db.models import MediaEntry, User, LocalUser
from mediagoblin.plugins.archivalook.models import FeaturedMedia
from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
from mediagoblin.plugins.archivalook.models import FeaturedMedia
@@ -32,11 +34,12 @@ def get_media_entry_from_uploader_slug(uploader_username, slug):
:returns media A MediaEntry object or None if no entry
matches the specifications.
"""
- uploader = User.query.filter(
- User.username == uploader_username).first()
+ uploader = LocalUser.query.filter(
+ LocalUser.username==uploader_username
+ ).first()
media = MediaEntry.query.filter(
- MediaEntry.get_uploader == uploader ).filter(
- MediaEntry.slug == slug).first()
+ MediaEntry.get_actor == uploader ).filter(
+ MediaEntry.slug == slug).first()
return media
@@ -53,7 +56,7 @@ def parse_url(url):
who uploaded the piece of media, slug is
the media entry's url slug.
"""
- url = unicode(url)
+ url = six.text_type(url)
u_end, m_start, m_end, end = (url.find('/u/') + 3,
url.find('/m/'),
url.find('/m/') + 3,
@@ -84,7 +87,7 @@ def split_featured_media_list(featured_media):
or tertiary)
"""
- featured_media = unicode(featured_media)
+ featured_media = six.text_type(featured_media)
featured_media_list = featured_media.split("\n")
display_type = 0
media_already_featured = []
@@ -138,7 +141,7 @@ def create_featured_media_textbox():
for feature in feature_list:
media_entry = feature.media_entry
output_text += u'/u/{uploader_username}/m/{media_slug}/\n'.format(
- uploader_username = media_entry.get_uploader.username,
+ uploader_username = media_entry.get_actor.username,
media_slug = media_entry.slug)
@@ -289,4 +292,3 @@ def demote_feature(media_entry):
elif target_feature.display_type == u'primary':
target_feature.display_type = u'secondary'
target_feature.save()
-
diff --git a/mediagoblin/plugins/basic_auth/README.rst b/mediagoblin/plugins/basic_auth/README.rst
index 82f247ed..87a7b16f 100644
--- a/mediagoblin/plugins/basic_auth/README.rst
+++ b/mediagoblin/plugins/basic_auth/README.rst
@@ -5,7 +5,7 @@
===================
The basic_auth plugin is enabled by default in mediagoblin.ini. This plugin
-provides basic username and password authentication for GNU Mediagoblin.
+provides basic username and password authentication for GNU MediaGoblin.
This plugin can be enabled alongside :ref:`openid-chapter` and
:ref:`persona-chapter`.
diff --git a/mediagoblin/plugins/basic_auth/__init__.py b/mediagoblin/plugins/basic_auth/__init__.py
index 64564c7f..31a4fd95 100644
--- a/mediagoblin/plugins/basic_auth/__init__.py
+++ b/mediagoblin/plugins/basic_auth/__init__.py
@@ -19,7 +19,7 @@ import os
from mediagoblin.plugins.basic_auth import forms as auth_forms
from mediagoblin.plugins.basic_auth import tools as auth_tools
from mediagoblin.auth.tools import create_basic_user
-from mediagoblin.db.models import User
+from mediagoblin.db.models import LocalUser
from mediagoblin.tools import pluginapi
from sqlalchemy import or_
from mediagoblin.tools.staticdirect import PluginStatic
@@ -56,10 +56,10 @@ def setup_plugin():
def get_user(**kwargs):
username = kwargs.pop('username', None)
if username:
- user = User.query.filter(
+ user = LocalUser.query.filter(
or_(
- User.username == username,
- User.email == username,
+ LocalUser.username == username,
+ LocalUser.email == username,
)).first()
return user
diff --git a/mediagoblin/plugins/basic_auth/forms.py b/mediagoblin/plugins/basic_auth/forms.py
index 42b84bf3..3d684e91 100644
--- a/mediagoblin/plugins/basic_auth/forms.py
+++ b/mediagoblin/plugins/basic_auth/forms.py
@@ -20,7 +20,7 @@ from mediagoblin.auth.tools import normalize_user_or_email_field
class RegistrationForm(wtforms.Form):
- username = wtforms.TextField(
+ username = wtforms.StringField(
_('Username'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field(allow_email=False)])
@@ -28,17 +28,17 @@ class RegistrationForm(wtforms.Form):
_('Password'),
[wtforms.validators.InputRequired(),
wtforms.validators.Length(min=5, max=1024)])
- email = wtforms.TextField(
+ email = wtforms.StringField(
_('Email address'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field(allow_user=False)])
class LoginForm(wtforms.Form):
- username = wtforms.TextField(
+ username = wtforms.StringField(
_('Username or Email'),
[wtforms.validators.InputRequired(),
- normalize_user_or_email_field()])
+ normalize_user_or_email_field(is_login=True)])
password = wtforms.PasswordField(
_('Password'))
stay_logged_in = wtforms.BooleanField(
@@ -47,7 +47,7 @@ class LoginForm(wtforms.Form):
class ForgotPassForm(wtforms.Form):
- username = wtforms.TextField(
+ username = wtforms.StringField(
_('Username or email'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field()])
diff --git a/mediagoblin/plugins/basic_auth/tools.py b/mediagoblin/plugins/basic_auth/tools.py
index f943bf39..13f240b2 100644
--- a/mediagoblin/plugins/basic_auth/tools.py
+++ b/mediagoblin/plugins/basic_auth/tools.py
@@ -16,6 +16,8 @@
import bcrypt
import random
+import six
+
from mediagoblin import mg_globals
from mediagoblin.tools.crypto import get_timed_signer_url
from mediagoblin.tools.mail import send_email
@@ -66,7 +68,7 @@ def bcrypt_gen_password_hash(raw_pass, extra_salt=None):
if extra_salt:
raw_pass = u"%s:%s" % (extra_salt, raw_pass)
- return unicode(
+ return six.text_type(
bcrypt.hashpw(raw_pass.encode('utf-8'), bcrypt.gensalt()))
diff --git a/mediagoblin/plugins/basic_auth/views.py b/mediagoblin/plugins/basic_auth/views.py
index 02d370f0..f398f0d2 100644
--- a/mediagoblin/plugins/basic_auth/views.py
+++ b/mediagoblin/plugins/basic_auth/views.py
@@ -16,7 +16,7 @@
from itsdangerous import BadSignature
from mediagoblin import messages
-from mediagoblin.db.models import User
+from mediagoblin.db.models import LocalUser
from mediagoblin.decorators import require_active_login
from mediagoblin.plugins.basic_auth import forms, tools
from mediagoblin.tools.crypto import get_timed_signer_url
@@ -48,7 +48,7 @@ def forgot_password(request):
found_by_email = '@' in fp_form.username.data
if found_by_email:
- user = User.query.filter_by(
+ user = LocalUser.query.filter_by(
email=fp_form.username.data).first()
# Don't reveal success in case the lookup happened by email address.
success_message = _("If that email address (case sensitive!) is "
@@ -56,24 +56,27 @@ def forgot_password(request):
"instructions on how to change your password.")
else: # found by username
- user = User.query.filter_by(
+ user = LocalUser.query.filter_by(
username=fp_form.username.data).first()
if user is None:
- messages.add_message(request,
- messages.WARNING,
- _("Couldn't find someone with that username."))
- return redirect(request, 'mediagoblin.auth.forgot_password')
+ messages.add_message(
+ request,
+ messages.WARNING,
+ _("Couldn't find someone with that username."))
+ return redirect(request,
+ 'mediagoblin.plugins.basic_auth.forgot_password')
success_message = _("An email has been sent with instructions "
"on how to change your password.")
if user and user.has_privilege(u'active') is False:
# Don't send reminder because user is inactive or has no verified email
- messages.add_message(request,
+ messages.add_message(
+ request,
messages.WARNING,
- _("Could not send password recovery email as your username is in"
- "active or your account's email address has not been verified."))
+ _("Could not send password recovery email as your username is "
+ "inactive or your account's email address has not been verified."))
return redirect(request, 'mediagoblin.user_pages.user_home',
user=user.username)
@@ -114,7 +117,7 @@ def verify_forgot_password(request):
'index')
# check if it's a valid user id
- user = User.query.filter_by(id=int(token)).first()
+ user = LocalUser.query.filter_by(id=int(token)).first()
# no user in db
if not user:
@@ -153,15 +156,17 @@ def verify_forgot_password(request):
#
# if not user.email_verified:
# messages.add_message(
- # request, messages.ERROR,
+ # 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 account.'))
+ request,
+ messages.ERROR,
+ _("You are no longer an active user. Please contact the system "
+ "admin to reactivate your account."))
return redirect(
request, 'index')
@@ -211,7 +216,8 @@ def change_pass(request):
user.save()
messages.add_message(
- request, messages.SUCCESS,
+ request,
+ messages.SUCCESS,
_('Your password was changed successfully'))
return redirect(request, 'mediagoblin.edit.account')
diff --git a/mediagoblin/plugins/flatpagesfile/README.rst b/mediagoblin/plugins/flatpagesfile/README.rst
index 59cd6217..0354a46c 100644
--- a/mediagoblin/plugins/flatpagesfile/README.rst
+++ b/mediagoblin/plugins/flatpagesfile/README.rst
@@ -57,7 +57,7 @@ Examples: ``flatpages-about``, ``about-view``, ``contact-view``, ...
The value has two parts separated by commas:
-1. **route path**: This is the url that this route matches.
+1. **route path**: This is the URL that this route matches.
Examples: ``/about``, ``/contact``, ``/pages/about``, ...
@@ -74,7 +74,7 @@ The value has two parts separated by commas:
For example: ``'/siteadmin/{adminname:\w+}'``
-2. **template**: The template to use for this url. The template is in
+2. **template**: The template to use for this URL. The template is in
the flatpagesfile template directory, so you just need to specify
the file name.
@@ -139,10 +139,10 @@ template::
Recipes
=======
-Url variables
+URL variables
-------------
-You can handle urls like ``/about/{name}`` and access the name that's
+You can handle URLs like ``/about/{name}`` and access the name that's
passed in in the template.
Sample route::
diff --git a/mediagoblin/plugins/geolocation/__init__.py b/mediagoblin/plugins/geolocation/__init__.py
index 5d14590e..06aab68e 100644
--- a/mediagoblin/plugins/geolocation/__init__.py
+++ b/mediagoblin/plugins/geolocation/__init__.py
@@ -21,13 +21,13 @@ PLUGIN_DIR = os.path.dirname(__file__)
def setup_plugin():
config = pluginapi.get_config('mediagoblin.plugins.geolocation')
-
+
# Register the template path.
pluginapi.register_template_path(os.path.join(PLUGIN_DIR, 'templates'))
pluginapi.register_template_hooks(
- {"image_sideinfo": "mediagoblin/plugins/geolocation/map.html",
- "image_head": "mediagoblin/plugins/geolocation/map_js_head.html"})
+ {"location_info": "mediagoblin/plugins/geolocation/map.html",
+ "location_head": "mediagoblin/plugins/geolocation/map_js_head.html"})
hooks = {
diff --git a/mediagoblin/plugins/geolocation/templates/mediagoblin/plugins/geolocation/map.html b/mediagoblin/plugins/geolocation/templates/mediagoblin/plugins/geolocation/map.html
index 70f837ff..be608ac0 100644
--- a/mediagoblin/plugins/geolocation/templates/mediagoblin/plugins/geolocation/map.html
+++ b/mediagoblin/plugins/geolocation/templates/mediagoblin/plugins/geolocation/map.html
@@ -17,14 +17,13 @@
#}
{% block geolocation_map %}
- {% if media.media_data.gps_latitude is defined
- and media.media_data.gps_latitude
- and media.media_data.gps_longitude is defined
- and media.media_data.gps_longitude %}
- <h3>{% trans %}Location{% endtrans %}</h3>
+ {% if model.location
+ and model.get_location.position
+ and "latitude" in model.get_location.position
+ and "longitude" in model.get_location.position %}
<div>
- {%- set lon = media.media_data.gps_longitude %}
- {%- set lat = media.media_data.gps_latitude %}
+ {%- set lon = model.get_location.position.longitude %}
+ {%- set lat = model.get_location.position.latitude %}
{%- set osm_url = "http://openstreetmap.org/?mlat={lat}&mlon={lon}".format(lat=lat, lon=lon) %}
<div id="tile-map" style="width: 100%; height: 196px;">
<input type="hidden" id="gps-longitude"
@@ -44,8 +43,9 @@
href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>
contributors
</li><li>Imaging &copy;<a
- href="http://mapquest.com">MapQuest</a></li><li>Maps powered by
- <a href="http://leafletjs.com/"> Leaflet</a></li></ul>
+ href="https://www.openstreetmap.org">OpenStreetMap
+ contributors</a></li><li>Maps powered by
+ <a href="http://leafletjs.com/">Leaflet</a></li></ul>
</div>
<p>
<small>
diff --git a/mediagoblin/plugins/httpapiauth/__init__.py b/mediagoblin/plugins/httpapiauth/__init__.py
index 2b2d593c..d7180463 100644
--- a/mediagoblin/plugins/httpapiauth/__init__.py
+++ b/mediagoblin/plugins/httpapiauth/__init__.py
@@ -16,6 +16,8 @@
import logging
+import six
+
from werkzeug.exceptions import Unauthorized
from mediagoblin.auth.tools import check_login_simple
@@ -40,7 +42,7 @@ class HTTPAuth(Auth):
if not request.authorization:
return False
- user = check_login_simple(unicode(request.authorization['username']),
+ user = check_login_simple(six.text_type(request.authorization['username']),
request.authorization['password'])
if user:
diff --git a/mediagoblin/plugins/ldap/README.rst b/mediagoblin/plugins/ldap/README.rst
index ea9a34b3..049b5c4d 100644
--- a/mediagoblin/plugins/ldap/README.rst
+++ b/mediagoblin/plugins/ldap/README.rst
@@ -14,16 +14,16 @@
.. _ldap-plugin:
=============
- ldap plugin
+ LDAP plugin
=============
.. Warning::
This plugin is not compatible with the other authentication plugins.
-This plugin allow your GNU Mediagoblin instance to authenticate against an
+This plugin allow your GNU MediaGoblin instance to authenticate against an
LDAP server.
-Set up the ldap plugin
+Set up the LDAP plugin
======================
1. Install the ``python-ldap`` package.
@@ -32,13 +32,13 @@ Set up the ldap plugin
[[mediagoblin.plugins.ldap]]
-Configuring the ldap plugin
+Configuring the LDAP plugin
===========================
-This plugin allows you to use multiple ldap servers for authentication.
+This plugin allows you to use multiple LDAP servers for authentication.
In order to configure a server, add the following to you MediaGoblin .ini file
-under the ldap plugin::
+under the LDAP plugin::
[[mediagoblin.plugins.ldap]]
[[[server1]]]
@@ -50,15 +50,15 @@ under the ldap plugin::
Make any necessary changes to the above to work with your sever. Make sure
``{username}`` is where the username should be in LDAP_USER_DN_TEMPLATE.
-If you would like to fetch the users email from the ldap server upon account
+If you would like to fetch the users email from the LDAP server upon account
registration, add ``LDAP_SEARCH_BASE = 'ou=users,dc=testathon,dc=net'`` and
``EMAIL_SEARCH_FIELD = 'mail'`` under you server configuration in your
MediaGoblin .ini file.
.. Warning::
By default, this plugin provides no encryption when communicating with the
- ldap servers. If you would like to use an SSL connection, change
- LDAP_SERVER_URI to use ``ldaps://`` and whichever port you use. Default ldap
+ LDAP servers. If you would like to use an SSL connection, change
+ LDAP_SERVER_URI to use ``ldaps://`` and whichever port you use. Default LDAP
port for SSL connections is 636. If you would like to use a TLS connection,
add ``LDAP_START_TLS = 'true'`` under your server configuration in your
MediaGoblin .ini file.
diff --git a/mediagoblin/plugins/ldap/forms.py b/mediagoblin/plugins/ldap/forms.py
index 1f1439ab..3d966e03 100644
--- a/mediagoblin/plugins/ldap/forms.py
+++ b/mediagoblin/plugins/ldap/forms.py
@@ -24,14 +24,14 @@ class RegisterForm(wtforms.Form):
'',
[wtforms.validators.InputRequired(),
normalize_user_or_email_field(allow_email=False)])
- email = wtforms.TextField(
+ email = wtforms.StringField(
_('Email address'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field(allow_user=False)])
class LoginForm(wtforms.Form):
- username = wtforms.TextField(
+ username = wtforms.StringField(
_('Username'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field()])
diff --git a/mediagoblin/plugins/ldap/tools.py b/mediagoblin/plugins/ldap/tools.py
index 1c436792..2be2dcd7 100644
--- a/mediagoblin/plugins/ldap/tools.py
+++ b/mediagoblin/plugins/ldap/tools.py
@@ -16,6 +16,8 @@
import ldap
import logging
+import six
+
from mediagoblin.tools import pluginapi
_log = logging.getLogger(__name__)
@@ -47,7 +49,7 @@ class LDAP(object):
return email
def login(self, username, password):
- for k, v in self.ldap_settings.iteritems():
+ for k, v in six.iteritems(self.ldap_settings):
try:
self._connect(v)
user_dn = v['LDAP_USER_DN_TEMPLATE'].format(username=username)
diff --git a/mediagoblin/plugins/ldap/views.py b/mediagoblin/plugins/ldap/views.py
index aef1bf56..e10c7f60 100644
--- a/mediagoblin/plugins/ldap/views.py
+++ b/mediagoblin/plugins/ldap/views.py
@@ -13,9 +13,12 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import six
+
from mediagoblin import mg_globals, messages
from mediagoblin.auth.tools import register_user
-from mediagoblin.db.models import User
+from mediagoblin.db.models import User, LocalUser
from mediagoblin.decorators import allow_registration, auth_enabled
from mediagoblin.plugins.ldap import forms
from mediagoblin.plugins.ldap.tools import LDAP
@@ -35,12 +38,13 @@ def login(request):
login_form.password.data)
if username:
- user = User.query.filter_by(
- username=username).first()
+ user = LocalUser.query.filter(
+ LocalUser.username==username
+ ).first()
if user:
# set up login in session
- request.session['user_id'] = unicode(user.id)
+ request.session['user_id'] = six.text_type(user.id)
request.session.save()
if request.form.get('next'):
diff --git a/mediagoblin/plugins/metadata_display/templates/mediagoblin/plugins/metadata_display/metadata_table.html b/mediagoblin/plugins/metadata_display/templates/mediagoblin/plugins/metadata_display/metadata_table.html
index 3a9d872c..15ea1536 100644
--- a/mediagoblin/plugins/metadata_display/templates/mediagoblin/plugins/metadata_display/metadata_table.html
+++ b/mediagoblin/plugins/metadata_display/templates/mediagoblin/plugins/metadata_display/metadata_table.html
@@ -35,7 +35,7 @@
{% endif %}
{% if request.user and request.user.has_privilege('admin') %}
<a href="{{ request.urlgen('mediagoblin.edit.metadata',
- user=media.get_uploader.username,
+ user=media.get_actor.username,
media_id=media.id) }}">
{% trans %}Edit Metadata{% endtrans %}</a>
{% endif %}
diff --git a/mediagoblin/plugins/oauth/README.rst b/mediagoblin/plugins/oauth/README.rst
deleted file mode 100644
index 753b180f..00000000
--- a/mediagoblin/plugins/oauth/README.rst
+++ /dev/null
@@ -1,148 +0,0 @@
-==============
- OAuth plugin
-==============
-
-.. warning::
- In its current state. This plugin has received no security audit.
- Development has been entirely focused on Making It Work(TM). Use this
- plugin with caution.
-
- Additionally, this and the API may break... consider it pre-alpha.
- There's also a known issue that the OAuth client doesn't do
- refresh tokens so this might result in issues for users.
-
-The OAuth plugin enables third party web applications to authenticate as one or
-more GNU MediaGoblin users in a safe way in order retrieve, create and update
-content stored on the GNU MediaGoblin instance.
-
-The OAuth plugin is based on the `oauth v2.25 draft`_ and is pointing by using
-the ``oauthlib.oauth2.draft25.WebApplicationClient`` from oauthlib_ to a
-mediagoblin instance and building the OAuth 2 provider logic around the client.
-
-There are surely some aspects of the OAuth v2.25 draft that haven't made it
-into this plugin due to the technique used to develop it.
-
-.. _`oauth v2.25 draft`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25
-.. _oauthlib: http://pypi.python.org/pypi/oauthlib
-
-
-Set up the OAuth plugin
-=======================
-
-1. Add the following to your MediaGoblin .ini file in the ``[plugins]`` section::
-
- [[mediagoblin.plugins.oauth]]
-
-2. Run::
-
- gmg dbupdate
-
- in order to create and apply migrations to any database tables that the
- plugin requires.
-
-.. note::
- This only enables the OAuth plugin. To be able to let clients fetch data
- from the MediaGoblin instance you should also enable the API plugin or some
- other plugin that supports authenticating with OAuth credentials.
-
-
-Authenticate against GNU MediaGoblin
-====================================
-
-.. note::
- As mentioned in `capabilities`_ GNU MediaGoblin currently only supports the
- `Authorization Code Grant`_ procedure for obtaining an OAuth access token.
-
-Authorization Code Grant
-------------------------
-
-.. note::
- As mentioned in `incapabilities`_ GNU MediaGoblin currently does not
- support `client registration`_
-
-The `authorization code grant`_ works in the following way:
-
-`Definitions`
-
- Authorization server
- The GNU MediaGoblin instance
- Resource server
- Also the GNU MediaGoblin instance ;)
- Client
- The web application intended to use the data
- Redirect uri
- An URI pointing to a page controlled by the *client*
- Resource owner
- The GNU MediaGoblin user who's resources the client requests access to
- User agent
- Commonly the GNU MediaGoblin user's web browser
- Authorization code
- An intermediate token that is exchanged for an *access token*
- Access token
- A secret token that the *client* uses to authenticate itself agains the
- *resource server* as a specific *resource owner*.
-
-
-Brief description of the procedure
-++++++++++++++++++++++++++++++++++
-
-1. The *client* requests an *authorization code* from the *authorization
- server* by redirecting the *user agent* to the `Authorization Endpoint`_.
- Which parameters should be included in the redirect are covered later in
- this document.
-2. The *authorization server* authenticates the *resource owner* and redirects
- the *user agent* back to the *redirect uri* (covered later in this
- document).
-3. The *client* receives the request from the *user agent*, attached is the
- *authorization code*.
-4. The *client* requests an *access token* from the *authorization server*
-5. \?\?\?\?\?
-6. Profit!
-
-
-Detailed description of the procedure
-+++++++++++++++++++++++++++++++++++++
-
-TBD, in the meantime here is a proof-of-concept GNU MediaGoblin client:
-
-https://github.com/jwandborg/omgmg/
-
-and here are some detailed descriptions from other OAuth 2
-providers:
-
-- https://developers.google.com/accounts/docs/OAuth2WebServer
-- https://developers.facebook.com/docs/authentication/server-side/
-
-and if you're unsure about anything, there's the `OAuth v2.25 draft
-<http://tools.ietf.org/html/draft-ietf-oauth-v2-25>`_, the `OAuth plugin
-source code
-<http://gitorious.org/mediagoblin/mediagoblin/trees/master/mediagoblin/plugins/oauth>`_
-and the `#mediagoblin IRC channel <http://mediagoblin.org/pages/join.html#irc>`_.
-
-
-Capabilities
-============
-
-- `Authorization endpoint`_ - Located at ``/oauth/authorize``
-- `Token endpoint`_ - Located at ``/oauth/access_token``
-- `Authorization Code Grant`_
-- `Client Registration`_
-
-.. _`Authorization endpoint`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.1
-.. _`Token endpoint`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.2
-.. _`Authorization Code Grant`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-4.1
-.. _`Client Registration`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-2
-
-Incapabilities
-==============
-
-- Only `bearer tokens`_ are issued.
-- `Implicit Grant`_
-- `Force TLS for token endpoint`_ - This one is up the the siteadmin
-- Authorization `scope`_ and `state`
-- ...
-
-.. _`bearer tokens`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-08
-.. _`scope`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.3
-.. _`Implicit Grant`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-4.2
-.. _`Force TLS for token endpoint`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.2
diff --git a/mediagoblin/plugins/oauth/__init__.py b/mediagoblin/plugins/oauth/__init__.py
deleted file mode 100644
index 82c1f380..00000000
--- a/mediagoblin/plugins/oauth/__init__.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# 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 os
-import logging
-
-from mediagoblin.tools import pluginapi
-from mediagoblin.plugins.oauth.models import OAuthToken, OAuthClient, \
- OAuthUserClient
-from mediagoblin.plugins.api.tools import Auth
-
-_log = logging.getLogger(__name__)
-
-PLUGIN_DIR = os.path.dirname(__file__)
-
-
-def setup_plugin():
- config = pluginapi.get_config('mediagoblin.plugins.oauth')
-
- _log.info('Setting up OAuth...')
- _log.debug('OAuth config: {0}'.format(config))
-
- routes = [
- ('mediagoblin.plugins.oauth.authorize',
- '/oauth-2/authorize',
- 'mediagoblin.plugins.oauth.views:authorize'),
- ('mediagoblin.plugins.oauth.authorize_client',
- '/oauth-2/client/authorize',
- 'mediagoblin.plugins.oauth.views:authorize_client'),
- ('mediagoblin.plugins.oauth.access_token',
- '/oauth-2/access_token',
- 'mediagoblin.plugins.oauth.views:access_token'),
- ('mediagoblin.plugins.oauth.list_connections',
- '/oauth-2/client/connections',
- 'mediagoblin.plugins.oauth.views:list_connections'),
- ('mediagoblin.plugins.oauth.register_client',
- '/oauth-2/client/register',
- 'mediagoblin.plugins.oauth.views:register_client'),
- ('mediagoblin.plugins.oauth.list_clients',
- '/oauth-2/client/list',
- 'mediagoblin.plugins.oauth.views:list_clients')]
-
- pluginapi.register_routes(routes)
- pluginapi.register_template_path(os.path.join(PLUGIN_DIR, 'templates'))
-
-
-class OAuthAuth(Auth):
- def trigger(self, request):
- if 'access_token' in request.GET:
- return True
-
- return False
-
- def __call__(self, request, *args, **kw):
- self.errors = []
- # TODO: Add suport for client credentials authorization
- client_id = request.GET.get('client_id') # TODO: Not used
- client_secret = request.GET.get('client_secret') # TODO: Not used
- access_token = request.GET.get('access_token')
-
- _log.debug('Authorizing request {0}'.format(request.url))
-
- if access_token:
- token = OAuthToken.query.filter(OAuthToken.token == access_token)\
- .first()
-
- if not token:
- self.errors.append('Invalid access token')
- return False
-
- _log.debug('Access token: {0}'.format(token))
- _log.debug('Client: {0}'.format(token.client))
-
- relation = OAuthUserClient.query.filter(
- (OAuthUserClient.user == token.user)
- & (OAuthUserClient.client == token.client)
- & (OAuthUserClient.state == u'approved')).first()
-
- _log.debug('Relation: {0}'.format(relation))
-
- if not relation:
- self.errors.append(
- u'Client has not been approved by the resource owner')
- return False
-
- request.user = token.user
- return True
-
- self.errors.append(u'No access_token specified')
-
- return False
-
-hooks = {
- 'setup': setup_plugin,
- 'auth': OAuthAuth()
- }
diff --git a/mediagoblin/plugins/oauth/forms.py b/mediagoblin/plugins/oauth/forms.py
deleted file mode 100644
index ddf4d390..00000000
--- a/mediagoblin/plugins/oauth/forms.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# 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 wtforms
-
-from urlparse import urlparse
-
-from mediagoblin.tools.extlib.wtf_html5 import URLField
-from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
-
-
-class AuthorizationForm(wtforms.Form):
- client_id = wtforms.HiddenField(u'',
- validators=[wtforms.validators.InputRequired()])
- next = wtforms.HiddenField(u'', validators=[wtforms.validators.InputRequired()])
- allow = wtforms.SubmitField(_(u'Allow'))
- deny = wtforms.SubmitField(_(u'Deny'))
-
-
-class ClientRegistrationForm(wtforms.Form):
- name = wtforms.TextField(_('Name'), [wtforms.validators.InputRequired()],
- description=_('The name of the OAuth client'))
- description = wtforms.TextAreaField(_('Description'),
- [wtforms.validators.Length(min=0, max=500)],
- description=_('''This will be visible to users allowing your
- application to authenticate as them.'''))
- type = wtforms.SelectField(_('Type'),
- [wtforms.validators.InputRequired()],
- choices=[
- ('confidential', 'Confidential'),
- ('public', 'Public')],
- description=_('''<strong>Confidential</strong> - The client can
- make requests to the GNU MediaGoblin instance that can not be
- intercepted by the user agent (e.g. server-side client).<br />
- <strong>Public</strong> - The client can't make confidential
- requests to the GNU MediaGoblin instance (e.g. client-side
- JavaScript client).'''))
-
- redirect_uri = URLField(_('Redirect URI'),
- [wtforms.validators.Optional(), wtforms.validators.URL()],
- description=_('''The redirect URI for the applications, this field
- is <strong>required</strong> for public clients.'''))
-
- def __init__(self, *args, **kw):
- wtforms.Form.__init__(self, *args, **kw)
-
- def validate(self):
- if not wtforms.Form.validate(self):
- return False
-
- if self.type.data == 'public' and not self.redirect_uri.data:
- self.redirect_uri.errors.append(
- _('This field is required for public clients'))
- return False
-
- return True
diff --git a/mediagoblin/plugins/oauth/migrations.py b/mediagoblin/plugins/oauth/migrations.py
deleted file mode 100644
index d7b89da3..00000000
--- a/mediagoblin/plugins/oauth/migrations.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# 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 datetime import datetime, timedelta
-from sqlalchemy import (MetaData, Table, Column,
- Integer, Unicode, Enum, DateTime, ForeignKey)
-from sqlalchemy.ext.declarative import declarative_base
-
-from mediagoblin.db.migration_tools import RegisterMigration
-from mediagoblin.db.models import User
-
-
-MIGRATIONS = {}
-
-
-class OAuthClient_v0(declarative_base()):
- __tablename__ = 'oauth__client'
-
- id = Column(Integer, primary_key=True)
- created = Column(DateTime, nullable=False,
- default=datetime.now)
-
- name = Column(Unicode)
- description = Column(Unicode)
-
- identifier = Column(Unicode, unique=True, index=True)
- secret = Column(Unicode, index=True)
-
- owner_id = Column(Integer, ForeignKey(User.id))
- redirect_uri = Column(Unicode)
-
- type = Column(Enum(
- u'confidential',
- u'public',
- name=u'oauth__client_type'))
-
-
-class OAuthUserClient_v0(declarative_base()):
- __tablename__ = 'oauth__user_client'
- id = Column(Integer, primary_key=True)
-
- user_id = Column(Integer, ForeignKey(User.id))
- client_id = Column(Integer, ForeignKey(OAuthClient_v0.id))
-
- state = Column(Enum(
- u'approved',
- u'rejected',
- name=u'oauth__relation_state'))
-
-
-class OAuthToken_v0(declarative_base()):
- __tablename__ = 'oauth__tokens'
-
- id = Column(Integer, primary_key=True)
- created = Column(DateTime, nullable=False,
- default=datetime.now)
- expires = Column(DateTime, nullable=False,
- default=lambda: datetime.now() + timedelta(days=30))
- token = Column(Unicode, index=True)
- refresh_token = Column(Unicode, index=True)
-
- user_id = Column(Integer, ForeignKey(User.id), nullable=False,
- index=True)
-
- client_id = Column(Integer, ForeignKey(OAuthClient_v0.id), nullable=False)
-
- def __repr__(self):
- return '<{0} #{1} expires {2} [{3}, {4}]>'.format(
- self.__class__.__name__,
- self.id,
- self.expires.isoformat(),
- self.user,
- self.client)
-
-
-class OAuthCode_v0(declarative_base()):
- __tablename__ = 'oauth__codes'
-
- id = Column(Integer, primary_key=True)
- created = Column(DateTime, nullable=False,
- default=datetime.now)
- expires = Column(DateTime, nullable=False,
- default=lambda: datetime.now() + timedelta(minutes=5))
- code = Column(Unicode, index=True)
-
- user_id = Column(Integer, ForeignKey(User.id), nullable=False,
- index=True)
-
- client_id = Column(Integer, ForeignKey(OAuthClient_v0.id), nullable=False)
-
-
-class OAuthRefreshToken_v0(declarative_base()):
- __tablename__ = 'oauth__refresh_tokens'
-
- id = Column(Integer, primary_key=True)
- created = Column(DateTime, nullable=False,
- default=datetime.now)
-
- token = Column(Unicode, index=True)
-
- user_id = Column(Integer, ForeignKey(User.id), nullable=False)
-
- # XXX: Is it OK to use OAuthClient_v0.id in this way?
- client_id = Column(Integer, ForeignKey(OAuthClient_v0.id), nullable=False)
-
-
-@RegisterMigration(1, MIGRATIONS)
-def remove_and_replace_token_and_code(db):
- metadata = MetaData(bind=db.bind)
-
- token_table = Table('oauth__tokens', metadata, autoload=True,
- autoload_with=db.bind)
-
- token_table.drop()
-
- code_table = Table('oauth__codes', metadata, autoload=True,
- autoload_with=db.bind)
-
- code_table.drop()
-
- OAuthClient_v0.__table__.create(db.bind)
- OAuthUserClient_v0.__table__.create(db.bind)
- OAuthToken_v0.__table__.create(db.bind)
- OAuthCode_v0.__table__.create(db.bind)
-
- db.commit()
-
-
-@RegisterMigration(2, MIGRATIONS)
-def remove_refresh_token_field(db):
- metadata = MetaData(bind=db.bind)
-
- token_table = Table('oauth__tokens', metadata, autoload=True,
- autoload_with=db.bind)
-
- refresh_token = token_table.columns['refresh_token']
-
- refresh_token.drop()
- db.commit()
-
-@RegisterMigration(3, MIGRATIONS)
-def create_refresh_token_table(db):
- OAuthRefreshToken_v0.__table__.create(db.bind)
-
- db.commit()
diff --git a/mediagoblin/plugins/oauth/models.py b/mediagoblin/plugins/oauth/models.py
deleted file mode 100644
index 439424d3..00000000
--- a/mediagoblin/plugins/oauth/models.py
+++ /dev/null
@@ -1,192 +0,0 @@
-# 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 datetime import datetime, timedelta
-
-
-from sqlalchemy import (
- Column, Unicode, Integer, DateTime, ForeignKey, Enum)
-from sqlalchemy.orm import relationship, backref
-from mediagoblin.db.base import Base
-from mediagoblin.db.models import User
-from mediagoblin.plugins.oauth.tools import generate_identifier, \
- generate_secret, generate_token, generate_code, generate_refresh_token
-
-# Don't remove this, I *think* it applies sqlalchemy-migrate functionality onto
-# the models.
-from migrate import changeset
-
-
-class OAuthClient(Base):
- __tablename__ = 'oauth__client'
-
- id = Column(Integer, primary_key=True)
- created = Column(DateTime, nullable=False,
- default=datetime.now)
-
- name = Column(Unicode)
- description = Column(Unicode)
-
- identifier = Column(Unicode, unique=True, index=True,
- default=generate_identifier)
- secret = Column(Unicode, index=True, default=generate_secret)
-
- owner_id = Column(Integer, ForeignKey(User.id))
- owner = relationship(
- User,
- backref=backref('registered_clients', cascade='all, delete-orphan'))
-
- redirect_uri = Column(Unicode)
-
- type = Column(Enum(
- u'confidential',
- u'public',
- name=u'oauth__client_type'))
-
- def update_secret(self):
- self.secret = generate_secret()
-
- def __repr__(self):
- return '<{0} {1}:{2} ({3})>'.format(
- self.__class__.__name__,
- self.id,
- self.name.encode('ascii', 'replace'),
- self.owner.username.encode('ascii', 'replace'))
-
-
-class OAuthUserClient(Base):
- __tablename__ = 'oauth__user_client'
- id = Column(Integer, primary_key=True)
-
- user_id = Column(Integer, ForeignKey(User.id))
- user = relationship(
- User,
- backref=backref('oauth_client_relations',
- cascade='all, delete-orphan'))
-
- client_id = Column(Integer, ForeignKey(OAuthClient.id))
- client = relationship(
- OAuthClient,
- backref=backref('oauth_user_relations', cascade='all, delete-orphan'))
-
- state = Column(Enum(
- u'approved',
- u'rejected',
- name=u'oauth__relation_state'))
-
- def __repr__(self):
- return '<{0} #{1} {2} [{3}, {4}]>'.format(
- self.__class__.__name__,
- self.id,
- self.state.encode('ascii', 'replace'),
- self.user,
- self.client)
-
-
-class OAuthToken(Base):
- __tablename__ = 'oauth__tokens'
-
- id = Column(Integer, primary_key=True)
- created = Column(DateTime, nullable=False,
- default=datetime.now)
- expires = Column(DateTime, nullable=False,
- default=lambda: datetime.now() + timedelta(days=30))
- token = Column(Unicode, index=True, default=generate_token)
-
- user_id = Column(Integer, ForeignKey(User.id), nullable=False,
- index=True)
- user = relationship(
- User,
- backref=backref('oauth_tokens', cascade='all, delete-orphan'))
-
- client_id = Column(Integer, ForeignKey(OAuthClient.id), nullable=False)
- client = relationship(
- OAuthClient,
- backref=backref('oauth_tokens', cascade='all, delete-orphan'))
-
- def __repr__(self):
- return '<{0} #{1} expires {2} [{3}, {4}]>'.format(
- self.__class__.__name__,
- self.id,
- self.expires.isoformat(),
- self.user,
- self.client)
-
-class OAuthRefreshToken(Base):
- __tablename__ = 'oauth__refresh_tokens'
-
- id = Column(Integer, primary_key=True)
- created = Column(DateTime, nullable=False,
- default=datetime.now)
-
- token = Column(Unicode, index=True,
- default=generate_refresh_token)
-
- user_id = Column(Integer, ForeignKey(User.id), nullable=False)
-
- user = relationship(User, backref=backref('oauth_refresh_tokens',
- cascade='all, delete-orphan'))
-
- client_id = Column(Integer, ForeignKey(OAuthClient.id), nullable=False)
- client = relationship(OAuthClient,
- backref=backref(
- 'oauth_refresh_tokens',
- cascade='all, delete-orphan'))
-
- def __repr__(self):
- return '<{0} #{1} [{3}, {4}]>'.format(
- self.__class__.__name__,
- self.id,
- self.user,
- self.client)
-
-
-class OAuthCode(Base):
- __tablename__ = 'oauth__codes'
-
- id = Column(Integer, primary_key=True)
- created = Column(DateTime, nullable=False,
- default=datetime.now)
- expires = Column(DateTime, nullable=False,
- default=lambda: datetime.now() + timedelta(minutes=5))
- code = Column(Unicode, index=True, default=generate_code)
-
- user_id = Column(Integer, ForeignKey(User.id), nullable=False,
- index=True)
- user = relationship(User, backref=backref('oauth_codes',
- cascade='all, delete-orphan'))
-
- client_id = Column(Integer, ForeignKey(OAuthClient.id), nullable=False)
- client = relationship(OAuthClient, backref=backref(
- 'oauth_codes',
- cascade='all, delete-orphan'))
-
- def __repr__(self):
- return '<{0} #{1} expires {2} [{3}, {4}]>'.format(
- self.__class__.__name__,
- self.id,
- self.expires.isoformat(),
- self.user,
- self.client)
-
-
-MODELS = [
- OAuthToken,
- OAuthRefreshToken,
- OAuthCode,
- OAuthClient,
- OAuthUserClient]
diff --git a/mediagoblin/plugins/oauth/templates/oauth/authorize.html b/mediagoblin/plugins/oauth/templates/oauth/authorize.html
deleted file mode 100644
index 8a00c925..00000000
--- a/mediagoblin/plugins/oauth/templates/oauth/authorize.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{#
-# 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.
-#, se, seee
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
--#}
-{% extends "mediagoblin/base.html" %}
-{% import "mediagoblin/utils/wtforms.html" as wtforms_util %}
-
-{% block mediagoblin_content %}
-<form action="{{ request.urlgen('mediagoblin.plugins.oauth.authorize_client') }}"
- method="POST">
- <div class="form_box_xl">
- {{ csrf_token }}
- <h2>Authorize {{ client.name }}?</h2>
- <p class="client-description">{{ client.description }}</p>
- {{ wtforms_util.render_divs(form) }}
- </div>
-</form>
-{% endblock %}
diff --git a/mediagoblin/plugins/oauth/templates/oauth/client/list.html b/mediagoblin/plugins/oauth/templates/oauth/client/list.html
deleted file mode 100644
index 21024bb7..00000000
--- a/mediagoblin/plugins/oauth/templates/oauth/client/list.html
+++ /dev/null
@@ -1,45 +0,0 @@
-{#
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
--#}
-{% extends "mediagoblin/base.html" %}
-{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
-
-{% block mediagoblin_content %}
-<h1>{% trans %}Your OAuth clients{% endtrans %}</h1>
-{% if clients %}
-<ul>
- {% for client in clients %}
- <li>{{ client.name }}
- <dl>
- <dt>Type</dt>
- <dd>{{ client.type }}</dd>
- <dt>Description</dt>
- <dd>{{ client.description }}</dd>
- <dt>Identifier</dt>
- <dd>{{ client.identifier }}</dd>
- <dt>Secret</dt>
- <dd>{{ client.secret }}</dd>
- <dt>Redirect URI<dt>
- <dd>{{ client.redirect_uri }}</dd>
- </dl>
- </li>
- {% endfor %}
-</ul>
-{% else %}
-<p>You don't have any clients yet. <a href="{{ request.urlgen('mediagoblin.plugins.oauth.register_client') }}">Add one</a>.</p>
-{% endif %}
-{% endblock %}
diff --git a/mediagoblin/plugins/oauth/templates/oauth/client/register.html b/mediagoblin/plugins/oauth/templates/oauth/client/register.html
deleted file mode 100644
index 6fd700d3..00000000
--- a/mediagoblin/plugins/oauth/templates/oauth/client/register.html
+++ /dev/null
@@ -1,34 +0,0 @@
-{#
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
--#}
-{% extends "mediagoblin/base.html" %}
-{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
-
-{% block mediagoblin_content %}
-<form action="{{ request.urlgen('mediagoblin.plugins.oauth.register_client') }}"
- method="POST">
- <div class="form_box_xl">
- <h1>Register OAuth client</h1>
- {{ wtforms_util.render_divs(form) }}
- <div class="form_submit_buttons">
- {{ csrf_token }}
- <input type="submit" value="{% trans %}Add{% endtrans %}"
- class="button_form" />
- </div>
- </div>
-</form>
-{% endblock %}
diff --git a/mediagoblin/plugins/oauth/tools.py b/mediagoblin/plugins/oauth/tools.py
deleted file mode 100644
index af0a3305..00000000
--- a/mediagoblin/plugins/oauth/tools.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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 uuid
-
-from random import getrandbits
-
-from datetime import datetime
-
-from functools import wraps
-
-from mediagoblin.tools.response import json_response
-
-
-def require_client_auth(controller):
- '''
- View decorator
-
- - Requires the presence of ``?client_id``
- '''
- # Avoid circular import
- from mediagoblin.plugins.oauth.models import OAuthClient
-
- @wraps(controller)
- def wrapper(request, *args, **kw):
- if not request.GET.get('client_id'):
- return json_response({
- 'status': 400,
- 'errors': [u'No client identifier in URL']},
- _disable_cors=True)
-
- client = OAuthClient.query.filter(
- OAuthClient.identifier == request.GET.get('client_id')).first()
-
- if not client:
- return json_response({
- 'status': 400,
- 'errors': [u'No such client identifier']},
- _disable_cors=True)
-
- return controller(request, client)
-
- return wrapper
-
-
-def create_token(client, user):
- '''
- Create an OAuthToken and an OAuthRefreshToken entry in the database
-
- Returns the data structure expected by the OAuth clients.
- '''
- from mediagoblin.plugins.oauth.models import OAuthToken, OAuthRefreshToken
-
- token = OAuthToken()
- token.user = user
- token.client = client
- token.save()
-
- refresh_token = OAuthRefreshToken()
- refresh_token.user = user
- refresh_token.client = client
- refresh_token.save()
-
- # expire time of token in full seconds
- # timedelta.total_seconds is python >= 2.7 or we would use that
- td = token.expires - datetime.now()
- exp_in = 86400*td.days + td.seconds # just ignore µsec
-
- return {'access_token': token.token, 'token_type': 'bearer',
- 'refresh_token': refresh_token.token, 'expires_in': exp_in}
-
-
-def generate_identifier():
- ''' Generates a ``uuid.uuid4()`` '''
- return unicode(uuid.uuid4())
-
-
-def generate_token():
- ''' Uses generate_identifier '''
- return generate_identifier()
-
-
-def generate_refresh_token():
- ''' Uses generate_identifier '''
- return generate_identifier()
-
-
-def generate_code():
- ''' Uses generate_identifier '''
- return generate_identifier()
-
-
-def generate_secret():
- '''
- Generate a long string of pseudo-random characters
- '''
- # XXX: We might not want it to use bcrypt, since bcrypt takes its time to
- # generate the result.
- return unicode(getrandbits(192))
-
diff --git a/mediagoblin/plugins/oauth/views.py b/mediagoblin/plugins/oauth/views.py
deleted file mode 100644
index de637d6b..00000000
--- a/mediagoblin/plugins/oauth/views.py
+++ /dev/null
@@ -1,253 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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 urllib import urlencode
-
-from werkzeug.exceptions import BadRequest
-
-from mediagoblin.tools.response import render_to_response, redirect, json_response
-from mediagoblin.decorators import require_active_login
-from mediagoblin.messages import add_message, SUCCESS
-from mediagoblin.tools.translate import pass_to_ugettext as _
-from mediagoblin.plugins.oauth.models import OAuthCode, OAuthClient, \
- OAuthUserClient, OAuthRefreshToken
-from mediagoblin.plugins.oauth.forms import ClientRegistrationForm, \
- AuthorizationForm
-from mediagoblin.plugins.oauth.tools import require_client_auth, \
- create_token
-
-_log = logging.getLogger(__name__)
-
-
-@require_active_login
-def register_client(request):
- '''
- Register an OAuth client
- '''
- form = ClientRegistrationForm(request.form)
-
- if request.method == 'POST' and form.validate():
- client = OAuthClient()
- client.name = unicode(form.name.data)
- client.description = unicode(form.description.data)
- client.type = unicode(form.type.data)
- client.owner_id = request.user.id
- client.redirect_uri = unicode(form.redirect_uri.data)
-
- client.save()
-
- add_message(request, SUCCESS, _('The client {0} has been registered!')\
- .format(
- client.name))
-
- return redirect(request, 'mediagoblin.plugins.oauth.list_clients')
-
- return render_to_response(
- request,
- 'oauth/client/register.html',
- {'form': form})
-
-
-@require_active_login
-def list_clients(request):
- clients = request.db.OAuthClient.query.filter(
- OAuthClient.owner_id == request.user.id).all()
- return render_to_response(request, 'oauth/client/list.html',
- {'clients': clients})
-
-
-@require_active_login
-def list_connections(request):
- connections = OAuthUserClient.query.filter(
- OAuthUserClient.user == request.user).all()
- return render_to_response(request, 'oauth/client/connections.html',
- {'connections': connections})
-
-
-@require_active_login
-def authorize_client(request):
- form = AuthorizationForm(request.form)
-
- client = OAuthClient.query.filter(OAuthClient.id ==
- form.client_id.data).first()
-
- if not client:
- _log.error('No such client id as received from client authorization \
-form.')
- raise BadRequest()
-
- if form.validate():
- relation = OAuthUserClient()
- relation.user_id = request.user.id
- relation.client_id = form.client_id.data
- if form.allow.data:
- relation.state = u'approved'
- elif form.deny.data:
- relation.state = u'rejected'
- else:
- raise BadRequest()
-
- relation.save()
-
- return redirect(request, location=form.next.data)
-
- return render_to_response(
- request,
- 'oauth/authorize.html',
- {'form': form,
- 'client': client})
-
-
-@require_client_auth
-@require_active_login
-def authorize(request, client):
- # TODO: Get rid of the JSON responses in this view, it's called by the
- # user-agent, not the client.
- user_client_relation = OAuthUserClient.query.filter(
- (OAuthUserClient.user == request.user)
- & (OAuthUserClient.client == client))
-
- if user_client_relation.filter(OAuthUserClient.state ==
- u'approved').count():
- redirect_uri = None
-
- if client.type == u'public':
- if not client.redirect_uri:
- return json_response({
- 'status': 400,
- 'errors':
- [u'Public clients should have a redirect_uri pre-set.']},
- _disable_cors=True)
-
- redirect_uri = client.redirect_uri
-
- if client.type == u'confidential':
- redirect_uri = request.GET.get('redirect_uri', client.redirect_uri)
- if not redirect_uri:
- return json_response({
- 'status': 400,
- 'errors': [u'No redirect_uri supplied!']},
- _disable_cors=True)
-
- code = OAuthCode()
- code.user = request.user
- code.client = client
- code.save()
-
- redirect_uri = ''.join([
- redirect_uri,
- '?',
- urlencode({'code': code.code})])
-
- _log.debug('Redirecting to {0}'.format(redirect_uri))
-
- return redirect(request, location=redirect_uri)
- else:
- # Show prompt to allow client to access data
- # - on accept: send the user agent back to the redirect_uri with the
- # code parameter
- # - on deny: send the user agent back to the redirect uri with error
- # information
- form = AuthorizationForm(request.form)
- form.client_id.data = client.id
- form.next.data = request.url
- return render_to_response(
- request,
- 'oauth/authorize.html',
- {'form': form,
- 'client': client})
-
-
-def access_token(request):
- '''
- Access token endpoint provides access tokens to any clients that have the
- right grants/credentials
- '''
-
- client = None
- user = None
-
- if request.GET.get('code'):
- # Validate the code arg, then get the client object from the db.
- code = OAuthCode.query.filter(OAuthCode.code ==
- request.GET.get('code')).first()
-
- if not code:
- return json_response({
- 'error': 'invalid_request',
- 'error_description':
- 'Invalid code.'})
-
- client = code.client
- user = code.user
-
- elif request.args.get('refresh_token'):
- # Validate a refresh token, then get the client object from the db.
- refresh_token = OAuthRefreshToken.query.filter(
- OAuthRefreshToken.token ==
- request.args.get('refresh_token')).first()
-
- if not refresh_token:
- return json_response({
- 'error': 'invalid_request',
- 'error_description':
- 'Invalid refresh token.'})
-
- client = refresh_token.client
- user = refresh_token.user
-
- if client:
- client_identifier = request.GET.get('client_id')
-
- if not client_identifier:
- return json_response({
- 'error': 'invalid_request',
- 'error_description':
- 'Missing client_id in request.'})
-
- if not client_identifier == client.identifier:
- return json_response({
- 'error': 'invalid_client',
- 'error_description':
- 'Mismatching client credentials.'})
-
- if client.type == u'confidential':
- client_secret = request.GET.get('client_secret')
-
- if not client_secret:
- return json_response({
- 'error': 'invalid_request',
- 'error_description':
- 'Missing client_secret in request.'})
-
- if not client_secret == client.secret:
- return json_response({
- 'error': 'invalid_client',
- 'error_description':
- 'Mismatching client credentials.'})
-
-
- access_token_data = create_token(client, user)
-
- return json_response(access_token_data, _disable_cors=True)
-
- return json_response({
- 'error': 'invalid_request',
- 'error_description':
- 'Missing `code` or `refresh_token` parameter in request.'})
diff --git a/mediagoblin/plugins/openid/README.rst b/mediagoblin/plugins/openid/README.rst
index 870a2b58..1a777336 100644
--- a/mediagoblin/plugins/openid/README.rst
+++ b/mediagoblin/plugins/openid/README.rst
@@ -1,23 +1,23 @@
.. _openid-chapter:
===================
- openid plugin
+ OpenID plugin
===================
-The openid plugin allows user to login to your GNU Mediagoblin instance using
-their openid url.
+The OpenID plugin allows user to login to your GNU MediaGoblin instance using
+their OpenID URL.
This plugin can be enabled alongside :ref:`basic_auth-chapter` and
:ref:`persona-chapter`.
.. note::
- When :ref:`basic_auth-chapter` is enabled alongside this openid plugin, and
- a user creates an account using their openid. If they would like to add a
+ When :ref:`basic_auth-chapter` is enabled alongside this OpenID plugin, and
+ a user creates an account using their OpenID. If they would like to add a
password to their account, they can use the forgot password feature to do
so.
-Set up the openid plugin
+Set up the OpenID plugin
============================
1. Install the ``python-openid`` package.
diff --git a/mediagoblin/plugins/openid/__init__.py b/mediagoblin/plugins/openid/__init__.py
index ca17a7e8..b26087a2 100644
--- a/mediagoblin/plugins/openid/__init__.py
+++ b/mediagoblin/plugins/openid/__init__.py
@@ -19,7 +19,7 @@ import uuid
from sqlalchemy import or_
from mediagoblin.auth.tools import create_basic_user
-from mediagoblin.db.models import User
+from mediagoblin.db.models import User, LocalUser
from mediagoblin.plugins.openid.models import OpenIDUserURL
from mediagoblin.tools import pluginapi
from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
@@ -67,8 +67,8 @@ def create_user(register_form):
username = register_form.username.data
user = User.query.filter(
or_(
- User.username == username,
- User.email == username,
+ LocalUser.username == username,
+ LocalUser.email == username,
)).first()
if not user:
diff --git a/mediagoblin/plugins/openid/forms.py b/mediagoblin/plugins/openid/forms.py
index d47369dc..6dfde0c8 100644
--- a/mediagoblin/plugins/openid/forms.py
+++ b/mediagoblin/plugins/openid/forms.py
@@ -23,18 +23,18 @@ class RegistrationForm(wtforms.Form):
openid = wtforms.HiddenField(
'',
[wtforms.validators.InputRequired()])
- username = wtforms.TextField(
+ username = wtforms.StringField(
_('Username'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field(allow_email=False)])
- email = wtforms.TextField(
+ email = wtforms.StringField(
_('Email address'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field(allow_user=False)])
class LoginForm(wtforms.Form):
- openid = wtforms.TextField(
+ openid = wtforms.StringField(
_('OpenID'),
[wtforms.validators.InputRequired(),
# Can openid's only be urls?
diff --git a/mediagoblin/plugins/openid/migrations/071abb33d1da_openid_plugin_initial_migration.py b/mediagoblin/plugins/openid/migrations/071abb33d1da_openid_plugin_initial_migration.py
new file mode 100644
index 00000000..b6b97da9
--- /dev/null
+++ b/mediagoblin/plugins/openid/migrations/071abb33d1da_openid_plugin_initial_migration.py
@@ -0,0 +1,54 @@
+"""OpenID plugin initial migration
+
+Revision ID: 071abb33d1da
+Revises: 52bf0ccbedc1
+Create Date: 2016-03-12 23:32:58.191980
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '071abb33d1da'
+down_revision = '52bf0ccbedc1'
+branch_labels = ('openid_plugin',)
+depends_on = None
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ if op.get_bind().engine.has_table('openid__association'):
+ # Skip; this has already been instantiated
+ # (probably via sqlalchemy-migrate)
+ return
+
+ op.create_table(
+ 'openid__association',
+ sa.Column('server_url', sa.Unicode(), nullable=False),
+ sa.Column('handle', sa.Unicode(), nullable=False),
+ sa.Column('secret', sa.Unicode(), nullable=True),
+ sa.Column('issued', sa.Integer(), nullable=True),
+ sa.Column('lifetime', sa.Integer(), nullable=True),
+ sa.Column('assoc_type', sa.Unicode(), nullable=True),
+ sa.PrimaryKeyConstraint('server_url', 'handle'))
+
+ op.create_table(
+ 'openid__nonce',
+ sa.Column('server_url', sa.Unicode(), nullable=False),
+ sa.Column('timestamp', sa.Integer(), nullable=False),
+ sa.Column('salt', sa.Unicode(), nullable=False),
+ sa.PrimaryKeyConstraint('server_url', 'timestamp', 'salt'))
+
+ op.create_table(
+ 'openid__user_urls',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('openid_url', sa.Unicode(), nullable=False),
+ sa.Column('user_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['user_id'], ['core__users.id'], ),
+ sa.PrimaryKeyConstraint('id'))
+
+
+def downgrade():
+ op.drop_table('openid__user_urls')
+ op.drop_table('openid__nonce')
+ op.drop_table('openid__association')
diff --git a/mediagoblin/plugins/openid/store.py b/mediagoblin/plugins/openid/store.py
index 8f9a7012..24726814 100644
--- a/mediagoblin/plugins/openid/store.py
+++ b/mediagoblin/plugins/openid/store.py
@@ -16,6 +16,8 @@
import base64
import time
+import six
+
from openid.association import Association as OIDAssociation
from openid.store.interface import OpenIDStore
from openid.store import nonce
@@ -34,12 +36,12 @@ class SQLAlchemyOpenIDStore(OpenIDStore):
if not assoc:
assoc = Association()
- assoc.server_url = unicode(server_url)
+ assoc.server_url = six.text_type(server_url)
assoc.handle = association.handle
# django uses base64 encoding, python-openid uses a blob field for
# secret
- assoc.secret = unicode(base64.encodestring(association.secret))
+ assoc.secret = six.text_type(base64.encodestring(association.secret))
assoc.issued = association.issued
assoc.lifetime = association.lifetime
assoc.assoc_type = association.assoc_type
diff --git a/mediagoblin/plugins/openid/views.py b/mediagoblin/plugins/openid/views.py
index bb2de7ab..71f444fa 100644
--- a/mediagoblin/plugins/openid/views.py
+++ b/mediagoblin/plugins/openid/views.py
@@ -13,6 +13,9 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import six
+
from openid.consumer import consumer
from openid.consumer.discover import DiscoveryFailure
from openid.extensions.sreg import SRegRequest, SRegResponse
@@ -186,7 +189,7 @@ def finish_login(request):
if user:
# Set up login in session
- request.session['user_id'] = unicode(user.id)
+ request.session['user_id'] = six.text_type(user.id)
request.session.save()
if request.session.get('next'):
diff --git a/mediagoblin/plugins/persona/__init__.py b/mediagoblin/plugins/persona/__init__.py
index 700c18e2..8fab726a 100644
--- a/mediagoblin/plugins/persona/__init__.py
+++ b/mediagoblin/plugins/persona/__init__.py
@@ -19,7 +19,7 @@ import os
from sqlalchemy import or_
from mediagoblin.auth.tools import create_basic_user
-from mediagoblin.db.models import User
+from mediagoblin.db.models import User, LocalUser
from mediagoblin.plugins.persona.models import PersonaUserEmails
from mediagoblin.tools import pluginapi
from mediagoblin.tools.staticdirect import PluginStatic
@@ -60,8 +60,8 @@ def create_user(register_form):
username = register_form.username.data
user = User.query.filter(
or_(
- User.username == username,
- User.email == username,
+ LocalUser.username == username,
+ LocalUser.email == username,
)).first()
if not user:
diff --git a/mediagoblin/plugins/persona/forms.py b/mediagoblin/plugins/persona/forms.py
index 7d632344..f74d97fa 100644
--- a/mediagoblin/plugins/persona/forms.py
+++ b/mediagoblin/plugins/persona/forms.py
@@ -20,11 +20,11 @@ from mediagoblin.auth.tools import normalize_user_or_email_field
class RegistrationForm(wtforms.Form):
- username = wtforms.TextField(
+ username = wtforms.StringField(
_('Username'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field(allow_email=False)])
- email = wtforms.TextField(
+ email = wtforms.StringField(
_('Email address'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field(allow_user=False)])
@@ -35,7 +35,7 @@ class RegistrationForm(wtforms.Form):
class EditForm(wtforms.Form):
- email = wtforms.TextField(
+ email = wtforms.StringField(
_('Email address'),
[wtforms.validators.InputRequired(),
normalize_user_or_email_field(allow_user=False)])
diff --git a/mediagoblin/plugins/persona/migrations/c7d4840a5592_persona_plugin_initial_migration.py b/mediagoblin/plugins/persona/migrations/c7d4840a5592_persona_plugin_initial_migration.py
new file mode 100644
index 00000000..0c40893a
--- /dev/null
+++ b/mediagoblin/plugins/persona/migrations/c7d4840a5592_persona_plugin_initial_migration.py
@@ -0,0 +1,35 @@
+"""Persona plugin initial migration
+
+Revision ID: c7d4840a5592
+Revises: 52bf0ccbedc1
+Create Date: 2016-03-12 23:30:33.624390
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'c7d4840a5592'
+down_revision = '52bf0ccbedc1'
+branch_labels = ('persona_plugin',)
+depends_on = None
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ if op.get_bind().engine.has_table('persona__user_emails'):
+ # Skip; this has already been instantiated
+ # (probably via sqlalchemy-migrate)
+ return
+
+ op.create_table(
+ 'persona__user_emails',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('persona_email', sa.Unicode(), nullable=False),
+ sa.Column('user_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['user_id'], ['core__users.id'], ),
+ sa.PrimaryKeyConstraint('id'))
+
+
+def downgrade():
+ op.drop_table('persona__user_emails')
diff --git a/mediagoblin/plugins/persona/views.py b/mediagoblin/plugins/persona/views.py
index 1bba3b8c..41d38353 100644
--- a/mediagoblin/plugins/persona/views.py
+++ b/mediagoblin/plugins/persona/views.py
@@ -17,6 +17,8 @@ import json
import logging
import requests
+import six
+
from werkzeug.exceptions import BadRequest
from mediagoblin import messages, mg_globals
@@ -63,7 +65,7 @@ def login(request):
user = query.user if query else None
if user:
- request.session['user_id'] = unicode(user.id)
+ request.session['user_id'] = six.text_type(user.id)
request.session['persona_login_email'] = email
request.session.save()
diff --git a/mediagoblin/plugins/piwigo/forms.py b/mediagoblin/plugins/piwigo/forms.py
index fd545a3e..b501562b 100644
--- a/mediagoblin/plugins/piwigo/forms.py
+++ b/mediagoblin/plugins/piwigo/forms.py
@@ -20,10 +20,10 @@ import wtforms
class AddSimpleForm(wtforms.Form):
image = wtforms.FileField()
- name = wtforms.TextField(
+ name = wtforms.StringField(
validators=[wtforms.validators.Length(min=0, max=500)])
- comment = wtforms.TextField()
- # tags = wtforms.FieldList(wtforms.TextField())
+ comment = wtforms.StringField()
+ # tags = wtforms.FieldList(wtforms.StringField())
category = wtforms.IntegerField()
level = wtforms.IntegerField()
@@ -32,13 +32,13 @@ _md5_validator = wtforms.validators.Regexp(r"^[0-9a-fA-F]{32}$")
class AddForm(wtforms.Form):
- original_sum = wtforms.TextField(None,
+ original_sum = wtforms.StringField(None,
[_md5_validator,
wtforms.validators.InputRequired()])
- thumbnail_sum = wtforms.TextField(None,
+ thumbnail_sum = wtforms.StringField(None,
[wtforms.validators.Optional(),
_md5_validator])
- file_sum = wtforms.TextField(None, [_md5_validator])
- name = wtforms.TextField()
- date_creation = wtforms.TextField()
- categories = wtforms.TextField()
+ file_sum = wtforms.StringField(None, [_md5_validator])
+ name = wtforms.StringField()
+ date_creation = wtforms.StringField()
+ categories = wtforms.StringField()
diff --git a/mediagoblin/plugins/piwigo/tools.py b/mediagoblin/plugins/piwigo/tools.py
index 484ea531..7b9b7af3 100644
--- a/mediagoblin/plugins/piwigo/tools.py
+++ b/mediagoblin/plugins/piwigo/tools.py
@@ -47,7 +47,7 @@ class PwgNamedArray(list):
def _fill_element_dict(el, data, as_attr=()):
- for k, v in data.iteritems():
+ for k, v in six.iteritems(data):
if k in as_attr:
if not isinstance(v, six.string_types):
v = str(v)
diff --git a/mediagoblin/plugins/piwigo/views.py b/mediagoblin/plugins/piwigo/views.py
index f913a730..30c7ffa2 100644
--- a/mediagoblin/plugins/piwigo/views.py
+++ b/mediagoblin/plugins/piwigo/views.py
@@ -17,6 +17,8 @@
import logging
import re
+import six
+
from werkzeug.exceptions import MethodNotAllowed, BadRequest, NotImplemented
from werkzeug.wrappers import BaseResponse
@@ -80,7 +82,7 @@ def pwg_categories_getList(request):
if request.user:
collections = Collection.query.filter_by(
- get_creator=request.user).order_by(Collection.title)
+ get_actor=request.user).order_by(Collection.title)
for c in collections:
catlist.append({'id': c.id,
@@ -126,21 +128,18 @@ def pwg_images_addSimple(request):
if not check_file_field(request, 'image'):
raise BadRequest()
- upload_limit, max_file_size = get_upload_file_limits(request.user)
-
try:
entry = submit_media(
mg_app=request.app, user=request.user,
submitted_file=request.files['image'],
filename=request.files['image'].filename,
- title=unicode(form.name.data),
- description=unicode(form.comment.data),
- upload_limit=upload_limit, max_file_size=max_file_size)
+ title=six.text_type(form.name.data),
+ description=six.text_type(form.comment.data))
collection_id = form.category.data
if collection_id > 0:
collection = Collection.query.get(collection_id)
- if collection is not None and collection.creator == request.user.id:
+ if collection is not None and collection.actor == request.user.id:
add_media_to_collection(collection, entry, "")
return {
diff --git a/mediagoblin/plugins/processing_info/README.rst b/mediagoblin/plugins/processing_info/README.rst
new file mode 100644
index 00000000..3bacbf50
--- /dev/null
+++ b/mediagoblin/plugins/processing_info/README.rst
@@ -0,0 +1,5 @@
+==============
+ sampleplugin
+==============
+
+A plugin to insert some useful information about processing to templates
diff --git a/mediagoblin/plugins/processing_info/__init__.py b/mediagoblin/plugins/processing_info/__init__.py
new file mode 100644
index 00000000..fb824765
--- /dev/null
+++ b/mediagoblin/plugins/processing_info/__init__.py
@@ -0,0 +1,51 @@
+# 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
+import os
+
+from mediagoblin.tools.pluginapi import get_config
+from mediagoblin.db.models import MediaEntry
+from mediagoblin.tools import pluginapi
+
+_log = logging.getLogger(__name__)
+
+PLUGIN_DIR = os.path.dirname(__file__)
+
+def setup_plugin():
+ pluginapi.register_template_path(os.path.join(PLUGIN_DIR, 'templates'))
+ pluginapi.register_template_hooks(
+ {'header_left': 'mediagoblin/processing_info/header_left.html'})
+ return
+
+def make_stats(context):
+ request = context['request']
+ user = request.user
+ if user:
+ num_queued = MediaEntry.query.filter_by(
+ actor=user.id, state=u'processing').count()
+ context['num_queued'] = num_queued
+ num_failed = MediaEntry.query.filter_by(
+ actor=user.id, state=u'failed').count()
+ context['num_failed'] = num_failed
+ return context
+
+
+hooks = {
+ 'setup': setup_plugin,
+ 'template_context_prerender': make_stats
+ }
diff --git a/mediagoblin/plugins/oauth/templates/oauth/client/connections.html b/mediagoblin/plugins/processing_info/templates/mediagoblin/processing_info/header_left.html
index 63b0230a..1d5e724d 100644
--- a/mediagoblin/plugins/oauth/templates/oauth/client/connections.html
+++ b/mediagoblin/plugins/processing_info/templates/mediagoblin/processing_info/header_left.html
@@ -14,21 +14,19 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
--#}
-{% extends "mediagoblin/base.html" %}
-{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
-
-{% block mediagoblin_content %}
-<h1>{% trans %}OAuth client connections{% endtrans %}</h1>
-{% if connections %}
-<ul>
- {% for connection in connections %}
- <li><span title="{{ connection.client.description }}">{{
- connection.client.name }}</span> - {{ connection.state }}
- </li>
- {% endfor %}
-</ul>
-{% else %}
-<p>You haven't connected using an OAuth client before.</p>
+#}
+{#This injects some information about entries in processing #}
+{% if request.user and request.user.has_privilege('active') %}
+{% if num_queued is defined and num_queued != 0 %}
+<span class="num_queued status_icon">
+ <a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
+ user=request.user.username, state="processing") }}">&#9203; {{ num_queued }}</a>
+</span>
+{% endif %}
+{% if num_failed is defined and num_failed != 0 %}
+<span class="num_failed status_icon">
+ <a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
+ user=request.user.username, state="failed") }}">&#9785; {{ num_failed }}</a>
+</span>
+{% endif %}
{% endif %}
-{% endblock %}
diff --git a/mediagoblin/plugins/trim_whitespace/README.rst b/mediagoblin/plugins/trim_whitespace/README.rst
index db9a0c53..d83af06b 100644
--- a/mediagoblin/plugins/trim_whitespace/README.rst
+++ b/mediagoblin/plugins/trim_whitespace/README.rst
@@ -2,7 +2,7 @@
Trim whitespace plugin
=======================
-Mediagoblin templates are written with 80 char limit for better
+MediaGoblin templates are written with 80 char limit for better
readability. However that means that the HTML output is very verbose
containing *lots* of whitespace. This plugin inserts a middleware that
filters out whitespace from the returned HTML in the ``Response()``