Statistics
| Branch: | Tag: | Revision:

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

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 astakos.im.api.faults import (
47
    Fault, ItemNotFound, InternalServerError, BadRequest)
48
from astakos.im.settings import (
49
    INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED, QUOTAHOLDER_URL,
50
    PROJECTS_VISIBLE)
51
from astakos.im.forms import FeedbackForm
52
from astakos.im.functions import send_feedback as send_feedback_func
53

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

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

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

    
61

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

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

    
74

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

    
94

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

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

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

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

    
122

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

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

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

    
156

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

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

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

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

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

    
186

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

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

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

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

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

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

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

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

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

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

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

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