Skip to content
This repository has been archived by the owner on Oct 15, 2021. It is now read-only.

transifex OAuth support + Mobile templates + 2 new api requests #238

Open
wants to merge 3 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ transifex/site_media/static
htmlcov/*
.coverage
.*.swp
.settings/
14 changes: 14 additions & 0 deletions transifex/api/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
# -*- coding: utf-8 -*-
# Create your views here.
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseBadRequest
from django.utils.translation import ugettext as _

def request_token_ready(request, token):
error = request.GET.get('error', '')
ctx = RequestContext(request, {
'error' : error,
'token' : token
})
return render_to_response(
'piston/request_token_ready.html',
context_instance = ctx
)

def reject_legacy_api(request, *args, **kwargs):
return HttpResponseBadRequest(_("This version of API is obsolete. "\
"Please have a look at %(url)s for details."
Expand Down
Empty file added transifex/mobile/__init__.py
Empty file.
Empty file.
176 changes: 176 additions & 0 deletions transifex/mobile/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# -*- coding: utf-8 -*-
from django.conf.urls.defaults import *
from django.views.decorators.cache import never_cache
from piston.resource import Resource
from piston.authentication import OAuthAuthentication
from transifex.api.authentication import CustomHttpBasicAuthentication

#auth = OAuthAuthentication(realm='Transifex API')

from transifex.languages.api import LanguageHandler
from transifex.projects.api import ProjectHandler
from transifex.resources.api import ResourceHandler, StatsHandler, \
TranslationHandler, FormatsHandler, TranslationObjectsHandler,\
SingleTranslationHandler
from transifex.releases.api import ReleaseHandler
from transifex.actionlog.api import ActionlogHandler
from transifex.api.views import reject_legacy_api

auth = OAuthAuthentication(realm='Transifex API')
#auth = CustomHttpBasicAuthentication(realm='Transifex API')

resource_handler = Resource(ResourceHandler, authentication=auth)
release_handler = Resource(ReleaseHandler, authentication=auth)
project_handler = Resource(ProjectHandler, authentication=auth)
stats_handler = Resource(StatsHandler, authentication=auth)
translation_handler = Resource(TranslationHandler, authentication=auth)
actionlog_handler = Resource(ActionlogHandler, authentication=auth)
formats_handler = Resource(FormatsHandler, authentication=auth)
translation_objects_handler = Resource(TranslationObjectsHandler,
authentication=auth)
single_translation_handler = Resource(SingleTranslationHandler,
authentication=auth)

urlpatterns = patterns('',
url(
r'^languages/$',
reject_legacy_api,
{'api_version': 1},
name='api.languages',
), url(
r'^projects/$',
reject_legacy_api,
{'api_version': 1},
name='api_projects',
), url(
r'^project/',
reject_legacy_api,
{'api_version': 1},
name='api_project',
), url(
r'^storage/',
reject_legacy_api,
{'api_version': 1},
name='api.storage',
), url(
r'^1/',
reject_legacy_api,
{'api_version': 1},
name='api.languages',
), url(
r'^2/projects/$',
never_cache(project_handler),
{'api_version': 2},
name='apiv2_projects',
), url(
r'^2/project/(?P<project_slug>[-\w]+)/$',
never_cache(project_handler),
{'api_version': 2},
name='apiv2_project',
), url(
r'^2/project/(?P<project_slug>[-\w]+)/resources/$',
never_cache(resource_handler),
{'api_version': 2},
name='apiv2_resources',
), url(
r'^2/project/(?P<project_slug>[-\w]+)/resource/(?P<resource_slug>[-\w]+)/$',
never_cache(resource_handler),
{'api_version': 2},
name='apiv2_resource',
), url(
r'^2/project/(?P<project_slug>[-\w]+)/resource/(?P<resource_slug>[-\w]+)/content/$',
never_cache(translation_handler),
{'api_version': 2, 'lang_code': 'source'},
name='apiv2_source_content',
), url(
r'^2/project/(?P<project_slug>[-\w]+)/resource/(?P<resource_slug>[-\w]+)/pseudo/$',
never_cache(translation_handler),
{'api_version': 2, 'lang_code': 'source', 'is_pseudo':True},
name='apiv2_pseudo_content',
), url(
r'^2/project/(?P<project_slug>[-\w]+)/resource/(?P<resource_slug>[-\w]+)/translation/(?P<lang_code>[\-_@\w\.]+)/$',
never_cache(translation_handler),
{'api_version': 2},
name='apiv2_translation',
), url(
r'^2/project/(?P<project_slug>[-\w]+)/resource/(?P<resource_slug>[-\w]+)/stats/$',
never_cache(stats_handler),
{'api_version': 2, 'lang_code': None},
name='apiv2_stats',
), url(
r'^2/project/(?P<project_slug>[-\w]+)/resource/(?P<resource_slug>[-\w]+)/stats/(?P<lang_code>[\-_@\w\.]+)/$',
never_cache(stats_handler),
{'api_version': 2},
name='apiv2_stats',
), url(
r'^2/project/(?P<project_slug>[-\w]+)/release/(?P<release_slug>[-\w]+)/$',
never_cache(release_handler),
{'api_version': 2},
name='apiv2_release',
), url(
r'^2/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='global_actionlogs',
), url(
r'^2/accounts/profile/(?P<username>[\.\w-]+)/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='user_actionlogs',
), url(
r'^2/project/(?P<project_slug>[\w-]+)/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='project_actionlogs',
), url(
r'^2/projects/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='projects_actionlogs',
), url(
r'^2/project/(?P<project_slug>[\w-]+)/teams/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='project_teams_actionlogs',
), url(
r'^2/project/(?P<project_slug>[\w-]+)/team/(?P<language_code>[\-_@\w\.]+)/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='project_team_actionlogs',
), url(
r'^2/project/(?P<project_slug>[\w-]+)/releases/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='project_releases_actionlogs',
), url(
r'^2/project/(?P<project_slug>[\w-]+)/r/(?P<release_slug>[\w-]+)/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='project_release_actionlogs',
), url(
r'^2/project/(?P<project_slug>[\w-]+)/resources/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='project_resources_actionlogs',
), url(
r'^2/project/(?P<project_slug>[\w-]+)/resource/(?P<resource_slug>[\w-]+)/actionlog/$',
actionlog_handler,
{'api_version': 2},
name='project_resource_actionlogs',
), url(
r'^2/formats/$',
formats_handler,
{'api_version': 2},
name='supported_formats',
), url(
r'^2/project/(?P<project_slug>[\w-]+)/resource/(?P<resource_slug>[\w-]+)/translation/(?P<language_code>[\-_@\w\.]+)/strings/$',
translation_objects_handler,
{'api_version': 2},
name='translation_strings'
), url(
r'^2/project/(?P<project_slug>[\w-]+)/resource/(?P<resource_slug>[\w-]+)/translation/(?P<language_code>[\-_@\w\.]+)/string/(?P<source_hash>[0-9a-f]{32})/$',
single_translation_handler,
{'api_version': 2},
name='translation_string'
)
)
89 changes: 89 additions & 0 deletions transifex/mobile/authentication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import binascii

from piston import oauth
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.models import User, AnonymousUser
from django.contrib.auth.decorators import login_required
from django.template import loader
from django.contrib.auth import authenticate
from django.conf import settings
from django.core.urlresolvers import get_callable
from django.core.exceptions import ImproperlyConfigured
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.decorators.csrf import csrf_exempt

from piston import forms
from piston.authentication import send_oauth_error, initialize_server_request, oauth_auth_view, oauth_request_token

def oauth_auth_view(request, token, callback, params):
form = forms.OAuthAuthenticationForm(initial={
'oauth_token': token.key,
'oauth_callback': token.get_callback_url() or callback,
})

return render_to_response('mobile/authorize_token.html',
{ 'form': form }, RequestContext(request))

@login_required(login_url='/mobile/accounts/signin/')
def oauth_user_auth(request):
oauth_server, oauth_request = initialize_server_request(request)

if oauth_request is None:
return INVALID_PARAMS_RESPONSE

try:
token = oauth_server.fetch_request_token(oauth_request)
except oauth.OAuthError, err:
return send_oauth_error(err)

try:
callback = oauth_server.get_callback(oauth_request)
except:
callback = None

if request.method == "GET":
params = oauth_request.get_normalized_parameters()

oauth_view = getattr(settings, 'OAUTH_AUTH_VIEW', None)
if oauth_view is None:
return oauth_auth_view(request, token, callback, params)
else:
return get_callable(oauth_view)(request, token, callback, params)
elif request.method == "POST":
try:
form = forms.OAuthAuthenticationForm(request.POST)
if form.is_valid():
token = oauth_server.authorize_token(token, request.user)
args = '?'+token.to_string(only_key=True)
else:
args = '?error=%s' % 'Access not granted by user.'
print "FORM ERROR", form.errors

if not callback:
callback = getattr(settings, 'OAUTH_CALLBACK_VIEW')
return get_callable(callback)(request, token)

response = HttpResponseRedirect(callback+args)

except oauth.OAuthError, err:
response = send_oauth_error(err)
else:
response = HttpResponse('Action not allowed.')

return response

@csrf_exempt
def oauth_access_token(request):
oauth_server, oauth_request = initialize_server_request(request)

if oauth_request is None:
return INVALID_PARAMS_RESPONSE

try:
token = oauth_server.fetch_access_token(oauth_request)
return HttpResponse(token.to_string())
except oauth.OAuthError, err:
return send_oauth_error(err)

INVALID_PARAMS_RESPONSE = send_oauth_error(oauth.OAuthError('Invalid request parameters.'))
Loading