├── backend ├── appengine │ ├── static │ │ ├── robots.txt │ │ ├── img │ │ │ ├── ajax.gif │ │ │ └── favicon.ico │ │ ├── font-awesome-4.2.0 │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ └── fontawesome-webfont.woff │ │ │ └── css │ │ │ │ └── font-awesome.min.css │ │ ├── bootstrap │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ └── glyphicons-halflings-regular.woff │ │ │ └── css │ │ │ │ ├── bootstrap-theme.min.css │ │ │ │ ├── bootstrap-social.css │ │ │ │ ├── bootstrap-theme.css.map │ │ │ │ └── bootstrap-theme.css │ │ ├── css │ │ │ └── default.css │ │ └── permission │ │ │ ├── html │ │ │ ├── group_td.html │ │ │ ├── permission_form.html │ │ │ └── permission_table.html │ │ │ └── js │ │ │ └── permissions.js │ ├── routes │ │ ├── __init__.py │ │ ├── admin │ │ │ ├── __init__.py │ │ │ └── home.py │ │ ├── cookie │ │ │ ├── __init__.py │ │ │ └── tasks.py │ │ ├── login │ │ │ ├── __init__.py │ │ │ ├── pending.py │ │ │ ├── home.py │ │ │ ├── google.py │ │ │ ├── passwordless.py │ │ │ └── facebook.py │ │ ├── permission │ │ │ ├── __init__.py │ │ │ ├── home.py │ │ │ └── admin.py │ │ ├── home.py │ │ ├── logout.py │ │ ├── warmup.py │ │ └── account.py │ ├── config │ │ ├── __init__.py │ │ ├── template.py │ │ └── template_middleware.py │ ├── cron.yaml │ ├── templates │ │ ├── admin │ │ │ ├── base.html │ │ │ └── home.html │ │ ├── base │ │ │ ├── 404.html │ │ │ ├── 400.html │ │ │ └── base.html │ │ ├── updown │ │ │ ├── ok.html │ │ │ └── home.html │ │ ├── login │ │ │ ├── passwordless │ │ │ │ ├── home.html │ │ │ │ └── form.html │ │ │ ├── pending.html │ │ │ ├── facebook │ │ │ │ └── form.html │ │ │ └── home.html │ │ ├── permission │ │ │ ├── home.html │ │ │ ├── account_form.html │ │ │ └── admin.html │ │ ├── home.html │ │ └── multitenant │ │ │ └── home.html │ ├── index.yaml │ ├── app.yaml │ ├── convention.py │ └── settings.py ├── venv │ ├── dev_requirements.txt │ ├── requirements.txt │ ├── venv.sh │ └── venv.bat ├── test │ ├── admin_tests │ │ ├── __init__.py │ │ └── home_tests.py │ ├── login_tests │ │ ├── __init__.py │ │ ├── home_tests.py │ │ ├── logout.py │ │ ├── google.py │ │ ├── facebook.py │ │ └── passwordless.py │ ├── warmup_tests.py │ ├── HomeTests.py │ ├── cookie_tests.py │ ├── testloader.py │ ├── account_tests.py │ ├── permission_tests.py │ ├── multitenancy_tests.py │ └── base.py ├── apps │ ├── locale_app │ │ ├── __init__.py │ │ └── middleware.py │ ├── permission_app │ │ ├── __init__.py │ │ └── model.py │ └── multitenancy.py └── build_scripts │ └── babel │ ├── babel.cfg │ ├── i18n_extractor.py │ └── locale │ ├── en_US │ └── LC_MESSAGES │ │ └── messages.po │ └── pt_BR │ └── LC_MESSAGES │ └── messages.po ├── .gitignore ├── LICENSE └── README.md /backend/appengine/static/robots.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/venv/dev_requirements.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | mock==1.0.1 3 | mommygae==1.1 4 | -------------------------------------------------------------------------------- /backend/appengine/static/img/ajax.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renzon/tekton/HEAD/backend/appengine/static/img/ajax.gif -------------------------------------------------------------------------------- /backend/test/admin_tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals -------------------------------------------------------------------------------- /backend/test/login_tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals -------------------------------------------------------------------------------- /backend/appengine/routes/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | -------------------------------------------------------------------------------- /backend/appengine/config/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | -------------------------------------------------------------------------------- /backend/appengine/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renzon/tekton/HEAD/backend/appengine/static/img/favicon.ico -------------------------------------------------------------------------------- /backend/apps/locale_app/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | -------------------------------------------------------------------------------- /backend/apps/permission_app/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | -------------------------------------------------------------------------------- /backend/appengine/routes/admin/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | -------------------------------------------------------------------------------- /backend/appengine/routes/cookie/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | -------------------------------------------------------------------------------- /backend/appengine/routes/login/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | -------------------------------------------------------------------------------- /backend/appengine/routes/permission/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | -------------------------------------------------------------------------------- /backend/appengine/cron.yaml: -------------------------------------------------------------------------------- 1 | cron: 2 | - description: application secret renew 3 | url: /cookie/tasks/renew 4 | schedule: every sunday 04:00 5 | -------------------------------------------------------------------------------- /backend/appengine/templates/admin/base.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block tabs %} 3 | {{ select_tab('ADMIN') }} 4 | {% endblock %} -------------------------------------------------------------------------------- /backend/build_scripts/babel/babel.cfg: -------------------------------------------------------------------------------- 1 | [jinja2: **/templates/**] 2 | encoding = utf-8 3 | [python: **.py] 4 | [extractors] 5 | jinja2 = jinja2.ext:babel_extract 6 | -------------------------------------------------------------------------------- /backend/appengine/static/font-awesome-4.2.0/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renzon/tekton/HEAD/backend/appengine/static/font-awesome-4.2.0/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /backend/apps/permission_app/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | ADMIN = 'ADMIN' 5 | 6 | ALL_PERMISSIONS_LIST = [ADMIN] -------------------------------------------------------------------------------- /backend/appengine/static/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renzon/tekton/HEAD/backend/appengine/static/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /backend/appengine/static/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renzon/tekton/HEAD/backend/appengine/static/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /backend/appengine/static/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renzon/tekton/HEAD/backend/appengine/static/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /backend/appengine/static/font-awesome-4.2.0/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renzon/tekton/HEAD/backend/appengine/static/font-awesome-4.2.0/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /backend/appengine/static/font-awesome-4.2.0/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renzon/tekton/HEAD/backend/appengine/static/font-awesome-4.2.0/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /backend/appengine/static/font-awesome-4.2.0/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renzon/tekton/HEAD/backend/appengine/static/font-awesome-4.2.0/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /backend/venv/requirements.txt: -------------------------------------------------------------------------------- 1 | tekton==4.3 2 | gaebusiness==4.5.3 3 | gaecookie==0.7 4 | gaeforms==0.14 5 | gaegraph==3.9 6 | gaepermission==0.12 7 | pytz==2014.4 8 | Babel==1.3 9 | python-slugify==0.0.7 10 | Jinja2==2.7.3 11 | -------------------------------------------------------------------------------- /backend/test/warmup_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from routes.warmup import BaseHandler 5 | 6 | 7 | class BaseTest(GAETestCase): 8 | def test_warmup_success(self): 9 | BaseHandler().get() -------------------------------------------------------------------------------- /backend/test/HomeTests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from routes import home 5 | 6 | 7 | class HomeTests(GAETestCase): 8 | def test_index(self): 9 | response = home.index() 10 | self.assert_can_render(response) 11 | -------------------------------------------------------------------------------- /backend/test/admin_tests/home_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from routes.admin.home import index 5 | 6 | 7 | class HomeTests(GAETestCase): 8 | def test_index(self): 9 | response = index() 10 | self.assert_can_render(response) 11 | -------------------------------------------------------------------------------- /backend/test/login_tests/home_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from routes.login import home 5 | 6 | 7 | class HomeTests(GAETestCase): 8 | def test_index(self): 9 | response = home.index() 10 | self.assert_can_render(response) 11 | -------------------------------------------------------------------------------- /backend/appengine/routes/cookie/tasks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from gaecookie import facade 4 | from gaecookie.decorator import no_csrf 5 | from gaepermission.decorator import login_not_required 6 | 7 | 8 | @login_not_required 9 | @no_csrf 10 | def renew(): 11 | facade.renew().execute() 12 | -------------------------------------------------------------------------------- /backend/appengine/static/css/default.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | margin-top: 30px; 3 | padding-top: 10px; 4 | bottom: 0; 5 | width: 100%; 6 | /* Set the fixed height of the footer here */ 7 | height: 60px; 8 | border-top: solid #DDDDDD 1px; 9 | background-color: #f5f5f5; 10 | } 11 | .pad8 { 12 | margin: 8px 15px; 13 | color: #777 14 | } 15 | 16 | -------------------------------------------------------------------------------- /backend/appengine/templates/base/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block body %} 3 |
4 |
5 |
6 |

{% trans %}Sorry, page not found{% endtrans %}

7 |
8 |
9 |
10 | {% endblock %} 11 | 12 | 13 | -------------------------------------------------------------------------------- /backend/appengine/templates/base/400.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block body %} 3 |
4 |
5 |
6 |

{% trans %}Sorry, an error happened and we are investigating{% endtrans %}

7 |
8 |
9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /backend/appengine/routes/home.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from config.template_middleware import TemplateResponse 4 | from gaecookie.decorator import no_csrf 5 | from gaepermission.decorator import login_not_required 6 | 7 | 8 | @login_not_required 9 | @no_csrf 10 | def index(): 11 | return TemplateResponse(template_path='home.html') 12 | 13 | -------------------------------------------------------------------------------- /backend/appengine/routes/login/pending.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from gaecookie.decorator import no_csrf 4 | from gaepermission import facade 5 | from gaepermission.decorator import login_not_required 6 | 7 | 8 | @no_csrf 9 | @login_not_required 10 | def index(_handler, _resp, pending_id, ticket): 11 | facade.login_checking_email(pending_id, ticket, _resp).execute() 12 | _handler.redirect('/') -------------------------------------------------------------------------------- /backend/test/cookie_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from gaecookie import facade 5 | from routes.cookie.tasks import renew 6 | 7 | 8 | class RenewTests(GAETestCase): 9 | def test_success(self): 10 | signed = facade.sign('foo', 'bar')() 11 | renew() 12 | signed_after_renew = facade.sign('foo', 'bar')() 13 | self.assertNotEqual(signed, signed_after_renew) 14 | -------------------------------------------------------------------------------- /backend/appengine/static/permission/html/group_td.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 9 | 10 | {{ g }} 11 | 12 |
13 | 14 |
15 |
-------------------------------------------------------------------------------- /backend/appengine/templates/updown/ok.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block body %} 3 |
4 |
5 |
6 |
7 |

{% trans %}File Upload Finished{% endtrans %} 8 | Download File

9 | 10 |
11 |
12 |
13 |
14 | {% endblock %} -------------------------------------------------------------------------------- /backend/appengine/routes/permission/home.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from permission_app.model import ADMIN 4 | from gaecookie.decorator import no_csrf 5 | from gaepermission import facade 6 | from config.template_middleware import TemplateResponse 7 | from gaepermission.decorator import permissions 8 | 9 | 10 | @permissions(ADMIN) 11 | @no_csrf 12 | def index(): 13 | path_infos = facade.web_path_security_info() 14 | path_infos = sorted(path_infos, key=lambda i: i.path) 15 | return TemplateResponse({'path_infos': path_infos}, 'permission/home.html') 16 | -------------------------------------------------------------------------------- /backend/appengine/routes/logout.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from google.appengine.api.users import create_logout_url, get_current_user 4 | from gaepermission import facade 5 | from gaepermission.decorator import login_required 6 | from tekton.gae.middleware.redirect import RedirectResponse 7 | 8 | 9 | @login_required 10 | def index(_resp): 11 | facade.logout(_resp).execute() 12 | redirect_url = '/' 13 | google_user = get_current_user() 14 | if google_user: 15 | redirect_url = create_logout_url(redirect_url) 16 | return RedirectResponse(redirect_url) 17 | -------------------------------------------------------------------------------- /backend/appengine/static/permission/html/permission_form.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /backend/appengine/templates/login/passwordless/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/base.html' %} 2 | {% block body %} 3 |
4 |
5 |
6 |

{% trans %}Login Email Instructions{% endtrans %}

7 | 8 |

{% trans %}We will send you an email with the login link using 9 | Passwordless.{% endtrans %}

10 | 11 |

{% trans %}Check your spam box, once some providers can filter this email.{% endtrans %}

12 |
13 |
14 |
15 | 16 | {% endblock %} -------------------------------------------------------------------------------- /backend/appengine/templates/login/pending.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block body %} 3 |
4 |
5 |
6 |

{% trans %}Security Information{% endtrans %}

7 | 8 |

{% trans %}The email provided by {{ provider }} is already in use. For your security, we are going to 9 | send an email to {{ email }} to ensure it is yours.{% endtrans %}

10 | 11 |

{% trans %}This security procedure will be necessary only this time.{% endtrans %}

12 | 13 |

{% trans %}Check your spam box, once some providers can filter this email.{% endtrans %}

14 |
15 |
16 | {% endblock %} -------------------------------------------------------------------------------- /backend/test/login_tests/logout.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from mock import Mock 5 | from routes import logout 6 | from tekton.gae.middleware.redirect import RedirectResponse 7 | 8 | 9 | class LogoutTests(GAETestCase): 10 | def test_logout_without_logged_google_user(self): 11 | response = logout.index(Mock()) 12 | self.assertIsInstance(response, RedirectResponse) 13 | self.assertEqual('/', response.context) 14 | 15 | def test_logout_with_logged_google_user(self): 16 | self.set_current_user() 17 | response = logout.index(Mock()) 18 | self.assertIsInstance(response, RedirectResponse) 19 | self.assertEqual('https://www.google.com/accounts/Logout?continue=http%3A//testbed.example.com/', response.context) 20 | -------------------------------------------------------------------------------- /backend/appengine/config/template.py: -------------------------------------------------------------------------------- 1 | import json 2 | import jinja2 3 | from jinja2 import Markup 4 | from webapp2_extras import i18n 5 | import os 6 | 7 | _base = os.path.dirname(__file__) 8 | _base = os.path.join(_base, '..') 9 | _base = os.path.normpath(_base) 10 | _base_2 = os.path.join(_base, 'templates') 11 | _jinja_environment = jinja2.Environment( 12 | loader=jinja2.FileSystemLoader([_base_2]), 13 | trim_blocks=True, 14 | autoescape=True, 15 | extensions=['jinja2.ext.i18n']) 16 | 17 | _jinja_environment.install_gettext_translations(i18n) 18 | 19 | 20 | def _json_escaped(value): 21 | return Markup(json.dumps(value)) 22 | 23 | 24 | _jinja_environment.filters['json'] = _json_escaped 25 | 26 | 27 | def render(template_name, values=None): 28 | values = values or {} 29 | template = _jinja_environment.get_template(template_name) 30 | return template.render(values) 31 | 32 | -------------------------------------------------------------------------------- /backend/appengine/index.yaml: -------------------------------------------------------------------------------- 1 | indexes: 2 | 3 | # AUTOGENERATED 4 | 5 | # This index.yaml is automatically updated whenever the dev_appserver 6 | # detects that a new type of query is run. If you want to manage the 7 | # index.yaml file manually, remove the above marker line (the line 8 | # saying "# AUTOGENERATED"). If you want to manage some indexes 9 | # manually, move them above the marker line. The index.yaml file is 10 | # automatically uploaded to the admin console when you next deploy 11 | # your application using appcfg.py. 12 | 13 | - kind: Arc 14 | properties: 15 | - name: class 16 | - name: origin 17 | - name: creation 18 | 19 | - kind: Node 20 | properties: 21 | - name: class 22 | - name: creation 23 | 24 | - kind: Node 25 | properties: 26 | - name: class 27 | - name: creation 28 | direction: desc 29 | 30 | - kind: Node 31 | properties: 32 | - name: class 33 | - name: email 34 | -------------------------------------------------------------------------------- /backend/test/testloader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | import unittest 5 | import sys 6 | import os 7 | 8 | PROJECT_PATH = os.path.sep.join(os.path.abspath(__file__).split(os.path.sep)[:-2]) 9 | ROOT_PATH = os.path.dirname(__file__) 10 | 11 | 12 | def main(): 13 | path_setup() 14 | tests = unittest.TestLoader().discover(ROOT_PATH, "*.py") 15 | result = unittest.TextTestRunner().run(tests) 16 | if not result.wasSuccessful(): 17 | sys.exit(1) 18 | 19 | 20 | def path_setup(): 21 | if 'GAE_SDK' in os.environ: 22 | SDK_PATH = os.environ['GAE_SDK'] 23 | 24 | sys.path.insert(0, SDK_PATH) 25 | 26 | import dev_appserver 27 | 28 | dev_appserver.fix_sys_path() 29 | sys.path.append(os.path.join(PROJECT_PATH, 'appengine')) 30 | sys.path.append(os.path.join(PROJECT_PATH, 'apps')) 31 | 32 | 33 | if __name__ == '__main__': 34 | main() 35 | -------------------------------------------------------------------------------- /backend/appengine/templates/admin/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/base.html' %} 2 | {% block body %} 3 |
4 |
5 |

{% trans %}Administration Panel{% endtrans %}

6 | 11 |

{% trans %} Configurations{% endtrans %}

12 | 17 |
18 |
19 | {% endblock %} -------------------------------------------------------------------------------- /backend/appengine/routes/admin/home.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from gaecookie.decorator import no_csrf 4 | from gaepermission.decorator import permissions 5 | from config.template_middleware import TemplateResponse 6 | from permission_app.model import ADMIN 7 | from tekton import router 8 | from routes.login import passwordless, facebook 9 | from routes.permission import home as permission_home, admin 10 | 11 | 12 | @permissions(ADMIN) 13 | @no_csrf 14 | def index(): 15 | return TemplateResponse({'security_table_path': router.to_path(permission_home.index), 16 | 'permission_admin_path': router.to_path(admin), 17 | 'passwordless_admin_path': router.to_path(passwordless.form), 18 | 'facebook_admin_path': router.to_path(facebook.form)}, 19 | 'admin/home.html') 20 | -------------------------------------------------------------------------------- /backend/appengine/routes/warmup.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | import time 4 | import os 5 | 6 | # Put lib on path, once Google App Engine does not allow doing it directly 7 | src_path = os.path.dirname(__file__) 8 | src_path = os.path.join(src_path, '..') 9 | src_path = os.path.normpath(src_path) 10 | sys.path.append(os.path.join(src_path, 'lib')) 11 | sys.path.append(os.path.join(src_path, 'apps')) 12 | from gaepermission import facade 13 | 14 | import webapp2 15 | 16 | 17 | class BaseHandler(webapp2.RequestHandler): 18 | def get(self): 19 | # This import all paths so it's good pre-initialize them here 20 | init_time = time.time() 21 | [p for p in facade.web_path_security_info()] 22 | end_time = time.time() 23 | delta_seconds = (end_time - init_time) 24 | logging.info('Startup: %s miliseconds' % (delta_seconds * 1000)) 25 | 26 | 27 | app = webapp2.WSGIApplication([('/.*', BaseHandler)], debug=False) 28 | 29 | -------------------------------------------------------------------------------- /backend/apps/multitenancy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from collections import defaultdict 4 | from google.appengine.api.namespace_manager.namespace_manager import set_namespace 5 | from tekton.gae.middleware import Middleware 6 | 7 | _domain_dct = {} 8 | _subdomain_dct = defaultdict(lambda: '') 9 | 10 | 11 | def set_domain(domain, namespace): 12 | _domain_dct[domain] = namespace 13 | 14 | 15 | def set_subdomain(subdomain, namespace): 16 | _subdomain_dct[subdomain] = namespace 17 | 18 | 19 | def get_namespace(host): 20 | domain = host.split(':')[0] 21 | ns = _domain_dct.get(domain) 22 | if ns: 23 | return ns 24 | subdomain = domain.split('.')[0] 25 | return _subdomain_dct[subdomain] 26 | 27 | 28 | class MultitenacyMiddleware(Middleware): 29 | def set_up(self): 30 | ns = get_namespace(self.handler.request.host) 31 | if ns: 32 | set_namespace(ns) 33 | -------------------------------------------------------------------------------- /backend/appengine/routes/login/home.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from google.appengine.api import users 4 | from gaecookie.decorator import no_csrf 5 | from gaepermission import facade 6 | from gaepermission.decorator import login_not_required 7 | from tekton import router 8 | from config.template_middleware import TemplateResponse 9 | from routes.login import google, facebook 10 | from routes.login.passwordless import send_email 11 | 12 | 13 | @login_not_required 14 | @no_csrf 15 | def index(ret_path='/'): 16 | g_path = router.to_path(google.index, ret_path=ret_path) 17 | dct = {'login_google_path': users.create_login_url(g_path), 18 | 'login_passwordless_path': router.to_path(send_email, ret_path=ret_path), 19 | 'login_facebook_path': router.to_path(facebook.index, ret_path=ret_path), 20 | 'faceapp': facade.get_facebook_app_data().execute().result} 21 | return TemplateResponse(dct, 'login/home.html') 22 | -------------------------------------------------------------------------------- /backend/appengine/app.yaml: -------------------------------------------------------------------------------- 1 | application: tekton-fullstack 2 | version: 1 3 | runtime: python27 4 | api_version: 1 5 | threadsafe: yes 6 | 7 | inbound_services: 8 | - warmup 9 | 10 | libraries: 11 | - name: webapp2 12 | version: "2.5.2" 13 | 14 | - name: webob 15 | version: "1.2.3" 16 | 17 | - name: markupsafe 18 | version: "0.15" 19 | 20 | - name: setuptools 21 | version: "0.6c11" 22 | 23 | handlers: 24 | - url: / 25 | script: convention.app 26 | secure: always 27 | 28 | - url: /robots\.txt 29 | static_files: static/robots.txt 30 | upload: static/robots.txt 31 | 32 | - url: /favicon\.ico 33 | static_files: static/img/favicon.ico 34 | upload: static/img/favicon.ico 35 | 36 | - url: /static(.*) 37 | static_files: static\1 38 | upload: static.* 39 | 40 | - url: /.*tasks.* 41 | script: convention.app 42 | secure: always 43 | login: admin 44 | 45 | - url: /[^_].* 46 | script: convention.app 47 | secure: always 48 | 49 | - url: /_ah/warmup 50 | script: routes.warmup.app 51 | 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | backend/plugins/appengine/lib/ 18 | backend/plugins/appengine/locale 19 | backend/plugins/appengine/apps/ 20 | backend/venv/* 21 | !backend/venv/requirements.txt 22 | !backend/venv/dev_requirements.txt 23 | !backend/venv/venv.bat 24 | !backend/venv/venv.sh 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | 46 | # Translations 47 | *.mo 48 | 49 | # Mr Developer 50 | .mr.developer.cfg 51 | .project 52 | .pydevproject 53 | .idea 54 | 55 | # Rope 56 | .ropeproject 57 | 58 | # Django stuff: 59 | *.log 60 | *.pot 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | -------------------------------------------------------------------------------- /backend/appengine/templates/updown/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block body %} 3 |
4 |
5 |
6 |
7 |

{% trans %}File Upload Form{% endtrans %}

8 |
9 | {{ csrf_input() }} 10 |
11 | 13 | 14 |
15 | 16 |
17 |
18 |
19 |
20 |
21 | {% endblock %} -------------------------------------------------------------------------------- /backend/venv/venv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # If occur any error, exit 4 | 5 | function to_console { 6 | echo -e "\n*** $1 ***\n" 7 | } 8 | 9 | cd $(dirname $0) && cd .. 10 | 11 | to_console "creating virtual env on venv folder" 12 | virtualenv venv --python=python2 13 | 14 | to_console "Activating virtualenv" 15 | source venv/bin/activate 16 | 17 | to_console "Checking up dependencies" 18 | if [ ! -z "$1" ] 19 | then 20 | to_console "Running with proxy "$1 21 | pip install -r venv/dev_requirements.txt --proxy=$1 22 | else 23 | to_console 'Runing with no proxy' 24 | pip install -r venv/dev_requirements.txt 25 | fi 26 | 27 | cd appengine 28 | 29 | if [ -d lib ]; then 30 | rm lib 31 | fi 32 | to_console "Creating symlink on plugins/appengine/lib so installed libs become visible to Google App Engine" 33 | ln -s ../venv/lib/python2.7/site-packages lib 34 | 35 | if [ -d apps ]; then 36 | rm apps 37 | fi 38 | to_console "Creating symlink on plugins/appengine/apps so apps become visible to Google App Engine" 39 | ln -s ../apps apps 40 | 41 | -------------------------------------------------------------------------------- /backend/appengine/routes/login/google.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from google.appengine.api import users 4 | from config.template_middleware import TemplateResponse 5 | from gaecookie.decorator import no_csrf 6 | from gaepermission import facade 7 | from gaepermission.decorator import login_not_required 8 | from tekton import router 9 | from tekton.gae.middleware.redirect import RedirectResponse 10 | import settings 11 | from routes.login import pending 12 | 13 | 14 | @login_not_required 15 | @no_csrf 16 | def index(_resp, ret_path='/'): 17 | user = users.get_current_user() 18 | if user: 19 | cmd = facade.login_google(user, _resp).execute() 20 | if cmd.pending_link: 21 | pending_path = router.to_path(pending.index, cmd.pending_link.key.id()) 22 | facade.send_passwordless_login_link(user.email(), 23 | settings.APP_URL + pending_path).execute() 24 | return TemplateResponse({'provider': 'Google', 'email': user.email()}, 'login/pending.html') 25 | return RedirectResponse(ret_path) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 renzon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /backend/appengine/routes/account.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from config.template_middleware import TemplateResponse 4 | from gaecookie.decorator import no_csrf 5 | from gaepermission.decorator import login_required 6 | from pytz import common_timezones 7 | import settings 8 | from tekton import router 9 | from tekton.gae.middleware.redirect import RedirectResponse 10 | 11 | 12 | @login_required 13 | def edit(_logged_user, name, user_locale, timezone): 14 | if name: 15 | _logged_user.name = name 16 | _logged_user.locale = user_locale 17 | _logged_user.timezone = timezone 18 | _logged_user.put() 19 | return RedirectResponse('/') 20 | 21 | 22 | @login_required 23 | @no_csrf 24 | def index(_logged_user): 25 | _logged_user.locale = _logged_user.locale or settings.DEFAULT_LOCALE 26 | _logged_user.timezone = _logged_user.timezone or settings.DEFAULT_TIMEZONE 27 | context = {'user': _logged_user, 28 | 'timezones': common_timezones, 29 | 'save_path': router.to_path(edit)} 30 | return TemplateResponse(context, 'permission/account_form.html') 31 | -------------------------------------------------------------------------------- /backend/appengine/templates/login/passwordless/form.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/base.html' %} 2 | {% block body %} 3 |
4 |
5 |
6 |

{% trans %}Passworless App Form{% endtrans %}

7 | 8 |

{% trans %}This data is used to proceed passswordless login. More information in{% endtrans %} Passwordless. 10 |

11 | 12 |
13 | {{ csrf_input() }} 14 | 16 | 18 | 19 |
20 |
21 |
22 |
23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /backend/appengine/static/permission/html/permission_table.html: -------------------------------------------------------------------------------- 1 |
2 |

Results for emails starting with "{{ searchPrefix }}"

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 |
IdEmailNameGroups
{{ u.id }}{{ u.email }}{{ u.name }} 17 | 18 |
21 | 23 | No more results for search "{{ searchPrefix }}" 24 |
25 | 26 |
27 |
28 | No results for emails starting with "{{ searchPrefix }}" 29 |
30 |
-------------------------------------------------------------------------------- /backend/appengine/templates/login/facebook/form.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/base.html' %} 2 | {% block body %} 3 |
4 |
5 |
6 |

{% trans %}Facebook App Form{% endtrans %}

7 | 8 |

{% trans %}This data is used to proceed Facebook login. More information in{% endtrans %} {% trans %}Facebook Login Docs{% endtrans %}.

11 | 12 |
13 | {{ csrf_input() }} 14 | 16 | 18 | 19 |
20 |
21 |
22 |
23 | {% endblock %} -------------------------------------------------------------------------------- /backend/appengine/convention.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from google.appengine.ext.webapp.blobstore_handlers import BlobstoreDownloadHandler, BlobstoreUploadHandler 3 | import os 4 | 5 | # Put lib on path, once Google App Engine does not allow doing it directly 6 | sys.path.append(os.path.join(os.path.dirname(__file__), "lib")) 7 | sys.path.append(os.path.join(os.path.dirname(__file__), "apps")) 8 | 9 | import settings 10 | from tekton.gae import middleware 11 | import webapp2 12 | from webapp2_extras import i18n 13 | 14 | i18n.default_config['default_locale'] = settings.DEFAULT_LOCALE 15 | i18n.default_config['default_timezone'] = settings.DEFAULT_TIMEZONE 16 | 17 | 18 | class HandlerMixin(): 19 | def get(self): 20 | self.make_convention() 21 | 22 | def post(self): 23 | self.make_convention() 24 | 25 | def make_convention(self): 26 | middleware.execute(settings.MIDDLEWARE_LIST, self) 27 | 28 | 29 | class BaseHandler(HandlerMixin, webapp2.RequestHandler): 30 | pass 31 | 32 | 33 | class DownloadHandler(HandlerMixin, BlobstoreDownloadHandler): 34 | pass 35 | 36 | 37 | class UploadHandler(HandlerMixin, BlobstoreUploadHandler): 38 | pass 39 | 40 | 41 | app = webapp2.WSGIApplication( 42 | [("/.*download.*", DownloadHandler), ("/.*upload.*", UploadHandler), ("/.*", BaseHandler)], 43 | debug=False) 44 | -------------------------------------------------------------------------------- /backend/test/account_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from gaepermission import facade 5 | from routes import account 6 | import settings 7 | from tekton.gae.middleware.redirect import RedirectResponse 8 | 9 | 10 | class AccountTests(GAETestCase): 11 | def test_index(self): 12 | email = 'foo@gmail.com' 13 | user = facade.save_user_cmd(email)() 14 | response = account.index(user) 15 | self.assert_can_render(response) 16 | 17 | def test_edit(self): 18 | email = 'foo@gmail.com' 19 | initial_name = 'initial_name' 20 | user = facade.save_user_cmd(email, initial_name)() 21 | self.assertEqual(initial_name, user.name) 22 | self.assertEqual(settings.DEFAULT_LOCALE, user.locale) 23 | self.assertEqual(settings.DEFAULT_TIMEZONE, user.timezone) 24 | edited_name = 'edited_name' 25 | locale = 'pt_BR' 26 | timezone = 'America/Sao_Paulo' 27 | response = account.edit(user, edited_name, locale, timezone) 28 | user = user.key.get() 29 | self.assertIsInstance(response, RedirectResponse) 30 | self.assertEqual(edited_name, user.name) 31 | self.assertEqual(locale, user.locale) 32 | self.assertEqual(timezone, user.timezone) 33 | -------------------------------------------------------------------------------- /backend/venv/venv.bat: -------------------------------------------------------------------------------- 1 | call :to_console "Setting up virtualenv on venv" 2 | 3 | cd %~dp0 || goto :error 4 | 5 | call :to_console "creating virtual env on venv folder" 6 | virtualenv . --python=C:\Python27\python.exe|| goto :error 7 | 8 | call :to_console "Activating virtualenv" 9 | call Scripts\activate || goto :error 10 | 11 | 12 | IF "%1"=="" ( 13 | call :to_console "Checking up dependencies" 14 | pip install -r dev_requirements.txt --upgrade || goto :error 15 | ) ELSE ( 16 | call :to_console "Checking up dependencies with proxy %1" 17 | pip install -r dev_requirements.txt --upgrade --proxy=%1 || goto :error 18 | ) 19 | cd ..\appengine || goto :error 20 | 21 | IF EXIST %cd%\lib ( 22 | rmdir %cd%\lib 23 | ) 24 | 25 | call :to_console "Creating symlink on plugins\appengine so installed libs become visible to Google App Engine" 26 | MKLINK /D lib ..\venv\Lib\site-packages 27 | 28 | IF EXIST %cd%\apps ( 29 | rmdir %cd%\apps 30 | ) 31 | 32 | call :to_console "Creating symlink on plugins\appengine so apps become visible to Google App Engine" 33 | MKLINK /D apps ..\apps 34 | 35 | 36 | cd ..\venv || goto :error 37 | call :to_console "virtualenv and dependencies installed" 38 | goto:EOF 39 | 40 | 41 | :to_console 42 | echo "------------ %~1 -----------" 43 | goto:EOF 44 | 45 | :error 46 | call :to_console Failed with error #%errorlevel%. 47 | exit /b %errorlevel% 48 | goto:EOF -------------------------------------------------------------------------------- /backend/appengine/routes/permission/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from config.template_middleware import TemplateResponse 4 | from gaecookie.decorator import no_csrf 5 | from gaepermission import facade 6 | from gaepermission.decorator import permissions 7 | from permission_app.model import ALL_PERMISSIONS_LIST, ADMIN 8 | from tekton import router 9 | from tekton.gae.middleware.json_middleware import JsonResponse 10 | 11 | 12 | @permissions(ADMIN) 13 | @no_csrf 14 | def index(): 15 | dct = {'list_users_path': router.to_path(list_users), 16 | 'groups': ALL_PERMISSIONS_LIST} 17 | return TemplateResponse(dct, 'permission/admin.html') 18 | 19 | 20 | @permissions(ADMIN) 21 | def list_users(email_prefix='', cursor=None): 22 | cmd = facade.find_users_by_email_starting_with(email_prefix, cursor) 23 | users = cmd.execute().result 24 | 25 | def to_dict(user): 26 | d = user.to_dict(include=['id', 'email', 'name', 'groups']) 27 | d['update'] = router.to_path(update, user.key.id()) 28 | return d 29 | 30 | users = [to_dict(u) for u in users] 31 | cursor_str = cmd.cursor.urlsafe() if cmd.cursor else '' 32 | next_page = router.to_path(list_users, email_prefix=email_prefix, cursor=cursor_str) 33 | return JsonResponse({'users': users, 'next_page': next_page, 'more': cmd.more}) 34 | 35 | 36 | @permissions(ADMIN) 37 | def update(user_id, groups): 38 | facade.update_user_groups(user_id, groups).execute() 39 | -------------------------------------------------------------------------------- /backend/appengine/templates/permission/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/base.html' %} 2 | {% block body %} 3 |
4 |
5 |
6 |

{% trans %}Security Table{% endtrans %}

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {% for info in path_infos %} 17 | 18 | 19 | 20 | 25 | 26 | {% endfor %} 27 | 28 |
{% trans %}Path{% endtrans %}{% trans %}Allowed Groups{% endtrans %}{% trans %}CSRF Security{% endtrans %}
{{ info.path }}{{ info.groups }} 21 | 22 | {{ _('Secure') if info.csrf else _('Unsecure') }} 23 | 24 |
29 |
30 |
31 |
32 | {% endblock %} -------------------------------------------------------------------------------- /backend/test/login_tests/google.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from gaepermission import facade 5 | from mock import Mock, patch 6 | from routes.login import google 7 | from tekton.gae.middleware.redirect import RedirectResponse 8 | 9 | 10 | class IndexTests(GAETestCase): 11 | def test_no_google_user_logged(self): 12 | response = google.index(Mock()) 13 | self.assertIsInstance(response, RedirectResponse) 14 | 15 | @patch('routes.login.google.facade') 16 | def test_google_user_logged(self, facade_mock): 17 | self.set_current_user() 18 | cmd_mock = Mock() 19 | cmd_mock.pending_link = False 20 | execute_mock = Mock() 21 | execute_mock.execute = Mock(return_value=cmd_mock) 22 | facade_mock.login_google = Mock(return_value=execute_mock) 23 | response = google.index(Mock()) 24 | self.assertIsInstance(response, RedirectResponse) 25 | 26 | @patch('routes.login.google.facade.send_passwordless_login_link') 27 | def test_google_user_logged_for_already_email_registered_user(self, send_login_mock): 28 | email = 'foo@gmail.com' 29 | self.set_current_user(user_email=email) 30 | facade.save_or_update_passwordless_app_data('id', 'token').execute() 31 | facade.save_user_cmd(email).execute() 32 | response = google.index(Mock()) 33 | self.assertEqual(email, send_login_mock.call_args[0][0]) 34 | self.assert_can_render(response) 35 | -------------------------------------------------------------------------------- /backend/appengine/routes/login/passwordless.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from gaecookie.decorator import no_csrf 4 | from gaepermission import facade 5 | from gaepermission.decorator import login_not_required, permissions 6 | from permission_app.model import ADMIN 7 | from tekton import router 8 | from config.template_middleware import TemplateResponse 9 | from tekton.gae.middleware.redirect import RedirectResponse 10 | import settings 11 | from routes import admin 12 | 13 | 14 | @no_csrf 15 | @login_not_required 16 | def index(): 17 | return TemplateResponse(template_path='login/passwordless/home.html') 18 | 19 | 20 | @login_not_required 21 | def send_email(email, ret_path='/'): 22 | url = settings.APP_URL + router.to_path(check, ret_path=ret_path) 23 | facade.send_passwordless_login_link(email, url).execute() 24 | return RedirectResponse(index) 25 | 26 | 27 | @no_csrf 28 | @login_not_required 29 | def check(_resp, ticket, ret_path='/'): 30 | facade.login_passwordless(ticket, _resp).execute() 31 | return RedirectResponse(ret_path) 32 | 33 | 34 | @permissions(ADMIN) 35 | @no_csrf 36 | def form(): 37 | app = facade.get_passwordless_app_data().execute().result 38 | dct = {'save_app_path': router.to_path(save), 'app': app} 39 | return TemplateResponse(dct, 'login/passwordless/form.html') 40 | 41 | 42 | @permissions(ADMIN) 43 | def save(app_id, token): 44 | facade.save_or_update_passwordless_app_data(app_id, token).execute() 45 | return RedirectResponse(admin) 46 | -------------------------------------------------------------------------------- /backend/apps/locale_app/middleware.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from webapp2_extras import i18n 4 | from tekton.gae.middleware import Middleware 5 | 6 | 7 | class LocaleMiddleware(Middleware): 8 | def _handle(self, locale_key): 9 | if locale_key in self.request_args: 10 | locale = self.request_args.get(locale_key, '') 11 | print 'Locale ' + locale_key 12 | 13 | self.request_args.pop(locale_key) 14 | if locale: 15 | locale_obj = i18n.get_i18n() 16 | locale_obj.set_locale(locale) 17 | import settings # this is here to avoid cyclic dependency 18 | 19 | locale_obj.set_timezone(settings.DEFAULT_TIMEZONE) 20 | return True 21 | 22 | def set_up(self): 23 | handled = self._handle('locale') 24 | # fucking Facebook scrapper sending undesired param 25 | handled = self._handle('fb_locale') or handled 26 | user = self.dependencies['_logged_user'] 27 | import settings # this is here to avoid cyclic dependency 28 | 29 | if user: 30 | locale_obj = i18n.get_i18n() 31 | locale_obj.set_locale(user.locale or settings.DEFAULT_LOCALE) 32 | locale_obj.set_timezone(user.timezone or settings.DEFAULT_TIMEZONE) 33 | elif not handled: 34 | locale_obj = i18n.get_i18n() 35 | locale_obj.set_locale(settings.DEFAULT_LOCALE) 36 | locale_obj.set_timezone(settings.DEFAULT_TIMEZONE) 37 | 38 | 39 | -------------------------------------------------------------------------------- /backend/appengine/routes/login/facebook.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from config.template_middleware import TemplateResponse 4 | from gaecookie.decorator import no_csrf 5 | from gaepermission import facade 6 | from gaepermission.decorator import login_not_required, permissions 7 | from permission_app.model import ADMIN 8 | import settings 9 | from tekton import router 10 | from tekton.gae.middleware.redirect import RedirectResponse 11 | from routes import admin 12 | from routes.login import pending 13 | 14 | 15 | @login_not_required 16 | def index(_resp, token, ret_path='/'): 17 | cmd = facade.login_facebook(token, _resp) 18 | cmd() 19 | if cmd.pending_link: 20 | pending_path = router.to_path(pending.index, cmd.pending_link.key.id()) 21 | user_email = cmd.main_user_from_email.email 22 | facade.send_passwordless_login_link(user_email, 23 | settings.APP_URL + pending_path).execute() 24 | return TemplateResponse({'provider': 'Facebook', 'email': user_email}, 'login/pending.html') 25 | return RedirectResponse(ret_path) 26 | 27 | 28 | @permissions(ADMIN) 29 | @no_csrf 30 | def form(): 31 | app = facade.get_facebook_app_data().execute().result 32 | dct = {'save_app_path': router.to_path(save), 'app': app} 33 | return TemplateResponse(dct, 'login/facebook/form.html') 34 | 35 | 36 | @permissions(ADMIN) 37 | def save(app_id, token): 38 | facade.save_or_update_facebook_app_data(app_id, token).execute() 39 | return RedirectResponse(admin) 40 | -------------------------------------------------------------------------------- /backend/test/permission_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from gaepermission import facade 5 | from routes.permission import home, admin 6 | 7 | 8 | class HomeTests(GAETestCase): 9 | def test_render(self): 10 | response = home.index() 11 | self.assert_can_render(response) 12 | 13 | 14 | class AdminTests(GAETestCase): 15 | def test_render(self): 16 | response = admin.index() 17 | self.assert_can_render(response) 18 | 19 | def test_list_users_empty(self): 20 | response = admin.list_users() 21 | self.assert_can_serialize_as_json(response) 22 | 23 | def test_list_users(self): 24 | email = 'foo@gmail.com' 25 | facade.save_user_cmd(email).execute() 26 | response = admin.list_users() 27 | self.assert_can_serialize_as_json(response) 28 | self.assertEqual(email, response.context['users'][0]['email']) 29 | 30 | def test_update_user(self): 31 | email = 'foo@gmail.com' 32 | facade.save_user_cmd(email).execute() 33 | user = facade.get_user_by_email(email)() 34 | self.assertListEqual([''], user.groups) 35 | 36 | ADMIN = 'ADMIN' 37 | admin.update(str(user.key.id()), [ADMIN]) 38 | 39 | user = user.key.get() 40 | self.assertListEqual([ADMIN], user.groups) 41 | 42 | MANAGER = 'MANAGER' 43 | admin.update(str(user.key.id()), [ADMIN, MANAGER]) 44 | 45 | user = user.key.get() 46 | self.assertListEqual([ADMIN, MANAGER], user.groups) 47 | -------------------------------------------------------------------------------- /backend/test/multitenancy_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from collections import defaultdict 4 | from unittest.case import TestCase 5 | from multitenancy import get_namespace 6 | import multitenancy 7 | 8 | 9 | class GetNamespaceTests(TestCase): 10 | def setUp(self): 11 | multitenancy._subdomain_dct = defaultdict(lambda: '') 12 | multitenancy._domain_dct = {} 13 | 14 | def test_not_registered_namespace(self): 15 | self.assertEqual('', get_namespace('www')) 16 | self.assertEqual('', get_namespace('www.foo.bar')) 17 | 18 | def test_registered_domain(self): 19 | multitenancy.set_domain('www.foo.bar', 'some_namespace') 20 | self.assertEqual('', get_namespace('www')) 21 | self.assertEqual('some_namespace', get_namespace('www.foo.bar')) 22 | 23 | def test_registered_subdomain(self): 24 | multitenancy.set_subdomain('www', 'some_namespace') 25 | self.assertEqual('some_namespace', get_namespace('www')) 26 | self.assertEqual('some_namespace', get_namespace('www.foo.bar')) 27 | 28 | def test_domain_precedence(self): 29 | multitenancy.set_domain('www.foo.bar', 'some_namespace') 30 | multitenancy.set_subdomain('www', 'other_namespace') 31 | self.assertEqual('other_namespace', get_namespace('www')) 32 | self.assertEqual('some_namespace', get_namespace('www.foo.bar')) 33 | self.assertEqual('other_namespace', get_namespace('www.anotherdomain')) 34 | 35 | def test_domain_with_port(self): 36 | multitenancy.set_domain('www.foo.bar', 'some_namespace') 37 | multitenancy.set_subdomain('www', 'other_namespace') 38 | self.assertEqual('other_namespace', get_namespace('www')) 39 | self.assertEqual('some_namespace', get_namespace('www.foo.bar:8080')) 40 | -------------------------------------------------------------------------------- /backend/appengine/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from gaecookie.middleware import CSRFMiddleware, CSRFInputToDependency 4 | from locale_app.middleware import LocaleMiddleware 5 | from multitenancy import MultitenacyMiddleware, set_subdomain, set_domain 6 | from tekton.gae.middleware.json_middleware import JsonResponseMiddleware 7 | from config.template_middleware import TemplateMiddleware, TemplateWriteMiddleware 8 | from tekton.gae.middleware.email_errors import EmailMiddleware 9 | from tekton.gae.middleware.parameter import RequestParamsMiddleware 10 | from tekton.gae.middleware.redirect import RedirectMiddleware 11 | from tekton.gae.middleware.router_middleware import RouterMiddleware, ExecutionMiddleware 12 | from tekton.gae.middleware.webapp2_dependencies import Webapp2Dependencies 13 | from gaepermission.middleware import LoggedUserMiddleware, PermissionMiddleware 14 | 15 | APP_URL = 'https://tekton-fullstack.appspot.com' 16 | SENDER_EMAIL = 'renzon@gmail.com' 17 | DEFAULT_LOCALE = 'en_US' 18 | DEFAULT_TIMEZONE = 'US/Eastern' 19 | LOCALES = ['en_US', 'pt_BR'] 20 | TEMPLATE_404_ERROR = 'base/404.html' 21 | TEMPLATE_400_ERROR = 'base/400.html' 22 | 23 | 24 | MIDDLEWARE_LIST = [MultitenacyMiddleware, 25 | LoggedUserMiddleware, 26 | TemplateMiddleware, 27 | EmailMiddleware, 28 | Webapp2Dependencies, 29 | RequestParamsMiddleware, 30 | CSRFInputToDependency, 31 | LocaleMiddleware, 32 | RouterMiddleware, 33 | CSRFMiddleware, 34 | PermissionMiddleware, 35 | ExecutionMiddleware, 36 | TemplateWriteMiddleware, 37 | JsonResponseMiddleware, 38 | RedirectMiddleware] 39 | 40 | 41 | -------------------------------------------------------------------------------- /backend/test/login_tests/facebook.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from gaepermission import facade 5 | from mock import Mock, patch 6 | from routes.login import google, facebook 7 | import settings 8 | from tekton.gae.middleware.redirect import RedirectResponse 9 | 10 | 11 | class IndexTests(GAETestCase): 12 | @patch('routes.login.facebook.facade') 13 | def test_facebook_login(self, facade_mock): 14 | cmd_mock = Mock() 15 | cmd_mock.pending_link = False 16 | facade_mock.login_facebook = Mock(return_value=cmd_mock) 17 | response = facebook.index(Mock(), 'token') 18 | self.assertIsInstance(response, RedirectResponse) 19 | 20 | @patch('routes.login.facebook.facade.login_facebook') 21 | @patch('routes.login.facebook.facade.send_passwordless_login_link') 22 | def test_facebook_login_for_already_email_registered_user(self, send_login_mock, login_facebook_mock): 23 | email = 'foo@gmail.com' 24 | cmd_mock = Mock() 25 | cmd_mock.main_user_from_email.email = email 26 | login_facebook_mock.return_value = cmd_mock 27 | token = 'token' 28 | facade.save_or_update_passwordless_app_data('id', token).execute() 29 | facade.save_user_cmd(email).execute() 30 | resp_mock = Mock() 31 | response = facebook.index(resp_mock, token) 32 | login_facebook_mock.assert_called_once_with(token, resp_mock) 33 | self.assertEqual(email, send_login_mock.call_args[0][0]) 34 | self.assert_can_render(response) 35 | 36 | 37 | class FormTests(GAETestCase): 38 | def test_success(self): 39 | response = facebook.form() 40 | self.assert_can_render(response) 41 | 42 | 43 | class SaveTests(GAETestCase): 44 | def test_success(self): 45 | app_id = 'app_id' 46 | token = 'token' 47 | response=facebook.save(app_id, token) 48 | self.assertIsInstance(response,RedirectResponse) 49 | -------------------------------------------------------------------------------- /backend/appengine/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block css %} 3 | 4 | 5 | {% endblock %} 6 | {% block body %} 7 |
8 |
9 |
10 |

Tekton

11 |

{% trans %}A Full-Stack project for Google App Engine{% endtrans %}

12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 |

{% trans %}Create CRUD code on 20 | App Engine{% endtrans %} 21 |

22 |
23 | 24 |

25 | {% trans %} Check Courses App 26 | Out{% endtrans %} » 27 |

28 |
29 |
30 |
31 |
32 |

{% trans %}Tekton is open source.{% endtrans %}

33 |
34 | 35 |

36 | {% trans %}Check Git Hub Out{% endtrans %} » 38 |

39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /backend/appengine/templates/multitenant/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block css %} 3 | 4 | 5 | {% endblock %} 6 | {% block body %} 7 |
8 |
9 |
10 |

Tekton

11 |

{% trans %}A Full-Stack project for Google App Engine{% endtrans %}

12 |

But this is the multinant version ;)

13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 |

{% trans %}Create CRUD code on 21 | App Engine{% endtrans %} 22 |

23 |
24 | 25 |

26 | {% trans %} Check Courses App 27 | Out{% endtrans %} » 28 |

29 |
30 |
31 |
32 |
33 |

{% trans %}Tekton is open source.{% endtrans %}

34 |
35 | 36 |

37 | {% trans %}Check Git Hub Out{% endtrans %} » 39 |

40 |
41 |
42 |
43 | 44 |
45 |
46 |
47 | {% endblock %} -------------------------------------------------------------------------------- /backend/test/login_tests/passwordless.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from base import GAETestCase 4 | from gaepermission import facade 5 | from mock import patch, Mock 6 | from routes.login import passwordless 7 | from routes.login.passwordless import check 8 | import settings 9 | from tekton.gae.middleware.redirect import RedirectResponse 10 | from tekton.router import to_path 11 | 12 | 13 | class IndexTests(GAETestCase): 14 | def test_success(self): 15 | response = passwordless.index() 16 | self.assert_can_render(response) 17 | 18 | 19 | class FormTests(GAETestCase): 20 | def test_render(self): 21 | response = passwordless.form() 22 | self.assert_can_render(response) 23 | 24 | 25 | class SaveAppDataTests(GAETestCase): 26 | def test_success(self): 27 | token = 'token' 28 | app_id = 'app_id' 29 | response = passwordless.save(app_id, token) 30 | self.assertIsInstance(response, RedirectResponse) 31 | app = facade.get_passwordless_app_data()() 32 | self.assertEqual(app_id, app.app_id) 33 | self.assertEqual(token, app.token) 34 | 35 | 36 | class SendEmailTests(GAETestCase): 37 | @patch('routes.login.passwordless.facade.send_passwordless_login_link') 38 | def test_success(self, send_link_mock): 39 | email = 'foo@bar.com' 40 | passwordless.send_email(email) 41 | send_link_mock.assert_called_once_with(email, 42 | settings.APP_URL + to_path(check, ret_path='/')) 43 | 44 | 45 | class CheckEmailTests(GAETestCase): 46 | @patch('routes.login.passwordless.facade.login_passwordless') 47 | def test_success(self, login_mock): 48 | http_resp = Mock() 49 | ticket = 'ticket' 50 | cmd_mock = Mock() 51 | login_mock.return_value = cmd_mock 52 | response = passwordless.check(http_resp, ticket) 53 | self.assertIsInstance(response, RedirectResponse) 54 | login_mock.assert_called_once_with(ticket, http_resp) 55 | cmd_mock.execute.assert_called_once_with() 56 | -------------------------------------------------------------------------------- /backend/appengine/templates/permission/account_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block body %} 3 | {% set user=user or None %} 4 | {% set errors=errors or None %} 5 |
6 |
7 |
8 |
9 | 10 |
11 |

{% trans %}Your Account{% endtrans %}

12 | 13 |
14 | {{ csrf_input() }} 15 | {{ form_input(_('Name'),'name',user.name,errors.name) }} 16 |
17 | 18 |
19 | {% macro locale_input(locale,label) %} 20 | 24 | {% endmacro %} 25 | {{ locale_input('en_US','English - USA') }} 26 | {{ locale_input('pt_BR','Português - Brasil') }} 27 |
28 |
29 | 30 | 35 |
36 | 37 |
38 |
39 |
40 |
41 |
42 | {% endblock %} -------------------------------------------------------------------------------- /backend/appengine/templates/permission/admin.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/base.html' %} 2 | {% block js %} 3 | 4 | 5 | 36 | {% endblock %} 37 | {% block body %} 38 |
39 |
40 |
41 |

{% trans %}Users Table{% endtrans %}

42 | 43 |
44 | 45 |
46 | 51 |
52 |
53 |
54 |
55 | {% endblock %} -------------------------------------------------------------------------------- /backend/build_scripts/babel/i18n_extractor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | import logging 4 | import shutil 5 | import sys 6 | import os 7 | 8 | 9 | def patch(system): 10 | def sys(cmd): 11 | print cmd 12 | return system(cmd) 13 | 14 | return sys 15 | 16 | 17 | os.system = patch(os.system) 18 | 19 | # workaround to add src to path 20 | babel_dir = os.path.dirname(__file__) 21 | logging.info("babel dir: %s" % babel_dir) 22 | proj_dir = os.path.join(babel_dir, "..", '..') 23 | proj_dir = os.path.normpath(proj_dir) 24 | logging.info("project dir: %s" % proj_dir) 25 | sys.path.append(os.path.join(proj_dir, 'appengine')) 26 | sys.path.append(os.path.join(proj_dir, 'apps')) 27 | if 'GAE_SDK' in os.environ: 28 | SDK_PATH = os.environ['GAE_SDK'] 29 | 30 | sys.path.insert(0, SDK_PATH) 31 | 32 | import dev_appserver 33 | 34 | dev_appserver.fix_sys_path() 35 | 36 | import settings 37 | 38 | 39 | def create_or_update_catalog(loc, compile_target, msgs_pot): 40 | result = os.system("pybabel update -l %s -d %s -i %s" % (loc, compile_target, msgs_pot)) 41 | if result != 0: 42 | os.system("pybabel init -l %s -d %s -i %s" % (loc, compile_target, msgs_pot)) 43 | 44 | 45 | def compile_po_files(compile_target, locale_target): 46 | for root, dirs, files in os.walk(compile_target): 47 | file_name = "messages.po" if "messages.po" in files else None 48 | if file_name: 49 | po_file = os.path.join(root, file_name) 50 | mo_dir = root.replace(os.path.join('.', 'locale'), locale_target) 51 | mo_file = os.path.join(mo_dir, "messages.mo") 52 | if not os.path.exists(mo_dir): 53 | os.makedirs(mo_dir) 54 | print "Created dir: %s" % mo_dir 55 | 56 | c = "pybabel compile -f -i %s -o %s " % (po_file, mo_file) 57 | print c 58 | os.system(c) 59 | 60 | 61 | if __name__ == "__main__": 62 | compile_target = os.path.join(".", "locale") 63 | target = os.path.join(proj_dir, 'appengine') 64 | if os.path.sep == r'/': 65 | compile_targets = target + ' ' + os.path.join(proj_dir, "venv") 66 | else: 67 | compile_targets = target 68 | # if len(sys.argv) == 1: 69 | babel_cfg = os.path.join(babel_dir, "babel.cfg") 70 | msgs_pot = os.path.join(compile_target, "messages.pot") 71 | os.system("pybabel extract -F %s -o %s %s" % (babel_cfg, msgs_pot, compile_targets)) 72 | locales = settings.LOCALES 73 | for loc in locales: 74 | create_or_update_catalog(loc, compile_target, msgs_pot) 75 | 76 | locale_target = os.path.join(target, "locale") 77 | compile_po_files(compile_target, locale_target) 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tekton 2 | ================ 3 | 4 | A full stack project for Google App Engine based on modules [Tekton-micro](https://github.com/renzon/tekton-micro), 5 | [Gaegraph](https://github.com/renzon/gaegraph), [Gaeforms](https://github.com/renzon/gaeforms) and [Gaepermission](https://github.com/renzon/gaepermission), Jinja2 and Babel 6 | 7 | This application is running on 8 | 9 | 1. [Book App Engne and Python in pt_BR](https://leanpub.com/appengine) 10 | 2. [Vìdeos about the project in pt_BR](https://www.youtube.com/playlist?list=PLA05yVJtRWYRGIeBxag8uT-3ftcMVT5oF) 11 | 12 | # Installation: 13 | * Install [Google App Engine SDK](https://cloud.google.com/appengine/downloads) 14 | * Download the tekton source code 15 | * Run venv.sh to create virtual env 16 | ``` cd backend/venv && ./venv.sh (venv.bat if you use Windows) ``` 17 | * Virtualenv 18 | ``` source ./bin/activate ``` 19 | * Execute server on backend/appengine folder 20 | ``` cd ../appengine && dev_appserver.py . ``` 21 | * See if it works on ```http://localhost:8080``` 22 | 23 | # python manager.py 24 | 25 | manager.py is Tekton’s command-line utility for code generation. 26 | 27 | ## Commands available 28 | 29 | - model 30 | - app 31 | - delete 32 | 33 | ### app 34 | 35 | It's possible to determine the app creation, specifying the name and the central entity from the module. Example: 36 | 37 | ``` python manager.py app course Course title:string price:currency begin:date ``` 38 | 39 | - int: integer number. 40 | - float: floating-point number. 41 | - decimal: floating-point number with 2 decimal precision. 42 | - currency: money with 2 decimal precision. 43 | - string: string of characters 44 | - date: day, month and year. 45 | - datetime: day, month, year, hour, minutes and seconds. 46 | 47 | # Quickstart 48 | 49 | This is how you create a Hello World using Tekton. 50 | 51 | ```python 52 | # -*- coding: utf-8 -*- 53 | from __future__ import absolute_import, unicode_literals 54 | from config.template_middleware import TemplateResponse 55 | from gaecookie.decorator import no_csrf 56 | from gaepermission.decorator import login_not_required 57 | 58 | 59 | @login_not_required 60 | @no_csrf 61 | def index(): 62 | _resp.write('Hello world') 63 | ``` 64 | Just save it as *hello.py* inside *backend/appengine/routes* and run your server on *appengine* directory. 65 | 66 | ``` 67 | dev_appserver.py . 68 | ``` 69 | 70 | Now head over to http://localhost:8080/hello/, and you should see your *Hello world!* greeting. 71 | 72 | # Contributors: 73 | 74 | * [Denis Costa](https://github.com/deniscostadsc) 75 | * [Lucas Campos](https://github.com/lucasgcampos) 76 | * [Tony Lâmpada](https://github.com/tonylampada) 77 | * [Willian Ribeiro](https://github.com/willianribeiro) 78 | * [Guido Percú](https://github.com/GuidoBR) 79 | -------------------------------------------------------------------------------- /backend/test/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | import testloader 4 | testloader.path_setup() 5 | from config.template_middleware import TemplateResponse 6 | import json 7 | import unittest 8 | from google.appengine.ext import testbed 9 | from google.appengine.api import files 10 | import webapp2 11 | from webapp2_extras import i18n 12 | from config.template import render 13 | 14 | 15 | # workaround for i18n. without this test will not run 16 | 17 | app = webapp2.WSGIApplication( 18 | [webapp2.Route('/', None, name='upload_handler')]) 19 | 20 | request = webapp2.Request({'SERVER_NAME': 'test', 'SERVER_PORT': 80, 21 | 'wsgi.url_scheme': 'http'}) 22 | request.app = app 23 | app.set_globals(app=app, request=request) 24 | 25 | i18n.default_config['default_locale'] = 'en_US' 26 | i18n.default_config['default_timezone'] = 'UTC' 27 | 28 | _APP_ID = "foobar" 29 | 30 | 31 | class GAETestCase(unittest.TestCase): 32 | def setUp(self): 33 | self.testbed = testbed.Testbed() 34 | self.testbed.setup_env(app_id=_APP_ID) 35 | self.testbed.activate() 36 | self.testbed.init_datastore_v3_stub() 37 | self.testbed.init_user_stub() 38 | self.testbed.init_urlfetch_stub() 39 | self.testbed.init_memcache_stub() 40 | self.testbed.init_mail_stub() 41 | self.testbed.init_taskqueue_stub() 42 | 43 | def set_current_user(self, user_email='foo@gmail.com', user_id='1', user_is_admin=False): 44 | self.testbed.setup_env(True, USER_EMAIL=user_email, USER_ID=user_id, 45 | USER_IS_ADMIN='1' if user_is_admin else '0') 46 | self.testbed.init_user_stub() 47 | 48 | def tearDown(self): 49 | self.testbed.deactivate() 50 | 51 | def assert_can_render(self, template_response): 52 | """ 53 | Asserts that a template can be rendered. It raises an Exception otherwise 54 | :param template_response: a TemplateResponse instance 55 | :return: 56 | """ 57 | self.assertIsInstance(template_response,TemplateResponse) 58 | render(template_response.template_path, template_response.context) 59 | 60 | def assert_can_serialize_as_json(self, json_response): 61 | """ 62 | Asserts that a json_response contains json serializable data. It raises an Exception otherwise 63 | :param template_response: a JsonResponse or JsonUnsecureResponse instance 64 | :return: 65 | """ 66 | json.dumps(json_response.context) 67 | 68 | 69 | class BlobstoreTestCase(GAETestCase): 70 | def setUp(self): 71 | GAETestCase.setUp(self) 72 | self.testbed.init_blobstore_stub() 73 | self.testbed.init_files_stub() 74 | 75 | def save_blob(self, blobdata='blobdata'): 76 | file_name = files.blobstore.create(mime_type='application/octet-stream') 77 | with files.open(file_name, 'a') as f: 78 | f.write(blobdata) 79 | files.finalize(file_name) 80 | blob_key = files.blobstore.get_blob_key(file_name) 81 | return blob_key -------------------------------------------------------------------------------- /backend/appengine/config/template_middleware.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from google.appengine.api.namespace_manager import get_namespace 4 | 5 | from jinja2.exceptions import TemplateNotFound 6 | from tekton import router 7 | from tekton.gae.middleware.response import ResponseBase 8 | from tekton.gae.middleware import Middleware 9 | 10 | from config import template 11 | 12 | 13 | class TemplateResponse(ResponseBase): 14 | def __init__(self, context=None, template_path=None): 15 | """ 16 | Class to render template and send it through HTTP response 17 | context: the context dict form template rendering 18 | template_path: the path for te template. If None it will find the template by convention, according to path 19 | """ 20 | 21 | super(TemplateResponse, self).__init__(context) 22 | self.template_path = template_path 23 | 24 | 25 | class TemplateMiddleware(Middleware): 26 | def set_up(self): 27 | self.dependencies["_render"] = render_by_namespace 28 | 29 | 30 | _TMPL_NOT_FOUND_MSG = '''Template not found 31 | Looked by convention in /routes/templates directory for: 32 | 33 | 1) %s 34 | 2) %s 35 | 36 | Create one of the two template files or explicit indicate which one to use on TemplateResponse''' 37 | 38 | 39 | def render_by_namespace(template_path, context={}): 40 | ns = get_namespace() 41 | if not ns: 42 | return template.render(template_path, context) 43 | 44 | try: 45 | return template.render('/'.join([ns, template_path]), context) 46 | except TemplateNotFound: 47 | return template.render(template_path, context) 48 | 49 | 50 | def render_by_convention(fcn, context): 51 | template_path = router.to_path(fcn) 52 | 53 | def try_render(suffix): 54 | if template_path == '/': 55 | return '/home.html', render_by_namespace('/home.html', context) 56 | 57 | try: 58 | template_file = template_path + suffix 59 | return template_file, render_by_namespace(template_file, context) 60 | except TemplateNotFound: 61 | return template_file, None 62 | 63 | template_1, tmpl_rendered = try_render('.html') 64 | if tmpl_rendered is None: 65 | template_2, tmpl_rendered = try_render('/home.html') 66 | if tmpl_rendered is None: 67 | raise TemplateNotFound(_TMPL_NOT_FOUND_MSG % (template_1, template_2)) 68 | return tmpl_rendered 69 | 70 | 71 | class TemplateWriteMiddleware(Middleware): 72 | def set_up(self): 73 | fcn_response = self.dependencies['_fcn_response'] 74 | fcn = self.dependencies['_fcn'] 75 | if isinstance(fcn_response, TemplateResponse): 76 | context = fcn_response.context or {} 77 | for key in ('_logged_user', '_login_path', '_logout_path'): 78 | context[key] = self.dependencies[key] 79 | if '_csrf_code' in self.dependencies: 80 | context['_csrf_code'] = self.dependencies['_csrf_code'] 81 | template_path = fcn_response.template_path 82 | if template_path is None: 83 | tmpl_rendered = render_by_convention(fcn, context) 84 | 85 | 86 | else: 87 | tmpl_rendered = render_by_namespace(template_path, context) 88 | self.handler.response.write(tmpl_rendered) 89 | return True # after response, there is no need to look for more middlewares -------------------------------------------------------------------------------- /backend/appengine/static/permission/js/permissions.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('app', ['rest']) 2 | .controller('AppCtrl', ['$scope', 'RestApi', function ($scope, RestApi) { 3 | $scope.users = []; 4 | $scope.searchingUsersFlag = false; 5 | $scope.moreResultsFlag = false; 6 | $scope.searchPrefix = '' 7 | $scope.searchUsers = function (emailPrefix) { 8 | $scope.searchPrefix = emailPrefix; 9 | $scope.searchingUsersFlag = true; 10 | RestApi.searchUsers(emailPrefix).success(function (obj) { 11 | $scope.users = obj.users; 12 | $scope.moreResultsFlag = obj.more; 13 | $scope.nextPage = obj.next_page; 14 | }).always(function () { 15 | $scope.searchingUsersFlag = false; 16 | }); 17 | } 18 | $scope.searchUsers(''); 19 | }]); 20 | 21 | app.directive('permform', function () { 22 | return{ 23 | restrict: 'E', 24 | replace: true, 25 | templateUrl: '/static/permission/html/permission_form.html', 26 | scope: { 27 | search: '=', 28 | showButton: '=' 29 | }, 30 | controller: ['$scope', function ($scope) { 31 | $scope.emailPrefix = ''; 32 | }] 33 | }; 34 | }); 35 | 36 | app.directive('checkList', function () { 37 | return { 38 | scope: { 39 | list: '=checkList', 40 | value: '@' 41 | }, 42 | link: function (scope, elem, attrs) { 43 | var handler = function (setup) { 44 | var checked = elem.prop('checked'); 45 | var index = scope.list.indexOf(scope.value); 46 | 47 | if (checked && index == -1) { 48 | if (setup) elem.prop('checked', false); 49 | else scope.list.push(scope.value); 50 | } else if (!checked && index != -1) { 51 | if (setup) elem.prop('checked', true); 52 | else scope.list.splice(index, 1); 53 | } 54 | }; 55 | 56 | var setupHandler = handler.bind(null, true); 57 | var changeHandler = handler.bind(null, false); 58 | 59 | elem.on('change', function () { 60 | scope.$apply(changeHandler); 61 | }); 62 | scope.$watch('list', setupHandler, true); 63 | } 64 | }; 65 | }); 66 | 67 | app.directive('permtable', function () { 68 | return{ 69 | restrict: 'E', 70 | replace: true, 71 | templateUrl: '/static/permission/html/permission_table.html', 72 | scope: { 73 | users: '=', 74 | nextPage: '=', 75 | showTable: '=', 76 | moreResultsFlag: '=', 77 | searchPrefix: '=', 78 | groups: '=' 79 | }, 80 | controller: ['$scope', 'RestApi', function ($scope, rest) { 81 | $scope.searchingNextPage = false; 82 | 83 | $scope.searchNextPage = function (nextPage) { 84 | $scope.searchingNextPage = true; 85 | rest.searchNextPage(nextPage).success(function (obj) { 86 | for (var i = 0; i < obj.users.length; i++) { 87 | $scope.users.push(obj.users[i]); 88 | } 89 | $scope.nextPage = obj.next_page; 90 | $scope.moreResultsFlag = obj.more; 91 | }).always(function () { 92 | $scope.searchingNextPage = false; 93 | }); 94 | } 95 | 96 | }] 97 | }; 98 | }); 99 | 100 | app.directive('grouptd', function () { 101 | return{ 102 | restrict: 'E', 103 | replace: true, 104 | templateUrl: '/static/permission/html/group_td.html', 105 | scope: { 106 | groups: '=', 107 | user: '=' 108 | }, 109 | controller: ['$scope', 'RestApi', function ($scope, rest) { 110 | $scope.updatingGroups = false; 111 | 112 | $scope.updateUserGroups = function (user) { 113 | $scope.updatingGroups = true; 114 | rest.updateUserGroups(user.update, {'groups': user.groups}) 115 | .error(function(){ 116 | alert('It is not possible to save changes. Refresh the page and try again'); 117 | }) 118 | .always(function () { 119 | $scope.updatingGroups = false; 120 | }); 121 | } 122 | 123 | }] 124 | 125 | }; 126 | }); -------------------------------------------------------------------------------- /backend/appengine/templates/login/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% block css %} 3 | 4 | 5 | {% endblock %} 6 | {% block js %} 7 | 8 | {% endblock %} 9 | {% block body %} 10 |
11 |
12 | 13 |

{% trans %}Login Options{% endtrans %}

14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |

Email


22 | 23 |
24 |
25 | 27 | 28 | 29 | 30 |
31 | {{ csrf_input() }} 32 |
33 |
34 |
35 |
36 |
37 |

Google


38 | Google 40 |
41 |
42 |
43 |
44 |

Facebook


45 | 49 |
50 | {{ csrf_input() }} 51 | 52 |
53 |
54 |
55 |
56 |
57 | {# see https://developers.facebook.com/docs/facebook-login/login-flow-for-web/v2.0#} 58 | 118 | {% endblock %} -------------------------------------------------------------------------------- /backend/appengine/templates/base/base.html: -------------------------------------------------------------------------------- 1 | 2 | {% macro csrf_input() %} 3 | 4 | {% endmacro %} 5 | {% macro form_input(label,property,value,error_msg) %} 6 |
7 | 9 | 11 | {% if error_msg %}{{ error_msg }}{% endif %} 12 |
13 | {% endmacro %} 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% block title %}Tekton Fullstrack{% endblock %} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | {% block css %}{% endblock %} 35 | {% block js %}{% endblock %} 36 | 37 | 38 | 91 | {% block body %}{% endblock %} 92 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /backend/build_scripts/babel/locale/en_US/LC_MESSAGES/messages.po: -------------------------------------------------------------------------------- 1 | # English (United States) translations for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2015-06-29 18:50-0300\n" 12 | "PO-Revision-Date: 2014-06-26 00:14-0300\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: en_US \n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 1.3\n" 20 | 21 | #: ../../appengine/templates/home.html:11 22 | #: ../../appengine/templates/multitenant/home.html:11 23 | msgid "A Full-Stack project for Google App Engine" 24 | msgstr "" 25 | 26 | #: ../../appengine/templates/home.html:19 27 | #: ../../appengine/templates/multitenant/home.html:20 28 | msgid "" 29 | "Create CRUD code on\n" 30 | " App Engine" 32 | msgstr "" 33 | 34 | #: ../../appengine/templates/home.html:25 35 | #: ../../appengine/templates/multitenant/home.html:26 36 | msgid "" 37 | " Check Courses App\n" 38 | " Out" 39 | msgstr "" 40 | 41 | #: ../../appengine/templates/home.html:32 42 | #: ../../appengine/templates/multitenant/home.html:33 43 | msgid "Tekton is open source." 44 | msgstr "" 45 | 46 | #: ../../appengine/templates/home.html:37 47 | #: ../../appengine/templates/multitenant/home.html:38 48 | msgid "Check Git Hub Out" 49 | msgstr "" 50 | 51 | #: ../../appengine/templates/admin/home.html:5 52 | msgid "Administration Panel" 53 | msgstr "" 54 | 55 | #: ../../appengine/templates/admin/home.html:8 56 | msgid "Security Paths Table" 57 | msgstr "" 58 | 59 | #: ../../appengine/templates/admin/home.html:11 60 | msgid " Configurations" 61 | msgstr "" 62 | 63 | #: ../../appengine/templates/admin/home.html:13 64 | msgid " User Permissions" 65 | msgstr "" 66 | 67 | #: ../../appengine/templates/admin/home.html:14 68 | msgid "Facebook App Data" 69 | msgstr "" 70 | 71 | #: ../../appengine/templates/admin/home.html:15 72 | msgid "Passwordless App Data" 73 | msgstr "" 74 | 75 | #: ../../appengine/templates/base/400.html:6 76 | msgid "Sorry, an error happened and we are investigating" 77 | msgstr "" 78 | 79 | #: ../../appengine/templates/base/404.html:6 80 | msgid "Sorry, page not found" 81 | msgstr "" 82 | 83 | #: ../../appengine/templates/base/base.html:51 84 | msgid "Courses" 85 | msgstr "" 86 | 87 | #: ../../appengine/templates/base/base.html:54 88 | msgid "Admin" 89 | msgstr "" 90 | 91 | #: ../../appengine/templates/base/base.html:71 92 | msgid "Account" 93 | msgstr "" 94 | 95 | #: ../../appengine/templates/base/base.html:75 96 | msgid "" 97 | "\n" 98 | " Logout" 99 | msgstr "" 100 | 101 | #: ../../appengine/templates/base/base.html:83 102 | msgid "Login" 103 | msgstr "" 104 | 105 | #: ../../appengine/templates/login/home.html:13 106 | msgid "Login Options" 107 | msgstr "" 108 | 109 | #: ../../appengine/templates/login/home.html:25 110 | msgid "Type your email" 111 | msgstr "" 112 | 113 | #: ../../appengine/templates/login/pending.html:6 114 | msgid "Security Information" 115 | msgstr "" 116 | 117 | #: ../../appengine/templates/login/pending.html:8 118 | #, python-format 119 | msgid "" 120 | "The email provided by %(provider)s is already in use. For your security, " 121 | "we are going to\n" 122 | " send an email to %(email)s to ensure it is yours." 123 | msgstr "" 124 | 125 | #: ../../appengine/templates/login/pending.html:11 126 | msgid "This security procedure will be necessary only this time." 127 | msgstr "" 128 | 129 | #: ../../appengine/templates/login/pending.html:13 130 | #: ../../appengine/templates/login/passwordless/home.html:11 131 | msgid "Check your spam box, once some providers can filter this email." 132 | msgstr "" 133 | 134 | #: ../../appengine/templates/login/facebook/form.html:6 135 | msgid "Facebook App Form" 136 | msgstr "" 137 | 138 | #: ../../appengine/templates/login/facebook/form.html:8 139 | msgid "This data is used to proceed Facebook login. More information in" 140 | msgstr "" 141 | 142 | #: ../../appengine/templates/login/facebook/form.html:10 143 | msgid "Facebook Login Docs" 144 | msgstr "" 145 | 146 | #: ../../appengine/templates/login/facebook/form.html:14 147 | #: ../../appengine/templates/login/passwordless/form.html:14 148 | msgid "Type the app's id" 149 | msgstr "" 150 | 151 | #: ../../appengine/templates/login/facebook/form.html:16 152 | #: ../../appengine/templates/login/passwordless/form.html:16 153 | msgid "Type the app's token" 154 | msgstr "" 155 | 156 | #: ../../appengine/templates/login/facebook/form.html:18 157 | #: ../../appengine/templates/login/passwordless/form.html:18 158 | #: ../../appengine/templates/permission/account_form.html:36 159 | msgid "Save" 160 | msgstr "" 161 | 162 | #: ../../appengine/templates/login/passwordless/form.html:6 163 | msgid "Passworless App Form" 164 | msgstr "" 165 | 166 | #: ../../appengine/templates/login/passwordless/form.html:8 167 | msgid "This data is used to proceed passswordless login. More information in" 168 | msgstr "" 169 | 170 | #: ../../appengine/templates/login/passwordless/home.html:6 171 | msgid "Login Email Instructions" 172 | msgstr "" 173 | 174 | #: ../../appengine/templates/login/passwordless/home.html:8 175 | msgid "" 176 | "We will send you an email with the login link using\n" 177 | " Passwordless." 179 | msgstr "" 180 | 181 | #: ../../appengine/templates/permission/account_form.html:11 182 | msgid "Your Account" 183 | msgstr "" 184 | 185 | #: ../../appengine/templates/permission/account_form.html:15 186 | msgid "Name" 187 | msgstr "" 188 | 189 | #: ../../appengine/templates/permission/account_form.html:17 190 | msgid "Language" 191 | msgstr "" 192 | 193 | #: ../../appengine/templates/permission/account_form.html:29 194 | msgid "Time Zone" 195 | msgstr "" 196 | 197 | #: ../../appengine/templates/permission/admin.html:41 198 | msgid "Users Table" 199 | msgstr "" 200 | 201 | #: ../../appengine/templates/permission/home.html:6 202 | msgid "Security Table" 203 | msgstr "" 204 | 205 | #: ../../appengine/templates/permission/home.html:10 206 | msgid "Path" 207 | msgstr "" 208 | 209 | #: ../../appengine/templates/permission/home.html:11 210 | msgid "Allowed Groups" 211 | msgstr "" 212 | 213 | #: ../../appengine/templates/permission/home.html:12 214 | msgid "" 215 | "CSRF Security" 217 | msgstr "" 218 | 219 | #: ../../appengine/templates/permission/home.html:22 220 | msgid "Secure" 221 | msgstr "" 222 | 223 | #: ../../appengine/templates/permission/home.html:22 224 | msgid "Unsecure" 225 | msgstr "" 226 | 227 | #: ../../appengine/templates/updown/home.html:7 228 | msgid "File Upload Form" 229 | msgstr "" 230 | 231 | #: ../../appengine/templates/updown/home.html:12 232 | msgid "Choose a file" 233 | msgstr "" 234 | 235 | #: ../../appengine/templates/updown/home.html:15 236 | msgid "Upload" 237 | msgstr "" 238 | 239 | #: ../../appengine/templates/updown/ok.html:7 240 | msgid "File Upload Finished" 241 | msgstr "" 242 | 243 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:48 244 | #, python-format 245 | msgid "Must be one of: %(choices)s" 246 | msgstr "" 247 | 248 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:53 249 | msgid "Required field" 250 | msgstr "" 251 | 252 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:134 253 | #, python-format 254 | msgid "Has %(len)s characters and it must have exactly %(exactly_len)s" 255 | msgstr "" 256 | 257 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:137 258 | #, python-format 259 | msgid "Has %(len)s characters and it must have %(max_len)s or less" 260 | msgstr "" 261 | 262 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:140 263 | #, python-format 264 | msgid "Has %(len)s characters and it must have %(min_len)s or more" 265 | msgstr "" 266 | 267 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:149 268 | msgid "Invalid email" 269 | msgstr "" 270 | 271 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:173 272 | msgid "Key's kind should be defined" 273 | msgstr "" 274 | 275 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:178 276 | msgid "Invalid key" 277 | msgstr "" 278 | 279 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:225 280 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:285 281 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:332 282 | #, python-format 283 | msgid "Must be greater than %(lower)s" 284 | msgstr "" 285 | 286 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:227 287 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:287 288 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:334 289 | #, python-format 290 | msgid "Must be less than %(upper)s" 291 | msgstr "" 292 | 293 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:230 294 | msgid "Must be integer" 295 | msgstr "" 296 | 297 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:253 298 | #: ../../venv/lib/python2.7/site-packages/gaeforms/ndb/field.py:10 299 | msgid "Must be true or false" 300 | msgstr "" 301 | 302 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:290 303 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:337 304 | msgid "Must be a number" 305 | msgstr "" 306 | 307 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:371 308 | #, python-format 309 | msgid "Invalid date. Must be on format %(format)s" 310 | msgstr "" 311 | 312 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:371 313 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:377 314 | msgid "MM/dd/YYYY" 315 | msgstr "" 316 | 317 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:411 318 | #, python-format 319 | msgid "Invalid datetime. Must be on format %(format)s" 320 | msgstr "" 321 | 322 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:411 323 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:416 324 | msgid "MM/dd/YYYY HH:mm:ss" 325 | msgstr "" 326 | 327 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:482 328 | msgid "CEP must have exactly 8 characters" 329 | msgstr "" 330 | 331 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:486 332 | msgid "CEP must contain only numbers" 333 | msgstr "" 334 | 335 | #~ msgid "Tekton Full-Stack is open soure. " 336 | #~ msgstr "" 337 | 338 | #~ msgid "Tekton Full-Stack is open source. " 339 | #~ msgstr "" 340 | 341 | #~ msgid "A Full-Stak project for Google App Engine" 342 | #~ msgstr "" 343 | 344 | #~ msgid "Set your permission do ADMIN and play around" 345 | #~ msgstr "" 346 | 347 | #~ msgid "Start Date" 348 | #~ msgstr "" 349 | 350 | #~ msgid "Has %(len)s characters and it must have less than 500" 351 | #~ msgstr "" 352 | 353 | #~ msgid "This is a generic home for course_app " 354 | #~ msgstr "" 355 | 356 | #~ msgid "List of Courses" 357 | #~ msgstr "" 358 | 359 | #~ msgid "D" 360 | #~ msgstr "" 361 | 362 | #~ msgid "F" 363 | #~ msgstr "" 364 | 365 | #~ msgid "I" 366 | #~ msgstr "" 367 | 368 | #~ msgid "Price" 369 | #~ msgstr "" 370 | 371 | #~ msgid "Start" 372 | #~ msgstr "" 373 | 374 | #~ msgid "Name" 375 | #~ msgstr "" 376 | 377 | #~ msgid "Course Form" 378 | #~ msgstr "" 379 | 380 | #~ msgid "Create New Course" 381 | #~ msgstr "" 382 | 383 | #~ msgid "Id" 384 | #~ msgstr "" 385 | 386 | #~ msgid "Creation" 387 | #~ msgstr "" 388 | 389 | #~ msgid "Are you sure to delete? Press cancel to avoid deletion." 390 | #~ msgstr "" 391 | 392 | #~ msgid "Must be one of: %(choices)s" 393 | #~ msgstr "" 394 | 395 | #~ msgid "Required field" 396 | #~ msgstr "" 397 | 398 | #~ msgid "Has %(len)s characters and it must have less than %(max_len)s" 399 | #~ msgstr "" 400 | 401 | #~ msgid "Invalid email" 402 | #~ msgstr "" 403 | 404 | #~ msgid "Must be greater than %(lower)s" 405 | #~ msgstr "" 406 | 407 | #~ msgid "Must be less than %(upper)s" 408 | #~ msgstr "" 409 | 410 | #~ msgid "Must be integer" 411 | #~ msgstr "" 412 | 413 | #~ msgid "Must be a number" 414 | #~ msgstr "" 415 | 416 | #~ msgid "Invalid date. Must be on format %(format)s" 417 | #~ msgstr "" 418 | 419 | #~ msgid "MM/dd/YYYY" 420 | #~ msgstr "" 421 | 422 | #~ msgid "Invalid datetime. Must be on format %(format)s" 423 | #~ msgstr "" 424 | 425 | #~ msgid "MM/dd/YYYY HH:mm:ss" 426 | #~ msgstr "" 427 | 428 | #~ msgid "Logout" 429 | #~ msgstr "" 430 | 431 | #~ msgid "" 432 | #~ msgstr "" 433 | 434 | #~ msgid "Has %(len)s characters and it must have exactly %(exactly_len)s" 435 | #~ msgstr "" 436 | 437 | #~ msgid "Has %(len)s characters and it must have %(max_len)s or less" 438 | #~ msgstr "" 439 | 440 | #~ msgid "Has %(len)s characters and it must have %(min_len)s or more" 441 | #~ msgstr "" 442 | 443 | #~ msgid "Key's kind should be defined" 444 | #~ msgstr "" 445 | 446 | #~ msgid "Invalid key" 447 | #~ msgstr "" 448 | 449 | #~ msgid "Must be true or false" 450 | #~ msgstr "" 451 | 452 | #~ msgid "CEP must have exactly 8 characters" 453 | #~ msgstr "" 454 | 455 | #~ msgid "CEP must contain only numbers" 456 | #~ msgstr "" 457 | 458 | -------------------------------------------------------------------------------- /backend/build_scripts/babel/locale/pt_BR/LC_MESSAGES/messages.po: -------------------------------------------------------------------------------- 1 | # Portuguese (Brazil) translations for PROJECT. 2 | # Copyright (C) 2014 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2014. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2015-06-29 18:50-0300\n" 12 | "PO-Revision-Date: 2014-06-26 00:13-0300\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: pt_BR \n" 15 | "Plural-Forms: nplurals=2; plural=(n > 1)\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 1.3\n" 20 | 21 | #: ../../appengine/templates/home.html:11 22 | #: ../../appengine/templates/multitenant/home.html:11 23 | msgid "A Full-Stack project for Google App Engine" 24 | msgstr "Um projeto Full-Stack para o Google App Engine" 25 | 26 | #: ../../appengine/templates/home.html:19 27 | #: ../../appengine/templates/multitenant/home.html:20 28 | msgid "" 29 | "Create CRUD code on\n" 30 | " App Engine" 32 | msgstr "" 33 | "Crie código CRUD no App Engine" 35 | 36 | #: ../../appengine/templates/home.html:25 37 | #: ../../appengine/templates/multitenant/home.html:26 38 | msgid "" 39 | " Check Courses App\n" 40 | " Out" 41 | msgstr "Veja a App de Cursos" 42 | 43 | #: ../../appengine/templates/home.html:32 44 | #: ../../appengine/templates/multitenant/home.html:33 45 | #, fuzzy 46 | msgid "Tekton is open source." 47 | msgstr "O código do Tekton é aberto. " 48 | 49 | #: ../../appengine/templates/home.html:37 50 | #: ../../appengine/templates/multitenant/home.html:38 51 | msgid "Check Git Hub Out" 52 | msgstr "Confira no GitHub" 53 | 54 | #: ../../appengine/templates/admin/home.html:5 55 | msgid "Administration Panel" 56 | msgstr "Painel de Administração" 57 | 58 | #: ../../appengine/templates/admin/home.html:8 59 | msgid "Security Paths Table" 60 | msgstr "Tabela de Segurança de Paths" 61 | 62 | #: ../../appengine/templates/admin/home.html:11 63 | msgid " Configurations" 64 | msgstr " Configurações" 65 | 66 | #: ../../appengine/templates/admin/home.html:13 67 | msgid " User Permissions" 68 | msgstr " Permissões de Usuários" 69 | 70 | #: ../../appengine/templates/admin/home.html:14 71 | msgid "Facebook App Data" 72 | msgstr "Dados de App do Facebook" 73 | 74 | #: ../../appengine/templates/admin/home.html:15 75 | msgid "Passwordless App Data" 76 | msgstr "Dados de App do Passwordless" 77 | 78 | #: ../../appengine/templates/base/400.html:6 79 | msgid "Sorry, an error happened and we are investigating" 80 | msgstr "Desculpe, um erro ocorreu e estamos investigando." 81 | 82 | #: ../../appengine/templates/base/404.html:6 83 | msgid "Sorry, page not found" 84 | msgstr "Desculpe, página não encontrada" 85 | 86 | #: ../../appengine/templates/base/base.html:51 87 | msgid "Courses" 88 | msgstr "Cursos" 89 | 90 | #: ../../appengine/templates/base/base.html:54 91 | msgid "Admin" 92 | msgstr "Admin" 93 | 94 | #: ../../appengine/templates/base/base.html:71 95 | msgid "Account" 96 | msgstr "Conta" 97 | 98 | #: ../../appengine/templates/base/base.html:75 99 | #, fuzzy 100 | msgid "" 101 | "\n" 102 | " Logout" 103 | msgstr "Sair" 104 | 105 | #: ../../appengine/templates/base/base.html:83 106 | msgid "Login" 107 | msgstr "Login" 108 | 109 | #: ../../appengine/templates/login/home.html:13 110 | msgid "Login Options" 111 | msgstr "Opções de Login" 112 | 113 | #: ../../appengine/templates/login/home.html:25 114 | msgid "Type your email" 115 | msgstr "Digite seu Email" 116 | 117 | #: ../../appengine/templates/login/pending.html:6 118 | msgid "Security Information" 119 | msgstr "Informação de Segurança" 120 | 121 | #: ../../appengine/templates/login/pending.html:8 122 | #, python-format 123 | msgid "" 124 | "The email provided by %(provider)s is already in use. For your security, " 125 | "we are going to\n" 126 | " send an email to %(email)s to ensure it is yours." 127 | msgstr "" 128 | "O email fornecido pelo %(provider)s já está em uso. Para sua segurança, " 129 | "enviaremos um email de login para %(email)s para assegurar que ele é seu." 130 | 131 | #: ../../appengine/templates/login/pending.html:11 132 | msgid "This security procedure will be necessary only this time." 133 | msgstr "Esse procedimento de segurança só será necessário essa vez." 134 | 135 | #: ../../appengine/templates/login/pending.html:13 136 | #: ../../appengine/templates/login/passwordless/home.html:11 137 | msgid "Check your spam box, once some providers can filter this email." 138 | msgstr "Cheque sua caixa de spam pois alguns provedores podem filtrar esse email." 139 | 140 | #: ../../appengine/templates/login/facebook/form.html:6 141 | msgid "Facebook App Form" 142 | msgstr "Formulário de Aplicação do Facebook" 143 | 144 | #: ../../appengine/templates/login/facebook/form.html:8 145 | msgid "This data is used to proceed Facebook login. More information in" 146 | msgstr "" 147 | "Esses dados são necessáŕios para implementar o login com Facebook. Mais " 148 | "informações em" 149 | 150 | #: ../../appengine/templates/login/facebook/form.html:10 151 | msgid "Facebook Login Docs" 152 | msgstr "Documentação de Login com Facebook" 153 | 154 | #: ../../appengine/templates/login/facebook/form.html:14 155 | #: ../../appengine/templates/login/passwordless/form.html:14 156 | msgid "Type the app's id" 157 | msgstr "Digite o id da aplicação" 158 | 159 | #: ../../appengine/templates/login/facebook/form.html:16 160 | #: ../../appengine/templates/login/passwordless/form.html:16 161 | msgid "Type the app's token" 162 | msgstr "Digite o token da aplicação" 163 | 164 | #: ../../appengine/templates/login/facebook/form.html:18 165 | #: ../../appengine/templates/login/passwordless/form.html:18 166 | #: ../../appengine/templates/permission/account_form.html:36 167 | msgid "Save" 168 | msgstr "Salvar" 169 | 170 | #: ../../appengine/templates/login/passwordless/form.html:6 171 | msgid "Passworless App Form" 172 | msgstr "Formulário de Aplicação Passworless" 173 | 174 | #: ../../appengine/templates/login/passwordless/form.html:8 175 | msgid "This data is used to proceed passswordless login. More information in" 176 | msgstr "" 177 | "Esses dados são necessáŕios para implementar o login por email. Mais " 178 | "informações em" 179 | 180 | #: ../../appengine/templates/login/passwordless/home.html:6 181 | msgid "Login Email Instructions" 182 | msgstr "Instruções de Login por Email" 183 | 184 | #: ../../appengine/templates/login/passwordless/home.html:8 185 | msgid "" 186 | "We will send you an email with the login link using\n" 187 | " Passwordless." 189 | msgstr "" 190 | "Enviaremos um email para você contendo o link para login. Ele será " 191 | "enviado através do serviço Passwordless." 193 | 194 | #: ../../appengine/templates/permission/account_form.html:11 195 | msgid "Your Account" 196 | msgstr "Sua Conta" 197 | 198 | #: ../../appengine/templates/permission/account_form.html:15 199 | msgid "Name" 200 | msgstr "Nome" 201 | 202 | #: ../../appengine/templates/permission/account_form.html:17 203 | msgid "Language" 204 | msgstr "Idioma" 205 | 206 | #: ../../appengine/templates/permission/account_form.html:29 207 | msgid "Time Zone" 208 | msgstr "Fuso Horário" 209 | 210 | #: ../../appengine/templates/permission/admin.html:41 211 | msgid "Users Table" 212 | msgstr "Tabela de Usuários" 213 | 214 | #: ../../appengine/templates/permission/home.html:6 215 | msgid "Security Table" 216 | msgstr "Tabela de Segurança" 217 | 218 | #: ../../appengine/templates/permission/home.html:10 219 | msgid "Path" 220 | msgstr "Path" 221 | 222 | #: ../../appengine/templates/permission/home.html:11 223 | msgid "Allowed Groups" 224 | msgstr "Grupos Permitidos" 225 | 226 | #: ../../appengine/templates/permission/home.html:12 227 | msgid "" 228 | "CSRF Security" 230 | msgstr "" 231 | "Segurança CSRF" 233 | 234 | #: ../../appengine/templates/permission/home.html:22 235 | msgid "Secure" 236 | msgstr "Seguro" 237 | 238 | #: ../../appengine/templates/permission/home.html:22 239 | msgid "Unsecure" 240 | msgstr "Inseguro" 241 | 242 | #: ../../appengine/templates/updown/home.html:7 243 | msgid "File Upload Form" 244 | msgstr "Formulário de Upload de Arquivo" 245 | 246 | #: ../../appengine/templates/updown/home.html:12 247 | msgid "Choose a file" 248 | msgstr "Escolha um arquivo" 249 | 250 | #: ../../appengine/templates/updown/home.html:15 251 | msgid "Upload" 252 | msgstr "Carregar" 253 | 254 | #: ../../appengine/templates/updown/ok.html:7 255 | msgid "File Upload Finished" 256 | msgstr "Upload de Arquivo Finalizado" 257 | 258 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:48 259 | #, python-format 260 | msgid "Must be one of: %(choices)s" 261 | msgstr "Deve ser um dos seguintes: %(choices)s" 262 | 263 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:53 264 | msgid "Required field" 265 | msgstr "Campo obrigatório" 266 | 267 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:134 268 | #, python-format 269 | msgid "Has %(len)s characters and it must have exactly %(exactly_len)s" 270 | msgstr "Possui %(len)s caracteres e deve ter menos de %(exactly_len)s" 271 | 272 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:137 273 | #, python-format 274 | msgid "Has %(len)s characters and it must have %(max_len)s or less" 275 | msgstr "Tem caracteres% (LEN) s e deve ter% (max_len) s ou menos" 276 | 277 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:140 278 | #, python-format 279 | msgid "Has %(len)s characters and it must have %(min_len)s or more" 280 | msgstr "Possui %(len)s caracteres e deve ter menos de %(min_len)s" 281 | 282 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:149 283 | msgid "Invalid email" 284 | msgstr "E-mail inválido" 285 | 286 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:173 287 | msgid "Key's kind should be defined" 288 | msgstr "Tipo de chave deve ser definida" 289 | 290 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:178 291 | msgid "Invalid key" 292 | msgstr "Chave inválida" 293 | 294 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:225 295 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:285 296 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:332 297 | #, python-format 298 | msgid "Must be greater than %(lower)s" 299 | msgstr "Deve ser maior que %(lower)s" 300 | 301 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:227 302 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:287 303 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:334 304 | #, python-format 305 | msgid "Must be less than %(upper)s" 306 | msgstr "Deve ser menor que %(upper)s" 307 | 308 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:230 309 | msgid "Must be integer" 310 | msgstr "Deve ser inteiro" 311 | 312 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:253 313 | #: ../../venv/lib/python2.7/site-packages/gaeforms/ndb/field.py:10 314 | msgid "Must be true or false" 315 | msgstr "Deve ser verdadeiro ou falso" 316 | 317 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:290 318 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:337 319 | msgid "Must be a number" 320 | msgstr "Deve ser um número" 321 | 322 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:371 323 | #, python-format 324 | msgid "Invalid date. Must be on format %(format)s" 325 | msgstr "Data inválida. Deve ser em formato %(format)s" 326 | 327 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:371 328 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:377 329 | msgid "MM/dd/YYYY" 330 | msgstr "dd/MM/YYYY" 331 | 332 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:411 333 | #, python-format 334 | msgid "Invalid datetime. Must be on format %(format)s" 335 | msgstr "Datahora inválida. Deve estar no formato %(format) s" 336 | 337 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:411 338 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:416 339 | msgid "MM/dd/YYYY HH:mm:ss" 340 | msgstr "dd/MM/YYYY HH:mm:ss" 341 | 342 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:482 343 | msgid "CEP must have exactly 8 characters" 344 | msgstr "CEP deve ter exatamente 8 caracteres" 345 | 346 | #: ../../venv/lib/python2.7/site-packages/gaeforms/base.py:486 347 | msgid "CEP must contain only numbers" 348 | msgstr "CEP deve conter apenas números" 349 | 350 | #~ msgid "Set your permission do ADMIN and play around" 351 | #~ msgstr "" 352 | 353 | #~ msgid "Start Date" 354 | #~ msgstr "Data de Início" 355 | 356 | #~ msgid "This is a generic home for course_app " 357 | #~ msgstr "Esta é uma home genérica para app de Cursos " 358 | 359 | #~ msgid "List of Courses" 360 | #~ msgstr "Lista de Cursos" 361 | 362 | #~ msgid "D" 363 | #~ msgstr "Id" 364 | 365 | #~ msgid "F" 366 | #~ msgstr "" 367 | 368 | #~ msgid "I" 369 | #~ msgstr "" 370 | 371 | #~ msgid "Price" 372 | #~ msgstr "Preço" 373 | 374 | #~ msgid "Start" 375 | #~ msgstr "" 376 | 377 | #~ msgid "Name" 378 | #~ msgstr "Nome" 379 | 380 | #~ msgid "Course Form" 381 | #~ msgstr "Formulário de Curso" 382 | 383 | #~ msgid "Create New Course" 384 | #~ msgstr "Criar Novo Curso" 385 | 386 | #~ msgid "Id" 387 | #~ msgstr "Id (identificador)" 388 | 389 | #~ msgid "Creation" 390 | #~ msgstr "Criação" 391 | 392 | #~ msgid "Are you sure to delete? Press cancel to avoid deletion." 393 | #~ msgstr "" 394 | 395 | #~ msgid "Must be one of: %(choices)s" 396 | #~ msgstr "Deve ser um das opções: %(choices)s" 397 | 398 | #~ msgid "Required field" 399 | #~ msgstr "Campo obrigatório" 400 | 401 | #~ msgid "Has %(len)s characters and it must have less than %(max_len)s" 402 | #~ msgstr "Possui %(len)s caracteres e deve ter menos de 500" 403 | 404 | #~ msgid "Invalid email" 405 | #~ msgstr "E-mail inválido" 406 | 407 | #~ msgid "Must be greater than %(lower)s" 408 | #~ msgstr "Deve ser maior que %(lower)s" 409 | 410 | #~ msgid "Must be less than %(upper)s" 411 | #~ msgstr "Deve ser menor que %(upper)s" 412 | 413 | #~ msgid "Must be integer" 414 | #~ msgstr "Deve ser inteiro" 415 | 416 | #~ msgid "Must be a number" 417 | #~ msgstr "Deve ser um número" 418 | 419 | #~ msgid "Invalid date. Must be on format %(format)s" 420 | #~ msgstr "Data inválida. Deve estar no formato %(format)s" 421 | 422 | #~ msgid "MM/dd/YYYY" 423 | #~ msgstr "dd/MM/YYYY" 424 | 425 | #~ msgid "Invalid datetime. Must be on format %(format)s" 426 | #~ msgstr "Datahora inválida. Deve estar no formato %(format) s" 427 | 428 | #~ msgid "MM/dd/YYYY HH:mm:ss" 429 | #~ msgstr "dd/MM/YYYY HH:mm:ss" 430 | 431 | #~ msgid "" 432 | #~ msgstr "" 433 | 434 | #~ msgid "Has %(len)s characters and it must have exactly %(exactly_len)s" 435 | #~ msgstr "Possui %(len)s caracteres e deve ter menos de %(exactly_len)s" 436 | 437 | #~ msgid "Has %(len)s characters and it must have %(max_len)s or less" 438 | #~ msgstr "Possui %(len)s caracteres e deve ter menos de %(max_len)s" 439 | 440 | #~ msgid "Has %(len)s characters and it must have %(min_len)s or more" 441 | #~ msgstr "Possui %(len)s caracteres e deve ter menos de %(min_len)s" 442 | 443 | #~ msgid "Key's kind should be defined" 444 | #~ msgstr "Tipo de chave deve ser definida" 445 | 446 | #~ msgid "Invalid key" 447 | #~ msgstr "E-mail inválido" 448 | 449 | #~ msgid "Must be true or false" 450 | #~ msgstr "Deve ser verdadeiro ou falso" 451 | 452 | #~ msgid "CEP must have exactly 8 characters" 453 | #~ msgstr "CEP deve ter exatamente 8 caracteres" 454 | 455 | #~ msgid "CEP must contain only numbers" 456 | #~ msgstr "CEP deve conter apenas números" 457 | 458 | -------------------------------------------------------------------------------- /backend/appengine/static/bootstrap/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-o-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#2d6ca2));background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-primary:disabled,.btn-primary[disabled]{background-color:#2d6ca2;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f3f3f3));background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:-o-linear-gradient(top,#222 0,#282828 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#222),to(#282828));background-image:linear-gradient(to bottom,#222 0,#282828 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-o-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3071a9));background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-o-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3278b3));background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);background-repeat:repeat-x;border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -------------------------------------------------------------------------------- /backend/appengine/static/bootstrap/css/bootstrap-social.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Social Buttons for Bootstrap 3 | * 4 | * Copyright 2013-2014 Panayiotis Lipiridis 5 | * Licensed under the MIT License 6 | * 7 | * https://github.com/lipis/bootstrap-social 8 | */ 9 | 10 | .btn-social{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social :first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} 11 | .btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg :first-child{line-height:45px;width:45px;font-size:1.8em} 12 | .btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm :first-child{line-height:28px;width:28px;font-size:1.4em} 13 | .btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs :first-child{line-height:20px;width:20px;font-size:1.2em} 14 | .btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon :first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} 15 | .btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg :first-child{line-height:45px;width:45px;font-size:1.8em} 16 | .btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm :first-child{line-height:28px;width:28px;font-size:1.4em} 17 | .btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs :first-child{line-height:20px;width:20px;font-size:1.2em} 18 | .btn-social-icon :first-child{border:none;text-align:center;width:100% !important} 19 | .btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0} 20 | .btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0} 21 | .btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0} 22 | .btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,0.2)}.btn-adn:hover,.btn-adn:focus,.btn-adn:active,.btn-adn.active,.open .dropdown-toggle.btn-adn{color:#fff;background-color:#d05d48;border-color:rgba(0,0,0,0.2)} 23 | .btn-adn:active,.btn-adn.active,.open .dropdown-toggle.btn-adn{background-image:none} 24 | .btn-adn.disabled,.btn-adn[disabled],fieldset[disabled] .btn-adn,.btn-adn.disabled:hover,.btn-adn[disabled]:hover,fieldset[disabled] .btn-adn:hover,.btn-adn.disabled:focus,.btn-adn[disabled]:focus,fieldset[disabled] .btn-adn:focus,.btn-adn.disabled:active,.btn-adn[disabled]:active,fieldset[disabled] .btn-adn:active,.btn-adn.disabled.active,.btn-adn[disabled].active,fieldset[disabled] .btn-adn.active{background-color:#d87a68;border-color:rgba(0,0,0,0.2)} 25 | .btn-adn .badge{color:#d87a68;background-color:#fff} 26 | .btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:hover,.btn-bitbucket:focus,.btn-bitbucket:active,.btn-bitbucket.active,.open .dropdown-toggle.btn-bitbucket{color:#fff;background-color:#183c60;border-color:rgba(0,0,0,0.2)} 27 | .btn-bitbucket:active,.btn-bitbucket.active,.open .dropdown-toggle.btn-bitbucket{background-image:none} 28 | .btn-bitbucket.disabled,.btn-bitbucket[disabled],fieldset[disabled] .btn-bitbucket,.btn-bitbucket.disabled:hover,.btn-bitbucket[disabled]:hover,fieldset[disabled] .btn-bitbucket:hover,.btn-bitbucket.disabled:focus,.btn-bitbucket[disabled]:focus,fieldset[disabled] .btn-bitbucket:focus,.btn-bitbucket.disabled:active,.btn-bitbucket[disabled]:active,fieldset[disabled] .btn-bitbucket:active,.btn-bitbucket.disabled.active,.btn-bitbucket[disabled].active,fieldset[disabled] .btn-bitbucket.active{background-color:#205081;border-color:rgba(0,0,0,0.2)} 29 | .btn-bitbucket .badge{color:#205081;background-color:#fff} 30 | .btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,0.2)}.btn-dropbox:hover,.btn-dropbox:focus,.btn-dropbox:active,.btn-dropbox.active,.open .dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d70b7;border-color:rgba(0,0,0,0.2)} 31 | .btn-dropbox:active,.btn-dropbox.active,.open .dropdown-toggle.btn-dropbox{background-image:none} 32 | .btn-dropbox.disabled,.btn-dropbox[disabled],fieldset[disabled] .btn-dropbox,.btn-dropbox.disabled:hover,.btn-dropbox[disabled]:hover,fieldset[disabled] .btn-dropbox:hover,.btn-dropbox.disabled:focus,.btn-dropbox[disabled]:focus,fieldset[disabled] .btn-dropbox:focus,.btn-dropbox.disabled:active,.btn-dropbox[disabled]:active,fieldset[disabled] .btn-dropbox:active,.btn-dropbox.disabled.active,.btn-dropbox[disabled].active,fieldset[disabled] .btn-dropbox.active{background-color:#1087dd;border-color:rgba(0,0,0,0.2)} 33 | .btn-dropbox .badge{color:#1087dd;background-color:#fff} 34 | .btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,0.2)}.btn-facebook:hover,.btn-facebook:focus,.btn-facebook:active,.btn-facebook.active,.open .dropdown-toggle.btn-facebook{color:#fff;background-color:#30487b;border-color:rgba(0,0,0,0.2)} 35 | .btn-facebook:active,.btn-facebook.active,.open .dropdown-toggle.btn-facebook{background-image:none} 36 | .btn-facebook.disabled,.btn-facebook[disabled],fieldset[disabled] .btn-facebook,.btn-facebook.disabled:hover,.btn-facebook[disabled]:hover,fieldset[disabled] .btn-facebook:hover,.btn-facebook.disabled:focus,.btn-facebook[disabled]:focus,fieldset[disabled] .btn-facebook:focus,.btn-facebook.disabled:active,.btn-facebook[disabled]:active,fieldset[disabled] .btn-facebook:active,.btn-facebook.disabled.active,.btn-facebook[disabled].active,fieldset[disabled] .btn-facebook.active{background-color:#3b5998;border-color:rgba(0,0,0,0.2)} 37 | .btn-facebook .badge{color:#3b5998;background-color:#fff} 38 | .btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,0.2)}.btn-flickr:hover,.btn-flickr:focus,.btn-flickr:active,.btn-flickr.active,.open .dropdown-toggle.btn-flickr{color:#fff;background-color:#d6006f;border-color:rgba(0,0,0,0.2)} 39 | .btn-flickr:active,.btn-flickr.active,.open .dropdown-toggle.btn-flickr{background-image:none} 40 | .btn-flickr.disabled,.btn-flickr[disabled],fieldset[disabled] .btn-flickr,.btn-flickr.disabled:hover,.btn-flickr[disabled]:hover,fieldset[disabled] .btn-flickr:hover,.btn-flickr.disabled:focus,.btn-flickr[disabled]:focus,fieldset[disabled] .btn-flickr:focus,.btn-flickr.disabled:active,.btn-flickr[disabled]:active,fieldset[disabled] .btn-flickr:active,.btn-flickr.disabled.active,.btn-flickr[disabled].active,fieldset[disabled] .btn-flickr.active{background-color:#ff0084;border-color:rgba(0,0,0,0.2)} 41 | .btn-flickr .badge{color:#ff0084;background-color:#fff} 42 | .btn-foursquare{color:#fff;background-color:#0072b1;border-color:rgba(0,0,0,0.2)}.btn-foursquare:hover,.btn-foursquare:focus,.btn-foursquare:active,.btn-foursquare.active,.open .dropdown-toggle.btn-foursquare{color:#fff;background-color:#005888;border-color:rgba(0,0,0,0.2)} 43 | .btn-foursquare:active,.btn-foursquare.active,.open .dropdown-toggle.btn-foursquare{background-image:none} 44 | .btn-foursquare.disabled,.btn-foursquare[disabled],fieldset[disabled] .btn-foursquare,.btn-foursquare.disabled:hover,.btn-foursquare[disabled]:hover,fieldset[disabled] .btn-foursquare:hover,.btn-foursquare.disabled:focus,.btn-foursquare[disabled]:focus,fieldset[disabled] .btn-foursquare:focus,.btn-foursquare.disabled:active,.btn-foursquare[disabled]:active,fieldset[disabled] .btn-foursquare:active,.btn-foursquare.disabled.active,.btn-foursquare[disabled].active,fieldset[disabled] .btn-foursquare.active{background-color:#0072b1;border-color:rgba(0,0,0,0.2)} 45 | .btn-foursquare .badge{color:#0072b1;background-color:#fff} 46 | .btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,0.2)}.btn-github:hover,.btn-github:focus,.btn-github:active,.btn-github.active,.open .dropdown-toggle.btn-github{color:#fff;background-color:#303030;border-color:rgba(0,0,0,0.2)} 47 | .btn-github:active,.btn-github.active,.open .dropdown-toggle.btn-github{background-image:none} 48 | .btn-github.disabled,.btn-github[disabled],fieldset[disabled] .btn-github,.btn-github.disabled:hover,.btn-github[disabled]:hover,fieldset[disabled] .btn-github:hover,.btn-github.disabled:focus,.btn-github[disabled]:focus,fieldset[disabled] .btn-github:focus,.btn-github.disabled:active,.btn-github[disabled]:active,fieldset[disabled] .btn-github:active,.btn-github.disabled.active,.btn-github[disabled].active,fieldset[disabled] .btn-github.active{background-color:#444;border-color:rgba(0,0,0,0.2)} 49 | .btn-github .badge{color:#444;background-color:#fff} 50 | .btn-google-plus{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google-plus:hover,.btn-google-plus:focus,.btn-google-plus:active,.btn-google-plus.active,.open .dropdown-toggle.btn-google-plus{color:#fff;background-color:#ca3523;border-color:rgba(0,0,0,0.2)} 51 | .btn-google-plus:active,.btn-google-plus.active,.open .dropdown-toggle.btn-google-plus{background-image:none} 52 | .btn-google-plus.disabled,.btn-google-plus[disabled],fieldset[disabled] .btn-google-plus,.btn-google-plus.disabled:hover,.btn-google-plus[disabled]:hover,fieldset[disabled] .btn-google-plus:hover,.btn-google-plus.disabled:focus,.btn-google-plus[disabled]:focus,fieldset[disabled] .btn-google-plus:focus,.btn-google-plus.disabled:active,.btn-google-plus[disabled]:active,fieldset[disabled] .btn-google-plus:active,.btn-google-plus.disabled.active,.btn-google-plus[disabled].active,fieldset[disabled] .btn-google-plus.active{background-color:#dd4b39;border-color:rgba(0,0,0,0.2)} 53 | .btn-google-plus .badge{color:#dd4b39;background-color:#fff} 54 | .btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,0.2)}.btn-instagram:hover,.btn-instagram:focus,.btn-instagram:active,.btn-instagram.active,.open .dropdown-toggle.btn-instagram{color:#fff;background-color:#335d7e;border-color:rgba(0,0,0,0.2)} 55 | .btn-instagram:active,.btn-instagram.active,.open .dropdown-toggle.btn-instagram{background-image:none} 56 | .btn-instagram.disabled,.btn-instagram[disabled],fieldset[disabled] .btn-instagram,.btn-instagram.disabled:hover,.btn-instagram[disabled]:hover,fieldset[disabled] .btn-instagram:hover,.btn-instagram.disabled:focus,.btn-instagram[disabled]:focus,fieldset[disabled] .btn-instagram:focus,.btn-instagram.disabled:active,.btn-instagram[disabled]:active,fieldset[disabled] .btn-instagram:active,.btn-instagram.disabled.active,.btn-instagram[disabled].active,fieldset[disabled] .btn-instagram.active{background-color:#3f729b;border-color:rgba(0,0,0,0.2)} 57 | .btn-instagram .badge{color:#3f729b;background-color:#fff} 58 | .btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,0.2)}.btn-linkedin:hover,.btn-linkedin:focus,.btn-linkedin:active,.btn-linkedin.active,.open .dropdown-toggle.btn-linkedin{color:#fff;background-color:#005f8d;border-color:rgba(0,0,0,0.2)} 59 | .btn-linkedin:active,.btn-linkedin.active,.open .dropdown-toggle.btn-linkedin{background-image:none} 60 | .btn-linkedin.disabled,.btn-linkedin[disabled],fieldset[disabled] .btn-linkedin,.btn-linkedin.disabled:hover,.btn-linkedin[disabled]:hover,fieldset[disabled] .btn-linkedin:hover,.btn-linkedin.disabled:focus,.btn-linkedin[disabled]:focus,fieldset[disabled] .btn-linkedin:focus,.btn-linkedin.disabled:active,.btn-linkedin[disabled]:active,fieldset[disabled] .btn-linkedin:active,.btn-linkedin.disabled.active,.btn-linkedin[disabled].active,fieldset[disabled] .btn-linkedin.active{background-color:#007bb6;border-color:rgba(0,0,0,0.2)} 61 | .btn-linkedin .badge{color:#007bb6;background-color:#fff} 62 | .btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,0.2)}.btn-microsoft:hover,.btn-microsoft:focus,.btn-microsoft:active,.btn-microsoft.active,.open .dropdown-toggle.btn-microsoft{color:#fff;background-color:#135ed6;border-color:rgba(0,0,0,0.2)} 63 | .btn-microsoft:active,.btn-microsoft.active,.open .dropdown-toggle.btn-microsoft{background-image:none} 64 | .btn-microsoft.disabled,.btn-microsoft[disabled],fieldset[disabled] .btn-microsoft,.btn-microsoft.disabled:hover,.btn-microsoft[disabled]:hover,fieldset[disabled] .btn-microsoft:hover,.btn-microsoft.disabled:focus,.btn-microsoft[disabled]:focus,fieldset[disabled] .btn-microsoft:focus,.btn-microsoft.disabled:active,.btn-microsoft[disabled]:active,fieldset[disabled] .btn-microsoft:active,.btn-microsoft.disabled.active,.btn-microsoft[disabled].active,fieldset[disabled] .btn-microsoft.active{background-color:#2672ec;border-color:rgba(0,0,0,0.2)} 65 | .btn-microsoft .badge{color:#2672ec;background-color:#fff} 66 | .btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:hover,.btn-openid:focus,.btn-openid:active,.btn-openid.active,.open .dropdown-toggle.btn-openid{color:#fff;background-color:#e47f08;border-color:rgba(0,0,0,0.2)} 67 | .btn-openid:active,.btn-openid.active,.open .dropdown-toggle.btn-openid{background-image:none} 68 | .btn-openid.disabled,.btn-openid[disabled],fieldset[disabled] .btn-openid,.btn-openid.disabled:hover,.btn-openid[disabled]:hover,fieldset[disabled] .btn-openid:hover,.btn-openid.disabled:focus,.btn-openid[disabled]:focus,fieldset[disabled] .btn-openid:focus,.btn-openid.disabled:active,.btn-openid[disabled]:active,fieldset[disabled] .btn-openid:active,.btn-openid.disabled.active,.btn-openid[disabled].active,fieldset[disabled] .btn-openid.active{background-color:#f7931e;border-color:rgba(0,0,0,0.2)} 69 | .btn-openid .badge{color:#f7931e;background-color:#fff} 70 | .btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}.btn-reddit:hover,.btn-reddit:focus,.btn-reddit:active,.btn-reddit.active,.open .dropdown-toggle.btn-reddit{color:#000;background-color:#c6e3ff;border-color:rgba(0,0,0,0.2)} 71 | .btn-reddit:active,.btn-reddit.active,.open .dropdown-toggle.btn-reddit{background-image:none} 72 | .btn-reddit.disabled,.btn-reddit[disabled],fieldset[disabled] .btn-reddit,.btn-reddit.disabled:hover,.btn-reddit[disabled]:hover,fieldset[disabled] .btn-reddit:hover,.btn-reddit.disabled:focus,.btn-reddit[disabled]:focus,fieldset[disabled] .btn-reddit:focus,.btn-reddit.disabled:active,.btn-reddit[disabled]:active,fieldset[disabled] .btn-reddit:active,.btn-reddit.disabled.active,.btn-reddit[disabled].active,fieldset[disabled] .btn-reddit.active{background-color:#eff7ff;border-color:rgba(0,0,0,0.2)} 73 | .btn-reddit .badge{color:#eff7ff;background-color:#000} 74 | .btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:hover,.btn-soundcloud:focus,.btn-soundcloud:active,.btn-soundcloud.active,.open .dropdown-toggle.btn-soundcloud{color:#fff;background-color:#d64700;border-color:rgba(0,0,0,0.2)} 75 | .btn-soundcloud:active,.btn-soundcloud.active,.open .dropdown-toggle.btn-soundcloud{background-image:none} 76 | .btn-soundcloud.disabled,.btn-soundcloud[disabled],fieldset[disabled] .btn-soundcloud,.btn-soundcloud.disabled:hover,.btn-soundcloud[disabled]:hover,fieldset[disabled] .btn-soundcloud:hover,.btn-soundcloud.disabled:focus,.btn-soundcloud[disabled]:focus,fieldset[disabled] .btn-soundcloud:focus,.btn-soundcloud.disabled:active,.btn-soundcloud[disabled]:active,fieldset[disabled] .btn-soundcloud:active,.btn-soundcloud.disabled.active,.btn-soundcloud[disabled].active,fieldset[disabled] .btn-soundcloud.active{background-color:#f50;border-color:rgba(0,0,0,0.2)} 77 | .btn-soundcloud .badge{color:#f50;background-color:#fff} 78 | .btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,0.2)}.btn-tumblr:hover,.btn-tumblr:focus,.btn-tumblr:active,.btn-tumblr.active,.open .dropdown-toggle.btn-tumblr{color:#fff;background-color:#1f3346;border-color:rgba(0,0,0,0.2)} 79 | .btn-tumblr:active,.btn-tumblr.active,.open .dropdown-toggle.btn-tumblr{background-image:none} 80 | .btn-tumblr.disabled,.btn-tumblr[disabled],fieldset[disabled] .btn-tumblr,.btn-tumblr.disabled:hover,.btn-tumblr[disabled]:hover,fieldset[disabled] .btn-tumblr:hover,.btn-tumblr.disabled:focus,.btn-tumblr[disabled]:focus,fieldset[disabled] .btn-tumblr:focus,.btn-tumblr.disabled:active,.btn-tumblr[disabled]:active,fieldset[disabled] .btn-tumblr:active,.btn-tumblr.disabled.active,.btn-tumblr[disabled].active,fieldset[disabled] .btn-tumblr.active{background-color:#2c4762;border-color:rgba(0,0,0,0.2)} 81 | .btn-tumblr .badge{color:#2c4762;background-color:#fff} 82 | .btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,0.2)}.btn-twitter:hover,.btn-twitter:focus,.btn-twitter:active,.btn-twitter.active,.open .dropdown-toggle.btn-twitter{color:#fff;background-color:#309aea;border-color:rgba(0,0,0,0.2)} 83 | .btn-twitter:active,.btn-twitter.active,.open .dropdown-toggle.btn-twitter{background-image:none} 84 | .btn-twitter.disabled,.btn-twitter[disabled],fieldset[disabled] .btn-twitter,.btn-twitter.disabled:hover,.btn-twitter[disabled]:hover,fieldset[disabled] .btn-twitter:hover,.btn-twitter.disabled:focus,.btn-twitter[disabled]:focus,fieldset[disabled] .btn-twitter:focus,.btn-twitter.disabled:active,.btn-twitter[disabled]:active,fieldset[disabled] .btn-twitter:active,.btn-twitter.disabled.active,.btn-twitter[disabled].active,fieldset[disabled] .btn-twitter.active{background-color:#55acee;border-color:rgba(0,0,0,0.2)} 85 | .btn-twitter .badge{color:#55acee;background-color:#fff} 86 | .btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}.btn-vimeo:hover,.btn-vimeo:focus,.btn-vimeo:active,.btn-vimeo.active,.open .dropdown-toggle.btn-vimeo{color:#fff;background-color:#129cc9;border-color:rgba(0,0,0,0.2)} 87 | .btn-vimeo:active,.btn-vimeo.active,.open .dropdown-toggle.btn-vimeo{background-image:none} 88 | .btn-vimeo.disabled,.btn-vimeo[disabled],fieldset[disabled] .btn-vimeo,.btn-vimeo.disabled:hover,.btn-vimeo[disabled]:hover,fieldset[disabled] .btn-vimeo:hover,.btn-vimeo.disabled:focus,.btn-vimeo[disabled]:focus,fieldset[disabled] .btn-vimeo:focus,.btn-vimeo.disabled:active,.btn-vimeo[disabled]:active,fieldset[disabled] .btn-vimeo:active,.btn-vimeo.disabled.active,.btn-vimeo[disabled].active,fieldset[disabled] .btn-vimeo.active{background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)} 89 | .btn-vimeo .badge{color:#1ab7ea;background-color:#fff} 90 | .btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,0.2)}.btn-vk:hover,.btn-vk:focus,.btn-vk:active,.btn-vk.active,.open .dropdown-toggle.btn-vk{color:#fff;background-color:#4a6a89;border-color:rgba(0,0,0,0.2)} 91 | .btn-vk:active,.btn-vk.active,.open .dropdown-toggle.btn-vk{background-image:none} 92 | .btn-vk.disabled,.btn-vk[disabled],fieldset[disabled] .btn-vk,.btn-vk.disabled:hover,.btn-vk[disabled]:hover,fieldset[disabled] .btn-vk:hover,.btn-vk.disabled:focus,.btn-vk[disabled]:focus,fieldset[disabled] .btn-vk:focus,.btn-vk.disabled:active,.btn-vk[disabled]:active,fieldset[disabled] .btn-vk:active,.btn-vk.disabled.active,.btn-vk[disabled].active,fieldset[disabled] .btn-vk.active{background-color:#587ea3;border-color:rgba(0,0,0,0.2)} 93 | .btn-vk .badge{color:#587ea3;background-color:#fff} 94 | .btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,0.2)}.btn-yahoo:hover,.btn-yahoo:focus,.btn-yahoo:active,.btn-yahoo.active,.open .dropdown-toggle.btn-yahoo{color:#fff;background-color:#570b79;border-color:rgba(0,0,0,0.2)} 95 | .btn-yahoo:active,.btn-yahoo.active,.open .dropdown-toggle.btn-yahoo{background-image:none} 96 | .btn-yahoo.disabled,.btn-yahoo[disabled],fieldset[disabled] .btn-yahoo,.btn-yahoo.disabled:hover,.btn-yahoo[disabled]:hover,fieldset[disabled] .btn-yahoo:hover,.btn-yahoo.disabled:focus,.btn-yahoo[disabled]:focus,fieldset[disabled] .btn-yahoo:focus,.btn-yahoo.disabled:active,.btn-yahoo[disabled]:active,fieldset[disabled] .btn-yahoo:active,.btn-yahoo.disabled.active,.btn-yahoo[disabled].active,fieldset[disabled] .btn-yahoo.active{background-color:#720e9e;border-color:rgba(0,0,0,0.2)} 97 | .btn-yahoo .badge{color:#720e9e;background-color:#fff} 98 | -------------------------------------------------------------------------------- /backend/appengine/static/font-awesome-4.2.0/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.2.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"} -------------------------------------------------------------------------------- /backend/appengine/static/bootstrap/css/bootstrap-theme.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"bootstrap-theme.css","sources":["less/theme.less","less/mixins/vendor-prefixes.less","bootstrap-theme.css","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAeA;;;;;;EAME,0CAAA;EC+CA,6FAAA;EACQ,qFAAA;EC5DT;AFiBC;;;;;;;;;;;;EC0CA,0DAAA;EACQ,kDAAA;EC7CT;AFqCC;;EAEE,wBAAA;EEnCH;AFwCD;EG/CI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EA+B2C,2BAAA;EAA2B,oBAAA;EE7BvE;AFAC;;EAEE,2BAAA;EACA,8BAAA;EEEH;AFCC;;EAEE,2BAAA;EACA,uBAAA;EECH;AFEC;;EAEE,2BAAA;EACA,wBAAA;EEAH;AFeD;EGhDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EE0BD;AFxBC;;EAEE,2BAAA;EACA,8BAAA;EE0BH;AFvBC;;EAEE,2BAAA;EACA,uBAAA;EEyBH;AFtBC;;EAEE,2BAAA;EACA,wBAAA;EEwBH;AFRD;EGjDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EEkDD;AFhDC;;EAEE,2BAAA;EACA,8BAAA;EEkDH;AF/CC;;EAEE,2BAAA;EACA,uBAAA;EEiDH;AF9CC;;EAEE,2BAAA;EACA,wBAAA;EEgDH;AF/BD;EGlDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EE0ED;AFxEC;;EAEE,2BAAA;EACA,8BAAA;EE0EH;AFvEC;;EAEE,2BAAA;EACA,uBAAA;EEyEH;AFtEC;;EAEE,2BAAA;EACA,wBAAA;EEwEH;AFtDD;EGnDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EEkGD;AFhGC;;EAEE,2BAAA;EACA,8BAAA;EEkGH;AF/FC;;EAEE,2BAAA;EACA,uBAAA;EEiGH;AF9FC;;EAEE,2BAAA;EACA,wBAAA;EEgGH;AF7ED;EGpDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EE0HD;AFxHC;;EAEE,2BAAA;EACA,8BAAA;EE0HH;AFvHC;;EAEE,2BAAA;EACA,uBAAA;EEyHH;AFtHC;;EAEE,2BAAA;EACA,wBAAA;EEwHH;AF7FD;;ECbE,oDAAA;EACQ,4CAAA;EC8GT;AFvFD;;EGvEI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHsEF,2BAAA;EE6FD;AF3FD;;;EG5EI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4EF,2BAAA;EEiGD;AFvFD;EG1FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EJ4GA,oBAAA;EC9CA,6FAAA;EACQ,qFAAA;EC4IT;AFlGD;EG1FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,0DAAA;EACQ,kDAAA;ECqJT;AF/FD;;EAEE,gDAAA;EEiGD;AF7FD;EG5GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EFgOD;AFrGD;EG5GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,yDAAA;EACQ,iDAAA;EC0KT;AF9GD;;EAWI,2CAAA;EEuGH;AFlGD;;;EAGE,kBAAA;EEoGD;AF1FD;EACE,+CAAA;EC3FA,4FAAA;EACQ,oFAAA;ECwLT;AFlFD;EGtJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EE8FD;AFzFD;EGvJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EEsGD;AFhGD;EGxJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EE8GD;AFvGD;EGzJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EEsHD;AFtGD;EGlKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED2QH;AFnGD;EG5KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDkRH;AFzGD;EG7KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDyRH;AF/GD;EG9KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDgSH;AFrHD;EG/KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDuSH;AF3HD;EGhLI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED8SH;AF9HD;EGnJI,+MAAA;EACA,0MAAA;EACA,uMAAA;EDoRH;AF1HD;EACE,oBAAA;EC/IA,oDAAA;EACQ,4CAAA;EC4QT;AF3HD;;;EAGE,+BAAA;EGpME,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHkMF,uBAAA;EEiID;AFvHD;ECjKE,mDAAA;EACQ,2CAAA;EC2RT;AFjHD;EG1NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED8UH;AFvHD;EG3NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDqVH;AF7HD;EG5NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED4VH;AFnID;EG7NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDmWH;AFzID;EG9NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED0WH;AF/ID;EG/NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDiXH;AF9ID;EGvOI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHqOF,uBAAA;EC1LA,2FAAA;EACQ,mFAAA;EC+UT","sourcesContent":["\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &:disabled,\n &[disabled] {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-bg, 5%); @end-color: darken(@navbar-default-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-bg; @end-color: lighten(@navbar-inverse-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n}\n\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n &::-moz-placeholder { color: @color; // Firefox\n opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n",null,"// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} -------------------------------------------------------------------------------- /backend/appengine/static/bootstrap/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn:active, 33 | .btn.active { 34 | background-image: none; 35 | } 36 | .btn-default { 37 | text-shadow: 0 1px 0 #fff; 38 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 39 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 40 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 41 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 42 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 43 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 44 | background-repeat: repeat-x; 45 | border-color: #dbdbdb; 46 | border-color: #ccc; 47 | } 48 | .btn-default:hover, 49 | .btn-default:focus { 50 | background-color: #e0e0e0; 51 | background-position: 0 -15px; 52 | } 53 | .btn-default:active, 54 | .btn-default.active { 55 | background-color: #e0e0e0; 56 | border-color: #dbdbdb; 57 | } 58 | .btn-default:disabled, 59 | .btn-default[disabled] { 60 | background-color: #e0e0e0; 61 | background-image: none; 62 | } 63 | .btn-primary { 64 | background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 65 | background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 66 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2)); 67 | background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); 68 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); 69 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 70 | background-repeat: repeat-x; 71 | border-color: #2b669a; 72 | } 73 | .btn-primary:hover, 74 | .btn-primary:focus { 75 | background-color: #2d6ca2; 76 | background-position: 0 -15px; 77 | } 78 | .btn-primary:active, 79 | .btn-primary.active { 80 | background-color: #2d6ca2; 81 | border-color: #2b669a; 82 | } 83 | .btn-primary:disabled, 84 | .btn-primary[disabled] { 85 | background-color: #2d6ca2; 86 | background-image: none; 87 | } 88 | .btn-success { 89 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 90 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 91 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 92 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 93 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 94 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 95 | background-repeat: repeat-x; 96 | border-color: #3e8f3e; 97 | } 98 | .btn-success:hover, 99 | .btn-success:focus { 100 | background-color: #419641; 101 | background-position: 0 -15px; 102 | } 103 | .btn-success:active, 104 | .btn-success.active { 105 | background-color: #419641; 106 | border-color: #3e8f3e; 107 | } 108 | .btn-success:disabled, 109 | .btn-success[disabled] { 110 | background-color: #419641; 111 | background-image: none; 112 | } 113 | .btn-info { 114 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 115 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 116 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 117 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 118 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 119 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 120 | background-repeat: repeat-x; 121 | border-color: #28a4c9; 122 | } 123 | .btn-info:hover, 124 | .btn-info:focus { 125 | background-color: #2aabd2; 126 | background-position: 0 -15px; 127 | } 128 | .btn-info:active, 129 | .btn-info.active { 130 | background-color: #2aabd2; 131 | border-color: #28a4c9; 132 | } 133 | .btn-info:disabled, 134 | .btn-info[disabled] { 135 | background-color: #2aabd2; 136 | background-image: none; 137 | } 138 | .btn-warning { 139 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 140 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 141 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 142 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 143 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 144 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 145 | background-repeat: repeat-x; 146 | border-color: #e38d13; 147 | } 148 | .btn-warning:hover, 149 | .btn-warning:focus { 150 | background-color: #eb9316; 151 | background-position: 0 -15px; 152 | } 153 | .btn-warning:active, 154 | .btn-warning.active { 155 | background-color: #eb9316; 156 | border-color: #e38d13; 157 | } 158 | .btn-warning:disabled, 159 | .btn-warning[disabled] { 160 | background-color: #eb9316; 161 | background-image: none; 162 | } 163 | .btn-danger { 164 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 165 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 166 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 167 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 168 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 169 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 170 | background-repeat: repeat-x; 171 | border-color: #b92c28; 172 | } 173 | .btn-danger:hover, 174 | .btn-danger:focus { 175 | background-color: #c12e2a; 176 | background-position: 0 -15px; 177 | } 178 | .btn-danger:active, 179 | .btn-danger.active { 180 | background-color: #c12e2a; 181 | border-color: #b92c28; 182 | } 183 | .btn-danger:disabled, 184 | .btn-danger[disabled] { 185 | background-color: #c12e2a; 186 | background-image: none; 187 | } 188 | .thumbnail, 189 | .img-thumbnail { 190 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 191 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 192 | } 193 | .dropdown-menu > li > a:hover, 194 | .dropdown-menu > li > a:focus { 195 | background-color: #e8e8e8; 196 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 197 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 198 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 199 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 200 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 201 | background-repeat: repeat-x; 202 | } 203 | .dropdown-menu > .active > a, 204 | .dropdown-menu > .active > a:hover, 205 | .dropdown-menu > .active > a:focus { 206 | background-color: #357ebd; 207 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 208 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); 209 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); 210 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 211 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 212 | background-repeat: repeat-x; 213 | } 214 | .navbar-default { 215 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 216 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 217 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 218 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 219 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 220 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 221 | background-repeat: repeat-x; 222 | border-radius: 4px; 223 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 224 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 225 | } 226 | .navbar-default .navbar-nav > .active > a { 227 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 228 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 229 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3)); 230 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); 231 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); 232 | background-repeat: repeat-x; 233 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 234 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 235 | } 236 | .navbar-brand, 237 | .navbar-nav > li > a { 238 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 239 | } 240 | .navbar-inverse { 241 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 242 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 243 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 244 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 245 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 246 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 247 | background-repeat: repeat-x; 248 | } 249 | .navbar-inverse .navbar-nav > .active > a { 250 | background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%); 251 | background-image: -o-linear-gradient(top, #222 0%, #282828 100%); 252 | background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828)); 253 | background-image: linear-gradient(to bottom, #222 0%, #282828 100%); 254 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); 255 | background-repeat: repeat-x; 256 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 257 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 258 | } 259 | .navbar-inverse .navbar-brand, 260 | .navbar-inverse .navbar-nav > li > a { 261 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 262 | } 263 | .navbar-static-top, 264 | .navbar-fixed-top, 265 | .navbar-fixed-bottom { 266 | border-radius: 0; 267 | } 268 | .alert { 269 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 270 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 271 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 272 | } 273 | .alert-success { 274 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 275 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 276 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 277 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 278 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 279 | background-repeat: repeat-x; 280 | border-color: #b2dba1; 281 | } 282 | .alert-info { 283 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 284 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 285 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 286 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 287 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 288 | background-repeat: repeat-x; 289 | border-color: #9acfea; 290 | } 291 | .alert-warning { 292 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 293 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 294 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 295 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 296 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 297 | background-repeat: repeat-x; 298 | border-color: #f5e79e; 299 | } 300 | .alert-danger { 301 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 302 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 303 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 304 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 305 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 306 | background-repeat: repeat-x; 307 | border-color: #dca7a7; 308 | } 309 | .progress { 310 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 311 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 312 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 313 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 314 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 315 | background-repeat: repeat-x; 316 | } 317 | .progress-bar { 318 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); 319 | background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%); 320 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9)); 321 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 322 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 323 | background-repeat: repeat-x; 324 | } 325 | .progress-bar-success { 326 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 327 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 328 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 329 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 330 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 331 | background-repeat: repeat-x; 332 | } 333 | .progress-bar-info { 334 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 335 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 336 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 337 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 338 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 339 | background-repeat: repeat-x; 340 | } 341 | .progress-bar-warning { 342 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 343 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 344 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 345 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 346 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 347 | background-repeat: repeat-x; 348 | } 349 | .progress-bar-danger { 350 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 351 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 352 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 353 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 354 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 355 | background-repeat: repeat-x; 356 | } 357 | .progress-bar-striped { 358 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 359 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 360 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 361 | } 362 | .list-group { 363 | border-radius: 4px; 364 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 365 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 366 | } 367 | .list-group-item.active, 368 | .list-group-item.active:hover, 369 | .list-group-item.active:focus { 370 | text-shadow: 0 -1px 0 #3071a9; 371 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); 372 | background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%); 373 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3)); 374 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); 375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); 376 | background-repeat: repeat-x; 377 | border-color: #3278b3; 378 | } 379 | .panel { 380 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 381 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 382 | } 383 | .panel-default > .panel-heading { 384 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 385 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 386 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 387 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 388 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 389 | background-repeat: repeat-x; 390 | } 391 | .panel-primary > .panel-heading { 392 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 393 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); 394 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); 395 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 396 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 397 | background-repeat: repeat-x; 398 | } 399 | .panel-success > .panel-heading { 400 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 401 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 403 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 405 | background-repeat: repeat-x; 406 | } 407 | .panel-info > .panel-heading { 408 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 409 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 410 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 411 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 412 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 413 | background-repeat: repeat-x; 414 | } 415 | .panel-warning > .panel-heading { 416 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 417 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 418 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 419 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 420 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 421 | background-repeat: repeat-x; 422 | } 423 | .panel-danger > .panel-heading { 424 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 425 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 426 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 427 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 428 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 429 | background-repeat: repeat-x; 430 | } 431 | .well { 432 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 433 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 435 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 437 | background-repeat: repeat-x; 438 | border-color: #dcdcdc; 439 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 440 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 441 | } 442 | /*# sourceMappingURL=bootstrap-theme.css.map */ 443 | --------------------------------------------------------------------------------