├── __init__.py ├── questions ├── __init__.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── dump_questions_json.py ├── migrations │ ├── __init__.py │ ├── 0002_auto__add_field_settings_syntax_highlighting.py │ ├── 0003_auto__add_field_group_added_by.py │ ├── 0004_auto__del_field_question_group.py │ └── 0001_initial.py ├── admin.py ├── forms.py ├── shortcuts.py ├── models.py └── views.py ├── coffee └── questions.coffee ├── clean.sh ├── README.md ├── static ├── img │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png ├── js │ ├── questions.js │ ├── tag-it.min.js │ └── bootstrap.min.js └── css │ ├── custom-theme │ ├── images │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_454545_256x240.png │ │ ├── ui-icons_888888_256x240.png │ │ ├── ui-icons_cd0a0a_256x240.png │ │ ├── ui-icons_f6cf3b_256x240.png │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ ├── ui-bg_glass_75_ffffff_1x400.png │ │ ├── ui-bg_inset-soft_95_fef1ec_1x100.png │ │ └── ui-bg_highlight-soft_75_cccccc_1x100.png │ └── jquery-ui-1.10.0.custom.css │ ├── questions.css │ ├── jquery.tagit.css │ ├── pygments.css │ └── bootstrap-responsive.min.css ├── templates ├── view_orphans.tmpl ├── 404.html ├── view_tag.tmpl ├── add_group.tmpl ├── edit_group.tmpl ├── edit_settings.tmpl ├── add_question.tmpl ├── edit_question.tmpl ├── index.tmpl ├── users.tmpl ├── login_failed.tmpl ├── view_revisions.tmpl ├── groups.tmpl ├── settings.tmpl ├── question_form.tmpl ├── view_group.tmpl ├── question_list.tmpl ├── base.tmpl ├── help.tmpl └── view_question.tmpl ├── manage.py ├── requirements.txt ├── .gitignore ├── auth.py ├── LICENSE ├── wsgi.py ├── urls.py └── settings.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /questions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /questions/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /questions/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /questions/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /coffee/questions.coffee: -------------------------------------------------------------------------------- 1 | window.questions = {} 2 | 3 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find . -type f -name '*.pyc' -exec rm {} \; 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | questions 2 | ========= 3 | 4 | Simple application for storing interview questions 5 | -------------------------------------------------------------------------------- /static/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /static/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /static/js/questions.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | (function() { 3 | 4 | window.questions = {}; 5 | 6 | }).call(this); 7 | -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-icons_f6cf3b_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-icons_f6cf3b_256x240.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-bg_glass_75_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-bg_glass_75_ffffff_1x400.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-bg_inset-soft_95_fef1ec_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-bg_inset-soft_95_fef1ec_1x100.png -------------------------------------------------------------------------------- /static/css/custom-theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/questions/HEAD/static/css/custom-theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /questions/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from questions import models 3 | 4 | admin.site.register(models.Group) 5 | admin.site.register(models.Question) 6 | admin.site.register(models.QuestionRevision) 7 | -------------------------------------------------------------------------------- /templates/view_orphans.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Orphans - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 | 7 | {% include "question_list.tmpl" %} 8 | 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.4.3 2 | Markdown==2.2.1 3 | Pygments==1.6 4 | South==0.7.6 5 | argparse==1.2.1 6 | django-markdown-deux==1.0.3 7 | django-openid-auth==0.4 8 | django-ratings==0.3.7 9 | django-tagging==0.3.1 10 | markdown2==2.1.0 11 | python-openid==2.2.5 12 | wsgiref==0.1.2 13 | -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} 404 - Page Not Found - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 | 7 |
 
8 |
9 | 404 - Page Not Found. 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/view_tag.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Tag - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 | 7 |

{{tagname}}

8 | 9 |
 
10 |

Questions

11 | {% include "question_list.tmpl" %} 12 | 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /static/css/questions.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding-top: 12px; 3 | } 4 | 5 | #id_title { 6 | width: 700px; 7 | } 8 | 9 | #id_body { 10 | width: 700px; 11 | height: 400px; 12 | } 13 | 14 | #id_comment { 15 | width: 700px; 16 | height: 50px; 17 | } 18 | 19 | ul.tagit { 20 | width: 700px; 21 | } 22 | -------------------------------------------------------------------------------- /templates/add_group.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Add Group - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 |
{% csrf_token %} 7 | 8 | {{form.as_table}} 9 |
10 | 11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /templates/edit_group.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Edit Group - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 |
{% csrf_token %} 7 | 8 | {{form.as_table}} 9 |
10 | 11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /templates/edit_settings.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Edit Settings - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 |
{% csrf_token %} 7 | 8 | {{form.as_table}} 9 |
10 | 11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /templates/add_question.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Add Question - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 | 7 | {% include "question_form.tmpl" %} 8 | 9 | {% endblock %} 10 | 11 | {% block extra_foot %} 12 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /templates/edit_question.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Edit Question - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 | 7 | {% include "question_form.tmpl" %} 8 | 9 | {% endblock %} 10 | 11 | {% block extra_foot %} 12 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | -------------------------------------------------------------------------------- /templates/index.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Index - {{ block.super }} {% endblock %} 4 | 5 | {% block nav-home %} class="active" {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 | Add Question 12 |
13 |
14 |
 
15 | 16 | {% include "question_list.tmpl" %} 17 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /templates/users.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Users - {{ block.super }} {% endblock %} 4 | {% block nav-users %} class="active" {% endblock %} 5 | 6 | {% block content %} 7 | 8 |
9 | 10 | 11 | 14 | {% for email, count in counts %} 15 | 16 | 17 | 18 | 19 | {% endfor %} 20 |
Email 12 | Questions Submitted 13 |
{{email}}{{count}}
21 |
22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/login_failed.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Login Failed - {{ block.super }} {% endblock %} 4 | 5 | {# Clear Navigation for login failure #} 6 | {% block navigation %} {% endblock %} 7 | 8 | {% block content %} 9 | 10 |
 
11 |
12 | Failure to login. This likely occured because you're logged into a Google Account not on one of the following domains: 13 | 18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /questions/forms.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm, ChoiceField, RadioSelect, Textarea, CharField 2 | 3 | from models import Question, QuestionRevision, Group, Settings 4 | 5 | 6 | class QuestionForm(ModelForm): 7 | 8 | body = CharField(widget=Textarea()) 9 | 10 | class Meta: 11 | model = Question 12 | exclude = ("status", "added_by") 13 | 14 | 15 | class GroupForm(ModelForm): 16 | class Meta: 17 | model = Group 18 | exclude = ("added_by", ) 19 | 20 | 21 | class SettingsForm(ModelForm): 22 | class Meta: 23 | model = Settings 24 | exclude = ("user",) 25 | -------------------------------------------------------------------------------- /questions/shortcuts.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render_to_response 2 | from django.template import RequestContext 3 | from django.http import HttpResponse 4 | 5 | import json 6 | 7 | def r2r(request, template, payload=None): 8 | if payload is None: 9 | payload = {} 10 | if not template.endswith(".tmpl"): 11 | template += ".tmpl" 12 | return render_to_response( 13 | template, payload, context_instance=RequestContext(request)) 14 | 15 | def choices(seq): 16 | return zip(seq, seq) 17 | 18 | def json_response(data): 19 | return HttpResponse(json.dumps(data), content_type="application/json") 20 | -------------------------------------------------------------------------------- /templates/view_revisions.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Revisions - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% for revision in revisions %} 14 | {% if revision.active %} 15 | 16 | {% else %} 17 | 18 | {% endif %} 19 | 20 | 21 | 22 | 23 | {% endfor %} 24 |
IDAdded ByAdded On
{{revision.id}}{{revision.user.email}}{{revision.date_added}}
25 | 26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /questions/management/commands/dump_questions_json.py: -------------------------------------------------------------------------------- 1 | 2 | import json 3 | from django.core.management.base import BaseCommand, CommandError 4 | from questions.models import Group, Question 5 | 6 | class Command(BaseCommand): 7 | help = 'Dumps Questions to JSON' 8 | 9 | def handle(self, *args, **options): 10 | data = {} 11 | 12 | groups = Group.objects.all() 13 | for group in groups: 14 | data[group.name] = [] 15 | 16 | for question in group.question_set.all(): 17 | if question.status != "Approved": 18 | continue 19 | data[group.name].append((question.title, question.tags.split(","))) 20 | 21 | print json.dumps(data) 22 | -------------------------------------------------------------------------------- /templates/groups.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Groups - {{ block.super }} {% endblock %} 4 | {% block nav-groups %} class="active" {% endblock %} 5 | 6 | 7 | {% block content %} 8 | 9 |
10 |
11 | Add Group 12 |
13 |
14 |
 
15 | 16 | 17 | 18 | 19 | 20 | 21 | {% for group in groups %} 22 | 23 | 24 | 25 | 26 | {% endfor %} 27 |
NameDescription
{{group.name}}{{group.description}}
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /auth.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.contrib.auth.models import User 3 | from django.core.mail import mail_admins 4 | from openid.consumer.consumer import SUCCESS 5 | from django_openid_auth.auth import OpenIDBackend 6 | 7 | 8 | class GoogleAuthBackend(OpenIDBackend): 9 | def authenticate(self, **kwargs): 10 | 11 | openid_response = kwargs.get('openid_response') 12 | 13 | if openid_response is None: 14 | return None 15 | 16 | if openid_response.status != SUCCESS: 17 | return None 18 | 19 | email = openid_response.getSigned('http://openid.net/srv/ax/1.0', 'value.email') 20 | domain = email.split("@", 1)[1] 21 | 22 | if domain not in getattr(settings, "OPENID_RESTRICT_TO_DOMAINS", tuple()): 23 | return None 24 | 25 | return OpenIDBackend.authenticate(self, **kwargs) 26 | -------------------------------------------------------------------------------- /templates/settings.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Settings - {{ block.super }} {% endblock %} 4 | {% block nav-settings %} class="active" {% endblock %} 5 | 6 | {% block content %} 7 | 8 |
9 |
10 | Edit Settings 11 |
12 |
13 |
 
14 |
15 | 16 | 17 | 20 | 21 | 22 | {% if settings.group.all %} 23 | 24 | {% else %} 25 | 26 | {% endif %} 27 | 28 | 29 | 30 | 31 | 32 |
Name 18 | Value 19 |
Group Filter:{{settings.group}}{{settings.group}}
Syntax Highlighting:{{settings.syntax_highlighting}}
33 |
34 | 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /templates/question_form.tmpl: -------------------------------------------------------------------------------- 1 |
{% csrf_token %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 |
{{ form.title.errors }}{{ form.title }}
{{ form.tags.errors }}{{ form.tags }}
{{ form.difficulty.errors }}{{ form.difficulty }}
{{ form.groups.errors }}{{ form.groups }}
{{ form.body.errors }}{{ form.body }} 22 | 23 |
26 | 27 |
28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Dropbox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for questions project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | 18 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") 19 | 20 | # This application object is used by any WSGI server configured to use this 21 | # file. This includes Django's development server, if the WSGI_APPLICATION 22 | # setting points here. 23 | from django.core.wsgi import get_wsgi_application 24 | application = get_wsgi_application() 25 | 26 | # Apply WSGI middleware here. 27 | # from helloworld.wsgi import HelloWorldApplication 28 | # application = HelloWorldApplication(application) 29 | -------------------------------------------------------------------------------- /templates/view_group.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %} Group - {{ block.super }} {% endblock %} 4 | 5 | {% block content %} 6 | 7 | {% if user.is_staff or user.email == group.added_by.email %} 8 |
9 |
10 | Edit Group 11 | Delete Group 12 | 13 | 26 |
27 |
28 |
 
29 | {% endif %} 30 | 31 |

{{group.name}}

32 |

{{group.description}}

33 | 34 |
 
35 |

Questions

36 | {% include "question_list.tmpl" %} 37 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /static/css/jquery.tagit.css: -------------------------------------------------------------------------------- 1 | ul.tagit { 2 | padding: 1px 5px; 3 | overflow: auto; 4 | margin-left: inherit; /* usually we don't want the regular ul margins. */ 5 | margin-right: inherit; 6 | } 7 | ul.tagit li { 8 | display: block; 9 | float: left; 10 | margin: 2px 5px 2px 0; 11 | } 12 | ul.tagit li.tagit-choice { 13 | position: relative; 14 | line-height: inherit; 15 | } 16 | 17 | ul.tagit li.tagit-choice-read-only { 18 | padding: .2em .5em .2em .5em; 19 | } 20 | 21 | ul.tagit li.tagit-choice-editable { 22 | padding: .2em 18px .2em .5em; 23 | } 24 | 25 | ul.tagit li.tagit-new { 26 | padding: .25em 4px .25em 0; 27 | } 28 | 29 | ul.tagit li.tagit-choice a.tagit-label { 30 | cursor: pointer; 31 | text-decoration: none; 32 | } 33 | ul.tagit li.tagit-choice .tagit-close { 34 | cursor: pointer; 35 | position: absolute; 36 | right: .1em; 37 | top: 50%; 38 | margin-top: -8px; 39 | line-height: 17px; 40 | } 41 | 42 | /* used for some custom themes that don't need image icons */ 43 | ul.tagit li.tagit-choice .tagit-close .text-icon { 44 | display: none; 45 | } 46 | 47 | ul.tagit li.tagit-choice input { 48 | display: block; 49 | float: left; 50 | margin: 2px 5px 2px 0; 51 | } 52 | ul.tagit input[type="text"] { 53 | -moz-box-sizing: border-box; 54 | -webkit-box-sizing: border-box; 55 | box-sizing: border-box; 56 | 57 | -moz-box-shadow: none; 58 | -webkit-box-shadow: none; 59 | box-shadow: none; 60 | 61 | border: none; 62 | margin: 0; 63 | padding: 0; 64 | width: inherit; 65 | background-color: inherit; 66 | outline: none; 67 | } 68 | -------------------------------------------------------------------------------- /templates/question_list.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% if not settings.group %} 5 | 6 | {% endif %} 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% for question in approved_questions %} 14 | 15 | 16 | {% if not settings.group %} 17 | 18 | {% endif %} 19 | 20 | 25 | 26 | {% endfor %} 27 | 28 | 29 | 30 | 31 | {% for question in pending_questions %} 32 | 33 | 34 | {% if not settings.group %} 35 | 36 | {% endif %} 37 | 38 | 43 | 44 | {% endfor %} 45 | 46 | 47 | 48 | 49 | {% for question in banned_questions %} 50 | 51 | 52 | {% if not settings.group %} 53 | 54 | {% endif %} 55 | 56 | 61 | 62 | {% endfor %} 63 |
TitleGroupsDifficultyTags
Approved
{{question.title|truncatechars:80}}{{question.groups.all|join:", "}}{{question.difficulty}} 21 | {% for tag in question.tag_list %} 22 | {{tag}} 23 | {% endfor %} 24 |
Pending
{{question.title|truncatechars:80}}{{question.groups.all|join:", "}}{{question.difficulty}} 39 | {% for tag in question.tag_list %} 40 | {{tag}} 41 | {% endfor %} 42 |
Banned
{{question.title|truncatechars:80}}{{question.groups.all|join:", "}}{{question.difficulty}} 57 | {% for tag in question.tag_list %} 58 | {{tag}} 59 | {% endfor %} 60 |
64 | -------------------------------------------------------------------------------- /templates/base.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block title %} Questions {% endblock %} 5 | 6 | 7 | 8 | 9 | {% block extra_head %} {% endblock %} 10 | 11 | 12 | {% block navigation %} 13 |
14 |
15 |
16 | 31 |
32 |
33 | {% endblock %} 34 |
35 |
36 | {% block content %} {% endblock %} 37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | {% block extra_foot %} {% endblock %} 46 | 47 | 48 | -------------------------------------------------------------------------------- /urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns 3 | 4 | from django.contrib import admin 5 | admin.autodiscover() 6 | 7 | urlpatterns = patterns('', 8 | url(r'^$', 'questions.views.index'), 9 | url(r'^questions/$', 'questions.views.index'), 10 | url(r'^questions/orphans$', 'questions.views.view_orphans'), 11 | 12 | url(r'^question/add/$', 'questions.views.add_question'), 13 | url(r'^question/(?P\d+)$', 'questions.views.view_question'), 14 | url(r'^question/(?P\d+)/edit$', 'questions.views.edit_question'), 15 | url(r'^question/(?P\d+)/delete$', 'questions.views.delete_question'), 16 | url(r'^question/(?P\d+)/revisions$', 'questions.views.view_revisions'), 17 | url(r'^question/(?P\d+)/status/(?P(Banned|Pending|Approved))$', 18 | 'questions.views.set_status'), 19 | url(r'^question/(?P\d+)/set_revision/(?P\d+)$', 20 | 'questions.views.set_revision'), 21 | 22 | url(r'^tag/(?P[\w ]+)$', 'questions.views.view_tag'), 23 | url(r'^tags_ajax$', 'questions.views.tags_ajax'), 24 | 25 | 26 | url(r'users', 'questions.views.users'), 27 | 28 | url(r'help', 'questions.views.help'), 29 | 30 | url(r'^group/add/$', 'questions.views.add_group'), 31 | url(r'^groups/$', 'questions.views.groups'), 32 | url(r'^group/(?P\d+)$', 'questions.views.view_group'), 33 | url(r'^group/(?P\d+)/edit$', 'questions.views.edit_group'), 34 | url(r'^group/(?P\d+)/delete$', 'questions.views.delete_group'), 35 | 36 | url(r'^settings/$', 'questions.views.settings'), 37 | url(r'^settings/edit$', 'questions.views.edit_settings'), 38 | 39 | url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/',}, name='logout'), 40 | (r'^openid/', include('django_openid_auth.urls')), 41 | (r'^comments/', include('django.contrib.comments.urls')), 42 | url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 43 | url(r'^admin/', include(admin.site.urls)), 44 | ) 45 | 46 | urlpatterns += staticfiles_urlpatterns() 47 | -------------------------------------------------------------------------------- /questions/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | from djangoratings.fields import RatingField 4 | from tagging.fields import TagField 5 | import tagging 6 | 7 | from shortcuts import choices 8 | 9 | class Group(models.Model): 10 | name = models.CharField(max_length=60, unique=True) 11 | description = models.TextField(blank=True, null=True) 12 | added_by = models.ForeignKey(User, blank=True, null=True) 13 | 14 | def __unicode__(self): 15 | return self.name 16 | 17 | 18 | class Settings(models.Model): 19 | user = models.ForeignKey(User) 20 | 21 | group = models.ForeignKey(Group, blank=True, null=True) 22 | syntax_highlighting = models.BooleanField(default=True) 23 | 24 | def __unicode__(self): 25 | return self.user.email 26 | 27 | @classmethod 28 | def get_by_user(cls, user): 29 | try: 30 | settings = cls.objects.get(user_id=user.id) 31 | except cls.DoesNotExist: 32 | settings = cls(user=user) 33 | settings.save() 34 | return settings 35 | 36 | 37 | 38 | class Question(models.Model): 39 | title = models.CharField(max_length=255) 40 | 41 | status_choices = choices(('Approved', 'Pending', 'Banned')) 42 | status = models.CharField(max_length=30, choices=status_choices, default="Pending") 43 | 44 | tags = TagField(blank=True, null=True) 45 | 46 | difficulty_choices = choices(("Softball", "Easy", "Medium", "Hard")) 47 | difficulty = models.CharField(max_length=20, choices=difficulty_choices, default="Easy") 48 | 49 | groups = models.ManyToManyField(Group) 50 | 51 | date_added = models.DateTimeField(auto_now_add=True) 52 | added_by = models.ForeignKey(User) 53 | 54 | def get_latest_revision(self): 55 | return QuestionRevision.objects.filter(question_id=self.id).latest('date_added') 56 | 57 | def get_active_revision(self): 58 | return QuestionRevision.objects.get(question_id=self.id, active=True) 59 | 60 | tagging.register(Question, tag_descriptor_attr="tag_list") 61 | 62 | 63 | class QuestionRevision(models.Model): 64 | question = models.ForeignKey(Question) 65 | user = models.ForeignKey(User) 66 | text = models.TextField() 67 | active = models.BooleanField() 68 | date_added = models.DateTimeField(auto_now_add=True) 69 | -------------------------------------------------------------------------------- /templates/help.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | {% load markdown_deux_tags %} 3 | 4 | {% block title %} Help - {{ block.super }} {% endblock %} 5 | {% block nav-help %} class="active" {% endblock %} 6 | 7 | 8 | {% block content %} 9 | 10 |
11 |

Markdown Cheatsheet

12 | 13 |

Phrase Emphasis

14 | 15 |
*italic*   **bold**
16 | 17 |

Links

18 | 19 |

Inline:

20 | 21 |
A [link](http://url.com/)
 22 | A [link](http://url.com/
 23 | "with a title")
 24 | 
25 | 26 |

Reference-style labels:

27 | 28 |
A [link][id]. Then, anywhere
 29 | else, define the link:
 30 | 
 31 | [id]: http://url.com/ "title"
 32 | 
33 | 34 |

Code Spans

35 | 36 |
`<code>` spans are delimited
 37 | by backticks.
 38 | 
 39 | You can include literal
 40 | backticks like `` `this` ``.
 41 | 
42 | 43 |

Fenced Code Blocks

44 |

Fencing with ``` on a line before and after based on GFM.

45 | 46 |
```
 47 | def foo():
 48 |     return "bar"
 49 | ```
50 | 51 |

Syntax highlighting is supported in the following format

52 |
```python
 53 | def foo():
 54 |     return "bar"
 55 | ```
56 | 57 | 58 |

Preformatted Code Blocks

59 |

Indent every line of a code block by at least 4 spaces.

60 | 61 |
This is a normal paragraph.
 62 | 
 63 |     This is a preformatted
 64 |     code block.
 65 | 
66 | 67 |

Lists

68 | 69 |
1. an ordered
 70 | 2. list
 71 | 
 72 | * an unordered list
 73 | * '-' bullets work too
 74 | 
75 | 76 |

Headers

77 | 78 |
# Header 1
 79 | 
 80 | ## Header 2
 81 | 
 82 | ###### Header 6
 83 | 
84 | 85 |

Blockquotes

86 | 87 |
> Email-style angle brackets
 88 | > are used for blockquotes.
 89 | 
 90 | > > And, they can be nested.
 91 | 
 92 | > ## Headers in blockquotes
 93 | > 
 94 | > * You can quote a list.
 95 | > * Etc.
 96 | 
97 | 98 |

Manual Line Breaks

99 | 100 |

End a line with two or more spaces:

101 | 102 |
Roses are red,   
103 | Violets are blue.
104 | 
105 | 106 |

More markdown syntax help here.

107 | 108 |
109 | 110 | {% endblock %} 111 | -------------------------------------------------------------------------------- /static/css/pygments.css: -------------------------------------------------------------------------------- 1 | .hll { background-color: #ffffcc } 2 | .c { color: #408080; font-style: italic } /* Comment */ 3 | .err { border: 1px solid #FF0000 } /* Error */ 4 | .k { color: #008000; font-weight: bold } /* Keyword */ 5 | .o { color: #666666 } /* Operator */ 6 | .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 7 | .cp { color: #BC7A00 } /* Comment.Preproc */ 8 | .c1 { color: #408080; font-style: italic } /* Comment.Single */ 9 | .cs { color: #408080; font-style: italic } /* Comment.Special */ 10 | .gd { color: #A00000 } /* Generic.Deleted */ 11 | .ge { font-style: italic } /* Generic.Emph */ 12 | .gr { color: #FF0000 } /* Generic.Error */ 13 | .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 14 | .gi { color: #00A000 } /* Generic.Inserted */ 15 | .go { color: #888888 } /* Generic.Output */ 16 | .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 17 | .gs { font-weight: bold } /* Generic.Strong */ 18 | .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 19 | .gt { color: #0044DD } /* Generic.Traceback */ 20 | .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 21 | .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 22 | .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 23 | .kp { color: #008000 } /* Keyword.Pseudo */ 24 | .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 25 | .kt { color: #B00040 } /* Keyword.Type */ 26 | .m { color: #666666 } /* Literal.Number */ 27 | .s { color: #BA2121 } /* Literal.String */ 28 | .na { color: #7D9029 } /* Name.Attribute */ 29 | .nb { color: #008000 } /* Name.Builtin */ 30 | .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 31 | .no { color: #880000 } /* Name.Constant */ 32 | .nd { color: #AA22FF } /* Name.Decorator */ 33 | .ni { color: #999999; font-weight: bold } /* Name.Entity */ 34 | .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 35 | .nf { color: #0000FF } /* Name.Function */ 36 | .nl { color: #A0A000 } /* Name.Label */ 37 | .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 38 | .nt { color: #008000; font-weight: bold } /* Name.Tag */ 39 | .nv { color: #19177C } /* Name.Variable */ 40 | .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 41 | .w { color: #bbbbbb } /* Text.Whitespace */ 42 | .mf { color: #666666 } /* Literal.Number.Float */ 43 | .mh { color: #666666 } /* Literal.Number.Hex */ 44 | .mi { color: #666666 } /* Literal.Number.Integer */ 45 | .mo { color: #666666 } /* Literal.Number.Oct */ 46 | .sb { color: #BA2121 } /* Literal.String.Backtick */ 47 | .sc { color: #BA2121 } /* Literal.String.Char */ 48 | .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 49 | .s2 { color: #BA2121 } /* Literal.String.Double */ 50 | .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 51 | .sh { color: #BA2121 } /* Literal.String.Heredoc */ 52 | .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 53 | .sx { color: #008000 } /* Literal.String.Other */ 54 | .sr { color: #BB6688 } /* Literal.String.Regex */ 55 | .s1 { color: #BA2121 } /* Literal.String.Single */ 56 | .ss { color: #19177C } /* Literal.String.Symbol */ 57 | .bp { color: #008000 } /* Name.Builtin.Pseudo */ 58 | .vc { color: #19177C } /* Name.Variable.Class */ 59 | .vg { color: #19177C } /* Name.Variable.Global */ 60 | .vi { color: #19177C } /* Name.Variable.Instance */ 61 | .il { color: #666666 } /* Literal.Number.Integer.Long */ 62 | 63 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | SITE_ROOT = os.path.dirname(os.path.realpath(__file__)) 4 | 5 | DEBUG = False 6 | TEMPLATE_DEBUG = DEBUG 7 | 8 | ADMINS = ( 9 | # ('Name', 'email@address.foo'), 10 | ) 11 | MANAGERS = ADMINS 12 | 13 | DATABASES = { 14 | 'default': { 15 | # 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 16 | 'ENGINE': 'django.db.backends.sqlite3', 17 | 'NAME': os.path.join(SITE_ROOT, 'devdb.sqlite'), 18 | 'USER': '', 19 | 'PASSWORD': '', 20 | 'HOST': '', 21 | 'PORT': '', 22 | } 23 | } 24 | 25 | TIME_ZONE = 'America/Los_Angeles' 26 | LANGUAGE_CODE = 'en-us' 27 | USE_I18N = True 28 | USE_L10N = True 29 | USE_TZ = True 30 | SITE_ID = 1 31 | 32 | 33 | STATIC_URL = '/static/' 34 | STATICFILES_DIRS = ( 35 | os.path.join(SITE_ROOT, "static"), 36 | ) 37 | STATICFILES_FINDERS = ( 38 | 'django.contrib.staticfiles.finders.FileSystemFinder', 39 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 40 | ) 41 | 42 | #SECRET_KEY = '' 43 | 44 | TEMPLATE_LOADERS = ( 45 | 'django.template.loaders.filesystem.Loader', 46 | 'django.template.loaders.app_directories.Loader', 47 | ) 48 | 49 | MIDDLEWARE_CLASSES = ( 50 | 'django.middleware.common.CommonMiddleware', 51 | 'django.contrib.sessions.middleware.SessionMiddleware', 52 | 'django.middleware.csrf.CsrfViewMiddleware', 53 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 54 | 'django.contrib.messages.middleware.MessageMiddleware', 55 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 56 | ) 57 | 58 | ROOT_URLCONF = 'urls' 59 | WSGI_APPLICATION = 'wsgi.application' 60 | 61 | TEMPLATE_DIRS = ( 62 | os.path.join(SITE_ROOT, 'templates'), 63 | ) 64 | 65 | INSTALLED_APPS = ( 66 | 'django.contrib.auth', 67 | 'django_openid_auth', 68 | 'django.contrib.contenttypes', 69 | 'django.contrib.sessions', 70 | 'django.contrib.messages', 71 | 'django.contrib.staticfiles', 72 | 'django.contrib.admin', 73 | 'django.contrib.admindocs', 74 | 'django.contrib.sites', 75 | 'django.contrib.comments', 76 | 'djangoratings', 77 | 'tagging', 78 | 'markdown_deux', 79 | 'questions', 80 | 'south', 81 | ) 82 | 83 | AUTHENTICATION_BACKENDS = ( 84 | 'auth.GoogleAuthBackend', 85 | 'django.contrib.auth.backends.ModelBackend', 86 | ) 87 | 88 | LOGIN_URL = '/openid/login/' 89 | LOGIN_REDIRECT_URL = '/' 90 | LOGOUT_URL = '/openid/logout/' 91 | 92 | from questions.views import failure_handler 93 | OPENID_RENDER_FAILURE = failure_handler 94 | OPENID_SSO_SERVER_URL = 'https://www.google.com/accounts/o8/id' 95 | OPENID_CREATE_USERS = True 96 | #OPENID_RESTRICT_TO_DOMAINS = ('domain.com', ) 97 | 98 | 99 | MARKDOWN_DEUX_STYLES = { 100 | "default": { 101 | "extras": ["code-friendly", "fenced-code-blocks"], 102 | "safe_mode": "escape", 103 | }, 104 | } 105 | 106 | 107 | # A sample logging configuration. The only tangible logging 108 | # performed by this configuration is to send an email to 109 | # the site admins on every HTTP 500 error when DEBUG=False. 110 | # See http://docs.djangoproject.com/en/dev/topics/logging for 111 | # more details on how to customize your logging configuration. 112 | LOGGING = { 113 | 'version': 1, 114 | 'disable_existing_loggers': False, 115 | 'filters': { 116 | 'require_debug_false': { 117 | '()': 'django.utils.log.RequireDebugFalse' 118 | } 119 | }, 120 | 'handlers': { 121 | 'mail_admins': { 122 | 'level': 'ERROR', 123 | 'filters': ['require_debug_false'], 124 | 'class': 'django.utils.log.AdminEmailHandler' 125 | } 126 | }, 127 | 'loggers': { 128 | 'django.request': { 129 | 'handlers': ['mail_admins'], 130 | 'level': 'ERROR', 131 | 'propagate': True, 132 | }, 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /templates/view_question.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | {% load comments %} 3 | {% load markdown_deux_tags %} 4 | 5 | {% block title %} Question - {{ block.super }} {% endblock %} 6 | 7 | {% block extra_head %} 8 | {% if settings.syntax_highlighting %} 9 | 10 | {% endif %} 11 | {% endblock %} 12 | 13 | {% block content %} 14 | 15 |
16 |
17 |
18 | {% if question.status == "Pending" %} 19 | 20 | {% else %} 21 | 22 | {% endif %} 23 | 24 | {% if question.status == "Approved" %} 25 | 26 | {% else %} 27 | 28 | {% endif %} 29 | 30 | {% if question.status == "Banned" %} 31 | 32 | {% else %} 33 | 34 | {% endif %} 35 |
36 |
37 |
38 | {% if not active_revision %} 39 | Mark Active 40 | {% endif %} 41 | View Revisions 42 | Edit Question 43 | {% if user.is_staff or user.email == question.added_by.email %} 44 | Delete Question 45 | 46 | 59 | {% endif %} 60 | 61 |
62 |
63 | 64 | {% if not active_revision or question.status != "Active" %} 65 |
 
66 | {% endif %} 67 | {% if not active_revision %} 68 |
69 | You are currently viewing a non-active revision. 70 |
71 | {% endif %} 72 | {% if question.status == "Pending" %} 73 |
74 | This question is pending. If used in an interview discuss its signal before approving. 75 |
76 | {% endif %} 77 | {% if question.status == "Banned" %} 78 |
79 | This question is banned. Don't use it in future interviews. 80 |
81 | {% endif %} 82 | 83 |

{{question.title}}

84 |
85 |
Difficulty
86 |
{{question.difficulty}}
87 |
Status
88 |
{{question.status}}
89 |
Groups
90 |
{{question.groups.all|join:", "}}
91 |
92 |
93 |
Added By
94 |
{{question.added_by.email}}
95 |
On
96 |
{{question.date_added}}
97 |
98 |
99 |
Modified By
100 |
{{revision.user.email}}
101 |
On
102 |
{{revision.date_added}}
103 |
104 |
105 |
Tags
106 |
107 | {% for tag in question.tag_list %}{{tag}} {% endfor %} 108 |
109 |
110 |
 
111 |
112 |
113 | {{revision.text|markdown}} 114 |
115 |
116 | 117 |
 
118 | 119 |
120 | {% get_comment_form for question as form %} 121 |
122 | {% csrf_token %} 123 | {{ form.comment }} 124 | {{ form.honeypot }} 125 | {{ form.content_type }} 126 | {{ form.object_pk }} 127 | {{ form.timestamp }} 128 | {{ form.security_hash }} 129 | 130 | 131 |
132 |
133 | 134 | {% get_comment_list for question as comment_list %} 135 | {% for comment in comment_list %} 136 | 137 |
138 |
{{comment.submit_date}} - {{comment.user_email}} says:
139 |
140 |
141 |
{{comment.comment}}
142 |
 
143 |
144 | 145 | {% endfor %} 146 | 147 | {% endblock %} 148 | -------------------------------------------------------------------------------- /questions/migrations/0002_auto__add_field_settings_syntax_highlighting.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | # Adding field 'Settings.syntax_highlighting' 12 | db.add_column('questions_settings', 'syntax_highlighting', 13 | self.gf('django.db.models.fields.BooleanField')(default=True), 14 | keep_default=False) 15 | 16 | 17 | def backwards(self, orm): 18 | # Deleting field 'Settings.syntax_highlighting' 19 | db.delete_column('questions_settings', 'syntax_highlighting') 20 | 21 | 22 | models = { 23 | 'auth.group': { 24 | 'Meta': {'object_name': 'Group'}, 25 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 26 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 27 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 28 | }, 29 | 'auth.permission': { 30 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 31 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 32 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 33 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 34 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 35 | }, 36 | 'auth.user': { 37 | 'Meta': {'object_name': 'User'}, 38 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 39 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 40 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 41 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 42 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 43 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 44 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 45 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 46 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 47 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 48 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 49 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 50 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 51 | }, 52 | 'contenttypes.contenttype': { 53 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 54 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 55 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 56 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 57 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 58 | }, 59 | 'questions.group': { 60 | 'Meta': {'object_name': 'Group'}, 61 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 62 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 63 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '60'}) 64 | }, 65 | 'questions.question': { 66 | 'Meta': {'object_name': 'Question'}, 67 | 'added_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), 68 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 69 | 'difficulty': ('django.db.models.fields.CharField', [], {'default': "'Easy'", 'max_length': '20'}), 70 | 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Group']"}), 71 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 72 | 'status': ('django.db.models.fields.CharField', [], {'default': "'Pending'", 'max_length': '30'}), 73 | 'tags': ('tagging.fields.TagField', [], {'null': 'True'}), 74 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) 75 | }, 76 | 'questions.questionrevision': { 77 | 'Meta': {'object_name': 'QuestionRevision'}, 78 | 'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 79 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 80 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 81 | 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Question']"}), 82 | 'text': ('django.db.models.fields.TextField', [], {}), 83 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 84 | }, 85 | 'questions.settings': { 86 | 'Meta': {'object_name': 'Settings'}, 87 | 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Group']", 'null': 'True', 'blank': 'True'}), 88 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 89 | 'syntax_highlighting': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 90 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 91 | } 92 | } 93 | 94 | complete_apps = ['questions'] -------------------------------------------------------------------------------- /questions/migrations/0003_auto__add_field_group_added_by.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | # Adding field 'Group.added_by' 12 | db.add_column('questions_group', 'added_by', 13 | self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True), 14 | keep_default=False) 15 | 16 | 17 | def backwards(self, orm): 18 | # Deleting field 'Group.added_by' 19 | db.delete_column('questions_group', 'added_by_id') 20 | 21 | 22 | models = { 23 | 'auth.group': { 24 | 'Meta': {'object_name': 'Group'}, 25 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 26 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 27 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 28 | }, 29 | 'auth.permission': { 30 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 31 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 32 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 33 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 34 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 35 | }, 36 | 'auth.user': { 37 | 'Meta': {'object_name': 'User'}, 38 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 39 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 40 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 41 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 42 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 43 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 44 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 45 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 46 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 47 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 48 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 49 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 50 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 51 | }, 52 | 'contenttypes.contenttype': { 53 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 54 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 55 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 56 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 57 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 58 | }, 59 | 'questions.group': { 60 | 'Meta': {'object_name': 'Group'}, 61 | 'added_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), 62 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 63 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 64 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '60'}) 65 | }, 66 | 'questions.question': { 67 | 'Meta': {'object_name': 'Question'}, 68 | 'added_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), 69 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 70 | 'difficulty': ('django.db.models.fields.CharField', [], {'default': "'Easy'", 'max_length': '20'}), 71 | 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Group']"}), 72 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 73 | 'status': ('django.db.models.fields.CharField', [], {'default': "'Pending'", 'max_length': '30'}), 74 | 'tags': ('tagging.fields.TagField', [], {'null': 'True'}), 75 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) 76 | }, 77 | 'questions.questionrevision': { 78 | 'Meta': {'object_name': 'QuestionRevision'}, 79 | 'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 80 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 81 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 82 | 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Question']"}), 83 | 'text': ('django.db.models.fields.TextField', [], {}), 84 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 85 | }, 86 | 'questions.settings': { 87 | 'Meta': {'object_name': 'Settings'}, 88 | 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Group']", 'null': 'True', 'blank': 'True'}), 89 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 90 | 'syntax_highlighting': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 91 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 92 | } 93 | } 94 | 95 | complete_apps = ['questions'] -------------------------------------------------------------------------------- /questions/migrations/0004_auto__del_field_question_group.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | # Deleting field 'Question.group' 12 | db.delete_column('questions_question', 'group_id') 13 | 14 | # Adding M2M table for field groups on 'Question' 15 | db.create_table('questions_question_groups', ( 16 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), 17 | ('question', models.ForeignKey(orm['questions.question'], null=False)), 18 | ('group', models.ForeignKey(orm['questions.group'], null=False)) 19 | )) 20 | db.create_unique('questions_question_groups', ['question_id', 'group_id']) 21 | 22 | 23 | def backwards(self, orm): 24 | 25 | # User chose to not deal with backwards NULL issues for 'Question.group' 26 | raise RuntimeError("Cannot reverse this migration. 'Question.group' and its values cannot be restored.") 27 | # Removing M2M table for field groups on 'Question' 28 | db.delete_table('questions_question_groups') 29 | 30 | 31 | models = { 32 | 'auth.group': { 33 | 'Meta': {'object_name': 'Group'}, 34 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 35 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 36 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 37 | }, 38 | 'auth.permission': { 39 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 40 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 41 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 42 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 43 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 44 | }, 45 | 'auth.user': { 46 | 'Meta': {'object_name': 'User'}, 47 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 48 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 49 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 50 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 51 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 52 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 53 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 54 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 55 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 56 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 57 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 58 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 59 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 60 | }, 61 | 'contenttypes.contenttype': { 62 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 63 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 64 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 65 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 66 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 67 | }, 68 | 'questions.group': { 69 | 'Meta': {'object_name': 'Group'}, 70 | 'added_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), 71 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 72 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 73 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '60'}) 74 | }, 75 | 'questions.question': { 76 | 'Meta': {'object_name': 'Question'}, 77 | 'added_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), 78 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 79 | 'difficulty': ('django.db.models.fields.CharField', [], {'default': "'Easy'", 'max_length': '20'}), 80 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['questions.Group']", 'symmetrical': 'False'}), 81 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 82 | 'status': ('django.db.models.fields.CharField', [], {'default': "'Pending'", 'max_length': '30'}), 83 | 'tags': ('tagging.fields.TagField', [], {'null': 'True'}), 84 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) 85 | }, 86 | 'questions.questionrevision': { 87 | 'Meta': {'object_name': 'QuestionRevision'}, 88 | 'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 89 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 90 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 91 | 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Question']"}), 92 | 'text': ('django.db.models.fields.TextField', [], {}), 93 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 94 | }, 95 | 'questions.settings': { 96 | 'Meta': {'object_name': 'Settings'}, 97 | 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Group']", 'null': 'True', 'blank': 'True'}), 98 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 99 | 'syntax_highlighting': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 100 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 101 | } 102 | } 103 | 104 | complete_apps = ['questions'] -------------------------------------------------------------------------------- /static/js/tag-it.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.widget("ui.tagit",{options:{allowDuplicates:!1,caseSensitive:!0,fieldName:"tags",placeholderText:null,readOnly:!1,removeConfirmation:!1,tagLimit:null,availableTags:[],autocomplete:{},showAutocompleteOnFocus:!1,allowSpaces:!1,singleField:!1,singleFieldDelimiter:",",singleFieldNode:null,animate:!0,tabIndex:null,beforeTagAdded:null,afterTagAdded:null,beforeTagRemoved:null,afterTagRemoved:null,onTagClicked:null,onTagLimitExceeded:null,onTagAdded:null,onTagRemoved:null,tagSource:null},_create:function(){var a= 2 | this;this.element.is("input")?(this.tagList=b("
    ").insertAfter(this.element),this.options.singleField=!0,this.options.singleFieldNode=this.element,this.element.css("display","none")):this.tagList=this.element.find("ul, ol").andSelf().last();this.tagInput=b('').addClass("ui-widget-content");this.options.readOnly&&this.tagInput.attr("disabled","disabled");this.options.tabIndex&&this.tagInput.attr("tabindex",this.options.tabIndex);this.options.placeholderText&&this.tagInput.attr("placeholder", 3 | this.options.placeholderText);this.options.autocomplete.source||(this.options.autocomplete.source=function(a,c){var d=a.term.toLowerCase(),e=b.grep(this.options.availableTags,function(a){return 0===a.toLowerCase().indexOf(d)});c(this._subtractArray(e,this.assignedTags()))});this.options.showAutocompleteOnFocus&&(this.tagInput.focus(function(){a._showAutocomplete()}),"undefined"===typeof this.options.autocomplete.minLength&&(this.options.autocomplete.minLength=0));b.isFunction(this.options.autocomplete.source)&& 4 | (this.options.autocomplete.source=b.proxy(this.options.autocomplete.source,this));b.isFunction(this.options.tagSource)&&(this.options.tagSource=b.proxy(this.options.tagSource,this));this.tagList.addClass("tagit").addClass("ui-widget ui-widget-content ui-corner-all").append(b('
  • ').append(this.tagInput)).click(function(c){var d=b(c.target);d.hasClass("tagit-label")?(d=d.closest(".tagit-choice"),d.hasClass("removed")||a._trigger("onTagClicked",c,{tag:d,tagLabel:a.tagLabel(d)})): 5 | a.tagInput.focus()});var d=!1;if(this.options.singleField)if(this.options.singleFieldNode){var c=b(this.options.singleFieldNode),e=c.val().split(this.options.singleFieldDelimiter);c.val("");b.each(e,function(b,c){a.createTag(c,null,!0);d=!0})}else this.options.singleFieldNode=b(''),this.tagList.after(this.options.singleFieldNode);d||this.tagList.children("li").each(function(){b(this).hasClass("tagit-new")||(a.createTag(b(this).text(), 6 | b(this).attr("class"),!0),b(this).remove())});this.tagInput.keydown(function(c){if(c.which==b.ui.keyCode.BACKSPACE&&""===a.tagInput.val()){var d=a._lastTag();!a.options.removeConfirmation||d.hasClass("remove")?a.removeTag(d):a.options.removeConfirmation&&d.addClass("remove ui-state-highlight")}else a.options.removeConfirmation&&a._lastTag().removeClass("remove ui-state-highlight");if(c.which===b.ui.keyCode.COMMA||c.which===b.ui.keyCode.ENTER||c.which==b.ui.keyCode.TAB&&""!==a.tagInput.val()||c.which== 7 | b.ui.keyCode.SPACE&&!0!==a.options.allowSpaces&&('"'!=b.trim(a.tagInput.val()).replace(/^s*/,"").charAt(0)||'"'==b.trim(a.tagInput.val()).charAt(0)&&'"'==b.trim(a.tagInput.val()).charAt(b.trim(a.tagInput.val()).length-1)&&0!==b.trim(a.tagInput.val()).length-1))c.which===b.ui.keyCode.ENTER&&""===a.tagInput.val()||c.preventDefault(),a.createTag(a._cleanedInput()),a.tagInput.autocomplete("close")}).blur(function(){a.tagInput.data("autocomplete-open")||a.createTag(a._cleanedInput())});if(this.options.availableTags|| 8 | this.options.tagSource||this.options.autocomplete.source)c={select:function(b,c){a.createTag(c.item.value);return!1}},b.extend(c,this.options.autocomplete),c.source=this.options.tagSource||c.source,this.tagInput.autocomplete(c).bind("autocompleteopen",function(){a.tagInput.data("autocomplete-open",!0)}).bind("autocompleteclose",function(){a.tagInput.data("autocomplete-open",!1)})},_cleanedInput:function(){return b.trim(this.tagInput.val().replace(/^"(.*)"$/,"$1"))},_lastTag:function(){return this.tagList.find(".tagit-choice:last:not(.removed)")}, 9 | _tags:function(){return this.tagList.find(".tagit-choice:not(.removed)")},assignedTags:function(){var a=this,d=[];this.options.singleField?(d=b(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter),""===d[0]&&(d=[])):this._tags().each(function(){d.push(a.tagLabel(this))});return d},_updateSingleTagsField:function(a){b(this.options.singleFieldNode).val(a.join(this.options.singleFieldDelimiter)).trigger("change")},_subtractArray:function(a,d){for(var c=[],e=0;e=this.options.tagLimit)return this._trigger("onTagLimitExceeded", 12 | null,{duringInitialization:c}),!1;var g=b(this.options.onTagClicked?'':'').text(a),f=b("
  • ").addClass("tagit-choice ui-widget-content ui-state-default ui-corner-all").addClass(d).append(g);this.options.readOnly?f.addClass("tagit-choice-read-only"):(f.addClass("tagit-choice-editable"),d=b("").addClass("ui-icon ui-icon-close"),d=b('\u00d7').addClass("tagit-close").append(d).click(function(){e.removeTag(f)}), 13 | f.append(d));this.options.singleField||(g=g.html(),f.append(''));!1!==this._trigger("beforeTagAdded",null,{tag:f,tagLabel:this.tagLabel(f),duringInitialization:c})&&(this.options.singleField&&(g=this.assignedTags(),g.push(a),this._updateSingleTagsField(g)),this._trigger("onTagAdded",null,f),this.tagInput.val(""),this.tagInput.parent().before(f),this._trigger("afterTagAdded",null,{tag:f,tagLabel:this.tagLabel(f), 14 | duringInitialization:c}),this.options.showAutocompleteOnFocus&&!c&&setTimeout(function(){e._showAutocomplete()},0))},removeTag:function(a,d){d="undefined"===typeof d?this.options.animate:d;a=b(a);this._trigger("onTagRemoved",null,a);if(!1!==this._trigger("beforeTagRemoved",null,{tag:a,tagLabel:this.tagLabel(a)})){if(this.options.singleField){var c=this.assignedTags(),e=this.tagLabel(a),c=b.grep(c,function(a){return a!=e});this._updateSingleTagsField(c)}d?(a.addClass("removed"),c=this._effectExists("blind")? 15 | ["blind",{direction:"horizontal"},"fast"]:["fast"],c.push(function(){a.remove()}),a.fadeOut("fast").hide.apply(a,c).dequeue()):a.remove();this._trigger("afterTagRemoved",null,{tag:a,tagLabel:this.tagLabel(a)})}},removeTagByLabel:function(a,b){var c=this._findTagByLabel(a);if(!c)throw"No such tag exists with the name '"+a+"'";this.removeTag(c,b)},removeAll:function(){var a=this;this._tags().each(function(b,c){a.removeTag(c,!1)})}})})(jQuery); 16 | -------------------------------------------------------------------------------- /questions/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | # Adding model 'Group' 12 | db.create_table('questions_group', ( 13 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 14 | ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=60)), 15 | ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), 16 | )) 17 | db.send_create_signal('questions', ['Group']) 18 | 19 | # Adding model 'Settings' 20 | db.create_table('questions_settings', ( 21 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 22 | ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), 23 | ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['questions.Group'], null=True, blank=True)), 24 | )) 25 | db.send_create_signal('questions', ['Settings']) 26 | 27 | # Adding model 'Question' 28 | db.create_table('questions_question', ( 29 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 30 | ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), 31 | ('status', self.gf('django.db.models.fields.CharField')(default='Pending', max_length=30)), 32 | ('tags', self.gf('tagging.fields.TagField')(null=True)), 33 | ('difficulty', self.gf('django.db.models.fields.CharField')(default='Easy', max_length=20)), 34 | ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['questions.Group'])), 35 | ('date_added', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), 36 | ('added_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), 37 | )) 38 | db.send_create_signal('questions', ['Question']) 39 | 40 | # Adding model 'QuestionRevision' 41 | db.create_table('questions_questionrevision', ( 42 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 43 | ('question', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['questions.Question'])), 44 | ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), 45 | ('text', self.gf('django.db.models.fields.TextField')()), 46 | ('active', self.gf('django.db.models.fields.BooleanField')(default=False)), 47 | ('date_added', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), 48 | )) 49 | db.send_create_signal('questions', ['QuestionRevision']) 50 | 51 | 52 | def backwards(self, orm): 53 | # Deleting model 'Group' 54 | db.delete_table('questions_group') 55 | 56 | # Deleting model 'Settings' 57 | db.delete_table('questions_settings') 58 | 59 | # Deleting model 'Question' 60 | db.delete_table('questions_question') 61 | 62 | # Deleting model 'QuestionRevision' 63 | db.delete_table('questions_questionrevision') 64 | 65 | 66 | models = { 67 | 'auth.group': { 68 | 'Meta': {'object_name': 'Group'}, 69 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 70 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 71 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 72 | }, 73 | 'auth.permission': { 74 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 75 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 76 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 77 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 78 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 79 | }, 80 | 'auth.user': { 81 | 'Meta': {'object_name': 'User'}, 82 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 83 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 84 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 85 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 86 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 87 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 88 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 89 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 90 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 91 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 92 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 93 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 94 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 95 | }, 96 | 'contenttypes.contenttype': { 97 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 98 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 99 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 100 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 101 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 102 | }, 103 | 'questions.group': { 104 | 'Meta': {'object_name': 'Group'}, 105 | 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 106 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 107 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '60'}) 108 | }, 109 | 'questions.question': { 110 | 'Meta': {'object_name': 'Question'}, 111 | 'added_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), 112 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 113 | 'difficulty': ('django.db.models.fields.CharField', [], {'default': "'Easy'", 'max_length': '20'}), 114 | 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Group']"}), 115 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 116 | 'status': ('django.db.models.fields.CharField', [], {'default': "'Pending'", 'max_length': '30'}), 117 | 'tags': ('tagging.fields.TagField', [], {'null': 'True'}), 118 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) 119 | }, 120 | 'questions.questionrevision': { 121 | 'Meta': {'object_name': 'QuestionRevision'}, 122 | 'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 123 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 124 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 125 | 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Question']"}), 126 | 'text': ('django.db.models.fields.TextField', [], {}), 127 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 128 | }, 129 | 'questions.settings': { 130 | 'Meta': {'object_name': 'Settings'}, 131 | 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['questions.Group']", 'null': 'True', 'blank': 'True'}), 132 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 133 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 134 | } 135 | } 136 | 137 | complete_apps = ['questions'] -------------------------------------------------------------------------------- /questions/views.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings as django_settings 2 | from django.contrib.auth.decorators import login_required 3 | from django.contrib.auth.models import User 4 | from django.http import HttpResponseRedirect 5 | 6 | 7 | from shortcuts import r2r, json_response 8 | from models import Group, Question, QuestionRevision, Settings 9 | from forms import QuestionForm, GroupForm, SettingsForm 10 | 11 | from tagging.models import Tag, TaggedItem 12 | 13 | import operator 14 | 15 | @login_required 16 | def index(request): 17 | context = {} 18 | approved_questions = Question.objects.filter(status__exact="Approved") 19 | pending_questions = Question.objects.filter(status__exact="Pending") 20 | banned_questions = Question.objects.filter(status__exact="Banned") 21 | 22 | settings = Settings.get_by_user(request.user) 23 | if settings.group: 24 | approved_questions = approved_questions.filter(groups__id=settings.group.id) 25 | pending_questions = pending_questions.filter(groups__id=settings.group.id) 26 | banned_questions = banned_questions.filter(groups__id=settings.group.id) 27 | 28 | context["approved_questions"] = approved_questions 29 | context["pending_questions"] = pending_questions 30 | context["banned_questions"] = banned_questions 31 | context["settings"] = settings 32 | return r2r(request, "index", context) 33 | 34 | 35 | @login_required 36 | def view_orphans(request): 37 | context = {} 38 | 39 | approved_questions = Question.objects.filter(status__exact="Approved", groups=None) 40 | pending_questions = Question.objects.filter(status__exact="Pending", groups=None) 41 | banned_questions = Question.objects.filter(status__exact="Banned", groups=None) 42 | 43 | context["approved_questions"] = approved_questions 44 | context["pending_questions"] = pending_questions 45 | context["banned_questions"] = banned_questions 46 | 47 | return r2r(request, "view_orphans", context) 48 | 49 | 50 | @login_required 51 | def add_question(request): 52 | context = {} 53 | 54 | settings = Settings.get_by_user(request.user) 55 | initial = {} 56 | 57 | if settings.group: 58 | initial["groups"] = [settings.group.id] 59 | 60 | if request.method == "POST": 61 | form = QuestionForm(request.POST) 62 | if form.is_valid(): 63 | question = form.save(commit=False) 64 | question.added_by = request.user 65 | question.save() 66 | 67 | revision = QuestionRevision( 68 | question=question, user=request.user, 69 | text=form.cleaned_data["body"], active=True) 70 | revision.save() 71 | return HttpResponseRedirect("/question/%s" % question.id) 72 | else: 73 | form = QuestionForm(initial=initial) 74 | 75 | context["form"] = form 76 | 77 | 78 | return r2r(request, "add_question", context) 79 | 80 | 81 | @login_required 82 | def view_question(request, question_id): 83 | context = { 84 | "active_revision": True, 85 | "settings": Settings.get_by_user(request.user), 86 | } 87 | 88 | revision_id = request.GET.get("revision_id", None) 89 | question = Question.objects.get(pk=question_id) 90 | active_revision = question.get_active_revision() 91 | revision = active_revision 92 | 93 | if revision_id is not None and revision.id != revision_id: 94 | context["active_revision"] = False 95 | revision = QuestionRevision.objects.get(question_id=question_id, pk=revision_id) 96 | 97 | 98 | context["question"] = question 99 | context["revision"] = revision 100 | return r2r(request, "view_question", context) 101 | 102 | 103 | @login_required 104 | def set_revision(request, question_id, revision_id): 105 | context = {} 106 | question = Question.objects.get(pk=question_id) 107 | active_revision = question.get_active_revision() 108 | revision = active_revision 109 | 110 | if revision.id != revision_id: 111 | revision = QuestionRevision.objects.get(question_id=question_id, pk=revision_id) 112 | revision.active = True 113 | revision.save() 114 | 115 | active_revision.active=False 116 | active_revision.save() 117 | 118 | return HttpResponseRedirect("/question/%s" % question_id) 119 | 120 | 121 | @login_required 122 | def view_revisions(request, question_id): 123 | context = {} 124 | question = Question.objects.get(pk=question_id) 125 | context["question"] = question 126 | context["revisions"] = QuestionRevision.objects.filter(question_id=question.id).order_by('date_added') 127 | return r2r(request, "view_revisions", context) 128 | 129 | @login_required 130 | def delete_question(request, question_id): 131 | context = {} 132 | question = Question.objects.get(pk=question_id) 133 | if request.user.is_staff or request.user.email == question.added_by.email: 134 | question.delete() 135 | return HttpResponseRedirect("/questions") 136 | 137 | 138 | @login_required 139 | def edit_question(request, question_id): 140 | context = {} 141 | question = Question.objects.get(pk=question_id) 142 | old_revision = question.get_active_revision() 143 | 144 | if request.method == "POST": 145 | form = QuestionForm(request.POST, instance=question) 146 | if form.is_valid(): 147 | form.save() 148 | question = form.save() 149 | if old_revision.text != form.cleaned_data["body"]: 150 | new_revision = QuestionRevision( 151 | question=question, user=request.user, 152 | text=form.cleaned_data["body"], active=True) 153 | new_revision.save() 154 | old_revision.active = False 155 | old_revision.save() 156 | return HttpResponseRedirect("/question/%s" % question_id) 157 | else: 158 | form = QuestionForm(instance=question, initial={ 159 | "body": old_revision.text 160 | }) 161 | 162 | context["form"] = form 163 | return r2r(request, "edit_question", context) 164 | 165 | 166 | @login_required 167 | def set_status(request, question_id, status): 168 | question = Question.objects.get(pk=question_id) 169 | if question.status != status: 170 | question.status = status 171 | question.save() 172 | return HttpResponseRedirect("/question/%s" % question_id) 173 | 174 | 175 | @login_required 176 | def add_group(request): 177 | context = {} 178 | 179 | if request.method == "POST": 180 | form = GroupForm(request.POST) 181 | if form.is_valid(): 182 | group = form.save(commit=False) 183 | group.added_by = request.user 184 | group.save() 185 | return HttpResponseRedirect("/group/%s" % group.id) 186 | else: 187 | form = GroupForm() 188 | 189 | context["form"] = form 190 | return r2r(request, "add_group", context) 191 | 192 | 193 | @login_required 194 | def view_group(request, group_id): 195 | context = {} 196 | context["group"] = Group.objects.get(pk=group_id) 197 | 198 | approved_questions = Question.objects.filter(status__exact="Approved") 199 | pending_questions = Question.objects.filter(status__exact="Pending") 200 | banned_questions = Question.objects.filter(status__exact="Banned") 201 | 202 | approved_questions = approved_questions.filter(groups__id=group_id) 203 | pending_questions = pending_questions.filter(groups__id=group_id) 204 | banned_questions = banned_questions.filter(groups__id=group_id) 205 | 206 | context["approved_questions"] = approved_questions 207 | context["pending_questions"] = pending_questions 208 | context["banned_questions"] = banned_questions 209 | context["settings"] = Settings.get_by_user(request.user) 210 | return r2r(request, "view_group", context) 211 | 212 | 213 | @login_required 214 | def delete_group(request, group_id): 215 | context = {} 216 | group = Group.objects.get(pk=group_id) 217 | if request.user.is_staff or request.user.email == group.added_by.email: 218 | group.delete() 219 | return HttpResponseRedirect("/groups") 220 | 221 | 222 | @login_required 223 | def edit_group(request, group_id): 224 | context = {} 225 | group = Group.objects.get(pk=group_id) 226 | 227 | if not request.user.is_staff and request.user.email != group.added_by.email: 228 | return HttpResponseRedirect("/group/%s" % group_id) 229 | 230 | if request.method == "POST": 231 | form = GroupForm(request.POST, instance=group) 232 | if form.is_valid(): 233 | form.save() 234 | return HttpResponseRedirect("/group/%s" % group_id) 235 | else: 236 | form = GroupForm(instance=group) 237 | 238 | context["form"] = form 239 | return r2r(request, "edit_group", context) 240 | 241 | @login_required 242 | def view_tag(request, tagname): 243 | context = {} 244 | 245 | approved_questions = Question.objects.filter(status__exact="Approved") 246 | pending_questions = Question.objects.filter(status__exact="Pending") 247 | banned_questions = Question.objects.filter(status__exact="Banned") 248 | 249 | settings = Settings.get_by_user(request.user) 250 | if settings.group: 251 | approved_questions = approved_questions.filter(groups__id=settings.group.id) 252 | pending_questions = pending_questions.filter(groups__id=settings.group.id) 253 | banned_questions = banned_questions.filter(groups__id=settings.group.id) 254 | 255 | context["tagname"] = tagname 256 | tagname = '"%s"' % tagname 257 | context["approved_questions"] = TaggedItem.objects.get_by_model(approved_questions, tagname) 258 | context["pending_questions"] = TaggedItem.objects.get_by_model(pending_questions, tagname) 259 | context["banned_questions"] = TaggedItem.objects.get_by_model(banned_questions, tagname) 260 | context["settings"] = settings 261 | 262 | return r2r(request, "view_tag", context) 263 | 264 | 265 | @login_required 266 | def tags_ajax(request): 267 | term = request.GET.get("term") 268 | 269 | if term: 270 | tags = Tag.objects.filter(name__startswith=term) 271 | else: 272 | tags = Tag.objects.all() 273 | 274 | return json_response([tag.name for tag in tags]) 275 | 276 | 277 | @login_required 278 | def settings(request): 279 | context = {} 280 | context["settings"] = Settings.get_by_user(request.user) 281 | return r2r(request, "settings", context) 282 | 283 | 284 | @login_required 285 | def edit_settings(request): 286 | context = {} 287 | settings = Settings.get_by_user(request.user) 288 | 289 | if request.method == "POST": 290 | form = SettingsForm(request.POST, instance=settings) 291 | if form.is_valid(): 292 | form.save() 293 | return HttpResponseRedirect("/settings") 294 | else: 295 | form = SettingsForm(instance=settings) 296 | 297 | context["form"] = form 298 | return r2r(request, "edit_settings", context) 299 | 300 | 301 | @login_required 302 | def users(request): 303 | context = {} 304 | counts = {} 305 | 306 | for questions in Question.objects.all(): 307 | email = questions.added_by.email 308 | if email not in counts: 309 | counts[email] = 0 310 | counts[email] += 1 311 | 312 | for user in User.objects.all(): 313 | if user.email not in counts: 314 | counts[user.email] = 0 315 | 316 | # Double sort since sort is stable. Want names alpha after count 317 | context["counts"] = sorted(counts.items(), key=operator.itemgetter(0)) 318 | context["counts"] = sorted(context["counts"], key=operator.itemgetter(1), reverse=True) 319 | return r2r(request, "users", context) 320 | 321 | 322 | @login_required 323 | def groups(request): 324 | context = {} 325 | context["groups"] = Group.objects.all() 326 | return r2r(request, "groups", context) 327 | 328 | 329 | @login_required 330 | def help(request): 331 | return r2r(request, "help") 332 | 333 | 334 | def failure_handler(request, message, status=None, template_name=None, exception=None): 335 | context = { 336 | 'allowed_domains': getattr(django_settings, "OPENID_RESTRICT_TO_DOMAINS", tuple()), 337 | } 338 | return r2r(request, "login_failed", context) 339 | -------------------------------------------------------------------------------- /static/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.2.2 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */@-ms-viewport{width:device-width}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /static/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2012 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function($){"use strict";$(function(){$.support.transition=function(){var transitionEnd=function(){var name,el=document.createElement("bootstrap"),transEndEventNames={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(name in transEndEventNames)if(void 0!==el.style[name])return transEndEventNames[name]}();return transitionEnd&&{end:transitionEnd}}()})}(window.jQuery),!function($){"use strict";var dismiss='[data-dismiss="alert"]',Alert=function(el){$(el).on("click",dismiss,this.close)};Alert.prototype.close=function(e){function removeElement(){$parent.trigger("closed").remove()}var $parent,$this=$(this),selector=$this.attr("data-target");selector||(selector=$this.attr("href"),selector=selector&&selector.replace(/.*(?=#[^\s]*$)/,"")),$parent=$(selector),e&&e.preventDefault(),$parent.length||($parent=$this.hasClass("alert")?$this:$this.parent()),$parent.trigger(e=$.Event("close")),e.isDefaultPrevented()||($parent.removeClass("in"),$.support.transition&&$parent.hasClass("fade")?$parent.on($.support.transition.end,removeElement):removeElement())};var old=$.fn.alert;$.fn.alert=function(option){return this.each(function(){var $this=$(this),data=$this.data("alert");data||$this.data("alert",data=new Alert(this)),"string"==typeof option&&data[option].call($this)})},$.fn.alert.Constructor=Alert,$.fn.alert.noConflict=function(){return $.fn.alert=old,this},$(document).on("click.alert.data-api",dismiss,Alert.prototype.close)}(window.jQuery),!function($){"use strict";var Button=function(element,options){this.$element=$(element),this.options=$.extend({},$.fn.button.defaults,options)};Button.prototype.setState=function(state){var d="disabled",$el=this.$element,data=$el.data(),val=$el.is("input")?"val":"html";state+="Text",data.resetText||$el.data("resetText",$el[val]()),$el[val](data[state]||this.options[state]),setTimeout(function(){"loadingText"==state?$el.addClass(d).attr(d,d):$el.removeClass(d).removeAttr(d)},0)},Button.prototype.toggle=function(){var $parent=this.$element.closest('[data-toggle="buttons-radio"]');$parent&&$parent.find(".active").removeClass("active"),this.$element.toggleClass("active")};var old=$.fn.button;$.fn.button=function(option){return this.each(function(){var $this=$(this),data=$this.data("button"),options="object"==typeof option&&option;data||$this.data("button",data=new Button(this,options)),"toggle"==option?data.toggle():option&&data.setState(option)})},$.fn.button.defaults={loadingText:"loading..."},$.fn.button.Constructor=Button,$.fn.button.noConflict=function(){return $.fn.button=old,this},$(document).on("click.button.data-api","[data-toggle^=button]",function(e){var $btn=$(e.target);$btn.hasClass("btn")||($btn=$btn.closest(".btn")),$btn.button("toggle")})}(window.jQuery),!function($){"use strict";var Carousel=function(element,options){this.$element=$(element),this.options=options,"hover"==this.options.pause&&this.$element.on("mouseenter",$.proxy(this.pause,this)).on("mouseleave",$.proxy(this.cycle,this))};Carousel.prototype={cycle:function(e){return e||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval($.proxy(this.next,this),this.options.interval)),this},to:function(pos){var $active=this.$element.find(".item.active"),children=$active.parent().children(),activePos=children.index($active),that=this;if(!(pos>children.length-1||0>pos))return this.sliding?this.$element.one("slid",function(){that.to(pos)}):activePos==pos?this.pause().cycle():this.slide(pos>activePos?"next":"prev",$(children[pos]))},pause:function(e){return e||(this.paused=!0),this.$element.find(".next, .prev").length&&$.support.transition.end&&(this.$element.trigger($.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){return this.sliding?void 0:this.slide("next")},prev:function(){return this.sliding?void 0:this.slide("prev")},slide:function(type,next){var e,$active=this.$element.find(".item.active"),$next=next||$active[type](),isCycling=this.interval,direction="next"==type?"left":"right",fallback="next"==type?"first":"last",that=this;if(this.sliding=!0,isCycling&&this.pause(),$next=$next.length?$next:this.$element.find(".item")[fallback](),e=$.Event("slide",{relatedTarget:$next[0]}),!$next.hasClass("active")){if($.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(e),e.isDefaultPrevented())return;$next.addClass(type),$next[0].offsetWidth,$active.addClass(direction),$next.addClass(direction),this.$element.one($.support.transition.end,function(){$next.removeClass([type,direction].join(" ")).addClass("active"),$active.removeClass(["active",direction].join(" ")),that.sliding=!1,setTimeout(function(){that.$element.trigger("slid")},0)})}else{if(this.$element.trigger(e),e.isDefaultPrevented())return;$active.removeClass("active"),$next.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return isCycling&&this.cycle(),this}}};var old=$.fn.carousel;$.fn.carousel=function(option){return this.each(function(){var $this=$(this),data=$this.data("carousel"),options=$.extend({},$.fn.carousel.defaults,"object"==typeof option&&option),action="string"==typeof option?option:options.slide;data||$this.data("carousel",data=new Carousel(this,options)),"number"==typeof option?data.to(option):action?data[action]():options.interval&&data.cycle()})},$.fn.carousel.defaults={interval:5e3,pause:"hover"},$.fn.carousel.Constructor=Carousel,$.fn.carousel.noConflict=function(){return $.fn.carousel=old,this},$(document).on("click.carousel.data-api","[data-slide]",function(e){var href,$this=$(this),$target=$($this.attr("data-target")||(href=$this.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,"")),options=$.extend({},$target.data(),$this.data());$target.carousel(options),e.preventDefault()})}(window.jQuery),!function($){"use strict";var Collapse=function(element,options){this.$element=$(element),this.options=$.extend({},$.fn.collapse.defaults,options),this.options.parent&&(this.$parent=$(this.options.parent)),this.options.toggle&&this.toggle()};Collapse.prototype={constructor:Collapse,dimension:function(){var hasWidth=this.$element.hasClass("width");return hasWidth?"width":"height"},show:function(){var dimension,scroll,actives,hasData;if(!this.transitioning){if(dimension=this.dimension(),scroll=$.camelCase(["scroll",dimension].join("-")),actives=this.$parent&&this.$parent.find("> .accordion-group > .in"),actives&&actives.length){if(hasData=actives.data("collapse"),hasData&&hasData.transitioning)return;actives.collapse("hide"),hasData||actives.data("collapse",null)}this.$element[dimension](0),this.transition("addClass",$.Event("show"),"shown"),$.support.transition&&this.$element[dimension](this.$element[0][scroll])}},hide:function(){var dimension;this.transitioning||(dimension=this.dimension(),this.reset(this.$element[dimension]()),this.transition("removeClass",$.Event("hide"),"hidden"),this.$element[dimension](0))},reset:function(size){var dimension=this.dimension();return this.$element.removeClass("collapse")[dimension](size||"auto")[0].offsetWidth,this.$element[null!==size?"addClass":"removeClass"]("collapse"),this},transition:function(method,startEvent,completeEvent){var that=this,complete=function(){"show"==startEvent.type&&that.reset(),that.transitioning=0,that.$element.trigger(completeEvent)};this.$element.trigger(startEvent),startEvent.isDefaultPrevented()||(this.transitioning=1,this.$element[method]("in"),$.support.transition&&this.$element.hasClass("collapse")?this.$element.one($.support.transition.end,complete):complete())},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var old=$.fn.collapse;$.fn.collapse=function(option){return this.each(function(){var $this=$(this),data=$this.data("collapse"),options="object"==typeof option&&option;data||$this.data("collapse",data=new Collapse(this,options)),"string"==typeof option&&data[option]()})},$.fn.collapse.defaults={toggle:!0},$.fn.collapse.Constructor=Collapse,$.fn.collapse.noConflict=function(){return $.fn.collapse=old,this},$(document).on("click.collapse.data-api","[data-toggle=collapse]",function(e){var href,$this=$(this),target=$this.attr("data-target")||e.preventDefault()||(href=$this.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,""),option=$(target).data("collapse")?"toggle":$this.data();$this[$(target).hasClass("in")?"addClass":"removeClass"]("collapsed"),$(target).collapse(option)})}(window.jQuery),!function($){"use strict";function clearMenus(){$(toggle).each(function(){getParent($(this)).removeClass("open")})}function getParent($this){var $parent,selector=$this.attr("data-target");return selector||(selector=$this.attr("href"),selector=selector&&/#/.test(selector)&&selector.replace(/.*(?=#[^\s]*$)/,"")),$parent=$(selector),$parent.length||($parent=$this.parent()),$parent}var toggle="[data-toggle=dropdown]",Dropdown=function(element){var $el=$(element).on("click.dropdown.data-api",this.toggle);$("html").on("click.dropdown.data-api",function(){$el.parent().removeClass("open")})};Dropdown.prototype={constructor:Dropdown,toggle:function(){var $parent,isActive,$this=$(this);if(!$this.is(".disabled, :disabled"))return $parent=getParent($this),isActive=$parent.hasClass("open"),clearMenus(),isActive||$parent.toggleClass("open"),$this.focus(),!1},keydown:function(e){var $this,$items,$parent,isActive,index;if(/(38|40|27)/.test(e.keyCode)&&($this=$(this),e.preventDefault(),e.stopPropagation(),!$this.is(".disabled, :disabled"))){if($parent=getParent($this),isActive=$parent.hasClass("open"),!isActive||isActive&&27==e.keyCode)return $this.click();$items=$("[role=menu] li:not(.divider):visible a",$parent),$items.length&&(index=$items.index($items.filter(":focus")),38==e.keyCode&&index>0&&index--,40==e.keyCode&&$items.length-1>index&&index++,~index||(index=0),$items.eq(index).focus())}}};var old=$.fn.dropdown;$.fn.dropdown=function(option){return this.each(function(){var $this=$(this),data=$this.data("dropdown");data||$this.data("dropdown",data=new Dropdown(this)),"string"==typeof option&&data[option].call($this)})},$.fn.dropdown.Constructor=Dropdown,$.fn.dropdown.noConflict=function(){return $.fn.dropdown=old,this},$(document).on("click.dropdown.data-api touchstart.dropdown.data-api",clearMenus).on("click.dropdown touchstart.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("touchstart.dropdown.data-api",".dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api touchstart.dropdown.data-api",toggle,Dropdown.prototype.toggle).on("keydown.dropdown.data-api touchstart.dropdown.data-api",toggle+", [role=menu]",Dropdown.prototype.keydown)}(window.jQuery),!function($){"use strict";var Modal=function(element,options){this.options=options,this.$element=$(element).delegate('[data-dismiss="modal"]',"click.dismiss.modal",$.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};Modal.prototype={constructor:Modal,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var that=this,e=$.Event("show");this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.escape(),this.backdrop(function(){var transition=$.support.transition&&that.$element.hasClass("fade");that.$element.parent().length||that.$element.appendTo(document.body),that.$element.show(),transition&&that.$element[0].offsetWidth,that.$element.addClass("in").attr("aria-hidden",!1),that.enforceFocus(),transition?that.$element.one($.support.transition.end,function(){that.$element.focus().trigger("shown")}):that.$element.focus().trigger("shown")}))},hide:function(e){e&&e.preventDefault(),e=$.Event("hide"),this.$element.trigger(e),this.isShown&&!e.isDefaultPrevented()&&(this.isShown=!1,this.escape(),$(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),$.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal())},enforceFocus:function(){var that=this;$(document).on("focusin.modal",function(e){that.$element[0]===e.target||that.$element.has(e.target).length||that.$element.focus()})},escape:function(){var that=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(e){27==e.which&&that.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var that=this,timeout=setTimeout(function(){that.$element.off($.support.transition.end),that.hideModal()},500);this.$element.one($.support.transition.end,function(){clearTimeout(timeout),that.hideModal()})},hideModal:function(){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(callback){var animate=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var doAnimate=$.support.transition&&animate;this.$backdrop=$('