From 4b691c402707775c3048a90531024f3bc5be6f91 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Sep 2012 13:06:27 +0100 Subject: [PATCH] Change package name: djangorestframework -> rest_framework --- .gitignore | 2 +- MANIFEST.in | 4 +- README.md | 4 +- docs/api-guide/authentication.md | 14 +- docs/api-guide/permissions.md | 6 +- docs/api-guide/requests.md | 6 +- docs/api-guide/reverse.md | 6 +- docs/api-guide/settings.md | 28 +- docs/api-guide/status-codes.md | 2 +- docs/api-guide/throttling.md | 14 +- docs/index.md | 12 +- docs/topics/browsable-api.md | 4 +- docs/tutorial/1-serialization.md | 16 +- docs/tutorial/2-requests-and-responses.md | 10 +- docs/tutorial/3-class-based-views.md | 14 +- .../6-resource-orientated-projects.md | 4 +- mkdocs.py | 2 +- rest_framework/__init__.py | 3 + rest_framework/authentication.py | 132 +++ rest_framework/authtoken/__init__.py | 0 .../authtoken/migrations/0001_initial.py | 72 ++ .../authtoken/migrations/__init__.py | 0 rest_framework/authtoken/models.py | 23 + rest_framework/authtoken/views.py | 0 rest_framework/compat.py | 480 ++++++++++ rest_framework/decorators.py | 53 ++ rest_framework/exceptions.py | 79 ++ rest_framework/fields.py | 446 ++++++++++ rest_framework/generics.py | 113 +++ rest_framework/mixins.py | 97 ++ rest_framework/models.py | 1 + rest_framework/negotiation.py | 74 ++ rest_framework/parsers.py | 260 ++++++ rest_framework/permissions.py | 116 +++ rest_framework/renderers.py | 402 +++++++++ rest_framework/request.py | 284 ++++++ rest_framework/resources.py | 87 ++ rest_framework/response.py | 39 + rest_framework/reverse.py | 20 + rest_framework/runtests/__init__.py | 0 rest_framework/runtests/runcoverage.py | 66 ++ rest_framework/runtests/runtests.py | 40 + rest_framework/runtests/settings.py | 118 +++ rest_framework/runtests/urls.py | 7 + rest_framework/serializers.py | 348 ++++++++ rest_framework/settings.py | 125 +++ .../css/bootstrap-tweaks.css | 22 + .../djangorestframework/css/bootstrap.min.css | 841 ++++++++++++++++++ .../djangorestframework/css/prettify.css | 30 + .../static/djangorestframework/css/style.css | 74 ++ .../img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes .../img/glyphicons-halflings.png | Bin 0 -> 12762 bytes .../djangorestframework/js/bootstrap.min.js | 7 + .../js/jquery-1.8.1-min.js | 2 + .../djangorestframework/js/prettify-min.js | 28 + .../rest_framework/css/bootstrap-tweaks.css | 22 + .../rest_framework/css/bootstrap.min.css | 841 ++++++++++++++++++ .../static/rest_framework/css/prettify.css | 30 + .../static/rest_framework/css/style.css | 74 ++ .../img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes .../img/glyphicons-halflings.png | Bin 0 -> 12762 bytes .../static/rest_framework/js/bootstrap.min.js | 7 + .../rest_framework/js/jquery-1.8.1-min.js | 2 + .../static/rest_framework/js/prettify-min.js | 28 + rest_framework/status.py | 52 ++ .../templates/rest_framework/api.html | 3 + .../templates/rest_framework/api.txt | 8 + .../templates/rest_framework/base.html | 214 +++++ .../templates/rest_framework/login.html | 45 + rest_framework/templatetags/__init__.py | 0 rest_framework/templatetags/add_class.py | 40 + .../templatetags/add_query_param.py | 20 + rest_framework/templatetags/optional_login.py | 32 + .../templatetags/urlize_quoted_links.py | 102 +++ rest_framework/tests/__init__.py | 12 + rest_framework/tests/authentication.py | 153 ++++ rest_framework/tests/breadcrumbs.py | 72 ++ rest_framework/tests/description.py | 113 +++ rest_framework/tests/files.py | 34 + rest_framework/tests/methods.py | 0 rest_framework/tests/mixins.py | 285 ++++++ rest_framework/tests/models.py | 28 + rest_framework/tests/modelviews.py | 90 ++ rest_framework/tests/oauthentication.py | 211 +++++ rest_framework/tests/package.py | 11 + rest_framework/tests/parsers.py | 212 +++++ rest_framework/tests/renderers.py | 375 ++++++++ rest_framework/tests/request.py | 252 ++++++ rest_framework/tests/response.py | 177 ++++ rest_framework/tests/reverse.py | 35 + rest_framework/tests/serializer.py | 117 +++ rest_framework/tests/status.py | 12 + rest_framework/tests/testcases.py | 63 ++ rest_framework/tests/throttling.py | 144 +++ rest_framework/tests/validators.py | 329 +++++++ rest_framework/tests/views.py | 128 +++ rest_framework/throttling.py | 217 +++++ rest_framework/urlpatterns.py | 24 + rest_framework/urls.py | 23 + rest_framework/utils/__init__.py | 101 +++ rest_framework/utils/breadcrumbs.py | 34 + rest_framework/utils/encoders.py | 38 + rest_framework/utils/mediatypes.py | 113 +++ rest_framework/views.py | 304 +++++++ setup.py | 12 +- 105 files changed, 9797 insertions(+), 79 deletions(-) create mode 100644 rest_framework/__init__.py create mode 100644 rest_framework/authentication.py create mode 100644 rest_framework/authtoken/__init__.py create mode 100644 rest_framework/authtoken/migrations/0001_initial.py create mode 100644 rest_framework/authtoken/migrations/__init__.py create mode 100644 rest_framework/authtoken/models.py create mode 100644 rest_framework/authtoken/views.py create mode 100644 rest_framework/compat.py create mode 100644 rest_framework/decorators.py create mode 100644 rest_framework/exceptions.py create mode 100644 rest_framework/fields.py create mode 100644 rest_framework/generics.py create mode 100644 rest_framework/mixins.py create mode 100644 rest_framework/models.py create mode 100644 rest_framework/negotiation.py create mode 100644 rest_framework/parsers.py create mode 100644 rest_framework/permissions.py create mode 100644 rest_framework/renderers.py create mode 100644 rest_framework/request.py create mode 100644 rest_framework/resources.py create mode 100644 rest_framework/response.py create mode 100644 rest_framework/reverse.py create mode 100644 rest_framework/runtests/__init__.py create mode 100755 rest_framework/runtests/runcoverage.py create mode 100755 rest_framework/runtests/runtests.py create mode 100644 rest_framework/runtests/settings.py create mode 100644 rest_framework/runtests/urls.py create mode 100644 rest_framework/serializers.py create mode 100644 rest_framework/settings.py create mode 100644 rest_framework/static/djangorestframework/css/bootstrap-tweaks.css create mode 100644 rest_framework/static/djangorestframework/css/bootstrap.min.css create mode 100644 rest_framework/static/djangorestframework/css/prettify.css create mode 100644 rest_framework/static/djangorestframework/css/style.css create mode 100755 rest_framework/static/djangorestframework/img/glyphicons-halflings-white.png create mode 100755 rest_framework/static/djangorestframework/img/glyphicons-halflings.png create mode 100755 rest_framework/static/djangorestframework/js/bootstrap.min.js create mode 100644 rest_framework/static/djangorestframework/js/jquery-1.8.1-min.js create mode 100644 rest_framework/static/djangorestframework/js/prettify-min.js create mode 100644 rest_framework/static/rest_framework/css/bootstrap-tweaks.css create mode 100644 rest_framework/static/rest_framework/css/bootstrap.min.css create mode 100644 rest_framework/static/rest_framework/css/prettify.css create mode 100644 rest_framework/static/rest_framework/css/style.css create mode 100755 rest_framework/static/rest_framework/img/glyphicons-halflings-white.png create mode 100755 rest_framework/static/rest_framework/img/glyphicons-halflings.png create mode 100755 rest_framework/static/rest_framework/js/bootstrap.min.js create mode 100644 rest_framework/static/rest_framework/js/jquery-1.8.1-min.js create mode 100644 rest_framework/static/rest_framework/js/prettify-min.js create mode 100644 rest_framework/status.py create mode 100644 rest_framework/templates/rest_framework/api.html create mode 100644 rest_framework/templates/rest_framework/api.txt create mode 100644 rest_framework/templates/rest_framework/base.html create mode 100644 rest_framework/templates/rest_framework/login.html create mode 100644 rest_framework/templatetags/__init__.py create mode 100644 rest_framework/templatetags/add_class.py create mode 100644 rest_framework/templatetags/add_query_param.py create mode 100644 rest_framework/templatetags/optional_login.py create mode 100644 rest_framework/templatetags/urlize_quoted_links.py create mode 100644 rest_framework/tests/__init__.py create mode 100644 rest_framework/tests/authentication.py create mode 100644 rest_framework/tests/breadcrumbs.py create mode 100644 rest_framework/tests/description.py create mode 100644 rest_framework/tests/files.py create mode 100644 rest_framework/tests/methods.py create mode 100644 rest_framework/tests/mixins.py create mode 100644 rest_framework/tests/models.py create mode 100644 rest_framework/tests/modelviews.py create mode 100644 rest_framework/tests/oauthentication.py create mode 100644 rest_framework/tests/package.py create mode 100644 rest_framework/tests/parsers.py create mode 100644 rest_framework/tests/renderers.py create mode 100644 rest_framework/tests/request.py create mode 100644 rest_framework/tests/response.py create mode 100644 rest_framework/tests/reverse.py create mode 100644 rest_framework/tests/serializer.py create mode 100644 rest_framework/tests/status.py create mode 100644 rest_framework/tests/testcases.py create mode 100644 rest_framework/tests/throttling.py create mode 100644 rest_framework/tests/validators.py create mode 100644 rest_framework/tests/views.py create mode 100644 rest_framework/throttling.py create mode 100644 rest_framework/urlpatterns.py create mode 100644 rest_framework/urls.py create mode 100644 rest_framework/utils/__init__.py create mode 100644 rest_framework/utils/breadcrumbs.py create mode 100644 rest_framework/utils/encoders.py create mode 100644 rest_framework/utils/mediatypes.py create mode 100644 rest_framework/views.py diff --git a/.gitignore b/.gitignore index 27fc33727..96186b465 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ html/ coverage/ build/ dist/ -djangorestframework.egg-info/ +rest_framework.egg-info/ MANIFEST !.gitignore diff --git a/MANIFEST.in b/MANIFEST.in index 5c6a1c578..1c62e2c90 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ -recursive-include djangorestframework/static *.ico *.txt *.css -recursive-include djangorestframework/templates *.txt *.html +recursive-include rest_framework/static *.ico *.txt *.css +recursive-include rest_framework/templates *.txt *.html recursive-include examples .keep *.py *.txt recursive-include docs *.py *.rst *.html *.txt include AUTHORS LICENSE CHANGELOG.rst requirements.txt tox.ini diff --git a/README.md b/README.md index e62e19cea..92490bd47 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ For more information, check out [the documentation][docs], in particular, the tu Install using `pip`... - pip install djangorestframework + pip install rest_framework ...or clone the project from github. @@ -47,7 +47,7 @@ To build the docs. To run the tests. - ./djangorestframework/runtests/runtests.py + ./rest_framework/runtests/runtests.py # Changelog diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 799509466..f24c6a81b 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -28,10 +28,10 @@ The value of `request.user` and `request.auth` for unauthenticated requests can The default authentication policy may be set globally, using the `DEFAULT_AUTHENTICATION` setting. For example. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION': ( - 'djangorestframework.authentication.UserBasicAuthentication', - 'djangorestframework.authentication.SessionAuthentication', + 'rest_framework.authentication.UserBasicAuthentication', + 'rest_framework.authentication.SessionAuthentication', ) } @@ -75,11 +75,11 @@ If successfully authenticated, `BasicAuthentication` provides the following cred This policy uses a simple token-based HTTP Authentication scheme. Token authentication is appropriate for client-server setups, such as native desktop and mobile clients. -To use the `TokenAuthentication` policy, include `djangorestframework.authtoken` in your `INSTALLED_APPS` setting. +To use the `TokenAuthentication` policy, include `rest_framework.authtoken` in your `INSTALLED_APPS` setting. You'll also need to create tokens for your users. - from djangorestframework.authtoken.models import Token + from rest_framework.authtoken.models import Token token = Token.objects.create(user=...) print token.key @@ -91,7 +91,7 @@ For clients to authenticate, the token key should be included in the `Authorizat If successfully authenticated, `TokenAuthentication` provides the following credentials. * `request.user` will be a `django.contrib.auth.models.User` instance. -* `request.auth` will be a `djangorestframework.tokenauth.models.BasicToken` instance. +* `request.auth` will be a `rest_framework.tokenauth.models.BasicToken` instance. **Note:** If you use `TokenAuthentication` in production you must ensure that your API is only available over `https` only. @@ -102,7 +102,7 @@ This policy uses the [OAuth 2.0][oauth] protocol to authenticate requests. OAut If successfully authenticated, `OAuthAuthentication` provides the following credentials. * `request.user` will be a `django.contrib.auth.models.User` instance. -* `request.auth` will be a `djangorestframework.models.OAuthToken` instance. +* `request.auth` will be a `rest_framework.models.OAuthToken` instance. ## SessionAuthentication diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index fafef3054..e0ceb1eac 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -27,9 +27,9 @@ Object level permissions are run by REST framework's generic views when `.get_ob The default permission policy may be set globally, using the `DEFAULT_PERMISSIONS` setting. For example. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_PERMISSIONS': ( - 'djangorestframework.permissions.IsAuthenticated', + 'rest_framework.permissions.IsAuthenticated', ) } @@ -97,4 +97,4 @@ The method should return `True` if the request should be granted access, and `Fa [authentication]: authentication.md [throttling]: throttling.md [contribauth]: https://docs.djangoproject.com/en/1.0/topics/auth/#permissions -[guardian]: https://github.com/lukaszb/django-guardian \ No newline at end of file +[guardian]: https://github.com/lukaszb/django-guardian diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 6746bb20d..b223da80f 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -49,7 +49,7 @@ This allows you to support file uploads from multiple content-types. For exampl `request.parsers` may no longer be altered once `request.DATA`, `request.FILES` or `request.POST` have been accessed. -If you're using the `djangorestframework.views.View` class... **[TODO]** +If you're using the `rest_framework.views.View` class... **[TODO]** ## .stream @@ -63,6 +63,6 @@ You will not typically need to access `request.stream`, unless you're writing a `request.authentication` may no longer be altered once `request.user` or `request.auth` have been accessed. -If you're using the `djangorestframework.views.View` class... **[TODO]** +If you're using the `rest_framework.views.View` class... **[TODO]** -[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion \ No newline at end of file +[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index f3cb0c642..3fa654c09 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -23,8 +23,8 @@ There's no requirement for you to use them, but if you do then the self-describi Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. - from djangorestframework.utils import reverse - from djangorestframework.views import APIView + from rest_framework.utils import reverse + from rest_framework.views import APIView class MyView(APIView): def get(self, request): @@ -40,4 +40,4 @@ Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy] [cite]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 [reverse]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse -[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy \ No newline at end of file +[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 2513928c5..0f66e85ec 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -6,16 +6,16 @@ > > — [The Zen of Python][cite] -Configuration for REST framework is all namespaced inside a single Django setting, named `API_SETTINGS`. +Configuration for REST framework is all namespaced inside a single Django setting, named `REST_FRAMEWORK`. For example your project's `settings.py` file might include something like this: - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_RENDERERS': ( - 'djangorestframework.renderers.YAMLRenderer', + 'rest_framework.renderers.YAMLRenderer', ) 'DEFAULT_PARSERS': ( - 'djangorestframework.parsers.YAMLParser', + 'rest_framework.parsers.YAMLParser', ) } @@ -24,7 +24,7 @@ For example your project's `settings.py` file might include something like this: If you need to access the values of REST framework's API settings in your project, you should use the `api_settings` object. For example. - from djangorestframework.settings import api_settings + from rest_framework.settings import api_settings print api_settings.DEFAULT_AUTHENTICATION @@ -37,9 +37,9 @@ A list or tuple of renderer classes, that determines the default set of renderer Default: ( - 'djangorestframework.renderers.JSONRenderer', - 'djangorestframework.renderers.DocumentingHTMLRenderer' - 'djangorestframework.renderers.TemplateHTMLRenderer' + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.DocumentingHTMLRenderer' + 'rest_framework.renderers.TemplateHTMLRenderer' ) ## DEFAULT_PARSERS @@ -49,8 +49,8 @@ A list or tuple of parser classes, that determines the default set of parsers us Default: ( - 'djangorestframework.parsers.JSONParser', - 'djangorestframework.parsers.FormParser' + 'rest_framework.parsers.JSONParser', + 'rest_framework.parsers.FormParser' ) ## DEFAULT_AUTHENTICATION @@ -60,8 +60,8 @@ A list or tuple of authentication classes, that determines the default set of au Default: ( - 'djangorestframework.authentication.SessionAuthentication', - 'djangorestframework.authentication.UserBasicAuthentication' + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.UserBasicAuthentication' ) ## DEFAULT_PERMISSIONS @@ -80,13 +80,13 @@ Default: `()` **TODO** -Default: `djangorestframework.serializers.ModelSerializer` +Default: `rest_framework.serializers.ModelSerializer` ## DEFAULT_PAGINATION_SERIALIZER **TODO** -Default: `djangorestframework.pagination.PaginationSerializer` +Default: `rest_framework.pagination.PaginationSerializer` ## FORMAT_SUFFIX_KWARG diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index 6693c79f5..401f45ce0 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -8,7 +8,7 @@ Using bare status codes in your responses isn't recommended. REST framework includes a set of named constants that you can use to make more code more obvious and readable. - from djangorestframework import status + from rest_framework import status def empty_view(self): content = {'please move along': 'nothing to see here'} diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 109978011..7861e9baa 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -29,10 +29,10 @@ If any throttle check fails an `exceptions.Throttled` exception will be raised, The default throttling policy may be set globally, using the `DEFAULT_THROTTLES` and `DEFAULT_THROTTLE_RATES` settings. For example. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_THROTTLES': ( - 'djangorestframework.throttles.AnonThrottle', - 'djangorestframework.throttles.UserThrottle', + 'rest_framework.throttles.AnonThrottle', + 'rest_framework.throttles.UserThrottle', ) 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', @@ -95,7 +95,7 @@ For example, multiple user throttle rates could be implemented by using the foll ...and the following settings. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_THROTTLES': ( 'example.throttles.BurstRateThrottle', 'example.throttles.SustainedRateThrottle', @@ -130,9 +130,9 @@ For example, given the following views... ...and the following settings. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_THROTTLES': ( - 'djangorestframework.throttles.ScopedRateThrottle', + 'rest_framework.throttles.ScopedRateThrottle', ) 'DEFAULT_THROTTLE_RATES': { 'contacts': '1000/day', @@ -148,4 +148,4 @@ To create a custom throttle, override `BaseThrottle` and implement `.allow_reque Optionally you may also override the `.wait()` method. If implemented, `.wait()` should return a recomended number of seconds to wait before attempting the next request, or `None`. The `.wait()` method will only be called if `.check_throttle()` has previously returned `False`. -[permissions]: permissions.md \ No newline at end of file +[permissions]: permissions.md diff --git a/docs/index.md b/docs/index.md index 2376d38e3..e7db5dbc7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -40,20 +40,22 @@ Install using `pip`, including any optional packages you want... pip install -r requirements.txt pip install -r optionals.txt -Add `djangorestframework` to your `INSTALLED_APPS`. +Add `rest_framework` to your `INSTALLED_APPS`. INSTALLED_APPS = ( ... - 'djangorestframework', + 'rest_framework', ) If you're intending to use the browserable API you'll want to add REST framework's login and logout views. Add the following to your root `urls.py` file. urlpatterns = patterns('', ... - url(r'^api-auth/', include('djangorestframework.urls', namespace='djangorestframework')) + url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) ) - + +Note that the base URL can be whatever you want, but you must include `rest_framework.urls` with the `rest_framework` namespace. + ## Quickstart **TODO** @@ -110,7 +112,7 @@ Build the docs: Run the tests: - ./djangorestframework/runtests/runtests.py + ./rest_framework/runtests/runtests.py ## License diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md index 6f8920bb2..ed27752f8 100644 --- a/docs/topics/browsable-api.md +++ b/docs/topics/browsable-api.md @@ -4,7 +4,7 @@ API may stand for Application *Programming* Interface, but humans have to be abl ## URLs -If you include fully-qualified URLs in your resource output, they will be 'urlized' and made clickable for easy browsing by humans. The `djangorestframework` package includes a [`reverse`][drfreverse] helper for this purpose. +If you include fully-qualified URLs in your resource output, they will be 'urlized' and made clickable for easy browsing by humans. The `rest_framework` package includes a [`reverse`][drfreverse] helper for this purpose. ## Formats @@ -14,7 +14,7 @@ By default, the API will return the format specified by the headers, which in th ## Customizing -To customize the look-and-feel, create a template called `api.html` and add it to your project, eg: `templates/djangorestframework/api.html`, that extends the `djangorestframework/base.html` template. +To customize the look-and-feel, create a template called `api.html` and add it to your project, eg: `templates/rest_framework/api.html`, that extends the `rest_framework/base.html` template. The included browsable API template is built with [Bootstrap (2.1.1)][bootstrap], making it easy to customize the look-and-feel. diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 34990084c..e3656bd0f 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -45,11 +45,11 @@ The simplest way to get up and running will probably be to use an `sqlite3` data } } -We'll also need to add our new `blog` app and the `djangorestframework` app to `INSTALLED_APPS`. +We'll also need to add our new `blog` app and the `rest_framework` app to `INSTALLED_APPS`. INSTALLED_APPS = ( ... - 'djangorestframework', + 'rest_framework', 'blog' ) @@ -81,7 +81,7 @@ Don't forget to sync the database for the first time. We're going to create a simple Web API that we can use to edit these comment objects with. The first thing we need is a way of serializing and deserializing the objects into representations such as `json`. We do this by declaring serializers, that work very similarly to Django's forms. Create a file in the project named `serializers.py` and add the following. from blog import models - from djangorestframework import serializers + from rest_framework import serializers class CommentSerializer(serializers.Serializer): @@ -114,8 +114,8 @@ Okay, once we've got a few imports out of the way, we'd better create a few comm from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework.renderers import JSONRenderer - from djangorestframework.parsers import JSONParser + from rest_framework.renderers import JSONRenderer + from rest_framework.parsers import JSONParser c1 = Comment(email='leila@example.com', content='nothing to say') c2 = Comment(email='tom@example.com', content='foo bar') @@ -159,8 +159,8 @@ Edit the `blog/views.py` file, and add the following. from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework.renderers import JSONRenderer - from djangorestframework.parsers import JSONParser + from rest_framework.renderers import JSONRenderer + from rest_framework.parsers import JSONParser from django.http import HttpResponse @@ -251,4 +251,4 @@ Our API views don't do anything particularly special at the moment, beyond serve We'll see how we can start to improve things in [part 2 of the tutorial][tut-2]. [virtualenv]: http://www.virtualenv.org/en/latest/index.html -[tut-2]: 2-requests-and-responses.md \ No newline at end of file +[tut-2]: 2-requests-and-responses.md diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 906f11d0b..d889b1e00 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -40,9 +40,9 @@ We don't need our `JSONResponse` class anymore, so go ahead and delete that. On from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework import status - from djangorestframework.decorators import api_view - from djangorestframework.response import Response + from rest_framework import status + from rest_framework.decorators import api_view + from rest_framework.response import Response @api_view(['GET', 'POST']) def comment_root(request): @@ -112,7 +112,7 @@ and Now update the `urls.py` file slightly, to append a set of `format_suffix_patterns` in addition to the existing URLs. from django.conf.urls import patterns, url - from djangorestframework.urlpatterns import format_suffix_patterns + from rest_framework.urlpatterns import format_suffix_patterns urlpatterns = patterns('blogpost.views', url(r'^$', 'comment_root'), @@ -148,4 +148,4 @@ In [tutorial part 3][tut-3], we'll start using class based views, and see how ge [devserver]: http://127.0.0.1:8000/ [browseable-api]: ../topics/browsable-api.md [tut-1]: 1-serialization.md -[tut-3]: 3-class-based-views.md \ No newline at end of file +[tut-3]: 3-class-based-views.md diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 79866f0da..8db293084 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -9,9 +9,9 @@ We'll start by rewriting the root view as a class based view. All this involves from blog.models import Comment from blog.serializers import CommentSerializer from django.http import Http404 - from djangorestframework.views import APIView - from djangorestframework.response import Response - from djangorestframework import status + from rest_framework.views import APIView + from rest_framework.response import Response + from rest_framework import status class CommentRoot(APIView): @@ -68,7 +68,7 @@ That's looking good. Again, it's still pretty similar to the function based vie We'll also need to refactor our URLconf slightly now we're using class based views. from django.conf.urls import patterns, url - from djangorestframework.urlpatterns import format_suffix_patterns + from rest_framework.urlpatterns import format_suffix_patterns from blogpost import views urlpatterns = patterns('', @@ -90,8 +90,8 @@ Let's take a look at how we can compose our views by using the mixin classes. from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework import mixins - from djangorestframework import generics + from rest_framework import mixins + from rest_framework import generics class CommentRoot(mixins.ListModelMixin, mixins.CreateModelMixin, @@ -133,7 +133,7 @@ Using the mixin classes we've rewritten the views to use slightly less code than from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework import generics + from rest_framework import generics class CommentRoot(generics.RootAPIView): diff --git a/docs/tutorial/6-resource-orientated-projects.md b/docs/tutorial/6-resource-orientated-projects.md index 7da409fb6..3c3e7fedc 100644 --- a/docs/tutorial/6-resource-orientated-projects.md +++ b/docs/tutorial/6-resource-orientated-projects.md @@ -50,7 +50,7 @@ The handler methods only get bound to the actions when we define the URLConf. He Right now that hasn't really saved us a lot of code. However, now that we're using Resources rather than Views, we actually don't need to design the urlconf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using `Router` classes. All we need to do is register the appropriate resources with a router, and let it do the rest. Here's our re-wired `urls.py` file. from blog import resources - from djangorestframework.routers import DefaultRouter + from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(resources.BlogPostResource) @@ -73,4 +73,4 @@ We've reached the end of our tutorial. If you want to get more involved in the **Now go build some awesome things.** -[twitter]: https://twitter.com/_tomchristie \ No newline at end of file +[twitter]: https://twitter.com/_tomchristie diff --git a/mkdocs.py b/mkdocs.py index e04cfe0e9..8e1010dbf 100755 --- a/mkdocs.py +++ b/mkdocs.py @@ -24,7 +24,7 @@ else: main_header = '
  • {{ title }}
  • ' sub_header = '
  • {{ title }}
  • ' -code_label = r'\1' +code_label = r'\1' page = open(os.path.join(docs_dir, 'template.html'), 'r').read() diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py new file mode 100644 index 000000000..557f5943f --- /dev/null +++ b/rest_framework/__init__.py @@ -0,0 +1,3 @@ +__version__ = '2.0.0' + +VERSION = __version__ # synonym diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py new file mode 100644 index 000000000..fd5973977 --- /dev/null +++ b/rest_framework/authentication.py @@ -0,0 +1,132 @@ +""" +The :mod:`authentication` module provides a set of pluggable authentication classes. + +Authentication behavior is provided by mixing the :class:`mixins.RequestMixin` class into a :class:`View` class. +""" + +from django.contrib.auth import authenticate +from rest_framework.compat import CsrfViewMiddleware +from rest_framework.authtoken.models import Token +import base64 + + +class BaseAuthentication(object): + """ + All authentication classes should extend BaseAuthentication. + """ + + def authenticate(self, request): + """ + Authenticate the :obj:`request` and return a :obj:`User` or :const:`None`. [*]_ + + .. [*] The authentication context *will* typically be a :obj:`User`, + but it need not be. It can be any user-like object so long as the + permissions classes (see the :mod:`permissions` module) on the view can + handle the object and use it to determine if the request has the required + permissions or not. + + This can be an important distinction if you're implementing some token + based authentication mechanism, where the authentication context + may be more involved than simply mapping to a :obj:`User`. + """ + return None + + +class BasicAuthentication(BaseAuthentication): + """ + Base class for HTTP Basic authentication. + Subclasses should implement `.authenticate_credentials()`. + """ + + def authenticate(self, request): + """ + Returns a `User` if a correct username and password have been supplied + using HTTP Basic authentication. Otherwise returns `None`. + """ + from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError + + if 'HTTP_AUTHORIZATION' in request.META: + auth = request.META['HTTP_AUTHORIZATION'].split() + if len(auth) == 2 and auth[0].lower() == "basic": + try: + auth_parts = base64.b64decode(auth[1]).partition(':') + except TypeError: + return None + + try: + userid, password = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2]) + except DjangoUnicodeDecodeError: + return None + + return self.authenticate_credentials(userid, password) + + def authenticate_credentials(self, userid, password): + """ + Given the Basic authentication userid and password, authenticate + and return a user instance. + """ + raise NotImplementedError('.authenticate_credentials() must be overridden') + + +class UserBasicAuthentication(BasicAuthentication): + def authenticate_credentials(self, userid, password): + """ + Authenticate the userid and password against username and password. + """ + user = authenticate(username=userid, password=password) + if user is not None and user.is_active: + return (user, None) + + +class SessionAuthentication(BaseAuthentication): + """ + Use Django's session framework for authentication. + """ + + def authenticate(self, request): + """ + Returns a :obj:`User` if the request session currently has a logged in user. + Otherwise returns :const:`None`. + """ + user = getattr(request._request, 'user', None) + + if user and user.is_active: + # Enforce CSRF validation for session based authentication. + resp = CsrfViewMiddleware().process_view(request, None, (), {}) + + if resp is None: # csrf passed + return (user, None) + + +class TokenAuthentication(BaseAuthentication): + """ + Simple token based authentication. + + Clients should authenticate by passing the token key in the "Authorization" + HTTP header, prepended with the string "Token ". For example: + + Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a + """ + + model = Token + """ + A custom token model may be used, but must have the following properties. + + * key -- The string identifying the token + * user -- The user to which the token belongs + """ + + def authenticate(self, request): + auth = request.META.get('HTTP_AUTHORIZATION', '').split() + + if len(auth) == 2 and auth[0].lower() == "token": + key = auth[1] + try: + token = self.model.objects.get(key=key) + except self.model.DoesNotExist: + return None + + if token.user.is_active and not getattr(token, 'revoked', False): + return (token.user, token) + +# TODO: OAuthAuthentication diff --git a/rest_framework/authtoken/__init__.py b/rest_framework/authtoken/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/rest_framework/authtoken/migrations/0001_initial.py b/rest_framework/authtoken/migrations/0001_initial.py new file mode 100644 index 000000000..a91006b00 --- /dev/null +++ b/rest_framework/authtoken/migrations/0001_initial.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Token' + db.create_table('authtoken_token', ( + ('key', self.gf('django.db.models.fields.CharField')(max_length=40, primary_key=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('revoked', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + )) + db.send_create_signal('authtoken', ['Token']) + + + def backwards(self, orm): + # Deleting model 'Token' + db.delete_table('authtoken_token') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'authtoken.token': { + 'Meta': {'object_name': 'Token'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}), + 'revoked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['authtoken'] \ No newline at end of file diff --git a/rest_framework/authtoken/migrations/__init__.py b/rest_framework/authtoken/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/rest_framework/authtoken/models.py b/rest_framework/authtoken/models.py new file mode 100644 index 000000000..fd47e6c72 --- /dev/null +++ b/rest_framework/authtoken/models.py @@ -0,0 +1,23 @@ +import uuid +import hmac +from hashlib import sha1 +from django.db import models + + +class Token(models.Model): + """ + The default authorization token model. + """ + key = models.CharField(max_length=40, primary_key=True) + user = models.ForeignKey('auth.User') + revoked = models.BooleanField(default=False) + created = models.DateTimeField(auto_now_add=True) + + def save(self, *args, **kwargs): + if not self.key: + self.key = self.generate_key() + return super(Token, self).save(*args, **kwargs) + + def generate_key(self): + unique = str(uuid.uuid4()) + return hmac.new(unique, digestmod=sha1).hexdigest() diff --git a/rest_framework/authtoken/views.py b/rest_framework/authtoken/views.py new file mode 100644 index 000000000..e69de29bb diff --git a/rest_framework/compat.py b/rest_framework/compat.py new file mode 100644 index 000000000..fb3f67d94 --- /dev/null +++ b/rest_framework/compat.py @@ -0,0 +1,480 @@ +""" +The :mod:`compat` module provides support for backwards compatibility with older versions of django/python. +""" +import django + +# cStringIO only if it's available, otherwise StringIO +try: + import cStringIO as StringIO +except ImportError: + import StringIO + + +# parse_qs from 'urlparse' module unless python 2.5, in which case from 'cgi' +try: + # python >= 2.6 + from urlparse import parse_qs +except ImportError: + # python < 2.6 + from cgi import parse_qs + + +# django.test.client.RequestFactory (Required for Django < 1.3) +try: + from django.test.client import RequestFactory +except ImportError: + from django.test import Client + from django.core.handlers.wsgi import WSGIRequest + + # From: http://djangosnippets.org/snippets/963/ + # Lovely stuff + class RequestFactory(Client): + """ + Class that lets you create mock :obj:`Request` objects for use in testing. + + Usage:: + + rf = RequestFactory() + get_request = rf.get('/hello/') + post_request = rf.post('/submit/', {'foo': 'bar'}) + + This class re-uses the :class:`django.test.client.Client` interface. Of which + you can find the docs here__. + + __ http://www.djangoproject.com/documentation/testing/#the-test-client + + Once you have a `request` object you can pass it to any :func:`view` function, + just as if that :func:`view` had been hooked up using a URLconf. + """ + def request(self, **request): + """ + Similar to parent class, but returns the :obj:`request` object as soon as it + has created it. + """ + environ = { + 'HTTP_COOKIE': self.cookies, + 'PATH_INFO': '/', + 'QUERY_STRING': '', + 'REQUEST_METHOD': 'GET', + 'SCRIPT_NAME': '', + 'SERVER_NAME': 'testserver', + 'SERVER_PORT': 80, + 'SERVER_PROTOCOL': 'HTTP/1.1', + } + environ.update(self.defaults) + environ.update(request) + return WSGIRequest(environ) + +# django.views.generic.View (Django >= 1.3) +try: + from django.views.generic import View + if not hasattr(View, 'head'): + # First implementation of Django class-based views did not include head method + # in base View class - https://code.djangoproject.com/ticket/15668 + class ViewPlusHead(View): + def head(self, request, *args, **kwargs): + return self.get(request, *args, **kwargs) + View = ViewPlusHead + +except ImportError: + from django import http + from django.utils.functional import update_wrapper + # from django.utils.log import getLogger + # from django.utils.decorators import classonlymethod + + # logger = getLogger('django.request') - We'll just drop support for logger if running Django <= 1.2 + # Might be nice to fix this up sometime to allow rest_framework.compat.View to match 1.3's View more closely + + class View(object): + """ + Intentionally simple parent class for all views. Only implements + dispatch-by-method and simple sanity checking. + """ + + http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace'] + + def __init__(self, **kwargs): + """ + Constructor. Called in the URLconf; can contain helpful extra + keyword arguments, and other things. + """ + # Go through keyword arguments, and either save their values to our + # instance, or raise an error. + for key, value in kwargs.iteritems(): + setattr(self, key, value) + + # @classonlymethod - We'll just us classmethod instead if running Django <= 1.2 + @classmethod + def as_view(cls, **initkwargs): + """ + Main entry point for a request-response process. + """ + # sanitize keyword arguments + for key in initkwargs: + if key in cls.http_method_names: + raise TypeError(u"You tried to pass in the %s method name as a " + u"keyword argument to %s(). Don't do that." + % (key, cls.__name__)) + if not hasattr(cls, key): + raise TypeError(u"%s() received an invalid keyword %r" % ( + cls.__name__, key)) + + def view(request, *args, **kwargs): + self = cls(**initkwargs) + return self.dispatch(request, *args, **kwargs) + + # take name and docstring from class + update_wrapper(view, cls, updated=()) + + # and possible attributes set by decorators + # like csrf_exempt from dispatch + update_wrapper(view, cls.dispatch, assigned=()) + return view + + def dispatch(self, request, *args, **kwargs): + # Try to dispatch to the right method; if a method doesn't exist, + # defer to the error handler. Also defer to the error handler if the + # request method isn't on the approved list. + if request.method.lower() in self.http_method_names: + handler = getattr(self, request.method.lower(), self.http_method_not_allowed) + else: + handler = self.http_method_not_allowed + self.request = request + self.args = args + self.kwargs = kwargs + return handler(request, *args, **kwargs) + + def http_method_not_allowed(self, request, *args, **kwargs): + allowed_methods = [m for m in self.http_method_names if hasattr(self, m)] + #logger.warning('Method Not Allowed (%s): %s' % (request.method, request.path), + # extra={ + # 'status_code': 405, + # 'request': self.request + # } + #) + return http.HttpResponseNotAllowed(allowed_methods) + + def head(self, request, *args, **kwargs): + return self.get(request, *args, **kwargs) + +# PUT, DELETE do not require CSRF until 1.4. They should. Make it better. +if django.VERSION >= (1, 4): + from django.middleware.csrf import CsrfViewMiddleware +else: + import hashlib + import re + import random + import logging + import urlparse + + from django.conf import settings + from django.core.urlresolvers import get_callable + + try: + from logging import NullHandler + except ImportError: + class NullHandler(logging.Handler): + def emit(self, record): + pass + + logger = logging.getLogger('django.request') + + if not logger.handlers: + logger.addHandler(NullHandler()) + + def same_origin(url1, url2): + """ + Checks if two URLs are 'same-origin' + """ + p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2) + return p1[0:2] == p2[0:2] + + def constant_time_compare(val1, val2): + """ + Returns True if the two strings are equal, False otherwise. + + The time taken is independent of the number of characters that match. + """ + if len(val1) != len(val2): + return False + result = 0 + for x, y in zip(val1, val2): + result |= ord(x) ^ ord(y) + return result == 0 + + # Use the system (hardware-based) random number generator if it exists. + if hasattr(random, 'SystemRandom'): + randrange = random.SystemRandom().randrange + else: + randrange = random.randrange + _MAX_CSRF_KEY = 18446744073709551616L # 2 << 63 + + REASON_NO_REFERER = "Referer checking failed - no Referer." + REASON_BAD_REFERER = "Referer checking failed - %s does not match %s." + REASON_NO_CSRF_COOKIE = "CSRF cookie not set." + REASON_BAD_TOKEN = "CSRF token missing or incorrect." + + def _get_failure_view(): + """ + Returns the view to be used for CSRF rejections + """ + return get_callable(settings.CSRF_FAILURE_VIEW) + + def _get_new_csrf_key(): + return hashlib.md5("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest() + + def get_token(request): + """ + Returns the the CSRF token required for a POST form. The token is an + alphanumeric value. + + A side effect of calling this function is to make the the csrf_protect + decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie' + header to the outgoing response. For this reason, you may need to use this + function lazily, as is done by the csrf context processor. + """ + request.META["CSRF_COOKIE_USED"] = True + return request.META.get("CSRF_COOKIE", None) + + def _sanitize_token(token): + # Allow only alphanum, and ensure we return a 'str' for the sake of the post + # processing middleware. + token = re.sub('[^a-zA-Z0-9]', '', str(token.decode('ascii', 'ignore'))) + if token == "": + # In case the cookie has been truncated to nothing at some point. + return _get_new_csrf_key() + else: + return token + + class CsrfViewMiddleware(object): + """ + Middleware that requires a present and correct csrfmiddlewaretoken + for POST requests that have a CSRF cookie, and sets an outgoing + CSRF cookie. + + This middleware should be used in conjunction with the csrf_token template + tag. + """ + # The _accept and _reject methods currently only exist for the sake of the + # requires_csrf_token decorator. + def _accept(self, request): + # Avoid checking the request twice by adding a custom attribute to + # request. This will be relevant when both decorator and middleware + # are used. + request.csrf_processing_done = True + return None + + def _reject(self, request, reason): + return _get_failure_view()(request, reason=reason) + + def process_view(self, request, callback, callback_args, callback_kwargs): + + if getattr(request, 'csrf_processing_done', False): + return None + + try: + csrf_token = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME]) + # Use same token next time + request.META['CSRF_COOKIE'] = csrf_token + except KeyError: + csrf_token = None + # Generate token and store it in the request, so it's available to the view. + request.META["CSRF_COOKIE"] = _get_new_csrf_key() + + # Wait until request.META["CSRF_COOKIE"] has been manipulated before + # bailing out, so that get_token still works + if getattr(callback, 'csrf_exempt', False): + return None + + # Assume that anything not defined as 'safe' by RC2616 needs protection. + if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): + if getattr(request, '_dont_enforce_csrf_checks', False): + # Mechanism to turn off CSRF checks for test suite. It comes after + # the creation of CSRF cookies, so that everything else continues to + # work exactly the same (e.g. cookies are sent etc), but before the + # any branches that call reject() + return self._accept(request) + + if request.is_secure(): + # Suppose user visits http://example.com/ + # An active network attacker,(man-in-the-middle, MITM) sends a + # POST form which targets https://example.com/detonate-bomb/ and + # submits it via javascript. + # + # The attacker will need to provide a CSRF cookie and token, but + # that is no problem for a MITM and the session independent + # nonce we are using. So the MITM can circumvent the CSRF + # protection. This is true for any HTTP connection, but anyone + # using HTTPS expects better! For this reason, for + # https://example.com/ we need additional protection that treats + # http://example.com/ as completely untrusted. Under HTTPS, + # Barth et al. found that the Referer header is missing for + # same-domain requests in only about 0.2% of cases or less, so + # we can use strict Referer checking. + referer = request.META.get('HTTP_REFERER') + if referer is None: + logger.warning('Forbidden (%s): %s' % (REASON_NO_REFERER, request.path), + extra={ + 'status_code': 403, + 'request': request, + } + ) + return self._reject(request, REASON_NO_REFERER) + + # Note that request.get_host() includes the port + good_referer = 'https://%s/' % request.get_host() + if not same_origin(referer, good_referer): + reason = REASON_BAD_REFERER % (referer, good_referer) + logger.warning('Forbidden (%s): %s' % (reason, request.path), + extra={ + 'status_code': 403, + 'request': request, + } + ) + return self._reject(request, reason) + + if csrf_token is None: + # No CSRF cookie. For POST requests, we insist on a CSRF cookie, + # and in this way we can avoid all CSRF attacks, including login + # CSRF. + logger.warning('Forbidden (%s): %s' % (REASON_NO_CSRF_COOKIE, request.path), + extra={ + 'status_code': 403, + 'request': request, + } + ) + return self._reject(request, REASON_NO_CSRF_COOKIE) + + # check non-cookie token for match + request_csrf_token = "" + if request.method == "POST": + request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') + + if request_csrf_token == "": + # Fall back to X-CSRFToken, to make things easier for AJAX, + # and possible for PUT/DELETE + request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '') + + if not constant_time_compare(request_csrf_token, csrf_token): + logger.warning('Forbidden (%s): %s' % (REASON_BAD_TOKEN, request.path), + extra={ + 'status_code': 403, + 'request': request, + } + ) + return self._reject(request, REASON_BAD_TOKEN) + + return self._accept(request) + +# timezone support is new in Django 1.4 +try: + from django.utils import timezone +except ImportError: + timezone = None + +# dateparse is ALSO new in Django 1.4 +try: + from django.utils.dateparse import parse_date, parse_datetime +except ImportError: + import datetime + import re + + date_re = re.compile( + r'(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})$' + ) + + datetime_re = re.compile( + r'(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})' + r'[T ](?P\d{1,2}):(?P\d{1,2})' + r'(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?' + r'(?PZ|[+-]\d{1,2}:\d{1,2})?$' + ) + + time_re = re.compile( + r'(?P\d{1,2}):(?P\d{1,2})' + r'(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?' + ) + + def parse_date(value): + match = date_re.match(value) + if match: + kw = dict((k, int(v)) for k, v in match.groupdict().iteritems()) + return datetime.date(**kw) + + def parse_time(value): + match = time_re.match(value) + if match: + kw = match.groupdict() + if kw['microsecond']: + kw['microsecond'] = kw['microsecond'].ljust(6, '0') + kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None) + return datetime.time(**kw) + + def parse_datetime(value): + """Parse datetime, but w/o the timezone awareness in 1.4""" + match = datetime_re.match(value) + if match: + kw = match.groupdict() + if kw['microsecond']: + kw['microsecond'] = kw['microsecond'].ljust(6, '0') + kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None) + return datetime.datetime(**kw) + +# Markdown is optional +try: + import markdown + + def apply_markdown(text): + """ + Simple wrapper around :func:`markdown.markdown` to set the base level + of '#' style headers to

    . + """ + + extensions = ['headerid(level=2)'] + safe_mode = False, + md = markdown.Markdown(extensions=extensions, safe_mode=safe_mode) + return md.convert(text) + +except ImportError: + apply_markdown = None + + +# Yaml is optional +try: + import yaml +except ImportError: + yaml = None + + +import unittest +try: + import unittest.skip +except ImportError: # python < 2.7 + from unittest import TestCase + import functools + + def skip(reason): + # Pasted from py27/lib/unittest/case.py + """ + Unconditionally skip a test. + """ + def decorator(test_item): + if not (isinstance(test_item, type) and issubclass(test_item, TestCase)): + @functools.wraps(test_item) + def skip_wrapper(*args, **kwargs): + pass + test_item = skip_wrapper + + test_item.__unittest_skip__ = True + test_item.__unittest_skip_why__ = reason + return test_item + return decorator + + unittest.skip = skip + + +# xml.etree.parse only throws ParseError for python >= 2.7 +try: + from xml.etree import ParseError as ETParseError +except ImportError: # python < 2.7 + ETParseError = None diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py new file mode 100644 index 000000000..9836c9660 --- /dev/null +++ b/rest_framework/decorators.py @@ -0,0 +1,53 @@ +from functools import wraps +from django.http import Http404 +from django.utils.decorators import available_attrs +from django.core.exceptions import PermissionDenied +from rest_framework import exceptions +from rest_framework import status +from rest_framework.response import Response +from rest_framework.request import Request +from rest_framework.settings import api_settings + + +def api_view(allowed_methods): + """ + Decorator for function based views. + + @api_view(['GET', 'POST']) + def my_view(request): + # request will be an instance of `Request` + # `Response` objects will have .request set automatically + # APIException instances will be handled + """ + allowed_methods = [method.upper() for method in allowed_methods] + + def decorator(func): + @wraps(func, assigned=available_attrs(func)) + def inner(request, *args, **kwargs): + try: + + request = Request(request) + + if request.method not in allowed_methods: + raise exceptions.MethodNotAllowed(request.method) + + response = func(request, *args, **kwargs) + + if isinstance(response, Response): + response.request = request + if api_settings.FORMAT_SUFFIX_KWARG: + response.format = kwargs.get(api_settings.FORMAT_SUFFIX_KWARG, None) + return response + + except exceptions.APIException as exc: + return Response({'detail': exc.detail}, status=exc.status_code) + + except Http404 as exc: + return Response({'detail': 'Not found'}, + status=status.HTTP_404_NOT_FOUND) + + except PermissionDenied as exc: + return Response({'detail': 'Permission denied'}, + status=status.HTTP_403_FORBIDDEN) + return inner + return decorator diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py new file mode 100644 index 000000000..e7836ecd3 --- /dev/null +++ b/rest_framework/exceptions.py @@ -0,0 +1,79 @@ +""" +Handled exceptions raised by REST framework. + +In addition Django's built in 403 and 404 exceptions are handled. +(`django.http.Http404` and `django.core.exceptions.PermissionDenied`) +""" +from rest_framework import status + + +class APIException(Exception): + """ + Base class for REST framework exceptions. + Subclasses should provide `.status_code` and `.detail` properties. + """ + pass + + +class ParseError(APIException): + status_code = status.HTTP_400_BAD_REQUEST + default_detail = 'Malformed request.' + + def __init__(self, detail=None): + self.detail = detail or self.default_detail + + +class PermissionDenied(APIException): + status_code = status.HTTP_403_FORBIDDEN + default_detail = 'You do not have permission to perform this action.' + + def __init__(self, detail=None): + self.detail = detail or self.default_detail + + +class InvalidFormat(APIException): + status_code = status.HTTP_404_NOT_FOUND + default_detail = "Format suffix '.%s' not found." + + def __init__(self, format, detail=None): + self.detail = (detail or self.default_detail) % format + + +class MethodNotAllowed(APIException): + status_code = status.HTTP_405_METHOD_NOT_ALLOWED + default_detail = "Method '%s' not allowed." + + def __init__(self, method, detail=None): + self.detail = (detail or self.default_detail) % method + + +class NotAcceptable(APIException): + status_code = status.HTTP_406_NOT_ACCEPTABLE + default_detail = "Could not satisfy the request's Accept header" + + def __init__(self, detail=None, available_renderers=None): + self.detail = detail or self.default_detail + self.available_renderers = available_renderers + + +class UnsupportedMediaType(APIException): + status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE + default_detail = "Unsupported media type '%s' in request." + + def __init__(self, media_type, detail=None): + self.detail = (detail or self.default_detail) % media_type + + +class Throttled(APIException): + status_code = status.HTTP_429_TOO_MANY_REQUESTS + default_detail = "Request was throttled." + extra_detail = "Expected available in %d second%s." + + def __init__(self, wait=None, detail=None): + import math + self.wait = wait and math.ceil(wait) or None + if wait is not None: + format = detail or self.default_detail + self.extra_detail + self.detail = format % (self.wait, self.wait != 1 and 's' or '') + else: + self.detail = detail or self.default_detail diff --git a/rest_framework/fields.py b/rest_framework/fields.py new file mode 100644 index 000000000..2d44a1e15 --- /dev/null +++ b/rest_framework/fields.py @@ -0,0 +1,446 @@ +import copy +import datetime +import inspect +import warnings + +from django.core import validators +from django.core.exceptions import ValidationError +from django.conf import settings +from django.db import DEFAULT_DB_ALIAS +from django.db.models.related import RelatedObject +from django.utils.encoding import is_protected_type, smart_unicode +from django.utils.translation import ugettext_lazy as _ +from rest_framework.compat import parse_date, parse_datetime +from rest_framework.compat import timezone + + +def is_simple_callable(obj): + """ + True if the object is a callable that takes no arguments. + """ + return ( + (inspect.isfunction(obj) and not inspect.getargspec(obj)[0]) or + (inspect.ismethod(obj) and len(inspect.getargspec(obj)[0]) <= 1) + ) + + +class Field(object): + creation_counter = 0 + default_validators = [] + default_error_messages = { + 'required': _('This field is required.'), + 'invalid': _('Invalid value.'), + } + empty = '' + + def __init__(self, source=None, readonly=False, required=None, + validators=[], error_messages=None): + self.parent = None + + self.creation_counter = Field.creation_counter + Field.creation_counter += 1 + + self.source = source + self.readonly = readonly + self.required = not(readonly) + + messages = {} + for c in reversed(self.__class__.__mro__): + messages.update(getattr(c, 'default_error_messages', {})) + messages.update(error_messages or {}) + self.error_messages = messages + + self.validators = self.default_validators + validators + + def initialize(self, parent, model_field=None): + """ + Called to set up a field prior to field_to_native or field_from_native. + + parent - The parent serializer. + model_field - The model field this field corrosponds to, if one exists. + """ + self.parent = parent + self.root = parent.root or parent + self.context = self.root.context + if model_field: + self.model_field = model_field + + def validate(self, value): + pass + # if value in validators.EMPTY_VALUES and self.required: + # raise ValidationError(self.error_messages['required']) + + def run_validators(self, value): + if value in validators.EMPTY_VALUES: + return + errors = [] + for v in self.validators: + try: + v(value) + except ValidationError as e: + if hasattr(e, 'code') and e.code in self.error_messages: + message = self.error_messages[e.code] + if e.params: + message = message % e.params + errors.append(message) + else: + errors.extend(e.messages) + if errors: + raise ValidationError(errors) + + def field_from_native(self, data, field_name, into): + """ + Given a dictionary and a field name, updates the dictionary `into`, + with the field and it's deserialized value. + """ + if self.readonly: + return + + try: + native = data[field_name] + except KeyError: + return # TODO Consider validation behaviour, 'required' opt etc... + + value = self.from_native(native) + if self.source == '*': + if value: + into.update(value) + else: + self.validate(value) + self.run_validators(value) + into[self.source or field_name] = value + + def from_native(self, value): + """ + Reverts a simple representation back to the field's value. + """ + if hasattr(self, 'model_field'): + try: + return self.model_field.rel.to._meta.get_field(self.model_field.rel.field_name).to_python(value) + except: + return self.model_field.to_python(value) + return value + + def field_to_native(self, obj, field_name): + """ + Given and object and a field name, returns the value that should be + serialized for that field. + """ + if obj is None: + return self.empty + + if self.source == '*': + return self.to_native(obj) + + self.obj = obj # Need to hang onto this in the case of model fields + if hasattr(self, 'model_field'): + return self.to_native(self.model_field._get_val_from_obj(obj)) + + return self.to_native(getattr(obj, self.source or field_name)) + + def to_native(self, value): + """ + Converts the field's value into it's simple representation. + """ + if is_simple_callable(value): + value = value() + + if is_protected_type(value): + return value + elif hasattr(self, 'model_field'): + return self.model_field.value_to_string(self.obj) + return smart_unicode(value) + + def attributes(self): + """ + Returns a dictionary of attributes to be used when serializing to xml. + """ + try: + return { + "type": self.model_field.get_internal_type() + } + except AttributeError: + return {} + + +class RelatedField(Field): + """ + A base class for model related fields or related managers. + + Subclass this and override `convert` to define custom behaviour when + serializing related objects. + """ + + def field_to_native(self, obj, field_name): + obj = getattr(obj, field_name) + if obj.__class__.__name__ in ('RelatedManager', 'ManyRelatedManager'): + return [self.to_native(item) for item in obj.all()] + return self.to_native(obj) + + def attributes(self): + try: + return { + "rel": self.model_field.rel.__class__.__name__, + "to": smart_unicode(self.model_field.rel.to._meta) + } + except AttributeError: + return {} + + +class PrimaryKeyRelatedField(RelatedField): + """ + Serializes a model related field or related manager to a pk value. + """ + + # Note the we use ModelRelatedField's implementation, as we want to get the + # raw database value directly, since that won't involve another + # database lookup. + # + # An alternative implementation would simply be this... + # + # class PrimaryKeyRelatedField(RelatedField): + # def to_native(self, obj): + # return obj.pk + + def to_native(self, pk): + """ + Simply returns the object's pk. You can subclass this method to + provide different serialization behavior of the pk. + (For example returning a URL based on the model's pk.) + """ + return pk + + def field_to_native(self, obj, field_name): + try: + obj = obj.serializable_value(field_name) + except AttributeError: + field = obj._meta.get_field_by_name(field_name)[0] + obj = getattr(obj, field_name) + if obj.__class__.__name__ == 'RelatedManager': + return [self.to_native(item.pk) for item in obj.all()] + elif isinstance(field, RelatedObject): + return self.to_native(obj.pk) + raise + if obj.__class__.__name__ == 'ManyRelatedManager': + return [self.to_native(item.pk) for item in obj.all()] + return self.to_native(obj) + + def field_from_native(self, data, field_name, into): + value = data.get(field_name) + if hasattr(value, '__iter__'): + into[field_name] = [self.from_native(item) for item in value] + else: + into[field_name + '_id'] = self.from_native(value) + + +class NaturalKeyRelatedField(RelatedField): + """ + Serializes a model related field or related manager to a natural key value. + """ + is_natural_key = True # XML renderer handles these differently + + def to_native(self, obj): + if hasattr(obj, 'natural_key'): + return obj.natural_key() + return obj + + def field_from_native(self, data, field_name, into): + value = data.get(field_name) + into[self.model_field.attname] = self.from_native(value) + + def from_native(self, value): + # TODO: Support 'using' : db = options.pop('using', DEFAULT_DB_ALIAS) + manager = self.model_field.rel.to._default_manager + manager = manager.db_manager(DEFAULT_DB_ALIAS) + return manager.get_by_natural_key(*value).pk + + +class BooleanField(Field): + default_error_messages = { + 'invalid': _(u"'%s' value must be either True or False."), + } + + def from_native(self, value): + if value in (True, False): + # if value is 1 or 0 than it's equal to True or False, but we want + # to return a true bool for semantic reasons. + return bool(value) + if value in ('t', 'True', '1'): + return True + if value in ('f', 'False', '0'): + return False + raise ValidationError(self.error_messages['invalid'] % value) + + +class CharField(Field): + def __init__(self, max_length=None, min_length=None, *args, **kwargs): + self.max_length, self.min_length = max_length, min_length + super(CharField, self).__init__(*args, **kwargs) + if min_length is not None: + self.validators.append(validators.MinLengthValidator(min_length)) + if max_length is not None: + self.validators.append(validators.MaxLengthValidator(max_length)) + + def from_native(self, value): + if isinstance(value, basestring) or value is None: + return value + return smart_unicode(value) + + +class EmailField(CharField): + default_error_messages = { + 'invalid': _('Enter a valid e-mail address.'), + } + default_validators = [validators.validate_email] + + def from_native(self, value): + return super(EmailField, self).from_native(value).strip() + + def __deepcopy__(self, memo): + result = copy.copy(self) + memo[id(self)] = result + #result.widget = copy.deepcopy(self.widget, memo) + result.validators = self.validators[:] + return result + + +class DateField(Field): + default_error_messages = { + 'invalid': _(u"'%s' value has an invalid date format. It must be " + u"in YYYY-MM-DD format."), + 'invalid_date': _(u"'%s' value has the correct format (YYYY-MM-DD) " + u"but it is an invalid date."), + } + empty = None + + def from_native(self, value): + if value is None: + return value + if isinstance(value, datetime.datetime): + if timezone and settings.USE_TZ and timezone.is_aware(value): + # Convert aware datetimes to the default time zone + # before casting them to dates (#17742). + default_timezone = timezone.get_default_timezone() + value = timezone.make_naive(value, default_timezone) + return value.date() + if isinstance(value, datetime.date): + return value + + try: + parsed = parse_date(value) + if parsed is not None: + return parsed + except ValueError: + msg = self.error_messages['invalid_date'] % value + raise ValidationError(msg) + + msg = self.error_messages['invalid'] % value + raise ValidationError(msg) + + +class DateTimeField(Field): + default_error_messages = { + 'invalid': _(u"'%s' value has an invalid format. It must be in " + u"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."), + 'invalid_date': _(u"'%s' value has the correct format " + u"(YYYY-MM-DD) but it is an invalid date."), + 'invalid_datetime': _(u"'%s' value has the correct format " + u"(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) " + u"but it is an invalid date/time."), + } + empty = None + + def from_native(self, value): + if value is None: + return value + if isinstance(value, datetime.datetime): + return value + if isinstance(value, datetime.date): + value = datetime.datetime(value.year, value.month, value.day) + if settings.USE_TZ: + # For backwards compatibility, interpret naive datetimes in + # local time. This won't work during DST change, but we can't + # do much about it, so we let the exceptions percolate up the + # call stack. + warnings.warn(u"DateTimeField received a naive datetime (%s)" + u" while time zone support is active." % value, + RuntimeWarning) + default_timezone = timezone.get_default_timezone() + value = timezone.make_aware(value, default_timezone) + return value + + try: + parsed = parse_datetime(value) + if parsed is not None: + return parsed + except ValueError: + msg = self.error_messages['invalid_datetime'] % value + raise ValidationError(msg) + + try: + parsed = parse_date(value) + if parsed is not None: + return datetime.datetime(parsed.year, parsed.month, parsed.day) + except ValueError: + msg = self.error_messages['invalid_date'] % value + raise ValidationError(msg) + + msg = self.error_messages['invalid'] % value + raise ValidationError(msg) + + +class IntegerField(Field): + default_error_messages = { + 'invalid': _('Enter a whole number.'), + 'max_value': _('Ensure this value is less than or equal to %(limit_value)s.'), + 'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'), + } + + def __init__(self, max_value=None, min_value=None, *args, **kwargs): + self.max_value, self.min_value = max_value, min_value + super(IntegerField, self).__init__(*args, **kwargs) + + if max_value is not None: + self.validators.append(validators.MaxValueValidator(max_value)) + if min_value is not None: + self.validators.append(validators.MinValueValidator(min_value)) + + def from_native(self, value): + if value in validators.EMPTY_VALUES: + return None + try: + value = int(str(value)) + except (ValueError, TypeError): + raise ValidationError(self.error_messages['invalid']) + return value + + +class FloatField(Field): + default_error_messages = { + 'invalid': _("'%s' value must be a float."), + } + + def from_native(self, value): + if value is None: + return value + try: + return float(value) + except (TypeError, ValueError): + msg = self.error_messages['invalid'] % value + raise ValidationError(msg) + +# field_mapping = { +# models.AutoField: IntegerField, +# models.BooleanField: BooleanField, +# models.CharField: CharField, +# models.DateTimeField: DateTimeField, +# models.DateField: DateField, +# models.BigIntegerField: IntegerField, +# models.IntegerField: IntegerField, +# models.PositiveIntegerField: IntegerField, +# models.FloatField: FloatField +# } + + +# def modelfield_to_serializerfield(field): +# return field_mapping.get(type(field), Field) diff --git a/rest_framework/generics.py b/rest_framework/generics.py new file mode 100644 index 000000000..106c99925 --- /dev/null +++ b/rest_framework/generics.py @@ -0,0 +1,113 @@ +""" +Generic views that provide commmonly needed behaviour. +""" + +from rest_framework import views, mixins +from django.views.generic.detail import SingleObjectMixin +from django.views.generic.list import MultipleObjectMixin + + +### Base classes for the generic views ### + +class BaseView(views.APIView): + """ + Base class for all other generic views. + """ + serializer_class = None + + def get_serializer(self, data=None, files=None, instance=None): + # TODO: add support for files + # TODO: add support for seperate serializer/deserializer + context = { + 'request': self.request, + 'format': self.kwargs.get('format', None) + } + return self.serializer_class(data, instance=instance, context=context) + + +class MultipleObjectBaseView(MultipleObjectMixin, BaseView): + """ + Base class for generic views onto a queryset. + """ + pass + + +class SingleObjectBaseView(SingleObjectMixin, BaseView): + """ + Base class for generic views onto a model instance. + """ + + def get_object(self): + """ + Override default to add support for object-level permissions. + """ + obj = super(SingleObjectBaseView, self).get_object() + self.check_permissions(self.request, obj) + return obj + + +### Concrete view classes that provide method handlers ### +### by composing the mixin classes with a base view. ### + +class ListAPIView(mixins.ListModelMixin, + mixins.MetadataMixin, + MultipleObjectBaseView): + """ + Concrete view for listing a queryset. + """ + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def options(self, request, *args, **kwargs): + return self.metadata(request, *args, **kwargs) + + +class RootAPIView(mixins.ListModelMixin, + mixins.CreateModelMixin, + mixins.MetadataMixin, + MultipleObjectBaseView): + """ + Concrete view for listing a queryset or creating a model instance. + """ + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + return self.create(request, *args, **kwargs) + + def options(self, request, *args, **kwargs): + return self.metadata(request, *args, **kwargs) + + +class DetailAPIView(mixins.RetrieveModelMixin, + mixins.MetadataMixin, + SingleObjectBaseView): + """ + Concrete view for retrieving a model instance. + """ + def get(self, request, *args, **kwargs): + return self.retrieve(request, *args, **kwargs) + + def options(self, request, *args, **kwargs): + return self.metadata(request, *args, **kwargs) + + +class InstanceAPIView(mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + mixins.MetadataMixin, + SingleObjectBaseView): + """ + Concrete view for retrieving, updating or deleting a model instance. + """ + def get(self, request, *args, **kwargs): + return self.retrieve(request, *args, **kwargs) + + def put(self, request, *args, **kwargs): + return self.update(request, *args, **kwargs) + + def delete(self, request, *args, **kwargs): + return self.destroy(request, *args, **kwargs) + + def options(self, request, *args, **kwargs): + return self.metadata(request, *args, **kwargs) diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py new file mode 100644 index 000000000..3dd4a0475 --- /dev/null +++ b/rest_framework/mixins.py @@ -0,0 +1,97 @@ +""" +Basic building blocks for generic class based views. + +We don't bind behaviour to http method handlers yet, +which allows mixin classes to be composed in interesting ways. + +Eg. Use mixins to build a Resource class, and have a Router class + perform the binding of http methods to actions for us. +""" +from rest_framework import status +from rest_framework.response import Response + + +class CreateModelMixin(object): + """ + Create a model instance. + Should be mixed in with any `BaseView`. + """ + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.DATA) + if serializer.is_valid(): + self.object = serializer.object + self.object.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class ListModelMixin(object): + """ + List a queryset. + Should be mixed in with `MultipleObjectBaseView`. + """ + def list(self, request, *args, **kwargs): + self.object_list = self.get_queryset() + serializer = self.get_serializer(instance=self.object_list) + return Response(serializer.data) + + +class RetrieveModelMixin(object): + """ + Retrieve a model instance. + Should be mixed in with `SingleObjectBaseView`. + """ + def retrieve(self, request, *args, **kwargs): + self.object = self.get_object() + serializer = self.get_serializer(instance=self.object) + return Response(serializer.data) + + +class UpdateModelMixin(object): + """ + Update a model instance. + Should be mixed in with `SingleObjectBaseView`. + """ + def update(self, request, *args, **kwargs): + self.object = self.get_object() + serializer = self.get_serializer(data=request.DATA, instance=self.object) + if serializer.is_valid(): + self.object = serializer.object + self.object.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class DestroyModelMixin(object): + """ + Destroy a model instance. + Should be mixed in with `SingleObjectBaseView`. + """ + def destroy(self, request, *args, **kwargs): + self.object = self.get_object() + self.object.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + + +class MetadataMixin(object): + """ + Return a dicitonary of view metadata. + Should be mixed in with any `BaseView`. + + This mixin is typically used for the HTTP 'OPTIONS' method. + """ + def metadata(self, request, *args, **kwargs): + content = { + 'name': self.get_name(), + 'description': self.get_description(), + 'renders': self._rendered_media_types, + 'parses': self._parsed_media_types, + } + # TODO: Add 'fields', from serializer info. + # form = self.get_bound_form() + # if form is not None: + # field_name_types = {} + # for name, field in form.fields.iteritems(): + # field_name_types[name] = field.__class__.__name__ + # content['fields'] = field_name_types + return Response(content, status=status.HTTP_200_OK) diff --git a/rest_framework/models.py b/rest_framework/models.py new file mode 100644 index 000000000..5b53a5264 --- /dev/null +++ b/rest_framework/models.py @@ -0,0 +1 @@ +# Just to keep things like ./manage.py test happy diff --git a/rest_framework/negotiation.py b/rest_framework/negotiation.py new file mode 100644 index 000000000..0d3b368cb --- /dev/null +++ b/rest_framework/negotiation.py @@ -0,0 +1,74 @@ +from rest_framework import exceptions +from rest_framework.settings import api_settings +from rest_framework.utils.mediatypes import order_by_precedence + + +class BaseContentNegotiation(object): + def negotiate(self, request, renderers, format=None, force=False): + raise NotImplementedError('.negotiate() must be implemented') + + +class DefaultContentNegotiation(object): + settings = api_settings + + def negotiate(self, request, renderers, format=None, force=False): + """ + Given a request and a list of renderers, return a two-tuple of: + (renderer, media type). + + If force is set, then suppress exceptions, and forcibly return a + fallback renderer and media_type. + """ + try: + return self.unforced_negotiate(request, renderers, format) + except (exceptions.InvalidFormat, exceptions.NotAcceptable): + if force: + return (renderers[0], renderers[0].media_type) + raise + + def unforced_negotiate(self, request, renderers, format=None): + """ + As `.negotiate()`, but does not take the optional `force` agument, + or suppress exceptions. + """ + # Allow URL style format override. eg. "?format=json + format = format or request.GET.get(self.settings.URL_FORMAT_OVERRIDE) + + if format: + renderers = self.filter_renderers(renderers, format) + + accepts = self.get_accept_list(request) + + # Check the acceptable media types against each renderer, + # attempting more specific media types first + # NB. The inner loop here isn't as bad as it first looks :) + # Worst case is we're looping over len(accept_list) * len(self.renderers) + for media_type_set in order_by_precedence(accepts): + for renderer in renderers: + for media_type in media_type_set: + if renderer.can_handle_media_type(media_type): + return renderer, media_type + + raise exceptions.NotAcceptable(available_renderers=renderers) + + def filter_renderers(self, renderers, format): + """ + If there is a '.json' style format suffix, filter the renderers + so that we only negotiation against those that accept that format. + """ + renderers = [renderer for renderer in renderers + if renderer.can_handle_format(format)] + if not renderers: + raise exceptions.InvalidFormat(format) + return renderers + + def get_accept_list(self, request): + """ + Given the incoming request, return a tokenised list of media + type strings. + + Allows URL style accept override. eg. "?accept=application/json" + """ + header = request.META.get('HTTP_ACCEPT', '*/*') + header = request.GET.get(self.settings.URL_ACCEPT_OVERRIDE, header) + return [token.strip() for token in header.split(',')] diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py new file mode 100644 index 000000000..b34231319 --- /dev/null +++ b/rest_framework/parsers.py @@ -0,0 +1,260 @@ +""" +Django supports parsing the content of an HTTP request, but only for form POST requests. +That behavior is sufficient for dealing with standard HTML forms, but it doesn't map well +to general HTTP requests. + +We need a method to be able to: + +1.) Determine the parsed content on a request for methods other than POST (eg typically also PUT) + +2.) Determine the parsed content on a request for media types other than application/x-www-form-urlencoded + and multipart/form-data. (eg also handle multipart/json) +""" + +from django.http import QueryDict +from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser +from django.http.multipartparser import MultiPartParserError +from django.utils import simplejson as json +from rest_framework.compat import yaml +from rest_framework.exceptions import ParseError +from rest_framework.utils.mediatypes import media_type_matches +from xml.etree import ElementTree as ET +from rest_framework.compat import ETParseError +from xml.parsers.expat import ExpatError +import datetime +import decimal +from io import BytesIO + + +__all__ = ( + 'BaseParser', + 'JSONParser', + 'PlainTextParser', + 'FormParser', + 'MultiPartParser', + 'YAMLParser', + 'XMLParser' +) + + +class DataAndFiles(object): + def __init__(self, data, files): + self.data = data + self.files = files + + +class BaseParser(object): + """ + All parsers should extend :class:`BaseParser`, specifying a :attr:`media_type` attribute, + and overriding the :meth:`parse` method. + """ + + media_type = None + + def can_handle_request(self, content_type): + """ + Returns :const:`True` if this parser is able to deal with the given *content_type*. + + The default implementation for this function is to check the *content_type* + argument against the :attr:`media_type` attribute set on the class to see if + they match. + + This may be overridden to provide for other behavior, but typically you'll + instead want to just set the :attr:`media_type` attribute on the class. + """ + return media_type_matches(self.media_type, content_type) + + def parse(self, string_or_stream, **opts): + """ + The main entry point to parsers. This is a light wrapper around + `parse_stream`, that instead handles both string and stream objects. + """ + if isinstance(string_or_stream, basestring): + stream = BytesIO(string_or_stream) + else: + stream = string_or_stream + return self.parse_stream(stream, **opts) + + def parse_stream(self, stream, **opts): + """ + Given a *stream* to read from, return the deserialized output. + Should return parsed data, or a DataAndFiles object consisting of the + parsed data and files. + """ + raise NotImplementedError(".parse_stream() must be overridden.") + + +class JSONParser(BaseParser): + """ + Parses JSON-serialized data. + """ + + media_type = 'application/json' + + def parse_stream(self, stream, **opts): + """ + Returns a 2-tuple of `(data, files)`. + + `data` will be an object which is the parsed content of the response. + `files` will always be `None`. + """ + try: + return json.load(stream) + except ValueError, exc: + raise ParseError('JSON parse error - %s' % unicode(exc)) + + +class YAMLParser(BaseParser): + """ + Parses YAML-serialized data. + """ + + media_type = 'application/yaml' + + def parse_stream(self, stream, **opts): + """ + Returns a 2-tuple of `(data, files)`. + + `data` will be an object which is the parsed content of the response. + `files` will always be `None`. + """ + try: + return yaml.safe_load(stream) + except (ValueError, yaml.parser.ParserError), exc: + raise ParseError('YAML parse error - %s' % unicode(exc)) + + +class PlainTextParser(BaseParser): + """ + Plain text parser. + """ + + media_type = 'text/plain' + + def parse_stream(self, stream, **opts): + """ + Returns a 2-tuple of `(data, files)`. + + `data` will simply be a string representing the body of the request. + `files` will always be `None`. + """ + return stream.read() + + +class FormParser(BaseParser): + """ + Parser for form data. + """ + + media_type = 'application/x-www-form-urlencoded' + + def parse_stream(self, stream, **opts): + """ + Returns a 2-tuple of `(data, files)`. + + `data` will be a :class:`QueryDict` containing all the form parameters. + `files` will always be :const:`None`. + """ + data = QueryDict(stream.read()) + return data + + +class MultiPartParser(BaseParser): + """ + Parser for multipart form data, which may include file data. + """ + + media_type = 'multipart/form-data' + + def parse_stream(self, stream, **opts): + """ + Returns a DataAndFiles object. + + `.data` will be a `QueryDict` containing all the form parameters. + `.files` will be a `QueryDict` containing all the form files. + """ + meta = opts['meta'] + upload_handlers = opts['upload_handlers'] + try: + parser = DjangoMultiPartParser(meta, stream, upload_handlers) + data, files = parser.parse() + return DataAndFiles(data, files) + except MultiPartParserError, exc: + raise ParseError('Multipart form parse error - %s' % unicode(exc)) + + +class XMLParser(BaseParser): + """ + XML parser. + """ + + media_type = 'application/xml' + + def parse_stream(self, stream, **opts): + try: + tree = ET.parse(stream) + except (ExpatError, ETParseError, ValueError), exc: + raise ParseError('XML parse error - %s' % unicode(exc)) + data = self._xml_convert(tree.getroot()) + + return data + + def _xml_convert(self, element): + """ + convert the xml `element` into the corresponding python object + """ + + children = element.getchildren() + + if len(children) == 0: + return self._type_convert(element.text) + else: + # if the fist child tag is list-item means all children are list-item + if children[0].tag == "list-item": + data = [] + for child in children: + data.append(self._xml_convert(child)) + else: + data = {} + for child in children: + data[child.tag] = self._xml_convert(child) + + return data + + def _type_convert(self, value): + """ + Converts the value returned by the XMl parse into the equivalent + Python type + """ + if value is None: + return value + + try: + return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S') + except ValueError: + pass + + try: + return int(value) + except ValueError: + pass + + try: + return decimal.Decimal(value) + except decimal.InvalidOperation: + pass + + return value + + +DEFAULT_PARSERS = ( + JSONParser, + FormParser, + MultiPartParser, + XMLParser +) + +if yaml: + DEFAULT_PARSERS += (YAMLParser, ) +else: + YAMLParser = None diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py new file mode 100644 index 000000000..3a669822c --- /dev/null +++ b/rest_framework/permissions.py @@ -0,0 +1,116 @@ +""" +The :mod:`permissions` module bundles a set of permission classes that are used +for checking if a request passes a certain set of constraints. + +Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` class into a :class:`View` class. +""" + +__all__ = ( + 'BasePermission', + 'FullAnonAccess', + 'IsAuthenticated', + 'IsAdminUser', + 'IsUserOrIsAnonReadOnly', + 'PerUserThrottling', + 'PerViewThrottling', +) + +SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] + + +class BasePermission(object): + """ + A base class from which all permission classes should inherit. + """ + def __init__(self, view): + """ + Permission classes are always passed the current view on creation. + """ + self.view = view + + def has_permission(self, request, obj=None): + """ + Should simply return, or raise an :exc:`response.ImmediateResponse`. + """ + raise NotImplementedError(".has_permission() must be overridden.") + + +class IsAuthenticated(BasePermission): + """ + Allows access only to authenticated users. + """ + + def has_permission(self, request, obj=None): + if request.user and request.user.is_authenticated(): + return True + return False + + +class IsAdminUser(BasePermission): + """ + Allows access only to admin users. + """ + + def has_permission(self, request, obj=None): + if request.user and request.user.is_staff: + return True + return False + + +class IsAuthenticatedOrReadOnly(BasePermission): + """ + The request is authenticated as a user, or is a read-only request. + """ + + def has_permission(self, request, obj=None): + if (request.method in SAFE_METHODS or + request.user and + request.user.is_authenticated()): + return True + return False + + +class DjangoModelPermissions(BasePermission): + """ + The request is authenticated using `django.contrib.auth` permissions. + See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions + + It ensures that the user is authenticated, and has the appropriate + `add`/`change`/`delete` permissions on the model. + + This permission should only be used on views with a `ModelResource`. + """ + + # Map methods into required permission codes. + # Override this if you need to also provide 'view' permissions, + # or if you want to provide custom permission codes. + perms_map = { + 'GET': [], + 'OPTIONS': [], + 'HEAD': [], + 'POST': ['%(app_label)s.add_%(model_name)s'], + 'PUT': ['%(app_label)s.change_%(model_name)s'], + 'PATCH': ['%(app_label)s.change_%(model_name)s'], + 'DELETE': ['%(app_label)s.delete_%(model_name)s'], + } + + def get_required_permissions(self, method, model_cls): + """ + Given a model and an HTTP method, return the list of permission + codes that the user is required to have. + """ + kwargs = { + 'app_label': model_cls._meta.app_label, + 'model_name': model_cls._meta.module_name + } + return [perm % kwargs for perm in self.perms_map[method]] + + def has_permission(self, request, obj=None): + model_cls = self.view.model + perms = self.get_required_permissions(request.method, model_cls) + + if (request.user and + request.user.is_authenticated() and + request.user.has_perms(perms, obj)): + return True + return False diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py new file mode 100644 index 000000000..66394af3e --- /dev/null +++ b/rest_framework/renderers.py @@ -0,0 +1,402 @@ +""" +Renderers are used to serialize a View's output into specific media types. + +Django REST framework also provides HTML and PlainText renderers that help self-document the API, +by serializing the output along with documentation regarding the View, output status and headers, +and providing forms and links depending on the allowed methods, renderers and parsers on the View. +""" +from django import forms +from django.template import RequestContext, loader +from django.utils import simplejson as json + +from rest_framework.compat import yaml +from rest_framework.settings import api_settings +from rest_framework.utils import dict2xml +from rest_framework.utils import encoders +from rest_framework.utils.breadcrumbs import get_breadcrumbs +from rest_framework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches +from rest_framework import VERSION +from rest_framework.fields import FloatField, IntegerField, DateTimeField, DateField, EmailField, CharField, BooleanField + +import string + + +__all__ = ( + 'BaseRenderer', + 'TemplateRenderer', + 'JSONRenderer', + 'JSONPRenderer', + 'DocumentingHTMLRenderer', + 'DocumentingXHTMLRenderer', + 'DocumentingPlainTextRenderer', + 'XMLRenderer', + 'YAMLRenderer' +) + + +class BaseRenderer(object): + """ + All renderers must extend this class, set the :attr:`media_type` attribute, + and override the :meth:`render` method. + """ + + _FORMAT_QUERY_PARAM = 'format' + + media_type = None + format = None + + def __init__(self, view=None): + self.view = view + + def can_handle_format(self, format): + return format == self.format + + def can_handle_media_type(self, media_type): + """ + Returns `True` if this renderer is able to deal with the given + media type. + + The default implementation for this function is to check the media type + argument against the media_type attribute set on the class to see if + they match. + + This may be overridden to provide for other behavior, but typically + you'll instead want to just set the `media_type` attribute on the class. + """ + return media_type_matches(self.media_type, media_type) + + def render(self, obj=None, media_type=None): + """ + Given an object render it into a string. + + The requested media type is also passed to this method, + as it may contain parameters relevant to how the parser + should render the output. + EG: ``application/json; indent=4`` + + By default render simply returns the output as-is. + Override this method to provide for other behavior. + """ + if obj is None: + return '' + + return str(obj) + + +class JSONRenderer(BaseRenderer): + """ + Renderer which serializes to JSON + """ + + media_type = 'application/json' + format = 'json' + encoder_class = encoders.JSONEncoder + + def render(self, obj=None, media_type=None): + """ + Renders *obj* into serialized JSON. + """ + if obj is None: + return '' + + # If the media type looks like 'application/json; indent=4', then + # pretty print the result. + indent = get_media_type_params(media_type).get('indent', None) + sort_keys = False + try: + indent = max(min(int(indent), 8), 0) + sort_keys = True + except (ValueError, TypeError): + indent = None + + return json.dumps(obj, cls=self.encoder_class, indent=indent, sort_keys=sort_keys) + + +class JSONPRenderer(JSONRenderer): + """ + Renderer which serializes to JSONP + """ + + media_type = 'application/javascript' + format = 'jsonp' + renderer_class = JSONRenderer + callback_parameter = 'callback' + + def _get_callback(self): + return self.view.request.GET.get(self.callback_parameter, self.callback_parameter) + + def _get_renderer(self): + return self.renderer_class(self.view) + + def render(self, obj=None, media_type=None): + callback = self._get_callback() + json = self._get_renderer().render(obj, media_type) + return "%s(%s);" % (callback, json) + + +class XMLRenderer(BaseRenderer): + """ + Renderer which serializes to XML. + """ + + media_type = 'application/xml' + format = 'xml' + + def render(self, obj=None, media_type=None): + """ + Renders *obj* into serialized XML. + """ + if obj is None: + return '' + return dict2xml(obj) + + +class YAMLRenderer(BaseRenderer): + """ + Renderer which serializes to YAML. + """ + + media_type = 'application/yaml' + format = 'yaml' + + def render(self, obj=None, media_type=None): + """ + Renders *obj* into serialized YAML. + """ + if obj is None: + return '' + + return yaml.safe_dump(obj) + + +class TemplateRenderer(BaseRenderer): + """ + A Base class provided for convenience. + + Render the object simply by using the given template. + To create a template renderer, subclass this class, and set + the :attr:`media_type` and :attr:`template` attributes. + """ + + media_type = None + template = None + + def render(self, obj=None, media_type=None): + """ + Renders *obj* using the :attr:`template` specified on the class. + """ + if obj is None: + return '' + + template = loader.get_template(self.template) + context = RequestContext(self.view.request, {'object': obj}) + return template.render(context) + + +class DocumentingTemplateRenderer(BaseRenderer): + """ + Base class for renderers used to self-document the API. + Implementing classes should extend this class and set the template attribute. + """ + + template = None + + def _get_content(self, view, request, obj, media_type): + """ + Get the content as if it had been rendered by a non-documenting renderer. + + (Typically this will be the content as it would have been if the Resource had been + requested with an 'Accept: */*' header, although with verbose style formatting if appropriate.) + """ + + # Find the first valid renderer and render the content. (Don't use another documenting renderer.) + renderers = [renderer for renderer in view.renderer_classes + if not issubclass(renderer, DocumentingTemplateRenderer)] + if not renderers: + return '[No renderers were found]' + + media_type = add_media_type_param(media_type, 'indent', '4') + content = renderers[0](view).render(obj, media_type) + if not all(char in string.printable for char in content): + return '[%d bytes of binary content]' + + return content + + def _get_form_instance(self, view, method): + """ + Get a form, possibly bound to either the input or output data. + In the absence on of the Resource having an associated form then + provide a form that can be used to submit arbitrary content. + """ + if not hasattr(self.view, 'get_serializer'): # No serializer, no form. + return + # We need to map our Fields to Django's Fields. + field_mapping = dict([ + [FloatField.__name__, forms.FloatField], + [IntegerField.__name__, forms.IntegerField], + [DateTimeField.__name__, forms.DateTimeField], + [DateField.__name__, forms.DateField], + [EmailField.__name__, forms.EmailField], + [CharField.__name__, forms.CharField], + [BooleanField.__name__, forms.BooleanField] + ]) + + # Creating an on the fly form see: http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python + fields = {} + object, data = None, None + if hasattr(self.view, 'object'): + object = self.view.object + serializer = self.view.get_serializer(instance=object) + for k, v in serializer.fields.items(): + fields[k] = field_mapping[v.__class__.__name__]() + OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields) + if object and not self.view.request.method == 'DELETE': # Don't fill in the form when the object is deleted + data = serializer.data + form_instance = OnTheFlyForm(data) + return form_instance + + def _get_generic_content_form(self, view): + """ + Returns a form that allows for arbitrary content types to be tunneled via standard HTML forms + (Which are typically application/x-www-form-urlencoded) + """ + + # If we're not using content overloading there's no point in supplying a generic form, + # as the view won't treat the form's value as the content of the request. + if not getattr(view.request, '_USE_FORM_OVERLOADING', False): + return None + + # NB. http://jacobian.org/writing/dynamic-form-generation/ + class GenericContentForm(forms.Form): + def __init__(self, view, request): + """We don't know the names of the fields we want to set until the point the form is instantiated, + as they are determined by the Resource the form is being created against. + Add the fields dynamically.""" + super(GenericContentForm, self).__init__() + + contenttype_choices = [(media_type, media_type) for media_type in view._parsed_media_types] + initial_contenttype = view._default_parser.media_type + + self.fields[request._CONTENTTYPE_PARAM] = forms.ChoiceField(label='Content Type', + choices=contenttype_choices, + initial=initial_contenttype) + self.fields[request._CONTENT_PARAM] = forms.CharField(label='Content', + widget=forms.Textarea) + + # If either of these reserved parameters are turned off then content tunneling is not possible + if self.view.request._CONTENTTYPE_PARAM is None or self.view.request._CONTENT_PARAM is None: + return None + + # Okey doke, let's do it + return GenericContentForm(view, view.request) + + def get_name(self): + try: + return self.view.get_name() + except AttributeError: + return self.view.__doc__ + + def get_description(self, html=None): + if html is None: + html = bool('html' in self.format) + try: + return self.view.get_description(html) + except AttributeError: + return self.view.__doc__ + + def render(self, obj=None, media_type=None): + """ + Renders *obj* using the :attr:`template` set on the class. + + The context used in the template contains all the information + needed to self-document the response to this request. + """ + + content = self._get_content(self.view, self.view.request, obj, media_type) + + put_form_instance = self._get_form_instance(self.view, 'put') + post_form_instance = self._get_form_instance(self.view, 'post') + + name = self.get_name() + description = self.get_description() + + breadcrumb_list = get_breadcrumbs(self.view.request.path) + + template = loader.get_template(self.template) + context = RequestContext(self.view.request, { + 'content': content, + 'view': self.view, + 'request': self.view.request, + 'response': self.view.response, + 'description': description, + 'name': name, + 'version': VERSION, + 'breadcrumblist': breadcrumb_list, + 'allowed_methods': self.view.allowed_methods, + 'available_formats': self.view._rendered_formats, + 'put_form': put_form_instance, + 'post_form': post_form_instance, + 'FORMAT_PARAM': self._FORMAT_QUERY_PARAM, + 'METHOD_PARAM': getattr(self.view, '_METHOD_PARAM', None), + 'api_settings': api_settings + }) + + ret = template.render(context) + + # Munge DELETE Response code to allow us to return content + # (Do this *after* we've rendered the template so that we include + # the normal deletion response code in the output) + if self.view.response.status_code == 204: + self.view.response.status_code = 200 + + return ret + + +class DocumentingHTMLRenderer(DocumentingTemplateRenderer): + """ + Renderer which provides a browsable HTML interface for an API. + See the examples at http://api.django-rest-framework.org to see this in action. + """ + + media_type = 'text/html' + format = 'html' + template = 'rest_framework/api.html' + + +class DocumentingXHTMLRenderer(DocumentingTemplateRenderer): + """ + Identical to DocumentingHTMLRenderer, except with an xhtml media type. + We need this to be listed in preference to xml in order to return HTML to WebKit based browsers, + given their Accept headers. + """ + + media_type = 'application/xhtml+xml' + format = 'xhtml' + template = 'rest_framework/api.html' + + +class DocumentingPlainTextRenderer(DocumentingTemplateRenderer): + """ + Renderer that serializes the object with the default renderer, but also provides plain-text + documentation of the returned status and headers, and of the resource's name and description. + Useful for browsing an API with command line tools. + """ + + media_type = 'text/plain' + format = 'txt' + template = 'rest_framework/api.txt' + + +DEFAULT_RENDERERS = ( + JSONRenderer, + JSONPRenderer, + DocumentingHTMLRenderer, + DocumentingXHTMLRenderer, + DocumentingPlainTextRenderer, + XMLRenderer +) + +if yaml: + DEFAULT_RENDERERS += (YAMLRenderer, ) +else: + YAMLRenderer = None diff --git a/rest_framework/request.py b/rest_framework/request.py new file mode 100644 index 000000000..ee5e46d04 --- /dev/null +++ b/rest_framework/request.py @@ -0,0 +1,284 @@ +""" +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` + - full support of PUT method, including support for file uploads + - form overloading of HTTP method, content type and content +""" +from StringIO import StringIO + +from rest_framework import exceptions +from rest_framework.settings import api_settings +from rest_framework.utils.mediatypes import is_form_media_type + + +__all__ = ('Request',) + + +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 + + +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, parser_classes=None, authentication_classes=None): + self._request = request + self.parser_classes = parser_classes or () + self.authentication_classes = authentication_classes or () + self._data = Empty + self._files = Empty + self._method = Empty + self._content_type = Empty + self._stream = Empty + + def get_parsers(self): + """ + Instantiates and returns the list of parsers the request will use. + """ + return [parser() for parser in self.parser_classes] + + def get_authentications(self): + """ + Instantiates and returns the list of parsers the request will use. + """ + return [authentication() for authentication in self.authentication_classes] + + @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 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._user, self._auth = self._authenticate() + return self._user + + @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._user, self._auth = self._authenticate() + return self._auth + + 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() + self._data.pop(self._METHOD_PARAM) + + # Content overloading - modify the content type, and 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.pop(self._CONTENTTYPE_PARAM) + self._data.pop(self._CONTENT_PARAM) + self._data, self._files = self._parse() + + def _parse(self): + """ + Parse the request content, returning a two-tuple of (data, files) + + May raise an `UnsupportedMediaType`, or `ParseError` exception. + """ + if self.stream is None or self.content_type is None: + return (None, None) + + for parser in self.get_parsers(): + if parser.can_handle_request(self.content_type): + parsed = parser.parse(self.stream, meta=self.META, + upload_handlers=self.upload_handlers) + # 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) + + raise exceptions.UnsupportedMediaType(self._content_type) + + def _authenticate(self): + """ + Attempt to authenticate the request using each authentication instance in turn. + Returns a two-tuple of (user, authtoken). + """ + for authentication in self.get_authentications(): + user_auth_tuple = authentication.authenticate(self) + if not user_auth_tuple is None: + return user_auth_tuple + return self._not_authenticated() + + def _not_authenticated(self): + """ + Return a two-tuple of (user, authtoken), representing an + unauthenticated request. + + By default this will be (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 (user, auth) + + def __getattr__(self, attr): + """ + Proxy other attributes to the underlying HttpRequest object. + """ + return getattr(self._request, attr) diff --git a/rest_framework/resources.py b/rest_framework/resources.py new file mode 100644 index 000000000..41b7c1c6a --- /dev/null +++ b/rest_framework/resources.py @@ -0,0 +1,87 @@ +from functools import update_wrapper +import inspect +from django.utils.decorators import classonlymethod +from djanorestframework import views, generics + + +def wrapped(source, dest): + """ + Copy public, non-method attributes from source to dest, and return dest. + """ + for attr in [attr for attr in dir(source) + if not attr.startswith('_') and not inspect.ismethod(attr)]: + setattr(dest, attr, getattr(source, attr)) + return dest + + +class ResourceMixin(object): + """ + Clone Django's `View.as_view()` behaviour *except* using REST framework's + 'method -> action' binding for resources. + """ + + @classonlymethod + def as_view(cls, actions, **initkwargs): + """ + Main entry point for a request-response process. + """ + # sanitize keyword arguments + for key in initkwargs: + if key in cls.http_method_names: + raise TypeError("You tried to pass in the %s method name as a " + "keyword argument to %s(). Don't do that." + % (key, cls.__name__)) + if not hasattr(cls, key): + raise TypeError("%s() received an invalid keyword %r" % ( + cls.__name__, key)) + + def view(request, *args, **kwargs): + self = cls(**initkwargs) + + # Bind methods to actions + for method, action in actions.items(): + handler = getattr(self, action) + setattr(self, method, handler) + + # As you were, solider. + if hasattr(self, 'get') and not hasattr(self, 'head'): + self.head = self.get + return self.dispatch(request, *args, **kwargs) + + # take name and docstring from class + update_wrapper(view, cls, updated=()) + + # and possible attributes set by decorators + # like csrf_exempt from dispatch + update_wrapper(view, cls.dispatch, assigned=()) + return view + + +class Resource(ResourceMixin, views.APIView): + pass + + +class ModelResource(ResourceMixin, views.APIView): + root_class = generics.RootAPIView + detail_class = generics.InstanceAPIView + + def root_view(self): + return wrapped(self, self.root_class()) + + def detail_view(self): + return wrapped(self, self.detail_class()) + + def list(self, request, *args, **kwargs): + return self.root_view().list(request, args, kwargs) + + def create(self, request, *args, **kwargs): + return self.root_view().create(request, args, kwargs) + + def retrieve(self, request, *args, **kwargs): + return self.detail_view().retrieve(request, args, kwargs) + + def update(self, request, *args, **kwargs): + return self.detail_view().update(request, args, kwargs) + + def destroy(self, request, *args, **kwargs): + return self.detail_view().destroy(request, args, kwargs) diff --git a/rest_framework/response.py b/rest_framework/response.py new file mode 100644 index 000000000..29034e257 --- /dev/null +++ b/rest_framework/response.py @@ -0,0 +1,39 @@ +from django.template.response import SimpleTemplateResponse +from django.core.handlers.wsgi import STATUS_CODE_TEXT + + +class Response(SimpleTemplateResponse): + """ + An HttpResponse that allows it's data to be rendered into + arbitrary media types. + """ + + def __init__(self, data=None, status=None, headers=None, + renderer=None, media_type=None): + """ + Alters the init arguments slightly. + For example, drop 'template_name', and instead use 'data'. + + Setting 'renderer' and 'media_type' will typically be defered, + For example being set automatically by the `APIView`. + """ + super(Response, self).__init__(None, status=status) + self.data = data + self.headers = headers and headers[:] or [] + self.renderer = renderer + self.media_type = media_type + + @property + def rendered_content(self): + self['Content-Type'] = self.renderer.media_type + if self.data is None: + return self.renderer.render() + return self.renderer.render(self.data, self.media_type) + + @property + def status_text(self): + """ + Returns reason text corresponding to our HTTP response status code. + Provided for convenience. + """ + return STATUS_CODE_TEXT.get(self.status_code, '') diff --git a/rest_framework/reverse.py b/rest_framework/reverse.py new file mode 100644 index 000000000..ba663f98f --- /dev/null +++ b/rest_framework/reverse.py @@ -0,0 +1,20 @@ +""" +Provide reverse functions that return fully qualified URLs +""" +from django.core.urlresolvers import reverse as django_reverse +from django.utils.functional import lazy + + +def reverse(viewname, *args, **kwargs): + """ + Same as `django.core.urlresolvers.reverse`, but optionally takes a request + and returns a fully qualified URL, using the request to get the base URL. + """ + request = kwargs.pop('request', None) + url = django_reverse(viewname, *args, **kwargs) + if request: + return request.build_absolute_uri(url) + return url + + +reverse_lazy = lazy(reverse, str) diff --git a/rest_framework/runtests/__init__.py b/rest_framework/runtests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/rest_framework/runtests/runcoverage.py b/rest_framework/runtests/runcoverage.py new file mode 100755 index 000000000..5f7fc876f --- /dev/null +++ b/rest_framework/runtests/runcoverage.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +""" +Useful tool to run the test suite for rest_framework and generate a coverage report. +""" + +# http://ericholscher.com/blog/2009/jun/29/enable-setuppy-test-your-django-apps/ +# http://www.travisswicegood.com/2010/01/17/django-virtualenv-pip-and-fabric/ +# http://code.djangoproject.com/svn/django/trunk/tests/runtests.py +import os +import sys +os.environ['DJANGO_SETTINGS_MODULE'] = 'rest_framework.runtests.settings' + +from coverage import coverage + + +def main(): + """Run the tests for rest_framework and generate a coverage report.""" + + cov = coverage() + cov.erase() + cov.start() + + from django.conf import settings + from django.test.utils import get_runner + TestRunner = get_runner(settings) + + if hasattr(TestRunner, 'func_name'): + # Pre 1.2 test runners were just functions, + # and did not support the 'failfast' option. + import warnings + warnings.warn( + 'Function-based test runners are deprecated. Test runners should be classes with a run_tests() method.', + DeprecationWarning + ) + failures = TestRunner(['rest_framework']) + else: + test_runner = TestRunner() + failures = test_runner.run_tests(['rest_framework']) + cov.stop() + + # Discover the list of all modules that we should test coverage for + import rest_framework + + project_dir = os.path.dirname(rest_framework.__file__) + cov_files = [] + + for (path, dirs, files) in os.walk(project_dir): + # Drop tests and runtests directories from the test coverage report + if os.path.basename(path) == 'tests' or os.path.basename(path) == 'runtests': + continue + + # Drop the compat module from coverage, since we're not interested in the coverage + # of a module which is specifically for resolving environment dependant imports. + # (Because we'll end up getting different coverage reports for it for each environment) + if 'compat.py' in files: + files.remove('compat.py') + + cov_files.extend([os.path.join(path, file) for file in files if file.endswith('.py')]) + + cov.report(cov_files) + if '--html' in sys.argv: + cov.html_report(cov_files, directory='coverage') + sys.exit(failures) + +if __name__ == '__main__': + main() diff --git a/rest_framework/runtests/runtests.py b/rest_framework/runtests/runtests.py new file mode 100755 index 000000000..b2438c9b2 --- /dev/null +++ b/rest_framework/runtests/runtests.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# http://ericholscher.com/blog/2009/jun/29/enable-setuppy-test-your-django-apps/ +# http://www.travisswicegood.com/2010/01/17/django-virtualenv-pip-and-fabric/ +# http://code.djangoproject.com/svn/django/trunk/tests/runtests.py +import os +import sys +os.environ['DJANGO_SETTINGS_MODULE'] = 'rest_framework.runtests.settings' + +from django.conf import settings +from django.test.utils import get_runner + + +def usage(): + return """ + Usage: python runtests.py [UnitTestClass].[method] + + You can pass the Class name of the `UnitTestClass` you want to test. + + Append a method name if you only want to test a specific method of that class. + """ + + +def main(): + TestRunner = get_runner(settings) + + test_runner = TestRunner() + if len(sys.argv) == 2: + test_case = '.' + sys.argv[1] + elif len(sys.argv) == 1: + test_case = '' + else: + print usage() + sys.exit(1) + failures = test_runner.run_tests(['rest_framework' + test_case]) + + sys.exit(failures) + +if __name__ == '__main__': + main() diff --git a/rest_framework/runtests/settings.py b/rest_framework/runtests/settings.py new file mode 100644 index 000000000..93e38f467 --- /dev/null +++ b/rest_framework/runtests/settings.py @@ -0,0 +1,118 @@ +# Django settings for testproject project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG +DEBUG_PROPAGATE_EXCEPTIONS = True + +ADMINS = ( + # ('Your Name', 'your_email@domain.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': 'sqlite.db', # Or path to database file if using sqlite3. + 'USER': '', # Not used with sqlite3. + 'PASSWORD': '', # Not used with sqlite3. + 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. + 'PORT': '', # Set to empty string for default. Not used with sqlite3. + } +} + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'Europe/London' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-uk' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale +USE_L10N = True + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com", "http://example.com/media/" +MEDIA_URL = '' + +# Make this unique, and don't share it with anybody. +SECRET_KEY = 'u@x-aj9(hoh#rb-^ymf#g2jx_hp0vj7u5#b@ag1n^seu9e!%cy' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +) + +ROOT_URLCONF = 'urls' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + # Uncomment the next line to enable the admin: + # 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', + 'rest_framework', + 'rest_framework.authtoken', +) + +STATIC_URL = '/static/' + +import django + +if django.VERSION < (1, 3): + INSTALLED_APPS += ('staticfiles',) + + +# OAuth support is optional, so we only test oauth if it's installed. +try: + import oauth_provider +except ImportError: + pass +else: + INSTALLED_APPS += ('oauth_provider',) + +# If we're running on the Jenkins server we want to archive the coverage reports as XML. +import os +if os.environ.get('HUDSON_URL', None): + TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' + TEST_OUTPUT_VERBOSE = True + TEST_OUTPUT_DESCRIPTIONS = True + TEST_OUTPUT_DIR = 'xmlrunner' diff --git a/rest_framework/runtests/urls.py b/rest_framework/runtests/urls.py new file mode 100644 index 000000000..4b7da7871 --- /dev/null +++ b/rest_framework/runtests/urls.py @@ -0,0 +1,7 @@ +""" +Blank URLConf just to keep runtests.py happy. +""" +from django.conf.urls.defaults import * + +urlpatterns = patterns('', +) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py new file mode 100644 index 000000000..afc9f5113 --- /dev/null +++ b/rest_framework/serializers.py @@ -0,0 +1,348 @@ +from decimal import Decimal +from django.core.serializers.base import DeserializedObject +from django.utils.datastructures import SortedDict +import copy +import datetime +import types +from rest_framework.fields import * + + +class DictWithMetadata(dict): + """ + A dict-like object, that can have additional properties attached. + """ + pass + + +class SortedDictWithMetadata(SortedDict, DictWithMetadata): + """ + A sorted dict-like object, that can have additional properties attached. + """ + pass + + +class RecursionOccured(BaseException): + pass + + +def _is_protected_type(obj): + """ + True if the object is a native datatype that does not need to + be serialized further. + """ + return isinstance(obj, ( + types.NoneType, + int, long, + datetime.datetime, datetime.date, datetime.time, + float, Decimal, + basestring) + ) + + +def _get_declared_fields(bases, attrs): + """ + Create a list of serializer field instances from the passed in 'attrs', + plus any fields on the base classes (in 'bases'). + + Note that all fields from the base classes are used. + """ + fields = [(field_name, attrs.pop(field_name)) + for field_name, obj in attrs.items() + if isinstance(obj, Field)] + fields.sort(key=lambda x: x[1].creation_counter) + + # If this class is subclassing another Serializer, add that Serializer's + # fields. Note that we loop over the bases in *reverse*. This is necessary + # in order to the correct order of fields. + for base in bases[::-1]: + if hasattr(base, 'base_fields'): + fields = base.base_fields.items() + fields + + return SortedDict(fields) + + +class SerializerMetaclass(type): + def __new__(cls, name, bases, attrs): + attrs['base_fields'] = _get_declared_fields(bases, attrs) + return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs) + + +class SerializerOptions(object): + """ + Meta class options for ModelSerializer + """ + def __init__(self, meta): + self.nested = getattr(meta, 'nested', False) + self.fields = getattr(meta, 'fields', ()) + self.exclude = getattr(meta, 'exclude', ()) + + +class BaseSerializer(Field): + class Meta(object): + pass + + _options_class = SerializerOptions + _dict_class = SortedDictWithMetadata # Set to unsorted dict for backwards compatability with unsorted implementations. + + def __init__(self, data=None, instance=None, context=None, **kwargs): + super(BaseSerializer, self).__init__(**kwargs) + self.fields = copy.deepcopy(self.base_fields) + self.opts = self._options_class(self.Meta) + self.parent = None + self.root = None + + self.stack = [] + self.context = context or {} + + self.init_data = data + self.instance = instance + + self._data = None + self._errors = None + + ##### + # Methods to determine which fields to use when (de)serializing objects. + + def default_fields(self, serialize, obj=None, data=None, nested=False): + """ + Return the complete set of default fields for the object, as a dict. + """ + return {} + + def get_fields(self, serialize, obj=None, data=None, nested=False): + """ + Returns the complete set of fields for the object as a dict. + + This will be the set of any explicitly declared fields, + plus the set of fields returned by get_default_fields(). + """ + ret = SortedDict() + + # Get the explicitly declared fields + for key, field in self.fields.items(): + ret[key] = field + # Determine if the declared field corrosponds to a model field. + try: + if key == 'pk': + model_field = obj._meta.pk + else: + model_field = obj._meta.get_field_by_name(key)[0] + except: + model_field = None + # Set up the field + field.initialize(parent=self, model_field=model_field) + + # Add in the default fields + fields = self.default_fields(serialize, obj, data, nested) + for key, val in fields.items(): + if key not in ret: + ret[key] = val + + # If 'fields' is specified, use those fields, in that order. + if self.opts.fields: + new = SortedDict() + for key in self.opts.fields: + new[key] = ret[key] + ret = new + + # Remove anything in 'exclude' + if self.opts.exclude: + for key in self.opts.exclude: + ret.pop(key, None) + + return ret + + ##### + # Field methods - used when the serializer class is itself used as a field. + + def initialize(self, parent, model_field=None): + """ + Same behaviour as usual Field, except that we need to keep track + of state so that we can deal with handling maximum depth and recursion. + """ + super(BaseSerializer, self).initialize(parent, model_field) + self.stack = parent.stack[:] + if parent.opts.nested and not isinstance(parent.opts.nested, bool): + self.opts.nested = parent.opts.nested - 1 + else: + self.opts.nested = parent.opts.nested + + ##### + # Methods to convert or revert from objects <--> primative representations. + + def get_field_key(self, field_name): + """ + Return the key that should be used for a given field. + """ + return field_name + + def convert_object(self, obj): + """ + Core of serialization. + Convert an object into a dictionary of serialized field values. + """ + if obj in self.stack and not self.source == '*': + raise RecursionOccured() + self.stack.append(obj) + + ret = self._dict_class() + ret.fields = {} + + fields = self.get_fields(serialize=True, obj=obj, nested=self.opts.nested) + for field_name, field in fields.items(): + key = self.get_field_key(field_name) + try: + value = field.field_to_native(obj, field_name) + except RecursionOccured: + field = self.get_fields(serialize=True, obj=obj, nested=False)[field_name] + value = field.field_to_native(obj, field_name) + ret[key] = value + ret.fields[key] = field + return ret + + def restore_fields(self, data): + """ + Core of deserialization, together with `restore_object`. + Converts a dictionary of data into a dictionary of deserialized fields. + """ + fields = self.get_fields(serialize=False, data=data, nested=self.opts.nested) + reverted_data = {} + for field_name, field in fields.items(): + try: + field.field_from_native(data, field_name, reverted_data) + except ValidationError as err: + self._errors[field_name] = list(err.messages) + + return reverted_data + + def restore_object(self, attrs, instance=None): + """ + Deserialize a dictionary of attributes into an object instance. + You should override this method to control how deserialized objects + are instantiated. + """ + if instance is not None: + instance.update(attrs) + return instance + return attrs + + def to_native(self, obj): + """ + Serialize objects -> primatives. + """ + if isinstance(obj, dict): + return dict([(key, self.to_native(val)) + for (key, val) in obj.items()]) + elif hasattr(obj, '__iter__'): + return (self.to_native(item) for item in obj) + return self.convert_object(obj) + + def from_native(self, data): + """ + Deserialize primatives -> objects. + """ + if hasattr(data, '__iter__') and not isinstance(data, dict): + # TODO: error data when deserializing lists + return (self.from_native(item) for item in data) + self._errors = {} + attrs = self.restore_fields(data) + if not self._errors: + return self.restore_object(attrs, instance=getattr(self, 'instance', None)) + + @property + def errors(self): + """ + Run deserialization and return error data, + setting self.object if no errors occured. + """ + if self._errors is None: + obj = self.from_native(self.init_data) + if not self._errors: + self.object = obj + return self._errors + + def is_valid(self): + return not self.errors + + @property + def data(self): + if self._data is None: + self._data = self.to_native(self.instance) + return self._data + + +class Serializer(BaseSerializer): + __metaclass__ = SerializerMetaclass + + +class ModelSerializerOptions(SerializerOptions): + """ + Meta class options for ModelSerializer + """ + def __init__(self, meta): + super(ModelSerializerOptions, self).__init__(meta) + self.model = getattr(meta, 'model', None) + + +class ModelSerializer(RelatedField, Serializer): + """ + A serializer that deals with model instances and querysets. + """ + _options_class = ModelSerializerOptions + + def default_fields(self, serialize, obj=None, data=None, nested=False): + """ + Return all the fields that should be serialized for the model. + """ + if serialize: + cls = obj.__class__ + else: + cls = self.opts.model + + opts = cls._meta.concrete_model._meta + pk_field = opts.pk + while pk_field.rel: + pk_field = pk_field.rel.to._meta.pk + fields = [pk_field] + fields += [field for field in opts.fields if field.serialize] + fields += [field for field in opts.many_to_many if field.serialize] + + ret = SortedDict() + for model_field in fields: + if model_field.rel and nested: + field = self.get_nested_field(model_field) + elif model_field.rel: + field = self.get_related_field(model_field) + else: + field = self.get_field(model_field) + field.initialize(parent=self, model_field=model_field) + ret[model_field.name] = field + return ret + + def get_nested_field(self, model_field): + """ + Creates a default instance of a nested relational field. + """ + return ModelSerializer() + + def get_related_field(self, model_field): + """ + Creates a default instance of a flat relational field. + """ + return PrimaryKeyRelatedField() + + def get_field(self, model_field): + """ + Creates a default instance of a basic field. + """ + return Field() + + def restore_object(self, attrs, instance=None): + """ + Restore the model instance. + """ + m2m_data = {} + for field in self.opts.model._meta.many_to_many: + if field.name in attrs: + m2m_data[field.name] = attrs.pop(field.name) + return DeserializedObject(self.opts.model(**attrs), m2m_data) diff --git a/rest_framework/settings.py b/rest_framework/settings.py new file mode 100644 index 000000000..fef8097e9 --- /dev/null +++ b/rest_framework/settings.py @@ -0,0 +1,125 @@ +""" +Settings for REST framework are all namespaced in the REST_FRAMEWORK setting. +For example your project's `settings.py` file might look like this: + +REST_FRAMEWORK = { + 'DEFAULT_RENDERERS': ( + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.YAMLRenderer', + ) + 'DEFAULT_PARSERS': ( + 'rest_framework.parsers.JSONParser', + 'rest_framework.parsers.YAMLParser', + ) +} + +This module provides the `api_setting` object, that is used to access +REST framework settings, checking for user settings first, then falling +back to the defaults. +""" +from django.conf import settings +from django.utils import importlib + + +DEFAULTS = { + 'DEFAULT_RENDERERS': ( + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.JSONPRenderer', + 'rest_framework.renderers.DocumentingHTMLRenderer', + 'rest_framework.renderers.DocumentingPlainTextRenderer', + ), + 'DEFAULT_PARSERS': ( + 'rest_framework.parsers.JSONParser', + 'rest_framework.parsers.FormParser' + ), + 'DEFAULT_AUTHENTICATION': ( + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.UserBasicAuthentication' + ), + 'DEFAULT_PERMISSIONS': (), + 'DEFAULT_THROTTLES': (), + 'DEFAULT_CONTENT_NEGOTIATION': 'rest_framework.negotiation.DefaultContentNegotiation', + + 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser', + 'UNAUTHENTICATED_TOKEN': None, + + 'FORM_METHOD_OVERRIDE': '_method', + 'FORM_CONTENT_OVERRIDE': '_content', + 'FORM_CONTENTTYPE_OVERRIDE': '_content_type', + 'URL_ACCEPT_OVERRIDE': '_accept', + 'URL_FORMAT_OVERRIDE': 'format', + + 'FORMAT_SUFFIX_KWARG': 'format' +} + + +# List of settings that may be in string import notation. +IMPORT_STRINGS = ( + 'DEFAULT_RENDERERS', + 'DEFAULT_PARSERS', + 'DEFAULT_AUTHENTICATION', + 'DEFAULT_PERMISSIONS', + 'DEFAULT_THROTTLES', + 'DEFAULT_CONTENT_NEGOTIATION', + 'UNAUTHENTICATED_USER', + 'UNAUTHENTICATED_TOKEN', +) + + +def perform_import(val, setting): + """ + If the given setting is a string import notation, + then perform the necessary import or imports. + """ + if val is None or not setting in IMPORT_STRINGS: + return val + + if isinstance(val, basestring): + return import_from_string(val, setting) + elif isinstance(val, (list, tuple)): + return [import_from_string(item, setting) for item in val] + return val + + +def import_from_string(val, setting): + """ + Attempt to import a class from a string representation. + """ + try: + # Nod to tastypie's use of importlib. + parts = val.split('.') + module_path, class_name = '.'.join(parts[:-1]), parts[-1] + module = importlib.import_module(module_path) + return getattr(module, class_name) + except: + msg = "Could not import '%s' for API setting '%s'" % (val, setting) + raise ImportError(msg) + + +class APISettings(object): + """ + A settings object, that allows API settings to be accessed as properties. + For example: + + from rest_framework.settings import api_settings + print api_settings.DEFAULT_RENDERERS + + Any setting with string import paths will be automatically resolved + and return the class, rather than the string literal. + """ + def __getattr__(self, attr): + if attr not in DEFAULTS.keys(): + raise AttributeError("Invalid API setting: '%s'" % attr) + + try: + # Check if present in user settings + val = perform_import(settings.REST_FRAMEWORK[attr], attr) + except (AttributeError, KeyError): + # Fall back to defaults + val = perform_import(DEFAULTS[attr], attr) + + # Cache the result + setattr(self, attr, val) + return val + +api_settings = APISettings() diff --git a/rest_framework/static/djangorestframework/css/bootstrap-tweaks.css b/rest_framework/static/djangorestframework/css/bootstrap-tweaks.css new file mode 100644 index 000000000..c650ef2e9 --- /dev/null +++ b/rest_framework/static/djangorestframework/css/bootstrap-tweaks.css @@ -0,0 +1,22 @@ +/* + +This CSS file contains some tweaks specific to the included Bootstrap theme. +It's separate from `style.css` so that it can be easily overridden by replacing +a single block in the template. + +*/ + + +.form-actions { + background: transparent; + border-top-color: transparent; + padding-top: 0; +} + +.navbar-inverse .brand a { + color: #999; +} +.navbar-inverse .brand:hover a { + color: white; + text-decoration: none; +} \ No newline at end of file diff --git a/rest_framework/static/djangorestframework/css/bootstrap.min.css b/rest_framework/static/djangorestframework/css/bootstrap.min.css new file mode 100644 index 000000000..373f4b430 --- /dev/null +++ b/rest_framework/static/djangorestframework/css/bootstrap.min.css @@ -0,0 +1,841 @@ +/*! + * Bootstrap v2.1.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ +.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0;} +.clearfix:after{clear:both;} +.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;} +.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;} +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} +audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} +audio:not([controls]){display:none;} +html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} +a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +a:hover,a:active{outline:0;} +sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;} +sup{top:-0.5em;} +sub{bottom:-0.25em;} +img{max-width:100%;width:auto\9;height:auto;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;} +#map_canvas img{max-width:none;} +button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;} +button,input{*overflow:visible;line-height:normal;} +button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;} +button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} +input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield;} +input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;} +textarea{overflow:auto;vertical-align:top;} +body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333333;background-color:#ffffff;} +a{color:#0088cc;text-decoration:none;} +a:hover{color:#005580;text-decoration:underline;} +.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);} +.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px;} +.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;} +.row:after{clear:both;} +[class*="span"]{float:left;min-height:1px;margin-left:20px;} +.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;} +.span12{width:940px;} +.span11{width:860px;} +.span10{width:780px;} +.span9{width:700px;} +.span8{width:620px;} +.span7{width:540px;} +.span6{width:460px;} +.span5{width:380px;} +.span4{width:300px;} +.span3{width:220px;} +.span2{width:140px;} +.span1{width:60px;} +.offset12{margin-left:980px;} +.offset11{margin-left:900px;} +.offset10{margin-left:820px;} +.offset9{margin-left:740px;} +.offset8{margin-left:660px;} +.offset7{margin-left:580px;} +.offset6{margin-left:500px;} +.offset5{margin-left:420px;} +.offset4{margin-left:340px;} +.offset3{margin-left:260px;} +.offset2{margin-left:180px;} +.offset1{margin-left:100px;} +.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;} +.row-fluid:after{clear:both;} +.row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;} +.row-fluid [class*="span"]:first-child{margin-left:0;} +.row-fluid .span12{width:100%;*width:99.94680851063829%;} +.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%;} +.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%;} +.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%;} +.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%;} +.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%;} +.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%;} +.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%;} +.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%;} +.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%;} +.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%;} +.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%;} +.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%;} +.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%;} +.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%;} +.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%;} +.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%;} +.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%;} +.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%;} +.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%;} +.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%;} +.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%;} +.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%;} +.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%;} +.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%;} +.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%;} +.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%;} +.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%;} +.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%;} +.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%;} +.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%;} +.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%;} +.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%;} +.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%;} +.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%;} +.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%;} +[class*="span"].hide,.row-fluid [class*="span"].hide{display:none;} +[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right;} +.container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";line-height:0;} +.container:after{clear:both;} +.container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";line-height:0;} +.container-fluid:after{clear:both;} +p{margin:0 0 10px;} +.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px;} +small{font-size:85%;} +strong{font-weight:bold;} +em{font-style:italic;} +cite{font-style:normal;} +.muted{color:#999999;} +.text-warning{color:#c09853;} +.text-error{color:#b94a48;} +.text-info{color:#3a87ad;} +.text-success{color:#468847;} +h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:1;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999999;} +h1{font-size:36px;line-height:40px;} +h2{font-size:30px;line-height:40px;} +h3{font-size:24px;line-height:40px;} +h4{font-size:18px;line-height:20px;} +h5{font-size:14px;line-height:20px;} +h6{font-size:12px;line-height:20px;} +h1 small{font-size:24px;} +h2 small{font-size:18px;} +h3 small{font-size:14px;} +h4 small{font-size:14px;} +.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eeeeee;} +ul,ol{padding:0;margin:0 0 10px 25px;} +ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} +li{line-height:20px;} +ul.unstyled,ol.unstyled{margin-left:0;list-style:none;} +dl{margin-bottom:20px;} +dt,dd{line-height:20px;} +dt{font-weight:bold;} +dd{margin-left:10px;} +.dl-horizontal{*zoom:1;}.dl-horizontal:before,.dl-horizontal:after{display:table;content:"";line-height:0;} +.dl-horizontal:after{clear:both;} +.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;} +.dl-horizontal dd{margin-left:180px;} +hr{margin:20px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;} +abbr[title]{cursor:help;border-bottom:1px dotted #999999;} +abbr.initialism{font-size:90%;text-transform:uppercase;} +blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px;} +blockquote small{display:block;line-height:20px;color:#999999;}blockquote small:before{content:'\2014 \00A0';} +blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eeeeee;border-left:0;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;} +blockquote.pull-right small:before{content:'';} +blockquote.pull-right small:after{content:'\00A0 \2014';} +q:before,q:after,blockquote:before,blockquote:after{content:"";} +address{display:block;margin-bottom:20px;font-style:normal;line-height:20px;} +code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} +pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{margin-bottom:20px;} +pre code{padding:0;color:inherit;background-color:transparent;border:0;} +.pre-scrollable{max-height:340px;overflow-y:scroll;} +.label,.badge{font-size:11.844px;font-weight:bold;line-height:14px;color:#ffffff;vertical-align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;} +.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;} +a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;} +.label-important,.badge-important{background-color:#b94a48;} +.label-important[href],.badge-important[href]{background-color:#953b39;} +.label-warning,.badge-warning{background-color:#f89406;} +.label-warning[href],.badge-warning[href]{background-color:#c67605;} +.label-success,.badge-success{background-color:#468847;} +.label-success[href],.badge-success[href]{background-color:#356635;} +.label-info,.badge-info{background-color:#3a87ad;} +.label-info[href],.badge-info[href]{background-color:#2d6987;} +.label-inverse,.badge-inverse{background-color:#333333;} +.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;} +.btn .label,.btn .badge{position:relative;top:-1px;} +.btn-mini .label,.btn-mini .badge{top:0;} +table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;} +.table{width:100%;margin-bottom:20px;}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;} +.table th{font-weight:bold;} +.table thead th{vertical-align:bottom;} +.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;} +.table tbody+tbody{border-top:2px solid #dddddd;} +.table-condensed th,.table-condensed td{padding:4px 5px;} +.table-bordered{border:1px solid #dddddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;} +.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;} +.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;} +.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px;} +.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child,.table-bordered tfoot:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;} +.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child,.table-bordered tfoot:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;} +.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;} +.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topleft:4px;} +.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} +.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5;} +table [class*=span],.row-fluid table [class*=span]{display:table-cell;float:none;margin-left:0;} +.table .span1{float:none;width:44px;margin-left:0;} +.table .span2{float:none;width:124px;margin-left:0;} +.table .span3{float:none;width:204px;margin-left:0;} +.table .span4{float:none;width:284px;margin-left:0;} +.table .span5{float:none;width:364px;margin-left:0;} +.table .span6{float:none;width:444px;margin-left:0;} +.table .span7{float:none;width:524px;margin-left:0;} +.table .span8{float:none;width:604px;margin-left:0;} +.table .span9{float:none;width:684px;margin-left:0;} +.table .span10{float:none;width:764px;margin-left:0;} +.table .span11{float:none;width:844px;margin-left:0;} +.table .span12{float:none;width:924px;margin-left:0;} +.table .span13{float:none;width:1004px;margin-left:0;} +.table .span14{float:none;width:1084px;margin-left:0;} +.table .span15{float:none;width:1164px;margin-left:0;} +.table .span16{float:none;width:1244px;margin-left:0;} +.table .span17{float:none;width:1324px;margin-left:0;} +.table .span18{float:none;width:1404px;margin-left:0;} +.table .span19{float:none;width:1484px;margin-left:0;} +.table .span20{float:none;width:1564px;margin-left:0;} +.table .span21{float:none;width:1644px;margin-left:0;} +.table .span22{float:none;width:1724px;margin-left:0;} +.table .span23{float:none;width:1804px;margin-left:0;} +.table .span24{float:none;width:1884px;margin-left:0;} +.table tbody tr.success td{background-color:#dff0d8;} +.table tbody tr.error td{background-color:#f2dede;} +.table tbody tr.warning td{background-color:#fcf8e3;} +.table tbody tr.info td{background-color:#d9edf7;} +.table-hover tbody tr.success:hover td{background-color:#d0e9c6;} +.table-hover tbody tr.error:hover td{background-color:#ebcccc;} +.table-hover tbody tr.warning:hover td{background-color:#faf2cc;} +.table-hover tbody tr.info:hover td{background-color:#c4e3f3;} +form{margin:0 0 20px;} +fieldset{padding:0;margin:0;border:0;} +legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333333;border:0;border-bottom:1px solid #e5e5e5;}legend small{font-size:15px;color:#999999;} +label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px;} +input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;} +label{display:block;margin-bottom:5px;} +select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:9px;font-size:14px;line-height:20px;color:#555555;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +input,textarea,.uneditable-input{width:206px;} +textarea{height:auto;} +textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#ffffff;border:1px solid #cccccc;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear .2s, box-shadow linear .2s;-moz-transition:border linear .2s, box-shadow linear .2s;-o-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82, 168, 236, 0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);} +input[type="radio"],input[type="checkbox"]{margin:4px 0 0;*margin-top:0;margin-top:1px \9;line-height:normal;cursor:pointer;} +input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto;} +select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px;} +select{width:220px;border:1px solid #cccccc;background-color:#ffffff;} +select[multiple],select[size]{height:auto;} +select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +.uneditable-input,.uneditable-textarea{color:#999999;background-color:#fcfcfc;border-color:#cccccc;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;} +.uneditable-input{overflow:hidden;white-space:nowrap;} +.uneditable-textarea{width:auto;height:auto;} +input:-moz-placeholder,textarea:-moz-placeholder{color:#999999;} +input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999999;} +input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999999;} +.radio,.checkbox{min-height:18px;padding-left:18px;} +.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;} +.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;} +.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;} +.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;} +.input-mini{width:60px;} +.input-small{width:90px;} +.input-medium{width:150px;} +.input-large{width:210px;} +.input-xlarge{width:270px;} +.input-xxlarge{width:530px;} +input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0;} +.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block;} +input,textarea,.uneditable-input{margin-left:0;} +.controls-row [class*="span"]+[class*="span"]{margin-left:20px;} +input.span12, textarea.span12, .uneditable-input.span12{width:926px;} +input.span11, textarea.span11, .uneditable-input.span11{width:846px;} +input.span10, textarea.span10, .uneditable-input.span10{width:766px;} +input.span9, textarea.span9, .uneditable-input.span9{width:686px;} +input.span8, textarea.span8, .uneditable-input.span8{width:606px;} +input.span7, textarea.span7, .uneditable-input.span7{width:526px;} +input.span6, textarea.span6, .uneditable-input.span6{width:446px;} +input.span5, textarea.span5, .uneditable-input.span5{width:366px;} +input.span4, textarea.span4, .uneditable-input.span4{width:286px;} +input.span3, textarea.span3, .uneditable-input.span3{width:206px;} +input.span2, textarea.span2, .uneditable-input.span2{width:126px;} +input.span1, textarea.span1, .uneditable-input.span1{width:46px;} +.controls-row{*zoom:1;}.controls-row:before,.controls-row:after{display:table;content:"";line-height:0;} +.controls-row:after{clear:both;} +.controls-row [class*="span"]{float:left;} +input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eeeeee;} +input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent;} +.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;} +.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;} +.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;} +.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;} +.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;} +.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;} +.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;} +.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;} +.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;} +.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;} +.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;} +.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;} +.control-group.info>label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad;} +.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad;} +.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;} +.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad;} +input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;} +.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";line-height:0;} +.form-actions:after{clear:both;} +.help-block,.help-inline{color:#595959;} +.help-block{display:block;margin-bottom:10px;} +.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;} +.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap;}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;font-size:14px;vertical-align:top;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2;} +.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #ffffff;background-color:#eeeeee;border:1px solid #ccc;} +.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546;} +.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;} +.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-append .add-on,.input-append .btn{margin-left:-1px;} +.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;} +.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;} +.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;} +.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;} +.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;} +.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;*zoom:1;margin-bottom:0;vertical-align:middle;} +.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;} +.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block;} +.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;} +.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;} +.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0;} +.control-group{margin-bottom:10px;} +legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate;} +.form-horizontal .control-group{margin-bottom:20px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";line-height:0;} +.form-horizontal .control-group:after{clear:both;} +.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right;} +.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0;}.form-horizontal .controls:first-child{*padding-left:180px;} +.form-horizontal .help-block{margin-bottom:0;} +.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block{margin-top:10px;} +.form-horizontal .form-actions{padding-left:180px;} +.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 14px;margin-bottom:0;font-size:14px;line-height:20px;*line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333333;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(to bottom, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border:1px solid #bbbbbb;*border:0;border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333333;background-color:#e6e6e6;*background-color:#d9d9d9;} +.btn:active,.btn.active{background-color:#cccccc \9;} +.btn:first-child{*margin-left:0;} +.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;} +.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);} +.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn-large{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.btn-large [class^="icon-"]{margin-top:2px;} +.btn-small{padding:3px 9px;font-size:12px;line-height:18px;} +.btn-small [class^="icon-"]{margin-top:0;} +.btn-mini{padding:2px 6px;font-size:11px;line-height:17px;} +.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;} +.btn-block+.btn-block{margin-top:5px;} +input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%;} +.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);} +.btn{border-color:#c5c5c5;border-color:rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25);} +.btn-primary{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#006dcc;background-image:-moz-linear-gradient(top, #0088cc, #0044cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));background-image:-webkit-linear-gradient(top, #0088cc, #0044cc);background-image:-o-linear-gradient(top, #0088cc, #0044cc);background-image:linear-gradient(to bottom, #0088cc, #0044cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);border-color:#0044cc #0044cc #002a80;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#0044cc;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#ffffff;background-color:#0044cc;*background-color:#003bb3;} +.btn-primary:active,.btn-primary.active{background-color:#003399 \9;} +.btn-warning{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(to bottom, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#ffffff;background-color:#f89406;*background-color:#df8505;} +.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;} +.btn-danger{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(to bottom, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#ffffff;background-color:#bd362f;*background-color:#a9302a;} +.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;} +.btn-success{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(to bottom, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#ffffff;background-color:#51a351;*background-color:#499249;} +.btn-success:active,.btn-success.active{background-color:#408140 \9;} +.btn-info{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(to bottom, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#ffffff;background-color:#2f96b4;*background-color:#2a85a0;} +.btn-info:active,.btn-info.active{background-color:#24748c \9;} +.btn-inverse{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#363636;background-image:-moz-linear-gradient(top, #444444, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));background-image:-webkit-linear-gradient(top, #444444, #222222);background-image:-o-linear-gradient(top, #444444, #222222);background-image:linear-gradient(to bottom, #444444, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#222222;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#ffffff;background-color:#222222;*background-color:#151515;} +.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;} +button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;} +button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;} +button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;} +button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;} +.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn-link{border-color:transparent;cursor:pointer;color:#0088cc;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent;} +.btn-link[disabled]:hover{color:#333333;text-decoration:none;} +[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;margin-top:1px;} +.icon-white,.nav-tabs>.active>a>[class^="icon-"],.nav-tabs>.active>a>[class*=" icon-"],.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png");} +.icon-glass{background-position:0 0;} +.icon-music{background-position:-24px 0;} +.icon-search{background-position:-48px 0;} +.icon-envelope{background-position:-72px 0;} +.icon-heart{background-position:-96px 0;} +.icon-star{background-position:-120px 0;} +.icon-star-empty{background-position:-144px 0;} +.icon-user{background-position:-168px 0;} +.icon-film{background-position:-192px 0;} +.icon-th-large{background-position:-216px 0;} +.icon-th{background-position:-240px 0;} +.icon-th-list{background-position:-264px 0;} +.icon-ok{background-position:-288px 0;} +.icon-remove{background-position:-312px 0;} +.icon-zoom-in{background-position:-336px 0;} +.icon-zoom-out{background-position:-360px 0;} +.icon-off{background-position:-384px 0;} +.icon-signal{background-position:-408px 0;} +.icon-cog{background-position:-432px 0;} +.icon-trash{background-position:-456px 0;} +.icon-home{background-position:0 -24px;} +.icon-file{background-position:-24px -24px;} +.icon-time{background-position:-48px -24px;} +.icon-road{background-position:-72px -24px;} +.icon-download-alt{background-position:-96px -24px;} +.icon-download{background-position:-120px -24px;} +.icon-upload{background-position:-144px -24px;} +.icon-inbox{background-position:-168px -24px;} +.icon-play-circle{background-position:-192px -24px;} +.icon-repeat{background-position:-216px -24px;} +.icon-refresh{background-position:-240px -24px;} +.icon-list-alt{background-position:-264px -24px;} +.icon-lock{background-position:-287px -24px;} +.icon-flag{background-position:-312px -24px;} +.icon-headphones{background-position:-336px -24px;} +.icon-volume-off{background-position:-360px -24px;} +.icon-volume-down{background-position:-384px -24px;} +.icon-volume-up{background-position:-408px -24px;} +.icon-qrcode{background-position:-432px -24px;} +.icon-barcode{background-position:-456px -24px;} +.icon-tag{background-position:0 -48px;} +.icon-tags{background-position:-25px -48px;} +.icon-book{background-position:-48px -48px;} +.icon-bookmark{background-position:-72px -48px;} +.icon-print{background-position:-96px -48px;} +.icon-camera{background-position:-120px -48px;} +.icon-font{background-position:-144px -48px;} +.icon-bold{background-position:-167px -48px;} +.icon-italic{background-position:-192px -48px;} +.icon-text-height{background-position:-216px -48px;} +.icon-text-width{background-position:-240px -48px;} +.icon-align-left{background-position:-264px -48px;} +.icon-align-center{background-position:-288px -48px;} +.icon-align-right{background-position:-312px -48px;} +.icon-align-justify{background-position:-336px -48px;} +.icon-list{background-position:-360px -48px;} +.icon-indent-left{background-position:-384px -48px;} +.icon-indent-right{background-position:-408px -48px;} +.icon-facetime-video{background-position:-432px -48px;} +.icon-picture{background-position:-456px -48px;} +.icon-pencil{background-position:0 -72px;} +.icon-map-marker{background-position:-24px -72px;} +.icon-adjust{background-position:-48px -72px;} +.icon-tint{background-position:-72px -72px;} +.icon-edit{background-position:-96px -72px;} +.icon-share{background-position:-120px -72px;} +.icon-check{background-position:-144px -72px;} +.icon-move{background-position:-168px -72px;} +.icon-step-backward{background-position:-192px -72px;} +.icon-fast-backward{background-position:-216px -72px;} +.icon-backward{background-position:-240px -72px;} +.icon-play{background-position:-264px -72px;} +.icon-pause{background-position:-288px -72px;} +.icon-stop{background-position:-312px -72px;} +.icon-forward{background-position:-336px -72px;} +.icon-fast-forward{background-position:-360px -72px;} +.icon-step-forward{background-position:-384px -72px;} +.icon-eject{background-position:-408px -72px;} +.icon-chevron-left{background-position:-432px -72px;} +.icon-chevron-right{background-position:-456px -72px;} +.icon-plus-sign{background-position:0 -96px;} +.icon-minus-sign{background-position:-24px -96px;} +.icon-remove-sign{background-position:-48px -96px;} +.icon-ok-sign{background-position:-72px -96px;} +.icon-question-sign{background-position:-96px -96px;} +.icon-info-sign{background-position:-120px -96px;} +.icon-screenshot{background-position:-144px -96px;} +.icon-remove-circle{background-position:-168px -96px;} +.icon-ok-circle{background-position:-192px -96px;} +.icon-ban-circle{background-position:-216px -96px;} +.icon-arrow-left{background-position:-240px -96px;} +.icon-arrow-right{background-position:-264px -96px;} +.icon-arrow-up{background-position:-289px -96px;} +.icon-arrow-down{background-position:-312px -96px;} +.icon-share-alt{background-position:-336px -96px;} +.icon-resize-full{background-position:-360px -96px;} +.icon-resize-small{background-position:-384px -96px;} +.icon-plus{background-position:-408px -96px;} +.icon-minus{background-position:-433px -96px;} +.icon-asterisk{background-position:-456px -96px;} +.icon-exclamation-sign{background-position:0 -120px;} +.icon-gift{background-position:-24px -120px;} +.icon-leaf{background-position:-48px -120px;} +.icon-fire{background-position:-72px -120px;} +.icon-eye-open{background-position:-96px -120px;} +.icon-eye-close{background-position:-120px -120px;} +.icon-warning-sign{background-position:-144px -120px;} +.icon-plane{background-position:-168px -120px;} +.icon-calendar{background-position:-192px -120px;} +.icon-random{background-position:-216px -120px;width:16px;} +.icon-comment{background-position:-240px -120px;} +.icon-magnet{background-position:-264px -120px;} +.icon-chevron-up{background-position:-288px -120px;} +.icon-chevron-down{background-position:-313px -119px;} +.icon-retweet{background-position:-336px -120px;} +.icon-shopping-cart{background-position:-360px -120px;} +.icon-folder-close{background-position:-384px -120px;} +.icon-folder-open{background-position:-408px -120px;width:16px;} +.icon-resize-vertical{background-position:-432px -119px;} +.icon-resize-horizontal{background-position:-456px -118px;} +.icon-hdd{background-position:0 -144px;} +.icon-bullhorn{background-position:-24px -144px;} +.icon-bell{background-position:-48px -144px;} +.icon-certificate{background-position:-72px -144px;} +.icon-thumbs-up{background-position:-96px -144px;} +.icon-thumbs-down{background-position:-120px -144px;} +.icon-hand-right{background-position:-144px -144px;} +.icon-hand-left{background-position:-168px -144px;} +.icon-hand-up{background-position:-192px -144px;} +.icon-hand-down{background-position:-216px -144px;} +.icon-circle-arrow-right{background-position:-240px -144px;} +.icon-circle-arrow-left{background-position:-264px -144px;} +.icon-circle-arrow-up{background-position:-288px -144px;} +.icon-circle-arrow-down{background-position:-312px -144px;} +.icon-globe{background-position:-336px -144px;} +.icon-wrench{background-position:-360px -144px;} +.icon-tasks{background-position:-384px -144px;} +.icon-filter{background-position:-408px -144px;} +.icon-briefcase{background-position:-432px -144px;} +.icon-fullscreen{background-position:-456px -144px;} +.btn-group{position:relative;font-size:0;vertical-align:middle;white-space:nowrap;*margin-left:.3em;}.btn-group:first-child{*margin-left:0;} +.btn-group+.btn-group{margin-left:5px;} +.btn-toolbar{font-size:0;margin-top:10px;margin-bottom:10px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;} +.btn-toolbar .btn+.btn,.btn-toolbar .btn-group+.btn,.btn-toolbar .btn+.btn-group{margin-left:5px;} +.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.btn-group>.btn+.btn{margin-left:-1px;} +.btn-group>.btn,.btn-group>.dropdown-menu{font-size:14px;} +.btn-group>.btn-mini{font-size:11px;} +.btn-group>.btn-small{font-size:12px;} +.btn-group>.btn-large{font-size:16px;} +.btn-group>.btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;} +.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;} +.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;} +.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;} +.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2;} +.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;} +.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:5px;*padding-bottom:5px;} +.btn-group>.btn-mini+.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:2px;*padding-bottom:2px;} +.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px;} +.btn-group>.btn-large+.dropdown-toggle{padding-left:12px;padding-right:12px;*padding-top:7px;*padding-bottom:7px;} +.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);} +.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6;} +.btn-group.open .btn-primary.dropdown-toggle{background-color:#0044cc;} +.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406;} +.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f;} +.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351;} +.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4;} +.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222222;} +.btn .caret{margin-top:8px;margin-left:0;} +.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px;} +.btn-large .caret{border-left-width:5px;border-right-width:5px;border-top-width:5px;} +.dropup .btn-large .caret{border-bottom:5px solid #000000;border-top:0;} +.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;} +.btn-group-vertical{display:inline-block;*display:inline;*zoom:1;} +.btn-group-vertical .btn{display:block;float:none;width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.btn-group-vertical .btn+.btn{margin-left:0;margin-top:-1px;} +.btn-group-vertical .btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;} +.btn-group-vertical .btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;} +.btn-group-vertical .btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0;} +.btn-group-vertical .btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;} +.nav{margin-left:0;margin-bottom:20px;list-style:none;} +.nav>li>a{display:block;} +.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;} +.nav>.pull-right{float:right;} +.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;} +.nav li+.nav-header{margin-top:9px;} +.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;} +.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} +.nav-list>li>a{padding:3px 15px;} +.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;} +.nav-list [class^="icon-"]{margin-right:2px;} +.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;} +.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";line-height:0;} +.nav-tabs:after,.nav-pills:after{clear:both;} +.nav-tabs>li,.nav-pills>li{float:left;} +.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;} +.nav-tabs{border-bottom:1px solid #ddd;} +.nav-tabs>li{margin-bottom:-1px;} +.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;} +.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} +.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;} +.nav-stacked>li{float:none;} +.nav-stacked>li>a{margin-right:0;} +.nav-tabs.nav-stacked{border-bottom:0;} +.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;} +.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;} +.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;} +.nav-pills.nav-stacked>li>a{margin-bottom:3px;} +.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;} +.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;} +.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.nav .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;} +.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;} +.nav-tabs .dropdown-toggle .caret{margin-top:8px;} +.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff;} +.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555555;border-bottom-color:#555555;} +.nav>.dropdown.active>a:hover{cursor:pointer;} +.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;} +.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);} +.tabs-stacked .open>a:hover{border-color:#999999;} +.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";line-height:0;} +.tabbable:after{clear:both;} +.tab-content{overflow:auto;} +.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0;} +.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;} +.tab-content>.active,.pill-content>.active{display:block;} +.tabs-below>.nav-tabs{border-top:1px solid #ddd;} +.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0;} +.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below>.nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;} +.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd;} +.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none;} +.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;} +.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;} +.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;} +.tabs-left>.nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;} +.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;} +.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;} +.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;} +.tabs-right>.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;} +.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;} +.nav>.disabled>a{color:#999999;} +.nav>.disabled>a:hover{text-decoration:none;background-color:transparent;cursor:default;} +.navbar{overflow:visible;margin-bottom:20px;color:#777777;*position:relative;*z-index:2;} +.navbar-inner{min-height:40px;padding-left:20px;padding-right:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top, #ffffff, #f2f2f2);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));background-image:-webkit-linear-gradient(top, #ffffff, #f2f2f2);background-image:-o-linear-gradient(top, #ffffff, #f2f2f2);background-image:linear-gradient(to bottom, #ffffff, #f2f2f2);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);-moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);*zoom:1;}.navbar-inner:before,.navbar-inner:after{display:table;content:"";line-height:0;} +.navbar-inner:after{clear:both;} +.navbar .container{width:auto;} +.nav-collapse.collapse{height:auto;} +.navbar .brand{float:left;display:block;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777777;text-shadow:0 1px 0 #ffffff;}.navbar .brand:hover{text-decoration:none;} +.navbar-text{margin-bottom:0;line-height:40px;} +.navbar-link{color:#777777;}.navbar-link:hover{color:#333333;} +.navbar .divider-vertical{height:40px;margin:0 9px;border-left:1px solid #f2f2f2;border-right:1px solid #ffffff;} +.navbar .btn,.navbar .btn-group{margin-top:5px;} +.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0;} +.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";line-height:0;} +.navbar-form:after{clear:both;} +.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;} +.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0;} +.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;} +.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;} +.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0;}.navbar-search .search-query{margin-bottom:0;padding:4px 14px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;} +.navbar-static-top{position:static;width:100%;margin-bottom:0;}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;} +.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px;} +.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0;} +.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;} +.navbar-fixed-top{top:0;} +.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);} +.navbar-fixed-bottom{bottom:0;}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);} +.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;} +.navbar .nav.pull-right{float:right;margin-right:0;} +.navbar .nav>li{float:left;} +.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777777;text-decoration:none;text-shadow:0 1px 0 #ffffff;} +.navbar .nav .dropdown-toggle .caret{margin-top:8px;} +.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{background-color:transparent;color:#333333;text-decoration:none;} +.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);-moz-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);} +.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#ededed;background-image:-moz-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));background-image:-webkit-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:-o-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:linear-gradient(to bottom, #f2f2f2, #e5e5e5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e5e5e5;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#ffffff;background-color:#e5e5e5;*background-color:#d9d9d9;} +.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#cccccc \9;} +.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);} +.btn-navbar .icon-bar+.icon-bar{margin-top:3px;} +.navbar .nav>li>.dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;} +.navbar .nav>li>.dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;} +.navbar-fixed-bottom .nav>li>.dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;} +.navbar-fixed-bottom .nav>li>.dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;} +.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:#e5e5e5;color:#555555;} +.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777777;border-bottom-color:#777777;} +.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555555;border-bottom-color:#555555;} +.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{left:auto;right:0;}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{left:auto;right:12px;} +.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{left:auto;right:13px;} +.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{left:auto;right:100%;margin-left:0;margin-right:-1px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;} +.navbar-inverse{color:#999999;}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top, #222222, #111111);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));background-image:-webkit-linear-gradient(top, #222222, #111111);background-image:-o-linear-gradient(top, #222222, #111111);background-image:linear-gradient(to bottom, #222222, #111111);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);border-color:#252525;} +.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999999;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#ffffff;} +.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{background-color:transparent;color:#ffffff;} +.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#ffffff;background-color:#111111;} +.navbar-inverse .navbar-link{color:#999999;}.navbar-inverse .navbar-link:hover{color:#ffffff;} +.navbar-inverse .divider-vertical{border-left-color:#111111;border-right-color:#222222;} +.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{background-color:#111111;color:#ffffff;} +.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999999;border-bottom-color:#999999;} +.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;} +.navbar-inverse .navbar-search .search-query{color:#ffffff;background-color:#515151;border-color:#111111;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none;}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#cccccc;} +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#cccccc;} +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;} +.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;} +.navbar-inverse .btn-navbar{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e0e0e;background-image:-moz-linear-gradient(top, #151515, #040404);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));background-image:-webkit-linear-gradient(top, #151515, #040404);background-image:-o-linear-gradient(top, #151515, #040404);background-image:linear-gradient(to bottom, #151515, #040404);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);border-color:#040404 #040404 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#040404;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#ffffff;background-color:#040404;*background-color:#000000;} +.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000000 \9;} +.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;} +.breadcrumb .divider{padding:0 5px;color:#ccc;} +.breadcrumb .active{color:#999999;} +.pagination{height:40px;margin:20px 0;} +.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);} +.pagination ul>li{display:inline;} +.pagination ul>li>a,.pagination ul>li>span{float:left;padding:0 14px;line-height:38px;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd;border-left-width:0;} +.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5;} +.pagination ul>.active>a,.pagination ul>.active>span{color:#999999;cursor:default;} +.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999999;background-color:transparent;cursor:default;} +.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.pagination-centered{text-align:center;} +.pagination-right{text-align:right;} +.pager{margin:20px 0;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";line-height:0;} +.pager:after{clear:both;} +.pager li{display:inline;} +.pager a,.pager span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;} +.pager a:hover{text-decoration:none;background-color:#f5f5f5;} +.pager .next a,.pager .next span{float:right;} +.pager .previous a{float:left;} +.pager .disabled a,.pager .disabled a:hover,.pager .disabled span{color:#999999;background-color:#fff;cursor:default;} +.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";line-height:0;} +.thumbnails:after{clear:both;} +.row-fluid .thumbnails{margin-left:0;} +.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px;} +.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;} +a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);} +.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;} +.thumbnail .caption{padding:9px;color:#555555;} +.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;} +.alert h4{margin:0;} +.alert .close{position:relative;top:-2px;right:-21px;line-height:20px;} +.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;} +.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;} +.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;} +.alert-block{padding-top:14px;padding-bottom:14px;} +.alert-block>p,.alert-block>ul{margin-bottom:0;} +.alert-block p+p{margin-top:5px;} +@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-o-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(to bottom, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.progress .bar{width:0%;height:100%;color:#ffffff;float:left;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(to bottom, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;} +.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);} +.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;} +.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;} +.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(to bottom, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);} +.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(to bottom, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);} +.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(to bottom, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);} +.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(to bottom, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);} +.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;} +.hero-unit p{font-size:18px;font-weight:200;line-height:30px;color:inherit;} +.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);} +.tooltip.top{margin-top:-3px;} +.tooltip.right{margin-left:3px;} +.tooltip.bottom{margin-top:3px;} +.tooltip.left{margin-left:-3px;} +.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid;} +.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000;} +.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000;} +.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000;} +.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000;} +.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;background-color:#ffffff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);}.popover.top{margin-bottom:10px;} +.popover.right{margin-left:10px;} +.popover.bottom{margin-top:10px;} +.popover.left{margin-right:10px;} +.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0;} +.popover-content{padding:9px 14px;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;} +.popover .arrow,.popover .arrow:after{position:absolute;display:inline-block;width:0;height:0;border-color:transparent;border-style:solid;} +.popover .arrow:after{content:"";z-index:-1;} +.popover.top .arrow{bottom:-10px;left:50%;margin-left:-10px;border-width:10px 10px 0;border-top-color:#ffffff;}.popover.top .arrow:after{border-width:11px 11px 0;border-top-color:rgba(0, 0, 0, 0.25);bottom:-1px;left:-11px;} +.popover.right .arrow{top:50%;left:-10px;margin-top:-10px;border-width:10px 10px 10px 0;border-right-color:#ffffff;}.popover.right .arrow:after{border-width:11px 11px 11px 0;border-right-color:rgba(0, 0, 0, 0.25);bottom:-11px;left:-1px;} +.popover.bottom .arrow{top:-10px;left:50%;margin-left:-10px;border-width:0 10px 10px;border-bottom-color:#ffffff;}.popover.bottom .arrow:after{border-width:0 11px 11px;border-bottom-color:rgba(0, 0, 0, 0.25);top:-1px;left:-11px;} +.popover.left .arrow{top:50%;right:-10px;margin-top:-10px;border-width:10px 0 10px 10px;border-left-color:#ffffff;}.popover.left .arrow:after{border-width:11px 0 11px 11px;border-left-color:rgba(0, 0, 0, 0.25);bottom:-11px;right:-1px;} +.modal-open .modal .dropdown-menu{z-index:2050;} +.modal-open .modal .dropdown.open{*z-index:2050;} +.modal-open .modal .popover{z-index:2060;} +.modal-open .modal .tooltip{z-index:2080;} +.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;} +.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);} +.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;} +.modal.fade.in{top:50%;} +.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;} +.modal-header h3{margin:0;line-height:30px;} +.modal-body{overflow-y:auto;max-height:400px;padding:15px;} +.modal-form{margin-bottom:0;} +.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";line-height:0;} +.modal-footer:after{clear:both;} +.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;} +.modal-footer .btn-group .btn+.btn{margin-left:-1px;} +.dropup,.dropdown{position:relative;} +.dropdown-toggle{*margin-bottom:-3px;} +.dropdown-toggle:active,.open .dropdown-toggle{outline:0;} +.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";} +.dropdown .caret{margin-top:8px;margin-left:2px;} +.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#ffffff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}.dropdown-menu.pull-right{right:0;left:auto;} +.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;} +.dropdown-menu a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333333;white-space:nowrap;} +.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{text-decoration:none;color:#ffffff;background-color:#0088cc;background-color:#0081c2;background-image:-moz-linear-gradient(top, #0088cc, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));background-image:-webkit-linear-gradient(top, #0088cc, #0077b3);background-image:-o-linear-gradient(top, #0088cc, #0077b3);background-image:linear-gradient(to bottom, #0088cc, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);} +.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;outline:0;background-color:#0088cc;background-color:#0081c2;background-image:-moz-linear-gradient(top, #0088cc, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));background-image:-webkit-linear-gradient(top, #0088cc, #0077b3);background-image:-o-linear-gradient(top, #0088cc, #0077b3);background-image:linear-gradient(to bottom, #0088cc, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);} +.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999999;} +.dropdown-menu .disabled>a:hover{text-decoration:none;background-color:transparent;cursor:default;} +.open{*z-index:1000;}.open >.dropdown-menu{display:block;} +.pull-right>.dropdown-menu{right:0;left:auto;} +.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"";} +.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;} +.dropdown-submenu{position:relative;} +.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;} +.dropdown-submenu:hover>.dropdown-menu{display:block;} +.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#cccccc;margin-top:5px;margin-right:-10px;} +.dropdown-submenu:hover>a:after{border-left-color:#ffffff;} +.dropdown .dropdown-menu .nav-header{padding-left:20px;padding-right:20px;} +.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.accordion{margin-bottom:20px;} +.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.accordion-heading{border-bottom:0;} +.accordion-heading .accordion-toggle{display:block;padding:8px 15px;} +.accordion-toggle{cursor:pointer;} +.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;} +.carousel{position:relative;margin-bottom:20px;line-height:1;} +.carousel-inner{overflow:hidden;width:100%;position:relative;} +.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;} +.carousel .item>img{display:block;line-height:1;} +.carousel .active,.carousel .next,.carousel .prev{display:block;} +.carousel .active{left:0;} +.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;} +.carousel .next{left:100%;} +.carousel .prev{left:-100%;} +.carousel .next.left,.carousel .prev.right{left:0;} +.carousel .active.left{left:-100%;} +.carousel .active.right{left:100%;} +.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;} +.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);} +.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:15px;background:#333333;background:rgba(0, 0, 0, 0.75);} +.carousel-caption h4,.carousel-caption p{color:#ffffff;line-height:20px;} +.carousel-caption h4{margin:0 0 5px;} +.carousel-caption p{margin-bottom:0;} +.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);} +.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;cursor:pointer;opacity:0.4;filter:alpha(opacity=40);} +button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;} +.pull-right{float:right;} +.pull-left{float:left;} +.hide{display:none;} +.show{display:block;} +.invisible{visibility:hidden;} +.affix{position:fixed;} +.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;}.fade.in{opacity:1;} +.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;}.collapse.in{height:auto;} +.hidden{display:none;visibility:hidden;} +.visible-phone{display:none !important;} +.visible-tablet{display:none !important;} +.hidden-desktop{display:none !important;} +.visible-desktop{display:inherit !important;} +@media (min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit !important;} .visible-desktop{display:none !important ;} .visible-tablet{display:inherit !important;} .hidden-tablet{display:none !important;}}@media (max-width:767px){.hidden-desktop{display:inherit !important;} .visible-desktop{display:none !important;} .visible-phone{display:inherit !important;} .hidden-phone{display:none !important;}}@media (max-width:767px){body{padding-left:20px;padding-right:20px;} .navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-left:-20px;margin-right:-20px;} .container-fluid{padding:0;} .dl-horizontal dt{float:none;clear:none;width:auto;text-align:left;} .dl-horizontal dd{margin-left:0;} .container{width:auto;} .row-fluid{width:100%;} .row,.thumbnails{margin-left:0;} .thumbnails>li{float:none;margin-left:0;} [class*="span"],.row-fluid [class*="span"]{float:none;display:block;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;} .span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;} .input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;} .input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto;} .controls-row [class*="span"]+[class*="span"]{margin-left:0;} .modal{position:fixed;top:20px;left:20px;right:20px;width:auto;margin:0;}.modal.fade.in{top:auto;}}@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:20px;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{top:10px;left:10px;right:10px;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;} .row:after{clear:both;} [class*="span"]{float:left;min-height:1px;margin-left:20px;} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%;} .row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%;} .row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%;} .row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%;} .row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%;} .row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%;} .row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%;} .row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%;} .row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%;} .row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%;} .row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%;} .row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%;} .row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%;} .row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%;} .row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%;} .row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%;} .row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%;} .row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%;} .row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%;} .row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%;} .row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%;} .row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%;} .row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%;} .row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%;} .row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%;} .row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%;} .row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%;} .row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%;} .row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%;} .row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%;} .row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%;} .row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%;} .row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%;} .row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%;} .row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%;} input,textarea,.uneditable-input{margin-left:0;} .controls-row [class*="span"]+[class*="span"]{margin-left:20px;} input.span12, textarea.span12, .uneditable-input.span12{width:710px;} input.span11, textarea.span11, .uneditable-input.span11{width:648px;} input.span10, textarea.span10, .uneditable-input.span10{width:586px;} input.span9, textarea.span9, .uneditable-input.span9{width:524px;} input.span8, textarea.span8, .uneditable-input.span8{width:462px;} input.span7, textarea.span7, .uneditable-input.span7{width:400px;} input.span6, textarea.span6, .uneditable-input.span6{width:338px;} input.span5, textarea.span5, .uneditable-input.span5{width:276px;} input.span4, textarea.span4, .uneditable-input.span4{width:214px;} input.span3, textarea.span3, .uneditable-input.span3{width:152px;} input.span2, textarea.span2, .uneditable-input.span2{width:90px;} input.span1, textarea.span1, .uneditable-input.span1{width:28px;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;} .row:after{clear:both;} [class*="span"]{float:left;min-height:1px;margin-left:30px;} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%;} .row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%;} .row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%;} .row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%;} .row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%;} .row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%;} .row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%;} .row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%;} .row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%;} .row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%;} .row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%;} .row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%;} .row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%;} .row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%;} .row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%;} .row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%;} .row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%;} .row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%;} .row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%;} .row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%;} .row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%;} .row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%;} .row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%;} .row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%;} .row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%;} .row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%;} .row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%;} .row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%;} .row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%;} .row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%;} .row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%;} .row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%;} .row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%;} .row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%;} .row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%;} input,textarea,.uneditable-input{margin-left:0;} .controls-row [class*="span"]+[class*="span"]{margin-left:30px;} input.span12, textarea.span12, .uneditable-input.span12{width:1156px;} input.span11, textarea.span11, .uneditable-input.span11{width:1056px;} input.span10, textarea.span10, .uneditable-input.span10{width:956px;} input.span9, textarea.span9, .uneditable-input.span9{width:856px;} input.span8, textarea.span8, .uneditable-input.span8{width:756px;} input.span7, textarea.span7, .uneditable-input.span7{width:656px;} input.span6, textarea.span6, .uneditable-input.span6{width:556px;} input.span5, textarea.span5, .uneditable-input.span5{width:456px;} input.span4, textarea.span4, .uneditable-input.span4{width:356px;} input.span3, textarea.span3, .uneditable-input.span3{width:256px;} input.span2, textarea.span2, .uneditable-input.span2{width:156px;} input.span1, textarea.span1, .uneditable-input.span1{width:56px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;} .row-fluid .thumbnails{margin-left:0;}}@media (max-width:979px){body{padding-top:0;} .navbar-fixed-top,.navbar-fixed-bottom{position:static;} .navbar-fixed-top{margin-bottom:20px;} .navbar-fixed-bottom{margin-top:20px;} .navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .nav-collapse{clear:both;} .nav-collapse .nav{float:none;margin:0 0 10px;} .nav-collapse .nav>li{float:none;} .nav-collapse .nav>li>a{margin-bottom:2px;} .nav-collapse .nav>.divider-vertical{display:none;} .nav-collapse .nav .nav-header{color:#777777;text-shadow:none;} .nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} .nav-collapse .dropdown-menu li+li a{margin-bottom:2px;} .nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2;} .navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111111;} .nav-collapse.in .btn-group{margin-top:5px;padding:0;} .nav-collapse .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none;} .nav-collapse .dropdown-menu .divider{display:none;} .nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none;} .nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);} .navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111111;border-bottom-color:#111111;} .navbar .nav-collapse .nav.pull-right{float:none;margin-left:0;} .nav-collapse,.nav-collapse.collapse{overflow:hidden;height:0;} .navbar .btn-navbar{display:block;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;overflow:visible !important;}} diff --git a/rest_framework/static/djangorestframework/css/prettify.css b/rest_framework/static/djangorestframework/css/prettify.css new file mode 100644 index 000000000..d437aff62 --- /dev/null +++ b/rest_framework/static/djangorestframework/css/prettify.css @@ -0,0 +1,30 @@ +.com { color: #93a1a1; } +.lit { color: #195f91; } +.pun, .opn, .clo { color: #93a1a1; } +.fun { color: #dc322f; } +.str, .atv { color: #D14; } +.kwd, .prettyprint .tag { color: #1e347b; } +.typ, .atn, .dec, .var { color: teal; } +.pln { color: #48484c; } + +.prettyprint { + padding: 8px; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} +.prettyprint.linenums { + -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; + -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; + box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin: 0 0 0 33px; /* IE indents via margin-left */ +} +ol.linenums li { + padding-left: 12px; + color: #bebec5; + line-height: 20px; + text-shadow: 0 1px 0 #fff; +} \ No newline at end of file diff --git a/rest_framework/static/djangorestframework/css/style.css b/rest_framework/static/djangorestframework/css/style.css new file mode 100644 index 000000000..cfd41460c --- /dev/null +++ b/rest_framework/static/djangorestframework/css/style.css @@ -0,0 +1,74 @@ +body { + padding-top: 0; + padding-bottom: 1em; +} + +/* The navbar is fixed at >= 980px wide, so add padding to the body to prevent +content running up underneath it. */ +@media (min-width:980px) { + body { + padding-top: 60px; + } +} + +h1 { + font-weight: 500; +} + +h2, h3 { + font-weight: 300; +} + +.resource-description, .response-info { + margin-bottom: 2em; +} + +#footer { + border-top: 1px solid #eee; + margin-top: 2em; + padding-top: 1em; + text-align: right; +} + +.version:before { + content: "v"; + opacity: 0.6; + padding-right: 0.25em; +} + +.format-option { + font-family: Menlo, Consolas, "Andale Mono", "Lucida Console", monospace; +} + +#options-form { + position: relative; +} + +/* To allow tooltips to work on disabled elements */ +.disabled-tooltip-shield { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +#options-form { + margin-right: 1em; +} + +.errorlist { + margin-top: 0.5em; +} + +pre { + overflow: auto; + word-wrap: normal; + white-space: pre; + font-size: 12px; +} + +.page-header { + border-bottom: none; + padding-bottom: 0px; +} \ No newline at end of file diff --git a/rest_framework/static/djangorestframework/img/glyphicons-halflings-white.png b/rest_framework/static/djangorestframework/img/glyphicons-halflings-white.png new file mode 100755 index 0000000000000000000000000000000000000000..3bf6484a29d8da269f9bc874b25493a45fae3bae GIT binary patch literal 8777 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 B7G?kd literal 0 HcmV?d00001 diff --git a/rest_framework/static/djangorestframework/img/glyphicons-halflings.png b/rest_framework/static/djangorestframework/img/glyphicons-halflings.png new file mode 100755 index 0000000000000000000000000000000000000000..36c3b1ed9e71dce20dca9e35c36c97fd8afd718c GIT binary patch literal 12762 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 JeEj_Fe*q52p!xs+ literal 0 HcmV?d00001 diff --git a/rest_framework/static/djangorestframework/js/bootstrap.min.js b/rest_framework/static/djangorestframework/js/bootstrap.min.js new file mode 100755 index 000000000..e0b220f40 --- /dev/null +++ b/rest_framework/static/djangorestframework/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/** +* Bootstrap.js by @fat & @mdo +* plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js, bootstrap-scrollspy.js, bootstrap-tab.js, bootstrap-tooltip.js, bootstrap-popover.js, bootstrap-affix.js, bootstrap-alert.js, bootstrap-button.js, bootstrap-collapse.js, bootstrap-carousel.js, bootstrap-typeahead.js +* Copyright 2012 Twitter, Inc. +* http://www.apache.org/licenses/LICENSE-2.0.txt +*/ +!function(a){a(function(){a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery),!function(a){var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;a("body").addClass("modal-open"),this.isShown=!0,this.escape(),this.backdrop(function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in").attr("aria-hidden",!1).focus(),b.enforceFocus(),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();var c=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,a("body").removeClass("modal-open"),this.escape(),a(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),a.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var b=this;a(document).on("focusin.modal",function(a){b.$element[0]!==a.target&&!b.$element.has(a.target).length&&b.$element.focus()})},escape:function(){var a=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(b){b.which==27&&a.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),b.hideModal()},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),b.hideModal()})},hideModal:function(a){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('