aboutsummaryrefslogtreecommitdiffstats
path: root/src/angular-bootstrap.js
blob: 1159b32ec2d40af42faec469433c32c8e185153a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
 * The MIT License
 *
 * Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
(function(previousOnLoad){
  var filename = /^(.*)\/angular-bootstrap.js(#.*)?$/,
      scripts = document.getElementsByTagName("SCRIPT"),
      serverPath,
      match;
  for(var j = 0; j < scripts.length; j++) {
    match = (scripts[j].src || "").match(filename);
    if (match) {
      serverPath = match[1];
    }
  }

  function addScript(file){
    document.write('<script type="text/javascript" src="' + serverPath + file +'"></script>');
  }

  function addCss(file) {
    document.write('<link rel="stylesheet" type="text/css" href="' +
                      serverPath + '/../css' + file  + '"/>');
  }

  addCss("/angular.css");

  addScript("/Angular.js");
  addScript("/JSON.js");
  addScript("/Compiler.js");
  addScript("/Scope.js");
  addScript("/Injector.js");
  addScript("/jqLite.js");
  addScript("/parser.js");
  addScript("/Resource.js");
  addScript("/Browser.js");
  addScript("/sanitizer.js");
  addScript("/AngularPublic.js");

  // Extension points
  addScript("/services.js");
  addScript("/apis.js");
  addScript("/filters.js");
  addScript("/formatters.js");
  addScript("/validators.js");
  addScript("/directives.js");
  addScript("/markups.js");
  addScript("/widgets.js");


  window.onload = function(){
    try {
      if (previousOnLoad) previousOnLoad();
    } catch(e) {}

    //angular-ie-compat.js needs to be pregenerated for development with IE<8
    if (msie<8) addScript('../angular-ie-compat.js');

    angularInit(angularJsConfig(document));
  };

})(window.onload);

"""
The :mod:`request` module provides a :class:`Request` class used to wrap the standard `request`
object received in all the views.

The wrapped request then offers a richer API, in particular :

    - content automatically parsed according to `Content-Type` header,
      and available as :meth:`.DATA<Request.DATA>`
    - full support of PUT method, including support for file uploads
    - form overloading of HTTP method, content type and content
"""
from StringIO import StringIO

from django.http.multipartparser import parse_header
from rest_framework import exceptions
from rest_framework.settings import api_settings


def is_form_media_type(media_type):
    """
    Return True if the media type is a valid form media type.
    """
    base_media_type, params = parse_header(media_type)
    return (base_media_type == 'application/x-www-form-urlencoded' or
            base_media_type == 'multipart/form-data')


class Empty(object):
    """
    Placeholder for unset attributes.
    Cannot use `None`, as that may be a valid value.
    """
    pass


def _hasattr(obj, name):
    return not getattr(obj, name) is Empty


def clone_request(request, method):
    """
    Internal helper method to clone a request, replacing with a different
    HTTP method.  Used for checking permissions against other methods.
    """
    ret = Request(request._request,
                  request.parsers,
                  request.authenticators,
                  request.parser_context)
    ret._data = request._data
    ret._files = request._files
    ret._content_type = request._content_type
    ret._stream = request._stream
    ret._method = method
    if hasattr(request, '_user'):
        ret._user = request._user
    if hasattr(request, '_auth'):
        ret._auth = request._auth
    return ret


class Request(object):
    """
    Wrapper allowing to enhance a standard `HttpRequest` instance.

    Kwargs:
        - request(HttpRequest). The original request instance.
        - parsers_classes(list/tuple). The parsers to use for parsing the
          request content.
        - authentication_classes(list/tuple). The authentications used to try
          authenticating the request's user.
    """

    _METHOD_PARAM = api_settings.FORM_METHOD_OVERRIDE
    _CONTENT_PARAM = api_settings.FORM_CONTENT_OVERRIDE
    _CONTENTTYPE_PARAM = api_settings.FORM_CONTENTTYPE_OVERRIDE

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        self.negotiator = negotiator or self._default_negotiator()
        self.parser_context = parser_context
        self._data = Empty
        self._files = Empty
        self._method = Empty
        self._content_type = Empty
        self._stream = Empty
        self._authenticator = None

        if self.parser_context is None:
            self.parser_context = {}
        self.parser_context['request'] = self

    def _default_negotiator(self):
        return api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS()

    @property
    def method(self):
        """
        Returns the HTTP method.

        This allows the `method` to be overridden by using a hidden `form`
        field on a form POST request.
        """
        if not _hasattr(self, '_method'):
            self._load_method_and_content_type()
        return self._method

    @property
    def content_type(self):
        """
        Returns the content type header.

        This should be used instead of `request.META.get('HTTP_CONTENT_TYPE')`,
        as it allows the content type to be overridden by using a hidden form
        field on a form POST request.
        """
        if not _hasattr(self, '_content_type'):
            self._load_method_and_content_type()
        return self._content_type

    @property
    def stream(self):
        """
        Returns an object that may be used to stream the request content.
        """
        if not _hasattr(self, '_stream'):
            self._load_stream()
        return self._stream

    @property
    def QUERY_PARAMS(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET

    @property
    def DATA(self):
        """
        Parses the request body and returns the data.

        Similar to usual behaviour of `request.POST`, except that it handles
        arbitrary parsers, and also works on methods other than POST (eg PUT).
        """
        if not _hasattr(self, '_data'):
            self._load_data_and_files()
        return self._data

    @property
    def FILES(self):
        """
        Parses the request body and returns any files uploaded in the request.

        Similar to usual behaviour of `request.FILES`, except that it handles
        arbitrary parsers, and also works on methods other than POST (eg PUT).
        """
        if not _hasattr(self, '_files'):
            self._load_data_and_files()
        return self._files

    @property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            self._authenticator, self._user, self._auth = self._authenticate()
        return self._user

    @user.setter
    def user(self, value):
         """
         Sets the user on the current request. This is necessary to maintain
         compatilbility with django.contrib.auth where the user proprety is
         set in the login and logout functions.
         """
         self._user = value

    @property
    def auth(self):
        """
        Returns any non-user authentication information associated with the
        request, such as an authentication token.
        """
        if not hasattr(self, '_auth'):
            self._authenticator, self._user, self._auth = self._authenticate()
        return self._auth

    @auth.setter
    def auth(self, value):
        """
        Sets any non-user authentication information associated with the
        request, such as an authentication token.
        """
        self._auth = value

    @property
    def successful_authenticator(self):
        """
        Return the instance of the authentication instance class that was used
        to authenticate the request, or `None`.
        """
        return self._authenticator

    def _load_data_and_files(self):
        """
        Parses the request content into self.DATA and self.FILES.
        """
        if not _hasattr(self, '_content_type'):
            self._load_method_and_content_type()

        if not _hasattr(self, '_data'):
            self._data, self._files = self._parse()

    def _load_method_and_content_type(self):
        """
        Sets the method and content_type, and then check if they've
        been overridden.
        """
        self._content_type = self.META.get('HTTP_CONTENT_TYPE',
                                           self.META.get('CONTENT_TYPE', ''))
        self._perform_form_overloading()
        # if the HTTP method was not overloaded, we take the raw HTTP method
        if not _hasattr(self, '_method'):
            self._method = self._request.method

    def _load_stream(self):
        """
        Return the content body of the request, as a stream.
        """
        try:
            content_length = int(self.META.get('CONTENT_LENGTH',
                                    self.META.get('HTTP_CONTENT_LENGTH')))
        except (ValueError, TypeError):
            content_length = 0

        if content_length == 0:
            self._stream = None
        elif hasattr(self._request, 'read'):
            self._stream = self._request
        else:
            self._stream = StringIO(self.raw_post_data)

    def _perform_form_overloading(self):
        """
        If this is a form POST request, then we need to check if the method and
        content/content_type have been overridden by setting them in hidden
        form fields or not.
        """

        USE_FORM_OVERLOADING = (
            self._METHOD_PARAM or
            (self._CONTENT_PARAM and self._CONTENTTYPE_PARAM)
        )

        # We only need to use form overloading on form POST requests.
        if (not USE_FORM_OVERLOADING
            or self._request.method != 'POST'
            or not is_form_media_type(self._content_type)):
            return

        # At this point we're committed to parsing the request as form data.
        self._data = self._request.POST
        self._files = self._request.FILES

        # Method overloading - change the method and remove the param from the content.
        if (self._METHOD_PARAM and
            self._METHOD_PARAM in self._data):
            self._method = self._data[self._METHOD_PARAM].upper()

        # Content overloading - modify the content type, and force re-parse.
        if (self._CONTENT_PARAM and
            self._CONTENTTYPE_PARAM and
            self._CONTENT_PARAM in self._data and
            self._CONTENTTYPE_PARAM in self._data):
            self._content_type = self._data[self._CONTENTTYPE_PARAM]
            self._stream = StringIO(self._data[self._CONTENT_PARAM])
            self._data, self._files = (Empty, Empty)

    def _parse(self):
        """
        Parse the request content, returning a two-tuple of (data, files)

        May raise an `UnsupportedMediaType`, or `ParseError` exception.
        """
        stream = self.stream
        media_type = self.content_type

        if stream is None or media_type is None:
            return (None, None)

        parser = self.negotiator.select_parser(self, self.parsers)

        if not parser:
            raise exceptions.UnsupportedMediaType(media_type)

        parsed = parser.parse(stream, media_type, self.parser_context)

        # Parser classes may return the raw data, or a
        # DataAndFiles object.  Unpack the result as required.
        try:
            return (parsed.data, parsed.files)
        except AttributeError:
            return (parsed, None)

    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        Returns a three-tuple of (authenticator, user, authtoken).
        """
        for authenticator in self.authenticators:
            user_auth_tuple = authenticator.authenticate(self)
            if not user_auth_tuple is None:
                user, auth = user_auth_tuple
                return (authenticator, user, auth)
        return self._not_authenticated()

    def _not_authenticated(self):
        """
        Return a three-tuple of (authenticator, user, authtoken), representing
        an unauthenticated request.

        By default this will be (None, AnonymousUser, None).
        """
        if api_settings.UNAUTHENTICATED_USER:
            user = api_settings.UNAUTHENTICATED_USER()
        else:
            user = None

        if api_settings.UNAUTHENTICATED_TOKEN:
            auth = api_settings.UNAUTHENTICATED_TOKEN()
        else:
            auth = None

        return (None, user, auth)

    def __getattr__(self, attr):
        """
        Proxy other attributes to the underlying HttpRequest object.
        """
        return getattr(self._request, attr)