Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / api / __init__.py @ bd40abfa

History | View | Annotate | Download (9.7 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from functools import wraps
35
from traceback import format_exc
36
from urllib import quote, unquote
37

    
38
from django.http import HttpResponse
39
from django.utils import simplejson as json
40
from django.conf import settings
41
from django.core.urlresolvers import reverse
42
from django.utils.translation import ugettext as _
43
from django.contrib import messages
44

    
45
from astakos.im.models import AstakosUser, Service, Resource
46
from snf_django.lib.api import faults
47
from astakos.im.settings import (
48
    INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED, QUOTAHOLDER_URL,
49
    PROJECTS_VISIBLE)
50
from astakos.im.forms import FeedbackForm
51
from astakos.im.functions import send_feedback as send_feedback_func
52

    
53
import logging
54
logger = logging.getLogger(__name__)
55

    
56
format = ('%a, %d %b %Y %H:%M:%S GMT')
57

    
58
absolute = lambda request, url: request.build_absolute_uri(url)
59

    
60

    
61
def render_fault(request, fault):
62
    if isinstance(fault, faults.InternalServerError) and settings.DEBUG:
63
        fault.details = format_exc(fault)
64

    
65
    request.serialization = 'text'
66
    data = fault.message + '\n'
67
    if fault.details:
68
        data += '\n' + fault.details
69
    response = HttpResponse(data, status=fault.code)
70
    response['Content-Length'] = len(response.content)
71
    return response
72

    
73

    
74
def api_method(http_method=None):
75
    """Decorator function for views that implement an API method."""
76
    def decorator(func):
77
        @wraps(func)
78
        def wrapper(request, *args, **kwargs):
79
            try:
80
                if http_method and request.method != http_method:
81
                    raise faults.BadRequest('Method not allowed.')
82
                response = func(request, *args, **kwargs)
83
                return response
84
            except faults.Fault, fault:
85
                return render_fault(request, fault)
86
            except BaseException, e:
87
                logger.exception('Unexpected error: %s' % e)
88
                fault = faults.InternalServerError('Unexpected error')
89
                return render_fault(request, fault)
90
        return wrapper
91
    return decorator
92

    
93

    
94
def get_services_dict():
95
    services = Service.objects.all()
96
    data = tuple({'id': s.pk, 'name': s.name, 'url': s.url, 'icon':
97
                 s.icon} for s in services)
98
    return data
99

    
100
@api_method(http_method=None)
101
def get_services(request):
102
    callback = request.GET.get('callback', None)
103
    mimetype = 'application/json'
104
    data = json.dumps(get_services_dict())
105

    
106
    if callback:
107
        # Consume session messages. When get_services is loaded from an astakos
108
        # page, messages should have already been consumed in the html
109
        # response. When get_services is loaded from another domain/service we
110
        # consume them here so that no stale messages to appear if user visits
111
        # an astakos view later on.
112
        # TODO: messages could be served to other services/sites in the dict
113
        # response of get_services and/or get_menu. Services could handle those
114
        # messages respectively.
115
        messages_list = list(messages.get_messages(request))
116
        mimetype = 'application/javascript'
117
        data = '%s(%s)' % (callback, data)
118

    
119
    return HttpResponse(content=data, mimetype=mimetype)
120

    
121

    
122
@api_method()
123
def get_menu(request, with_extra_links=False, with_signout=True):
124
    user = request.user
125
    from_location = request.GET.get('location')
126
    index_url = reverse('index')
127

    
128
    l = [{'url': absolute(request, index_url), 'name': _("Sign in")}]
129
    if user.is_authenticated():
130
        l = []
131
        append = l.append
132
        item = MenuItem
133
        item.current_path = absolute(request, request.path)
134
        append(item(
135
               url=absolute(request, reverse('index')),
136
               name=user.email))
137
        if with_extra_links:
138
            append(item(
139
                url=absolute(request, reverse('landing')),
140
                name="Overview"))
141
        if with_signout:
142
            append(item(
143
                   url=absolute(request, reverse('edit_profile')),
144
                   name="Dashboard"))
145
        if with_extra_links:
146
            append(item(url=absolute(request, reverse('edit_profile')),
147
                    name="Profile"))
148

    
149
        if with_extra_links:
150
            if INVITATIONS_ENABLED:
151
                append(item(
152
                       url=absolute(request, reverse('invite')),
153
                       name="Invitations"))
154

    
155

    
156
            append(item(
157
                   url=absolute(request, reverse('resource_usage')),
158
                   name="Usage"))
159
            if QUOTAHOLDER_URL and PROJECTS_VISIBLE:
160
                append(item(
161
                       url=absolute(request, reverse('project_list')),
162
                       name="Projects"))
163
            #append(item(
164
                #url=absolute(request, reverse('api_access')),
165
                #name="API Access"))
166

    
167
            append(item(
168
                   url=absolute(request, reverse('feedback')),
169
                   name="Contact"))
170
        if with_signout:
171
            append(item(
172
                   url=absolute(request, reverse('logout')),
173
                   name="Sign out"))
174

    
175
    callback = request.GET.get('callback', None)
176
    data = json.dumps(tuple(l))
177
    mimetype = 'application/json'
178

    
179
    if callback:
180
        mimetype = 'application/javascript'
181
        data = '%s(%s)' % (callback, data)
182

    
183
    return HttpResponse(content=data, mimetype=mimetype)
184

    
185

    
186
class MenuItem(dict):
187
    current_path = ''
188

    
189
    def __init__(self, *args, **kwargs):
190
        super(MenuItem, self).__init__(*args, **kwargs)
191
        if kwargs.get('url') or kwargs.get('submenu'):
192
            self.__set_is_active__()
193

    
194
    def __setitem__(self, key, value):
195
        super(MenuItem, self).__setitem__(key, value)
196
        if key in ('url', 'submenu'):
197
            self.__set_is_active__()
198

    
199
    def __set_is_active__(self):
200
        if self.get('is_active'):
201
            return
202
        if self.current_path.startswith(self.get('url')):
203
            self.__setitem__('is_active', True)
204
        else:
205
            submenu = self.get('submenu', ())
206
            current = (i for i in submenu if i.get('url') == self.current_path)
207
            try:
208
                current_node = current.next()
209
                if not current_node.get('is_active'):
210
                    current_node.__setitem__('is_active', True)
211
                self.__setitem__('is_active', True)
212
            except StopIteration:
213
                return
214

    
215
    def __setattribute__(self, name, value):
216
        super(MenuItem, self).__setattribute__(name, value)
217
        if name == 'current_path':
218
            self.__set_is_active__()
219

    
220
def __get_uuid_displayname_catalogs(request, user_call=True):
221
    # Normal Response Codes: 200
222
    # Error Response Codes: BadRequest (400)
223

    
224
    try:
225
        input_data = json.loads(request.raw_post_data)
226
    except:
227
        raise faults.BadRequest('Request body should be json formatted.')
228
    else:
229
        uuids = input_data.get('uuids', [])
230
        if uuids == None and user_call:
231
            uuids = []
232
        displaynames = input_data.get('displaynames', [])
233
        if displaynames == None and user_call:
234
            displaynames = []
235
        d  = {'uuid_catalog':AstakosUser.objects.uuid_catalog(uuids),
236
              'displayname_catalog':AstakosUser.objects.displayname_catalog(displaynames)}
237

    
238
        response = HttpResponse()
239
        response.content = json.dumps(d)
240
        response['Content-Type'] = 'application/json; charset=UTF-8'
241
        response['Content-Length'] = len(response.content)
242
        return response
243

    
244
def __send_feedback(request, email_template_name='im/feedback_mail.txt', user=None):
245
    if not user:
246
        auth_token = request.POST.get('auth', '')
247
        if not auth_token:
248
            raise faults.BadRequest('Missing user authentication')
249

    
250
        try:
251
            user = AstakosUser.objects.get(auth_token=auth_token)
252
        except AstakosUser.DoesNotExist:
253
            raise faults.BadRequest('Invalid user authentication')
254

    
255
    form = FeedbackForm(request.POST)
256
    if not form.is_valid():
257
        logger.error("Invalid feedback request: %r", form.errors)
258
        raise faults.BadRequest('Invalid data')
259

    
260
    msg = form.cleaned_data['feedback_msg']
261
    data = form.cleaned_data['feedback_data']
262
    try:
263
        send_feedback_func(msg, data, user, email_template_name)
264
    except:
265
        return HttpResponse(status=502)
266
    return HttpResponse(status=200)