diff --git a/.gitignore b/.gitignore index 7bbd45a1..52a15f38 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,8 @@ kcdc3/kcdc3/settings.py kcdc3/kcdc3/settings.py -kcdc3/public/media/event_images -kcdc3/public/media/mugshots + +venv_kcdc codekit-config.json .sass-cache @@ -29,4 +29,28 @@ kcdc3/public/static/fonts/ss-standard/ss-standard.eot kcdc3/public/static/fonts/ss-standard/ss-standard.js kcdc3/public/static/fonts/ss-standard/ss-standard.svg kcdc3/public/static/fonts/ss-standard/ss-standard.ttf -kcdc3/public/static/fonts/ss-standard/ss-standard.woff \ No newline at end of file +kcdc3/public/static/fonts/ss-standard/ss-standard.woff + +kcdc3/public/media/bio +kcdc3/public/media/blog +kcdc3/public/media/event_images +kcdc3/public/media/front_slides +kcdc3/public/media/mugshots +kcdc3/public/media/press +kcdc3/public/media/uploads + +kcdc3/public/media/csv/export_2013-02-24.csv + +kcdc3/public/static/tmp/index.html + +kcdc3/kcdc3/settings/prod.py +kcdc3/kcdc3/settings/prod.py + +kcdc3/apps/pinata/.views.py.swp +kcdc3/settings/prod.py +kcdc3/tmp/ + + +kcdc3/public/static/fonts/ss-social/ss-social.min.js + +kcdc3/public/static/fonts/ss-standard/ss-standard.min.js diff --git a/config.rb b/config.rb index 3b82826e..035e65ca 100644 --- a/config.rb +++ b/config.rb @@ -5,6 +5,6 @@ sass_dir = "kcdc3/public/static/sass" images_dir = "kcdc3/public/static/images" javascripts_dir = "kcdc3/public/static/assets/js" -output_style = :expanded +output_style = :compressed css_dir = "kcdc3/public/static/css" diff --git a/kcdc3/accounts/__init__.py b/kcdc3/__init__.py similarity index 100% rename from kcdc3/accounts/__init__.py rename to kcdc3/__init__.py diff --git a/kcdc3/accounts/migrations/__init__.py b/kcdc3/apps/__init__.py similarity index 100% rename from kcdc3/accounts/migrations/__init__.py rename to kcdc3/apps/__init__.py diff --git a/kcdc3/classes/__init__.py b/kcdc3/apps/accounts/__init__.py similarity index 100% rename from kcdc3/classes/__init__.py rename to kcdc3/apps/accounts/__init__.py diff --git a/kcdc3/accounts/migrations/0001_initial.py b/kcdc3/apps/accounts/migrations/0001_initial.py similarity index 100% rename from kcdc3/accounts/migrations/0001_initial.py rename to kcdc3/apps/accounts/migrations/0001_initial.py diff --git a/kcdc3/accounts/migrations/0002_auto__chg_field_extendedprofile_phone_number__chg_field_extendedprofil.py b/kcdc3/apps/accounts/migrations/0002_auto__chg_field_extendedprofile_phone_number__chg_field_extendedprofil.py similarity index 100% rename from kcdc3/accounts/migrations/0002_auto__chg_field_extendedprofile_phone_number__chg_field_extendedprofil.py rename to kcdc3/apps/accounts/migrations/0002_auto__chg_field_extendedprofile_phone_number__chg_field_extendedprofil.py diff --git a/kcdc3/classes/migrations/__init__.py b/kcdc3/apps/accounts/migrations/__init__.py similarity index 100% rename from kcdc3/classes/migrations/__init__.py rename to kcdc3/apps/accounts/migrations/__init__.py diff --git a/kcdc3/accounts/models.py b/kcdc3/apps/accounts/models.py similarity index 100% rename from kcdc3/accounts/models.py rename to kcdc3/apps/accounts/models.py diff --git a/kcdc3/apps/accounts/templates/disabled.html b/kcdc3/apps/accounts/templates/disabled.html new file mode 100644 index 00000000..0a30f606 --- /dev/null +++ b/kcdc3/apps/accounts/templates/disabled.html @@ -0,0 +1,17 @@ +{% extends 'userena/base_userena.html' %} +{% load i18n %} + +{% block title %}{% trans "Disabled account" %}{% endblock %} +{% block content_title %}

{% trans "Your account has been disabled" %}

{% endblock %} +{% block content %} + +

Your account is currently disabled.

+

If you just signed up, you still need to active your account. + Please check your email and find the activation message that we sent you. (Your mail provider may have wrongly marked it as spam.) + You'll need to click on the link in that message to activate your account.

+

Check your spam folder; your mail provider may have wrongly marked the message as spam.

+

If you're still having problems, please email us at + contact@knowledgecommonsdc.org. +

+ +{% endblock %} diff --git a/kcdc3/apps/accounts/templates/profile_detail.html b/kcdc3/apps/accounts/templates/profile_detail.html new file mode 100644 index 00000000..5b425b35 --- /dev/null +++ b/kcdc3/apps/accounts/templates/profile_detail.html @@ -0,0 +1,60 @@ +{% extends 'userena/base_userena.html' %} +{% load i18n %} +{% load url from future %} + +{% block title %}{% blocktrans with profile.user.username as username %}{{ username }}'s profile.{% endblocktrans %}{% endblock %} +{% block content_title %}

{{ profile.user.username }} {% if profile.user.get_full_name %}({{ profile.user.get_full_name }}){% endif %}

{% endblock %} + +{% block content %} +
+ {% block profile_navigation %} + {% comment %}Dirty hack. Will use django-guardian in the future.{% endcomment %} + {% if user.username == profile.user.username %} + + {% endif %} + {% endblock %} + +
+ + {% comment %} + {% block profile_details %} + {% trans +
+ {% block profile_definition_list %} + {% if profile.user.get_full_name %} +
{% trans "Name" %}
+
{{ profile.user.get_full_name }}
+ {% endif %} + {% if profile.user.email and not hide_email %} +
{% trans "Email" %}
+
{{ profile.user.email }}
+ {% endif %} + {% if profile.age %} +
{% trans "Age" %}
+
{{ profile.age }}
+ {% endif %} + {% if profile.website %} +
{% trans "Website" %}
+
{{ profile.website|urlize }}
+ {% endif %} + {% if profile.location %} +
{% trans "Location" %}
+
{{ profile.location }}
+ {% endif %} + {% if profile.about_me %} +
{% trans "About me" %}
+
{{ profile.about_me }}
+ {% endif %} + {% endblock %} +
+ {% endblock %} + {% endcomment %} +
+
+{% endblock %} \ No newline at end of file diff --git a/kcdc3/apps/accounts/templates/signin_form.html b/kcdc3/apps/accounts/templates/signin_form.html new file mode 100644 index 00000000..46725626 --- /dev/null +++ b/kcdc3/apps/accounts/templates/signin_form.html @@ -0,0 +1,45 @@ +{% extends 'userena/base_userena.html' %} +{% load i18n %} +{% load url from future %} + +{% block title %}{% trans "Signin" %}{% endblock %} + +{% block content %} + +

Log in

+ +

+ We've moved to a new registration system. + If you created your account before August 24, 2012, we'll need you + to register a new account. +

+ +

Our new registration system lets us: give you more information about + class locations, makes it easier for you to update your class signups, + and features real-time, automatic waitlists.

+ +
+ {% csrf_token %} +
+ {% trans "Signin" %} + {{ form.non_field_errors }} + {% for field in form %} + {{ field.errors }} + {% comment %} Displaying checkboxes differently {% endcomment %} + {% if field.name == 'remember_me' %} +

+ +

+ {% else %} +

+ {{ field.label_tag }} + {{ field }} +

+ {% endif %} + {% endfor %} +
+ +

{% trans "Forgot your password?" %}

+ {% if next %}{% endif %} +
+{% endblock %} diff --git a/kcdc3/apps/accounts/templates/signup_complete.html b/kcdc3/apps/accounts/templates/signup_complete.html new file mode 100644 index 00000000..dc59e8c5 --- /dev/null +++ b/kcdc3/apps/accounts/templates/signup_complete.html @@ -0,0 +1,16 @@ +{% extends 'userena/base_userena.html' %} +{% load i18n %} + +{% block title %}{% trans "Signup almost done!" %}{% endblock %} + +{% block content_title %}

{% trans "Signup" %}

{% endblock %} + +{% block content %} +

{% trans "Thank you for signing up with us!" %}

+ +{% if userena_activation_required %} +

{% blocktrans %}Next, you need to active your account. We've just sent you an email message with an activation link. Check your spam folder for the message.{% endblocktrans %}

+{% else %} +

{% blocktrans %}You can now use the supplied credentials to signin.{% endblocktrans %}

+{% endif %} +{% endblock %} diff --git a/kcdc3/apps/accounts/templates/signup_form.html b/kcdc3/apps/accounts/templates/signup_form.html new file mode 100644 index 00000000..05c67075 --- /dev/null +++ b/kcdc3/apps/accounts/templates/signup_form.html @@ -0,0 +1,36 @@ +{% extends 'userena/base_userena.html' %} +{% load i18n %} + +{% block title %}{% trans "Signup" %}{% endblock %} + +{% block content %} + +

Create a new account

+ +
+ {% csrf_token %} + +
+ {% comment %}{% trans "Signup" %}{% endcomment %} + {{ form.non_field_errors }} + {% for field in form %} + {{ field.errors }} + {% comment %} Displaying checkboxes differently {% endcomment %} + {% if field.name == 'tos' %} +

+ +

+ {% else %} +

+ {{ field.label_tag }} + {{ field }} +

+ {% endif %} + {% endfor %} +
+ +

When you create an account, we will add you to the Knowledge Commons DC mailing list. (That takes time, though, so if you want to receive updates immediately, sign up for the list yourself – the form is in the footer.)

+ + +
+{% endblock %} diff --git a/kcdc3/accounts/tests.py b/kcdc3/apps/accounts/tests.py similarity index 100% rename from kcdc3/accounts/tests.py rename to kcdc3/apps/accounts/tests.py diff --git a/kcdc3/accounts/views.py b/kcdc3/apps/accounts/views.py similarity index 97% rename from kcdc3/accounts/views.py rename to kcdc3/apps/accounts/views.py index 80f17f5a..f4a80a21 100644 --- a/kcdc3/accounts/views.py +++ b/kcdc3/apps/accounts/views.py @@ -1,7 +1,7 @@ from django import forms from django.utils.translation import ugettext_lazy as _ from userena.forms import SignupForm -from accounts.models import ExtendedProfile +from models import ExtendedProfile class SignupFormExtra(SignupForm): """ diff --git a/kcdc3/kcdc3/__init__.py b/kcdc3/apps/classes/__init__.py similarity index 100% rename from kcdc3/kcdc3/__init__.py rename to kcdc3/apps/classes/__init__.py diff --git a/kcdc3/classes/admin.py b/kcdc3/apps/classes/admin.py similarity index 66% rename from kcdc3/classes/admin.py rename to kcdc3/apps/classes/admin.py index d4f81861..a70e06da 100644 --- a/kcdc3/classes/admin.py +++ b/kcdc3/apps/classes/admin.py @@ -1,4 +1,4 @@ -from classes.models import Event, Location, Bio, Registration, Session +from kcdc3.apps.classes.models import Event, Location, Bio, Registration, Session, Role from django import forms from django.contrib import admin from django.contrib.admin.sites import site @@ -9,6 +9,7 @@ from django.contrib.auth.models import User + # displays registrations as a list in the Event Admin screen class RegistrationInline(admin.TabularInline): model = Registration @@ -23,22 +24,67 @@ class RegistrationInline(admin.TabularInline): 'fk': ['student'], } + + class LocationAdmin(admin.ModelAdmin): model = Location list_display = ('name','neighborhood','show_exact',) admin.site.register(Location, LocationAdmin) + + +class RoleAdmin(admin.ModelAdmin): + + model = Role + list_display = ('name', 'sort_order',) + fieldsets = [ + (None, {'fields': ['name','description', 'extended_description', 'sort_order',]}), + ] + + class Media: + js = [ + 'tiny_mce/tiny_mce.js', + 'tinymce_setup.js', + ] + +admin.site.register(Role, RoleAdmin) + + + class BioAdmin(admin.ModelAdmin): + model = Bio - list_display = ('name','user',) + list_display = ('name', 'user', 'role', 'title',) fieldsets = [ - (None, {'fields': ['name','description','website','user',]}), + (None, {'fields': ['name','user','image',]}), + ('Teacher bio', { + 'classes': ('grp-collapse grp-open',), + 'fields': [ + 'description', 'website','bio_email', + ] + }), + ('Staff bio', { + 'classes': ('grp-collapse grp-open',), + 'fields': [ + 'role', 'title', 'staff_description', + ], + }), + ('Admin', { + 'classes': ('grp-collapse grp-open',), + 'fields': [ + 'rating', 'comments', + ], + }), ] raw_id_fields = ['user'] related_lookup_fields = { 'fk': ['user'], } + + list_filter = ('role',) + search_fields = ('name',) + class Media: js = [ 'tiny_mce/tiny_mce.js', @@ -47,6 +93,8 @@ class Media: admin.site.register(Bio, BioAdmin) + + # lets someone create/edit a Event class EventAdmin(admin.ModelAdmin): fieldsets = [ @@ -58,22 +106,27 @@ class EventAdmin(admin.ModelAdmin): 'summary', 'description', ('thumbnail', 'main_image'), ] }), - ('More pre-class details and additional dates', { + ('Registration', {'fields': [('registration_status', 'registration_opens',), ('max_students', 'waitlist_status', 'registration_count','waitlist_count'),]}), + ('More details', { 'classes': ('grp-collapse grp-closed',), 'fields': [ 'details', 'additional_dates_text', + 'bio_text', ] }), ('Automatic email messages', { - 'classes': ('grp-collapse grp-closed',), + 'classes': ('grp-collapse grp-open',), 'fields': ['email_welcome_text',] }), - ('Post-class documentation', { + ('Documentation', { 'classes': ('grp-collapse grp-closed',), 'fields': ['documentation'] }), - ('Registration', {'fields': [('max_students', 'registration_status', 'waitlist_status','registration_count','waitlist_count')]}), + ('Legacy data', { + 'classes': ('grp-collapse grp-closed',), + 'fields': ['teacher_text','location_text'] + }), ] readonly_fields = ('registration_count','waitlist_count') prepopulated_fields = {"slug": ("title",)} @@ -88,14 +141,15 @@ class Media: 'tinymce_setup.js', ] - list_display = ('title', 'status','date','session', 'registration_status','waitlist_status','max_students', 'registration_count', 'waitlist_count',) - list_editable = ('registration_status',) - list_filter = ('session', 'status', 'registration_status') + list_display = ('title', 'status','date','session', 'featured', 'registration_status', 'registration_opens', 'waitlist_status','max_students', 'registration_count', 'waitlist_count',) + list_editable = ('registration_status', 'featured', 'registration_opens',) + list_filter = ('session', 'status', 'registration_status','type',) search_fields = ('title',) admin.site.register(Event, EventAdmin) + # create/edit a Session class SessionAdmin(admin.ModelAdmin): fieldsets = [ @@ -111,8 +165,10 @@ class SessionAdmin(admin.ModelAdmin): ('Text', { 'classes': ('grp-collapse grp-open',), 'fields': [ + 'kicker', 'description', 'sidebar_text', + 'show_sidebar_text', ]}), ('Extended Text', { 'classes': ('grp-collapse grp-open',), diff --git a/kcdc3/classes/email.py b/kcdc3/apps/classes/email.py similarity index 100% rename from kcdc3/classes/email.py rename to kcdc3/apps/classes/email.py diff --git a/kcdc3/classes/fixtures/test-base.json b/kcdc3/apps/classes/fixtures/test-base.json similarity index 100% rename from kcdc3/classes/fixtures/test-base.json rename to kcdc3/apps/classes/fixtures/test-base.json diff --git a/kcdc3/classes/fixtures/test-empty-class.json b/kcdc3/apps/classes/fixtures/test-empty-class.json similarity index 100% rename from kcdc3/classes/fixtures/test-empty-class.json rename to kcdc3/apps/classes/fixtures/test-empty-class.json diff --git a/kcdc3/classes/helpers.py b/kcdc3/apps/classes/helpers.py similarity index 100% rename from kcdc3/classes/helpers.py rename to kcdc3/apps/classes/helpers.py diff --git a/kcdc3/classes/migrations/0001_initial.py b/kcdc3/apps/classes/migrations/0001_initial.py similarity index 100% rename from kcdc3/classes/migrations/0001_initial.py rename to kcdc3/apps/classes/migrations/0001_initial.py diff --git a/kcdc3/classes/migrations/0002_auto__add_field_event_location_description.py b/kcdc3/apps/classes/migrations/0002_auto__add_field_event_location_description.py similarity index 100% rename from kcdc3/classes/migrations/0002_auto__add_field_event_location_description.py rename to kcdc3/apps/classes/migrations/0002_auto__add_field_event_location_description.py diff --git a/kcdc3/classes/migrations/0003_auto__add_field_event_waitlist_status.py b/kcdc3/apps/classes/migrations/0003_auto__add_field_event_waitlist_status.py similarity index 100% rename from kcdc3/classes/migrations/0003_auto__add_field_event_waitlist_status.py rename to kcdc3/apps/classes/migrations/0003_auto__add_field_event_waitlist_status.py diff --git a/kcdc3/classes/migrations/0004_auto__add_registration__add_field_event_teachers.py b/kcdc3/apps/classes/migrations/0004_auto__add_registration__add_field_event_teachers.py similarity index 100% rename from kcdc3/classes/migrations/0004_auto__add_registration__add_field_event_teachers.py rename to kcdc3/apps/classes/migrations/0004_auto__add_registration__add_field_event_teachers.py diff --git a/kcdc3/classes/migrations/0005_auto__del_field_event_teachers.py b/kcdc3/apps/classes/migrations/0005_auto__del_field_event_teachers.py similarity index 100% rename from kcdc3/classes/migrations/0005_auto__del_field_event_teachers.py rename to kcdc3/apps/classes/migrations/0005_auto__del_field_event_teachers.py diff --git a/kcdc3/classes/migrations/0006_auto__add_field_registration_student__add_field_registration_event__ad.py b/kcdc3/apps/classes/migrations/0006_auto__add_field_registration_student__add_field_registration_event__ad.py similarity index 100% rename from kcdc3/classes/migrations/0006_auto__add_field_registration_student__add_field_registration_event__ad.py rename to kcdc3/apps/classes/migrations/0006_auto__add_field_registration_student__add_field_registration_event__ad.py diff --git a/kcdc3/classes/migrations/0007_auto__chg_field_registration_date_registered.py b/kcdc3/apps/classes/migrations/0007_auto__chg_field_registration_date_registered.py similarity index 100% rename from kcdc3/classes/migrations/0007_auto__chg_field_registration_date_registered.py rename to kcdc3/apps/classes/migrations/0007_auto__chg_field_registration_date_registered.py diff --git a/kcdc3/classes/migrations/0008_auto__chg_field_registration_date_registered.py b/kcdc3/apps/classes/migrations/0008_auto__chg_field_registration_date_registered.py similarity index 100% rename from kcdc3/classes/migrations/0008_auto__chg_field_registration_date_registered.py rename to kcdc3/apps/classes/migrations/0008_auto__chg_field_registration_date_registered.py diff --git a/kcdc3/classes/migrations/0009_auto__add_field_event_slug.py b/kcdc3/apps/classes/migrations/0009_auto__add_field_event_slug.py similarity index 100% rename from kcdc3/classes/migrations/0009_auto__add_field_event_slug.py rename to kcdc3/apps/classes/migrations/0009_auto__add_field_event_slug.py diff --git a/kcdc3/classes/migrations/0010_auto__add_unique_event_slug.py b/kcdc3/apps/classes/migrations/0010_auto__add_unique_event_slug.py similarity index 100% rename from kcdc3/classes/migrations/0010_auto__add_unique_event_slug.py rename to kcdc3/apps/classes/migrations/0010_auto__add_unique_event_slug.py diff --git a/kcdc3/classes/migrations/0011_auto__add_field_registration_cancelled__add_field_registration_date_ca.py b/kcdc3/apps/classes/migrations/0011_auto__add_field_registration_cancelled__add_field_registration_date_ca.py similarity index 100% rename from kcdc3/classes/migrations/0011_auto__add_field_registration_cancelled__add_field_registration_date_ca.py rename to kcdc3/apps/classes/migrations/0011_auto__add_field_registration_cancelled__add_field_registration_date_ca.py diff --git a/kcdc3/classes/migrations/0012_auto__chg_field_registration_date_registered.py b/kcdc3/apps/classes/migrations/0012_auto__chg_field_registration_date_registered.py similarity index 100% rename from kcdc3/classes/migrations/0012_auto__chg_field_registration_date_registered.py rename to kcdc3/apps/classes/migrations/0012_auto__chg_field_registration_date_registered.py diff --git a/kcdc3/classes/migrations/0013_auto__add_field_event_num_students_registered__add_field_event_adding_.py b/kcdc3/apps/classes/migrations/0013_auto__add_field_event_num_students_registered__add_field_event_adding_.py similarity index 100% rename from kcdc3/classes/migrations/0013_auto__add_field_event_num_students_registered__add_field_event_adding_.py rename to kcdc3/apps/classes/migrations/0013_auto__add_field_event_num_students_registered__add_field_event_adding_.py diff --git a/kcdc3/classes/migrations/0014_auto__del_field_event_num_students_registered__del_field_event_adding_.py b/kcdc3/apps/classes/migrations/0014_auto__del_field_event_num_students_registered__del_field_event_adding_.py similarity index 100% rename from kcdc3/classes/migrations/0014_auto__del_field_event_num_students_registered__del_field_event_adding_.py rename to kcdc3/apps/classes/migrations/0014_auto__del_field_event_num_students_registered__del_field_event_adding_.py diff --git a/kcdc3/classes/migrations/0015_auto__add_field_event_registration_status.py b/kcdc3/apps/classes/migrations/0015_auto__add_field_event_registration_status.py similarity index 100% rename from kcdc3/classes/migrations/0015_auto__add_field_event_registration_status.py rename to kcdc3/apps/classes/migrations/0015_auto__add_field_event_registration_status.py diff --git a/kcdc3/classes/migrations/0016_auto__add_field_event_status__add_field_event_featured__add_field_even.py b/kcdc3/apps/classes/migrations/0016_auto__add_field_event_status__add_field_event_featured__add_field_even.py similarity index 100% rename from kcdc3/classes/migrations/0016_auto__add_field_event_status__add_field_event_featured__add_field_even.py rename to kcdc3/apps/classes/migrations/0016_auto__add_field_event_status__add_field_event_featured__add_field_even.py diff --git a/kcdc3/classes/migrations/0017_auto__add_field_event_type.py b/kcdc3/apps/classes/migrations/0017_auto__add_field_event_type.py similarity index 100% rename from kcdc3/classes/migrations/0017_auto__add_field_event_type.py rename to kcdc3/apps/classes/migrations/0017_auto__add_field_event_type.py diff --git a/kcdc3/classes/migrations/0018_auto__chg_field_event_location_address1__chg_field_event_location_addr.py b/kcdc3/apps/classes/migrations/0018_auto__chg_field_event_location_address1__chg_field_event_location_addr.py similarity index 100% rename from kcdc3/classes/migrations/0018_auto__chg_field_event_location_address1__chg_field_event_location_addr.py rename to kcdc3/apps/classes/migrations/0018_auto__chg_field_event_location_address1__chg_field_event_location_addr.py diff --git a/kcdc3/classes/migrations/0019_auto__del_field_event_location_description__add_field_event_location_n.py b/kcdc3/apps/classes/migrations/0019_auto__del_field_event_location_description__add_field_event_location_n.py similarity index 100% rename from kcdc3/classes/migrations/0019_auto__del_field_event_location_description__add_field_event_location_n.py rename to kcdc3/apps/classes/migrations/0019_auto__del_field_event_location_description__add_field_event_location_n.py diff --git a/kcdc3/classes/migrations/0020_auto__add_session.py b/kcdc3/apps/classes/migrations/0020_auto__add_session.py similarity index 100% rename from kcdc3/classes/migrations/0020_auto__add_session.py rename to kcdc3/apps/classes/migrations/0020_auto__add_session.py diff --git a/kcdc3/classes/migrations/0021_auto__add_field_session_email_reminder_days.py b/kcdc3/apps/classes/migrations/0021_auto__add_field_session_email_reminder_days.py similarity index 100% rename from kcdc3/classes/migrations/0021_auto__add_field_session_email_reminder_days.py rename to kcdc3/apps/classes/migrations/0021_auto__add_field_session_email_reminder_days.py diff --git a/kcdc3/classes/migrations/0022_auto__add_field_event_session.py b/kcdc3/apps/classes/migrations/0022_auto__add_field_event_session.py similarity index 100% rename from kcdc3/classes/migrations/0022_auto__add_field_event_session.py rename to kcdc3/apps/classes/migrations/0022_auto__add_field_event_session.py diff --git a/kcdc3/classes/migrations/0023_auto__add_location__del_field_event_location_address1__del_field_event.py b/kcdc3/apps/classes/migrations/0023_auto__add_location__del_field_event_location_address1__del_field_event.py similarity index 100% rename from kcdc3/classes/migrations/0023_auto__add_location__del_field_event_location_address1__del_field_event.py rename to kcdc3/apps/classes/migrations/0023_auto__add_location__del_field_event_location_address1__del_field_event.py diff --git a/kcdc3/classes/migrations/0024_auto__add_field_event_teacher_text.py b/kcdc3/apps/classes/migrations/0024_auto__add_field_event_teacher_text.py similarity index 100% rename from kcdc3/classes/migrations/0024_auto__add_field_event_teacher_text.py rename to kcdc3/apps/classes/migrations/0024_auto__add_field_event_teacher_text.py diff --git a/kcdc3/classes/migrations/0025_auto__chg_field_event_teacher_text.py b/kcdc3/apps/classes/migrations/0025_auto__chg_field_event_teacher_text.py similarity index 100% rename from kcdc3/classes/migrations/0025_auto__chg_field_event_teacher_text.py rename to kcdc3/apps/classes/migrations/0025_auto__chg_field_event_teacher_text.py diff --git a/kcdc3/classes/migrations/0026_auto__add_field_location_hint.py b/kcdc3/apps/classes/migrations/0026_auto__add_field_location_hint.py similarity index 100% rename from kcdc3/classes/migrations/0026_auto__add_field_location_hint.py rename to kcdc3/apps/classes/migrations/0026_auto__add_field_location_hint.py diff --git a/kcdc3/classes/migrations/0027_auto__add_bio.py b/kcdc3/apps/classes/migrations/0027_auto__add_bio.py similarity index 100% rename from kcdc3/classes/migrations/0027_auto__add_bio.py rename to kcdc3/apps/classes/migrations/0027_auto__add_bio.py diff --git a/kcdc3/classes/migrations/0028_auto__add_field_event_end_time__add_field_bio_website.py b/kcdc3/apps/classes/migrations/0028_auto__add_field_event_end_time__add_field_bio_website.py similarity index 100% rename from kcdc3/classes/migrations/0028_auto__add_field_event_end_time__add_field_bio_website.py rename to kcdc3/apps/classes/migrations/0028_auto__add_field_event_end_time__add_field_bio_website.py diff --git a/kcdc3/classes/migrations/0029_auto__add_field_session_sidebar_text.py b/kcdc3/apps/classes/migrations/0029_auto__add_field_session_sidebar_text.py similarity index 100% rename from kcdc3/classes/migrations/0029_auto__add_field_session_sidebar_text.py rename to kcdc3/apps/classes/migrations/0029_auto__add_field_session_sidebar_text.py diff --git a/kcdc3/apps/classes/migrations/0029_auto__add_role__del_field_bio_description__add_field_bio_title__add_fi.py b/kcdc3/apps/classes/migrations/0029_auto__add_role__del_field_bio_description__add_field_bio_title__add_fi.py new file mode 100644 index 00000000..386dcb90 --- /dev/null +++ b/kcdc3/apps/classes/migrations/0029_auto__add_role__del_field_bio_description__add_field_bio_title__add_fi.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Role' + db.create_table('classes_role', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=48)), + ('description', self.gf('django.db.models.fields.TextField')(blank=True)), + ('sort_order', self.gf('django.db.models.fields.IntegerField')(default=50, null=True, blank=True)), + )) + db.send_create_signal('classes', ['Role']) + + # Deleting field 'Bio.description' + db.delete_column('classes_bio', 'description') + + # Adding field 'Bio.title' + db.add_column('classes_bio', 'title', + self.gf('django.db.models.fields.CharField')(default='', max_length=100, blank=True), + keep_default=False) + + # Adding field 'Bio.staff_description' + db.add_column('classes_bio', 'staff_description', + self.gf('django.db.models.fields.TextField')(default='', blank=True), + keep_default=False) + + # Adding field 'Bio.role' + db.add_column('classes_bio', 'role', + self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='role', null=True, on_delete=models.SET_NULL, to=orm['classes.Role']), + keep_default=False) + + + def backwards(self, orm): + # Deleting model 'Role' + db.delete_table('classes_role') + + # Adding field 'Bio.description' + db.add_column('classes_bio', 'description', + self.gf('django.db.models.fields.TextField')(default='', blank=True), + keep_default=False) + + # Deleting field 'Bio.title' + db.delete_column('classes_bio', 'title') + + # Deleting field 'Bio.staff_description' + db.delete_column('classes_bio', 'staff_description') + + # Deleting field 'Bio.role' + db.delete_column('classes_bio', 'role_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.event': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Event'}, + 'additional_dates_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'details': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email_reminder_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_welcome_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}), + 'facilitators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'facilitators'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'location'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Location']"}), + 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_students': ('django.db.models.fields.IntegerField', [], {'default': '999', 'null': 'True', 'blank': 'True'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'AUTO'", 'max_length': '7'}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'session'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Session']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'students': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'students'", 'to': "orm['auth.User']", 'through': "orm['classes.Registration']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'teacher_bios': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'event'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'teacher_text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'teachers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'teachers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'CLASS'", 'max_length': '9'}), + 'waitlist_status': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'classes.location': { + 'Meta': {'object_name': 'Location'}, + 'address1': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'address2': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "'Washington'", 'max_length': '60', 'blank': 'True'}), + 'hint': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'neighborhood': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'show_exact': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'DC'", 'max_length': '2', 'blank': 'True'}), + 'zip': ('django.db.models.fields.CharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'classes.registration': { + 'Meta': {'ordering': "['date_registered']", 'object_name': 'Registration'}, + 'attended': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'cancelled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_cancelled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_registered': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['classes.Event']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'waitlist': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'classes.session': { + 'Meta': {'ordering': "['slug']", 'object_name': 'Session'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder_days': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'long_title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'PREVENT'", 'max_length': '7'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['classes'] \ No newline at end of file diff --git a/kcdc3/apps/classes/migrations/0030_auto__add_field_bio_description.py b/kcdc3/apps/classes/migrations/0030_auto__add_field_bio_description.py new file mode 100644 index 00000000..8e1a2174 --- /dev/null +++ b/kcdc3/apps/classes/migrations/0030_auto__add_field_bio_description.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Bio.description' + db.add_column('classes_bio', 'description', + self.gf('django.db.models.fields.TextField')(default='', blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Bio.description' + db.delete_column('classes_bio', 'description') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.event': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Event'}, + 'additional_dates_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'details': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email_reminder_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_welcome_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}), + 'facilitators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'facilitators'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'location'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Location']"}), + 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_students': ('django.db.models.fields.IntegerField', [], {'default': '999', 'null': 'True', 'blank': 'True'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'AUTO'", 'max_length': '7'}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'session'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Session']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'students': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'students'", 'to': "orm['auth.User']", 'through': "orm['classes.Registration']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'teacher_bios': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'event'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'teacher_text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'teachers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'teachers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'CLASS'", 'max_length': '9'}), + 'waitlist_status': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'classes.location': { + 'Meta': {'object_name': 'Location'}, + 'address1': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'address2': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "'Washington'", 'max_length': '60', 'blank': 'True'}), + 'hint': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'neighborhood': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'show_exact': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'DC'", 'max_length': '2', 'blank': 'True'}), + 'zip': ('django.db.models.fields.CharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'classes.registration': { + 'Meta': {'ordering': "['date_registered']", 'object_name': 'Registration'}, + 'attended': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'cancelled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_cancelled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_registered': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['classes.Event']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'waitlist': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'classes.session': { + 'Meta': {'ordering': "['slug']", 'object_name': 'Session'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder_days': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'long_title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'PREVENT'", 'max_length': '7'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['classes'] \ No newline at end of file diff --git a/kcdc3/classes/migrations/0030_auto__add_field_registration_date_promoted__add_field_registration_lat.py b/kcdc3/apps/classes/migrations/0030_auto__add_field_registration_date_promoted__add_field_registration_lat.py similarity index 100% rename from kcdc3/classes/migrations/0030_auto__add_field_registration_date_promoted__add_field_registration_lat.py rename to kcdc3/apps/classes/migrations/0030_auto__add_field_registration_date_promoted__add_field_registration_lat.py diff --git a/kcdc3/apps/classes/migrations/0031_auto__add_field_registration_date_promoted__add_field_registration_lat.py b/kcdc3/apps/classes/migrations/0031_auto__add_field_registration_date_promoted__add_field_registration_lat.py new file mode 100644 index 00000000..77332dd3 --- /dev/null +++ b/kcdc3/apps/classes/migrations/0031_auto__add_field_registration_date_promoted__add_field_registration_lat.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Bio.image' + db.add_column('classes_bio', 'image', + self.gf('django.db.models.fields.files.ImageField')(max_length=100, null=True, blank=True), + keep_default=False) + + def backwards(self, orm): + # Deleting field 'Bio.image' + db.delete_column('classes_bio', 'image') + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.event': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Event'}, + 'additional_dates_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'details': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email_reminder_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_welcome_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}), + 'facilitators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'facilitators'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'location'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Location']"}), + 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_students': ('django.db.models.fields.IntegerField', [], {'default': '999', 'null': 'True', 'blank': 'True'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'AUTO'", 'max_length': '7'}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'session'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Session']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'students': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'students'", 'to': "orm['auth.User']", 'through': "orm['classes.Registration']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'teacher_bios': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'event'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'teacher_text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'teachers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'teachers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'CLASS'", 'max_length': '9'}), + 'waitlist_status': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'classes.location': { + 'Meta': {'object_name': 'Location'}, + 'address1': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'address2': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "'Washington'", 'max_length': '60', 'blank': 'True'}), + 'hint': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'neighborhood': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'show_exact': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'DC'", 'max_length': '2', 'blank': 'True'}), + 'zip': ('django.db.models.fields.CharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'classes.registration': { + 'Meta': {'ordering': "['date_registered']", 'object_name': 'Registration'}, + 'attended': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'cancelled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_cancelled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_promoted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_registered': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['classes.Event']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'late_promotion': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'waitlist': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'classes.session': { + 'Meta': {'ordering': "['slug']", 'object_name': 'Session'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder_days': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'long_title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'PREVENT'", 'max_length': '7'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['classes'] \ No newline at end of file diff --git a/kcdc3/apps/classes/migrations/0032_auto__add_field_role_extended_description.py b/kcdc3/apps/classes/migrations/0032_auto__add_field_role_extended_description.py new file mode 100644 index 00000000..20eaa095 --- /dev/null +++ b/kcdc3/apps/classes/migrations/0032_auto__add_field_role_extended_description.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Role.extended_description' + db.add_column('classes_role', 'extended_description', + self.gf('django.db.models.fields.TextField')(default='', blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Role.extended_description' + db.delete_column('classes_role', 'extended_description') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.event': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Event'}, + 'additional_dates_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'details': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email_reminder_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_welcome_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}), + 'facilitators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'facilitators'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'location'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Location']"}), + 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_students': ('django.db.models.fields.IntegerField', [], {'default': '999', 'null': 'True', 'blank': 'True'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'AUTO'", 'max_length': '7'}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'session'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Session']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'students': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'students'", 'to': "orm['auth.User']", 'through': "orm['classes.Registration']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'teacher_bios': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'event'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'teacher_text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'teachers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'teachers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'CLASS'", 'max_length': '9'}), + 'waitlist_status': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'classes.location': { + 'Meta': {'object_name': 'Location'}, + 'address1': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'address2': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "'Washington'", 'max_length': '60', 'blank': 'True'}), + 'hint': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'neighborhood': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'show_exact': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'DC'", 'max_length': '2', 'blank': 'True'}), + 'zip': ('django.db.models.fields.CharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'classes.registration': { + 'Meta': {'ordering': "['date_registered']", 'object_name': 'Registration'}, + 'attended': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'cancelled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_cancelled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_promoted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_registered': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['classes.Event']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'late_promotion': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'waitlist': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'extended_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'classes.session': { + 'Meta': {'ordering': "['slug']", 'object_name': 'Session'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder_days': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'long_title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'PREVENT'", 'max_length': '7'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['classes'] \ No newline at end of file diff --git a/kcdc3/apps/classes/migrations/0033_auto__add_field_event_location_text.py b/kcdc3/apps/classes/migrations/0033_auto__add_field_event_location_text.py new file mode 100644 index 00000000..7623ea85 --- /dev/null +++ b/kcdc3/apps/classes/migrations/0033_auto__add_field_event_location_text.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Event.location_text' + db.add_column('classes_event', 'location_text', + self.gf('django.db.models.fields.CharField')(default='', max_length=300, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Event.location_text' + db.delete_column('classes_event', 'location_text') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.event': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Event'}, + 'additional_dates_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'details': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email_reminder_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_welcome_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}), + 'facilitators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'facilitators'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'location'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Location']"}), + 'location_text': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_students': ('django.db.models.fields.IntegerField', [], {'default': '999', 'null': 'True', 'blank': 'True'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'AUTO'", 'max_length': '7'}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'session'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Session']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'students': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'students'", 'to': "orm['auth.User']", 'through': "orm['classes.Registration']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'teacher_bios': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'event'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'teacher_text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'teachers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'teachers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'CLASS'", 'max_length': '9'}), + 'waitlist_status': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'classes.location': { + 'Meta': {'object_name': 'Location'}, + 'address1': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'address2': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "'Washington'", 'max_length': '60', 'blank': 'True'}), + 'hint': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'neighborhood': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'show_exact': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'DC'", 'max_length': '2', 'blank': 'True'}), + 'zip': ('django.db.models.fields.CharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'classes.registration': { + 'Meta': {'ordering': "['date_registered']", 'object_name': 'Registration'}, + 'attended': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'cancelled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_cancelled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_promoted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_registered': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['classes.Event']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'late_promotion': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'waitlist': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'extended_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'classes.session': { + 'Meta': {'ordering': "['slug']", 'object_name': 'Session'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder_days': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'long_title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'PREVENT'", 'max_length': '7'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['classes'] \ No newline at end of file diff --git a/kcdc3/apps/classes/migrations/0034_auto__add_unique_bio_name.py b/kcdc3/apps/classes/migrations/0034_auto__add_unique_bio_name.py new file mode 100644 index 00000000..cae1e5e3 --- /dev/null +++ b/kcdc3/apps/classes/migrations/0034_auto__add_unique_bio_name.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding unique constraint on 'Bio', fields ['name'] + db.create_unique('classes_bio', ['name']) + + + def backwards(self, orm): + # Removing unique constraint on 'Bio', fields ['name'] + db.delete_unique('classes_bio', ['name']) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.event': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Event'}, + 'additional_dates_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'details': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email_reminder_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_welcome_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}), + 'facilitators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'facilitators'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'location'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Location']"}), + 'location_text': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_students': ('django.db.models.fields.IntegerField', [], {'default': '999', 'null': 'True', 'blank': 'True'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'AUTO'", 'max_length': '7'}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'session'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Session']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'students': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'students'", 'to': "orm['auth.User']", 'through': "orm['classes.Registration']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'teacher_bios': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'event'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'teacher_text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'teachers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'teachers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'CLASS'", 'max_length': '9'}), + 'waitlist_status': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'classes.location': { + 'Meta': {'object_name': 'Location'}, + 'address1': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'address2': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "'Washington'", 'max_length': '60', 'blank': 'True'}), + 'hint': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'neighborhood': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'show_exact': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'DC'", 'max_length': '2', 'blank': 'True'}), + 'zip': ('django.db.models.fields.CharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'classes.registration': { + 'Meta': {'ordering': "['date_registered']", 'object_name': 'Registration'}, + 'attended': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'cancelled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_cancelled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_promoted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_registered': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['classes.Event']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'late_promotion': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'waitlist': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'extended_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'classes.session': { + 'Meta': {'ordering': "['slug']", 'object_name': 'Session'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder_days': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'long_title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'PREVENT'", 'max_length': '7'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['classes'] \ No newline at end of file diff --git a/kcdc3/apps/classes/migrations/0035_auto__add_field_event_registration_opens.py b/kcdc3/apps/classes/migrations/0035_auto__add_field_event_registration_opens.py new file mode 100644 index 00000000..8bb2e53c --- /dev/null +++ b/kcdc3/apps/classes/migrations/0035_auto__add_field_event_registration_opens.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Event.registration_opens' + db.add_column('classes_event', 'registration_opens', + self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Event.registration_opens' + db.delete_column('classes_event', 'registration_opens') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.event': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Event'}, + 'additional_dates_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'details': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email_reminder_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_welcome_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}), + 'facilitators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'facilitators'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'location'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Location']"}), + 'location_text': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_students': ('django.db.models.fields.IntegerField', [], {'default': '999', 'null': 'True', 'blank': 'True'}), + 'registration_opens': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'AUTO'", 'max_length': '7'}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'session'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Session']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'students': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'students'", 'to': "orm['auth.User']", 'through': "orm['classes.Registration']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'teacher_bios': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'event'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'teacher_text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'teachers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'teachers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'CLASS'", 'max_length': '9'}), + 'waitlist_status': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'classes.location': { + 'Meta': {'object_name': 'Location'}, + 'address1': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'address2': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "'Washington'", 'max_length': '60', 'blank': 'True'}), + 'hint': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'neighborhood': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'show_exact': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'DC'", 'max_length': '2', 'blank': 'True'}), + 'zip': ('django.db.models.fields.CharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'classes.registration': { + 'Meta': {'ordering': "['date_registered']", 'object_name': 'Registration'}, + 'attended': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'cancelled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_cancelled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_promoted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_registered': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['classes.Event']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'late_promotion': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'waitlist': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'extended_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'classes.session': { + 'Meta': {'ordering': "['slug']", 'object_name': 'Session'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder_days': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'long_title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'PREVENT'", 'max_length': '7'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['classes'] \ No newline at end of file diff --git a/kcdc3/apps/classes/migrations/0036_auto__add_field_bio_rating__add_field_bio_comments__add_field_bio_bio_.py b/kcdc3/apps/classes/migrations/0036_auto__add_field_bio_rating__add_field_bio_comments__add_field_bio_bio_.py new file mode 100644 index 00000000..13586ffc --- /dev/null +++ b/kcdc3/apps/classes/migrations/0036_auto__add_field_bio_rating__add_field_bio_comments__add_field_bio_bio_.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Bio.rating' + db.add_column('classes_bio', 'rating', + self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), + keep_default=False) + + # Adding field 'Bio.comments' + db.add_column('classes_bio', 'comments', + self.gf('django.db.models.fields.TextField')(null=True, blank=True), + keep_default=False) + + # Adding field 'Bio.bio_email' + db.add_column('classes_bio', 'bio_email', + self.gf('django.db.models.fields.EmailField')(max_length=75, null=True, blank=True), + keep_default=False) + + # Adding field 'Session.kicker' + db.add_column('classes_session', 'kicker', + self.gf('django.db.models.fields.CharField')(default='', max_length=200, blank=True), + keep_default=False) + + # Adding field 'Session.show_sidebar_text' + db.add_column('classes_session', 'show_sidebar_text', + self.gf('django.db.models.fields.BooleanField')(default=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Bio.rating' + db.delete_column('classes_bio', 'rating') + + # Deleting field 'Bio.comments' + db.delete_column('classes_bio', 'comments') + + # Deleting field 'Bio.bio_email' + db.delete_column('classes_bio', 'bio_email') + + # Deleting field 'Session.kicker' + db.delete_column('classes_session', 'kicker') + + # Deleting field 'Session.show_sidebar_text' + db.delete_column('classes_session', 'show_sidebar_text') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'bio_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'rating': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.event': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Event'}, + 'additional_dates_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'details': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email_reminder_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_welcome_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}), + 'facilitators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'facilitators'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'location'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Location']"}), + 'location_text': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_students': ('django.db.models.fields.IntegerField', [], {'default': '999', 'null': 'True', 'blank': 'True'}), + 'registration_opens': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'AUTO'", 'max_length': '7'}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'session'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Session']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'students': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'students'", 'to': "orm['auth.User']", 'through': "orm['classes.Registration']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'teacher_bios': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'event'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'teacher_text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'teachers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'teachers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'CLASS'", 'max_length': '9'}), + 'waitlist_status': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'classes.location': { + 'Meta': {'object_name': 'Location'}, + 'address1': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'address2': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "'Washington'", 'max_length': '60', 'blank': 'True'}), + 'hint': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'neighborhood': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'show_exact': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'DC'", 'max_length': '2', 'blank': 'True'}), + 'zip': ('django.db.models.fields.CharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'classes.registration': { + 'Meta': {'ordering': "['date_registered']", 'object_name': 'Registration'}, + 'attended': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'cancelled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_cancelled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_promoted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_registered': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['classes.Event']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'late_promotion': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'waitlist': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'extended_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'classes.session': { + 'Meta': {'ordering': "['slug']", 'object_name': 'Session'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder_days': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'kicker': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'long_title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'PREVENT'", 'max_length': '7'}), + 'show_sidebar_text': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['classes'] \ No newline at end of file diff --git a/kcdc3/apps/classes/migrations/0037_auto__add_field_event_bio_text.py b/kcdc3/apps/classes/migrations/0037_auto__add_field_event_bio_text.py new file mode 100644 index 00000000..846bfff5 --- /dev/null +++ b/kcdc3/apps/classes/migrations/0037_auto__add_field_event_bio_text.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Event.bio_text' + db.add_column('classes_event', 'bio_text', + self.gf('django.db.models.fields.TextField')(default='', blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Event.bio_text' + db.delete_column('classes_event', 'bio_text') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'bio_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'rating': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.event': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Event'}, + 'additional_dates_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bio_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'details': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email_reminder_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_welcome_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}), + 'facilitators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'facilitators'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'location'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Location']"}), + 'location_text': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'main_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_students': ('django.db.models.fields.IntegerField', [], {'default': '999', 'null': 'True', 'blank': 'True'}), + 'registration_opens': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'AUTO'", 'max_length': '7'}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'session'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Session']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'students': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'students'", 'to': "orm['auth.User']", 'through': "orm['classes.Registration']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'teacher_bios': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'event'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'teacher_text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'teachers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'teachers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'CLASS'", 'max_length': '9'}), + 'waitlist_status': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'classes.location': { + 'Meta': {'object_name': 'Location'}, + 'address1': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'address2': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "'Washington'", 'max_length': '60', 'blank': 'True'}), + 'hint': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'neighborhood': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'show_exact': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'DC'", 'max_length': '2', 'blank': 'True'}), + 'zip': ('django.db.models.fields.CharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'classes.registration': { + 'Meta': {'ordering': "['date_registered']", 'object_name': 'Registration'}, + 'attended': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'cancelled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_cancelled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_promoted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_registered': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['classes.Event']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'late_promotion': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'waitlist': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'extended_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'classes.session': { + 'Meta': {'ordering': "['slug']", 'object_name': 'Session'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'documentation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_reminder_days': ('django.db.models.fields.IntegerField', [], {'default': '2'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'kicker': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'long_title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'registration_status': ('django.db.models.fields.CharField', [], {'default': "'PREVENT'", 'max_length': '7'}), + 'show_sidebar_text': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['classes'] \ No newline at end of file diff --git a/kcdc3/tmp/restart.txt b/kcdc3/apps/classes/migrations/__init__.py similarity index 100% rename from kcdc3/tmp/restart.txt rename to kcdc3/apps/classes/migrations/__init__.py diff --git a/kcdc3/classes/models.py b/kcdc3/apps/classes/models.py similarity index 70% rename from kcdc3/classes/models.py rename to kcdc3/apps/classes/models.py index 8822c6ce..0eef4ea9 100644 --- a/kcdc3/classes/models.py +++ b/kcdc3/apps/classes/models.py @@ -20,8 +20,10 @@ class Session(models.Model): ) status = models.CharField(max_length=9, choices=STATUS_CHOICES, default='CURRENT') + kicker = models.CharField('Kicker (for Classes page hed)', max_length=200, blank=True) description = models.TextField('Intro/Description', blank=True) sidebar_text = models.TextField('Sidebar', blank=True) + show_sidebar_text = models.BooleanField(default=True) documentation = models.TextField('Documentation/Extended Text', blank=True) REGISTRATION_STATUS_CHOICES = ( @@ -37,6 +39,21 @@ class Meta: def __unicode__(self): return self.title + # Report regular, published classes + def get_live_classes(self): + return Event.objects.filter(session=self,status="PUBLISHED",type="CLASS") + + # Report regular, published classes + def get_registrations(self): + return Registration.objects.filter(event__session=self,cancelled=False,waitlist=False) + + # Report regular, published classes + def get_attended_registrations(self): + return Registration.objects.filter(event__session=self,cancelled=False,attended=True) + + + + class Location(models.Model): name = models.CharField('Description', max_length=100, blank=True) neighborhood = models.CharField('Neighborhood', max_length=100, blank=True) @@ -50,14 +67,75 @@ class Location(models.Model): def __unicode__(self): return self.name + + +class Role(models.Model): + + name = models.CharField('Name', max_length=48) + description = models.TextField(blank=True) + extended_description = models.TextField(blank=True) + sort_order = models.IntegerField(blank=True, null=True, default=50) + + class Meta: + ordering = ['sort_order'] + verbose_name=u'Staff Role' + + def __unicode__(self): + return self.name + + + class Bio(models.Model): - name = models.CharField('Name', max_length=100, blank=False) + + user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL, related_name='user') + + # Basic information, used in class descriptions and elsewhere by default + name = models.CharField('Name', max_length=100, blank=False, unique=True) description = models.TextField('Bio text', blank=True) website = models.URLField(blank=True) - user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL, related_name='user') + image = models.ImageField('Image (60x60px)', upload_to='bio', blank=True, null=True) + + # Fields for staff bios + title = models.CharField(max_length=100, blank=True) + staff_description = models.TextField('Staff bio text', blank=True) + role = models.ForeignKey(Role, blank=True, null=True, on_delete=models.SET_NULL, related_name='role') + + # Fields for internal use + rating = models.IntegerField('Rating', null=True, blank=True) + comments = models.TextField('Comments', null=True, blank=True) + bio_email = models.EmailField(null=True, blank=True) + + # Provide a filled-out description if one is available + def get_staff_description(self): + + if self.staff_description: + return self.staff_description + else: + return self.description + + # Provide email from user; otherwise use bio email + def get_email(self): + + if self.user: + return self.user.email + elif self.bio_email: + return self.bio_email + else: + return "" + + # Which classes is this person teaching? + def get_classes(self): + + return Event.objects.filter(teacher_bios=self) + + class Meta: + verbose_name=u'Staff/Teacher Bio' + def __unicode__(self): return self.name + + # an Event is a single class or other event class Event(models.Model): @@ -90,8 +168,8 @@ class Event(models.Model): summary = models.TextField(blank=True) description = models.TextField(blank=True) details = models.TextField('Pre-class details', blank=True) - thumbnail = models.ImageField(upload_to='event_images', blank=True, null=True) - main_image = models.ImageField(upload_to='event_images', blank=True, null=True) + thumbnail = models.ImageField('Thumbnail (max 432px wide)', upload_to='event_images', blank=True, null=True) + main_image = models.ImageField('Main image (max 660px wide)', upload_to='event_images', blank=True, null=True) email_welcome_text = models.TextField('Extra text for welcome email', blank=True) email_reminder = models.BooleanField('Send reminder email?', default=True) @@ -109,15 +187,24 @@ class Event(models.Model): ('HIDE', 'Hide forms'), ) registration_status = models.CharField(max_length=7, choices=REGISTRATION_STATUS_CHOICES, default='AUTO') + registration_opens = models.DateTimeField(blank=True, null=True) teacher_bios = models.ManyToManyField(Bio, blank=True, null=True, related_name='event') - teacher_text = models.CharField('Teacher (text)', max_length=200, blank=True) teachers = models.ManyToManyField(User, blank=True, null=True, related_name='teachers') facilitators = models.ManyToManyField(User, blank=True, null=True, related_name='facilitators') students = models.ManyToManyField(User, through='Registration', blank=True, null=True, related_name='students') + + # optional, course-by-course bio text + bio_text = models.TextField('Alternative teacher bio text for this class',blank=True) + + # legacy fields + teacher_text = models.CharField('Teacher (text)', max_length=200, blank=True) + location_text = models.CharField('Location (text)', max_length=300, blank=True) class Meta: ordering = ['-date'] + verbose_name=u'Class/Event' + verbose_name_plural=u'Classes/Events' def __unicode__(self): return self.title @@ -161,11 +248,14 @@ def is_registration_open(self): return False elif self.registration_status == 'ALLOW': return True - elif self.registration_status == 'AUTO' and self.session.registration_status == 'ALLOW': - return True + elif self.registration_status == 'AUTO' and self.registration_opens: + if self.registration_opens < datetime.datetime.now(): + return True + else: + return False else: return False - + def is_late_promotion(self): """ True if a user is being promoted from the waitlist close to the class meeting time.""" diff --git a/kcdc3/templates/classes/email_cancelled.txt b/kcdc3/apps/classes/templates/classes/email_cancelled.txt similarity index 100% rename from kcdc3/templates/classes/email_cancelled.txt rename to kcdc3/apps/classes/templates/classes/email_cancelled.txt diff --git a/kcdc3/templates/classes/email_cancelled_subject.txt b/kcdc3/apps/classes/templates/classes/email_cancelled_subject.txt similarity index 100% rename from kcdc3/templates/classes/email_cancelled_subject.txt rename to kcdc3/apps/classes/templates/classes/email_cancelled_subject.txt diff --git a/kcdc3/templates/classes/email_promoted.txt b/kcdc3/apps/classes/templates/classes/email_promoted.txt similarity index 90% rename from kcdc3/templates/classes/email_promoted.txt rename to kcdc3/apps/classes/templates/classes/email_promoted.txt index 919376d6..225ebe09 100644 --- a/kcdc3/templates/classes/email_promoted.txt +++ b/kcdc3/apps/classes/templates/classes/email_promoted.txt @@ -5,7 +5,7 @@ It’s your lucky day! There is a space for you in a Knowledge Commons DC class. We've added you to the class roster. We hope that you can attend. If you can't, please cancel your registration at: - http://c.knowledgecommonsdc.org/classes/{{ slug }} + http://knowledgecommonsdc.org/classes/{{ slug }} {% if is_late_promotion %}We realize that you are receiving this message quite late. We want to give you a chance to attend, but we understand @@ -19,7 +19,7 @@ DATE: {{ date|date:"l, j F Y, P" }} {% if end_time %}to {{ end_time|date:"P" }}{ {{ additional_dates_text|striptags }}{% endif %} {% if email_welcome_text %}ADDITIONAL INFORMATION: -{{ email_welcome_text }}{% endif %} +{{ email_welcome_text|safe }}{% endif %} LOCATION: {% ifnotequal location_name location_address1 %}{{ location_name }}{% endifnotequal %} @@ -31,7 +31,7 @@ LOCATION: {{ location_hint }}{% endif %} COURSE WEBPAGE: -http://c.knowledgecommonsdc.org/classes/{{ slug }} +http://knowledgecommonsdc.org/classes/{{ slug }} If you'd like to participate more actively in this gift economy, drop us a line at knowledgecomonsdc@gmail.com to volunteer. diff --git a/kcdc3/templates/classes/email_promoted_subject.txt b/kcdc3/apps/classes/templates/classes/email_promoted_subject.txt similarity index 100% rename from kcdc3/templates/classes/email_promoted_subject.txt rename to kcdc3/apps/classes/templates/classes/email_promoted_subject.txt diff --git a/kcdc3/templates/classes/email_registered.txt b/kcdc3/apps/classes/templates/classes/email_registered.txt similarity index 93% rename from kcdc3/templates/classes/email_registered.txt rename to kcdc3/apps/classes/templates/classes/email_registered.txt index b65c9bcb..619308da 100644 --- a/kcdc3/templates/classes/email_registered.txt +++ b/kcdc3/apps/classes/templates/classes/email_registered.txt @@ -6,7 +6,7 @@ advance as you can. Many of our classes have waitlists, and our teachers need to know how many students to expect. You can cancel your registration at: -http://c.knowledgecommonsdc.org/classes/{{ slug }} +http://knowledgecommonsdc.org/classes/{{ slug }} CLASS: {{ title }} @@ -27,7 +27,7 @@ LOCATION: {{ location_hint }}{% endif %} COURSE WEBPAGE: -http://c.knowledgecommonsdc.org/classes/{{ slug }} +http://knowledgecommonsdc.org/classes/{{ slug }} Please email us if you need special accommodations. diff --git a/kcdc3/templates/classes/email_registered_subject.txt b/kcdc3/apps/classes/templates/classes/email_registered_subject.txt similarity index 100% rename from kcdc3/templates/classes/email_registered_subject.txt rename to kcdc3/apps/classes/templates/classes/email_registered_subject.txt diff --git a/kcdc3/templates/classes/email_waitlisted.txt b/kcdc3/apps/classes/templates/classes/email_waitlisted.txt similarity index 83% rename from kcdc3/templates/classes/email_waitlisted.txt rename to kcdc3/apps/classes/templates/classes/email_waitlisted.txt index 716143cf..117a12ef 100644 --- a/kcdc3/templates/classes/email_waitlisted.txt +++ b/kcdc3/apps/classes/templates/classes/email_waitlisted.txt @@ -8,11 +8,11 @@ DATE: {{ date|date:"l, j F Y, P" }} {% if end_time %}to {{ end_time|date:"P" }}{ {% if additional_dates_text %}ADDITIONAL MEETINGS: {{ additional_dates_text|striptags }}{% endif %} COURSE WEBPAGE: -http://c.knowledgecommonsdc.org/classes/{{ slug }} +http://knowledgecommonsdc.org/classes/{{ slug }} If you would no longer like to remain on the waitlist, you can withdraw at: - http://c.knowledgecommonsdc.org/classes/{{ slug }} + http://knowledgecommonsdc.org/classes/{{ slug }} Onwards and upwards! diff --git a/kcdc3/templates/classes/email_waitlisted_subject.txt b/kcdc3/apps/classes/templates/classes/email_waitlisted_subject.txt similarity index 100% rename from kcdc3/templates/classes/email_waitlisted_subject.txt rename to kcdc3/apps/classes/templates/classes/email_waitlisted_subject.txt diff --git a/kcdc3/templates/classes/event_archive.html b/kcdc3/apps/classes/templates/classes/event_archive.html similarity index 84% rename from kcdc3/templates/classes/event_archive.html rename to kcdc3/apps/classes/templates/classes/event_archive.html index 76bb8002..3a7d1260 100644 --- a/kcdc3/templates/classes/event_archive.html +++ b/kcdc3/apps/classes/templates/classes/event_archive.html @@ -43,13 +43,18 @@

{% endifchanged %} {% endifchanged %} -
+

- + {% if event.type == "EXTERNAL" %}Partner Event:{% endif %} {{ event.title }}

+ + {% if event.type == "EVENT" %} + KCDC Event + {% endif %} + time {{ event.date|date:"P" }} {% if event.has_passed %} @@ -72,9 +77,11 @@

{% endif %}

+ {% if event.thumbnail %}{% endif %} {{ event.summary|safe }}

user Instructor{{ event.num_teachers|pluralize }}: {% for bio in event.teacher_bios.all %}{% if not forloop.first %}, {% endif %}{{ bio.name }}{% endfor %} + {% if event.teacher_text %}{{ event.teacher_text|safe }}{% endif %}

@@ -101,4 +108,4 @@

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/kcdc3/templates/classes/event_base.html b/kcdc3/apps/classes/templates/classes/event_base.html similarity index 82% rename from kcdc3/templates/classes/event_base.html rename to kcdc3/apps/classes/templates/classes/event_base.html index 2b09a1dc..921ad8bf 100644 --- a/kcdc3/templates/classes/event_base.html +++ b/kcdc3/apps/classes/templates/classes/event_base.html @@ -11,7 +11,6 @@ {% for session in sessions reversed %}
  • {{ session.title }}
  • {% endfor %} -
  • More sessions…
  • diff --git a/kcdc3/templates/classes/event_detail.html b/kcdc3/apps/classes/templates/classes/event_detail.html similarity index 72% rename from kcdc3/templates/classes/event_detail.html rename to kcdc3/apps/classes/templates/classes/event_detail.html index 1a0d71fc..12bbaffc 100644 --- a/kcdc3/templates/classes/event_detail.html +++ b/kcdc3/apps/classes/templates/classes/event_detail.html @@ -1,5 +1,7 @@ {% extends "base.html" %} +{% load pinata_filters %} + {% block title %} / {{ event.title }} {% endblock %} @@ -7,13 +9,18 @@ {% block content %}
    -

    {{ event.title }}

    + {% if event.type == "EXTERNAL" %}

    Partner Event

    {% endif %} +

    {{ event.title | smartquotes | safe }}

    + {% if event.type == "EVENT" %} + KCDC Event + {% endif %} + date {{ event.date|date:"l, j F Y" }} @@ -25,37 +32,50 @@

    {{ event.title }}

    👤 {% for bio in event.teacher_bios.all %}{% if not forloop.first %}, {% endif %}{{ bio.name }}{% endfor %} + {% if event.teacher_text %}{{ event.teacher_text|safe }}{% endif %} - + {% if event.location %} 🏢 {{ event.location }} - + {% endif %} +

    {% if event.has_passed %} Past - {% elif event.registration_status == "ALLOW" %} - {% if event.registration_count >= event.max_students %} - {% if event.waitlist_status %} + {% elif event.registration_status == "HIDE" %} + + {% else %} + {% if event.registration_status == "AUTO" and not event.is_registration_open %} + + {% elif event.registration_status == "PREVENT" %} + Registration is closed + {% elif event.registration_count >= event.max_students %} + {% if event.waitlist_status %} Waitlist {% else %} Class is full {% endif %} - {% else %} + {% else%} Registration is open {% endif %} - {% elif event.registration_status == 'PREVENT' %} - Registration is closed - {% endif %} + {% endif %}

    - {% if event.main_image %}

    event preview image

    {% endif %} + {% if event.main_image %}

    event preview image

    {% endif %} {{ event.description|safe }} + {% if event.type == "EXTERNAL" %} +

    + This is not a KCDC event. It is produced by another organization. + We include it because we think that it will be of interest to our students. +

    + {% endif %} + {% if event.documentation %}

    Documentation

    {{ event.documentation|safe }} @@ -67,6 +87,7 @@

    Additional meetings

    {% endif %}

    Location

    + {% if event.location_text %}

    {{ event.location_text|safe }}

    {% endif %}

    {% ifnotequal event.location.name event.location.address1 %}{{ event.location.name }}
    {% endifnotequal %} {% if event.location.address1 and event.location.show_exact %} @@ -95,8 +116,8 @@

    Past event

    {% elif event.registration_status == "HIDE" %} {% else %} - {% if event.registration_status == "AUTO" %} -

    Registration is not yet open.

    + {% if event.registration_status == "AUTO" and not event.is_registration_open %} +

    Registration for this class opens {{ event.registration_opens|date:"l, j F, P" }}

    {% elif event.registration_status == "PREVENT" %}

    Registration is closed.

    {% elif registration_count >= event.max_students %} @@ -105,7 +126,7 @@

    This class is full, but there is a waitlist available.

    {% else %}

    This class is full.

    {% endif %} - {% else %} + {% else%}

    Register

    {% endif %} {% endif %} @@ -166,10 +187,14 @@

    Register

    diff --git a/kcdc3/templates/classes/event_list.html b/kcdc3/apps/classes/templates/classes/event_list.html similarity index 64% rename from kcdc3/templates/classes/event_list.html rename to kcdc3/apps/classes/templates/classes/event_list.html index 6401673e..c439bb26 100644 --- a/kcdc3/templates/classes/event_list.html +++ b/kcdc3/apps/classes/templates/classes/event_list.html @@ -1,12 +1,18 @@ {% extends "classes/event_base.html" %} +{% load pinata_filters %} + {% block title %} / Classes and Events{% endblock %} {% block content %}

    Classes + Events

    -

    As always, KCDC classes are free – and they fill up fast!

    + {% for session in selected_session %} + {% if forloop.last %} +

    {{ session.kicker|safe }}

    + {% endif %} + {% endfor %}
    @@ -43,33 +49,45 @@

    Upcoming classes

    {% endifchanged %} -
    +

    - - {{ event.title }} + {% if event.type == "EXTERNAL" %}Partner Event:{% endif %} + {{ event.title | smartquotes | safe }}

    + + {% if event.type == "EVENT" %} + KCDC Event + {% endif %} + time {{ event.date|date:"P" }} - + {% if event.has_passed %} - {% elif event.registration_status == "ALLOW" %} - {% if event.registration_count >= event.max_students %} - {% if event.waitlist_status %} + {% elif event.registration_status == "HIDE" %} + + {% else %} + {% if event.registration_status == "AUTO" and not event.is_registration_open %} + + {% elif event.registration_status == "PREVENT" %} + Registration is closed + {% elif event.registration_count >= event.max_students %} + {% if event.waitlist_status %} Waitlist {% else %} Class is full {% endif %} - {% else %} + {% else%} Registration is open {% endif %} - {% elif event.registration_status == 'PREVENT' %} - Registration is closed - {% endif %} - + {% endif %}

    + + {% if event.thumbnail %}{% endif %} + {{ event.summary|safe }} +

    user Instructor{{ event.num_teachers|pluralize }}: {% for bio in event.teacher_bios.all %}{% if not forloop.first %}, {% endif %}{{ bio.name }}{% endfor %}

    @@ -102,21 +120,30 @@

    Recent classes

    {% endifchanged %} {% endifchanged %} -
    +

    - - {{ event.title }} + {% if event.type == "EXTERNAL" %}Partner Event:{% endif %} + {{ event.title | smartquotes | safe }}

    + + {% if event.type == "EVENT" %} + KCDC Event + {% endif %} + time {{ event.date|date:"P" }} + {% if event.documentation %} 📷 Documentation {% endif %} +

    + {% if event.thumbnail %}{% endif %} {{ event.summary|safe }}

    user Instructor{{ event.num_teachers|pluralize }}: {% for bio in event.teacher_bios.all %}{% if not forloop.first %}, {% endif %}{{ bio.name }}{% endfor %} + {% if event.teacher_text %}{{ event.teacher_text|safe }}{% endif %}

    @@ -139,9 +166,11 @@

    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/kcdc3/templates/classes/event_session.html b/kcdc3/apps/classes/templates/classes/event_session.html similarity index 100% rename from kcdc3/templates/classes/event_session.html rename to kcdc3/apps/classes/templates/classes/event_session.html diff --git a/kcdc3/templates/classes/facilitator_event_detail.html b/kcdc3/apps/classes/templates/classes/facilitator_event_detail.html similarity index 89% rename from kcdc3/templates/classes/facilitator_event_detail.html rename to kcdc3/apps/classes/templates/classes/facilitator_event_detail.html index 47ea948c..c140c5cb 100644 --- a/kcdc3/templates/classes/facilitator_event_detail.html +++ b/kcdc3/apps/classes/templates/classes/facilitator_event_detail.html @@ -16,6 +16,9 @@

    Registered students ({{ registration_count }})

    email + + phone + date registered @@ -34,6 +37,9 @@

    Registered students ({{ registration_count }})

    {{ registration.student.email }} + + {{ registration.student.extendedprofile.phone_number }} + {{ registration.date_registered }} @@ -65,6 +71,9 @@

    Waitlisted students ({{ waitlist_count }})

    email + + phone + date registered @@ -80,6 +89,9 @@

    Waitlisted students ({{ waitlist_count }})

    {{ registration.student.email }} + + {{ registration.student.extendedprofile.phone_number }} + {{ registration.date_registered }} diff --git a/kcdc3/templates/classes/registration_list.html b/kcdc3/apps/classes/templates/classes/registration_list.html similarity index 100% rename from kcdc3/templates/classes/registration_list.html rename to kcdc3/apps/classes/templates/classes/registration_list.html diff --git a/kcdc3/templates/classes/response.html b/kcdc3/apps/classes/templates/classes/response.html similarity index 100% rename from kcdc3/templates/classes/response.html rename to kcdc3/apps/classes/templates/classes/response.html diff --git a/kcdc3/apps/classes/templates/classes/staff.html b/kcdc3/apps/classes/templates/classes/staff.html new file mode 100644 index 00000000..d68f02d2 --- /dev/null +++ b/kcdc3/apps/classes/templates/classes/staff.html @@ -0,0 +1,92 @@ + + + + + + Knowledge Commons DC {% block title %}{% endblock %} + + + + + + + + + + + + + + + + {% block pre_content %}{% endblock %} + + {% block content_shell %} +
    + + {% block content %}{% endblock %} + {% block sidebar %}{% endblock %} + +
    + {% endblock %} + + {% block post_content %}{% endblock %} + + {% comment %} +
    + +
    + {% endcomment %} + + + + + + + + + {% block local_scripts %}{% endblock %} + + + diff --git a/kcdc3/apps/classes/templates/classes/staff_registration_list.html b/kcdc3/apps/classes/templates/classes/staff_registration_list.html new file mode 100644 index 00000000..315d5d5b --- /dev/null +++ b/kcdc3/apps/classes/templates/classes/staff_registration_list.html @@ -0,0 +1,77 @@ + + +{% block content %} + +

    Registrations

    + + + + + + + + + + + + + + {% for registration in registration_list %} + + + + + + + + + + + + + {% endfor %} +
    + name + + username + + email + + class + + class date + + date registered + + date cancelled + + date promoted + + late promotion + + attended +
    + {{ registration.student.first_name }} {{ registration.student.last_name }} + + {{ registration.student.username }} + + {{ registration.student.email }} + + {{ registration.event }} + + {{ registration.event.date }} + + {{ registration.date_registered }} + + {{ registration.date_cancelled }} + + {{ registration.date_promoted }} + + {{ registration.late_promotion }} + + {{ registration.attended }} +
    + + +{% endblock %} + diff --git a/kcdc3/apps/classes/templates/classes/staff_session_list.html b/kcdc3/apps/classes/templates/classes/staff_session_list.html new file mode 100644 index 00000000..e8f88608 --- /dev/null +++ b/kcdc3/apps/classes/templates/classes/staff_session_list.html @@ -0,0 +1,68 @@ +{% extends "classes/staff.html" %} + + +{% block content %} + +
    + +

    Sessions

    + + + + + + + + + + + {% for session in session_list %} + + + + + + + + + + + {% endfor %} +
    + + Session + + Status + + Regular classes + + Registrations + + Attended + + Reports +
    + Edit + + {{ session.long_title }} + + {{ session.status }} + + {{ session.get_live_classes.count }} + + {{ session.get_registrations.count }} + + {{ session.get_attended_registrations.count }} + + Teachers + + Registrations +
    + +
    + +

    All Teachers

    + + +{% endblock %} + diff --git a/kcdc3/apps/classes/templates/classes/staff_teacher_list.html b/kcdc3/apps/classes/templates/classes/staff_teacher_list.html new file mode 100644 index 00000000..c6ddabce --- /dev/null +++ b/kcdc3/apps/classes/templates/classes/staff_teacher_list.html @@ -0,0 +1,72 @@ + + +{% block content %} + +

    Teachers

    + + + + + + + + + + + + {% for bio in teacher_list %} + + + + + + + + + + + {% endfor %} +
    + + + name + + email + + rating + + comments + + total + + classes +
    + Edit + + {{ forloop.counter }} + + {{ bio.name }} + + {{ bio.get_email }} + + {% if bio.rating %}{{ bio.rating }}{% endif %} + + {% if bio.comments %}{{ bio.comments }}{% endif %} + + {{ bio.get_classes.count }} + + {% for event in bio.get_classes.all %} + {{ event.title }} + {% endfor %} +
    + +
    + + {% for bio in teacher_list %} + {{ bio.get_email }}, + {% endfor %} + + + +{% endblock %} + diff --git a/kcdc3/classes/tests.py b/kcdc3/apps/classes/tests.py similarity index 100% rename from kcdc3/classes/tests.py rename to kcdc3/apps/classes/tests.py diff --git a/kcdc3/apps/classes/urls.py b/kcdc3/apps/classes/urls.py new file mode 100644 index 00000000..d1c5491d --- /dev/null +++ b/kcdc3/apps/classes/urls.py @@ -0,0 +1,20 @@ +from django.conf.urls import patterns, include, url +from models import Event, Registration +from views import EventListView, EventDetailView, ResponseTemplateView, EventArchiveView, SessionView, RegistrationListView, SessionAdminListView, TeacherAdminListView, FilteredTeacherAdminListView + +urlpatterns = patterns('kcdc3.apps.classes.views', + + url(r'^$', EventListView.as_view()), + url(r'^staff/$', SessionAdminListView.as_view()), + url(r'^staff/teachers/$', TeacherAdminListView.as_view()), + url(r'^staff/teachers/session/(?P[A-Za-z0-9_-]+)/$', FilteredTeacherAdminListView.as_view()), + url(r'^staff/registrations/session/(?P[A-Za-z0-9_-]+)/$', RegistrationListView.as_view()), + url(r'^(?P[0-9_-]+)/$', EventArchiveView.as_view()), + url(r'^(?P[0-9_-]+)/background/$', SessionView.as_view()), + url(r'^response/(?P[A-Za-z0-9_-]+)$', ResponseTemplateView.as_view()), + url(r'^(?P[A-Za-z0-9_-]+)/$', EventDetailView.as_view(model=Event,)), + url(r'^(?P[A-Za-z0-9_-]+)/register$', 'register'), + url(r'^(?P[A-Za-z0-9_-]+)/cancel$', 'cancel'), + url(r'^(?P[A-Za-z0-9_-]+)/facilitator$', 'facilitator'), + +) diff --git a/kcdc3/classes/views.py b/kcdc3/apps/classes/views.py similarity index 78% rename from kcdc3/classes/views.py rename to kcdc3/apps/classes/views.py index bb05846e..1aace5c6 100644 --- a/kcdc3/classes/views.py +++ b/kcdc3/apps/classes/views.py @@ -1,8 +1,8 @@ -from django.http import HttpRequest, HttpResponse, HttpResponseRedirect +from django.http import HttpRequest, HttpResponse, HttpResponseRedirect, Http404 from django.shortcuts import render_to_response from datetime import datetime from django.views.generic import DetailView, TemplateView, ListView -from classes.models import Event, Registration, Bio, Session +from models import Event, Registration, Bio, Session from django.core.mail import send_mail from django.template.loader import render_to_string from django.template import Context @@ -82,7 +82,7 @@ class EventDetailView(DetailView): model = Event def get_context_data(self, **kwargs): - + context = super(EventDetailView, self).get_context_data(**kwargs) # get list of sessions for use in local navigation @@ -95,7 +95,7 @@ def get_context_data(self, **kwargs): user = None event = self.get_object() - + context['registration_count'] = event.registration_count() context['waitlist_count'] = event.waitlist_count() context['user_is_waitlisted'] = is_waitlisted(user, event) @@ -108,7 +108,10 @@ def get_context_data(self, **kwargs): if Event.objects.filter(slug=self.object.slug, facilitators=self.request.user).count() > 0 or self.request.user.is_staff: context['show_facilitator'] = True - return context + if event.status == 'PUBLISHED' or event.status == 'HIDDEN': + return context + else: + raise Http404 @@ -233,6 +236,7 @@ def facilitator(request, slug): # display a list of registrations for a given session class RegistrationListView(ListView): + template_name = "classes/staff_registration_list.html" context_object_name = "registration_list" model = Registration @@ -251,4 +255,77 @@ def get_context_data(self, **kwargs): @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(RegistrationListView, self).dispatch(*args, **kwargs) - \ No newline at end of file + + + + +# display a list of registrations for a given session +class SessionAdminListView(ListView): + + template_name = "classes/staff_session_list.html" + context_object_name = "session_list" + model = Session + + def get_context_data(self, **kwargs): + + context = super(SessionAdminListView, self).get_context_data(**kwargs) + context['session_list'] = Session.objects.all() + + # is the user staff? + if self.request.user.is_staff: + return context + else: + # TODO this should really return a 403 + return HttpResponse() + + @method_decorator(login_required) + def dispatch(self, *args, **kwargs): + return super(SessionAdminListView, self).dispatch(*args, **kwargs) + + + + + +# display a list of teacher (bios) in the system +class TeacherAdminListView(ListView): + + template_name = "classes/staff_teacher_list.html" + context_object_name = "teacher_list" + model = Bio + + def get_context_data(self, **kwargs): + + context = super(TeacherAdminListView, self).get_context_data(**kwargs) + context['teacher_list'] = Bio.objects.all().order_by('name') + + # is the user staff? + if self.request.user.is_staff: + return context + else: + # TODO this should really return a 403 + return HttpResponse() + + @method_decorator(login_required) + def dispatch(self, *args, **kwargs): + return super(TeacherAdminListView, self).dispatch(*args, **kwargs) + + + + +class FilteredTeacherAdminListView(TeacherAdminListView): + + def get_context_data(self, **kwargs): + + context = super(TeacherAdminListView, self).get_context_data(**kwargs) + context['teacher_list'] = Bio.objects.filter(event__session__slug__iexact=self.kwargs['slug']).order_by('name') + + # is the user staff? + if self.request.user.is_staff: + return context + else: + # TODO this should really return a 403 + return HttpResponse() + + # if self.kwargs['slug'] is not None: + # context['teacher_list'] = Bio.objects.filter(name__contains="teacher") + diff --git a/kcdc3/apps/pigeon/__init__.py b/kcdc3/apps/pigeon/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kcdc3/apps/pigeon/admin.py b/kcdc3/apps/pigeon/admin.py new file mode 100644 index 00000000..e30e6923 --- /dev/null +++ b/kcdc3/apps/pigeon/admin.py @@ -0,0 +1,50 @@ +from models import Post +from kcdc3.apps.classes.models import Bio +from django import forms +from django.contrib import admin +from django.contrib.admin.sites import site +from django.contrib.admin.widgets import ManyToManyRawIdWidget +from django.core.urlresolvers import reverse +from django.utils.encoding import smart_unicode +from django.utils.html import escape + + + +class PostAdmin(admin.ModelAdmin): + + fieldsets = [ + (None, {'fields': [ + 'title', 'slug', 'department_label', ('date', 'author',), ('status', 'featured',), + ]}), + ('Text', { + 'classes': ('grp-collapse grp-open',), + 'fields': [ + 'thumbnail', + 'teaser', + 'main_text', + ]}), + ('Tags and comments', { + 'classes': ('grp-collapse grp-open',), + 'fields': [ + 'tags', + 'allow_comments', + ]}), + ] + + prepopulated_fields = {"slug": ("title",)} + raw_id_fields = ['author'] + related_lookup_fields = { + 'm2m': ['author'], + } + + list_display = ('title', 'date', 'status', 'featured', 'allow_comments', 'tags',) + list_editable = ('status', 'featured',) + + class Media: + js = [ + 'tiny_mce/tiny_mce.js', + 'tinymce_setup.js', + ] + +admin.site.register(Post, PostAdmin) + diff --git a/kcdc3/apps/pigeon/migrations/0001_initial.py b/kcdc3/apps/pigeon/migrations/0001_initial.py new file mode 100644 index 00000000..28fe703c --- /dev/null +++ b/kcdc3/apps/pigeon/migrations/0001_initial.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Post' + db.create_table('pigeon_post', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), + ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50)), + ('status', self.gf('django.db.models.fields.CharField')(default='CURRENT', max_length=9)), + ('featured', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('date', self.gf('django.db.models.fields.DateTimeField')()), + ('thumbnail', self.gf('django.db.models.fields.files.ImageField')(max_length=100, null=True, blank=True)), + ('teaser', self.gf('django.db.models.fields.TextField')(blank=True)), + ('main_text', self.gf('django.db.models.fields.TextField')(blank=True)), + ('tags', self.gf('django.db.models.fields.CharField')(max_length=128, blank=True)), + ('allow_comments', self.gf('django.db.models.fields.BooleanField')(default=True)), + )) + db.send_create_signal('pigeon', ['Post']) + + # Adding M2M table for field author on 'Post' + db.create_table('pigeon_post_author', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('post', models.ForeignKey(orm['pigeon.post'], null=False)), + ('bio', models.ForeignKey(orm['classes.bio'], null=False)) + )) + db.create_unique('pigeon_post_author', ['post_id', 'bio_id']) + + + def backwards(self, orm): + # Deleting model 'Post' + db.delete_table('pigeon_post') + + # Removing M2M table for field author on 'Post' + db.delete_table('pigeon_post_author') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'pigeon.post': { + 'Meta': {'ordering': "['date']", 'object_name': 'Post'}, + 'allow_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'author': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'author'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'tags': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pigeon'] \ No newline at end of file diff --git a/kcdc3/apps/pigeon/migrations/0002_auto__add_field_post_department_label.py b/kcdc3/apps/pigeon/migrations/0002_auto__add_field_post_department_label.py new file mode 100644 index 00000000..ac84eab1 --- /dev/null +++ b/kcdc3/apps/pigeon/migrations/0002_auto__add_field_post_department_label.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Post.department_label' + db.add_column('pigeon_post', 'department_label', + self.gf('django.db.models.fields.TextField')(default='', blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Post.department_label' + db.delete_column('pigeon_post', 'department_label') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'pigeon.post': { + 'Meta': {'ordering': "['date']", 'object_name': 'Post'}, + 'allow_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'author': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'author'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'department_label': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'tags': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pigeon'] \ No newline at end of file diff --git a/kcdc3/apps/pigeon/migrations/0003_auto__chg_field_post_department_label.py b/kcdc3/apps/pigeon/migrations/0003_auto__chg_field_post_department_label.py new file mode 100644 index 00000000..b57d74c3 --- /dev/null +++ b/kcdc3/apps/pigeon/migrations/0003_auto__chg_field_post_department_label.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Post.department_label' + db.alter_column('pigeon_post', 'department_label', self.gf('django.db.models.fields.CharField')(max_length=128)) + + def backwards(self, orm): + + # Changing field 'Post.department_label' + db.alter_column('pigeon_post', 'department_label', self.gf('django.db.models.fields.TextField')()) + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'classes.bio': { + 'Meta': {'object_name': 'Bio'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'role'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['classes.Role']"}), + 'staff_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'user'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'classes.role': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Role'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '48'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'pigeon.post': { + 'Meta': {'ordering': "['date']", 'object_name': 'Post'}, + 'allow_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'author': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'author'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['classes.Bio']"}), + 'date': ('django.db.models.fields.DateTimeField', [], {}), + 'department_label': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'tags': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pigeon'] \ No newline at end of file diff --git a/kcdc3/apps/pigeon/migrations/__init__.py b/kcdc3/apps/pigeon/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kcdc3/apps/pigeon/models.py b/kcdc3/apps/pigeon/models.py new file mode 100644 index 00000000..5c1f0efc --- /dev/null +++ b/kcdc3/apps/pigeon/models.py @@ -0,0 +1,38 @@ +from django.db import models +from kcdc3.apps.classes.models import Bio +import datetime + + + +class Post(models.Model): + + title = models.CharField(max_length=200) + slug = models.SlugField(unique=True) + STATUS_CHOICES = ( + ('PUBLISHED', 'Published'), + ('HIDDEN', 'Hidden'), + ('DRAFT', 'Draft'), + ('REMOVED', 'Removed'), + ) + status = models.CharField(max_length=9, choices=STATUS_CHOICES, default='CURRENT') + featured = models.BooleanField(default=False) + date = models.DateTimeField() + + department_label = models.CharField(max_length=128, blank=True) + thumbnail = models.ImageField(upload_to='blog', blank=True, null=True) + teaser = models.TextField(blank=True) + main_text = models.TextField(blank=True) + + author = models.ManyToManyField(Bio, blank=True, null=True, related_name='author') + tags = models.CharField(max_length=128, blank=True) + allow_comments = models.BooleanField(default=False) + + class Meta: + ordering = ['-date'] + + def __unicode__(self): + return self.title + + @models.permalink + def get_absolute_url(self): + return ('post_view', [str(self.slug)]) diff --git a/kcdc3/apps/pigeon/templates/pigeon/base.html b/kcdc3/apps/pigeon/templates/pigeon/base.html new file mode 100644 index 00000000..c1e55265 --- /dev/null +++ b/kcdc3/apps/pigeon/templates/pigeon/base.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} + + + +{% block sidebar %} + + + +{% endblock %} + + + + diff --git a/kcdc3/apps/pigeon/templates/pigeon/post_detail.html b/kcdc3/apps/pigeon/templates/pigeon/post_detail.html new file mode 100644 index 00000000..ca84170d --- /dev/null +++ b/kcdc3/apps/pigeon/templates/pigeon/post_detail.html @@ -0,0 +1,43 @@ +{% extends "pigeon/base.html" %} + + + +{% block title %} + / {{ post.title }} +{% endblock %} + + + +{% block content %} + +
    + {% if post.department_label %}

    {{ post.department_label }}

    {% endif %} +

    {{ post.title }}

    + +
    + +
    + + {{ post.main_text|safe }} + + {% if post.author.count > 0 %} +
    + {% for bio in post.author.all %} + {{ bio.get_staff_description|safe }} + {% endfor %} + {% endif %} + +
    + +{% endblock %} + + diff --git a/kcdc3/apps/pigeon/templates/pigeon/post_list.html b/kcdc3/apps/pigeon/templates/pigeon/post_list.html new file mode 100644 index 00000000..6fecbb43 --- /dev/null +++ b/kcdc3/apps/pigeon/templates/pigeon/post_list.html @@ -0,0 +1,61 @@ +{% extends "pigeon/base.html" %} + + + +{% block title %} + / Blog +{% endblock %} + + + +{% block content %} + +
    +

    Blog

    +
    + + + +{% endblock %} + diff --git a/kcdc3/apps/pigeon/urls.py b/kcdc3/apps/pigeon/urls.py new file mode 100644 index 00000000..fea880cc --- /dev/null +++ b/kcdc3/apps/pigeon/urls.py @@ -0,0 +1,12 @@ +from django.conf.urls import patterns, include, url +from models import Post +from views import PostListView, PostArchiveView, PostDetailView, BlogFeed + +urlpatterns = patterns('pigeon.views', + + url(r'^$', PostListView.as_view()), + url(r'^rss/$', BlogFeed()), + url(r'^(?P[0-9_-]+)/$', PostArchiveView.as_view()), + url(r'^(?P[A-Za-z0-9_-]+)/$', PostDetailView.as_view(model=Post,), name='post_view'), + +) diff --git a/kcdc3/apps/pigeon/views.py b/kcdc3/apps/pigeon/views.py new file mode 100644 index 00000000..8d71dd8c --- /dev/null +++ b/kcdc3/apps/pigeon/views.py @@ -0,0 +1,80 @@ +from django.http import HttpRequest, HttpResponse, HttpResponseRedirect, Http404 +from django.views.generic import DetailView, TemplateView, ListView +from datetime import datetime +from django.template import Context +from django.db.models import Q +from django.contrib.syndication.views import Feed +from models import Post +from kcdc3.apps.classes.models import Bio + + + +# display a list of posts +class PostListView(ListView): + + context_object_name = "post_list" + model = Post + + def get_context_data(self, **kwargs): + + context = super(PostListView, self).get_context_data(**kwargs) + context['posts'] = Post.objects.filter(status='PUBLISHED').exclude(date__gte=datetime.now()).order_by('-date') + context['recent_posts'] = context['posts'][:15] + + return context + + + + +# display a list of posts for a particular month +class PostArchiveView(ListView): + + context_object_name = "post_list" + model = Post + + def get_context_data(self, **kwargs): + + # convert slug into date: remarkable thing, this datetime object + target_date = datetime.strptime(self.kwargs['date_slug'], '%y%m') + + context = super(PostArchiveView, self).get_context_data(**kwargs) + context['posts'] = Post.objects.filter(status='PUBLISHED').exclude(date__gte=datetime.now()) + context['recent_posts'] = context['posts'].filter(date__year=target_date.year).filter(date__month=target_date.month) + + return context + + + +# display a single post +class PostDetailView(DetailView): + + context_object_name = "post" + model = Post + + def get_context_data(self, **kwargs): + + context = super(PostDetailView, self).get_context_data(**kwargs) + context['posts'] = Post.objects.filter(status='PUBLISHED').exclude(date__gte=datetime.now()) + return context + + + + +# RSS feed +class BlogFeed(Feed): + title = "KCDC Blog" + link = "/blog/" + description = "Knowledge Commons DC Blog" + + def items(self): + return Post.objects.filter(status='PUBLISHED').exclude(date__gte=datetime.now()).order_by('-date')[:10] + + def item_title(self, item): + return item.title + + def item_description(self, item): + return item.teaser + + + + diff --git a/kcdc3/apps/pinata/__init__.py b/kcdc3/apps/pinata/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kcdc3/apps/pinata/admin.py b/kcdc3/apps/pinata/admin.py new file mode 100644 index 00000000..e23caf8b --- /dev/null +++ b/kcdc3/apps/pinata/admin.py @@ -0,0 +1,158 @@ +from kcdc3.apps.pinata.models import Page, Notice, Slide, Sponsor, PressClipping, Snippet +from django import forms +from django.contrib import admin +from django.contrib.admin.sites import site +from django.contrib.admin.widgets import ManyToManyRawIdWidget, ForeignKeyRawIdWidget +from django.core.urlresolvers import reverse +from django.utils.encoding import smart_unicode +from django.utils.html import escape + + + +class PageAdmin(admin.ModelAdmin): + + fieldsets = [ + (None, {'fields': [ + 'title', 'short_title', 'parent', 'path', 'status', 'sort_order', + ]}), + ('Text', { + 'classes': ('grp-collapse grp-open',), + 'fields': [ + #'teaser', + 'main_text' + ]}), + ('Additional Text', { + 'classes': ('grp-collapse grp-closed',), + 'fields': [ + 'sidebar_text', + ]}), + ('Layout', { + 'classes': ('grp-collapse grp-closed',), + 'fields': [ + 'template', + ]}), + ] + + list_display = ('title', 'short_title', 'status', 'parent', 'path', 'sort_order', 'template') + list_editable = ('status', 'sort_order', 'template') + + class Media: + js = [ + 'tiny_mce/tiny_mce.js', + 'tinymce_setup.js', + ] + +admin.site.register(Page, PageAdmin) + + + + + +class SnippetAdmin(admin.ModelAdmin): + + fieldsets = [ + (None, {'fields': [ + 'title', 'path', + ]}), + ('Text', { + 'classes': ('grp-collapse grp-open',), + 'fields': [ + 'main_text' + ]}), + ] + + list_display = ('title', 'path') + +admin.site.register(Snippet, SnippetAdmin) + + + + + +class NoticeAdmin(admin.ModelAdmin): + + fieldsets = [ + (None, {'fields': [ + 'title', 'main_text', 'live', 'sort_order', + ]}), + ] + + list_display = ('title', 'live', 'sort_order',) + list_editable = ('live', 'sort_order',) + +admin.site.register(Notice, NoticeAdmin) + + + + + +class SlideAdmin(admin.ModelAdmin): + + fieldsets = [ + (None, {'fields': [ + 'title', 'image', 'main_text', 'live', + ]}), + ] + + list_display = ('title', 'live', 'image',) + list_editable = ('live',) + +admin.site.register(Slide, SlideAdmin) + + + + + +class SponsorAdmin(admin.ModelAdmin): + + fieldsets = [ + (None, {'fields': [ + 'title', + ('group', 'status', 'sort_order',), + 'destination_url', + 'image', + # 'main_text', + ]}), + ] + + list_display = ('title', 'status', 'group',) + list_editable = ('status', 'group',) + + class Media: + js = [ + 'tiny_mce/tiny_mce.js', + 'tinymce_setup.js', + ] + +admin.site.register(Sponsor, SponsorAdmin) + + + + +class PressClippingAdmin(admin.ModelAdmin): + + fieldsets = [ + (None, {'fields': [ + 'title', + 'date', + 'publication', + 'image', + 'destination_url', + # 'main_text', + 'excerpt', + 'status', + ]}), + ] + + list_display = ('title', 'status', 'date',) + list_editable = ('status',) + + class Media: + js = [ + 'tiny_mce/tiny_mce.js', + 'tinymce_setup.js', + ] + + +admin.site.register(PressClipping, PressClippingAdmin) + diff --git a/kcdc3/apps/pinata/migrations/0001_initial.py b/kcdc3/apps/pinata/migrations/0001_initial.py new file mode 100644 index 00000000..9fea0f0b --- /dev/null +++ b/kcdc3/apps/pinata/migrations/0001_initial.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Page' + db.create_table('pinata_page', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), + ('short_title', self.gf('django.db.models.fields.CharField')(max_length=32)), + ('status', self.gf('django.db.models.fields.CharField')(default='CURRENT', max_length=9)), + ('featured', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('path', self.gf('django.db.models.fields.CharField')(unique=True, max_length=200)), + ('parent', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['pinata.Page'], null=True)), + ('teaser', self.gf('django.db.models.fields.TextField')(blank=True)), + ('main_text', self.gf('django.db.models.fields.TextField')(blank=True)), + ('sidebar_text', self.gf('django.db.models.fields.TextField')(blank=True)), + )) + db.send_create_signal('pinata', ['Page']) + + + def backwards(self, orm): + # Deleting model 'Page' + db.delete_table('pinata_page') + + + models = { + 'pinata.page': { + 'Meta': {'ordering': "['path']", 'object_name': 'Page'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pinata.Page']", 'null': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pinata'] \ No newline at end of file diff --git a/kcdc3/apps/pinata/migrations/0002_auto__add_field_page_sort_order.py b/kcdc3/apps/pinata/migrations/0002_auto__add_field_page_sort_order.py new file mode 100644 index 00000000..f299312f --- /dev/null +++ b/kcdc3/apps/pinata/migrations/0002_auto__add_field_page_sort_order.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Page.sort_order' + db.add_column('pinata_page', 'sort_order', + self.gf('django.db.models.fields.IntegerField')(default=50, null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Page.sort_order' + db.delete_column('pinata_page', 'sort_order') + + + models = { + 'pinata.page': { + 'Meta': {'ordering': "['path']", 'object_name': 'Page'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pinata.Page']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pinata'] \ No newline at end of file diff --git a/kcdc3/apps/pinata/migrations/0003_auto__add_notice__add_slide.py b/kcdc3/apps/pinata/migrations/0003_auto__add_notice__add_slide.py new file mode 100644 index 00000000..6ed2bcf2 --- /dev/null +++ b/kcdc3/apps/pinata/migrations/0003_auto__add_notice__add_slide.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Notice' + db.create_table('pinata_notice', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), + ('main_text', self.gf('django.db.models.fields.TextField')(blank=True)), + ('live', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('sort_order', self.gf('django.db.models.fields.IntegerField')(default=50, null=True, blank=True)), + )) + db.send_create_signal('pinata', ['Notice']) + + # Adding model 'Slide' + db.create_table('pinata_slide', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), + ('main_text', self.gf('django.db.models.fields.TextField')(blank=True)), + ('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100, null=True, blank=True)), + ('live', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('sort_order', self.gf('django.db.models.fields.IntegerField')(default=50, null=True, blank=True)), + )) + db.send_create_signal('pinata', ['Slide']) + + + def backwards(self, orm): + # Deleting model 'Notice' + db.delete_table('pinata_notice') + + # Deleting model 'Slide' + db.delete_table('pinata_slide') + + + models = { + 'pinata.notice': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Notice'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.page': { + 'Meta': {'ordering': "['path']", 'object_name': 'Page'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pinata.Page']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.slide': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Slide'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pinata'] \ No newline at end of file diff --git a/kcdc3/apps/pinata/migrations/0004_auto__add_sponsor.py b/kcdc3/apps/pinata/migrations/0004_auto__add_sponsor.py new file mode 100644 index 00000000..64579271 --- /dev/null +++ b/kcdc3/apps/pinata/migrations/0004_auto__add_sponsor.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Sponsor' + db.create_table('pinata_sponsor', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), + ('main_text', self.gf('django.db.models.fields.TextField')(blank=True)), + ('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100, null=True, blank=True)), + ('destination_url', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True)), + ('group', self.gf('django.db.models.fields.CharField')(default='B', max_length=3)), + ('status', self.gf('django.db.models.fields.CharField')(default='CURRENT', max_length=9)), + ('sort_order', self.gf('django.db.models.fields.IntegerField')(default=50, null=True, blank=True)), + )) + db.send_create_signal('pinata', ['Sponsor']) + + + def backwards(self, orm): + # Deleting model 'Sponsor' + db.delete_table('pinata_sponsor') + + + models = { + 'pinata.notice': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Notice'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.page': { + 'Meta': {'ordering': "['path']", 'object_name': 'Page'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pinata.Page']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.slide': { + 'Meta': {'ordering': "['sort_order']", 'object_name': 'Slide'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.sponsor': { + 'Meta': {'ordering': "['group', 'sort_order', 'title']", 'object_name': 'Sponsor'}, + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.CharField', [], {'default': "'B'", 'max_length': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'CURRENT'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pinata'] \ No newline at end of file diff --git a/kcdc3/apps/pinata/migrations/0005_auto__add_pressclipping.py b/kcdc3/apps/pinata/migrations/0005_auto__add_pressclipping.py new file mode 100644 index 00000000..f05d3ee3 --- /dev/null +++ b/kcdc3/apps/pinata/migrations/0005_auto__add_pressclipping.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'PressClipping' + db.create_table('pinata_pressclipping', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), + ('main_text', self.gf('django.db.models.fields.TextField')(blank=True)), + ('date', self.gf('django.db.models.fields.DateField')(blank=True)), + ('publication', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)), + ('excerpt', self.gf('django.db.models.fields.TextField')(blank=True)), + ('destination_url', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True)), + ('status', self.gf('django.db.models.fields.CharField')(default='PUBLISHED', max_length=9)), + )) + db.send_create_signal('pinata', ['PressClipping']) + + + def backwards(self, orm): + # Deleting model 'PressClipping' + db.delete_table('pinata_pressclipping') + + + models = { + 'pinata.notice': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Notice'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.page': { + 'Meta': {'ordering': "['path']", 'object_name': 'Page'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pinata.Page']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.pressclipping': { + 'Meta': {'ordering': "['date']", 'object_name': 'PressClipping'}, + 'date': ('django.db.models.fields.DateField', [], {'blank': 'True'}), + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'publication': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.slide': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Slide'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.sponsor': { + 'Meta': {'ordering': "['group', 'sort_order', 'title']", 'object_name': 'Sponsor'}, + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.CharField', [], {'default': "'B'", 'max_length': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pinata'] \ No newline at end of file diff --git a/kcdc3/apps/pinata/migrations/0006_auto__add_field_pressclipping_image.py b/kcdc3/apps/pinata/migrations/0006_auto__add_field_pressclipping_image.py new file mode 100644 index 00000000..099f95f6 --- /dev/null +++ b/kcdc3/apps/pinata/migrations/0006_auto__add_field_pressclipping_image.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'PressClipping.image' + db.add_column('pinata_pressclipping', 'image', + self.gf('django.db.models.fields.CharField')(default='', max_length=200, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'PressClipping.image' + db.delete_column('pinata_pressclipping', 'image') + + + models = { + 'pinata.notice': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Notice'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.page': { + 'Meta': {'ordering': "['path']", 'object_name': 'Page'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pinata.Page']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.pressclipping': { + 'Meta': {'ordering': "['date']", 'object_name': 'PressClipping'}, + 'date': ('django.db.models.fields.DateField', [], {'blank': 'True'}), + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'publication': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.slide': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Slide'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.sponsor': { + 'Meta': {'ordering': "['group', 'sort_order', 'title']", 'object_name': 'Sponsor'}, + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.CharField', [], {'default': "'B'", 'max_length': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pinata'] \ No newline at end of file diff --git a/kcdc3/apps/pinata/migrations/0007_auto__chg_field_page_status.py b/kcdc3/apps/pinata/migrations/0007_auto__chg_field_page_status.py new file mode 100644 index 00000000..e4cac27b --- /dev/null +++ b/kcdc3/apps/pinata/migrations/0007_auto__chg_field_page_status.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Page.status' + db.alter_column('pinata_page', 'status', self.gf('django.db.models.fields.CharField')(max_length=48)) + + def backwards(self, orm): + + # Changing field 'Page.status' + db.alter_column('pinata_page', 'status', self.gf('django.db.models.fields.CharField')(max_length=9)) + + models = { + 'pinata.notice': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Notice'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.page': { + 'Meta': {'ordering': "['path']", 'object_name': 'Page'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pinata.Page']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'basic.html'", 'max_length': '48'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.pressclipping': { + 'Meta': {'ordering': "['date']", 'object_name': 'PressClipping'}, + 'date': ('django.db.models.fields.DateField', [], {'blank': 'True'}), + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'publication': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.slide': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Slide'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.sponsor': { + 'Meta': {'ordering': "['group', 'sort_order', 'title']", 'object_name': 'Sponsor'}, + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.CharField', [], {'default': "'B'", 'max_length': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pinata'] \ No newline at end of file diff --git a/kcdc3/apps/pinata/migrations/0008_auto__add_field_page_template__chg_field_page_status.py b/kcdc3/apps/pinata/migrations/0008_auto__add_field_page_template__chg_field_page_status.py new file mode 100644 index 00000000..bc416880 --- /dev/null +++ b/kcdc3/apps/pinata/migrations/0008_auto__add_field_page_template__chg_field_page_status.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Page.template' + db.add_column('pinata_page', 'template', + self.gf('django.db.models.fields.CharField')(default='basic.html', max_length=48), + keep_default=False) + + + # Changing field 'Page.status' + db.alter_column('pinata_page', 'status', self.gf('django.db.models.fields.CharField')(max_length=9)) + + def backwards(self, orm): + # Deleting field 'Page.template' + db.delete_column('pinata_page', 'template') + + + # Changing field 'Page.status' + db.alter_column('pinata_page', 'status', self.gf('django.db.models.fields.CharField')(max_length=48)) + + models = { + 'pinata.notice': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Notice'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.page': { + 'Meta': {'ordering': "['path']", 'object_name': 'Page'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pinata.Page']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'template': ('django.db.models.fields.CharField', [], {'default': "'basic.html'", 'max_length': '48'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.pressclipping': { + 'Meta': {'ordering': "['date']", 'object_name': 'PressClipping'}, + 'date': ('django.db.models.fields.DateField', [], {'blank': 'True'}), + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'publication': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.slide': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Slide'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.sponsor': { + 'Meta': {'ordering': "['group', 'sort_order', 'title']", 'object_name': 'Sponsor'}, + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.CharField', [], {'default': "'B'", 'max_length': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pinata'] \ No newline at end of file diff --git a/kcdc3/apps/pinata/migrations/0009_auto__add_snippet.py b/kcdc3/apps/pinata/migrations/0009_auto__add_snippet.py new file mode 100644 index 00000000..b8489c7e --- /dev/null +++ b/kcdc3/apps/pinata/migrations/0009_auto__add_snippet.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Snippet' + db.create_table('pinata_snippet', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), + ('main_text', self.gf('django.db.models.fields.TextField')(blank=True)), + ('path', self.gf('django.db.models.fields.CharField')(unique=True, max_length=200)), + )) + db.send_create_signal('pinata', ['Snippet']) + + + def backwards(self, orm): + # Deleting model 'Snippet' + db.delete_table('pinata_snippet') + + + models = { + 'pinata.notice': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Notice'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.page': { + 'Meta': {'ordering': "['path']", 'object_name': 'Page'}, + 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pinata.Page']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'short_title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'sidebar_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'teaser': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'template': ('django.db.models.fields.CharField', [], {'default': "'basic.html'", 'max_length': '48'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.pressclipping': { + 'Meta': {'ordering': "['date']", 'object_name': 'PressClipping'}, + 'date': ('django.db.models.fields.DateField', [], {'blank': 'True'}), + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'publication': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.slide': { + 'Meta': {'ordering': "['sort_order', 'title']", 'object_name': 'Slide'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.snippet': { + 'Meta': {'ordering': "['path']", 'object_name': 'Snippet'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'pinata.sponsor': { + 'Meta': {'ordering': "['group', 'sort_order', 'title']", 'object_name': 'Sponsor'}, + 'destination_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.CharField', [], {'default': "'B'", 'max_length': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'main_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PUBLISHED'", 'max_length': '9'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + } + } + + complete_apps = ['pinata'] \ No newline at end of file diff --git a/kcdc3/apps/pinata/migrations/__init__.py b/kcdc3/apps/pinata/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kcdc3/apps/pinata/models.py b/kcdc3/apps/pinata/models.py new file mode 100644 index 00000000..cee11a94 --- /dev/null +++ b/kcdc3/apps/pinata/models.py @@ -0,0 +1,154 @@ +from django.db import models +import datetime + + + +class Page(models.Model): + title = models.CharField(max_length=200) + short_title = models.CharField(max_length=32) + STATUS_CHOICES = ( + ('PUBLISHED', 'Published'), + ('HIDDEN', 'Hidden'), + ('DRAFT', 'Draft'), + ('REMOVED', 'Removed'), + ) + status = models.CharField(max_length=9, choices=STATUS_CHOICES, default='PUBLISHED') + featured = models.BooleanField(default=False) + sort_order = models.IntegerField(blank=True, null=True, default=50) + + path = models.CharField(max_length=200, unique=True) + parent = models.ForeignKey('self', null=True,blank=True) + + teaser = models.TextField(blank=True) + main_text = models.TextField(blank=True) + sidebar_text = models.TextField(blank=True) + + TEMPLATE_CHOICES = ( + ('page.html', 'Basic'), + ('page_wide.html', 'Wide'), + ('page_nosidebar.html', 'No Sidebar'), + ('page_multipart.html', 'Multipart'), + ) + template = models.CharField(max_length=48, choices=TEMPLATE_CHOICES, default='basic.html') + + class Meta: + ordering = ['path'] + + def __unicode__(self): + return self.title + + + +class Snippet(models.Model): + """ Small pieces of content, inserted into special page templates. + The path should identify the snippet. Examples: + /contribute#section1 + /contribute/details#intro + sitewide#copyright + """ + + title = models.CharField(max_length=200) + main_text = models.TextField(blank=True) + path = models.CharField(max_length=200, unique=True) + + class Meta: + ordering = ['path'] + + def __unicode__(self): + return self.title + + + +class Notice(models.Model): + """ Short-term notices that appear on the front page """ + + title = models.CharField(max_length=200) + main_text = models.TextField(blank=True) + live = models.BooleanField(default=True) + sort_order = models.IntegerField(blank=True, null=True, default=50) + + class Meta: + ordering = ['sort_order','title'] + verbose_name=u'Front Notice' + + def __unicode__(self): + return self.title + + + +class Slide(models.Model): + """ Slides for the front page """ + + title = models.CharField(max_length=200) + main_text = models.TextField(blank=True) + image = models.ImageField('Image (960x360px)', upload_to='front_slides', blank=True, null=True) + live = models.BooleanField(default=True) + sort_order = models.IntegerField(blank=True, null=True, default=50) + + class Meta: + ordering = ['sort_order','title'] + verbose_name=u'Front Slide' + + def __unicode__(self): + return self.title + + + +class Sponsor(models.Model): + """ Partners and sponsors """ + + title = models.CharField(max_length=200) + main_text = models.TextField(blank=True) + image = models.ImageField('Image', upload_to='sponsors', blank=True, null=True) + destination_url = models.URLField(blank=True) + GROUP_CHOICES = ( + ('A', 'Major Partner'), + ('B', 'Minor Partner'), + ('S', 'Space'), + ) + group = models.CharField(max_length=3, choices=GROUP_CHOICES, default='B') + + STATUS_CHOICES = ( + ('PUBLISHED', 'Published'), + ('HIDDEN', 'Hidden'), + ('DRAFT', 'Draft'), + ('REMOVED', 'Removed'), + ) + status = models.CharField(max_length=9, choices=STATUS_CHOICES, default='PUBLISHED') + sort_order = models.IntegerField(blank=True, null=True, default=50) + + class Meta: + verbose_name = 'Partner' + ordering = ['group','sort_order','title'] + + def __unicode__(self): + return self.title + + + + +class PressClipping(models.Model): + """ A press article """ + + title = models.CharField(max_length=200) + main_text = models.TextField(blank=True) + date = models.DateField(blank=True) + publication = models.CharField(max_length=200,blank=True) + excerpt = models.TextField(blank=True) + destination_url = models.URLField(blank=True) + image = models.CharField(max_length=200,blank=True) + + STATUS_CHOICES = ( + ('PUBLISHED', 'Published'), + ('HIDDEN', 'Hidden'), + ('DRAFT', 'Draft'), + ('REMOVED', 'Removed'), + ) + status = models.CharField(max_length=9, choices=STATUS_CHOICES, default='PUBLISHED') + + class Meta: + ordering = ['date'] + verbose_name=u'Press Clipping' + + def __unicode__(self): + return self.title diff --git a/kcdc3/apps/pinata/templates/pinata/base.html b/kcdc3/apps/pinata/templates/pinata/base.html new file mode 100644 index 00000000..f5d733d9 --- /dev/null +++ b/kcdc3/apps/pinata/templates/pinata/base.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + + + +{% block sidebar %} + + + +{% endblock %} + + + + diff --git a/kcdc3/apps/pinata/templates/pinata/home.html b/kcdc3/apps/pinata/templates/pinata/home.html new file mode 100644 index 00000000..f3eee175 --- /dev/null +++ b/kcdc3/apps/pinata/templates/pinata/home.html @@ -0,0 +1,135 @@ +{% extends "pinata/base.html" %} + +{% load pinata_filters %} + +{% block pre_content %} + +
    +

    + Knowledge Commons DC is a free school for thinkers, doers, and tinkerers – taught anywhere, by anyone, for everyone. +

    +

    teach or take a class

    +
    + +
    +
    +
    +
      + {% for slide in slides %} + {% if slide.image %} +
    • {{ slide.title }}
    • + {% else %} +
    • {{ slide.main_text | smartquotes | safe }}
    • + {% endif %} + {% endfor %} +
    +
    +
    +
    + +{% endblock %} + + + +{% block content %} + +
    + {% for notice in notices %} + {{ notice.main_text | smartquotes | safe }} + {% endfor %} +
    + + + + + + + +{% endblock %} + + + + +{% block local_scripts %} + + + + +{% endblock %} + + + diff --git a/kcdc3/apps/pinata/templates/pinata/page.html b/kcdc3/apps/pinata/templates/pinata/page.html new file mode 100644 index 00000000..19a5b753 --- /dev/null +++ b/kcdc3/apps/pinata/templates/pinata/page.html @@ -0,0 +1,44 @@ +{% extends "pinata/base.html" %} + + + +{% block title %} + / {{ short_title }} +{% endblock %} + + + + +{% block content %} + +
    +

    {{ title }}

    +
    + +
    + {{ main_text|safe }} +
    + +{% endblock %} + + + +{% block sidebar_content %} + {{ sidebar_text|safe }} +{% endblock %} + + +{% if siblings or children %} + {% block sidebar_nav %} +
    + +
    + {% endblock %} +{% endif %} \ No newline at end of file diff --git a/kcdc3/apps/pinata/templates/pinata/page_nosidebar.html b/kcdc3/apps/pinata/templates/pinata/page_nosidebar.html new file mode 100644 index 00000000..36024310 --- /dev/null +++ b/kcdc3/apps/pinata/templates/pinata/page_nosidebar.html @@ -0,0 +1,8 @@ +{% extends "pinata/page.html" %} + + +{% block sidebar %} + + + +{% endblock %} diff --git a/kcdc3/apps/pinata/templates/pinata/page_wide.html b/kcdc3/apps/pinata/templates/pinata/page_wide.html new file mode 100644 index 00000000..003acad7 --- /dev/null +++ b/kcdc3/apps/pinata/templates/pinata/page_wide.html @@ -0,0 +1,49 @@ +{% extends "pinata/base.html" %} + + + +{% block title %} + / {{ short_title }} +{% endblock %} + + + + +{% block content_shell %} + +
    + + {% if siblings or children %} + {% block local_nav_wide %} +
    + +
    + {% endblock %} + {% endif %} + + {% block content %} + +
    +

    {{ title }}

    +
    + +
    + {{ main_text|safe }} +
    + + {% endblock %} + {% block sidebar %}{% endblock %} + +
    + +{% endblock %} + + + diff --git a/kcdc3/apps/pinata/templates/pinata/partners.html b/kcdc3/apps/pinata/templates/pinata/partners.html new file mode 100644 index 00000000..7d1bcc75 --- /dev/null +++ b/kcdc3/apps/pinata/templates/pinata/partners.html @@ -0,0 +1,49 @@ +{% extends "pinata/page.html" %} + + + + +{% block content %} + +
    +

    {{ title }}

    +
    + +
    + + {{ main_text|safe }} + +
    + {% for sponsor in sponsors_A %} +
    + {% if sponsor.destination_url %}{% endif %} + + {% if sponsor.destination_url %}{% endif %} +
    + {% endfor %} +
    + +
    + {% for sponsor in sponsors_S %} +
    + {% if sponsor.destination_url %}{% endif %} + + {% if sponsor.destination_url %}{% endif %} +
    + {% endfor %} +
    + +
    + {% for sponsor in sponsors_B %} +
    + {% if sponsor.destination_url %}{% endif %} + + {% if sponsor.destination_url %}{% endif %} +
    + {% endfor %} +
    + +
    + +{% endblock %} + diff --git a/kcdc3/apps/pinata/templates/pinata/pressclippings.html b/kcdc3/apps/pinata/templates/pinata/pressclippings.html new file mode 100644 index 00000000..17867bcb --- /dev/null +++ b/kcdc3/apps/pinata/templates/pinata/pressclippings.html @@ -0,0 +1,54 @@ +{% extends "pinata/page.html" %} + + +{% block body_class %}s_press p_press{% endblock %} + + +{% block content %} + +
    +

    {{ short_title }}

    +
    + + + +{% endblock %} + diff --git a/kcdc3/apps/pinata/templates/pinata/proposal.html b/kcdc3/apps/pinata/templates/pinata/proposal.html new file mode 100644 index 00000000..b96e7b93 --- /dev/null +++ b/kcdc3/apps/pinata/templates/pinata/proposal.html @@ -0,0 +1,38 @@ +{% extends "pinata/page.html" %} + + +{% block content %} + +
    +

    {{ title }}

    +
    + +
    + + {{ main_text|safe }} + + +
    + Fill out the proposal form. +
    + + +
    + +{% endblock %} + + + diff --git a/kcdc3/apps/pinata/templates/pinata/staff.html b/kcdc3/apps/pinata/templates/pinata/staff.html new file mode 100644 index 00000000..dfaeda6a --- /dev/null +++ b/kcdc3/apps/pinata/templates/pinata/staff.html @@ -0,0 +1,89 @@ +{% extends "pinata/page.html" %} + + + +{% block title %} + / {{ short_title }} +{% endblock %} + + + +{% block content %} + +
    +

    {{ title }}

    +
    + +
    + + {{ main_text|safe }} + +

    The Wonder Dojo

    + {{ dojo_role.description|safe }} +
    + {% for bio in dojo %} + + {% endfor %} +
    + {{ dojo_role.extended_description|safe }} + +

    Volunteers

    + {{ volunteers_role.description|safe }} + {% if volunteers %} +
    + {% for bio in volunteers %} +
    +
    {{ bio.name }}
    +

    {{ bio.title }}

    +
    + {% endfor %} +
    + {% endif %} + {{ volunteers_role.extended_description|safe }} + +

    Dojo-at-Large and Emeritus

    + + {#

    Dojo-at-Large

    #} + {# {{ dojo_at_large_role.description|safe }} #} + {% if dojo_at_large %} +

    + At-Large: + {% for bio in dojo_at_large %} + {{ bio.name }}{% if bio.title %} ({{ bio.title }}){% endif %}{% if not forloop.last %}, {% endif %} + {% endfor %} +

    + {% endif %} + {{ dojo_at_large_role.extended_description|safe }} + + {#

    Dojo Emeritus

    #} + {# {{ dojo_emeritus_role.description|safe }} #} + {% if dojo_emeritus %} +

    + Emeritus: + {% for bio in dojo_emeritus %} + {{ bio.name }}{% if bio.title %} ({{ bio.title }}){% endif %}{% if not forloop.last %}, {% endif %} + {% endfor %} +

    + {% endif %} + {{ dojo_emeritus_role.extended_description|safe }} + +
    + +{% endblock %} + + + +{% block sidebar_text %} + {{ sidebar_text|safe }} +{% endblock %} + diff --git a/kcdc3/apps/pinata/templatetags/__init__.py b/kcdc3/apps/pinata/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kcdc3/apps/pinata/templatetags/pinata_filters.py b/kcdc3/apps/pinata/templatetags/pinata_filters.py new file mode 100644 index 00000000..d1ed5457 --- /dev/null +++ b/kcdc3/apps/pinata/templatetags/pinata_filters.py @@ -0,0 +1,8 @@ +from django import template +from smartypants import smartypants + +register = template.Library() + +@register.filter +def smartquotes(text): + return smartypants(text) diff --git a/kcdc3/apps/pinata/urls.py b/kcdc3/apps/pinata/urls.py new file mode 100644 index 00000000..96b2b8ab --- /dev/null +++ b/kcdc3/apps/pinata/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls import patterns, include, url +from models import Page + +urlpatterns = patterns('kcdc3.apps.pinata.views', + + url(r'^$', 'page_view'), + url(r'^[0-9a-zA-Z_-]+/$', 'page_view'), + +) diff --git a/kcdc3/apps/pinata/views.py b/kcdc3/apps/pinata/views.py new file mode 100644 index 00000000..d572eb34 --- /dev/null +++ b/kcdc3/apps/pinata/views.py @@ -0,0 +1,204 @@ +from django.http import HttpRequest, HttpResponse, HttpResponseRedirect, Http404 +from django.shortcuts import render_to_response +from datetime import datetime +from django.template import Context +from django.views.generic import DetailView, TemplateView, ListView +from django.db.models import Q +from models import Page, Notice, Slide, Sponsor, PressClipping +from kcdc3.apps.classes.models import Bio, Role, Event, Session +from kcdc3.apps.pigeon.models import Post + + +def page_view(request): + """ Handle requests for pages. """ + + context = Context() + context['user'] = request.user + + # debug + # print('path: "'+request.path+'"') + + # Remove trailing slashes from incoming path. + # Probably best handled in urls.py, + # and there is probably a more future-proof way of doing this. + search_path = request.path.rstrip('/') + + # debug + # print('search path: "'+search_path+'"') + + e = Page.objects.get(path=search_path) + + context['title'] = e.title + context['short_title'] = e.short_title + context['id'] = e.id + context['path'] = e.path + context['teaser'] = e.teaser + context['main_text'] = e.main_text + context['sidebar_text'] = e.sidebar_text + context['parent'] = e.parent + + # get all other pages with the same parent + if e.parent: + context['siblings'] = Page.objects.filter(Q(parent=e.parent)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + # get all children + context['children'] = Page.objects.filter(Q(parent=e)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + # set up templates + template_path = 'pinata/' + e.template + + if e.status == 'PUBLISHED' or e.status == 'HIDDEN': + return render_to_response(template_path, context) + else: + raise Http404 + + + +def staff(request): + """ Front page of the About section. + Includes staff and volunteer listings. """ + + """ TODO: This really ought to be a class-based view """ + + context = Context() + context['user'] = request.user + + e = Page.objects.get(path='/about') + print(e.main_text) + context['main_text'] = e.main_text + context['short_title'] = e.short_title + context['title'] = e.title + + context['dojo'] = Bio.objects.filter(role__name='Dojo') + context['volunteers'] = Bio.objects.filter(role__name='Volunteers') + context['dojo_at_large'] = Bio.objects.filter(role__name='Dojo-at-Large') + context['dojo_emeritus'] = Bio.objects.filter(role__name='Dojo Emeritus') + + context['dojo_role'] = Role.objects.get(name='Dojo') + context['volunteers_role'] = Role.objects.get(name='Volunteers') + context['dojo_at_large_role'] = Role.objects.get(name='Dojo-at-Large') + context['dojo_emeritus_role'] = Role.objects.get(name='Dojo Emeritus') + + # get all other pages with the same parent + if e.parent: + context['siblings'] = Page.objects.filter(Q(parent=e.parent)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + # todo: rewrite this so it returns section home and subpages + # todo: write new function that returns children only for non-section-homes + + # get all children + context['children'] = Page.objects.filter(Q(parent=e)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + return render_to_response('pinata/staff.html',context) + + + +def home(request): + """ Sitewide home page """ + + context = Context() + context['user'] = request.user + + # Get information for front page content + context['notices'] = Notice.objects.filter(live=True) + context['slides'] = Slide.objects.filter(live=True) + + # Pull content from elsewhere in the site + context['events'] = Event.objects.filter(status='PUBLISHED', session__status="CURRENT", featured=True).order_by('date')[:4] + context['posts'] = Post.objects.filter(status='PUBLISHED').filter(featured=True).exclude(date__gte=datetime.now())[:4] + + return render_to_response('pinata/home.html',context) + + + + +def proposal(request): + """ Course proposal page """ + + """ TODO: The observant reader may note that this method + is identical to contribute(), except for the template name. + Really, though, it would be best to modify page_view() and + Page so that admins could choose a template on the backend. """ + + context = Context() + context['user'] = request.user + + e = Page.objects.get(path='/teach/proposal') + print(e.main_text) + context['main_text'] = e.main_text + context['short_title'] = e.short_title + context['title'] = e.title + context['id'] = e.id + + # get all other pages with the same parent + if e.parent: + context['siblings'] = Page.objects.filter(Q(parent=e.parent)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + # get all children + context['children'] = Page.objects.filter(Q(parent=e)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + return render_to_response('pinata/proposal.html',context) + + + + + +def partners(request): + """ List of sponsors. """ + + """ TODO: This really ought to be a class-based view """ + + context = Context() + context['user'] = request.user + + e = Page.objects.get(path='/about/partners') + print(e.main_text) + context['main_text'] = e.main_text + context['short_title'] = e.short_title + context['title'] = e.title + context['id'] = e.id + + context['sponsors_A'] = Sponsor.objects.filter(group='A').filter(status='PUBLISHED') + context['sponsors_B'] = Sponsor.objects.filter(group='B').filter(status='PUBLISHED') + context['sponsors_S'] = Sponsor.objects.filter(group='S').filter(status='PUBLISHED') + + # get all other pages with the same parent + if e.parent: + context['siblings'] = Page.objects.filter(Q(parent=e.parent)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + # get all children + context['children'] = Page.objects.filter(Q(parent=e)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + return render_to_response('pinata/partners.html',context) + + + + +def pressclippings(request): + """ List of press clippings. """ + + """ TODO: This really ought to class-based view """ + + context = Context() + context['user'] = request.user + + e = Page.objects.get(path='/press') + print(e.main_text) + context['main_text'] = e.main_text + context['short_title'] = e.short_title + context['title'] = e.title + context['id'] = e.id + + context['pressclippings'] = PressClipping.objects.filter(status='PUBLISHED') + + # get all other pages with the same parent + if e.parent: + context['siblings'] = Page.objects.filter(Q(parent=e.parent)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + # get all children + context['children'] = Page.objects.filter(Q(parent=e)&Q(status='PUBLISHED')).order_by('sort_order', 'path',) + + return render_to_response('pinata/pressclippings.html',context) + + + diff --git a/kcdc3/classes/urls.py b/kcdc3/classes/urls.py deleted file mode 100644 index 596df79c..00000000 --- a/kcdc3/classes/urls.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.conf.urls import patterns, include, url -from classes.models import Event, Registration -from classes.views import EventListView, EventDetailView, ResponseTemplateView, EventArchiveView, SessionView, RegistrationListView - -urlpatterns = patterns('classes.views', - - url(r'^$', EventListView.as_view()), - url(r'^dashboard/registrations/(?P[A-Za-z0-9_-]+)$', RegistrationListView.as_view()), - url(r'^(?P[0-9_-]+)/$', EventArchiveView.as_view()), - url(r'^(?P[0-9_-]+)/background/$', SessionView.as_view()), - url(r'^response/(?P[A-Za-z0-9_-]+)$', ResponseTemplateView.as_view()), - url(r'^(?P[A-Za-z0-9_-]+)/$', EventDetailView.as_view(model=Event,)), - url(r'^(?P[A-Za-z0-9_-]+)/register$', 'register'), - url(r'^(?P[A-Za-z0-9_-]+)/cancel$', 'cancel'), - url(r'^(?P[A-Za-z0-9_-]+)/facilitator$', 'facilitator'), - -) diff --git a/kcdc3/kcdc3/urls.py b/kcdc3/kcdc3/urls.py deleted file mode 100644 index 385a7152..00000000 --- a/kcdc3/kcdc3/urls.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.conf.urls.defaults import * -from accounts.views import SignupFormExtra -from filebrowser.sites import site - -# development -from django.contrib.staticfiles.urls import staticfiles_urlpatterns - -# Uncomment the next two lines to enable the admin: -from django.contrib import admin -admin.autodiscover() - -urlpatterns = patterns('', - - url(r'^$', include('classes.urls')), - url(r'^classes/', include('classes.urls')), - url(r'^admin/filebrowser/', include(site.urls)), - url(r'^admin/doc/', include('django.contrib.admindocs.urls')), - url(r'^admin/', include(admin.site.urls)), - url(r'^grappelli/', include('grappelli.urls')), - url(r'^accounts/signup/$', 'userena.views.signup', {'signup_form': SignupFormExtra}), - url(r'^accounts/', include('userena.urls')), - -) - -# development -urlpatterns += staticfiles_urlpatterns() diff --git a/kcdc3/passenger_wsgi.py b/kcdc3/passenger_wsgi.py deleted file mode 100644 index e3a683a9..00000000 --- a/kcdc3/passenger_wsgi.py +++ /dev/null @@ -1,21 +0,0 @@ -import sys, os -INTERP = "/home/kcdc/.pythonbrew/venvs/Python-2.7.3/kcdc_production/bin/python" -if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv) -sys.path.append(os.getcwd()) -os.environ['DJANGO_SETTINGS_MODULE'] = "kcdc3.settings" -import django.core.handlers.wsgi -application = django.core.handlers.wsgi.WSGIHandler() - -# To cut django out of the loop, comment the above application = ... line , -# and remove "test" from the below function definition. -# def application(environ, start_response): -# status = '200 OK' -# output = 'Hello World! Running Python version ' + sys.version + '\n\n' -# response_headers = [('Content-type', 'text/plain'), -# ('Content-Length', str(len(output)))] -# # to test paste's error catching prowess, uncomment the following line -# # while this function is the "application" -# raise("error") -# start_response(status, response_headers) -# return [output] -# application = ErrorMiddleware(application, debug=True) diff --git a/kcdc3/paste/__init__.py b/kcdc3/paste/__init__.py deleted file mode 100644 index ba666066..00000000 --- a/kcdc3/paste/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -try: - import pkg_resources - pkg_resources.declare_namespace(__name__) -except ImportError: - # don't prevent use of paste if pkg_resources isn't installed - from pkgutil import extend_path - __path__ = extend_path(__path__, __name__) - -try: - import modulefinder -except ImportError: - pass -else: - for p in __path__: - modulefinder.AddPackagePath(__name__, p) diff --git a/kcdc3/paste/auth/__init__.py b/kcdc3/paste/auth/__init__.py deleted file mode 100644 index 186e2eff..00000000 --- a/kcdc3/paste/auth/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -Package for authentication/identification of requests. - -The objective of this package is to provide single-focused middleware -components that implement a particular specification. Integration of -the components into a usable system is up to a higher-level framework. -""" diff --git a/kcdc3/paste/auth/auth_tkt.py b/kcdc3/paste/auth/auth_tkt.py deleted file mode 100644 index 8dfce00a..00000000 --- a/kcdc3/paste/auth/auth_tkt.py +++ /dev/null @@ -1,396 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -########################################################################## -# -# Copyright (c) 2005 Imaginary Landscape LLC and Contributors. -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -########################################################################## -""" -Implementation of cookie signing as done in `mod_auth_tkt -`_. - -mod_auth_tkt is an Apache module that looks for these signed cookies -and sets ``REMOTE_USER``, ``REMOTE_USER_TOKENS`` (a comma-separated -list of groups) and ``REMOTE_USER_DATA`` (arbitrary string data). - -This module is an alternative to the ``paste.auth.cookie`` module; -it's primary benefit is compatibility with mod_auth_tkt, which in turn -makes it possible to use the same authentication process with -non-Python code run under Apache. -""" - -import time as time_mod -try: - from hashlib import md5 -except ImportError: - from md5 import md5 -import Cookie -from paste import request -from urllib import quote as url_quote -from urllib import unquote as url_unquote - - -class AuthTicket(object): - - """ - This class represents an authentication token. You must pass in - the shared secret, the userid, and the IP address. Optionally you - can include tokens (a list of strings, representing role names), - 'user_data', which is arbitrary data available for your own use in - later scripts. Lastly, you can override the cookie name and - timestamp. - - Once you provide all the arguments, use .cookie_value() to - generate the appropriate authentication ticket. .cookie() - generates a Cookie object, the str() of which is the complete - cookie header to be sent. - - CGI usage:: - - token = auth_tkt.AuthTick('sharedsecret', 'username', - os.environ['REMOTE_ADDR'], tokens=['admin']) - print 'Status: 200 OK' - print 'Content-type: text/html' - print token.cookie() - print - ... redirect HTML ... - - Webware usage:: - - token = auth_tkt.AuthTick('sharedsecret', 'username', - self.request().environ()['REMOTE_ADDR'], tokens=['admin']) - self.response().setCookie('auth_tkt', token.cookie_value()) - - Be careful not to do an HTTP redirect after login; use meta - refresh or Javascript -- some browsers have bugs where cookies - aren't saved when set on a redirect. - """ - - def __init__(self, secret, userid, ip, tokens=(), user_data='', - time=None, cookie_name='auth_tkt', - secure=False): - self.secret = secret - self.userid = userid - self.ip = ip - self.tokens = ','.join(tokens) - self.user_data = user_data - if time is None: - self.time = time_mod.time() - else: - self.time = time - self.cookie_name = cookie_name - self.secure = secure - - def digest(self): - return calculate_digest( - self.ip, self.time, self.secret, self.userid, self.tokens, - self.user_data) - - def cookie_value(self): - v = '%s%08x%s!' % (self.digest(), int(self.time), url_quote(self.userid)) - if self.tokens: - v += self.tokens + '!' - v += self.user_data - return v - - def cookie(self): - c = Cookie.SimpleCookie() - c[self.cookie_name] = self.cookie_value().encode('base64').strip().replace('\n', '') - c[self.cookie_name]['path'] = '/' - if self.secure: - c[self.cookie_name]['secure'] = 'true' - return c - - -class BadTicket(Exception): - """ - Exception raised when a ticket can't be parsed. If we get - far enough to determine what the expected digest should have - been, expected is set. This should not be shown by default, - but can be useful for debugging. - """ - def __init__(self, msg, expected=None): - self.expected = expected - Exception.__init__(self, msg) - - -def parse_ticket(secret, ticket, ip): - """ - Parse the ticket, returning (timestamp, userid, tokens, user_data). - - If the ticket cannot be parsed, ``BadTicket`` will be raised with - an explanation. - """ - ticket = ticket.strip('"') - digest = ticket[:32] - try: - timestamp = int(ticket[32:40], 16) - except ValueError, e: - raise BadTicket('Timestamp is not a hex integer: %s' % e) - try: - userid, data = ticket[40:].split('!', 1) - except ValueError: - raise BadTicket('userid is not followed by !') - userid = url_unquote(userid) - if '!' in data: - tokens, user_data = data.split('!', 1) - else: - # @@: Is this the right order? - tokens = '' - user_data = data - - expected = calculate_digest(ip, timestamp, secret, - userid, tokens, user_data) - - if expected != digest: - raise BadTicket('Digest signature is not correct', - expected=(expected, digest)) - - tokens = tokens.split(',') - - return (timestamp, userid, tokens, user_data) - - -def calculate_digest(ip, timestamp, secret, userid, tokens, user_data): - secret = maybe_encode(secret) - userid = maybe_encode(userid) - tokens = maybe_encode(tokens) - user_data = maybe_encode(user_data) - digest0 = md5( - encode_ip_timestamp(ip, timestamp) + secret + userid + '\0' - + tokens + '\0' + user_data).hexdigest() - digest = md5(digest0 + secret).hexdigest() - return digest - - -def encode_ip_timestamp(ip, timestamp): - ip_chars = ''.join(map(chr, map(int, ip.split('.')))) - t = int(timestamp) - ts = ((t & 0xff000000) >> 24, - (t & 0xff0000) >> 16, - (t & 0xff00) >> 8, - t & 0xff) - ts_chars = ''.join(map(chr, ts)) - return ip_chars + ts_chars - - -def maybe_encode(s, encoding='utf8'): - if isinstance(s, unicode): - s = s.encode(encoding) - return s - - -class AuthTKTMiddleware(object): - - """ - Middleware that checks for signed cookies that match what - `mod_auth_tkt `_ - looks for (if you have mod_auth_tkt installed, you don't need this - middleware, since Apache will set the environmental variables for - you). - - Arguments: - - ``secret``: - A secret that should be shared by any instances of this application. - If this app is served from more than one machine, they should all - have the same secret. - - ``cookie_name``: - The name of the cookie to read and write from. Default ``auth_tkt``. - - ``secure``: - If the cookie should be set as 'secure' (only sent over SSL) and if - the login must be over SSL. (Defaults to False) - - ``httponly``: - If the cookie should be marked as HttpOnly, which means that it's - not accessible to JavaScript. (Defaults to False) - - ``include_ip``: - If the cookie should include the user's IP address. If so, then - if they change IPs their cookie will be invalid. - - ``logout_path``: - The path under this middleware that should signify a logout. The - page will be shown as usual, but the user will also be logged out - when they visit this page. - - If used with mod_auth_tkt, then these settings (except logout_path) should - match the analogous Apache configuration settings. - - This also adds two functions to the request: - - ``environ['paste.auth_tkt.set_user'](userid, tokens='', user_data='')`` - - This sets a cookie that logs the user in. ``tokens`` is a - string (comma-separated groups) or a list of strings. - ``user_data`` is a string for your own use. - - ``environ['paste.auth_tkt.logout_user']()`` - - Logs out the user. - """ - - def __init__(self, app, secret, cookie_name='auth_tkt', secure=False, - include_ip=True, logout_path=None, httponly=False, - no_domain_cookie=True, current_domain_cookie=True, - wildcard_cookie=True): - self.app = app - self.secret = secret - self.cookie_name = cookie_name - self.secure = secure - self.httponly = httponly - self.include_ip = include_ip - self.logout_path = logout_path - self.no_domain_cookie = no_domain_cookie - self.current_domain_cookie = current_domain_cookie - self.wildcard_cookie = wildcard_cookie - - def __call__(self, environ, start_response): - cookies = request.get_cookies(environ) - if self.cookie_name in cookies: - cookie_value = cookies[self.cookie_name].value - else: - cookie_value = '' - if cookie_value: - if self.include_ip: - remote_addr = environ['REMOTE_ADDR'] - else: - # mod_auth_tkt uses this dummy value when IP is not - # checked: - remote_addr = '0.0.0.0' - # @@: This should handle bad signatures better: - # Also, timeouts should cause cookie refresh - try: - timestamp, userid, tokens, user_data = parse_ticket( - self.secret, cookie_value, remote_addr) - tokens = ','.join(tokens) - environ['REMOTE_USER'] = userid - if environ.get('REMOTE_USER_TOKENS'): - # We want to add tokens/roles to what's there: - tokens = environ['REMOTE_USER_TOKENS'] + ',' + tokens - environ['REMOTE_USER_TOKENS'] = tokens - environ['REMOTE_USER_DATA'] = user_data - environ['AUTH_TYPE'] = 'cookie' - except BadTicket: - # bad credentials, just ignore without logging the user - # in or anything - pass - set_cookies = [] - - def set_user(userid, tokens='', user_data=''): - set_cookies.extend(self.set_user_cookie( - environ, userid, tokens, user_data)) - - def logout_user(): - set_cookies.extend(self.logout_user_cookie(environ)) - - environ['paste.auth_tkt.set_user'] = set_user - environ['paste.auth_tkt.logout_user'] = logout_user - if self.logout_path and environ.get('PATH_INFO') == self.logout_path: - logout_user() - - def cookie_setting_start_response(status, headers, exc_info=None): - headers.extend(set_cookies) - return start_response(status, headers, exc_info) - - return self.app(environ, cookie_setting_start_response) - - def set_user_cookie(self, environ, userid, tokens, user_data): - if not isinstance(tokens, basestring): - tokens = ','.join(tokens) - if self.include_ip: - remote_addr = environ['REMOTE_ADDR'] - else: - remote_addr = '0.0.0.0' - ticket = AuthTicket( - self.secret, - userid, - remote_addr, - tokens=tokens, - user_data=user_data, - cookie_name=self.cookie_name, - secure=self.secure) - # @@: Should we set REMOTE_USER etc in the current - # environment right now as well? - cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME')) - wild_domain = '.' + cur_domain - - cookie_options = "" - if self.secure: - cookie_options += "; secure" - if self.httponly: - cookie_options += "; HttpOnly" - - cookies = [] - if self.no_domain_cookie: - cookies.append(('Set-Cookie', '%s=%s; Path=/%s' % ( - self.cookie_name, ticket.cookie_value(), cookie_options))) - if self.current_domain_cookie: - cookies.append(('Set-Cookie', '%s=%s; Path=/; Domain=%s%s' % ( - self.cookie_name, ticket.cookie_value(), cur_domain, - cookie_options))) - if self.wildcard_cookie: - cookies.append(('Set-Cookie', '%s=%s; Path=/; Domain=%s%s' % ( - self.cookie_name, ticket.cookie_value(), wild_domain, - cookie_options))) - - return cookies - - def logout_user_cookie(self, environ): - cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME')) - wild_domain = '.' + cur_domain - expires = 'Sat, 01-Jan-2000 12:00:00 GMT' - cookies = [ - ('Set-Cookie', '%s=""; Expires="%s"; Path=/' % (self.cookie_name, expires)), - ('Set-Cookie', '%s=""; Expires="%s"; Path=/; Domain=%s' % - (self.cookie_name, expires, cur_domain)), - ('Set-Cookie', '%s=""; Expires="%s"; Path=/; Domain=%s' % - (self.cookie_name, expires, wild_domain)), - ] - return cookies - - -def make_auth_tkt_middleware( - app, - global_conf, - secret=None, - cookie_name='auth_tkt', - secure=False, - include_ip=True, - logout_path=None): - """ - Creates the `AuthTKTMiddleware - `_. - - ``secret`` is requird, but can be set globally or locally. - """ - from paste.deploy.converters import asbool - secure = asbool(secure) - include_ip = asbool(include_ip) - if secret is None: - secret = global_conf.get('secret') - if not secret: - raise ValueError( - "You must provide a 'secret' (in global or local configuration)") - return AuthTKTMiddleware( - app, secret, cookie_name, secure, include_ip, logout_path or None) diff --git a/kcdc3/paste/auth/basic.py b/kcdc3/paste/auth/basic.py deleted file mode 100644 index 69db1287..00000000 --- a/kcdc3/paste/auth/basic.py +++ /dev/null @@ -1,122 +0,0 @@ -# (c) 2005 Clark C. Evans -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -# This code was written with funding by http://prometheusresearch.com -""" -Basic HTTP/1.0 Authentication - -This module implements ``Basic`` authentication as described in -HTTP/1.0 specification [1]_ . Do not use this module unless you -are using SSL or need to work with very out-dated clients, instead -use ``digest`` authentication. - ->>> from paste.wsgilib import dump_environ ->>> from paste.httpserver import serve ->>> # from paste.auth.basic import AuthBasicHandler ->>> realm = 'Test Realm' ->>> def authfunc(environ, username, password): -... return username == password ->>> serve(AuthBasicHandler(dump_environ, realm, authfunc)) -serving on... - -.. [1] http://www.w3.org/Protocols/HTTP/1.0/draft-ietf-http-spec.html#BasicAA -""" -from paste.httpexceptions import HTTPUnauthorized -from paste.httpheaders import * - -class AuthBasicAuthenticator(object): - """ - implements ``Basic`` authentication details - """ - type = 'basic' - def __init__(self, realm, authfunc): - self.realm = realm - self.authfunc = authfunc - - def build_authentication(self): - head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm) - return HTTPUnauthorized(headers=head) - - def authenticate(self, environ): - authorization = AUTHORIZATION(environ) - if not authorization: - return self.build_authentication() - (authmeth, auth) = authorization.split(' ', 1) - if 'basic' != authmeth.lower(): - return self.build_authentication() - auth = auth.strip().decode('base64') - username, password = auth.split(':', 1) - if self.authfunc(environ, username, password): - return username - return self.build_authentication() - - __call__ = authenticate - -class AuthBasicHandler(object): - """ - HTTP/1.0 ``Basic`` authentication middleware - - Parameters: - - ``application`` - - The application object is called only upon successful - authentication, and can assume ``environ['REMOTE_USER']`` - is set. If the ``REMOTE_USER`` is already set, this - middleware is simply pass-through. - - ``realm`` - - This is a identifier for the authority that is requesting - authorization. It is shown to the user and should be unique - within the domain it is being used. - - ``authfunc`` - - This is a mandatory user-defined function which takes a - ``environ``, ``username`` and ``password`` for its first - three arguments. It should return ``True`` if the user is - authenticated. - - """ - def __init__(self, application, realm, authfunc): - self.application = application - self.authenticate = AuthBasicAuthenticator(realm, authfunc) - - def __call__(self, environ, start_response): - username = REMOTE_USER(environ) - if not username: - result = self.authenticate(environ) - if isinstance(result, str): - AUTH_TYPE.update(environ, 'basic') - REMOTE_USER.update(environ, result) - else: - return result.wsgi_application(environ, start_response) - return self.application(environ, start_response) - -middleware = AuthBasicHandler - -__all__ = ['AuthBasicHandler'] - -def make_basic(app, global_conf, realm, authfunc, **kw): - """ - Grant access via basic authentication - - Config looks like this:: - - [filter:grant] - use = egg:Paste#auth_basic - realm=myrealm - authfunc=somepackage.somemodule:somefunction - - """ - from paste.util.import_string import eval_import - import types - authfunc = eval_import(authfunc) - assert isinstance(authfunc, types.FunctionType), "authfunc must resolve to a function" - return AuthBasicHandler(app, realm, authfunc) - - -if "__main__" == __name__: - import doctest - doctest.testmod(optionflags=doctest.ELLIPSIS) diff --git a/kcdc3/paste/auth/cas.py b/kcdc3/paste/auth/cas.py deleted file mode 100644 index c3521a06..00000000 --- a/kcdc3/paste/auth/cas.py +++ /dev/null @@ -1,99 +0,0 @@ -# (c) 2005 Clark C. Evans -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -# This code was written with funding by http://prometheusresearch.com -""" -CAS 1.0 Authentication - -The Central Authentication System is a straight-forward single sign-on -mechanism developed by Yale University's ITS department. It has since -enjoyed widespread success and is deployed at many major universities -and some corporations. - - https://clearinghouse.ja-sig.org/wiki/display/CAS/Home - http://www.yale.edu/tp/auth/usingcasatyale.html - -This implementation has the goal of maintaining current path arguments -passed to the system so that it can be used as middleware at any stage -of processing. It has the secondary goal of allowing for other -authentication methods to be used concurrently. -""" -import urllib -from paste.request import construct_url -from paste.httpexceptions import HTTPSeeOther, HTTPForbidden - -class CASLoginFailure(HTTPForbidden): - """ The exception raised if the authority returns 'no' """ - -class CASAuthenticate(HTTPSeeOther): - """ The exception raised to authenticate the user """ - -def AuthCASHandler(application, authority): - """ - middleware to implement CAS 1.0 authentication - - There are several possible outcomes: - - 0. If the REMOTE_USER environment variable is already populated; - then this middleware is a no-op, and the request is passed along - to the application. - - 1. If a query argument 'ticket' is found, then an attempt to - validate said ticket /w the authentication service done. If the - ticket is not validated; an 403 'Forbidden' exception is raised. - Otherwise, the REMOTE_USER variable is set with the NetID that - was validated and AUTH_TYPE is set to "cas". - - 2. Otherwise, a 303 'See Other' is returned to the client directing - them to login using the CAS service. After logon, the service - will send them back to this same URL, only with a 'ticket' query - argument. - - Parameters: - - ``authority`` - - This is a fully-qualified URL to a CAS 1.0 service. The URL - should end with a '/' and have the 'login' and 'validate' - sub-paths as described in the CAS 1.0 documentation. - - """ - assert authority.endswith("/") and authority.startswith("http") - def cas_application(environ, start_response): - username = environ.get('REMOTE_USER','') - if username: - return application(environ, start_response) - qs = environ.get('QUERY_STRING','').split("&") - if qs and qs[-1].startswith("ticket="): - # assume a response from the authority - ticket = qs.pop().split("=", 1)[1] - environ['QUERY_STRING'] = "&".join(qs) - service = construct_url(environ) - args = urllib.urlencode( - {'service': service,'ticket': ticket}) - requrl = authority + "validate?" + args - result = urllib.urlopen(requrl).read().split("\n") - if 'yes' == result[0]: - environ['REMOTE_USER'] = result[1] - environ['AUTH_TYPE'] = 'cas' - return application(environ, start_response) - exce = CASLoginFailure() - else: - service = construct_url(environ) - args = urllib.urlencode({'service': service}) - location = authority + "login?" + args - exce = CASAuthenticate(location) - return exce.wsgi_application(environ, start_response) - return cas_application - -middleware = AuthCASHandler - -__all__ = ['CASLoginFailure', 'CASAuthenticate', 'AuthCASHandler' ] - -if '__main__' == __name__: - authority = "https://secure.its.yale.edu/cas/servlet/" - from paste.wsgilib import dump_environ - from paste.httpserver import serve - from paste.httpexceptions import * - serve(HTTPExceptionHandler( - AuthCASHandler(dump_environ, authority))) diff --git a/kcdc3/paste/auth/cookie.py b/kcdc3/paste/auth/cookie.py deleted file mode 100644 index 2601a904..00000000 --- a/kcdc3/paste/auth/cookie.py +++ /dev/null @@ -1,396 +0,0 @@ -# (c) 2005 Clark C. Evans -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -# This code was written with funding by http://prometheusresearch.com -""" -Cookie "Saved" Authentication - -This authentication middleware saves the current REMOTE_USER, -REMOTE_SESSION, and any other environment variables specified in a -cookie so that it can be retrieved during the next request without -requiring re-authentication. This uses a session cookie on the client -side (so it goes away when the user closes their window) and does -server-side expiration. - -Following is a very simple example where a form is presented asking for -a user name (no actual checking), and dummy session identifier (perhaps -corresponding to a database session id) is stored in the cookie. - -:: - - >>> from paste.httpserver import serve - >>> from paste.fileapp import DataApp - >>> from paste.httpexceptions import * - >>> from paste.auth.cookie import AuthCookieHandler - >>> from paste.wsgilib import parse_querystring - >>> def testapp(environ, start_response): - ... user = dict(parse_querystring(environ)).get('user','') - ... if user: - ... environ['REMOTE_USER'] = user - ... environ['REMOTE_SESSION'] = 'a-session-id' - ... if environ.get('REMOTE_USER'): - ... page = 'Welcome %s (%s)' - ... page %= (environ['REMOTE_USER'], environ['REMOTE_SESSION']) - ... else: - ... page = ('
    ' - ... '
    ') - ... return DataApp(page, content_type="text/html")( - ... environ, start_response) - >>> serve(AuthCookieHandler(testapp)) - serving on... - -""" - -import hmac, base64, random, time, warnings -try: - from hashlib import sha1 -except ImportError: - # NOTE: We have to use the callable with hashlib (hashlib.sha1), - # otherwise hmac only accepts the sha module object itself - import sha as sha1 -from paste.request import get_cookies - -def make_time(value): - return time.strftime("%Y%m%d%H%M", time.gmtime(value)) -_signature_size = len(hmac.new('x', 'x', sha1).digest()) -_header_size = _signature_size + len(make_time(time.time())) - -# @@: Should this be using urllib.quote? -# build encode/decode functions to safely pack away values -_encode = [('\\', '\\x5c'), ('"', '\\x22'), - ('=', '\\x3d'), (';', '\\x3b')] -_decode = [(v, k) for (k, v) in _encode] -_decode.reverse() -def encode(s, sublist = _encode): - return reduce((lambda a, (b, c): a.replace(b, c)), sublist, str(s)) -decode = lambda s: encode(s, _decode) - -class CookieTooLarge(RuntimeError): - def __init__(self, content, cookie): - RuntimeError.__init__("Signed cookie exceeds maximum size of 4096") - self.content = content - self.cookie = cookie - -_all_chars = ''.join([chr(x) for x in range(0, 255)]) -def new_secret(): - """ returns a 64 byte secret """ - return ''.join(random.sample(_all_chars, 64)) - -class AuthCookieSigner(object): - """ - save/restore ``environ`` entries via digially signed cookie - - This class converts content into a timed and digitally signed - cookie, as well as having the facility to reverse this procedure. - If the cookie, after the content is encoded and signed exceeds the - maximum length (4096), then CookieTooLarge exception is raised. - - The timeout of the cookie is handled on the server side for a few - reasons. First, if a 'Expires' directive is added to a cookie, then - the cookie becomes persistent (lasting even after the browser window - has closed). Second, the user's clock may be wrong (perhaps - intentionally). The timeout is specified in minutes; and expiration - date returned is rounded to one second. - - Constructor Arguments: - - ``secret`` - - This is a secret key if you want to syncronize your keys so - that the cookie will be good across a cluster of computers. - It is recommended via the HMAC specification (RFC 2104) that - the secret key be 64 bytes since this is the block size of - the hashing. If you do not provide a secret key, a random - one is generated each time you create the handler; this - should be sufficient for most cases. - - ``timeout`` - - This is the time (in minutes) from which the cookie is set - to expire. Note that on each request a new (replacement) - cookie is sent, hence this is effectively a session timeout - parameter for your entire cluster. If you do not provide a - timeout, it is set at 30 minutes. - - ``maxlen`` - - This is the maximum size of the *signed* cookie; hence the - actual content signed will be somewhat less. If the cookie - goes over this size, a ``CookieTooLarge`` exception is - raised so that unexpected handling of cookies on the client - side are avoided. By default this is set at 4k (4096 bytes), - which is the standard cookie size limit. - - """ - def __init__(self, secret = None, timeout = None, maxlen = None): - self.timeout = timeout or 30 - if isinstance(timeout, basestring): - raise ValueError( - "Timeout must be a number (minutes), not a string (%r)" - % timeout) - self.maxlen = maxlen or 4096 - self.secret = secret or new_secret() - - def sign(self, content): - """ - Sign the content returning a valid cookie (that does not - need to be escaped and quoted). The expiration of this - cookie is handled server-side in the auth() function. - """ - cookie = base64.encodestring( - hmac.new(self.secret, content, sha1).digest() + - make_time(time.time() + 60*self.timeout) + - content) - cookie = cookie.replace("/", "_").replace("=", "~") - cookie = cookie.replace('\n', '').replace('\r', '') - if len(cookie) > self.maxlen: - raise CookieTooLarge(content, cookie) - return cookie - - def auth(self, cookie): - """ - Authenticate the cooke using the signature, verify that it - has not expired; and return the cookie's content - """ - decode = base64.decodestring( - cookie.replace("_", "/").replace("~", "=")) - signature = decode[:_signature_size] - expires = decode[_signature_size:_header_size] - content = decode[_header_size:] - if signature == hmac.new(self.secret, content, sha1).digest(): - if int(expires) > int(make_time(time.time())): - return content - else: - # This is the normal case of an expired cookie; just - # don't bother doing anything here. - pass - else: - # This case can happen if the server is restarted with a - # different secret; or if the user's IP address changed - # due to a proxy. However, it could also be a break-in - # attempt -- so should it be reported? - pass - -class AuthCookieEnviron(list): - """ - a list of environment keys to be saved via cookie - - An instance of this object, found at ``environ['paste.auth.cookie']`` - lists the `environ` keys that were restored from or will be added - to the digially signed cookie. This object can be accessed from an - `environ` variable by using this module's name. - """ - def __init__(self, handler, scanlist): - list.__init__(self, scanlist) - self.handler = handler - def append(self, value): - if value in self: - return - list.append(self, str(value)) - -class AuthCookieHandler(object): - """ - the actual handler that should be put in your middleware stack - - This middleware uses cookies to stash-away a previously authenticated - user (and perhaps other variables) so that re-authentication is not - needed. This does not implement sessions; and therefore N servers - can be syncronized to accept the same saved authentication if they - all use the same cookie_name and secret. - - By default, this handler scans the `environ` for the REMOTE_USER - and REMOTE_SESSION key; if found, it is stored. It can be - configured to scan other `environ` keys as well -- but be careful - not to exceed 2-3k (so that the encoded and signed cookie does not - exceed 4k). You can ask it to handle other environment variables - by doing: - - ``environ['paste.auth.cookie'].append('your.environ.variable')`` - - - Constructor Arguments: - - ``application`` - - This is the wrapped application which will have access to - the ``environ['REMOTE_USER']`` restored by this middleware. - - ``cookie_name`` - - The name of the cookie used to store this content, by default - it is ``PASTE_AUTH_COOKIE``. - - ``scanlist`` - - This is the initial set of ``environ`` keys to - save/restore to the signed cookie. By default is consists - only of ``REMOTE_USER`` and ``REMOTE_SESSION``; any tuple - or list of environment keys will work. However, be - careful, as the total saved size is limited to around 3k. - - ``signer`` - - This is the signer object used to create the actual cookie - values, by default, it is ``AuthCookieSigner`` and is passed - the remaining arguments to this function: ``secret``, - ``timeout``, and ``maxlen``. - - At this time, each cookie is individually signed. To store more - than the 4k of data; it is possible to sub-class this object to - provide different ``environ_name`` and ``cookie_name`` - """ - environ_name = 'paste.auth.cookie' - cookie_name = 'PASTE_AUTH_COOKIE' - signer_class = AuthCookieSigner - environ_class = AuthCookieEnviron - - def __init__(self, application, cookie_name=None, scanlist=None, - signer=None, secret=None, timeout=None, maxlen=None): - if not signer: - signer = self.signer_class(secret, timeout, maxlen) - self.signer = signer - self.scanlist = scanlist or ('REMOTE_USER','REMOTE_SESSION') - self.application = application - self.cookie_name = cookie_name or self.cookie_name - - def __call__(self, environ, start_response): - if self.environ_name in environ: - raise AssertionError("AuthCookie already installed!") - scanlist = self.environ_class(self, self.scanlist) - jar = get_cookies(environ) - if jar.has_key(self.cookie_name): - content = self.signer.auth(jar[self.cookie_name].value) - if content: - for pair in content.split(";"): - (k, v) = pair.split("=") - k = decode(k) - if k not in scanlist: - scanlist.append(k) - if k in environ: - continue - environ[k] = decode(v) - if 'REMOTE_USER' == k: - environ['AUTH_TYPE'] = 'cookie' - environ[self.environ_name] = scanlist - if "paste.httpexceptions" in environ: - warnings.warn("Since paste.httpexceptions is hooked in your " - "processing chain before paste.auth.cookie, if an " - "HTTPRedirection is raised, the cookies this module sets " - "will not be included in your response.\n") - - def response_hook(status, response_headers, exc_info=None): - """ - Scan the environment for keys specified in the scanlist, - pack up their values, signs the content and issues a cookie. - """ - scanlist = environ.get(self.environ_name) - assert scanlist and isinstance(scanlist, self.environ_class) - content = [] - for k in scanlist: - v = environ.get(k) - if v is not None: - if type(v) is not str: - raise ValueError( - "The value of the environmental variable %r " - "is not a str (only str is allowed; got %r)" - % (k, v)) - content.append("%s=%s" % (encode(k), encode(v))) - if content: - content = ";".join(content) - content = self.signer.sign(content) - cookie = '%s=%s; Path=/;' % (self.cookie_name, content) - if 'https' == environ['wsgi.url_scheme']: - cookie += ' secure;' - response_headers.append(('Set-Cookie', cookie)) - return start_response(status, response_headers, exc_info) - return self.application(environ, response_hook) - -middleware = AuthCookieHandler - -# Paste Deploy entry point: -def make_auth_cookie( - app, global_conf, - # Should this get picked up from global_conf somehow?: - cookie_name='PASTE_AUTH_COOKIE', - scanlist=('REMOTE_USER', 'REMOTE_SESSION'), - # signer cannot be set - secret=None, - timeout=30, - maxlen=4096): - """ - This middleware uses cookies to stash-away a previously - authenticated user (and perhaps other variables) so that - re-authentication is not needed. This does not implement - sessions; and therefore N servers can be syncronized to accept the - same saved authentication if they all use the same cookie_name and - secret. - - By default, this handler scans the `environ` for the REMOTE_USER - and REMOTE_SESSION key; if found, it is stored. It can be - configured to scan other `environ` keys as well -- but be careful - not to exceed 2-3k (so that the encoded and signed cookie does not - exceed 4k). You can ask it to handle other environment variables - by doing: - - ``environ['paste.auth.cookie'].append('your.environ.variable')`` - - Configuration: - - ``cookie_name`` - - The name of the cookie used to store this content, by - default it is ``PASTE_AUTH_COOKIE``. - - ``scanlist`` - - This is the initial set of ``environ`` keys to - save/restore to the signed cookie. By default is consists - only of ``REMOTE_USER`` and ``REMOTE_SESSION``; any - space-separated list of environment keys will work. - However, be careful, as the total saved size is limited to - around 3k. - - ``secret`` - - The secret that will be used to sign the cookies. If you - don't provide one (and none is set globally) then a random - secret will be created. Each time the server is restarted - a new secret will then be created and all cookies will - become invalid! This can be any string value. - - ``timeout`` - - The time to keep the cookie, expressed in minutes. This - is handled server-side, so a new cookie with a new timeout - is added to every response. - - ``maxlen`` - - The maximum length of the cookie that is sent (default 4k, - which is a typical browser maximum) - - """ - if isinstance(scanlist, basestring): - scanlist = scanlist.split() - if secret is None and global_conf.get('secret'): - secret = global_conf['secret'] - try: - timeout = int(timeout) - except ValueError: - raise ValueError('Bad value for timeout (must be int): %r' - % timeout) - try: - maxlen = int(maxlen) - except ValueError: - raise ValueError('Bad value for maxlen (must be int): %r' - % maxlen) - return AuthCookieHandler( - app, cookie_name=cookie_name, scanlist=scanlist, - secret=secret, timeout=timeout, maxlen=maxlen) - -__all__ = ['AuthCookieHandler', 'AuthCookieSigner', 'AuthCookieEnviron'] - -if "__main__" == __name__: - import doctest - doctest.testmod(optionflags=doctest.ELLIPSIS) - diff --git a/kcdc3/paste/auth/digest.py b/kcdc3/paste/auth/digest.py deleted file mode 100644 index 1b5ba84b..00000000 --- a/kcdc3/paste/auth/digest.py +++ /dev/null @@ -1,214 +0,0 @@ -# (c) 2005 Clark C. Evans -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -# This code was written with funding by http://prometheusresearch.com -""" -Digest HTTP/1.1 Authentication - -This module implements ``Digest`` authentication as described by -RFC 2617 [1]_ . - -Basically, you just put this module before your application, and it -takes care of requesting and handling authentication requests. This -module has been tested with several common browsers "out-in-the-wild". - ->>> from paste.wsgilib import dump_environ ->>> from paste.httpserver import serve ->>> # from paste.auth.digest import digest_password, AuthDigestHandler ->>> realm = 'Test Realm' ->>> def authfunc(environ, realm, username): -... return digest_password(realm, username, username) ->>> serve(AuthDigestHandler(dump_environ, realm, authfunc)) -serving on... - -This code has not been audited by a security expert, please use with -caution (or better yet, report security holes). At this time, this -implementation does not provide for further challenges, nor does it -support Authentication-Info header. It also uses md5, and an option -to use sha would be a good thing. - -.. [1] http://www.faqs.org/rfcs/rfc2617.html -""" -from paste.httpexceptions import HTTPUnauthorized -from paste.httpheaders import * -try: - from hashlib import md5 -except ImportError: - from md5 import md5 -import time, random -from urllib import quote as url_quote - -def digest_password(realm, username, password): - """ construct the appropriate hashcode needed for HTTP digest """ - return md5("%s:%s:%s" % (username, realm, password)).hexdigest() - -class AuthDigestAuthenticator(object): - """ implementation of RFC 2617 - HTTP Digest Authentication """ - def __init__(self, realm, authfunc): - self.nonce = {} # list to prevent replay attacks - self.authfunc = authfunc - self.realm = realm - - def build_authentication(self, stale = ''): - """ builds the authentication error """ - nonce = md5( - "%s:%s" % (time.time(), random.random())).hexdigest() - opaque = md5( - "%s:%s" % (time.time(), random.random())).hexdigest() - self.nonce[nonce] = None - parts = {'realm': self.realm, 'qop': 'auth', - 'nonce': nonce, 'opaque': opaque } - if stale: - parts['stale'] = 'true' - head = ", ".join(['%s="%s"' % (k, v) for (k, v) in parts.items()]) - head = [("WWW-Authenticate", 'Digest %s' % head)] - return HTTPUnauthorized(headers=head) - - def compute(self, ha1, username, response, method, - path, nonce, nc, cnonce, qop): - """ computes the authentication, raises error if unsuccessful """ - if not ha1: - return self.build_authentication() - ha2 = md5('%s:%s' % (method, path)).hexdigest() - if qop: - chk = "%s:%s:%s:%s:%s:%s" % (ha1, nonce, nc, cnonce, qop, ha2) - else: - chk = "%s:%s:%s" % (ha1, nonce, ha2) - if response != md5(chk).hexdigest(): - if nonce in self.nonce: - del self.nonce[nonce] - return self.build_authentication() - pnc = self.nonce.get(nonce,'00000000') - if nc <= pnc: - if nonce in self.nonce: - del self.nonce[nonce] - return self.build_authentication(stale = True) - self.nonce[nonce] = nc - return username - - def authenticate(self, environ): - """ This function takes a WSGI environment and authenticates - the request returning authenticated user or error. - """ - method = REQUEST_METHOD(environ) - fullpath = url_quote(SCRIPT_NAME(environ)) + url_quote(PATH_INFO(environ)) - authorization = AUTHORIZATION(environ) - if not authorization: - return self.build_authentication() - (authmeth, auth) = authorization.split(" ", 1) - if 'digest' != authmeth.lower(): - return self.build_authentication() - amap = {} - for itm in auth.split(", "): - (k,v) = [s.strip() for s in itm.split("=", 1)] - amap[k] = v.replace('"', '') - try: - username = amap['username'] - authpath = amap['uri'] - nonce = amap['nonce'] - realm = amap['realm'] - response = amap['response'] - assert authpath.split("?", 1)[0] in fullpath - assert realm == self.realm - qop = amap.get('qop', '') - cnonce = amap.get('cnonce', '') - nc = amap.get('nc', '00000000') - if qop: - assert 'auth' == qop - assert nonce and nc - except: - return self.build_authentication() - ha1 = self.authfunc(environ, realm, username) - return self.compute(ha1, username, response, method, authpath, - nonce, nc, cnonce, qop) - - __call__ = authenticate - -class AuthDigestHandler(object): - """ - middleware for HTTP Digest authentication (RFC 2617) - - This component follows the procedure below: - - 0. If the REMOTE_USER environment variable is already populated; - then this middleware is a no-op, and the request is passed - along to the application. - - 1. If the HTTP_AUTHORIZATION header was not provided or specifies - an algorithem other than ``digest``, then a HTTPUnauthorized - response is generated with the challenge. - - 2. If the response is malformed or or if the user's credientials - do not pass muster, another HTTPUnauthorized is raised. - - 3. If all goes well, and the user's credintials pass; then - REMOTE_USER environment variable is filled in and the - AUTH_TYPE is listed as 'digest'. - - Parameters: - - ``application`` - - The application object is called only upon successful - authentication, and can assume ``environ['REMOTE_USER']`` - is set. If the ``REMOTE_USER`` is already set, this - middleware is simply pass-through. - - ``realm`` - - This is a identifier for the authority that is requesting - authorization. It is shown to the user and should be unique - within the domain it is being used. - - ``authfunc`` - - This is a callback function which performs the actual - authentication; the signature of this callback is: - - authfunc(environ, realm, username) -> hashcode - - This module provides a 'digest_password' helper function - which can help construct the hashcode; it is recommended - that the hashcode is stored in a database, not the user's - actual password (since you only need the hashcode). - """ - def __init__(self, application, realm, authfunc): - self.authenticate = AuthDigestAuthenticator(realm, authfunc) - self.application = application - - def __call__(self, environ, start_response): - username = REMOTE_USER(environ) - if not username: - result = self.authenticate(environ) - if isinstance(result, str): - AUTH_TYPE.update(environ,'digest') - REMOTE_USER.update(environ, result) - else: - return result.wsgi_application(environ, start_response) - return self.application(environ, start_response) - -middleware = AuthDigestHandler - -__all__ = ['digest_password', 'AuthDigestHandler' ] - -def make_digest(app, global_conf, realm, authfunc, **kw): - """ - Grant access via digest authentication - - Config looks like this:: - - [filter:grant] - use = egg:Paste#auth_digest - realm=myrealm - authfunc=somepackage.somemodule:somefunction - - """ - from paste.util.import_string import eval_import - import types - authfunc = eval_import(authfunc) - assert isinstance(authfunc, types.FunctionType), "authfunc must resolve to a function" - return AuthDigestHandler(app, realm, authfunc) - -if "__main__" == __name__: - import doctest - doctest.testmod(optionflags=doctest.ELLIPSIS) diff --git a/kcdc3/paste/auth/form.py b/kcdc3/paste/auth/form.py deleted file mode 100644 index 4e6aa49a..00000000 --- a/kcdc3/paste/auth/form.py +++ /dev/null @@ -1,149 +0,0 @@ -# (c) 2005 Clark C. Evans -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -# This code was written with funding by http://prometheusresearch.com -""" -Authentication via HTML Form - -This is a very simple HTML form login screen that asks for the username -and password. This middleware component requires that an authorization -function taking the name and passsword and that it be placed in your -application stack. This class does not include any session management -code or way to save the user's authorization; however, it is easy enough -to put ``paste.auth.cookie`` in your application stack. - ->>> from paste.wsgilib import dump_environ ->>> from paste.httpserver import serve ->>> from paste.auth.cookie import AuthCookieHandler ->>> from paste.auth.form import AuthFormHandler ->>> def authfunc(environ, username, password): -... return username == password ->>> serve(AuthCookieHandler( -... AuthFormHandler(dump_environ, authfunc))) -serving on... - -""" -from paste.request import construct_url, parse_formvars - -TEMPLATE = """\ - - Please Login! - -

    Please Login

    -
    -
    -
    Username:
    -
    -
    Password:
    -
    -
    - -
    -
    - - -""" - -class AuthFormHandler(object): - """ - HTML-based login middleware - - This causes a HTML form to be returned if ``REMOTE_USER`` is - not found in the ``environ``. If the form is returned, the - ``username`` and ``password`` combination are given to a - user-supplied authentication function, ``authfunc``. If this - is successful, then application processing continues. - - Parameters: - - ``application`` - - The application object is called only upon successful - authentication, and can assume ``environ['REMOTE_USER']`` - is set. If the ``REMOTE_USER`` is already set, this - middleware is simply pass-through. - - ``authfunc`` - - This is a mandatory user-defined function which takes a - ``environ``, ``username`` and ``password`` for its first - three arguments. It should return ``True`` if the user is - authenticated. - - ``template`` - - This is an optional (a default is provided) HTML - fragment that takes exactly one ``%s`` substution - argument; which *must* be used for the form's ``action`` - to ensure that this middleware component does not alter - the current path. The HTML form must use ``POST`` and - have two input names: ``username`` and ``password``. - - Since the authentication form is submitted (via ``POST``) - neither the ``PATH_INFO`` nor the ``QUERY_STRING`` are accessed, - and hence the current path remains _unaltered_ through the - entire authentication process. If authentication succeeds, the - ``REQUEST_METHOD`` is converted from a ``POST`` to a ``GET``, - so that a redirect is unnecessary (unlike most form auth - implementations) - """ - - def __init__(self, application, authfunc, template=None): - self.application = application - self.authfunc = authfunc - self.template = template or TEMPLATE - - def __call__(self, environ, start_response): - username = environ.get('REMOTE_USER','') - if username: - return self.application(environ, start_response) - - if 'POST' == environ['REQUEST_METHOD']: - formvars = parse_formvars(environ, include_get_vars=False) - username = formvars.get('username') - password = formvars.get('password') - if username and password: - if self.authfunc(environ, username, password): - environ['AUTH_TYPE'] = 'form' - environ['REMOTE_USER'] = username - environ['REQUEST_METHOD'] = 'GET' - environ['CONTENT_LENGTH'] = '' - environ['CONTENT_TYPE'] = '' - del environ['paste.parsed_formvars'] - return self.application(environ, start_response) - - content = self.template % construct_url(environ) - start_response("200 OK", [('Content-Type', 'text/html'), - ('Content-Length', str(len(content)))]) - return [content] - -middleware = AuthFormHandler - -__all__ = ['AuthFormHandler'] - -def make_form(app, global_conf, realm, authfunc, **kw): - """ - Grant access via form authentication - - Config looks like this:: - - [filter:grant] - use = egg:Paste#auth_form - realm=myrealm - authfunc=somepackage.somemodule:somefunction - - """ - from paste.util.import_string import eval_import - import types - authfunc = eval_import(authfunc) - assert isinstance(authfunc, types.FunctionType), "authfunc must resolve to a function" - template = kw.get('template') - if template is not None: - template = eval_import(template) - assert isinstance(template, str), "template must resolve to a string" - - return AuthFormHandler(app, authfunc, template) - -if "__main__" == __name__: - import doctest - doctest.testmod(optionflags=doctest.ELLIPSIS) diff --git a/kcdc3/paste/auth/grantip.py b/kcdc3/paste/auth/grantip.py deleted file mode 100644 index 94b39000..00000000 --- a/kcdc3/paste/auth/grantip.py +++ /dev/null @@ -1,113 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -Grant roles and logins based on IP address. -""" -from paste.util import ip4 - -class GrantIPMiddleware(object): - - """ - On each request, ``ip_map`` is checked against ``REMOTE_ADDR`` - and logins and roles are assigned based on that. - - ``ip_map`` is a map of {ip_mask: (username, roles)}. Either - ``username`` or ``roles`` may be None. Roles may also be prefixed - with ``-``, like ``'-system'`` meaning that role should be - revoked. ``'__remove__'`` for a username will remove the username. - - If ``clobber_username`` is true (default) then any user - specification will override the current value of ``REMOTE_USER``. - ``'__remove__'`` will always clobber the username. - - ``ip_mask`` is something that `paste.util.ip4:IP4Range - `_ can parse. Simple IP - addresses, IP/mask, ip<->ip ranges, and hostnames are allowed. - """ - - def __init__(self, app, ip_map, clobber_username=True): - self.app = app - self.ip_map = [] - for key, value in ip_map.items(): - self.ip_map.append((ip4.IP4Range(key), - self._convert_user_role(value[0], value[1]))) - self.clobber_username = clobber_username - - def _convert_user_role(self, username, roles): - if roles and isinstance(roles, basestring): - roles = roles.split(',') - return (username, roles) - - def __call__(self, environ, start_response): - addr = ip4.ip2int(environ['REMOTE_ADDR'], False) - remove_user = False - add_roles = [] - for range, (username, roles) in self.ip_map: - if addr in range: - if roles: - add_roles.extend(roles) - if username == '__remove__': - remove_user = True - elif username: - if (not environ.get('REMOTE_USER') - or self.clobber_username): - environ['REMOTE_USER'] = username - if (remove_user and 'REMOTE_USER' in environ): - del environ['REMOTE_USER'] - if roles: - self._set_roles(environ, add_roles) - return self.app(environ, start_response) - - def _set_roles(self, environ, roles): - cur_roles = environ.get('REMOTE_USER_TOKENS', '').split(',') - # Get rid of empty roles: - cur_roles = filter(None, cur_roles) - remove_roles = [] - for role in roles: - if role.startswith('-'): - remove_roles.append(role[1:]) - else: - if role not in cur_roles: - cur_roles.append(role) - for role in remove_roles: - if role in cur_roles: - cur_roles.remove(role) - environ['REMOTE_USER_TOKENS'] = ','.join(cur_roles) - - -def make_grantip(app, global_conf, clobber_username=False, **kw): - """ - Grant roles or usernames based on IP addresses. - - Config looks like this:: - - [filter:grant] - use = egg:Paste#grantip - clobber_username = true - # Give localhost system role (no username): - 127.0.0.1 = -:system - # Give everyone in 192.168.0.* editor role: - 192.168.0.0/24 = -:editor - # Give one IP the username joe: - 192.168.0.7 = joe - # And one IP is should not be logged in: - 192.168.0.10 = __remove__:-editor - - """ - from paste.deploy.converters import asbool - clobber_username = asbool(clobber_username) - ip_map = {} - for key, value in kw.items(): - if ':' in value: - username, role = value.split(':', 1) - else: - username = value - role = '' - if username == '-': - username = '' - if role == '-': - role = '' - ip_map[key] = value - return GrantIPMiddleware(app, ip_map, clobber_username) - - diff --git a/kcdc3/paste/auth/multi.py b/kcdc3/paste/auth/multi.py deleted file mode 100644 index b378fa6c..00000000 --- a/kcdc3/paste/auth/multi.py +++ /dev/null @@ -1,79 +0,0 @@ -# (c) 2005 Clark C. Evans -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -# This code was written with funding by http://prometheusresearch.com -""" -Authentication via Multiple Methods - -In some environments, the choice of authentication method to be used -depends upon the environment and is not "fixed". This middleware allows -N authentication methods to be registered along with a goodness function -which determines which method should be used. The following example -demonstrates how to use both form and digest authentication in a server -stack; by default it uses form-based authentication unless -``*authmeth=digest`` is specified as a query argument. - ->>> from paste.auth import form, cookie, digest, multi ->>> from paste.wsgilib import dump_environ ->>> from paste.httpserver import serve ->>> ->>> multi = multi.MultiHandler(dump_environ) ->>> def authfunc(environ, realm, user): -... return digest.digest_password(realm, user, user) ->>> multi.add_method('digest', digest.middleware, "Test Realm", authfunc) ->>> multi.set_query_argument('digest') ->>> ->>> def authfunc(environ, username, password): -... return username == password ->>> multi.add_method('form', form.middleware, authfunc) ->>> multi.set_default('form') ->>> serve(cookie.middleware(multi)) -serving on... - -""" - -class MultiHandler(object): - """ - Multiple Authentication Handler - - This middleware provides two othogonal facilities: - - - a manner to register any number of authentication middlewares - - - a mechanism to register predicates which cause one of the - registered middlewares to be used depending upon the request - - If none of the predicates returns True, then the application is - invoked directly without middleware - """ - def __init__(self, application): - self.application = application - self.default = application - self.binding = {} - self.predicate = [] - def add_method(self, name, factory, *args, **kwargs): - self.binding[name] = factory(self.application, *args, **kwargs) - def add_predicate(self, name, checker): - self.predicate.append((checker, self.binding[name])) - def set_default(self, name): - """ set default authentication method """ - self.default = self.binding[name] - def set_query_argument(self, name, key = '*authmeth', value = None): - """ choose authentication method based on a query argument """ - lookfor = "%s=%s" % (key, value or name) - self.add_predicate(name, - lambda environ: lookfor in environ.get('QUERY_STRING','')) - def __call__(self, environ, start_response): - for (checker, binding) in self.predicate: - if checker(environ): - return binding(environ, start_response) - return self.default(environ, start_response) - -middleware = MultiHandler - -__all__ = ['MultiHandler'] - -if "__main__" == __name__: - import doctest - doctest.testmod(optionflags=doctest.ELLIPSIS) - diff --git a/kcdc3/paste/auth/open_id.py b/kcdc3/paste/auth/open_id.py deleted file mode 100644 index f6efe61a..00000000 --- a/kcdc3/paste/auth/open_id.py +++ /dev/null @@ -1,412 +0,0 @@ -# (c) 2005 Ben Bangert -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -""" -OpenID Authentication (Consumer) - -OpenID is a distributed authentication system for single sign-on originally -developed at/for LiveJournal.com. - - http://openid.net/ - -URL. You can have multiple identities in the same way you can have multiple -URLs. All OpenID does is provide a way to prove that you own a URL (identity). -And it does this without passing around your password, your email address, or -anything you don't want it to. There's no profile exchange component at all: -your profiile is your identity URL, but recipients of your identity can then -learn more about you from any public, semantically interesting documents -linked thereunder (FOAF, RSS, Atom, vCARD, etc.). - -``Note``: paste.auth.openid requires installation of the Python-OpenID -libraries:: - - http://www.openidenabled.com/ - -This module is based highly off the consumer.py that Python OpenID comes with. - -Using the OpenID Middleware -=========================== - -Using the OpenID middleware is fairly easy, the most minimal example using the -basic login form thats included:: - - # Add to your wsgi app creation - from paste.auth import open_id - - wsgi_app = open_id.middleware(wsgi_app, '/somewhere/to/store/openid/data') - -You will now have the OpenID form available at /oid on your site. Logging in will -verify that the login worked. - -A more complete login should involve having the OpenID middleware load your own -login page after verifying the OpenID URL so that you can retain the login -information in your webapp (session, cookies, etc.):: - - wsgi_app = open_id.middleware(wsgi_app, '/somewhere/to/store/openid/data', - login_redirect='/your/login/code') - -Your login code should then be configured to retrieve 'paste.auth.open_id' for -the users OpenID URL. If this key does not exist, the user has not logged in. - -Once the login is retrieved, it should be saved in your webapp, and the user -should be redirected to wherever they would normally go after a successful -login. -""" - -__all__ = ['AuthOpenIDHandler'] - -import cgi -import urlparse -import re - -import paste.request -from paste import httpexceptions - -def quoteattr(s): - qs = cgi.escape(s, 1) - return '"%s"' % (qs,) - -# You may need to manually add the openid package into your -# python path if you don't have it installed with your system python. -# If so, uncomment the line below, and change the path where you have -# Python-OpenID. -# sys.path.append('/path/to/openid/') - -from openid.store import filestore -from openid.consumer import consumer -from openid.oidutil import appendArgs - -class AuthOpenIDHandler(object): - """ - This middleware implements OpenID Consumer behavior to authenticate a - URL against an OpenID Server. - """ - - def __init__(self, app, data_store_path, auth_prefix='/oid', - login_redirect=None, catch_401=False, - url_to_username=None): - """ - Initialize the OpenID middleware - - ``app`` - Your WSGI app to call - - ``data_store_path`` - Directory to store crypto data in for use with OpenID servers. - - ``auth_prefix`` - Location for authentication process/verification - - ``login_redirect`` - Location to load after successful process of login - - ``catch_401`` - If true, then any 401 responses will turn into open ID login - requirements. - - ``url_to_username`` - A function called like ``url_to_username(environ, url)``, which should - return a string username. If not given, the URL will be the username. - """ - store = filestore.FileOpenIDStore(data_store_path) - self.oidconsumer = consumer.OpenIDConsumer(store) - - self.app = app - self.auth_prefix = auth_prefix - self.data_store_path = data_store_path - self.login_redirect = login_redirect - self.catch_401 = catch_401 - self.url_to_username = url_to_username - - def __call__(self, environ, start_response): - if environ['PATH_INFO'].startswith(self.auth_prefix): - # Let's load everything into a request dict to pass around easier - request = dict(environ=environ, start=start_response, body=[]) - request['base_url'] = paste.request.construct_url(environ, with_path_info=False, - with_query_string=False) - - path = re.sub(self.auth_prefix, '', environ['PATH_INFO']) - request['parsed_uri'] = urlparse.urlparse(path) - request['query'] = dict(paste.request.parse_querystring(environ)) - - path = request['parsed_uri'][2] - if path == '/' or not path: - return self.render(request) - elif path == '/verify': - return self.do_verify(request) - elif path == '/process': - return self.do_process(request) - else: - return self.not_found(request) - else: - if self.catch_401: - return self.catch_401_app_call(environ, start_response) - return self.app(environ, start_response) - - def catch_401_app_call(self, environ, start_response): - """ - Call the application, and redirect if the app returns a 401 response - """ - was_401 = [] - def replacement_start_response(status, headers, exc_info=None): - if int(status.split(None, 1)) == 401: - # @@: Do I need to append something to go back to where we - # came from? - was_401.append(1) - def dummy_writer(v): - pass - return dummy_writer - else: - return start_response(status, headers, exc_info) - app_iter = self.app(environ, replacement_start_response) - if was_401: - try: - list(app_iter) - finally: - if hasattr(app_iter, 'close'): - app_iter.close() - redir_url = paste.request.construct_url(environ, with_path_info=False, - with_query_string=False) - exc = httpexceptions.HTTPTemporaryRedirect(redir_url) - return exc.wsgi_application(environ, start_response) - else: - return app_iter - - def do_verify(self, request): - """Process the form submission, initating OpenID verification. - """ - - # First, make sure that the user entered something - openid_url = request['query'].get('openid_url') - if not openid_url: - return self.render(request, 'Enter an identity URL to verify.', - css_class='error', form_contents=openid_url) - - oidconsumer = self.oidconsumer - - # Then, ask the library to begin the authorization. - # Here we find out the identity server that will verify the - # user's identity, and get a token that allows us to - # communicate securely with the identity server. - status, info = oidconsumer.beginAuth(openid_url) - - # If the URL was unusable (either because of network - # conditions, a server error, or that the response returned - # was not an OpenID identity page), the library will return - # an error code. Let the user know that that URL is unusable. - if status in [consumer.HTTP_FAILURE, consumer.PARSE_ERROR]: - if status == consumer.HTTP_FAILURE: - fmt = 'Failed to retrieve %s' - else: - fmt = 'Could not find OpenID information in %s' - - message = fmt % (cgi.escape(openid_url),) - return self.render(request, message, css_class='error', form_contents=openid_url) - elif status == consumer.SUCCESS: - # The URL was a valid identity URL. Now we construct a URL - # that will get us to process the server response. We will - # need the token from the beginAuth call when processing - # the response. A cookie or a session object could be used - # to accomplish this, but for simplicity here we just add - # it as a query parameter of the return-to URL. - return_to = self.build_url(request, 'process', token=info.token) - - # Now ask the library for the URL to redirect the user to - # his OpenID server. It is required for security that the - # return_to URL must be under the specified trust_root. We - # just use the base_url for this server as a trust root. - redirect_url = oidconsumer.constructRedirect( - info, return_to, trust_root=request['base_url']) - - # Send the redirect response - return self.redirect(request, redirect_url) - else: - assert False, 'Not reached' - - def do_process(self, request): - """Handle the redirect from the OpenID server. - """ - oidconsumer = self.oidconsumer - - # retrieve the token from the environment (in this case, the URL) - token = request['query'].get('token', '') - - # Ask the library to check the response that the server sent - # us. Status is a code indicating the response type. info is - # either None or a string containing more information about - # the return type. - status, info = oidconsumer.completeAuth(token, request['query']) - - css_class = 'error' - openid_url = None - if status == consumer.FAILURE and info: - # In the case of failure, if info is non-None, it is the - # URL that we were verifying. We include it in the error - # message to help the user figure out what happened. - openid_url = info - fmt = "Verification of %s failed." - message = fmt % (cgi.escape(openid_url),) - elif status == consumer.SUCCESS: - # Success means that the transaction completed without - # error. If info is None, it means that the user cancelled - # the verification. - css_class = 'alert' - if info: - # This is a successful verification attempt. If this - # was a real application, we would do our login, - # comment posting, etc. here. - openid_url = info - if self.url_to_username: - username = self.url_to_username(request['environ'], openid_url) - else: - username = openid_url - if 'paste.auth_tkt.set_user' in request['environ']: - request['environ']['paste.auth_tkt.set_user'](username) - if not self.login_redirect: - fmt = ("If you had supplied a login redirect path, you would have " - "been redirected there. " - "You have successfully verified %s as your identity.") - message = fmt % (cgi.escape(openid_url),) - else: - # @@: This stuff doesn't make sense to me; why not a remote redirect? - request['environ']['paste.auth.open_id'] = openid_url - request['environ']['PATH_INFO'] = self.login_redirect - return self.app(request['environ'], request['start']) - #exc = httpexceptions.HTTPTemporaryRedirect(self.login_redirect) - #return exc.wsgi_application(request['environ'], request['start']) - else: - # cancelled - message = 'Verification cancelled' - else: - # Either we don't understand the code or there is no - # openid_url included with the error. Give a generic - # failure message. The library should supply debug - # information in a log. - message = 'Verification failed.' - - return self.render(request, message, css_class, openid_url) - - def build_url(self, request, action, **query): - """Build a URL relative to the server base_url, with the given - query parameters added.""" - base = urlparse.urljoin(request['base_url'], self.auth_prefix + '/' + action) - return appendArgs(base, query) - - def redirect(self, request, redirect_url): - """Send a redirect response to the given URL to the browser.""" - response_headers = [('Content-type', 'text/plain'), - ('Location', redirect_url)] - request['start']('302 REDIRECT', response_headers) - return ["Redirecting to %s" % redirect_url] - - def not_found(self, request): - """Render a page with a 404 return code and a message.""" - fmt = 'The path %s was not understood by this server.' - msg = fmt % (request['parsed_uri'],) - openid_url = request['query'].get('openid_url') - return self.render(request, msg, 'error', openid_url, status='404 Not Found') - - def render(self, request, message=None, css_class='alert', form_contents=None, - status='200 OK', title="Python OpenID Consumer"): - """Render a page.""" - response_headers = [('Content-type', 'text/html')] - request['start'](str(status), response_headers) - - self.page_header(request, title) - if message: - request['body'].append("
    " % (css_class,)) - request['body'].append(message) - request['body'].append("
    ") - self.page_footer(request, form_contents) - return request['body'] - - def page_header(self, request, title): - """Render the page header""" - request['body'].append('''\ - - %s - - -

    %s

    -

    - This example consumer uses the Python OpenID library. It - just verifies that the URL that you enter is your identity URL. -

    -''' % (title, title)) - - def page_footer(self, request, form_contents): - """Render the page footer""" - if not form_contents: - form_contents = '' - - request['body'].append('''\ -
    -
    - Identity URL: - - -
    -
    - - -''' % (quoteattr(self.build_url(request, 'verify')), quoteattr(form_contents))) - - -middleware = AuthOpenIDHandler - -def make_open_id_middleware( - app, - global_conf, - # Should this default to something, or inherit something from global_conf?: - data_store_path, - auth_prefix='/oid', - login_redirect=None, - catch_401=False, - url_to_username=None, - apply_auth_tkt=False, - auth_tkt_logout_path=None): - from paste.deploy.converters import asbool - from paste.util import import_string - catch_401 = asbool(catch_401) - if url_to_username and isinstance(url_to_username, basestring): - url_to_username = import_string.eval_import(url_to_username) - apply_auth_tkt = asbool(apply_auth_tkt) - new_app = AuthOpenIDHandler( - app, data_store_path=data_store_path, auth_prefix=auth_prefix, - login_redirect=login_redirect, catch_401=catch_401, - url_to_username=url_to_username or None) - if apply_auth_tkt: - from paste.auth import auth_tkt - new_app = auth_tkt.make_auth_tkt_middleware( - new_app, global_conf, logout_path=auth_tkt_logout_path) - return new_app diff --git a/kcdc3/paste/cascade.py b/kcdc3/paste/cascade.py deleted file mode 100644 index 1c4acfb5..00000000 --- a/kcdc3/paste/cascade.py +++ /dev/null @@ -1,133 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php - -""" -Cascades through several applications, so long as applications -return ``404 Not Found``. -""" -from paste import httpexceptions -from paste.util import converters -import tempfile -from cStringIO import StringIO - -__all__ = ['Cascade'] - -def make_cascade(loader, global_conf, catch='404', **local_conf): - """ - Entry point for Paste Deploy configuration - - Expects configuration like:: - - [composit:cascade] - use = egg:Paste#cascade - # all start with 'app' and are sorted alphabetically - app1 = foo - app2 = bar - ... - catch = 404 500 ... - """ - catch = map(int, converters.aslist(catch)) - apps = [] - for name, value in local_conf.items(): - if not name.startswith('app'): - raise ValueError( - "Bad configuration key %r (=%r); all configuration keys " - "must start with 'app'" - % (name, value)) - app = loader.get_app(value, global_conf=global_conf) - apps.append((name, app)) - apps.sort() - apps = [app for name, app in apps] - return Cascade(apps, catch=catch) - -class Cascade(object): - - """ - Passed a list of applications, ``Cascade`` will try each of them - in turn. If one returns a status code listed in ``catch`` (by - default just ``404 Not Found``) then the next application is - tried. - - If all applications fail, then the last application's failure - response is used. - - Instances of this class are WSGI applications. - """ - - def __init__(self, applications, catch=(404,)): - self.apps = applications - self.catch_codes = {} - self.catch_exceptions = [] - for error in catch: - if isinstance(error, str): - error = int(error.split(None, 1)[0]) - if isinstance(error, httpexceptions.HTTPException): - exc = error - code = error.code - else: - exc = httpexceptions.get_exception(error) - code = error - self.catch_codes[code] = exc - self.catch_exceptions.append(exc) - self.catch_exceptions = tuple(self.catch_exceptions) - - def __call__(self, environ, start_response): - """ - WSGI application interface - """ - failed = [] - def repl_start_response(status, headers, exc_info=None): - code = int(status.split(None, 1)[0]) - if code in self.catch_codes: - failed.append(None) - return _consuming_writer - return start_response(status, headers, exc_info) - - try: - length = int(environ.get('CONTENT_LENGTH', 0) or 0) - except ValueError: - length = 0 - if length > 0: - # We have to copy wsgi.input - copy_wsgi_input = True - if length > 4096 or length < 0: - f = tempfile.TemporaryFile() - if length < 0: - f.write(environ['wsgi.input'].read()) - else: - copy_len = length - while copy_len > 0: - chunk = environ['wsgi.input'].read(min(copy_len, 4096)) - if not chunk: - raise IOError("Request body truncated") - f.write(chunk) - copy_len -= len(chunk) - f.seek(0) - else: - f = StringIO(environ['wsgi.input'].read(length)) - environ['wsgi.input'] = f - else: - copy_wsgi_input = False - for app in self.apps[:-1]: - environ_copy = environ.copy() - if copy_wsgi_input: - environ_copy['wsgi.input'].seek(0) - failed = [] - try: - v = app(environ_copy, repl_start_response) - if not failed: - return v - else: - if hasattr(v, 'close'): - # Exhaust the iterator first: - list(v) - # then close: - v.close() - except self.catch_exceptions, e: - pass - if copy_wsgi_input: - environ['wsgi.input'].seek(0) - return self.apps[-1](environ, start_response) - -def _consuming_writer(s): - pass diff --git a/kcdc3/paste/cgiapp.py b/kcdc3/paste/cgiapp.py deleted file mode 100644 index 2e5ae691..00000000 --- a/kcdc3/paste/cgiapp.py +++ /dev/null @@ -1,276 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php - -""" -Application that runs a CGI script. -""" -import os -import sys -import subprocess -import urllib -try: - import select -except ImportError: - select = None - -from paste.util import converters - -__all__ = ['CGIError', 'CGIApplication'] - -class CGIError(Exception): - """ - Raised when the CGI script can't be found or doesn't - act like a proper CGI script. - """ - -class CGIApplication(object): - - """ - This object acts as a proxy to a CGI application. You pass in the - script path (``script``), an optional path to search for the - script (if the name isn't absolute) (``path``). If you don't give - a path, then ``$PATH`` will be used. - """ - - def __init__(self, - global_conf, - script, - path=None, - include_os_environ=True, - query_string=None): - if global_conf: - raise NotImplemented( - "global_conf is no longer supported for CGIApplication " - "(use make_cgi_application); please pass None instead") - self.script_filename = script - if path is None: - path = os.environ.get('PATH', '').split(':') - self.path = path - if '?' in script: - assert query_string is None, ( - "You cannot have '?' in your script name (%r) and also " - "give a query_string (%r)" % (script, query_string)) - script, query_string = script.split('?', 1) - if os.path.abspath(script) != script: - # relative path - for path_dir in self.path: - if os.path.exists(os.path.join(path_dir, script)): - self.script = os.path.join(path_dir, script) - break - else: - raise CGIError( - "Script %r not found in path %r" - % (script, self.path)) - else: - self.script = script - self.include_os_environ = include_os_environ - self.query_string = query_string - - def __call__(self, environ, start_response): - if 'REQUEST_URI' not in environ: - environ['REQUEST_URI'] = ( - urllib.quote(environ.get('SCRIPT_NAME', '')) - + urllib.quote(environ.get('PATH_INFO', ''))) - if self.include_os_environ: - cgi_environ = os.environ.copy() - else: - cgi_environ = {} - for name in environ: - # Should unicode values be encoded? - if (name.upper() == name - and isinstance(environ[name], str)): - cgi_environ[name] = environ[name] - if self.query_string is not None: - old = cgi_environ.get('QUERY_STRING', '') - if old: - old += '&' - cgi_environ['QUERY_STRING'] = old + self.query_string - cgi_environ['SCRIPT_FILENAME'] = self.script - proc = subprocess.Popen( - [self.script], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=cgi_environ, - cwd=os.path.dirname(self.script), - ) - writer = CGIWriter(environ, start_response) - if select and sys.platform != 'win32': - proc_communicate( - proc, - stdin=StdinReader.from_environ(environ), - stdout=writer, - stderr=environ['wsgi.errors']) - else: - stdout, stderr = proc.communicate(StdinReader.from_environ(environ).read()) - if stderr: - environ['wsgi.errors'].write(stderr) - writer.write(stdout) - if not writer.headers_finished: - start_response(writer.status, writer.headers) - return [] - -class CGIWriter(object): - - def __init__(self, environ, start_response): - self.environ = environ - self.start_response = start_response - self.status = '200 OK' - self.headers = [] - self.headers_finished = False - self.writer = None - self.buffer = '' - - def write(self, data): - if self.headers_finished: - self.writer(data) - return - self.buffer += data - while '\n' in self.buffer: - if '\r\n' in self.buffer and self.buffer.find('\r\n') < self.buffer.find('\n'): - line1, self.buffer = self.buffer.split('\r\n', 1) - else: - line1, self.buffer = self.buffer.split('\n', 1) - if not line1: - self.headers_finished = True - self.writer = self.start_response( - self.status, self.headers) - self.writer(self.buffer) - del self.buffer - del self.headers - del self.status - break - elif ':' not in line1: - raise CGIError( - "Bad header line: %r" % line1) - else: - name, value = line1.split(':', 1) - value = value.lstrip() - name = name.strip() - if name.lower() == 'status': - if ' ' not in value: - # WSGI requires this space, sometimes CGI scripts don't set it: - value = '%s General' % value - self.status = value - else: - self.headers.append((name, value)) - -class StdinReader(object): - - def __init__(self, stdin, content_length): - self.stdin = stdin - self.content_length = content_length - - def from_environ(cls, environ): - length = environ.get('CONTENT_LENGTH') - if length: - length = int(length) - else: - length = 0 - return cls(environ['wsgi.input'], length) - - from_environ = classmethod(from_environ) - - def read(self, size=None): - if not self.content_length: - return '' - if size is None: - text = self.stdin.read(self.content_length) - else: - text = self.stdin.read(min(self.content_length, size)) - self.content_length -= len(text) - return text - -def proc_communicate(proc, stdin=None, stdout=None, stderr=None): - """ - Run the given process, piping input/output/errors to the given - file-like objects (which need not be actual file objects, unlike - the arguments passed to Popen). Wait for process to terminate. - - Note: this is taken from the posix version of - subprocess.Popen.communicate, but made more general through the - use of file-like objects. - """ - read_set = [] - write_set = [] - input_buffer = '' - trans_nl = proc.universal_newlines and hasattr(open, 'newlines') - - if proc.stdin: - # Flush stdio buffer. This might block, if the user has - # been writing to .stdin in an uncontrolled fashion. - proc.stdin.flush() - if input: - write_set.append(proc.stdin) - else: - proc.stdin.close() - else: - assert stdin is None - if proc.stdout: - read_set.append(proc.stdout) - else: - assert stdout is None - if proc.stderr: - read_set.append(proc.stderr) - else: - assert stderr is None - - while read_set or write_set: - rlist, wlist, xlist = select.select(read_set, write_set, []) - - if proc.stdin in wlist: - # When select has indicated that the file is writable, - # we can write up to PIPE_BUF bytes without risk - # blocking. POSIX defines PIPE_BUF >= 512 - next, input_buffer = input_buffer, '' - next_len = 512-len(next) - if next_len: - next += stdin.read(next_len) - if not next: - proc.stdin.close() - write_set.remove(proc.stdin) - else: - bytes_written = os.write(proc.stdin.fileno(), next) - if bytes_written < len(next): - input_buffer = next[bytes_written:] - - if proc.stdout in rlist: - data = os.read(proc.stdout.fileno(), 1024) - if data == "": - proc.stdout.close() - read_set.remove(proc.stdout) - if trans_nl: - data = proc._translate_newlines(data) - stdout.write(data) - - if proc.stderr in rlist: - data = os.read(proc.stderr.fileno(), 1024) - if data == "": - proc.stderr.close() - read_set.remove(proc.stderr) - if trans_nl: - data = proc._translate_newlines(data) - stderr.write(data) - - try: - proc.wait() - except OSError, e: - if e.errno != 10: - raise - -def make_cgi_application(global_conf, script, path=None, include_os_environ=None, - query_string=None): - """ - Paste Deploy interface for :class:`CGIApplication` - - This object acts as a proxy to a CGI application. You pass in the - script path (``script``), an optional path to search for the - script (if the name isn't absolute) (``path``). If you don't give - a path, then ``$PATH`` will be used. - """ - if path is None: - path = global_conf.get('path') or global_conf.get('PATH') - include_os_environ = converters.asbool(include_os_environ) - return CGIApplication( - script, path=path, include_os_environ=include_os_environ, - query_string=query_string) diff --git a/kcdc3/paste/cgitb_catcher.py b/kcdc3/paste/cgitb_catcher.py deleted file mode 100644 index 1c815b74..00000000 --- a/kcdc3/paste/cgitb_catcher.py +++ /dev/null @@ -1,116 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php - -""" -WSGI middleware - -Captures any exceptions and prints a pretty report. See the `cgitb -documentation `_ -for more. -""" - -import cgitb -from cStringIO import StringIO -import sys - -from paste.util import converters - -class NoDefault(object): - pass - -class CgitbMiddleware(object): - - def __init__(self, app, - global_conf=None, - display=NoDefault, - logdir=None, - context=5, - format="html"): - self.app = app - if global_conf is None: - global_conf = {} - if display is NoDefault: - display = global_conf.get('debug') - if isinstance(display, basestring): - display = converters.asbool(display) - self.display = display - self.logdir = logdir - self.context = int(context) - self.format = format - - def __call__(self, environ, start_response): - try: - app_iter = self.app(environ, start_response) - return self.catching_iter(app_iter, environ) - except: - exc_info = sys.exc_info() - start_response('500 Internal Server Error', - [('content-type', 'text/html')], - exc_info) - response = self.exception_handler(exc_info, environ) - return [response] - - def catching_iter(self, app_iter, environ): - if not app_iter: - raise StopIteration - error_on_close = False - try: - for v in app_iter: - yield v - if hasattr(app_iter, 'close'): - error_on_close = True - app_iter.close() - except: - response = self.exception_handler(sys.exc_info(), environ) - if not error_on_close and hasattr(app_iter, 'close'): - try: - app_iter.close() - except: - close_response = self.exception_handler( - sys.exc_info(), environ) - response += ( - '
    Error in .close():
    %s' - % close_response) - yield response - - def exception_handler(self, exc_info, environ): - dummy_file = StringIO() - hook = cgitb.Hook(file=dummy_file, - display=self.display, - logdir=self.logdir, - context=self.context, - format=self.format) - hook(*exc_info) - return dummy_file.getvalue() - -def make_cgitb_middleware(app, global_conf, - display=NoDefault, - logdir=None, - context=5, - format='html'): - """ - Wraps the application in the ``cgitb`` (standard library) - error catcher. - - display: - If true (or debug is set in the global configuration) - then the traceback will be displayed in the browser - - logdir: - Writes logs of all errors in that directory - - context: - Number of lines of context to show around each line of - source code - """ - from paste.deploy.converters import asbool - if display is not NoDefault: - display = asbool(display) - if 'debug' in global_conf: - global_conf['debug'] = asbool(global_conf['debug']) - return CgitbMiddleware( - app, global_conf=global_conf, - display=display, - logdir=logdir, - context=context, - format=format) diff --git a/kcdc3/paste/config.py b/kcdc3/paste/config.py deleted file mode 100644 index c5315797..00000000 --- a/kcdc3/paste/config.py +++ /dev/null @@ -1,120 +0,0 @@ -# (c) 2006 Ian Bicking, Philip Jenvey and contributors -# Written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -"""Paste Configuration Middleware and Objects""" -from paste.registry import RegistryManager, StackedObjectProxy - -__all__ = ['DispatchingConfig', 'CONFIG', 'ConfigMiddleware'] - -class DispatchingConfig(StackedObjectProxy): - """ - This is a configuration object that can be used globally, - imported, have references held onto. The configuration may differ - by thread (or may not). - - Specific configurations are registered (and deregistered) either - for the process or for threads. - """ - # @@: What should happen when someone tries to add this - # configuration to itself? Probably the conf should become - # resolved, and get rid of this delegation wrapper - - def __init__(self, name='DispatchingConfig'): - super(DispatchingConfig, self).__init__(name=name) - self.__dict__['_process_configs'] = [] - - def push_thread_config(self, conf): - """ - Make ``conf`` the active configuration for this thread. - Thread-local configuration always overrides process-wide - configuration. - - This should be used like:: - - conf = make_conf() - dispatching_config.push_thread_config(conf) - try: - ... do stuff ... - finally: - dispatching_config.pop_thread_config(conf) - """ - self._push_object(conf) - - def pop_thread_config(self, conf=None): - """ - Remove a thread-local configuration. If ``conf`` is given, - it is checked against the popped configuration and an error - is emitted if they don't match. - """ - self._pop_object(conf) - - def push_process_config(self, conf): - """ - Like push_thread_config, but applies the configuration to - the entire process. - """ - self._process_configs.append(conf) - - def pop_process_config(self, conf=None): - self._pop_from(self._process_configs, conf) - - def _pop_from(self, lst, conf): - popped = lst.pop() - if conf is not None and popped is not conf: - raise AssertionError( - "The config popped (%s) is not the same as the config " - "expected (%s)" - % (popped, conf)) - - def _current_obj(self): - try: - return super(DispatchingConfig, self)._current_obj() - except TypeError: - if self._process_configs: - return self._process_configs[-1] - raise AttributeError( - "No configuration has been registered for this process " - "or thread") - current = current_conf = _current_obj - -CONFIG = DispatchingConfig() - -no_config = object() -class ConfigMiddleware(RegistryManager): - """ - A WSGI middleware that adds a ``paste.config`` key (by default) - to the request environment, as well as registering the - configuration temporarily (for the length of the request) with - ``paste.config.CONFIG`` (or any other ``DispatchingConfig`` - object). - """ - - def __init__(self, application, config, dispatching_config=CONFIG, - environ_key='paste.config'): - """ - This delegates all requests to `application`, adding a *copy* - of the configuration `config`. - """ - def register_config(environ, start_response): - popped_config = environ.get(environ_key, no_config) - current_config = environ[environ_key] = config.copy() - environ['paste.registry'].register(dispatching_config, - current_config) - - try: - app_iter = application(environ, start_response) - finally: - if popped_config is no_config: - environ.pop(environ_key, None) - else: - environ[environ_key] = popped_config - return app_iter - - super(self.__class__, self).__init__(register_config) - -def make_config_filter(app, global_conf, **local_conf): - conf = global_conf.copy() - conf.update(local_conf) - return ConfigMiddleware(app, conf) - -make_config_middleware = ConfigMiddleware.__doc__ diff --git a/kcdc3/paste/cowbell/__init__.py b/kcdc3/paste/cowbell/__init__.py deleted file mode 100644 index 43b7097b..00000000 --- a/kcdc3/paste/cowbell/__init__.py +++ /dev/null @@ -1,104 +0,0 @@ -# Cowbell images: http://commons.wikimedia.org/wiki/Image:Cowbell-1.jpg -import os -import re -from paste.fileapp import FileApp -from paste.response import header_value, remove_header - -SOUND = "http://www.c-eye.net/eyeon/WalkenWAVS/explorestudiospace.wav" - -class MoreCowbell(object): - def __init__(self, app): - self.app = app - def __call__(self, environ, start_response): - path_info = environ.get('PATH_INFO', '') - script_name = environ.get('SCRIPT_NAME', '') - for filename in ['bell-ascending.png', 'bell-descending.png']: - if path_info == '/.cowbell/'+ filename: - app = FileApp(os.path.join(os.path.dirname(__file__), filename)) - return app(environ, start_response) - type = [] - body = [] - def repl_start_response(status, headers, exc_info=None): - ct = header_value(headers, 'content-type') - if ct and ct.startswith('text/html'): - type.append(ct) - remove_header(headers, 'content-length') - start_response(status, headers, exc_info) - return body.append - return start_response(status, headers, exc_info) - app_iter = self.app(environ, repl_start_response) - if type: - # Got text/html - body.extend(app_iter) - body = ''.join(body) - body = insert_head(body, self.javascript.replace('__SCRIPT_NAME__', script_name)) - body = insert_body(body, self.resources.replace('__SCRIPT_NAME__', script_name)) - return [body] - else: - return app_iter - - javascript = '''\ - -''' - - resources = '''\ - - -''' - -def insert_head(body, text): - end_head = re.search(r'', body, re.I) - if end_head: - return body[:end_head.start()] + text + body[end_head.end():] - else: - return text + body - -def insert_body(body, text): - end_body = re.search(r'', body, re.I) - if end_body: - return body[:end_body.start()] + text + body[end_body.end():] - else: - return body + text - -def make_cowbell(global_conf, app): - return MoreCowbell(app) - -if __name__ == '__main__': - from paste.debug.debugapp import SimpleApplication - app = MoreCowbell(SimpleApplication()) - from paste.httpserver import serve - serve(app) diff --git a/kcdc3/paste/debug/__init__.py b/kcdc3/paste/debug/__init__.py deleted file mode 100644 index daef7ccd..00000000 --- a/kcdc3/paste/debug/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -Package for debugging and development tools -""" diff --git a/kcdc3/paste/debug/debugapp.py b/kcdc3/paste/debug/debugapp.py deleted file mode 100644 index 190cbdde..00000000 --- a/kcdc3/paste/debug/debugapp.py +++ /dev/null @@ -1,79 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -# (c) 2005 Clark C. Evans -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -# This code was written with funding by http://prometheusresearch.com -""" -Various Applications for Debugging/Testing Purposes -""" - -import time -__all__ = ['SimpleApplication', 'SlowConsumer'] - - -class SimpleApplication(object): - """ - Produces a simple web page - """ - def __call__(self, environ, start_response): - body = "simple" - start_response("200 OK", [('Content-Type', 'text/html'), - ('Content-Length', str(len(body)))]) - return [body] - -class SlowConsumer(object): - """ - Consumes an upload slowly... - - NOTE: This should use the iterator form of ``wsgi.input``, - but it isn't implemented in paste.httpserver. - """ - def __init__(self, chunk_size = 4096, delay = 1, progress = True): - self.chunk_size = chunk_size - self.delay = delay - self.progress = True - - def __call__(self, environ, start_response): - size = 0 - total = environ.get('CONTENT_LENGTH') - if total: - remaining = int(total) - while remaining > 0: - if self.progress: - print "%s of %s remaining" % (remaining, total) - if remaining > 4096: - chunk = environ['wsgi.input'].read(4096) - else: - chunk = environ['wsgi.input'].read(remaining) - if not chunk: - break - size += len(chunk) - remaining -= len(chunk) - if self.delay: - time.sleep(self.delay) - body = "%d bytes" % size - else: - body = ('\n' - '
    \n' - '\n' - '\n' - '
    \n') - print "bingles" - start_response("200 OK", [('Content-Type', 'text/html'), - ('Content-Length', len(body))]) - return [body] - -def make_test_app(global_conf): - return SimpleApplication() - -make_test_app.__doc__ = SimpleApplication.__doc__ - -def make_slow_app(global_conf, chunk_size=4096, delay=1, progress=True): - from paste.deploy.converters import asbool - return SlowConsumer( - chunk_size=int(chunk_size), - delay=int(delay), - progress=asbool(progress)) - -make_slow_app.__doc__ = SlowConsumer.__doc__ diff --git a/kcdc3/paste/debug/doctest_webapp.py b/kcdc3/paste/debug/doctest_webapp.py deleted file mode 100644 index 935b2911..00000000 --- a/kcdc3/paste/debug/doctest_webapp.py +++ /dev/null @@ -1,435 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -#!/usr/bin/env python2.4 -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php - -""" -These are functions for use when doctest-testing a document. -""" - -try: - import subprocess -except ImportError: - from paste.util import subprocess24 as subprocess -import doctest -import os -import sys -import shutil -import re -import cgi -import rfc822 -from cStringIO import StringIO -from paste.util import PySourceColor - - -here = os.path.abspath(__file__) -paste_parent = os.path.dirname( - os.path.dirname(os.path.dirname(here))) - -def run(command): - data = run_raw(command) - if data: - print data - -def run_raw(command): - """ - Runs the string command, returns any output. - """ - proc = subprocess.Popen(command, shell=True, - stderr=subprocess.STDOUT, - stdout=subprocess.PIPE, env=_make_env()) - data = proc.stdout.read() - proc.wait() - while data.endswith('\n') or data.endswith('\r'): - data = data[:-1] - if data: - data = '\n'.join( - [l for l in data.splitlines() if l]) - return data - else: - return '' - -def run_command(command, name, and_print=False): - output = run_raw(command) - data = '$ %s\n%s' % (command, output) - show_file('shell-command', name, description='shell transcript', - data=data) - if and_print and output: - print output - -def _make_env(): - env = os.environ.copy() - env['PATH'] = (env.get('PATH', '') - + ':' - + os.path.join(paste_parent, 'scripts') - + ':' - + os.path.join(paste_parent, 'paste', '3rd-party', - 'sqlobject-files', 'scripts')) - env['PYTHONPATH'] = (env.get('PYTHONPATH', '') - + ':' - + paste_parent) - return env - -def clear_dir(dir): - """ - Clears (deletes) the given directory - """ - shutil.rmtree(dir, True) - -def ls(dir=None, recurse=False, indent=0): - """ - Show a directory listing - """ - dir = dir or os.getcwd() - fns = os.listdir(dir) - fns.sort() - for fn in fns: - full = os.path.join(dir, fn) - if os.path.isdir(full): - fn = fn + '/' - print ' '*indent + fn - if os.path.isdir(full) and recurse: - ls(dir=full, recurse=True, indent=indent+2) - -default_app = None -default_url = None - -def set_default_app(app, url): - global default_app - global default_url - default_app = app - default_url = url - -def resource_filename(fn): - """ - Returns the filename of the resource -- generally in the directory - resources/DocumentName/fn - """ - return os.path.join( - os.path.dirname(sys.testing_document_filename), - 'resources', - os.path.splitext(os.path.basename(sys.testing_document_filename))[0], - fn) - -def show(path_info, example_name): - fn = resource_filename(example_name + '.html') - out = StringIO() - assert default_app is not None, ( - "No default_app set") - url = default_url + path_info - out.write('%s
    \n' - % (url, url)) - out.write('
    \n') - proc = subprocess.Popen( - ['paster', 'serve' '--server=console', '--no-verbose', - '--url=' + path_info], - stderr=subprocess.PIPE, - stdout=subprocess.PIPE, - env=_make_env()) - stdout, errors = proc.communicate() - stdout = StringIO(stdout) - headers = rfc822.Message(stdout) - content = stdout.read() - for header, value in headers.items(): - if header.lower() == 'status' and int(value.split()[0]) == 200: - continue - if header.lower() in ('content-type', 'content-length'): - continue - if (header.lower() == 'set-cookie' - and value.startswith('_SID_')): - continue - out.write('%s: %s
    \n' - % (header, value)) - lines = [l for l in content.splitlines() if l.strip()] - for line in lines: - out.write(line + '\n') - if errors: - out.write('
    %s
    ' - % errors) - out.write('
    \n') - result = out.getvalue() - if not os.path.exists(fn): - f = open(fn, 'wb') - f.write(result) - f.close() - else: - f = open(fn, 'rb') - expected = f.read() - f.close() - if not html_matches(expected, result): - print 'Pages did not match. Expected from %s:' % fn - print '-'*60 - print expected - print '='*60 - print 'Actual output:' - print '-'*60 - print result - -def html_matches(pattern, text): - regex = re.escape(pattern) - regex = regex.replace(r'\.\.\.', '.*') - regex = re.sub(r'0x[0-9a-f]+', '.*', regex) - regex = '^%s$' % regex - return re.search(regex, text) - -def convert_docstring_string(data): - if data.startswith('\n'): - data = data[1:] - lines = data.splitlines() - new_lines = [] - for line in lines: - if line.rstrip() == '.': - new_lines.append('') - else: - new_lines.append(line) - data = '\n'.join(new_lines) + '\n' - return data - -def create_file(path, version, data): - data = convert_docstring_string(data) - write_data(path, data) - show_file(path, version) - -def append_to_file(path, version, data): - data = convert_docstring_string(data) - f = open(path, 'a') - f.write(data) - f.close() - # I think these appends can happen so quickly (in less than a second) - # that the .pyc file doesn't appear to be expired, even though it - # is after we've made this change; so we have to get rid of the .pyc - # file: - if path.endswith('.py'): - pyc_file = path + 'c' - if os.path.exists(pyc_file): - os.unlink(pyc_file) - show_file(path, version, description='added to %s' % path, - data=data) - -def show_file(path, version, description=None, data=None): - ext = os.path.splitext(path)[1] - if data is None: - f = open(path, 'rb') - data = f.read() - f.close() - if ext == '.py': - html = ('
    %s
    ' - % PySourceColor.str2html(data, PySourceColor.dark)) - else: - html = '
    %s
    ' % cgi.escape(data, 1) - html = '%s
    %s' % ( - description or path, html) - write_data(resource_filename('%s.%s.gen.html' % (path, version)), - html) - -def call_source_highlight(input, format): - proc = subprocess.Popen(['source-highlight', '--out-format=html', - '--no-doc', '--css=none', - '--src-lang=%s' % format], shell=False, - stdout=subprocess.PIPE) - stdout, stderr = proc.communicate(input) - result = stdout - proc.wait() - return result - - -def write_data(path, data): - dir = os.path.dirname(os.path.abspath(path)) - if not os.path.exists(dir): - os.makedirs(dir) - f = open(path, 'wb') - f.write(data) - f.close() - - -def change_file(path, changes): - f = open(os.path.abspath(path), 'rb') - lines = f.readlines() - f.close() - for change_type, line, text in changes: - if change_type == 'insert': - lines[line:line] = [text] - elif change_type == 'delete': - lines[line:text] = [] - else: - assert 0, ( - "Unknown change_type: %r" % change_type) - f = open(path, 'wb') - f.write(''.join(lines)) - f.close() - -class LongFormDocTestParser(doctest.DocTestParser): - - """ - This parser recognizes some reST comments as commands, without - prompts or expected output, like: - - .. run: - - do_this(... - ...) - """ - - _EXAMPLE_RE = re.compile(r""" - # Source consists of a PS1 line followed by zero or more PS2 lines. - (?: (?P - (?:^(?P [ ]*) >>> .*) # PS1 line - (?:\n [ ]* \.\.\. .*)*) # PS2 lines - \n? - # Want consists of any non-blank lines that do not start with PS1. - (?P (?:(?![ ]*$) # Not a blank line - (?![ ]*>>>) # Not a line starting with PS1 - .*$\n? # But any other line - )*)) - | - (?: # This is for longer commands that are prefixed with a reST - # comment like '.. run:' (two colons makes that a directive). - # These commands cannot have any output. - - (?:^\.\.[ ]*(?Prun):[ ]*\n) # Leading command/command - (?:[ ]*\n)? # Blank line following - (?P - (?:(?P [ ]+)[^ ].*$) - (?:\n [ ]+ .*)*) - ) - | - (?: # This is for shell commands - - (?P - (?:^(P [ ]*) [$] .*) # Shell line - (?:\n [ ]* [>] .*)*) # Continuation - \n? - # Want consists of any non-blank lines that do not start with $ - (?P (?:(?![ ]*$) - (?![ ]*[$]$) - .*$\n? - )*)) - """, re.MULTILINE | re.VERBOSE) - - def _parse_example(self, m, name, lineno): - r""" - Given a regular expression match from `_EXAMPLE_RE` (`m`), - return a pair `(source, want)`, where `source` is the matched - example's source code (with prompts and indentation stripped); - and `want` is the example's expected output (with indentation - stripped). - - `name` is the string's name, and `lineno` is the line number - where the example starts; both are used for error messages. - - >>> def parseit(s): - ... p = LongFormDocTestParser() - ... return p._parse_example(p._EXAMPLE_RE.search(s), '', 1) - >>> parseit('>>> 1\n1') - ('1', {}, '1', None) - >>> parseit('>>> (1\n... +1)\n2') - ('(1\n+1)', {}, '2', None) - >>> parseit('.. run:\n\n test1\n test2\n') - ('test1\ntest2', {}, '', None) - """ - # Get the example's indentation level. - runner = m.group('run') or '' - indent = len(m.group('%sindent' % runner)) - - # Divide source into lines; check that they're properly - # indented; and then strip their indentation & prompts. - source_lines = m.group('%ssource' % runner).split('\n') - if runner: - self._check_prefix(source_lines[1:], ' '*indent, name, lineno) - else: - self._check_prompt_blank(source_lines, indent, name, lineno) - self._check_prefix(source_lines[2:], ' '*indent + '.', name, lineno) - if runner: - source = '\n'.join([sl[indent:] for sl in source_lines]) - else: - source = '\n'.join([sl[indent+4:] for sl in source_lines]) - - if runner: - want = '' - exc_msg = None - else: - # Divide want into lines; check that it's properly indented; and - # then strip the indentation. Spaces before the last newline should - # be preserved, so plain rstrip() isn't good enough. - want = m.group('want') - want_lines = want.split('\n') - if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): - del want_lines[-1] # forget final newline & spaces after it - self._check_prefix(want_lines, ' '*indent, name, - lineno + len(source_lines)) - want = '\n'.join([wl[indent:] for wl in want_lines]) - - # If `want` contains a traceback message, then extract it. - m = self._EXCEPTION_RE.match(want) - if m: - exc_msg = m.group('msg') - else: - exc_msg = None - - # Extract options from the source. - options = self._find_options(source, name, lineno) - - return source, options, want, exc_msg - - - def parse(self, string, name=''): - """ - Divide the given string into examples and intervening text, - and return them as a list of alternating Examples and strings. - Line numbers for the Examples are 0-based. The optional - argument `name` is a name identifying this string, and is only - used for error messages. - """ - string = string.expandtabs() - # If all lines begin with the same indentation, then strip it. - min_indent = self._min_indent(string) - if min_indent > 0: - string = '\n'.join([l[min_indent:] for l in string.split('\n')]) - - output = [] - charno, lineno = 0, 0 - # Find all doctest examples in the string: - for m in self._EXAMPLE_RE.finditer(string): - # Add the pre-example text to `output`. - output.append(string[charno:m.start()]) - # Update lineno (lines before this example) - lineno += string.count('\n', charno, m.start()) - # Extract info from the regexp match. - (source, options, want, exc_msg) = \ - self._parse_example(m, name, lineno) - # Create an Example, and add it to the list. - if not self._IS_BLANK_OR_COMMENT(source): - # @@: Erg, this is the only line I need to change... - output.append(doctest.Example( - source, want, exc_msg, - lineno=lineno, - indent=min_indent+len(m.group('indent') or m.group('runindent')), - options=options)) - # Update lineno (lines inside this example) - lineno += string.count('\n', m.start(), m.end()) - # Update charno. - charno = m.end() - # Add any remaining post-example text to `output`. - output.append(string[charno:]) - return output - - - -if __name__ == '__main__': - if sys.argv[1:] and sys.argv[1] == 'doctest': - doctest.testmod() - sys.exit() - if not paste_parent in sys.path: - sys.path.append(paste_parent) - for fn in sys.argv[1:]: - fn = os.path.abspath(fn) - # @@: OK, ick; but this module gets loaded twice - sys.testing_document_filename = fn - doctest.testfile( - fn, module_relative=False, - optionflags=doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE, - parser=LongFormDocTestParser()) - new = os.path.splitext(fn)[0] + '.html' - assert new != fn - os.system('rst2html.py %s > %s' % (fn, new)) diff --git a/kcdc3/paste/debug/fsdiff.py b/kcdc3/paste/debug/fsdiff.py deleted file mode 100644 index 2849ea8d..00000000 --- a/kcdc3/paste/debug/fsdiff.py +++ /dev/null @@ -1,409 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -Module to find differences over time in a filesystem - -Basically this takes a snapshot of a directory, then sees what changes -were made. The contents of the files are not checked, so you can -detect that the content was changed, but not what the old version of -the file was. -""" - -import os -from fnmatch import fnmatch -from datetime import datetime -from paste.util.UserDict24 import IterableUserDict -import operator -import re - -__all__ = ['Diff', 'Snapshot', 'File', 'Dir', 'report_expected_diffs', - 'show_diff'] - -class Diff(object): - - """ - Represents the difference between two snapshots - """ - - def __init__(self, before, after): - self.before = before - self.after = after - self._calculate() - - def _calculate(self): - before = self.before.data - after = self.after.data - self.deleted = {} - self.updated = {} - self.created = after.copy() - for path, f in before.items(): - if path not in after: - self.deleted[path] = f - continue - del self.created[path] - if f.mtime < after[path].mtime: - self.updated[path] = after[path] - - def __str__(self): - return self.report() - - def report(self, header=True, dates=False): - s = [] - if header: - s.append('Difference in %s from %s to %s:' % - (self.before.base_path, - self.before.calculated, - self.after.calculated)) - for name, files, show_size in [ - ('created', self.created, True), - ('deleted', self.deleted, True), - ('updated', self.updated, True)]: - if files: - s.append('-- %s: -------------------' % name) - files = files.items() - files.sort() - last = '' - for path, f in files: - t = ' %s' % _space_prefix(last, path, indent=4, - include_sep=False) - last = path - if show_size and f.size != 'N/A': - t += ' (%s bytes)' % f.size - if dates: - parts = [] - if self.before.get(path): - parts.append(self.before[path].mtime) - if self.after.get(path): - parts.append(self.after[path].mtime) - t += ' (mtime: %s)' % ('->'.join(map(repr, parts))) - s.append(t) - if len(s) == 1: - s.append(' (no changes)') - return '\n'.join(s) - -class Snapshot(IterableUserDict): - - """ - Represents a snapshot of a set of files. Has a dictionary-like - interface, keyed relative to ``base_path`` - """ - - def __init__(self, base_path, files=None, ignore_wildcards=(), - ignore_paths=(), ignore_hidden=True): - self.base_path = base_path - self.ignore_wildcards = ignore_wildcards - self.ignore_hidden = ignore_hidden - self.ignore_paths = ignore_paths - self.calculated = None - self.data = files or {} - if files is None: - self.find_files() - - ############################################################ - ## File finding - ############################################################ - - def find_files(self): - """ - Find all the files under the base path, and put them in - ``self.data`` - """ - self._find_traverse('', self.data) - self.calculated = datetime.now() - - def _ignore_file(self, fn): - if fn in self.ignore_paths: - return True - if self.ignore_hidden and os.path.basename(fn).startswith('.'): - return True - for pat in self.ignore_wildcards: - if fnmatch(fn, pat): - return True - return False - - def _ignore_file(self, fn): - if fn in self.ignore_paths: - return True - if self.ignore_hidden and os.path.basename(fn).startswith('.'): - return True - return False - - def _find_traverse(self, path, result): - full = os.path.join(self.base_path, path) - if os.path.isdir(full): - if path: - # Don't actually include the base path - result[path] = Dir(self.base_path, path) - for fn in os.listdir(full): - fn = os.path.join(path, fn) - if self._ignore_file(fn): - continue - self._find_traverse(fn, result) - else: - result[path] = File(self.base_path, path) - - def __repr__(self): - return '<%s in %r from %r>' % ( - self.__class__.__name__, self.base_path, - self.calculated or '(no calculation done)') - - def compare_expected(self, expected, comparison=operator.eq, - differ=None, not_found=None, - include_success=False): - """ - Compares a dictionary of ``path: content`` to the - found files. Comparison is done by equality, or the - ``comparison(actual_content, expected_content)`` function given. - - Returns dictionary of differences, keyed by path. Each - difference is either noted, or the output of - ``differ(actual_content, expected_content)`` is given. - - If a file does not exist and ``not_found`` is given, then - ``not_found(path)`` is put in. - """ - result = {} - for path in expected: - orig_path = path - path = path.strip('/') - if path not in self.data: - if not_found: - msg = not_found(path) - else: - msg = 'not found' - result[path] = msg - continue - expected_content = expected[orig_path] - file = self.data[path] - actual_content = file.bytes - if not comparison(actual_content, expected_content): - if differ: - msg = differ(actual_content, expected_content) - else: - if len(actual_content) < len(expected_content): - msg = 'differ (%i bytes smaller)' % ( - len(expected_content) - len(actual_content)) - elif len(actual_content) > len(expected_content): - msg = 'differ (%i bytes larger)' % ( - len(actual_content) - len(expected_content)) - else: - msg = 'diff (same size)' - result[path] = msg - elif include_success: - result[path] = 'same!' - return result - - def diff_to_now(self): - return Diff(self, self.clone()) - - def clone(self): - return self.__class__(base_path=self.base_path, - ignore_wildcards=self.ignore_wildcards, - ignore_paths=self.ignore_paths, - ignore_hidden=self.ignore_hidden) - -class File(object): - - """ - Represents a single file found as the result of a command. - - Has attributes: - - ``path``: - The path of the file, relative to the ``base_path`` - - ``full``: - The full path - - ``stat``: - The results of ``os.stat``. Also ``mtime`` and ``size`` - contain the ``.st_mtime`` and ``st_size`` of the stat. - - ``bytes``: - The contents of the file. - - You may use the ``in`` operator with these objects (tested against - the contents of the file), and the ``.mustcontain()`` method. - """ - - file = True - dir = False - - def __init__(self, base_path, path): - self.base_path = base_path - self.path = path - self.full = os.path.join(base_path, path) - self.stat = os.stat(self.full) - self.mtime = self.stat.st_mtime - self.size = self.stat.st_size - self._bytes = None - - def bytes__get(self): - if self._bytes is None: - f = open(self.full, 'rb') - self._bytes = f.read() - f.close() - return self._bytes - bytes = property(bytes__get) - - def __contains__(self, s): - return s in self.bytes - - def mustcontain(self, s): - __tracebackhide__ = True - bytes = self.bytes - if s not in bytes: - print 'Could not find %r in:' % s - print bytes - assert s in bytes - - def __repr__(self): - return '<%s %s:%s>' % ( - self.__class__.__name__, - self.base_path, self.path) - -class Dir(File): - - """ - Represents a directory created by a command. - """ - - file = False - dir = True - - def __init__(self, base_path, path): - self.base_path = base_path - self.path = path - self.full = os.path.join(base_path, path) - self.size = 'N/A' - self.mtime = 'N/A' - - def __repr__(self): - return '<%s %s:%s>' % ( - self.__class__.__name__, - self.base_path, self.path) - - def bytes__get(self): - raise NotImplementedError( - "Directory %r doesn't have content" % self) - - bytes = property(bytes__get) - - -def _space_prefix(pref, full, sep=None, indent=None, include_sep=True): - """ - Anything shared by pref and full will be replaced with spaces - in full, and full returned. - - Example:: - - >>> _space_prefix('/foo/bar', '/foo') - ' /bar' - """ - if sep is None: - sep = os.path.sep - pref = pref.split(sep) - full = full.split(sep) - padding = [] - while pref and full and pref[0] == full[0]: - if indent is None: - padding.append(' ' * (len(full[0]) + len(sep))) - else: - padding.append(' ' * indent) - full.pop(0) - pref.pop(0) - if padding: - if include_sep: - return ''.join(padding) + sep + sep.join(full) - else: - return ''.join(padding) + sep.join(full) - else: - return sep.join(full) - -def report_expected_diffs(diffs, colorize=False): - """ - Takes the output of compare_expected, and returns a string - description of the differences. - """ - if not diffs: - return 'No differences' - diffs = diffs.items() - diffs.sort() - s = [] - last = '' - for path, desc in diffs: - t = _space_prefix(last, path, indent=4, include_sep=False) - if colorize: - t = color_line(t, 11) - last = path - if len(desc.splitlines()) > 1: - cur_indent = len(re.search(r'^[ ]*', t).group(0)) - desc = indent(cur_indent+2, desc) - if colorize: - t += '\n' - for line in desc.splitlines(): - if line.strip().startswith('+'): - line = color_line(line, 10) - elif line.strip().startswith('-'): - line = color_line(line, 9) - else: - line = color_line(line, 14) - t += line+'\n' - else: - t += '\n' + desc - else: - t += ' '+desc - s.append(t) - s.append('Files with differences: %s' % len(diffs)) - return '\n'.join(s) - -def color_code(foreground=None, background=None): - """ - 0 black - 1 red - 2 green - 3 yellow - 4 blue - 5 magenta (purple) - 6 cyan - 7 white (gray) - - Add 8 to get high-intensity - """ - if foreground is None and background is None: - # Reset - return '\x1b[0m' - codes = [] - if foreground is None: - codes.append('[39m') - elif foreground > 7: - codes.append('[1m') - codes.append('[%im' % (22+foreground)) - else: - codes.append('[%im' % (30+foreground)) - if background is None: - codes.append('[49m') - else: - codes.append('[%im' % (40+background)) - return '\x1b' + '\x1b'.join(codes) - -def color_line(line, foreground=None, background=None): - match = re.search(r'^(\s*)', line) - return (match.group(1) + color_code(foreground, background) - + line[match.end():] + color_code()) - -def indent(indent, text): - return '\n'.join( - [' '*indent + l for l in text.splitlines()]) - -def show_diff(actual_content, expected_content): - actual_lines = [l.strip() for l in actual_content.splitlines() - if l.strip()] - expected_lines = [l.strip() for l in expected_content.splitlines() - if l.strip()] - if len(actual_lines) == len(expected_lines) == 1: - return '%r not %r' % (actual_lines[0], expected_lines[0]) - if not actual_lines: - return 'Empty; should have:\n'+expected_content - import difflib - return '\n'.join(difflib.ndiff(actual_lines, expected_lines)) diff --git a/kcdc3/paste/debug/prints.py b/kcdc3/paste/debug/prints.py deleted file mode 100644 index d30fc8f7..00000000 --- a/kcdc3/paste/debug/prints.py +++ /dev/null @@ -1,148 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -Middleware that displays everything that is printed inline in -application pages. - -Anything printed during the request will get captured and included on -the page. It will usually be included as a floating element in the -top right hand corner of the page. If you want to override this -you can include a tag in your template where it will be placed:: - -
    
    -
    -You might want to include ``style="white-space: normal"``, as all the
    -whitespace will be quoted, and this allows the text to wrap if
    -necessary.
    -
    -"""
    -
    -from cStringIO import StringIO
    -import re
    -import cgi
    -from paste.util import threadedprint
    -from paste import wsgilib
    -from paste import response
    -import sys
    -
    -_threadedprint_installed = False
    -
    -__all__ = ['PrintDebugMiddleware']
    -
    -class TeeFile(object):
    -
    -    def __init__(self, files):
    -        self.files = files
    -
    -    def write(self, v):
    -        if isinstance(v, unicode):
    -            # WSGI is picky in this case
    -            v = str(v)
    -        for file in self.files:
    -            file.write(v)
    -
    -class PrintDebugMiddleware(object):
    -
    -    """
    -    This middleware captures all the printed statements, and inlines
    -    them in HTML pages, so that you can see all the (debug-intended)
    -    print statements in the page itself.
    -
    -    There are two keys added to the environment to control this:
    -    ``environ['paste.printdebug_listeners']`` is a list of functions
    -    that will be called everytime something is printed.
    -
    -    ``environ['paste.remove_printdebug']`` is a function that, if
    -    called, will disable printing of output for that request.
    -
    -    If you have ``replace_stdout=True`` then stdout is replaced, not
    -    captured.
    -    """
    -
    -    log_template = (
    -        '
    '
    -        'Log messages
    ' - '%s
    ') - - def __init__(self, app, global_conf=None, force_content_type=False, - print_wsgi_errors=True, replace_stdout=False): - # @@: global_conf should be handled separately and only for - # the entry point - self.app = app - self.force_content_type = force_content_type - if isinstance(print_wsgi_errors, basestring): - from paste.deploy.converters import asbool - print_wsgi_errors = asbool(print_wsgi_errors) - self.print_wsgi_errors = print_wsgi_errors - self.replace_stdout = replace_stdout - self._threaded_print_stdout = None - - def __call__(self, environ, start_response): - global _threadedprint_installed - if environ.get('paste.testing'): - # In a testing environment this interception isn't - # useful: - return self.app(environ, start_response) - if (not _threadedprint_installed - or self._threaded_print_stdout is not sys.stdout): - # @@: Not strictly threadsafe - _threadedprint_installed = True - threadedprint.install(leave_stdout=not self.replace_stdout) - self._threaded_print_stdout = sys.stdout - removed = [] - def remove_printdebug(): - removed.append(None) - environ['paste.remove_printdebug'] = remove_printdebug - logged = StringIO() - listeners = [logged] - environ['paste.printdebug_listeners'] = listeners - if self.print_wsgi_errors: - listeners.append(environ['wsgi.errors']) - replacement_stdout = TeeFile(listeners) - threadedprint.register(replacement_stdout) - try: - status, headers, body = wsgilib.intercept_output( - environ, self.app) - if status is None: - # Some error occurred - status = '500 Server Error' - headers = [('Content-type', 'text/html')] - start_response(status, headers) - if not body: - body = 'An error occurred' - content_type = response.header_value(headers, 'content-type') - if (removed or - (not self.force_content_type and - (not content_type - or not content_type.startswith('text/html')))): - if replacement_stdout == logged: - # Then the prints will be lost, unless... - environ['wsgi.errors'].write(logged.getvalue()) - start_response(status, headers) - return [body] - response.remove_header(headers, 'content-length') - body = self.add_log(body, logged.getvalue()) - start_response(status, headers) - return [body] - finally: - threadedprint.deregister() - - _body_re = re.compile(r']*>', re.I) - _explicit_re = re.compile(r']*id="paste-debug-prints".*?>', - re.I+re.S) - - def add_log(self, html, log): - if not log: - return html - text = cgi.escape(log) - text = text.replace('\n', '
    ') - text = text.replace(' ', '  ') - match = self._explicit_re.search(html) - if not match: - text = self.log_template % text - match = self._body_re.search(html) - if not match: - return text + html - else: - return html[:match.end()] + text + html[match.end():] diff --git a/kcdc3/paste/debug/profile.py b/kcdc3/paste/debug/profile.py deleted file mode 100644 index 8e2d40a1..00000000 --- a/kcdc3/paste/debug/profile.py +++ /dev/null @@ -1,227 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -Middleware that profiles the request and displays profiling -information at the bottom of each page. -""" - - -import sys -import os -import hotshot -import hotshot.stats -import threading -import cgi -import time -from cStringIO import StringIO -from paste import response - -__all__ = ['ProfileMiddleware', 'profile_decorator'] - -class ProfileMiddleware(object): - - """ - Middleware that profiles all requests. - - All HTML pages will have profiling information appended to them. - The data is isolated to that single request, and does not include - data from previous requests. - - This uses the ``hotshot`` module, which affects performance of the - application. It also runs in a single-threaded mode, so it is - only usable in development environments. - """ - - style = ('clear: both; background-color: #ff9; color: #000; ' - 'border: 2px solid #000; padding: 5px;') - - def __init__(self, app, global_conf=None, - log_filename='profile.log.tmp', - limit=40): - self.app = app - self.lock = threading.Lock() - self.log_filename = log_filename - self.limit = limit - - def __call__(self, environ, start_response): - catch_response = [] - body = [] - def replace_start_response(status, headers, exc_info=None): - catch_response.extend([status, headers]) - start_response(status, headers, exc_info) - return body.append - def run_app(): - app_iter = self.app(environ, replace_start_response) - try: - body.extend(app_iter) - finally: - if hasattr(app_iter, 'close'): - app_iter.close() - self.lock.acquire() - try: - prof = hotshot.Profile(self.log_filename) - prof.addinfo('URL', environ.get('PATH_INFO', '')) - try: - prof.runcall(run_app) - finally: - prof.close() - body = ''.join(body) - headers = catch_response[1] - content_type = response.header_value(headers, 'content-type') - if content_type is None or not content_type.startswith('text/html'): - # We can't add info to non-HTML output - return [body] - stats = hotshot.stats.load(self.log_filename) - stats.strip_dirs() - stats.sort_stats('time', 'calls') - output = capture_output(stats.print_stats, self.limit) - output_callers = capture_output( - stats.print_callers, self.limit) - body += '
    %s\n%s
    ' % ( - self.style, cgi.escape(output), cgi.escape(output_callers)) - return [body] - finally: - self.lock.release() - -def capture_output(func, *args, **kw): - # Not threadsafe! (that's okay when ProfileMiddleware uses it, - # though, since it synchronizes itself.) - out = StringIO() - old_stdout = sys.stdout - sys.stdout = out - try: - func(*args, **kw) - finally: - sys.stdout = old_stdout - return out.getvalue() - -def profile_decorator(**options): - - """ - Profile a single function call. - - Used around a function, like:: - - @profile_decorator(options...) - def ... - - All calls to the function will be profiled. The options are - all keywords, and are: - - log_file: - The filename to log to (or ``'stdout'`` or ``'stderr'``). - Default: stderr. - display_limit: - Only show the top N items, default: 20. - sort_stats: - A list of string-attributes to sort on. Default - ``('time', 'calls')``. - strip_dirs: - Strip directories/module names from files? Default True. - add_info: - If given, this info will be added to the report (for your - own tracking). Default: none. - log_filename: - The temporary filename to log profiling data to. Default; - ``./profile_data.log.tmp`` - no_profile: - If true, then don't actually profile anything. Useful for - conditional profiling. - """ - - if options.get('no_profile'): - def decorator(func): - return func - return decorator - def decorator(func): - def replacement(*args, **kw): - return DecoratedProfile(func, **options)(*args, **kw) - return replacement - return decorator - -class DecoratedProfile(object): - - lock = threading.Lock() - - def __init__(self, func, **options): - self.func = func - self.options = options - - def __call__(self, *args, **kw): - self.lock.acquire() - try: - return self.profile(self.func, *args, **kw) - finally: - self.lock.release() - - def profile(self, func, *args, **kw): - ops = self.options - prof_filename = ops.get('log_filename', 'profile_data.log.tmp') - prof = hotshot.Profile(prof_filename) - prof.addinfo('Function Call', - self.format_function(func, *args, **kw)) - if ops.get('add_info'): - prof.addinfo('Extra info', ops['add_info']) - exc_info = None - try: - start_time = time.time() - try: - result = prof.runcall(func, *args, **kw) - except: - exc_info = sys.exc_info() - end_time = time.time() - finally: - prof.close() - stats = hotshot.stats.load(prof_filename) - os.unlink(prof_filename) - if ops.get('strip_dirs', True): - stats.strip_dirs() - stats.sort_stats(*ops.get('sort_stats', ('time', 'calls'))) - display_limit = ops.get('display_limit', 20) - output = capture_output(stats.print_stats, display_limit) - output_callers = capture_output( - stats.print_callers, display_limit) - output_file = ops.get('log_file') - if output_file in (None, 'stderr'): - f = sys.stderr - elif output_file in ('-', 'stdout'): - f = sys.stdout - else: - f = open(output_file, 'a') - f.write('\n%s\n' % ('-'*60)) - f.write('Date: %s\n' % time.strftime('%c')) - f.write('Function call: %s\n' - % self.format_function(func, *args, **kw)) - f.write('Wall time: %0.2f seconds\n' - % (end_time - start_time)) - f.write(output) - f.write(output_callers) - if output_file not in (None, '-', 'stdout', 'stderr'): - f.close() - if exc_info: - # We captured an exception earlier, now we re-raise it - raise exc_info[0], exc_info[1], exc_info[2] - return result - - def format_function(self, func, *args, **kw): - args = map(repr, args) - args.extend( - ['%s=%r' % (k, v) for k, v in kw.items()]) - return '%s(%s)' % (func.__name__, ', '.join(args)) - - -def make_profile_middleware( - app, global_conf, - log_filename='profile.log.tmp', - limit=40): - """ - Wrap the application in a component that will profile each - request. The profiling data is then appended to the output - of each page. - - Note that this serializes all requests (i.e., removing - concurrency). Therefore never use this in production. - """ - limit = int(limit) - return ProfileMiddleware( - app, log_filename=log_filename, limit=limit) diff --git a/kcdc3/paste/debug/testserver.py b/kcdc3/paste/debug/testserver.py deleted file mode 100644 index 26c477a0..00000000 --- a/kcdc3/paste/debug/testserver.py +++ /dev/null @@ -1,93 +0,0 @@ -# (c) 2005 Clark C. Evans -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -# This code was written with funding by http://prometheusresearch.com -""" -WSGI Test Server - -This builds upon paste.util.baseserver to customize it for regressions -where using raw_interactive won't do. - - -""" -import time -from paste.httpserver import * - -class WSGIRegressionServer(WSGIServer): - """ - A threaded WSGIServer for use in regression testing. To use this - module, call serve(application, regression=True), and then call - server.accept() to let it handle one request. When finished, use - server.stop() to shutdown the server. Note that all pending requests - are processed before the server shuts down. - """ - defaulttimeout = 10 - def __init__ (self, *args, **kwargs): - WSGIServer.__init__(self, *args, **kwargs) - self.stopping = [] - self.pending = [] - self.timeout = self.defaulttimeout - # this is a local connection, be quick - self.socket.settimeout(2) - def serve_forever(self): - from threading import Thread - thread = Thread(target=self.serve_pending) - thread.start() - def reset_expires(self): - if self.timeout: - self.expires = time.time() + self.timeout - def close_request(self, *args, **kwargs): - WSGIServer.close_request(self, *args, **kwargs) - self.pending.pop() - self.reset_expires() - def serve_pending(self): - self.reset_expires() - while not self.stopping or self.pending: - now = time.time() - if now > self.expires and self.timeout: - # note regression test doesn't handle exceptions in - # threads very well; so we just print and exit - print "\nWARNING: WSGIRegressionServer timeout exceeded\n" - break - if self.pending: - self.handle_request() - time.sleep(.1) - def stop(self): - """ stop the server (called from tester's thread) """ - self.stopping.append(True) - def accept(self, count = 1): - """ accept another request (called from tester's thread) """ - assert not self.stopping - [self.pending.append(True) for x in range(count)] - -def serve(application, host=None, port=None, handler=None): - server = WSGIRegressionServer(application, host, port, handler) - print "serving on %s:%s" % server.server_address - server.serve_forever() - return server - -if __name__ == '__main__': - import urllib - from paste.wsgilib import dump_environ - server = serve(dump_environ) - baseuri = ("http://%s:%s" % server.server_address) - - def fetch(path): - # tell the server to humor exactly one more request - server.accept(1) - # not needed; but this is what you do if the server - # may not respond in a resonable time period - import socket - socket.setdefaulttimeout(5) - # build a uri, fetch and return - return urllib.urlopen(baseuri + path).read() - - assert "PATH_INFO: /foo" in fetch("/foo") - assert "PATH_INFO: /womble" in fetch("/womble") - - # ok, let's make one more final request... - server.accept(1) - # and then schedule a stop() - server.stop() - # and then... fetch it... - urllib.urlopen(baseuri) diff --git a/kcdc3/paste/debug/watchthreads.py b/kcdc3/paste/debug/watchthreads.py deleted file mode 100644 index 10482132..00000000 --- a/kcdc3/paste/debug/watchthreads.py +++ /dev/null @@ -1,347 +0,0 @@ -""" -Watches the key ``paste.httpserver.thread_pool`` to see how many -threads there are and report on any wedged threads. -""" -import sys -import cgi -import time -import traceback -from cStringIO import StringIO -from thread import get_ident -from paste import httpexceptions -from paste.request import construct_url, parse_formvars -from paste.util.template import HTMLTemplate, bunch - -page_template = HTMLTemplate(''' - - - - {{title}} - - -

    {{title}}

    - {{if kill_thread_id}} -
    - Thread {{kill_thread_id}} killed -
    - {{endif}} -
    Pool size: {{nworkers}} - {{if actual_workers > nworkers}} - + {{actual_workers-nworkers}} extra - {{endif}} - ({{nworkers_used}} used including current request)
    - idle: {{len(track_threads["idle"])}}, - busy: {{len(track_threads["busy"])}}, - hung: {{len(track_threads["hung"])}}, - dying: {{len(track_threads["dying"])}}, - zombie: {{len(track_threads["zombie"])}}
    - -{{for thread in threads}} - - - - - - - - - - - - - - - - -
    - Thread - {{if thread.thread_id == this_thread_id}} - (this request) - {{endif}} - {{thread.thread_id}} - {{if allow_kill}} -
    - - -
    - {{endif}} -
    -
    Time processing request{{thread.time_html|html}}
    URI{{if thread.uri == 'unknown'}} - unknown - {{else}}{{thread.uri_short}} - {{endif}} -
    - ▸ Show environ - - - - {{if thread.traceback}} - ▸ Show traceback - - - {{endif}} - -
    - -{{endfor}} - - - -''', name='watchthreads.page_template') - -class WatchThreads(object): - - """ - Application that watches the threads in ``paste.httpserver``, - showing the length each thread has been working on a request. - - If allow_kill is true, then you can kill errant threads through - this application. - - This application can expose private information (specifically in - the environment, like cookies), so it should be protected. - """ - - def __init__(self, allow_kill=False): - self.allow_kill = allow_kill - - def __call__(self, environ, start_response): - if 'paste.httpserver.thread_pool' not in environ: - start_response('403 Forbidden', [('Content-type', 'text/plain')]) - return ['You must use the threaded Paste HTTP server to use this application'] - if environ.get('PATH_INFO') == '/kill': - return self.kill(environ, start_response) - else: - return self.show(environ, start_response) - - def show(self, environ, start_response): - start_response('200 OK', [('Content-type', 'text/html')]) - form = parse_formvars(environ) - if form.get('kill'): - kill_thread_id = form['kill'] - else: - kill_thread_id = None - thread_pool = environ['paste.httpserver.thread_pool'] - nworkers = thread_pool.nworkers - now = time.time() - - - workers = thread_pool.worker_tracker.items() - workers.sort(key=lambda v: v[1][0]) - threads = [] - for thread_id, (time_started, worker_environ) in workers: - thread = bunch() - threads.append(thread) - if worker_environ: - thread.uri = construct_url(worker_environ) - else: - thread.uri = 'unknown' - thread.thread_id = thread_id - thread.time_html = format_time(now-time_started) - thread.uri_short = shorten(thread.uri) - thread.environ = worker_environ - thread.traceback = traceback_thread(thread_id) - - page = page_template.substitute( - title="Thread Pool Worker Tracker", - nworkers=nworkers, - actual_workers=len(thread_pool.workers), - nworkers_used=len(workers), - script_name=environ['SCRIPT_NAME'], - kill_thread_id=kill_thread_id, - allow_kill=self.allow_kill, - threads=threads, - this_thread_id=get_ident(), - track_threads=thread_pool.track_threads()) - - return [page] - - def kill(self, environ, start_response): - if not self.allow_kill: - exc = httpexceptions.HTTPForbidden( - 'Killing threads has not been enabled. Shame on you ' - 'for trying!') - return exc(environ, start_response) - vars = parse_formvars(environ) - thread_id = int(vars['thread_id']) - thread_pool = environ['paste.httpserver.thread_pool'] - if thread_id not in thread_pool.worker_tracker: - exc = httpexceptions.PreconditionFailed( - 'You tried to kill thread %s, but it is not working on ' - 'any requests' % thread_id) - return exc(environ, start_response) - thread_pool.kill_worker(thread_id) - script_name = environ['SCRIPT_NAME'] or '/' - exc = httpexceptions.HTTPFound( - headers=[('Location', script_name+'?kill=%s' % thread_id)]) - return exc(environ, start_response) - -def traceback_thread(thread_id): - """ - Returns a plain-text traceback of the given thread, or None if it - can't get a traceback. - """ - if not hasattr(sys, '_current_frames'): - # Only 2.5 has support for this, with this special function - return None - frames = sys._current_frames() - if not thread_id in frames: - return None - frame = frames[thread_id] - out = StringIO() - traceback.print_stack(frame, file=out) - return out.getvalue() - -hide_keys = ['paste.httpserver.thread_pool'] - -def format_environ(environ): - if environ is None: - return environ_template.substitute( - key='---', - value='No environment registered for this thread yet') - environ_rows = [] - for key, value in sorted(environ.items()): - if key in hide_keys: - continue - try: - if key.upper() != key: - value = repr(value) - environ_rows.append( - environ_template.substitute( - key=cgi.escape(str(key)), - value=cgi.escape(str(value)))) - except Exception, e: - environ_rows.append( - environ_template.substitute( - key=cgi.escape(str(key)), - value='Error in repr(): %s' % e)) - return ''.join(environ_rows) - -def format_time(time_length): - if time_length >= 60*60: - # More than an hour - time_string = '%i:%02i:%02i' % (int(time_length/60/60), - int(time_length/60) % 60, - time_length % 60) - elif time_length >= 120: - time_string = '%i:%02i' % (int(time_length/60), - time_length % 60) - elif time_length > 60: - time_string = '%i sec' % time_length - elif time_length > 1: - time_string = '%0.1f sec' % time_length - else: - time_string = '%0.2f sec' % time_length - if time_length < 5: - return time_string - elif time_length < 120: - return '%s' % time_string - else: - return '%s' % time_string - -def shorten(s): - if len(s) > 60: - return s[:40]+'...'+s[-10:] - else: - return s - -def make_watch_threads(global_conf, allow_kill=False): - from paste.deploy.converters import asbool - return WatchThreads(allow_kill=asbool(allow_kill)) -make_watch_threads.__doc__ = WatchThreads.__doc__ - -def make_bad_app(global_conf, pause=0): - pause = int(pause) - def bad_app(environ, start_response): - import thread - if pause: - time.sleep(pause) - else: - count = 0 - while 1: - print "I'm alive %s (%s)" % (count, thread.get_ident()) - time.sleep(10) - count += 1 - start_response('200 OK', [('content-type', 'text/plain')]) - return ['OK, paused %s seconds' % pause] - return bad_app diff --git a/kcdc3/paste/debug/wdg_validate.py b/kcdc3/paste/debug/wdg_validate.py deleted file mode 100644 index d3678fb0..00000000 --- a/kcdc3/paste/debug/wdg_validate.py +++ /dev/null @@ -1,121 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -Middleware that tests the validity of all generated HTML using the -`WDG HTML Validator `_ -""" - -from cStringIO import StringIO -try: - import subprocess -except ImportError: - from paste.util import subprocess24 as subprocess -from paste.response import header_value -import re -import cgi - -__all__ = ['WDGValidateMiddleware'] - -class WDGValidateMiddleware(object): - - """ - Middleware that checks HTML and appends messages about the validity of - the HTML. Uses: http://www.htmlhelp.com/tools/validator/ -- interacts - with the command line client. Use the configuration ``wdg_path`` to - override the path (default: looks for ``validate`` in $PATH). - - To install, in your web context's __init__.py:: - - def urlparser_wrap(environ, start_response, app): - return wdg_validate.WDGValidateMiddleware(app)( - environ, start_response) - - Or in your configuration:: - - middleware.append('paste.wdg_validate.WDGValidateMiddleware') - """ - - _end_body_regex = re.compile(r'', re.I) - - def __init__(self, app, global_conf=None, wdg_path='validate'): - self.app = app - self.wdg_path = wdg_path - - def __call__(self, environ, start_response): - output = StringIO() - response = [] - - def writer_start_response(status, headers, exc_info=None): - response.extend((status, headers)) - start_response(status, headers, exc_info) - return output.write - - app_iter = self.app(environ, writer_start_response) - try: - for s in app_iter: - output.write(s) - finally: - if hasattr(app_iter, 'close'): - app_iter.close() - page = output.getvalue() - status, headers = response - v = header_value(headers, 'content-type') or '' - if (not v.startswith('text/html') - and not v.startswith('text/xhtml') - and not v.startswith('application/xhtml')): - # Can't validate - # @@: Should validate CSS too... but using what? - return [page] - ops = [] - if v.startswith('text/xhtml+xml'): - ops.append('--xml') - # @@: Should capture encoding too - html_errors = self.call_wdg_validate( - self.wdg_path, ops, page) - if html_errors: - page = self.add_error(page, html_errors)[0] - headers.remove( - ('Content-Length', - str(header_value(headers, 'content-length')))) - headers.append(('Content-Length', str(len(page)))) - return [page] - - def call_wdg_validate(self, wdg_path, ops, page): - if subprocess is None: - raise ValueError( - "This middleware requires the subprocess module from " - "Python 2.4") - proc = subprocess.Popen([wdg_path] + ops, - shell=False, - close_fds=True, - stdout=subprocess.PIPE, - stdin=subprocess.PIPE, - stderr=subprocess.STDOUT) - stdout = proc.communicate(page)[0] - proc.wait() - return stdout - - def add_error(self, html_page, html_errors): - add_text = ('
    %s
    ' - % cgi.escape(html_errors)) - match = self._end_body_regex.search(html_page) - if match: - return [html_page[:match.start()] - + add_text - + html_page[match.start():]] - else: - return [html_page + add_text] - -def make_wdg_validate_middleware( - app, global_conf, wdg_path='validate'): - """ - Wraps the application in the WDG validator from - http://www.htmlhelp.com/tools/validator/ - - Validation errors are appended to the text of each page. - You can configure this by giving the path to the validate - executable (by default picked up from $PATH) - """ - return WDGValidateMiddleware( - app, global_conf, wdg_path=wdg_path) diff --git a/kcdc3/paste/errordocument.py b/kcdc3/paste/errordocument.py deleted file mode 100644 index e57c1624..00000000 --- a/kcdc3/paste/errordocument.py +++ /dev/null @@ -1,383 +0,0 @@ -# (c) 2005-2006 James Gardner -# This module is part of the Python Paste Project and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -""" -Middleware to display error documents for certain status codes - -The middleware in this module can be used to intercept responses with -specified status codes and internally forward the request to an appropriate -URL where the content can be displayed to the user as an error document. -""" - -import warnings -import sys -from urlparse import urlparse -from paste.recursive import ForwardRequestException, RecursiveMiddleware, RecursionLoop -from paste.util import converters -from paste.response import replace_header - -def forward(app, codes): - """ - Intercepts a response with a particular status code and returns the - content from a specified URL instead. - - The arguments are: - - ``app`` - The WSGI application or middleware chain. - - ``codes`` - A dictionary of integer status codes and the URL to be displayed - if the response uses that code. - - For example, you might want to create a static file to display a - "File Not Found" message at the URL ``/error404.html`` and then use - ``forward`` middleware to catch all 404 status codes and display the page - you created. In this example ``app`` is your exisiting WSGI - applicaiton:: - - from paste.errordocument import forward - app = forward(app, codes={404:'/error404.html'}) - - """ - for code in codes: - if not isinstance(code, int): - raise TypeError('All status codes should be type int. ' - '%s is not valid'%repr(code)) - - def error_codes_mapper(code, message, environ, global_conf, codes): - if codes.has_key(code): - return codes[code] - else: - return None - - #return _StatusBasedRedirect(app, error_codes_mapper, codes=codes) - return RecursiveMiddleware( - StatusBasedForward( - app, - error_codes_mapper, - codes=codes, - ) - ) - -class StatusKeeper(object): - def __init__(self, app, status, url, headers): - self.app = app - self.status = status - self.url = url - self.headers = headers - - def __call__(self, environ, start_response): - def keep_status_start_response(status, headers, exc_info=None): - for header, value in headers: - if header.lower() == 'set-cookie': - self.headers.append((header, value)) - else: - replace_header(self.headers, header, value) - return start_response(self.status, self.headers, exc_info) - parts = self.url.split('?') - environ['PATH_INFO'] = parts[0] - if len(parts) > 1: - environ['QUERY_STRING'] = parts[1] - else: - environ['QUERY_STRING'] = '' - #raise Exception(self.url, self.status) - try: - return self.app(environ, keep_status_start_response) - except RecursionLoop, e: - environ['wsgi.errors'].write('Recursion error getting error page: %s\n' % e) - keep_status_start_response('500 Server Error', [('Content-type', 'text/plain')], sys.exc_info()) - return ['Error: %s. (Error page could not be fetched)' - % self.status] - - -class StatusBasedForward(object): - """ - Middleware that lets you test a response against a custom mapper object to - programatically determine whether to internally forward to another URL and - if so, which URL to forward to. - - If you don't need the full power of this middleware you might choose to use - the simpler ``forward`` middleware instead. - - The arguments are: - - ``app`` - The WSGI application or middleware chain. - - ``mapper`` - A callable that takes a status code as the - first parameter, a message as the second, and accepts optional environ, - global_conf and named argments afterwards. It should return a - URL to forward to or ``None`` if the code is not to be intercepted. - - ``global_conf`` - Optional default configuration from your config file. If ``debug`` is - set to ``true`` a message will be written to ``wsgi.errors`` on each - internal forward stating the URL forwarded to. - - ``**params`` - Optional, any other configuration and extra arguments you wish to - pass which will in turn be passed back to the custom mapper object. - - Here is an example where a ``404 File Not Found`` status response would be - redirected to the URL ``/error?code=404&message=File%20Not%20Found``. This - could be useful for passing the status code and message into another - application to display an error document: - - .. code-block:: python - - from paste.errordocument import StatusBasedForward - from paste.recursive import RecursiveMiddleware - from urllib import urlencode - - def error_mapper(code, message, environ, global_conf, kw) - if code in [404, 500]: - params = urlencode({'message':message, 'code':code}) - url = '/error?'%(params) - return url - else: - return None - - app = RecursiveMiddleware( - StatusBasedForward(app, mapper=error_mapper), - ) - - """ - - def __init__(self, app, mapper, global_conf=None, **params): - if global_conf is None: - global_conf = {} - # @@: global_conf shouldn't really come in here, only in a - # separate make_status_based_forward function - if global_conf: - self.debug = converters.asbool(global_conf.get('debug', False)) - else: - self.debug = False - self.application = app - self.mapper = mapper - self.global_conf = global_conf - self.params = params - - def __call__(self, environ, start_response): - url = [] - writer = [] - - def change_response(status, headers, exc_info=None): - status_code = status.split(' ') - try: - code = int(status_code[0]) - except (ValueError, TypeError): - raise Exception( - 'StatusBasedForward middleware ' - 'received an invalid status code %s'%repr(status_code[0]) - ) - message = ' '.join(status_code[1:]) - new_url = self.mapper( - code, - message, - environ, - self.global_conf, - **self.params - ) - if not (new_url == None or isinstance(new_url, str)): - raise TypeError( - 'Expected the url to internally ' - 'redirect to in the StatusBasedForward mapper' - 'to be a string or None, not %r' % new_url) - if new_url: - url.append([new_url, status, headers]) - # We have to allow the app to write stuff, even though - # we'll ignore it: - return [].append - else: - return start_response(status, headers, exc_info) - - app_iter = self.application(environ, change_response) - if url: - if hasattr(app_iter, 'close'): - app_iter.close() - - def factory(app): - return StatusKeeper(app, status=url[0][1], url=url[0][0], - headers=url[0][2]) - raise ForwardRequestException(factory=factory) - else: - return app_iter - -def make_errordocument(app, global_conf, **kw): - """ - Paste Deploy entry point to create a error document wrapper. - - Use like:: - - [filter-app:main] - use = egg:Paste#errordocument - next = real-app - 500 = /lib/msg/500.html - 404 = /lib/msg/404.html - """ - map = {} - for status, redir_loc in kw.items(): - try: - status = int(status) - except ValueError: - raise ValueError('Bad status code: %r' % status) - map[status] = redir_loc - forwarder = forward(app, map) - return forwarder - -__pudge_all__ = [ - 'forward', - 'make_errordocument', - 'empty_error', - 'make_empty_error', - 'StatusBasedForward', -] - - -############################################################################### -## Deprecated -############################################################################### - -def custom_forward(app, mapper, global_conf=None, **kw): - """ - Deprectated; use StatusBasedForward instead. - """ - warnings.warn( - "errordocuments.custom_forward has been deprecated; please " - "use errordocuments.StatusBasedForward", - DeprecationWarning, 2) - if global_conf is None: - global_conf = {} - return _StatusBasedRedirect(app, mapper, global_conf, **kw) - -class _StatusBasedRedirect(object): - """ - Deprectated; use StatusBasedForward instead. - """ - def __init__(self, app, mapper, global_conf=None, **kw): - - warnings.warn( - "errordocuments._StatusBasedRedirect has been deprecated; please " - "use errordocuments.StatusBasedForward", - DeprecationWarning, 2) - - if global_conf is None: - global_conf = {} - self.application = app - self.mapper = mapper - self.global_conf = global_conf - self.kw = kw - self.fallback_template = """ - - - Error %(code)s - - -

    Error %(code)s

    -

    %(message)s

    -
    -

    - Additionally an error occurred trying to produce an - error document. A description of the error was logged - to wsgi.errors. -

    - - - """ - - def __call__(self, environ, start_response): - url = [] - code_message = [] - try: - def change_response(status, headers, exc_info=None): - new_url = None - parts = status.split(' ') - try: - code = int(parts[0]) - except (ValueError, TypeError): - raise Exception( - '_StatusBasedRedirect middleware ' - 'received an invalid status code %s'%repr(parts[0]) - ) - message = ' '.join(parts[1:]) - new_url = self.mapper( - code, - message, - environ, - self.global_conf, - self.kw - ) - if not (new_url == None or isinstance(new_url, str)): - raise TypeError( - 'Expected the url to internally ' - 'redirect to in the _StatusBasedRedirect error_mapper' - 'to be a string or None, not %s'%repr(new_url) - ) - if new_url: - url.append(new_url) - code_message.append([code, message]) - return start_response(status, headers, exc_info) - app_iter = self.application(environ, change_response) - except: - try: - import sys - error = str(sys.exc_info()[1]) - except: - error = '' - try: - code, message = code_message[0] - except: - code, message = ['', ''] - environ['wsgi.errors'].write( - 'Error occurred in _StatusBasedRedirect ' - 'intercepting the response: '+str(error) - ) - return [self.fallback_template - % {'message': message, 'code': code}] - else: - if url: - url_ = url[0] - new_environ = {} - for k, v in environ.items(): - if k != 'QUERY_STRING': - new_environ['QUERY_STRING'] = urlparse(url_)[4] - else: - new_environ[k] = v - class InvalidForward(Exception): - pass - def eat_start_response(status, headers, exc_info=None): - """ - We don't want start_response to do anything since it - has already been called - """ - if status[:3] != '200': - raise InvalidForward( - "The URL %s to internally forward " - "to in order to create an error document did not " - "return a '200' status code." % url_ - ) - forward = environ['paste.recursive.forward'] - old_start_response = forward.start_response - forward.start_response = eat_start_response - try: - app_iter = forward(url_, new_environ) - except InvalidForward, e: - code, message = code_message[0] - environ['wsgi.errors'].write( - 'Error occurred in ' - '_StatusBasedRedirect redirecting ' - 'to new URL: '+str(url[0]) - ) - return [ - self.fallback_template%{ - 'message':message, - 'code':code, - } - ] - else: - forward.start_response = old_start_response - return app_iter - else: - return app_iter diff --git a/kcdc3/paste/evalexception/__init__.py b/kcdc3/paste/evalexception/__init__.py deleted file mode 100644 index a19cf85f..00000000 --- a/kcdc3/paste/evalexception/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -An exception handler for interactive debugging -""" -from paste.evalexception.middleware import EvalException - diff --git a/kcdc3/paste/evalexception/evalcontext.py b/kcdc3/paste/evalexception/evalcontext.py deleted file mode 100644 index dca2a97a..00000000 --- a/kcdc3/paste/evalexception/evalcontext.py +++ /dev/null @@ -1,68 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -from cStringIO import StringIO -import traceback -import threading -import pdb -import sys - -exec_lock = threading.Lock() - -class EvalContext(object): - - """ - Class that represents a interactive interface. It has its own - namespace. Use eval_context.exec_expr(expr) to run commands; the - output of those commands is returned, as are print statements. - - This is essentially what doctest does, and is taken directly from - doctest. - """ - - def __init__(self, namespace, globs): - self.namespace = namespace - self.globs = globs - - def exec_expr(self, s): - out = StringIO() - exec_lock.acquire() - save_stdout = sys.stdout - try: - debugger = _OutputRedirectingPdb(save_stdout) - debugger.reset() - pdb.set_trace = debugger.set_trace - sys.stdout = out - try: - code = compile(s, '', "single", 0, 1) - exec code in self.namespace, self.globs - debugger.set_continue() - except KeyboardInterrupt: - raise - except: - traceback.print_exc(file=out) - debugger.set_continue() - finally: - sys.stdout = save_stdout - exec_lock.release() - return out.getvalue() - -# From doctest -class _OutputRedirectingPdb(pdb.Pdb): - """ - A specialized version of the python debugger that redirects stdout - to a given stream when interacting with the user. Stdout is *not* - redirected when traced code is executed. - """ - def __init__(self, out): - self.__out = out - pdb.Pdb.__init__(self) - - def trace_dispatch(self, *args): - # Redirect stdout to the given stream. - save_stdout = sys.stdout - sys.stdout = self.__out - # Call Pdb's trace dispatch method. - try: - return pdb.Pdb.trace_dispatch(self, *args) - finally: - sys.stdout = save_stdout diff --git a/kcdc3/paste/evalexception/media/MochiKit.packed.js b/kcdc3/paste/evalexception/media/MochiKit.packed.js deleted file mode 100644 index 15027d9d..00000000 --- a/kcdc3/paste/evalexception/media/MochiKit.packed.js +++ /dev/null @@ -1,7829 +0,0 @@ -/*** - - MochiKit.MochiKit 1.4.2 : PACKED VERSION - - THIS FILE IS AUTOMATICALLY GENERATED. If creating patches, please - diff against the source tree, not this file. - - See for documentation, downloads, license, etc. - - (c) 2005 Bob Ippolito. All rights Reserved. - -***/ - -if(typeof (dojo)!="undefined"){ -dojo.provide("MochiKit.Base"); -} -if(typeof (MochiKit)=="undefined"){ -MochiKit={}; -} -if(typeof (MochiKit.Base)=="undefined"){ -MochiKit.Base={}; -} -if(typeof (MochiKit.__export__)=="undefined"){ -MochiKit.__export__=(MochiKit.__compat__||(typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")); -} -MochiKit.Base.VERSION="1.4.2"; -MochiKit.Base.NAME="MochiKit.Base"; -MochiKit.Base.update=function(_1,_2){ -if(_1===null||_1===undefined){ -_1={}; -} -for(var i=1;i=0;i--){ -_18.unshift(o[i]); -} -}else{ -res.push(o); -} -} -return res; -},extend:function(_1b,obj,_1d){ -if(!_1d){ -_1d=0; -} -if(obj){ -var l=obj.length; -if(typeof (l)!="number"){ -if(typeof (MochiKit.Iter)!="undefined"){ -obj=MochiKit.Iter.list(obj); -l=obj.length; -}else{ -throw new TypeError("Argument not an array-like and MochiKit.Iter not present"); -} -} -if(!_1b){ -_1b=[]; -} -for(var i=_1d;i>b; -},zrshift:function(a,b){ -return a>>>b; -},eq:function(a,b){ -return a==b; -},ne:function(a,b){ -return a!=b; -},gt:function(a,b){ -return a>b; -},ge:function(a,b){ -return a>=b; -},lt:function(a,b){ -return al){ -_93=l; -} -} -_91=[]; -for(i=0;i<_93;i++){ -var _95=[]; -for(var j=1;j=0;i--){ -_b2=[_ae[i].apply(this,_b2)]; -} -return _b2[0]; -}; -},bind:function(_b4,_b5){ -if(typeof (_b4)=="string"){ -_b4=_b5[_b4]; -} -var _b6=_b4.im_func; -var _b7=_b4.im_preargs; -var _b8=_b4.im_self; -var m=MochiKit.Base; -if(typeof (_b4)=="function"&&typeof (_b4.apply)=="undefined"){ -_b4=m._wrapDumbFunction(_b4); -} -if(typeof (_b6)!="function"){ -_b6=_b4; -} -if(typeof (_b5)!="undefined"){ -_b8=_b5; -} -if(typeof (_b7)=="undefined"){ -_b7=[]; -}else{ -_b7=_b7.slice(); -} -m.extend(_b7,arguments,2); -var _ba=function(){ -var _bb=arguments; -var me=arguments.callee; -if(me.im_preargs.length>0){ -_bb=m.concat(me.im_preargs,_bb); -} -var _bd=me.im_self; -if(!_bd){ -_bd=this; -} -return me.im_func.apply(_bd,_bb); -}; -_ba.im_self=_b8; -_ba.im_func=_b6; -_ba.im_preargs=_b7; -return _ba; -},bindLate:function(_be,_bf){ -var m=MochiKit.Base; -if(typeof (_be)!="string"){ -return m.bind.apply(this,arguments); -} -var _c1=m.extend([],arguments,2); -var _c2=function(){ -var _c3=arguments; -var me=arguments.callee; -if(me.im_preargs.length>0){ -_c3=m.concat(me.im_preargs,_c3); -} -var _c5=me.im_self; -if(!_c5){ -_c5=this; -} -return _c5[me.im_func].apply(_c5,_c3); -}; -_c2.im_self=_bf; -_c2.im_func=_be; -_c2.im_preargs=_c1; -return _c2; -},bindMethods:function(_c6){ -var _c7=MochiKit.Base.bind; -for(var k in _c6){ -var _c9=_c6[k]; -if(typeof (_c9)=="function"){ -_c6[k]=_c7(_c9,_c6); -} -} -},registerComparator:function(_ca,_cb,_cc,_cd){ -MochiKit.Base.comparatorRegistry.register(_ca,_cb,_cc,_cd); -},_primitives:{"boolean":true,"string":true,"number":true},compare:function(a,b){ -if(a==b){ -return 0; -} -var _d0=(typeof (a)=="undefined"||a===null); -var _d1=(typeof (b)=="undefined"||b===null); -if(_d0&&_d1){ -return 0; -}else{ -if(_d0){ -return -1; -}else{ -if(_d1){ -return 1; -} -} -} -var m=MochiKit.Base; -var _d3=m._primitives; -if(!(typeof (a) in _d3&&typeof (b) in _d3)){ -try{ -return m.comparatorRegistry.match(a,b); -} -catch(e){ -if(e!=m.NotFound){ -throw e; -} -} -} -if(ab){ -return 1; -} -} -var _d4=m.repr; -throw new TypeError(_d4(a)+" and "+_d4(b)+" can not be compared"); -},compareDateLike:function(a,b){ -return MochiKit.Base.compare(a.getTime(),b.getTime()); -},compareArrayLike:function(a,b){ -var _d9=MochiKit.Base.compare; -var _da=a.length; -var _db=0; -if(_da>b.length){ -_db=1; -_da=b.length; -}else{ -if(_da=0;i--){ -sum+=o[i]; -} -}else{ -sum+=o; -} -} -if(_121<=0){ -throw new TypeError("mean() requires at least one argument"); -} -return sum/_121; -},median:function(){ -var data=MochiKit.Base.flattenArguments(arguments); -if(data.length===0){ -throw new TypeError("median() requires at least one argument"); -} -data.sort(compare); -if(data.length%2==0){ -var _125=data.length/2; -return (data[_125]+data[_125-1])/2; -}else{ -return data[(data.length-1)/2]; -} -},findValue:function(lst,_127,_128,end){ -if(typeof (end)=="undefined"||end===null){ -end=lst.length; -} -if(typeof (_128)=="undefined"||_128===null){ -_128=0; -} -var cmp=MochiKit.Base.compare; -for(var i=_128;i0))){ -var kv=MochiKit.DOM.formContents(_135); -_135=kv[0]; -_136=kv[1]; -}else{ -if(arguments.length==1){ -if(typeof (_135.length)=="number"&&_135.length==2){ -return arguments.callee(_135[0],_135[1]); -} -var o=_135; -_135=[]; -_136=[]; -for(var k in o){ -var v=o[k]; -if(typeof (v)=="function"){ -continue; -}else{ -if(MochiKit.Base.isArrayLike(v)){ -for(var i=0;i=stop){ -throw self.StopIteration; -} -_183+=step; -return rval; -}}; -},imap:function(fun,p,q){ -var m=MochiKit.Base; -var self=MochiKit.Iter; -var _18d=m.map(self.iter,m.extend(null,arguments,1)); -var map=m.map; -var next=self.next; -return {repr:function(){ -return "imap(...)"; -},toString:m.forwardCall("repr"),next:function(){ -return fun.apply(this,map(next,_18d)); -}}; -},applymap:function(fun,seq,self){ -seq=MochiKit.Iter.iter(seq); -var m=MochiKit.Base; -return {repr:function(){ -return "applymap(...)"; -},toString:m.forwardCall("repr"),next:function(){ -return fun.apply(self,seq.next()); -}}; -},chain:function(p,q){ -var self=MochiKit.Iter; -var m=MochiKit.Base; -if(arguments.length==1){ -return self.iter(arguments[0]); -} -var _198=m.map(self.iter,arguments); -return {repr:function(){ -return "chain(...)"; -},toString:m.forwardCall("repr"),next:function(){ -while(_198.length>1){ -try{ -var _199=_198[0].next(); -return _199; -} -catch(e){ -if(e!=self.StopIteration){ -throw e; -} -_198.shift(); -var _199=_198[0].next(); -return _199; -} -} -if(_198.length==1){ -var arg=_198.shift(); -this.next=m.bind("next",arg); -return this.next(); -} -throw self.StopIteration; -}}; -},takewhile:function(pred,seq){ -var self=MochiKit.Iter; -seq=self.iter(seq); -return {repr:function(){ -return "takewhile(...)"; -},toString:MochiKit.Base.forwardCall("repr"),next:function(){ -var rval=seq.next(); -if(!pred(rval)){ -this.next=function(){ -throw self.StopIteration; -}; -this.next(); -} -return rval; -}}; -},dropwhile:function(pred,seq){ -seq=MochiKit.Iter.iter(seq); -var m=MochiKit.Base; -var bind=m.bind; -return {"repr":function(){ -return "dropwhile(...)"; -},"toString":m.forwardCall("repr"),"next":function(){ -while(true){ -var rval=seq.next(); -if(!pred(rval)){ -break; -} -} -this.next=bind("next",seq); -return rval; -}}; -},_tee:function(_1a4,sync,_1a6){ -sync.pos[_1a4]=-1; -var m=MochiKit.Base; -var _1a8=m.listMin; -return {repr:function(){ -return "tee("+_1a4+", ...)"; -},toString:m.forwardCall("repr"),next:function(){ -var rval; -var i=sync.pos[_1a4]; -if(i==sync.max){ -rval=_1a6.next(); -sync.deque.push(rval); -sync.max+=1; -sync.pos[_1a4]+=1; -}else{ -rval=sync.deque[i-sync.min]; -sync.pos[_1a4]+=1; -if(i==sync.min&&_1a8(sync.pos)!=sync.min){ -sync.min+=1; -sync.deque.shift(); -} -} -return rval; -}}; -},tee:function(_1ab,n){ -var rval=[]; -var sync={"pos":[],"deque":[],"max":-1,"min":-1}; -if(arguments.length==1||typeof (n)=="undefined"||n===null){ -n=2; -} -var self=MochiKit.Iter; -_1ab=self.iter(_1ab); -var _tee=self._tee; -for(var i=0;i0&&_1bd>=stop)||(step<0&&_1bd<=stop)){ -throw MochiKit.Iter.StopIteration; -} -var rval=_1bd; -_1bd+=step; -return rval; -},repr:function(){ -return "range("+[_1bd,stop,step].join(", ")+")"; -},toString:MochiKit.Base.forwardCall("repr")}; -},sum:function(_1c1,_1c2){ -if(typeof (_1c2)=="undefined"||_1c2===null){ -_1c2=0; -} -var x=_1c2; -var self=MochiKit.Iter; -_1c1=self.iter(_1c1); -try{ -while(true){ -x+=_1c1.next(); -} -} -catch(e){ -if(e!=self.StopIteration){ -throw e; -} -} -return x; -},exhaust:function(_1c5){ -var self=MochiKit.Iter; -_1c5=self.iter(_1c5); -try{ -while(true){ -_1c5.next(); -} -} -catch(e){ -if(e!=self.StopIteration){ -throw e; -} -} -},forEach:function(_1c7,func,obj){ -var m=MochiKit.Base; -var self=MochiKit.Iter; -if(arguments.length>2){ -func=m.bind(func,obj); -} -if(m.isArrayLike(_1c7)&&!self.isIterable(_1c7)){ -try{ -for(var i=0;i<_1c7.length;i++){ -func(_1c7[i]); -} -} -catch(e){ -if(e!=self.StopIteration){ -throw e; -} -} -}else{ -self.exhaust(self.imap(func,_1c7)); -} -},every:function(_1cd,func){ -var self=MochiKit.Iter; -try{ -self.ifilterfalse(func,_1cd).next(); -return false; -} -catch(e){ -if(e!=self.StopIteration){ -throw e; -} -return true; -} -},sorted:function(_1d0,cmp){ -var rval=MochiKit.Iter.list(_1d0); -if(arguments.length==1){ -cmp=MochiKit.Base.compare; -} -rval.sort(cmp); -return rval; -},reversed:function(_1d3){ -var rval=MochiKit.Iter.list(_1d3); -rval.reverse(); -return rval; -},some:function(_1d5,func){ -var self=MochiKit.Iter; -try{ -self.ifilter(func,_1d5).next(); -return true; -} -catch(e){ -if(e!=self.StopIteration){ -throw e; -} -return false; -} -},iextend:function(lst,_1d9){ -var m=MochiKit.Base; -var self=MochiKit.Iter; -if(m.isArrayLike(_1d9)&&!self.isIterable(_1d9)){ -for(var i=0;i<_1d9.length;i++){ -lst.push(_1d9[i]); -} -}else{ -_1d9=self.iter(_1d9); -try{ -while(true){ -lst.push(_1d9.next()); -} -} -catch(e){ -if(e!=self.StopIteration){ -throw e; -} -} -} -return lst; -},groupby:function(_1dd,_1de){ -var m=MochiKit.Base; -var self=MochiKit.Iter; -if(arguments.length<2){ -_1de=m.operator.identity; -} -_1dd=self.iter(_1dd); -var pk=undefined; -var k=undefined; -var v; -function fetch(){ -v=_1dd.next(); -k=_1de(v); -} -function eat(){ -var ret=v; -v=undefined; -return ret; -} -var _1e5=true; -var _1e6=m.compare; -return {repr:function(){ -return "groupby(...)"; -},next:function(){ -while(_1e6(k,pk)===0){ -fetch(); -if(_1e5){ -_1e5=false; -break; -} -} -pk=k; -return [k,{next:function(){ -if(v==undefined){ -fetch(); -} -if(_1e6(k,pk)!==0){ -throw self.StopIteration; -} -return eat(); -}}]; -}}; -},groupby_as_array:function(_1e7,_1e8){ -var m=MochiKit.Base; -var self=MochiKit.Iter; -if(arguments.length<2){ -_1e8=m.operator.identity; -} -_1e7=self.iter(_1e7); -var _1eb=[]; -var _1ec=true; -var _1ed; -var _1ee=m.compare; -while(true){ -try{ -var _1ef=_1e7.next(); -var key=_1e8(_1ef); -} -catch(e){ -if(e==self.StopIteration){ -break; -} -throw e; -} -if(_1ec||_1ee(key,_1ed)!==0){ -var _1f1=[]; -_1eb.push([key,_1f1]); -} -_1f1.push(_1ef); -_1ec=false; -_1ed=key; -} -return _1eb; -},arrayLikeIter:function(_1f2){ -var i=0; -return {repr:function(){ -return "arrayLikeIter(...)"; -},toString:MochiKit.Base.forwardCall("repr"),next:function(){ -if(i>=_1f2.length){ -throw MochiKit.Iter.StopIteration; -} -return _1f2[i++]; -}}; -},hasIterateNext:function(_1f4){ -return (_1f4&&typeof (_1f4.iterateNext)=="function"); -},iterateNextIter:function(_1f5){ -return {repr:function(){ -return "iterateNextIter(...)"; -},toString:MochiKit.Base.forwardCall("repr"),next:function(){ -var rval=_1f5.iterateNext(); -if(rval===null||rval===undefined){ -throw MochiKit.Iter.StopIteration; -} -return rval; -}}; -}}); -MochiKit.Iter.EXPORT_OK=["iteratorRegistry","arrayLikeIter","hasIterateNext","iterateNextIter"]; -MochiKit.Iter.EXPORT=["StopIteration","registerIteratorFactory","iter","count","cycle","repeat","next","izip","ifilter","ifilterfalse","islice","imap","applymap","chain","takewhile","dropwhile","tee","list","reduce","range","sum","exhaust","forEach","every","sorted","reversed","some","iextend","groupby","groupby_as_array"]; -MochiKit.Iter.__new__=function(){ -var m=MochiKit.Base; -if(typeof (StopIteration)!="undefined"){ -this.StopIteration=StopIteration; -}else{ -this.StopIteration=new m.NamedError("StopIteration"); -} -this.iteratorRegistry=new m.AdapterRegistry(); -this.registerIteratorFactory("arrayLike",m.isArrayLike,this.arrayLikeIter); -this.registerIteratorFactory("iterateNext",this.hasIterateNext,this.iterateNextIter); -this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; -m.nameFunctions(this); -}; -MochiKit.Iter.__new__(); -if(MochiKit.__export__){ -reduce=MochiKit.Iter.reduce; -} -MochiKit.Base._exportSymbols(this,MochiKit.Iter); -MochiKit.Base._deps("Logging",["Base"]); -MochiKit.Logging.NAME="MochiKit.Logging"; -MochiKit.Logging.VERSION="1.4.2"; -MochiKit.Logging.__repr__=function(){ -return "["+this.NAME+" "+this.VERSION+"]"; -}; -MochiKit.Logging.toString=function(){ -return this.__repr__(); -}; -MochiKit.Logging.EXPORT=["LogLevel","LogMessage","Logger","alertListener","logger","log","logError","logDebug","logFatal","logWarning"]; -MochiKit.Logging.EXPORT_OK=["logLevelAtLeast","isLogMessage","compareLogMessage"]; -MochiKit.Logging.LogMessage=function(num,_1f9,info){ -this.num=num; -this.level=_1f9; -this.info=info; -this.timestamp=new Date(); -}; -MochiKit.Logging.LogMessage.prototype={repr:function(){ -var m=MochiKit.Base; -return "LogMessage("+m.map(m.repr,[this.num,this.level,this.info]).join(", ")+")"; -},toString:MochiKit.Base.forwardCall("repr")}; -MochiKit.Base.update(MochiKit.Logging,{logLevelAtLeast:function(_1fc){ -var self=MochiKit.Logging; -if(typeof (_1fc)=="string"){ -_1fc=self.LogLevel[_1fc]; -} -return function(msg){ -var _1ff=msg.level; -if(typeof (_1ff)=="string"){ -_1ff=self.LogLevel[_1ff]; -} -return _1ff>=_1fc; -}; -},isLogMessage:function(){ -var _200=MochiKit.Logging.LogMessage; -for(var i=0;i=MochiKit.Logging.LogLevel.FATAL){ -_20f="FATAL"; -}else{ -if(_20f>=MochiKit.Logging.LogLevel.ERROR){ -_20f="ERROR"; -}else{ -if(_20f>=MochiKit.Logging.LogLevel.WARNING){ -_20f="WARNING"; -}else{ -if(_20f>=MochiKit.Logging.LogLevel.INFO){ -_20f="INFO"; -}else{ -_20f="DEBUG"; -} -} -} -} -} -var msg=new MochiKit.Logging.LogMessage(this.counter,_20f,MochiKit.Base.extend(null,arguments,1)); -this._messages.push(msg); -this.dispatchListeners(msg); -if(this.useNativeConsole){ -this.logToConsole(msg.level+": "+msg.info.join(" ")); -} -this.counter+=1; -while(this.maxSize>=0&&this._messages.length>this.maxSize){ -this._messages.shift(); -} -},getMessages:function(_212){ -var _213=0; -if(!(typeof (_212)=="undefined"||_212===null)){ -_213=Math.max(0,this._messages.length-_212); -} -return this._messages.slice(_213); -},getMessageText:function(_214){ -if(typeof (_214)=="undefined"||_214===null){ -_214=30; -} -var _215=this.getMessages(_214); -if(_215.length){ -var lst=map(function(m){ -return "\n ["+m.num+"] "+m.level+": "+m.info.join(" "); -},_215); -lst.unshift("LAST "+_215.length+" MESSAGES:"); -return lst.join(""); -} -return ""; -},debuggingBookmarklet:function(_218){ -if(typeof (MochiKit.LoggingPane)=="undefined"){ -alert(this.getMessageText()); -}else{ -MochiKit.LoggingPane.createLoggingPane(_218||false); -} -}}; -MochiKit.Logging.__new__=function(){ -this.LogLevel={ERROR:40,FATAL:50,WARNING:30,INFO:20,DEBUG:10}; -var m=MochiKit.Base; -m.registerComparator("LogMessage",this.isLogMessage,this.compareLogMessage); -var _21a=m.partial; -var _21b=this.Logger; -var _21c=_21b.prototype.baseLog; -m.update(this.Logger.prototype,{debug:_21a(_21c,"DEBUG"),log:_21a(_21c,"INFO"),error:_21a(_21c,"ERROR"),fatal:_21a(_21c,"FATAL"),warning:_21a(_21c,"WARNING")}); -var self=this; -var _21e=function(name){ -return function(){ -self.logger[name].apply(self.logger,arguments); -}; -}; -this.log=_21e("log"); -this.logError=_21e("error"); -this.logDebug=_21e("debug"); -this.logFatal=_21e("fatal"); -this.logWarning=_21e("warning"); -this.logger=new _21b(); -this.logger.useNativeConsole=true; -this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; -m.nameFunctions(this); -}; -if(typeof (printfire)=="undefined"&&typeof (document)!="undefined"&&document.createEvent&&typeof (dispatchEvent)!="undefined"){ -printfire=function(){ -printfire.args=arguments; -var ev=document.createEvent("Events"); -ev.initEvent("printfire",false,true); -dispatchEvent(ev); -}; -} -MochiKit.Logging.__new__(); -MochiKit.Base._exportSymbols(this,MochiKit.Logging); -MochiKit.Base._deps("DateTime",["Base"]); -MochiKit.DateTime.NAME="MochiKit.DateTime"; -MochiKit.DateTime.VERSION="1.4.2"; -MochiKit.DateTime.__repr__=function(){ -return "["+this.NAME+" "+this.VERSION+"]"; -}; -MochiKit.DateTime.toString=function(){ -return this.__repr__(); -}; -MochiKit.DateTime.isoDate=function(str){ -str=str+""; -if(typeof (str)!="string"||str.length===0){ -return null; -} -var iso=str.split("-"); -if(iso.length===0){ -return null; -} -var date=new Date(iso[0],iso[1]-1,iso[2]); -date.setFullYear(iso[0]); -date.setMonth(iso[1]-1); -date.setDate(iso[2]); -return date; -}; -MochiKit.DateTime._isoRegexp=/(\d{4,})(?:-(\d{1,2})(?:-(\d{1,2})(?:[T ](\d{1,2}):(\d{1,2})(?::(\d{1,2})(?:\.(\d+))?)?(?:(Z)|([+-])(\d{1,2})(?::(\d{1,2}))?)?)?)?)?/; -MochiKit.DateTime.isoTimestamp=function(str){ -str=str+""; -if(typeof (str)!="string"||str.length===0){ -return null; -} -var res=str.match(MochiKit.DateTime._isoRegexp); -if(typeof (res)=="undefined"||res===null){ -return null; -} -var year,_227,day,hour,min,sec,msec; -year=parseInt(res[1],10); -if(typeof (res[2])=="undefined"||res[2]===""){ -return new Date(year); -} -_227=parseInt(res[2],10)-1; -day=parseInt(res[3],10); -if(typeof (res[4])=="undefined"||res[4]===""){ -return new Date(year,_227,day); -} -hour=parseInt(res[4],10); -min=parseInt(res[5],10); -sec=(typeof (res[6])!="undefined"&&res[6]!=="")?parseInt(res[6],10):0; -if(typeof (res[7])!="undefined"&&res[7]!==""){ -msec=Math.round(1000*parseFloat("0."+res[7])); -}else{ -msec=0; -} -if((typeof (res[8])=="undefined"||res[8]==="")&&(typeof (res[9])=="undefined"||res[9]==="")){ -return new Date(year,_227,day,hour,min,sec,msec); -} -var ofs; -if(typeof (res[9])!="undefined"&&res[9]!==""){ -ofs=parseInt(res[10],10)*3600000; -if(typeof (res[11])!="undefined"&&res[11]!==""){ -ofs+=parseInt(res[11],10)*60000; -} -if(res[9]=="-"){ -ofs=-ofs; -} -}else{ -ofs=0; -} -return new Date(Date.UTC(year,_227,day,hour,min,sec,msec)-ofs); -}; -MochiKit.DateTime.toISOTime=function(date,_22f){ -if(typeof (date)=="undefined"||date===null){ -return null; -} -var hh=date.getHours(); -var mm=date.getMinutes(); -var ss=date.getSeconds(); -var lst=[((_22f&&(hh<10))?"0"+hh:hh),((mm<10)?"0"+mm:mm),((ss<10)?"0"+ss:ss)]; -return lst.join(":"); -}; -MochiKit.DateTime.toISOTimestamp=function(date,_235){ -if(typeof (date)=="undefined"||date===null){ -return null; -} -var sep=_235?"T":" "; -var foot=_235?"Z":""; -if(_235){ -date=new Date(date.getTime()+(date.getTimezoneOffset()*60000)); -} -return MochiKit.DateTime.toISODate(date)+sep+MochiKit.DateTime.toISOTime(date,_235)+foot; -}; -MochiKit.DateTime.toISODate=function(date){ -if(typeof (date)=="undefined"||date===null){ -return null; -} -var _239=MochiKit.DateTime._padTwo; -var _23a=MochiKit.DateTime._padFour; -return [_23a(date.getFullYear()),_239(date.getMonth()+1),_239(date.getDate())].join("-"); -}; -MochiKit.DateTime.americanDate=function(d){ -d=d+""; -if(typeof (d)!="string"||d.length===0){ -return null; -} -var a=d.split("/"); -return new Date(a[2],a[0]-1,a[1]); -}; -MochiKit.DateTime._padTwo=function(n){ -return (n>9)?n:"0"+n; -}; -MochiKit.DateTime._padFour=function(n){ -switch(n.toString().length){ -case 1: -return "000"+n; -break; -case 2: -return "00"+n; -break; -case 3: -return "0"+n; -break; -case 4: -default: -return n; -} -}; -MochiKit.DateTime.toPaddedAmericanDate=function(d){ -if(typeof (d)=="undefined"||d===null){ -return null; -} -var _240=MochiKit.DateTime._padTwo; -return [_240(d.getMonth()+1),_240(d.getDate()),d.getFullYear()].join("/"); -}; -MochiKit.DateTime.toAmericanDate=function(d){ -if(typeof (d)=="undefined"||d===null){ -return null; -} -return [d.getMonth()+1,d.getDate(),d.getFullYear()].join("/"); -}; -MochiKit.DateTime.EXPORT=["isoDate","isoTimestamp","toISOTime","toISOTimestamp","toISODate","americanDate","toPaddedAmericanDate","toAmericanDate"]; -MochiKit.DateTime.EXPORT_OK=[]; -MochiKit.DateTime.EXPORT_TAGS={":common":MochiKit.DateTime.EXPORT,":all":MochiKit.DateTime.EXPORT}; -MochiKit.DateTime.__new__=function(){ -var base=this.NAME+"."; -for(var k in this){ -var o=this[k]; -if(typeof (o)=="function"&&typeof (o.NAME)=="undefined"){ -try{ -o.NAME=base+k; -} -catch(e){ -} -} -} -}; -MochiKit.DateTime.__new__(); -if(typeof (MochiKit.Base)!="undefined"){ -MochiKit.Base._exportSymbols(this,MochiKit.DateTime); -}else{ -(function(_245,_246){ -if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(MochiKit.__export__===false)){ -var all=_246.EXPORT_TAGS[":all"]; -for(var i=0;i_250){ -var i=_258.length-_250; -res=fmt.separator+_258.substring(i,_258.length)+res; -_258=_258.substring(0,i); -} -} -res=_258+res; -if(_24e>0){ -while(frac.length<_251){ -frac=frac+"0"; -} -res=res+fmt.decimal+frac; -} -return _253+res+_254; -}; -}; -MochiKit.Format.numberFormatter=function(_25c,_25d,_25e){ -if(typeof (_25d)=="undefined"){ -_25d=""; -} -var _25f=_25c.match(/((?:[0#]+,)?[0#]+)(?:\.([0#]+))?(%)?/); -if(!_25f){ -throw TypeError("Invalid pattern"); -} -var _260=_25c.substr(0,_25f.index); -var _261=_25c.substr(_25f.index+_25f[0].length); -if(_260.search(/-/)==-1){ -_260=_260+"-"; -} -var _262=_25f[1]; -var frac=(typeof (_25f[2])=="string"&&_25f[2]!="")?_25f[2]:""; -var _264=(typeof (_25f[3])=="string"&&_25f[3]!=""); -var tmp=_262.split(/,/); -var _266; -if(typeof (_25e)=="undefined"){ -_25e="default"; -} -if(tmp.length==1){ -_266=null; -}else{ -_266=tmp[1].length; -} -var _267=_262.length-_262.replace(/0/g,"").length; -var _268=frac.length-frac.replace(/0/g,"").length; -var _269=frac.length; -var rval=MochiKit.Format._numberFormatter(_25d,_260,_261,_25e,_264,_269,_267,_266,_268); -var m=MochiKit.Base; -if(m){ -var fn=arguments.callee; -var args=m.concat(arguments); -rval.repr=function(){ -return [self.NAME,"(",map(m.repr,args).join(", "),")"].join(""); -}; -} -return rval; -}; -MochiKit.Format.formatLocale=function(_26e){ -if(typeof (_26e)=="undefined"||_26e===null){ -_26e="default"; -} -if(typeof (_26e)=="string"){ -var rval=MochiKit.Format.LOCALE[_26e]; -if(typeof (rval)=="string"){ -rval=arguments.callee(rval); -MochiKit.Format.LOCALE[_26e]=rval; -} -return rval; -}else{ -return _26e; -} -}; -MochiKit.Format.twoDigitAverage=function(_270,_271){ -if(_271){ -var res=_270/_271; -if(!isNaN(res)){ -return MochiKit.Format.twoDigitFloat(res); -} -} -return "0"; -}; -MochiKit.Format.twoDigitFloat=function(_273){ -var res=roundToFixed(_273,2); -if(res.indexOf(".00")>0){ -return res.substring(0,res.length-3); -}else{ -if(res.charAt(res.length-1)=="0"){ -return res.substring(0,res.length-1); -}else{ -return res; -} -} -}; -MochiKit.Format.lstrip=function(str,_276){ -str=str+""; -if(typeof (str)!="string"){ -return null; -} -if(!_276){ -return str.replace(/^\s+/,""); -}else{ -return str.replace(new RegExp("^["+_276+"]+"),""); -} -}; -MochiKit.Format.rstrip=function(str,_278){ -str=str+""; -if(typeof (str)!="string"){ -return null; -} -if(!_278){ -return str.replace(/\s+$/,""); -}else{ -return str.replace(new RegExp("["+_278+"]+$"),""); -} -}; -MochiKit.Format.strip=function(str,_27a){ -var self=MochiKit.Format; -return self.rstrip(self.lstrip(str,_27a),_27a); -}; -MochiKit.Format.truncToFixed=function(_27c,_27d){ -var res=Math.floor(_27c).toFixed(0); -if(_27c<0){ -res=Math.ceil(_27c).toFixed(0); -if(res.charAt(0)!="-"&&_27d>0){ -res="-"+res; -} -} -if(res.indexOf("e")<0&&_27d>0){ -var tail=_27c.toString(); -if(tail.indexOf("e")>0){ -tail="."; -}else{ -if(tail.indexOf(".")<0){ -tail="."; -}else{ -tail=tail.substring(tail.indexOf(".")); -} -} -if(tail.length-1>_27d){ -tail=tail.substring(0,_27d+1); -} -while(tail.length-1<_27d){ -tail+="0"; -} -res+=tail; -} -return res; -}; -MochiKit.Format.roundToFixed=function(_280,_281){ -var _282=Math.abs(_280)+0.5*Math.pow(10,-_281); -var res=MochiKit.Format.truncToFixed(_282,_281); -if(_280<0){ -res="-"+res; -} -return res; -}; -MochiKit.Format.percentFormat=function(_284){ -return MochiKit.Format.twoDigitFloat(100*_284)+"%"; -}; -MochiKit.Format.EXPORT=["truncToFixed","roundToFixed","numberFormatter","formatLocale","twoDigitAverage","twoDigitFloat","percentFormat","lstrip","rstrip","strip"]; -MochiKit.Format.LOCALE={en_US:{separator:",",decimal:".",percent:"%"},de_DE:{separator:".",decimal:",",percent:"%"},pt_BR:{separator:".",decimal:",",percent:"%"},fr_FR:{separator:" ",decimal:",",percent:"%"},"default":"en_US"}; -MochiKit.Format.EXPORT_OK=[]; -MochiKit.Format.EXPORT_TAGS={":all":MochiKit.Format.EXPORT,":common":MochiKit.Format.EXPORT}; -MochiKit.Format.__new__=function(){ -var base=this.NAME+"."; -var k,v,o; -for(k in this.LOCALE){ -o=this.LOCALE[k]; -if(typeof (o)=="object"){ -o.repr=function(){ -return this.NAME; -}; -o.NAME=base+"LOCALE."+k; -} -} -for(k in this){ -o=this[k]; -if(typeof (o)=="function"&&typeof (o.NAME)=="undefined"){ -try{ -o.NAME=base+k; -} -catch(e){ -} -} -} -}; -MochiKit.Format.__new__(); -if(typeof (MochiKit.Base)!="undefined"){ -MochiKit.Base._exportSymbols(this,MochiKit.Format); -}else{ -(function(_289,_28a){ -if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(MochiKit.__export__===false)){ -var all=_28a.EXPORT_TAGS[":all"]; -for(var i=0;i1){ -fn=MochiKit.Base.partial.apply(null,arguments); -} -return this.addCallbacks(fn,fn); -},addCallback:function(fn){ -if(arguments.length>1){ -fn=MochiKit.Base.partial.apply(null,arguments); -} -return this.addCallbacks(fn,null); -},addErrback:function(fn){ -if(arguments.length>1){ -fn=MochiKit.Base.partial.apply(null,arguments); -} -return this.addCallbacks(null,fn); -},addCallbacks:function(cb,eb){ -if(this.chained){ -throw new Error("Chained Deferreds can not be re-used"); -} -this.chain.push([cb,eb]); -if(this.fired>=0){ -this._fire(); -} -return this; -},_fire:function(){ -var _299=this.chain; -var _29a=this.fired; -var res=this.results[_29a]; -var self=this; -var cb=null; -while(_299.length>0&&this.paused===0){ -var pair=_299.shift(); -var f=pair[_29a]; -if(f===null){ -continue; -} -try{ -res=f(res); -_29a=((res instanceof Error)?1:0); -if(res instanceof MochiKit.Async.Deferred){ -cb=function(res){ -self._resback(res); -self.paused--; -if((self.paused===0)&&(self.fired>=0)){ -self._fire(); -} -}; -this.paused++; -} -} -catch(err){ -_29a=1; -if(!(err instanceof Error)){ -err=new MochiKit.Async.GenericError(err); -} -res=err; -} -} -this.fired=_29a; -this.results[_29a]=res; -if(cb&&this.paused){ -res.addBoth(cb); -res.chained=true; -} -}}; -MochiKit.Base.update(MochiKit.Async,{evalJSONRequest:function(req){ -return MochiKit.Base.evalJSON(req.responseText); -},succeed:function(_2a2){ -var d=new MochiKit.Async.Deferred(); -d.callback.apply(d,arguments); -return d; -},fail:function(_2a4){ -var d=new MochiKit.Async.Deferred(); -d.errback.apply(d,arguments); -return d; -},getXMLHttpRequest:function(){ -var self=arguments.callee; -if(!self.XMLHttpRequest){ -var _2a7=[function(){ -return new XMLHttpRequest(); -},function(){ -return new ActiveXObject("Msxml2.XMLHTTP"); -},function(){ -return new ActiveXObject("Microsoft.XMLHTTP"); -},function(){ -return new ActiveXObject("Msxml2.XMLHTTP.4.0"); -},function(){ -throw new MochiKit.Async.BrowserComplianceError("Browser does not support XMLHttpRequest"); -}]; -for(var i=0;i<_2a7.length;i++){ -var func=_2a7[i]; -try{ -self.XMLHttpRequest=func; -return func(); -} -catch(e){ -} -} -} -return self.XMLHttpRequest(); -},_xhr_onreadystatechange:function(d){ -var m=MochiKit.Base; -if(this.readyState==4){ -try{ -this.onreadystatechange=null; -} -catch(e){ -try{ -this.onreadystatechange=m.noop; -} -catch(e){ -} -} -var _2ac=null; -try{ -_2ac=this.status; -if(!_2ac&&m.isNotEmpty(this.responseText)){ -_2ac=304; -} -} -catch(e){ -} -if(_2ac==200||_2ac==201||_2ac==204||_2ac==304||_2ac==1223){ -d.callback(this); -}else{ -var err=new MochiKit.Async.XMLHttpRequestError(this,"Request failed"); -if(err.number){ -d.errback(err); -}else{ -d.errback(err); -} -} -} -},_xhr_canceller:function(req){ -try{ -req.onreadystatechange=null; -} -catch(e){ -try{ -req.onreadystatechange=MochiKit.Base.noop; -} -catch(e){ -} -} -req.abort(); -},sendXMLHttpRequest:function(req,_2b0){ -if(typeof (_2b0)=="undefined"||_2b0===null){ -_2b0=""; -} -var m=MochiKit.Base; -var self=MochiKit.Async; -var d=new self.Deferred(m.partial(self._xhr_canceller,req)); -try{ -req.onreadystatechange=m.bind(self._xhr_onreadystatechange,req,d); -req.send(_2b0); -} -catch(e){ -try{ -req.onreadystatechange=null; -} -catch(ignore){ -} -d.errback(e); -} -return d; -},doXHR:function(url,opts){ -var self=MochiKit.Async; -return self.callLater(0,self._doXHR,url,opts); -},_doXHR:function(url,opts){ -var m=MochiKit.Base; -opts=m.update({method:"GET",sendContent:""},opts); -var self=MochiKit.Async; -var req=self.getXMLHttpRequest(); -if(opts.queryString){ -var qs=m.queryString(opts.queryString); -if(qs){ -url+="?"+qs; -} -} -if("username" in opts){ -req.open(opts.method,url,true,opts.username,opts.password); -}else{ -req.open(opts.method,url,true); -} -if(req.overrideMimeType&&opts.mimeType){ -req.overrideMimeType(opts.mimeType); -} -req.setRequestHeader("X-Requested-With","XMLHttpRequest"); -if(opts.headers){ -var _2bd=opts.headers; -if(!m.isArrayLike(_2bd)){ -_2bd=m.items(_2bd); -} -for(var i=0;i<_2bd.length;i++){ -var _2bf=_2bd[i]; -var name=_2bf[0]; -var _2c1=_2bf[1]; -req.setRequestHeader(name,_2c1); -} -} -return self.sendXMLHttpRequest(req,opts.sendContent); -},_buildURL:function(url){ -if(arguments.length>1){ -var m=MochiKit.Base; -var qs=m.queryString.apply(null,m.extend(null,arguments,1)); -if(qs){ -return url+"?"+qs; -} -} -return url; -},doSimpleXMLHttpRequest:function(url){ -var self=MochiKit.Async; -url=self._buildURL.apply(self,arguments); -return self.doXHR(url); -},loadJSONDoc:function(url){ -var self=MochiKit.Async; -url=self._buildURL.apply(self,arguments); -var d=self.doXHR(url,{"mimeType":"text/plain","headers":[["Accept","application/json"]]}); -d=d.addCallback(self.evalJSONRequest); -return d; -},wait:function(_2ca,_2cb){ -var d=new MochiKit.Async.Deferred(); -var m=MochiKit.Base; -if(typeof (_2cb)!="undefined"){ -d.addCallback(function(){ -return _2cb; -}); -} -var _2ce=setTimeout(m.bind("callback",d),Math.floor(_2ca*1000)); -d.canceller=function(){ -try{ -clearTimeout(_2ce); -} -catch(e){ -} -}; -return d; -},callLater:function(_2cf,func){ -var m=MochiKit.Base; -var _2d2=m.partial.apply(m,m.extend(null,arguments,1)); -return MochiKit.Async.wait(_2cf).addCallback(function(res){ -return _2d2(); -}); -}}); -MochiKit.Async.DeferredLock=function(){ -this.waiting=[]; -this.locked=false; -this.id=this._nextId(); -}; -MochiKit.Async.DeferredLock.prototype={__class__:MochiKit.Async.DeferredLock,acquire:function(){ -var d=new MochiKit.Async.Deferred(); -if(this.locked){ -this.waiting.push(d); -}else{ -this.locked=true; -d.callback(this); -} -return d; -},release:function(){ -if(!this.locked){ -throw TypeError("Tried to release an unlocked DeferredLock"); -} -this.locked=false; -if(this.waiting.length>0){ -this.locked=true; -this.waiting.shift().callback(this); -} -},_nextId:MochiKit.Base.counter(),repr:function(){ -var _2d5; -if(this.locked){ -_2d5="locked, "+this.waiting.length+" waiting"; -}else{ -_2d5="unlocked"; -} -return "DeferredLock("+this.id+", "+_2d5+")"; -},toString:MochiKit.Base.forwardCall("repr")}; -MochiKit.Async.DeferredList=function(list,_2d7,_2d8,_2d9,_2da){ -MochiKit.Async.Deferred.apply(this,[_2da]); -this.list=list; -var _2db=[]; -this.resultList=_2db; -this.finishedCount=0; -this.fireOnOneCallback=_2d7; -this.fireOnOneErrback=_2d8; -this.consumeErrors=_2d9; -var cb=MochiKit.Base.bind(this._cbDeferred,this); -for(var i=0;i=0){ -var opt=elem.options[elem.selectedIndex]; -var v=opt.value; -if(!v){ -var h=opt.outerHTML; -if(h&&!h.match(/^[^>]+\svalue\s*=/i)){ -v=opt.text; -} -} -_2fa.push(name); -_2fb.push(v); -return null; -} -_2fa.push(name); -_2fb.push(""); -return null; -}else{ -var opts=elem.options; -if(!opts.length){ -_2fa.push(name); -_2fb.push(""); -return null; -} -for(var i=0;i]+\svalue\s*=/i)){ -v=opt.text; -} -} -_2fa.push(name); -_2fb.push(v); -} -return null; -} -} -if(_300==="FORM"||_300==="P"||_300==="SPAN"||_300==="DIV"){ -return elem.childNodes; -} -_2fa.push(name); -_2fb.push(elem.value||""); -return null; -} -return elem.childNodes; -}); -return [_2fa,_2fb]; -},withDocument:function(doc,func){ -var self=MochiKit.DOM; -var _309=self._document; -var rval; -try{ -self._document=doc; -rval=func(); -} -catch(e){ -self._document=_309; -throw e; -} -self._document=_309; -return rval; -},registerDOMConverter:function(name,_30c,wrap,_30e){ -MochiKit.DOM.domConverters.register(name,_30c,wrap,_30e); -},coerceToDOM:function(node,ctx){ -var m=MochiKit.Base; -var im=MochiKit.Iter; -var self=MochiKit.DOM; -if(im){ -var iter=im.iter; -var _315=im.repeat; -} -var map=m.map; -var _317=self.domConverters; -var _318=arguments.callee; -var _319=m.NotFound; -while(true){ -if(typeof (node)=="undefined"||node===null){ -return null; -} -if(typeof (node)=="function"&&typeof (node.length)=="number"&&!(node instanceof Function)){ -node=im?im.list(node):m.extend(null,node); -} -if(typeof (node.nodeType)!="undefined"&&node.nodeType>0){ -return node; -} -if(typeof (node)=="number"||typeof (node)=="boolean"){ -node=node.toString(); -} -if(typeof (node)=="string"){ -return self._document.createTextNode(node); -} -if(typeof (node.__dom__)=="function"){ -node=node.__dom__(ctx); -continue; -} -if(typeof (node.dom)=="function"){ -node=node.dom(ctx); -continue; -} -if(typeof (node)=="function"){ -node=node.apply(ctx,[ctx]); -continue; -} -if(im){ -var _31a=null; -try{ -_31a=iter(node); -} -catch(e){ -} -if(_31a){ -return map(_318,_31a,_315(ctx)); -} -}else{ -if(m.isArrayLike(node)){ -var func=function(n){ -return _318(n,ctx); -}; -return map(func,node); -} -} -try{ -node=_317.match(node,ctx); -continue; -} -catch(e){ -if(e!=_319){ -throw e; -} -} -return self._document.createTextNode(node.toString()); -} -return undefined; -},isChildNode:function(node,_31e){ -var self=MochiKit.DOM; -if(typeof (node)=="string"){ -node=self.getElement(node); -} -if(typeof (_31e)=="string"){ -_31e=self.getElement(_31e); -} -if(typeof (node)=="undefined"||node===null){ -return false; -} -while(node!=null&&node!==self._document){ -if(node===_31e){ -return true; -} -node=node.parentNode; -} -return false; -},setNodeAttribute:function(node,attr,_322){ -var o={}; -o[attr]=_322; -try{ -return MochiKit.DOM.updateNodeAttributes(node,o); -} -catch(e){ -} -return null; -},getNodeAttribute:function(node,attr){ -var self=MochiKit.DOM; -var _327=self.attributeArray.renames[attr]; -var _328=self.attributeArray.ignoreAttr[attr]; -node=self.getElement(node); -try{ -if(_327){ -return node[_327]; -} -var _329=node.getAttribute(attr); -if(_329!=_328){ -return _329; -} -} -catch(e){ -} -return null; -},removeNodeAttribute:function(node,attr){ -var self=MochiKit.DOM; -var _32d=self.attributeArray.renames[attr]; -node=self.getElement(node); -try{ -if(_32d){ -return node[_32d]; -} -return node.removeAttribute(attr); -} -catch(e){ -} -return null; -},updateNodeAttributes:function(node,_32f){ -var elem=node; -var self=MochiKit.DOM; -if(typeof (node)=="string"){ -elem=self.getElement(node); -} -if(_32f){ -var _332=MochiKit.Base.updatetree; -if(self.attributeArray.compliant){ -for(var k in _32f){ -var v=_32f[k]; -if(typeof (v)=="object"&&typeof (elem[k])=="object"){ -if(k=="style"&&MochiKit.Style){ -MochiKit.Style.setStyle(elem,v); -}else{ -_332(elem[k],v); -} -}else{ -if(k.substring(0,2)=="on"){ -if(typeof (v)=="string"){ -v=new Function(v); -} -elem[k]=v; -}else{ -elem.setAttribute(k,v); -} -} -if(typeof (elem[k])=="string"&&elem[k]!=v){ -elem[k]=v; -} -} -}else{ -var _335=self.attributeArray.renames; -for(var k in _32f){ -v=_32f[k]; -var _336=_335[k]; -if(k=="style"&&typeof (v)=="string"){ -elem.style.cssText=v; -}else{ -if(typeof (_336)=="string"){ -elem[_336]=v; -}else{ -if(typeof (elem[k])=="object"&&typeof (v)=="object"){ -if(k=="style"&&MochiKit.Style){ -MochiKit.Style.setStyle(elem,v); -}else{ -_332(elem[k],v); -} -}else{ -if(k.substring(0,2)=="on"){ -if(typeof (v)=="string"){ -v=new Function(v); -} -elem[k]=v; -}else{ -elem.setAttribute(k,v); -} -} -} -} -if(typeof (elem[k])=="string"&&elem[k]!=v){ -elem[k]=v; -} -} -} -} -return elem; -},appendChildNodes:function(node){ -var elem=node; -var self=MochiKit.DOM; -if(typeof (node)=="string"){ -elem=self.getElement(node); -} -var _33a=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; -var _33b=MochiKit.Base.concat; -while(_33a.length){ -var n=_33a.shift(); -if(typeof (n)=="undefined"||n===null){ -}else{ -if(typeof (n.nodeType)=="number"){ -elem.appendChild(n); -}else{ -_33a=_33b(n,_33a); -} -} -} -return elem; -},insertSiblingNodesBefore:function(node){ -var elem=node; -var self=MochiKit.DOM; -if(typeof (node)=="string"){ -elem=self.getElement(node); -} -var _340=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; -var _341=elem.parentNode; -var _342=MochiKit.Base.concat; -while(_340.length){ -var n=_340.shift(); -if(typeof (n)=="undefined"||n===null){ -}else{ -if(typeof (n.nodeType)=="number"){ -_341.insertBefore(n,elem); -}else{ -_340=_342(n,_340); -} -} -} -return _341; -},insertSiblingNodesAfter:function(node){ -var elem=node; -var self=MochiKit.DOM; -if(typeof (node)=="string"){ -elem=self.getElement(node); -} -var _347=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; -if(elem.nextSibling){ -return self.insertSiblingNodesBefore(elem.nextSibling,_347); -}else{ -return self.appendChildNodes(elem.parentNode,_347); -} -},replaceChildNodes:function(node){ -var elem=node; -var self=MochiKit.DOM; -if(typeof (node)=="string"){ -elem=self.getElement(node); -arguments[0]=elem; -} -var _34b; -while((_34b=elem.firstChild)){ -elem.removeChild(_34b); -} -if(arguments.length<2){ -return elem; -}else{ -return self.appendChildNodes.apply(this,arguments); -} -},createDOM:function(name,_34d){ -var elem; -var self=MochiKit.DOM; -var m=MochiKit.Base; -if(typeof (_34d)=="string"||typeof (_34d)=="number"){ -var args=m.extend([name,null],arguments,1); -return arguments.callee.apply(this,args); -} -if(typeof (name)=="string"){ -var _352=self._xhtml; -if(_34d&&!self.attributeArray.compliant){ -var _353=""; -if("name" in _34d){ -_353+=" name=\""+self.escapeHTML(_34d.name)+"\""; -} -if(name=="input"&&"type" in _34d){ -_353+=" type=\""+self.escapeHTML(_34d.type)+"\""; -} -if(_353){ -name="<"+name+_353+">"; -_352=false; -} -} -var d=self._document; -if(_352&&d===document){ -elem=d.createElementNS("http://www.w3.org/1999/xhtml",name); -}else{ -elem=d.createElement(name); -} -}else{ -elem=name; -} -if(_34d){ -self.updateNodeAttributes(elem,_34d); -} -if(arguments.length<=2){ -return elem; -}else{ -var args=m.extend([elem],arguments,2); -return self.appendChildNodes.apply(this,args); -} -},createDOMFunc:function(){ -var m=MochiKit.Base; -return m.partial.apply(this,m.extend([MochiKit.DOM.createDOM],arguments)); -},removeElement:function(elem){ -var self=MochiKit.DOM; -var e=self.coerceToDOM(self.getElement(elem)); -e.parentNode.removeChild(e); -return e; -},swapDOM:function(dest,src){ -var self=MochiKit.DOM; -dest=self.getElement(dest); -var _35c=dest.parentNode; -if(src){ -src=self.coerceToDOM(self.getElement(src),_35c); -_35c.replaceChild(src,dest); -}else{ -_35c.removeChild(dest); -} -return src; -},getElement:function(id){ -var self=MochiKit.DOM; -if(arguments.length==1){ -return ((typeof (id)=="string")?self._document.getElementById(id):id); -}else{ -return MochiKit.Base.map(self.getElement,arguments); -} -},getElementsByTagAndClassName:function(_35f,_360,_361){ -var self=MochiKit.DOM; -if(typeof (_35f)=="undefined"||_35f===null){ -_35f="*"; -} -if(typeof (_361)=="undefined"||_361===null){ -_361=self._document; -} -_361=self.getElement(_361); -if(_361==null){ -return []; -} -var _363=(_361.getElementsByTagName(_35f)||self._document.all); -if(typeof (_360)=="undefined"||_360===null){ -return MochiKit.Base.extend(null,_363); -} -var _364=[]; -for(var i=0;i<_363.length;i++){ -var _366=_363[i]; -var cls=_366.className; -if(typeof (cls)!="string"){ -cls=_366.getAttribute("class"); -} -if(typeof (cls)=="string"){ -var _368=cls.split(" "); -for(var j=0;j<_368.length;j++){ -if(_368[j]==_360){ -_364.push(_366); -break; -} -} -} -} -return _364; -},_newCallStack:function(path,once){ -var rval=function(){ -var _36d=arguments.callee.callStack; -for(var i=0;i<_36d.length;i++){ -if(_36d[i].apply(this,arguments)===false){ -break; -} -} -if(once){ -try{ -this[path]=null; -} -catch(e){ -} -} -}; -rval.callStack=[]; -return rval; -},addToCallStack:function(_36f,path,func,once){ -var self=MochiKit.DOM; -var _374=_36f[path]; -var _375=_374; -if(!(typeof (_374)=="function"&&typeof (_374.callStack)=="object"&&_374.callStack!==null)){ -_375=self._newCallStack(path,once); -if(typeof (_374)=="function"){ -_375.callStack.push(_374); -} -_36f[path]=_375; -} -_375.callStack.push(func); -},addLoadEvent:function(func){ -var self=MochiKit.DOM; -self.addToCallStack(self._window,"onload",func,true); -},focusOnLoad:function(_378){ -var self=MochiKit.DOM; -self.addLoadEvent(function(){ -_378=self.getElement(_378); -if(_378){ -_378.focus(); -} -}); -},setElementClass:function(_37a,_37b){ -var self=MochiKit.DOM; -var obj=self.getElement(_37a); -if(self.attributeArray.compliant){ -obj.setAttribute("class",_37b); -}else{ -obj.setAttribute("className",_37b); -} -},toggleElementClass:function(_37e){ -var self=MochiKit.DOM; -for(var i=1;i/g,">"); -},toHTML:function(dom){ -return MochiKit.DOM.emitHTML(dom).join(""); -},emitHTML:function(dom,lst){ -if(typeof (lst)=="undefined"||lst===null){ -lst=[]; -} -var _3a1=[dom]; -var self=MochiKit.DOM; -var _3a3=self.escapeHTML; -var _3a4=self.attributeArray; -while(_3a1.length){ -dom=_3a1.pop(); -if(typeof (dom)=="string"){ -lst.push(dom); -}else{ -if(dom.nodeType==1){ -lst.push("<"+dom.tagName.toLowerCase()); -var _3a5=[]; -var _3a6=_3a4(dom); -for(var i=0;i<_3a6.length;i++){ -var a=_3a6[i]; -_3a5.push([" ",a.name,"=\"",_3a3(a.value),"\""]); -} -_3a5.sort(); -for(i=0;i<_3a5.length;i++){ -var _3a9=_3a5[i]; -for(var j=0;j<_3a9.length;j++){ -lst.push(_3a9[j]); -} -} -if(dom.hasChildNodes()){ -lst.push(">"); -_3a1.push(""); -var _3ab=dom.childNodes; -for(i=_3ab.length-1;i>=0;i--){ -_3a1.push(_3ab[i]); -} -}else{ -lst.push("/>"); -} -}else{ -if(dom.nodeType==3){ -lst.push(_3a3(dom.nodeValue)); -} -} -} -} -return lst; -},scrapeText:function(node,_3ad){ -var rval=[]; -(function(node){ -var cn=node.childNodes; -if(cn){ -for(var i=0;i0){ -var _3ca=m.filter; -_3c9=function(node){ -return _3ca(_3c9.ignoreAttrFilter,node.attributes); -}; -_3c9.ignoreAttr={}; -var _3cc=_3c8.attributes; -var _3cd=_3c9.ignoreAttr; -for(var i=0;i<_3cc.length;i++){ -var a=_3cc[i]; -_3cd[a.name]=a.value; -} -_3c9.ignoreAttrFilter=function(a){ -return (_3c9.ignoreAttr[a.name]!=a.value); -}; -_3c9.compliant=false; -_3c9.renames={"class":"className","checked":"defaultChecked","usemap":"useMap","for":"htmlFor","readonly":"readOnly","colspan":"colSpan","bgcolor":"bgColor","cellspacing":"cellSpacing","cellpadding":"cellPadding"}; -}else{ -_3c9=function(node){ -return node.attributes; -}; -_3c9.compliant=true; -_3c9.ignoreAttr={}; -_3c9.renames={}; -} -this.attributeArray=_3c9; -var _3d2=function(_3d3,arr){ -var _3d5=arr[0]; -var _3d6=arr[1]; -var _3d7=_3d6.split(".")[1]; -var str=""; -str+="if (!MochiKit."+_3d7+") { throw new Error(\""; -str+="This function has been deprecated and depends on MochiKit."; -str+=_3d7+".\");}"; -str+="return "+_3d6+".apply(this, arguments);"; -MochiKit[_3d3][_3d5]=new Function(str); -}; -for(var i=0;i0){ -abort(repr(expr)); -} -},buildMatchExpression:function(){ -var repr=MochiKit.Base.repr; -var _3e4=this.params; -var _3e5=[]; -var _3e6,i; -function childElements(_3e8){ -return "MochiKit.Base.filter(function (node) { return node.nodeType == 1; }, "+_3e8+".childNodes)"; -} -if(_3e4.wildcard){ -_3e5.push("true"); -} -if(_3e6=_3e4.id){ -_3e5.push("element.id == "+repr(_3e6)); -} -if(_3e6=_3e4.tagName){ -_3e5.push("element.tagName.toUpperCase() == "+repr(_3e6)); -} -if((_3e6=_3e4.classNames).length>0){ -for(i=0;i<_3e6.length;i++){ -_3e5.push("MochiKit.DOM.hasElementClass(element, "+repr(_3e6[i])+")"); -} -} -if((_3e6=_3e4.pseudoClassNames).length>0){ -for(i=0;i<_3e6.length;i++){ -var _3e9=_3e6[i].match(/^([^(]+)(?:\((.*)\))?$/); -var _3ea=_3e9[1]; -var _3eb=_3e9[2]; -switch(_3ea){ -case "root": -_3e5.push("element.nodeType == 9 || element === element.ownerDocument.documentElement"); -break; -case "nth-child": -case "nth-last-child": -case "nth-of-type": -case "nth-last-of-type": -_3e9=_3eb.match(/^((?:(\d+)n\+)?(\d+)|odd|even)$/); -if(!_3e9){ -throw "Invalid argument to pseudo element nth-child: "+_3eb; -} -var a,b; -if(_3e9[0]=="odd"){ -a=2; -b=1; -}else{ -if(_3e9[0]=="even"){ -a=2; -b=0; -}else{ -a=_3e9[2]&&parseInt(_3e9)||null; -b=parseInt(_3e9[3]); -} -} -_3e5.push("this.nthChild(element,"+a+","+b+","+!!_3ea.match("^nth-last")+","+!!_3ea.match("of-type$")+")"); -break; -case "first-child": -_3e5.push("this.nthChild(element, null, 1)"); -break; -case "last-child": -_3e5.push("this.nthChild(element, null, 1, true)"); -break; -case "first-of-type": -_3e5.push("this.nthChild(element, null, 1, false, true)"); -break; -case "last-of-type": -_3e5.push("this.nthChild(element, null, 1, true, true)"); -break; -case "only-child": -_3e5.push(childElements("element.parentNode")+".length == 1"); -break; -case "only-of-type": -_3e5.push("MochiKit.Base.filter(function (node) { return node.tagName == element.tagName; }, "+childElements("element.parentNode")+").length == 1"); -break; -case "empty": -_3e5.push("element.childNodes.length == 0"); -break; -case "enabled": -_3e5.push("(this.isUIElement(element) && element.disabled === false)"); -break; -case "disabled": -_3e5.push("(this.isUIElement(element) && element.disabled === true)"); -break; -case "checked": -_3e5.push("(this.isUIElement(element) && element.checked === true)"); -break; -case "not": -var _3ee=new MochiKit.Selector.Selector(_3eb); -_3e5.push("!( "+_3ee.buildMatchExpression()+")"); -break; -} -} -} -if(_3e6=_3e4.attributes){ -MochiKit.Base.map(function(_3ef){ -var _3f0="MochiKit.DOM.getNodeAttribute(element, "+repr(_3ef.name)+")"; -var _3f1=function(_3f2){ -return _3f0+".split("+repr(_3f2)+")"; -}; -_3e5.push(_3f0+" != null"); -switch(_3ef.operator){ -case "=": -_3e5.push(_3f0+" == "+repr(_3ef.value)); -break; -case "~=": -_3e5.push("MochiKit.Base.findValue("+_3f1(" ")+", "+repr(_3ef.value)+") > -1"); -break; -case "^=": -_3e5.push(_3f0+".substring(0, "+_3ef.value.length+") == "+repr(_3ef.value)); -break; -case "$=": -_3e5.push(_3f0+".substring("+_3f0+".length - "+_3ef.value.length+") == "+repr(_3ef.value)); -break; -case "*=": -_3e5.push(_3f0+".match("+repr(_3ef.value)+")"); -break; -case "|=": -_3e5.push(_3f1("-")+"[0].toUpperCase() == "+repr(_3ef.value.toUpperCase())); -break; -case "!=": -_3e5.push(_3f0+" != "+repr(_3ef.value)); -break; -case "": -case undefined: -break; -default: -throw "Unknown operator "+_3ef.operator+" in selector"; -} -},_3e6); -} -return _3e5.join(" && "); -},compileMatcher:function(){ -var code="return (!element.tagName) ? false : "+this.buildMatchExpression()+";"; -this.match=new Function("element",code); -},nthChild:function(_3f4,a,b,_3f7,_3f8){ -var _3f9=MochiKit.Base.filter(function(node){ -return node.nodeType==1; -},_3f4.parentNode.childNodes); -if(_3f8){ -_3f9=MochiKit.Base.filter(function(node){ -return node.tagName==_3f4.tagName; -},_3f9); -} -if(_3f7){ -_3f9=MochiKit.Iter.reversed(_3f9); -} -if(a){ -var _3fc=MochiKit.Base.findIdentical(_3f9,_3f4); -return ((_3fc+1-b)/a)%1==0; -}else{ -return b==MochiKit.Base.findIdentical(_3f9,_3f4)+1; -} -},isUIElement:function(_3fd){ -return MochiKit.Base.findValue(["input","button","select","option","textarea","object"],_3fd.tagName.toLowerCase())>-1; -},findElements:function(_3fe,axis){ -var _400; -if(axis==undefined){ -axis=""; -} -function inScope(_401,_402){ -if(axis==""){ -return MochiKit.DOM.isChildNode(_401,_402); -}else{ -if(axis==">"){ -return _401.parentNode===_402; -}else{ -if(axis=="+"){ -return _401===nextSiblingElement(_402); -}else{ -if(axis=="~"){ -var _403=_402; -while(_403=nextSiblingElement(_403)){ -if(_401===_403){ -return true; -} -} -return false; -}else{ -throw "Invalid axis: "+axis; -} -} -} -} -} -if(_400=MochiKit.DOM.getElement(this.params.id)){ -if(this.match(_400)){ -if(!_3fe||inScope(_400,_3fe)){ -return [_400]; -} -} -} -function nextSiblingElement(node){ -node=node.nextSibling; -while(node&&node.nodeType!=1){ -node=node.nextSibling; -} -return node; -} -if(axis==""){ -_3fe=(_3fe||MochiKit.DOM.currentDocument()).getElementsByTagName(this.params.tagName||"*"); -}else{ -if(axis==">"){ -if(!_3fe){ -throw "> combinator not allowed without preceeding expression"; -} -_3fe=MochiKit.Base.filter(function(node){ -return node.nodeType==1; -},_3fe.childNodes); -}else{ -if(axis=="+"){ -if(!_3fe){ -throw "+ combinator not allowed without preceeding expression"; -} -_3fe=nextSiblingElement(_3fe)&&[nextSiblingElement(_3fe)]; -}else{ -if(axis=="~"){ -if(!_3fe){ -throw "~ combinator not allowed without preceeding expression"; -} -var _406=[]; -while(nextSiblingElement(_3fe)){ -_3fe=nextSiblingElement(_3fe); -_406.push(_3fe); -} -_3fe=_406; -} -} -} -} -if(!_3fe){ -return []; -} -var _407=MochiKit.Base.filter(MochiKit.Base.bind(function(_408){ -return this.match(_408); -},this),_3fe); -return _407; -},repr:function(){ -return "Selector("+this.expression+")"; -},toString:MochiKit.Base.forwardCall("repr")}; -MochiKit.Base.update(MochiKit.Selector,{findChildElements:function(_409,_40a){ -var uniq=function(arr){ -var res=[]; -for(var i=0;i+~]$/)){ -_410=match[0]; -return _412; -}else{ -var _414=new MochiKit.Selector.Selector(expr); -var _415=MochiKit.Iter.reduce(function(_416,_417){ -return MochiKit.Base.extend(_416,_414.findElements(_417||_409,_410)); -},_412,[]); -_410=""; -return _415; -} -}; -var _418=_40f.replace(/(^\s+|\s+$)/g,"").split(/\s+/); -return uniq(MochiKit.Iter.reduce(_411,_418,[null])); -},_40a)); -},findDocElements:function(){ -return MochiKit.Selector.findChildElements(MochiKit.DOM.currentDocument(),arguments); -},__new__:function(){ -var m=MochiKit.Base; -this.$$=this.findDocElements; -this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; -m.nameFunctions(this); -}}); -MochiKit.Selector.__new__(); -MochiKit.Base._exportSymbols(this,MochiKit.Selector); -MochiKit.Base._deps("Style",["Base","DOM"]); -MochiKit.Style.NAME="MochiKit.Style"; -MochiKit.Style.VERSION="1.4.2"; -MochiKit.Style.__repr__=function(){ -return "["+this.NAME+" "+this.VERSION+"]"; -}; -MochiKit.Style.toString=function(){ -return this.__repr__(); -}; -MochiKit.Style.EXPORT_OK=[]; -MochiKit.Style.EXPORT=["setStyle","setOpacity","getStyle","getElementDimensions","elementDimensions","setElementDimensions","getElementPosition","elementPosition","setElementPosition","makePositioned","undoPositioned","makeClipping","undoClipping","setDisplayForElement","hideElement","showElement","getViewportDimensions","getViewportPosition","Dimensions","Coordinates"]; -MochiKit.Style.Dimensions=function(w,h){ -this.w=w; -this.h=h; -}; -MochiKit.Style.Dimensions.prototype.__repr__=function(){ -var repr=MochiKit.Base.repr; -return "{w: "+repr(this.w)+", h: "+repr(this.h)+"}"; -}; -MochiKit.Style.Dimensions.prototype.toString=function(){ -return this.__repr__(); -}; -MochiKit.Style.Coordinates=function(x,y){ -this.x=x; -this.y=y; -}; -MochiKit.Style.Coordinates.prototype.__repr__=function(){ -var repr=MochiKit.Base.repr; -return "{x: "+repr(this.x)+", y: "+repr(this.y)+"}"; -}; -MochiKit.Style.Coordinates.prototype.toString=function(){ -return this.__repr__(); -}; -MochiKit.Base.update(MochiKit.Style,{getStyle:function(elem,_421){ -var dom=MochiKit.DOM; -var d=dom._document; -elem=dom.getElement(elem); -_421=MochiKit.Base.camelize(_421); -if(!elem||elem==d){ -return undefined; -} -if(_421=="opacity"&&typeof (elem.filters)!="undefined"){ -var _424=(MochiKit.Style.getStyle(elem,"filter")||"").match(/alpha\(opacity=(.*)\)/); -if(_424&&_424[1]){ -return parseFloat(_424[1])/100; -} -return 1; -} -if(_421=="float"||_421=="cssFloat"||_421=="styleFloat"){ -if(elem.style["float"]){ -return elem.style["float"]; -}else{ -if(elem.style.cssFloat){ -return elem.style.cssFloat; -}else{ -if(elem.style.styleFloat){ -return elem.style.styleFloat; -}else{ -return "none"; -} -} -} -} -var _425=elem.style?elem.style[_421]:null; -if(!_425){ -if(d.defaultView&&d.defaultView.getComputedStyle){ -var css=d.defaultView.getComputedStyle(elem,null); -_421=_421.replace(/([A-Z])/g,"-$1").toLowerCase(); -_425=css?css.getPropertyValue(_421):null; -}else{ -if(elem.currentStyle){ -_425=elem.currentStyle[_421]; -if(/^\d/.test(_425)&&!/px$/.test(_425)&&_421!="fontWeight"){ -var left=elem.style.left; -var _428=elem.runtimeStyle.left; -elem.runtimeStyle.left=elem.currentStyle.left; -elem.style.left=_425||0; -_425=elem.style.pixelLeft+"px"; -elem.style.left=left; -elem.runtimeStyle.left=_428; -} -} -} -} -if(_421=="opacity"){ -_425=parseFloat(_425); -} -if(/Opera/.test(navigator.userAgent)&&(MochiKit.Base.findValue(["left","top","right","bottom"],_421)!=-1)){ -if(MochiKit.Style.getStyle(elem,"position")=="static"){ -_425="auto"; -} -} -return _425=="auto"?null:_425; -},setStyle:function(elem,_42a){ -elem=MochiKit.DOM.getElement(elem); -for(var name in _42a){ -switch(name){ -case "opacity": -MochiKit.Style.setOpacity(elem,_42a[name]); -break; -case "float": -case "cssFloat": -case "styleFloat": -if(typeof (elem.style["float"])!="undefined"){ -elem.style["float"]=_42a[name]; -}else{ -if(typeof (elem.style.cssFloat)!="undefined"){ -elem.style.cssFloat=_42a[name]; -}else{ -elem.style.styleFloat=_42a[name]; -} -} -break; -default: -elem.style[MochiKit.Base.camelize(name)]=_42a[name]; -} -} -},setOpacity:function(elem,o){ -elem=MochiKit.DOM.getElement(elem); -var self=MochiKit.Style; -if(o==1){ -var _42f=/Gecko/.test(navigator.userAgent)&&!(/Konqueror|AppleWebKit|KHTML/.test(navigator.userAgent)); -elem.style["opacity"]=_42f?0.999999:1; -if(/MSIE/.test(navigator.userAgent)){ -elem.style["filter"]=self.getStyle(elem,"filter").replace(/alpha\([^\)]*\)/gi,""); -} -}else{ -if(o<0.00001){ -o=0; -} -elem.style["opacity"]=o; -if(/MSIE/.test(navigator.userAgent)){ -elem.style["filter"]=self.getStyle(elem,"filter").replace(/alpha\([^\)]*\)/gi,"")+"alpha(opacity="+o*100+")"; -} -} -},getElementPosition:function(elem,_431){ -var self=MochiKit.Style; -var dom=MochiKit.DOM; -elem=dom.getElement(elem); -if(!elem||(!(elem.x&&elem.y)&&(!elem.parentNode===null||self.getStyle(elem,"display")=="none"))){ -return undefined; -} -var c=new self.Coordinates(0,0); -var box=null; -var _436=null; -var d=MochiKit.DOM._document; -var de=d.documentElement; -var b=d.body; -if(!elem.parentNode&&elem.x&&elem.y){ -c.x+=elem.x||0; -c.y+=elem.y||0; -}else{ -if(elem.getBoundingClientRect){ -box=elem.getBoundingClientRect(); -c.x+=box.left+(de.scrollLeft||b.scrollLeft)-(de.clientLeft||0); -c.y+=box.top+(de.scrollTop||b.scrollTop)-(de.clientTop||0); -}else{ -if(elem.offsetParent){ -c.x+=elem.offsetLeft; -c.y+=elem.offsetTop; -_436=elem.offsetParent; -if(_436!=elem){ -while(_436){ -c.x+=parseInt(_436.style.borderLeftWidth)||0; -c.y+=parseInt(_436.style.borderTopWidth)||0; -c.x+=_436.offsetLeft; -c.y+=_436.offsetTop; -_436=_436.offsetParent; -} -} -var ua=navigator.userAgent.toLowerCase(); -if((typeof (opera)!="undefined"&&parseFloat(opera.version())<9)||(ua.indexOf("AppleWebKit")!=-1&&self.getStyle(elem,"position")=="absolute")){ -c.x-=b.offsetLeft; -c.y-=b.offsetTop; -} -if(elem.parentNode){ -_436=elem.parentNode; -}else{ -_436=null; -} -while(_436){ -var _43b=_436.tagName.toUpperCase(); -if(_43b==="BODY"||_43b==="HTML"){ -break; -} -var disp=self.getStyle(_436,"display"); -if(disp.search(/^inline|table-row.*$/i)){ -c.x-=_436.scrollLeft; -c.y-=_436.scrollTop; -} -if(_436.parentNode){ -_436=_436.parentNode; -}else{ -_436=null; -} -} -} -} -} -if(typeof (_431)!="undefined"){ -_431=arguments.callee(_431); -if(_431){ -c.x-=(_431.x||0); -c.y-=(_431.y||0); -} -} -return c; -},setElementPosition:function(elem,_43e,_43f){ -elem=MochiKit.DOM.getElement(elem); -if(typeof (_43f)=="undefined"){ -_43f="px"; -} -var _440={}; -var _441=MochiKit.Base.isUndefinedOrNull; -if(!_441(_43e.x)){ -_440["left"]=_43e.x+_43f; -} -if(!_441(_43e.y)){ -_440["top"]=_43e.y+_43f; -} -MochiKit.DOM.updateNodeAttributes(elem,{"style":_440}); -},makePositioned:function(_442){ -_442=MochiKit.DOM.getElement(_442); -var pos=MochiKit.Style.getStyle(_442,"position"); -if(pos=="static"||!pos){ -_442.style.position="relative"; -if(/Opera/.test(navigator.userAgent)){ -_442.style.top=0; -_442.style.left=0; -} -} -},undoPositioned:function(_444){ -_444=MochiKit.DOM.getElement(_444); -if(_444.style.position=="relative"){ -_444.style.position=_444.style.top=_444.style.left=_444.style.bottom=_444.style.right=""; -} -},makeClipping:function(_445){ -_445=MochiKit.DOM.getElement(_445); -var s=_445.style; -var _447={"overflow":s.overflow,"overflow-x":s.overflowX,"overflow-y":s.overflowY}; -if((MochiKit.Style.getStyle(_445,"overflow")||"visible")!="hidden"){ -_445.style.overflow="hidden"; -_445.style.overflowX="hidden"; -_445.style.overflowY="hidden"; -} -return _447; -},undoClipping:function(_448,_449){ -_448=MochiKit.DOM.getElement(_448); -if(typeof (_449)=="string"){ -_448.style.overflow=_449; -}else{ -if(_449!=null){ -_448.style.overflow=_449["overflow"]; -_448.style.overflowX=_449["overflow-x"]; -_448.style.overflowY=_449["overflow-y"]; -} -} -},getElementDimensions:function(elem,_44b){ -var self=MochiKit.Style; -var dom=MochiKit.DOM; -if(typeof (elem.w)=="number"||typeof (elem.h)=="number"){ -return new self.Dimensions(elem.w||0,elem.h||0); -} -elem=dom.getElement(elem); -if(!elem){ -return undefined; -} -var disp=self.getStyle(elem,"display"); -if(disp=="none"||disp==""||typeof (disp)=="undefined"){ -var s=elem.style; -var _450=s.visibility; -var _451=s.position; -var _452=s.display; -s.visibility="hidden"; -s.position="absolute"; -s.display=self._getDefaultDisplay(elem); -var _453=elem.offsetWidth; -var _454=elem.offsetHeight; -s.display=_452; -s.position=_451; -s.visibility=_450; -}else{ -_453=elem.offsetWidth||0; -_454=elem.offsetHeight||0; -} -if(_44b){ -var _455="colSpan" in elem&&"rowSpan" in elem; -var _456=(_455&&elem.parentNode&&self.getStyle(elem.parentNode,"borderCollapse")=="collapse"); -if(_456){ -if(/MSIE/.test(navigator.userAgent)){ -var _457=elem.previousSibling?0.5:1; -var _458=elem.nextSibling?0.5:1; -}else{ -var _457=0.5; -var _458=0.5; -} -}else{ -var _457=1; -var _458=1; -} -_453-=Math.round((parseFloat(self.getStyle(elem,"paddingLeft"))||0)+(parseFloat(self.getStyle(elem,"paddingRight"))||0)+_457*(parseFloat(self.getStyle(elem,"borderLeftWidth"))||0)+_458*(parseFloat(self.getStyle(elem,"borderRightWidth"))||0)); -if(_455){ -if(/Gecko|Opera/.test(navigator.userAgent)&&!/Konqueror|AppleWebKit|KHTML/.test(navigator.userAgent)){ -var _459=0; -}else{ -if(/MSIE/.test(navigator.userAgent)){ -var _459=1; -}else{ -var _459=_456?0.5:1; -} -} -}else{ -var _459=1; -} -_454-=Math.round((parseFloat(self.getStyle(elem,"paddingTop"))||0)+(parseFloat(self.getStyle(elem,"paddingBottom"))||0)+_459*((parseFloat(self.getStyle(elem,"borderTopWidth"))||0)+(parseFloat(self.getStyle(elem,"borderBottomWidth"))||0))); -} -return new self.Dimensions(_453,_454); -},setElementDimensions:function(elem,_45b,_45c){ -elem=MochiKit.DOM.getElement(elem); -if(typeof (_45c)=="undefined"){ -_45c="px"; -} -var _45d={}; -var _45e=MochiKit.Base.isUndefinedOrNull; -if(!_45e(_45b.w)){ -_45d["width"]=_45b.w+_45c; -} -if(!_45e(_45b.h)){ -_45d["height"]=_45b.h+_45c; -} -MochiKit.DOM.updateNodeAttributes(elem,{"style":_45d}); -},_getDefaultDisplay:function(elem){ -var self=MochiKit.Style; -var dom=MochiKit.DOM; -elem=dom.getElement(elem); -if(!elem){ -return undefined; -} -var _462=elem.tagName.toUpperCase(); -return self._defaultDisplay[_462]||"block"; -},setDisplayForElement:function(_463,_464){ -var _465=MochiKit.Base.extend(null,arguments,1); -var _466=MochiKit.DOM.getElement; -for(var i=0;i<_465.length;i++){ -_464=_466(_465[i]); -if(_464){ -_464.style.display=_463; -} -} -},getViewportDimensions:function(){ -var d=new MochiKit.Style.Dimensions(); -var w=MochiKit.DOM._window; -var b=MochiKit.DOM._document.body; -if(w.innerWidth){ -d.w=w.innerWidth; -d.h=w.innerHeight; -}else{ -if(b&&b.parentElement&&b.parentElement.clientWidth){ -d.w=b.parentElement.clientWidth; -d.h=b.parentElement.clientHeight; -}else{ -if(b&&b.clientWidth){ -d.w=b.clientWidth; -d.h=b.clientHeight; -} -} -} -return d; -},getViewportPosition:function(){ -var c=new MochiKit.Style.Coordinates(0,0); -var d=MochiKit.DOM._document; -var de=d.documentElement; -var db=d.body; -if(de&&(de.scrollTop||de.scrollLeft)){ -c.x=de.scrollLeft; -c.y=de.scrollTop; -}else{ -if(db){ -c.x=db.scrollLeft; -c.y=db.scrollTop; -} -} -return c; -},__new__:function(){ -var m=MochiKit.Base; -var _470=["A","ABBR","ACRONYM","B","BASEFONT","BDO","BIG","BR","CITE","CODE","DFN","EM","FONT","I","IMG","KBD","LABEL","Q","S","SAMP","SMALL","SPAN","STRIKE","STRONG","SUB","SUP","TEXTAREA","TT","U","VAR"]; -this._defaultDisplay={"TABLE":"table","THEAD":"table-header-group","TBODY":"table-row-group","TFOOT":"table-footer-group","COLGROUP":"table-column-group","COL":"table-column","TR":"table-row","TD":"table-cell","TH":"table-cell","CAPTION":"table-caption","LI":"list-item","INPUT":"inline-block","SELECT":"inline-block"}; -if(/MSIE/.test(navigator.userAgent)){ -for(var k in this._defaultDisplay){ -var v=this._defaultDisplay[k]; -if(v.indexOf("table")==0){ -this._defaultDisplay[k]="block"; -} -} -} -for(var i=0;i<_470.length;i++){ -this._defaultDisplay[_470[i]]="inline"; -} -this.elementPosition=this.getElementPosition; -this.elementDimensions=this.getElementDimensions; -this.hideElement=m.partial(this.setDisplayForElement,"none"); -this.showElement=m.partial(this.setDisplayForElement,"block"); -this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; -m.nameFunctions(this); -}}); -MochiKit.Style.__new__(); -MochiKit.Base._exportSymbols(this,MochiKit.Style); -MochiKit.Base._deps("LoggingPane",["Base","Logging"]); -MochiKit.LoggingPane.NAME="MochiKit.LoggingPane"; -MochiKit.LoggingPane.VERSION="1.4.2"; -MochiKit.LoggingPane.__repr__=function(){ -return "["+this.NAME+" "+this.VERSION+"]"; -}; -MochiKit.LoggingPane.toString=function(){ -return this.__repr__(); -}; -MochiKit.LoggingPane.createLoggingPane=function(_474){ -var m=MochiKit.LoggingPane; -_474=!(!_474); -if(m._loggingPane&&m._loggingPane.inline!=_474){ -m._loggingPane.closePane(); -m._loggingPane=null; -} -if(!m._loggingPane||m._loggingPane.closed){ -m._loggingPane=new m.LoggingPane(_474,MochiKit.Logging.logger); -} -return m._loggingPane; -}; -MochiKit.LoggingPane.LoggingPane=function(_476,_477){ -if(typeof (_477)=="undefined"||_477===null){ -_477=MochiKit.Logging.logger; -} -this.logger=_477; -var _478=MochiKit.Base.update; -var _479=MochiKit.Base.updatetree; -var bind=MochiKit.Base.bind; -var _47b=MochiKit.Base.clone; -var win=window; -var uid="_MochiKit_LoggingPane"; -if(typeof (MochiKit.DOM)!="undefined"){ -win=MochiKit.DOM.currentWindow(); -} -if(!_476){ -var url=win.location.href.split("?")[0].replace(/[#:\/.><&%-]/g,"_"); -var name=uid+"_"+url; -var nwin=win.open("",name,"dependent,resizable,height=200"); -if(!nwin){ -alert("Not able to open debugging window due to pop-up blocking."); -return undefined; -} -nwin.document.write(""+"[MochiKit.LoggingPane]"+""); -nwin.document.close(); -nwin.document.title+=" "+win.document.title; -win=nwin; -} -var doc=win.document; -this.doc=doc; -var _482=doc.getElementById(uid); -var _483=!!_482; -if(_482&&typeof (_482.loggingPane)!="undefined"){ -_482.loggingPane.logger=this.logger; -_482.loggingPane.buildAndApplyFilter(); -return _482.loggingPane; -} -if(_483){ -var _484; -while((_484=_482.firstChild)){ -_482.removeChild(_484); -} -}else{ -_482=doc.createElement("div"); -_482.id=uid; -} -_482.loggingPane=this; -var _485=doc.createElement("input"); -var _486=doc.createElement("input"); -var _487=doc.createElement("button"); -var _488=doc.createElement("button"); -var _489=doc.createElement("button"); -var _48a=doc.createElement("button"); -var _48b=doc.createElement("div"); -var _48c=doc.createElement("div"); -var _48d=uid+"_Listener"; -this.colorTable=_47b(this.colorTable); -var _48e=[]; -var _48f=null; -var _490=function(msg){ -var _492=msg.level; -if(typeof (_492)=="number"){ -_492=MochiKit.Logging.LogLevel[_492]; -} -return _492; -}; -var _493=function(msg){ -return msg.info.join(" "); -}; -var _495=bind(function(msg){ -var _497=_490(msg); -var text=_493(msg); -var c=this.colorTable[_497]; -var p=doc.createElement("span"); -p.className="MochiKit-LogMessage MochiKit-LogLevel-"+_497; -p.style.cssText="margin: 0px; white-space: -moz-pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; white-space: pre-line; word-wrap: break-word; wrap-option: emergency; color: "+c; -p.appendChild(doc.createTextNode(_497+": "+text)); -_48c.appendChild(p); -_48c.appendChild(doc.createElement("br")); -if(_48b.offsetHeight>_48b.scrollHeight){ -_48b.scrollTop=0; -}else{ -_48b.scrollTop=_48b.scrollHeight; -} -},this); -var _49b=function(msg){ -_48e[_48e.length]=msg; -_495(msg); -}; -var _49d=function(){ -var _49e,_49f; -try{ -_49e=new RegExp(_485.value); -_49f=new RegExp(_486.value); -} -catch(e){ -logDebug("Error in filter regex: "+e.message); -return null; -} -return function(msg){ -return (_49e.test(_490(msg))&&_49f.test(_493(msg))); -}; -}; -var _4a1=function(){ -while(_48c.firstChild){ -_48c.removeChild(_48c.firstChild); -} -}; -var _4a2=function(){ -_48e=[]; -_4a1(); -}; -var _4a3=bind(function(){ -if(this.closed){ -return; -} -this.closed=true; -if(MochiKit.LoggingPane._loggingPane==this){ -MochiKit.LoggingPane._loggingPane=null; -} -this.logger.removeListener(_48d); -try{ -try{ -_482.loggingPane=null; -} -catch(e){ -logFatal("Bookmarklet was closed incorrectly."); -} -if(_476){ -_482.parentNode.removeChild(_482); -}else{ -this.win.close(); -} -} -catch(e){ -} -},this); -var _4a4=function(){ -_4a1(); -for(var i=0;i<_48e.length;i++){ -var msg=_48e[i]; -if(_48f===null||_48f(msg)){ -_495(msg); -} -} -}; -this.buildAndApplyFilter=function(){ -_48f=_49d(); -_4a4(); -this.logger.removeListener(_48d); -this.logger.addListener(_48d,_48f,_49b); -}; -var _4a7=bind(function(){ -_48e=this.logger.getMessages(); -_4a4(); -},this); -var _4a8=bind(function(_4a9){ -_4a9=_4a9||window.event; -key=_4a9.which||_4a9.keyCode; -if(key==13){ -this.buildAndApplyFilter(); -} -},this); -var _4aa="display: block; z-index: 1000; left: 0px; bottom: 0px; position: fixed; width: 100%; background-color: white; font: "+this.logFont; -if(_476){ -_4aa+="; height: 10em; border-top: 2px solid black"; -}else{ -_4aa+="; height: 100%;"; -} -_482.style.cssText=_4aa; -if(!_483){ -doc.body.appendChild(_482); -} -_4aa={"cssText":"width: 33%; display: inline; font: "+this.logFont}; -_479(_485,{"value":"FATAL|ERROR|WARNING|INFO|DEBUG","onkeypress":_4a8,"style":_4aa}); -_482.appendChild(_485); -_479(_486,{"value":".*","onkeypress":_4a8,"style":_4aa}); -_482.appendChild(_486); -_4aa="width: 8%; display:inline; font: "+this.logFont; -_487.appendChild(doc.createTextNode("Filter")); -_487.onclick=bind("buildAndApplyFilter",this); -_487.style.cssText=_4aa; -_482.appendChild(_487); -_488.appendChild(doc.createTextNode("Load")); -_488.onclick=_4a7; -_488.style.cssText=_4aa; -_482.appendChild(_488); -_489.appendChild(doc.createTextNode("Clear")); -_489.onclick=_4a2; -_489.style.cssText=_4aa; -_482.appendChild(_489); -_48a.appendChild(doc.createTextNode("Close")); -_48a.onclick=_4a3; -_48a.style.cssText=_4aa; -_482.appendChild(_48a); -_48b.style.cssText="overflow: auto; width: 100%"; -_48c.style.cssText="width: 100%; height: "+(_476?"8em":"100%"); -_48b.appendChild(_48c); -_482.appendChild(_48b); -this.buildAndApplyFilter(); -_4a7(); -if(_476){ -this.win=undefined; -}else{ -this.win=win; -} -this.inline=_476; -this.closePane=_4a3; -this.closed=false; -return this; -}; -MochiKit.LoggingPane.LoggingPane.prototype={"logFont":"8pt Verdana,sans-serif","colorTable":{"ERROR":"red","FATAL":"darkred","WARNING":"blue","INFO":"black","DEBUG":"green"}}; -MochiKit.LoggingPane.EXPORT_OK=["LoggingPane"]; -MochiKit.LoggingPane.EXPORT=["createLoggingPane"]; -MochiKit.LoggingPane.__new__=function(){ -this.EXPORT_TAGS={":common":this.EXPORT,":all":MochiKit.Base.concat(this.EXPORT,this.EXPORT_OK)}; -MochiKit.Base.nameFunctions(this); -MochiKit.LoggingPane._loggingPane=null; -}; -MochiKit.LoggingPane.__new__(); -MochiKit.Base._exportSymbols(this,MochiKit.LoggingPane); -MochiKit.Base._deps("Color",["Base","DOM","Style"]); -MochiKit.Color.NAME="MochiKit.Color"; -MochiKit.Color.VERSION="1.4.2"; -MochiKit.Color.__repr__=function(){ -return "["+this.NAME+" "+this.VERSION+"]"; -}; -MochiKit.Color.toString=function(){ -return this.__repr__(); -}; -MochiKit.Color.Color=function(red,_4ac,blue,_4ae){ -if(typeof (_4ae)=="undefined"||_4ae===null){ -_4ae=1; -} -this.rgb={r:red,g:_4ac,b:blue,a:_4ae}; -}; -MochiKit.Color.Color.prototype={__class__:MochiKit.Color.Color,colorWithAlpha:function(_4af){ -var rgb=this.rgb; -var m=MochiKit.Color; -return m.Color.fromRGB(rgb.r,rgb.g,rgb.b,_4af); -},colorWithHue:function(hue){ -var hsl=this.asHSL(); -hsl.h=hue; -var m=MochiKit.Color; -return m.Color.fromHSL(hsl); -},colorWithSaturation:function(_4b5){ -var hsl=this.asHSL(); -hsl.s=_4b5; -var m=MochiKit.Color; -return m.Color.fromHSL(hsl); -},colorWithLightness:function(_4b8){ -var hsl=this.asHSL(); -hsl.l=_4b8; -var m=MochiKit.Color; -return m.Color.fromHSL(hsl); -},darkerColorWithLevel:function(_4bb){ -var hsl=this.asHSL(); -hsl.l=Math.max(hsl.l-_4bb,0); -var m=MochiKit.Color; -return m.Color.fromHSL(hsl); -},lighterColorWithLevel:function(_4be){ -var hsl=this.asHSL(); -hsl.l=Math.min(hsl.l+_4be,1); -var m=MochiKit.Color; -return m.Color.fromHSL(hsl); -},blendedColor:function(_4c1,_4c2){ -if(typeof (_4c2)=="undefined"||_4c2===null){ -_4c2=0.5; -} -var sf=1-_4c2; -var s=this.rgb; -var d=_4c1.rgb; -var df=_4c2; -return MochiKit.Color.Color.fromRGB((s.r*sf)+(d.r*df),(s.g*sf)+(d.g*df),(s.b*sf)+(d.b*df),(s.a*sf)+(d.a*df)); -},compareRGB:function(_4c7){ -var a=this.asRGB(); -var b=_4c7.asRGB(); -return MochiKit.Base.compare([a.r,a.g,a.b,a.a],[b.r,b.g,b.b,b.a]); -},isLight:function(){ -return this.asHSL().b>0.5; -},isDark:function(){ -return (!this.isLight()); -},toHSLString:function(){ -var c=this.asHSL(); -var ccc=MochiKit.Color.clampColorComponent; -var rval=this._hslString; -if(!rval){ -var mid=(ccc(c.h,360).toFixed(0)+","+ccc(c.s,100).toPrecision(4)+"%"+","+ccc(c.l,100).toPrecision(4)+"%"); -var a=c.a; -if(a>=1){ -a=1; -rval="hsl("+mid+")"; -}else{ -if(a<=0){ -a=0; -} -rval="hsla("+mid+","+a+")"; -} -this._hslString=rval; -} -return rval; -},toRGBString:function(){ -var c=this.rgb; -var ccc=MochiKit.Color.clampColorComponent; -var rval=this._rgbString; -if(!rval){ -var mid=(ccc(c.r,255).toFixed(0)+","+ccc(c.g,255).toFixed(0)+","+ccc(c.b,255).toFixed(0)); -if(c.a!=1){ -rval="rgba("+mid+","+c.a+")"; -}else{ -rval="rgb("+mid+")"; -} -this._rgbString=rval; -} -return rval; -},asRGB:function(){ -return MochiKit.Base.clone(this.rgb); -},toHexString:function(){ -var m=MochiKit.Color; -var c=this.rgb; -var ccc=MochiKit.Color.clampColorComponent; -var rval=this._hexString; -if(!rval){ -rval=("#"+m.toColorPart(ccc(c.r,255))+m.toColorPart(ccc(c.g,255))+m.toColorPart(ccc(c.b,255))); -this._hexString=rval; -} -return rval; -},asHSV:function(){ -var hsv=this.hsv; -var c=this.rgb; -if(typeof (hsv)=="undefined"||hsv===null){ -hsv=MochiKit.Color.rgbToHSV(this.rgb); -this.hsv=hsv; -} -return MochiKit.Base.clone(hsv); -},asHSL:function(){ -var hsl=this.hsl; -var c=this.rgb; -if(typeof (hsl)=="undefined"||hsl===null){ -hsl=MochiKit.Color.rgbToHSL(this.rgb); -this.hsl=hsl; -} -return MochiKit.Base.clone(hsl); -},toString:function(){ -return this.toRGBString(); -},repr:function(){ -var c=this.rgb; -var col=[c.r,c.g,c.b,c.a]; -return this.__class__.NAME+"("+col.join(", ")+")"; -}}; -MochiKit.Base.update(MochiKit.Color.Color,{fromRGB:function(red,_4de,blue,_4e0){ -var _4e1=MochiKit.Color.Color; -if(arguments.length==1){ -var rgb=red; -red=rgb.r; -_4de=rgb.g; -blue=rgb.b; -if(typeof (rgb.a)=="undefined"){ -_4e0=undefined; -}else{ -_4e0=rgb.a; -} -} -return new _4e1(red,_4de,blue,_4e0); -},fromHSL:function(hue,_4e4,_4e5,_4e6){ -var m=MochiKit.Color; -return m.Color.fromRGB(m.hslToRGB.apply(m,arguments)); -},fromHSV:function(hue,_4e9,_4ea,_4eb){ -var m=MochiKit.Color; -return m.Color.fromRGB(m.hsvToRGB.apply(m,arguments)); -},fromName:function(name){ -var _4ee=MochiKit.Color.Color; -if(name.charAt(0)=="\""){ -name=name.substr(1,name.length-2); -} -var _4ef=_4ee._namedColors[name.toLowerCase()]; -if(typeof (_4ef)=="string"){ -return _4ee.fromHexString(_4ef); -}else{ -if(name=="transparent"){ -return _4ee.transparentColor(); -} -} -return null; -},fromString:function(_4f0){ -var self=MochiKit.Color.Color; -var _4f2=_4f0.substr(0,3); -if(_4f2=="rgb"){ -return self.fromRGBString(_4f0); -}else{ -if(_4f2=="hsl"){ -return self.fromHSLString(_4f0); -}else{ -if(_4f0.charAt(0)=="#"){ -return self.fromHexString(_4f0); -} -} -} -return self.fromName(_4f0); -},fromHexString:function(_4f3){ -if(_4f3.charAt(0)=="#"){ -_4f3=_4f3.substring(1); -} -var _4f4=[]; -var i,hex; -if(_4f3.length==3){ -for(i=0;i<3;i++){ -hex=_4f3.substr(i,1); -_4f4.push(parseInt(hex+hex,16)/255); -} -}else{ -for(i=0;i<6;i+=2){ -hex=_4f3.substr(i,2); -_4f4.push(parseInt(hex,16)/255); -} -} -var _4f7=MochiKit.Color.Color; -return _4f7.fromRGB.apply(_4f7,_4f4); -},_fromColorString:function(pre,_4f9,_4fa,_4fb){ -if(_4fb.indexOf(pre)===0){ -_4fb=_4fb.substring(_4fb.indexOf("(",3)+1,_4fb.length-1); -} -var _4fc=_4fb.split(/\s*,\s*/); -var _4fd=[]; -for(var i=0;i<_4fc.length;i++){ -var c=_4fc[i]; -var val; -var _501=c.substring(c.length-3); -if(c.charAt(c.length-1)=="%"){ -val=0.01*parseFloat(c.substring(0,c.length-1)); -}else{ -if(_501=="deg"){ -val=parseFloat(c)/360; -}else{ -if(_501=="rad"){ -val=parseFloat(c)/(Math.PI*2); -}else{ -val=_4fa[i]*parseFloat(c); -} -} -} -_4fd.push(val); -} -return this[_4f9].apply(this,_4fd); -},fromComputedStyle:function(elem,_503){ -var d=MochiKit.DOM; -var cls=MochiKit.Color.Color; -for(elem=d.getElement(elem);elem;elem=elem.parentNode){ -var _506=MochiKit.Style.getStyle.apply(d,arguments); -if(!_506){ -continue; -} -var _507=cls.fromString(_506); -if(!_507){ -break; -} -if(_507.asRGB().a>0){ -return _507; -} -} -return null; -},fromBackground:function(elem){ -var cls=MochiKit.Color.Color; -return cls.fromComputedStyle(elem,"backgroundColor","background-color")||cls.whiteColor(); -},fromText:function(elem){ -var cls=MochiKit.Color.Color; -return cls.fromComputedStyle(elem,"color","color")||cls.blackColor(); -},namedColors:function(){ -return MochiKit.Base.clone(MochiKit.Color.Color._namedColors); -}}); -MochiKit.Base.update(MochiKit.Color,{clampColorComponent:function(v,_50d){ -v*=_50d; -if(v<0){ -return 0; -}else{ -if(v>_50d){ -return _50d; -}else{ -return v; -} -} -},_hslValue:function(n1,n2,hue){ -if(hue>6){ -hue-=6; -}else{ -if(hue<0){ -hue+=6; -} -} -var val; -if(hue<1){ -val=n1+(n2-n1)*hue; -}else{ -if(hue<3){ -val=n2; -}else{ -if(hue<4){ -val=n1+(n2-n1)*(4-hue); -}else{ -val=n1; -} -} -} -return val; -},hsvToRGB:function(hue,_513,_514,_515){ -if(arguments.length==1){ -var hsv=hue; -hue=hsv.h; -_513=hsv.s; -_514=hsv.v; -_515=hsv.a; -} -var red; -var _518; -var blue; -if(_513===0){ -red=_514; -_518=_514; -blue=_514; -}else{ -var i=Math.floor(hue*6); -var f=(hue*6)-i; -var p=_514*(1-_513); -var q=_514*(1-(_513*f)); -var t=_514*(1-(_513*(1-f))); -switch(i){ -case 1: -red=q; -_518=_514; -blue=p; -break; -case 2: -red=p; -_518=_514; -blue=t; -break; -case 3: -red=p; -_518=q; -blue=_514; -break; -case 4: -red=t; -_518=p; -blue=_514; -break; -case 5: -red=_514; -_518=p; -blue=q; -break; -case 6: -case 0: -red=_514; -_518=t; -blue=p; -break; -} -} -return {r:red,g:_518,b:blue,a:_515}; -},hslToRGB:function(hue,_520,_521,_522){ -if(arguments.length==1){ -var hsl=hue; -hue=hsl.h; -_520=hsl.s; -_521=hsl.l; -_522=hsl.a; -} -var red; -var _525; -var blue; -if(_520===0){ -red=_521; -_525=_521; -blue=_521; -}else{ -var m2; -if(_521<=0.5){ -m2=_521*(1+_520); -}else{ -m2=_521+_520-(_521*_520); -} -var m1=(2*_521)-m2; -var f=MochiKit.Color._hslValue; -var h6=hue*6; -red=f(m1,m2,h6+2); -_525=f(m1,m2,h6); -blue=f(m1,m2,h6-2); -} -return {r:red,g:_525,b:blue,a:_522}; -},rgbToHSV:function(red,_52c,blue,_52e){ -if(arguments.length==1){ -var rgb=red; -red=rgb.r; -_52c=rgb.g; -blue=rgb.b; -_52e=rgb.a; -} -var max=Math.max(Math.max(red,_52c),blue); -var min=Math.min(Math.min(red,_52c),blue); -var hue; -var _533; -var _534=max; -if(min==max){ -hue=0; -_533=0; -}else{ -var _535=(max-min); -_533=_535/max; -if(red==max){ -hue=(_52c-blue)/_535; -}else{ -if(_52c==max){ -hue=2+((blue-red)/_535); -}else{ -hue=4+((red-_52c)/_535); -} -} -hue/=6; -if(hue<0){ -hue+=1; -} -if(hue>1){ -hue-=1; -} -} -return {h:hue,s:_533,v:_534,a:_52e}; -},rgbToHSL:function(red,_537,blue,_539){ -if(arguments.length==1){ -var rgb=red; -red=rgb.r; -_537=rgb.g; -blue=rgb.b; -_539=rgb.a; -} -var max=Math.max(red,Math.max(_537,blue)); -var min=Math.min(red,Math.min(_537,blue)); -var hue; -var _53e; -var _53f=(max+min)/2; -var _540=max-min; -if(_540===0){ -hue=0; -_53e=0; -}else{ -if(_53f<=0.5){ -_53e=_540/(max+min); -}else{ -_53e=_540/(2-max-min); -} -if(red==max){ -hue=(_537-blue)/_540; -}else{ -if(_537==max){ -hue=2+((blue-red)/_540); -}else{ -hue=4+((red-_537)/_540); -} -} -hue/=6; -if(hue<0){ -hue+=1; -} -if(hue>1){ -hue-=1; -} -} -return {h:hue,s:_53e,l:_53f,a:_539}; -},toColorPart:function(num){ -num=Math.round(num); -var _542=num.toString(16); -if(num<16){ -return "0"+_542; -} -return _542; -},__new__:function(){ -var m=MochiKit.Base; -this.Color.fromRGBString=m.bind(this.Color._fromColorString,this.Color,"rgb","fromRGB",[1/255,1/255,1/255,1]); -this.Color.fromHSLString=m.bind(this.Color._fromColorString,this.Color,"hsl","fromHSL",[1/360,0.01,0.01,1]); -var _544=1/3; -var _545={black:[0,0,0],blue:[0,0,1],brown:[0.6,0.4,0.2],cyan:[0,1,1],darkGray:[_544,_544,_544],gray:[0.5,0.5,0.5],green:[0,1,0],lightGray:[2*_544,2*_544,2*_544],magenta:[1,0,1],orange:[1,0.5,0],purple:[0.5,0,0.5],red:[1,0,0],transparent:[0,0,0,0],white:[1,1,1],yellow:[1,1,0]}; -var _546=function(name,r,g,b,a){ -var rval=this.fromRGB(r,g,b,a); -this[name]=function(){ -return rval; -}; -return rval; -}; -for(var k in _545){ -var name=k+"Color"; -var _54f=m.concat([_546,this.Color,name],_545[k]); -this.Color[name]=m.bind.apply(null,_54f); -} -var _550=function(){ -for(var i=0;i1){ -var src=MochiKit.DOM.getElement(arguments[0]); -var sig=arguments[1]; -var obj=arguments[2]; -var func=arguments[3]; -for(var i=_592.length-1;i>=0;i--){ -var o=_592[i]; -if(o.source===src&&o.signal===sig&&o.objOrFunc===obj&&o.funcOrStr===func){ -self._disconnect(o); -if(!self._lock){ -_592.splice(i,1); -}else{ -self._dirty=true; -} -return true; -} -} -}else{ -var idx=m.findIdentical(_592,_590); -if(idx>=0){ -self._disconnect(_590); -if(!self._lock){ -_592.splice(idx,1); -}else{ -self._dirty=true; -} -return true; -} -} -return false; -},disconnectAllTo:function(_59b,_59c){ -var self=MochiKit.Signal; -var _59e=self._observers; -var _59f=self._disconnect; -var _5a0=self._lock; -var _5a1=self._dirty; -if(typeof (_59c)==="undefined"){ -_59c=null; -} -for(var i=_59e.length-1;i>=0;i--){ -var _5a3=_59e[i]; -if(_5a3.objOrFunc===_59b&&(_59c===null||_5a3.funcOrStr===_59c)){ -_59f(_5a3); -if(_5a0){ -_5a1=true; -}else{ -_59e.splice(i,1); -} -} -} -self._dirty=_5a1; -},disconnectAll:function(src,sig){ -src=MochiKit.DOM.getElement(src); -var m=MochiKit.Base; -var _5a7=m.flattenArguments(m.extend(null,arguments,1)); -var self=MochiKit.Signal; -var _5a9=self._disconnect; -var _5aa=self._observers; -var i,_5ac; -var _5ad=self._lock; -var _5ae=self._dirty; -if(_5a7.length===0){ -for(i=_5aa.length-1;i>=0;i--){ -_5ac=_5aa[i]; -if(_5ac.source===src){ -_5a9(_5ac); -if(!_5ad){ -_5aa.splice(i,1); -}else{ -_5ae=true; -} -} -} -}else{ -var sigs={}; -for(i=0;i<_5a7.length;i++){ -sigs[_5a7[i]]=true; -} -for(i=_5aa.length-1;i>=0;i--){ -_5ac=_5aa[i]; -if(_5ac.source===src&&_5ac.signal in sigs){ -_5a9(_5ac); -if(!_5ad){ -_5aa.splice(i,1); -}else{ -_5ae=true; -} -} -} -} -self._dirty=_5ae; -},signal:function(src,sig){ -var self=MochiKit.Signal; -var _5b3=self._observers; -src=MochiKit.DOM.getElement(src); -var args=MochiKit.Base.extend(null,arguments,2); -var _5b5=[]; -self._lock=true; -for(var i=0;i<_5b3.length;i++){ -var _5b7=_5b3[i]; -if(_5b7.source===src&&_5b7.signal===sig&&_5b7.connected){ -try{ -_5b7.listener.apply(src,args); -} -catch(e){ -_5b5.push(e); -} -} -} -self._lock=false; -if(self._dirty){ -self._dirty=false; -for(var i=_5b3.length-1;i>=0;i--){ -if(!_5b3[i].connected){ -_5b3.splice(i,1); -} -} -} -if(_5b5.length==1){ -throw _5b5[0]; -}else{ -if(_5b5.length>1){ -var e=new Error("Multiple errors thrown in handling 'sig', see errors property"); -e.errors=_5b5; -throw e; -} -} -}}); -MochiKit.Signal.EXPORT_OK=[]; -MochiKit.Signal.EXPORT=["connect","disconnect","signal","disconnectAll","disconnectAllTo"]; -MochiKit.Signal.__new__=function(win){ -var m=MochiKit.Base; -this._document=document; -this._window=win; -this._lock=false; -this._dirty=false; -try{ -this.connect(window,"onunload",this._unloadCache); -} -catch(e){ -} -this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; -m.nameFunctions(this); -}; -MochiKit.Signal.__new__(this); -if(MochiKit.__export__){ -connect=MochiKit.Signal.connect; -disconnect=MochiKit.Signal.disconnect; -disconnectAll=MochiKit.Signal.disconnectAll; -signal=MochiKit.Signal.signal; -} -MochiKit.Base._exportSymbols(this,MochiKit.Signal); -MochiKit.Base._deps("Position",["Base","DOM","Style"]); -MochiKit.Position.NAME="MochiKit.Position"; -MochiKit.Position.VERSION="1.4.2"; -MochiKit.Position.__repr__=function(){ -return "["+this.NAME+" "+this.VERSION+"]"; -}; -MochiKit.Position.toString=function(){ -return this.__repr__(); -}; -MochiKit.Position.EXPORT_OK=[]; -MochiKit.Position.EXPORT=[]; -MochiKit.Base.update(MochiKit.Position,{includeScrollOffsets:false,prepare:function(){ -var _5bb=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0; -var _5bc=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0; -this.windowOffset=new MochiKit.Style.Coordinates(_5bb,_5bc); -},cumulativeOffset:function(_5bd){ -var _5be=0; -var _5bf=0; -do{ -_5be+=_5bd.offsetTop||0; -_5bf+=_5bd.offsetLeft||0; -_5bd=_5bd.offsetParent; -}while(_5bd); -return new MochiKit.Style.Coordinates(_5bf,_5be); -},realOffset:function(_5c0){ -var _5c1=0; -var _5c2=0; -do{ -_5c1+=_5c0.scrollTop||0; -_5c2+=_5c0.scrollLeft||0; -_5c0=_5c0.parentNode; -}while(_5c0); -return new MochiKit.Style.Coordinates(_5c2,_5c1); -},within:function(_5c3,x,y){ -if(this.includeScrollOffsets){ -return this.withinIncludingScrolloffsets(_5c3,x,y); -} -this.xcomp=x; -this.ycomp=y; -this.offset=this.cumulativeOffset(_5c3); -if(_5c3.style.position=="fixed"){ -this.offset.x+=this.windowOffset.x; -this.offset.y+=this.windowOffset.y; -} -return (y>=this.offset.y&&y=this.offset.x&&x=this.offset.y&&this.ycomp=this.offset.x&&this.xcomp"+el.innerHTML+"
    "; -},_roundTopCorners:function(el,_5f5,_5f6){ -var _5f7=this._createCorner(_5f6); -for(var i=0;i=0;i--){ -_5fc.appendChild(this._createCornerSlice(_5fa,_5fb,i,"bottom")); -} -el.style.paddingBottom=0; -el.appendChild(_5fc); -},_createCorner:function(_5fe){ -var dom=MochiKit.DOM; -return dom.DIV({style:{backgroundColor:_5fe.toString()}}); -},_createCornerSlice:function(_600,_601,n,_603){ -var _604=MochiKit.DOM.SPAN(); -var _605=_604.style; -_605.backgroundColor=_600.toString(); -_605.display="block"; -_605.height="1px"; -_605.overflow="hidden"; -_605.fontSize="1px"; -var _606=this._borderColor(_600,_601); -if(this.options.border&&n===0){ -_605.borderTopStyle="solid"; -_605.borderTopWidth="1px"; -_605.borderLeftWidth="0px"; -_605.borderRightWidth="0px"; -_605.borderBottomWidth="0px"; -_605.height="0px"; -_605.borderColor=_606.toString(); -}else{ -if(_606){ -_605.borderColor=_606.toString(); -_605.borderStyle="solid"; -_605.borderWidth="0px 1px"; -} -} -if(!this.options.compact&&(n==(this.options.numSlices-1))){ -_605.height="2px"; -} -this._setMargin(_604,n,_603); -this._setBorder(_604,n,_603); -return _604; -},_setOptions:function(_607){ -this.options={corners:"all",color:"fromElement",bgColor:"fromParent",blend:true,border:false,compact:false,__unstable__wrapElement:false}; -MochiKit.Base.update(this.options,_607); -this.options.numSlices=(this.options.compact?2:4); -},_whichSideTop:function(){ -var _608=this.options.corners; -if(this._hasString(_608,"all","top")){ -return ""; -} -var _609=(_608.indexOf("tl")!=-1); -var _60a=(_608.indexOf("tr")!=-1); -if(_609&&_60a){ -return ""; -} -if(_609){ -return "left"; -} -if(_60a){ -return "right"; -} -return ""; -},_whichSideBottom:function(){ -var _60b=this.options.corners; -if(this._hasString(_60b,"all","bottom")){ -return ""; -} -var _60c=(_60b.indexOf("bl")!=-1); -var _60d=(_60b.indexOf("br")!=-1); -if(_60c&&_60d){ -return ""; -} -if(_60c){ -return "left"; -} -if(_60d){ -return "right"; -} -return ""; -},_borderColor:function(_60e,_60f){ -if(_60e=="transparent"){ -return _60f; -}else{ -if(this.options.border){ -return this.options.border; -}else{ -if(this.options.blend){ -return _60f.blendedColor(_60e); -} -} -} -return ""; -},_setMargin:function(el,n,_612){ -var _613=this._marginSize(n)+"px"; -var _614=(_612=="top"?this._whichSideTop():this._whichSideBottom()); -var _615=el.style; -if(_614=="left"){ -_615.marginLeft=_613; -_615.marginRight="0px"; -}else{ -if(_614=="right"){ -_615.marginRight=_613; -_615.marginLeft="0px"; -}else{ -_615.marginLeft=_613; -_615.marginRight=_613; -} -} -},_setBorder:function(el,n,_618){ -var _619=this._borderSize(n)+"px"; -var _61a=(_618=="top"?this._whichSideTop():this._whichSideBottom()); -var _61b=el.style; -if(_61a=="left"){ -_61b.borderLeftWidth=_619; -_61b.borderRightWidth="0px"; -}else{ -if(_61a=="right"){ -_61b.borderRightWidth=_619; -_61b.borderLeftWidth="0px"; -}else{ -_61b.borderLeftWidth=_619; -_61b.borderRightWidth=_619; -} -} -},_marginSize:function(n){ -if(this.isTransparent){ -return 0; -} -var o=this.options; -if(o.compact&&o.blend){ -var _61e=[1,0]; -return _61e[n]; -}else{ -if(o.compact){ -var _61f=[2,1]; -return _61f[n]; -}else{ -if(o.blend){ -var _620=[3,2,1,0]; -return _620[n]; -}else{ -var _621=[5,3,2,1]; -return _621[n]; -} -} -} -},_borderSize:function(n){ -var o=this.options; -var _624; -if(o.compact&&(o.blend||this.isTransparent)){ -return 1; -}else{ -if(o.compact){ -_624=[1,0]; -}else{ -if(o.blend){ -_624=[2,1,1,1]; -}else{ -if(o.border){ -_624=[0,2,0,0]; -}else{ -if(this.isTransparent){ -_624=[5,3,2,1]; -}else{ -return 0; -} -} -} -} -} -return _624[n]; -},_hasString:function(str){ -for(var i=1;i=(_651||i)){ -_651=i; -} -},this.effects); -_64d=_651||_64d; -break; -case "break": -ma(function(e){ -e.finalize(); -},this.effects); -break; -} -_64c.startOn+=_64d; -_64c.finishOn+=_64d; -if(!_64c.options.queue.limit||this.effects.length<_64c.options.queue.limit){ -this.effects.push(_64c); -} -if(!this.interval){ -this.interval=this.startLoop(MochiKit.Base.bind(this.loop,this),40); -} -},startLoop:function(func,_656){ -return setInterval(func,_656); -},remove:function(_657){ -this.effects=MochiKit.Base.filter(function(e){ -return e!=_657; -},this.effects); -if(!this.effects.length){ -this.stopLoop(this.interval); -this.interval=null; -} -},stopLoop:function(_659){ -clearInterval(_659); -},loop:function(){ -var _65a=new Date().getTime(); -MochiKit.Base.map(function(_65b){ -_65b.loop(_65a); -},this.effects); -}}); -MochiKit.Visual.Queues={instances:{},get:function(_65c){ -if(typeof (_65c)!="string"){ -return _65c; -} -if(!this.instances[_65c]){ -this.instances[_65c]=new MochiKit.Visual.ScopedQueue(); -} -return this.instances[_65c]; -}}; -MochiKit.Visual.Queue=MochiKit.Visual.Queues.get("global"); -MochiKit.Visual.DefaultOptions={transition:MochiKit.Visual.Transitions.sinoidal,duration:1,fps:25,sync:false,from:0,to:1,delay:0,queue:"parallel"}; -MochiKit.Visual.Base=function(){ -}; -MochiKit.Visual.Base.prototype={__class__:MochiKit.Visual.Base,start:function(_65d){ -var v=MochiKit.Visual; -this.options=MochiKit.Base.setdefault(_65d,v.DefaultOptions); -this.currentFrame=0; -this.state="idle"; -this.startOn=this.options.delay*1000; -this.finishOn=this.startOn+(this.options.duration*1000); -this.event("beforeStart"); -if(!this.options.sync){ -v.Queues.get(typeof (this.options.queue)=="string"?"global":this.options.queue.scope).add(this); -} -},loop:function(_65f){ -if(_65f>=this.startOn){ -if(_65f>=this.finishOn){ -return this.finalize(); -} -var pos=(_65f-this.startOn)/(this.finishOn-this.startOn); -var _661=Math.round(pos*this.options.fps*this.options.duration); -if(_661>this.currentFrame){ -this.render(pos); -this.currentFrame=_661; -} -} -},render:function(pos){ -if(this.state=="idle"){ -this.state="running"; -this.event("beforeSetup"); -this.setup(); -this.event("afterSetup"); -} -if(this.state=="running"){ -if(this.options.transition){ -pos=this.options.transition(pos); -} -pos*=(this.options.to-this.options.from); -pos+=this.options.from; -this.event("beforeUpdate"); -this.update(pos); -this.event("afterUpdate"); -} -},cancel:function(){ -if(!this.options.sync){ -MochiKit.Visual.Queues.get(typeof (this.options.queue)=="string"?"global":this.options.queue.scope).remove(this); -} -this.state="finished"; -},finalize:function(){ -this.render(1); -this.cancel(); -this.event("beforeFinish"); -this.finish(); -this.event("afterFinish"); -},setup:function(){ -},finish:function(){ -},update:function(_663){ -},event:function(_664){ -if(this.options[_664+"Internal"]){ -this.options[_664+"Internal"](this); -} -if(this.options[_664]){ -this.options[_664](this); -} -},repr:function(){ -return "["+this.__class__.NAME+", options:"+MochiKit.Base.repr(this.options)+"]"; -}}; -MochiKit.Visual.Parallel=function(_665,_666){ -var cls=arguments.callee; -if(!(this instanceof cls)){ -return new cls(_665,_666); -} -this.__init__(_665,_666); -}; -MochiKit.Visual.Parallel.prototype=new MochiKit.Visual.Base(); -MochiKit.Base.update(MochiKit.Visual.Parallel.prototype,{__class__:MochiKit.Visual.Parallel,__init__:function(_668,_669){ -this.effects=_668||[]; -this.start(_669); -},update:function(_66a){ -MochiKit.Base.map(function(_66b){ -_66b.render(_66a); -},this.effects); -},finish:function(){ -MochiKit.Base.map(function(_66c){ -_66c.finalize(); -},this.effects); -}}); -MochiKit.Visual.Sequence=function(_66d,_66e){ -var cls=arguments.callee; -if(!(this instanceof cls)){ -return new cls(_66d,_66e); -} -this.__init__(_66d,_66e); -}; -MochiKit.Visual.Sequence.prototype=new MochiKit.Visual.Base(); -MochiKit.Base.update(MochiKit.Visual.Sequence.prototype,{__class__:MochiKit.Visual.Sequence,__init__:function(_670,_671){ -var defs={transition:MochiKit.Visual.Transitions.linear,duration:0}; -this.effects=_670||[]; -MochiKit.Base.map(function(_673){ -defs.duration+=_673.options.duration; -},this.effects); -MochiKit.Base.setdefault(_671,defs); -this.start(_671); -},update:function(_674){ -var time=_674*this.options.duration; -for(var i=0;i0){ -this.fontSize=parseFloat(_694); -this.fontSizeType=_695; -} -},this),["em","px","%"]); -this.factor=(this.options.scaleTo-this.options.scaleFrom)/100; -if(/^content/.test(this.options.scaleMode)){ -this.dims=[this.element.scrollHeight,this.element.scrollWidth]; -}else{ -if(this.options.scaleMode=="box"){ -this.dims=[this.element.offsetHeight,this.element.offsetWidth]; -}else{ -this.dims=[this.options.scaleMode.originalHeight,this.options.scaleMode.originalWidth]; -} -} -},update:function(_696){ -var _697=(this.options.scaleFrom/100)+(this.factor*_696); -if(this.options.scaleContent&&this.fontSize){ -MochiKit.Style.setStyle(this.element,{fontSize:this.fontSize*_697+this.fontSizeType}); -} -this.setDimensions(this.dims[0]*_697,this.dims[1]*_697); -},finish:function(){ -if(this.restoreAfterFinish){ -MochiKit.Style.setStyle(this.element,this.originalStyle); -} -},setDimensions:function(_698,_699){ -var d={}; -var r=Math.round; -if(/MSIE/.test(navigator.userAgent)){ -r=Math.ceil; -} -if(this.options.scaleX){ -d.width=r(_699)+"px"; -} -if(this.options.scaleY){ -d.height=r(_698)+"px"; -} -if(this.options.scaleFromCenter){ -var topd=(_698-this.dims[0])/2; -var _69d=(_699-this.dims[1])/2; -if(this.elementPositioning=="absolute"){ -if(this.options.scaleY){ -d.top=this.originalTop-topd+"px"; -} -if(this.options.scaleX){ -d.left=this.originalLeft-_69d+"px"; -} -}else{ -if(this.options.scaleY){ -d.top=-topd+"px"; -} -if(this.options.scaleX){ -d.left=-_69d+"px"; -} -} -} -MochiKit.Style.setStyle(this.element,d); -}}); -MochiKit.Visual.Highlight=function(_69e,_69f){ -var cls=arguments.callee; -if(!(this instanceof cls)){ -return new cls(_69e,_69f); -} -this.__init__(_69e,_69f); -}; -MochiKit.Visual.Highlight.prototype=new MochiKit.Visual.Base(); -MochiKit.Base.update(MochiKit.Visual.Highlight.prototype,{__class__:MochiKit.Visual.Highlight,__init__:function(_6a1,_6a2){ -this.element=MochiKit.DOM.getElement(_6a1); -_6a2=MochiKit.Base.update({startcolor:"#ffff99"},_6a2); -this.start(_6a2); -},setup:function(){ -var b=MochiKit.Base; -var s=MochiKit.Style; -if(s.getStyle(this.element,"display")=="none"){ -this.cancel(); -return; -} -this.oldStyle={backgroundImage:s.getStyle(this.element,"background-image")}; -s.setStyle(this.element,{backgroundImage:"none"}); -if(!this.options.endcolor){ -this.options.endcolor=MochiKit.Color.Color.fromBackground(this.element).toHexString(); -} -if(b.isUndefinedOrNull(this.options.restorecolor)){ -this.options.restorecolor=s.getStyle(this.element,"background-color"); -} -this._base=b.map(b.bind(function(i){ -return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16); -},this),[0,1,2]); -this._delta=b.map(b.bind(function(i){ -return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i]; -},this),[0,1,2]); -},update:function(_6a7){ -var m="#"; -MochiKit.Base.map(MochiKit.Base.bind(function(i){ -m+=MochiKit.Color.toColorPart(Math.round(this._base[i]+this._delta[i]*_6a7)); -},this),[0,1,2]); -MochiKit.Style.setStyle(this.element,{backgroundColor:m}); -},finish:function(){ -MochiKit.Style.setStyle(this.element,MochiKit.Base.update(this.oldStyle,{backgroundColor:this.options.restorecolor})); -}}); -MochiKit.Visual.ScrollTo=function(_6aa,_6ab){ -var cls=arguments.callee; -if(!(this instanceof cls)){ -return new cls(_6aa,_6ab); -} -this.__init__(_6aa,_6ab); -}; -MochiKit.Visual.ScrollTo.prototype=new MochiKit.Visual.Base(); -MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype,{__class__:MochiKit.Visual.ScrollTo,__init__:function(_6ad,_6ae){ -this.element=MochiKit.DOM.getElement(_6ad); -this.start(_6ae); -},setup:function(){ -var p=MochiKit.Position; -p.prepare(); -var _6b0=p.cumulativeOffset(this.element); -if(this.options.offset){ -_6b0.y+=this.options.offset; -} -var max; -if(window.innerHeight){ -max=window.innerHeight-window.height; -}else{ -if(document.documentElement&&document.documentElement.clientHeight){ -max=document.documentElement.clientHeight-document.body.scrollHeight; -}else{ -if(document.body){ -max=document.body.clientHeight-document.body.scrollHeight; -} -} -} -this.scrollStart=p.windowOffset.y; -this.delta=(_6b0.y>max?max:_6b0.y)-this.scrollStart; -},update:function(_6b2){ -var p=MochiKit.Position; -p.prepare(); -window.scrollTo(p.windowOffset.x,this.scrollStart+(_6b2*this.delta)); -}}); -MochiKit.Visual.CSS_LENGTH=/^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; -MochiKit.Visual.Morph=function(_6b4,_6b5){ -var cls=arguments.callee; -if(!(this instanceof cls)){ -return new cls(_6b4,_6b5); -} -this.__init__(_6b4,_6b5); -}; -MochiKit.Visual.Morph.prototype=new MochiKit.Visual.Base(); -MochiKit.Base.update(MochiKit.Visual.Morph.prototype,{__class__:MochiKit.Visual.Morph,__init__:function(_6b7,_6b8){ -this.element=MochiKit.DOM.getElement(_6b7); -this.start(_6b8); -},setup:function(){ -var b=MochiKit.Base; -var _6ba=this.options.style; -this.styleStart={}; -this.styleEnd={}; -this.units={}; -var _6bb,unit; -for(var s in _6ba){ -_6bb=_6ba[s]; -s=b.camelize(s); -if(MochiKit.Visual.CSS_LENGTH.test(_6bb)){ -var _6be=_6bb.match(/^([\+\-]?[0-9\.]+)(.*)$/); -_6bb=parseFloat(_6be[1]); -unit=(_6be.length==3)?_6be[2]:null; -this.styleEnd[s]=_6bb; -this.units[s]=unit; -_6bb=MochiKit.Style.getStyle(this.element,s); -_6be=_6bb.match(/^([\+\-]?[0-9\.]+)(.*)$/); -_6bb=parseFloat(_6be[1]); -this.styleStart[s]=_6bb; -}else{ -if(/[Cc]olor$/.test(s)){ -var c=MochiKit.Color.Color; -_6bb=c.fromString(_6bb); -if(_6bb){ -this.units[s]="color"; -this.styleEnd[s]=_6bb.toHexString(); -_6bb=MochiKit.Style.getStyle(this.element,s); -this.styleStart[s]=c.fromString(_6bb).toHexString(); -this.styleStart[s]=b.map(b.bind(function(i){ -return parseInt(this.styleStart[s].slice(i*2+1,i*2+3),16); -},this),[0,1,2]); -this.styleEnd[s]=b.map(b.bind(function(i){ -return parseInt(this.styleEnd[s].slice(i*2+1,i*2+3),16); -},this),[0,1,2]); -} -}else{ -this.element.style[s]=_6bb; -} -} -} -},update:function(_6c2){ -var _6c3; -for(var s in this.styleStart){ -if(this.units[s]=="color"){ -var m="#"; -var _6c6=this.styleStart[s]; -var end=this.styleEnd[s]; -MochiKit.Base.map(MochiKit.Base.bind(function(i){ -m+=MochiKit.Color.toColorPart(Math.round(_6c6[i]+(end[i]-_6c6[i])*_6c2)); -},this),[0,1,2]); -this.element.style[s]=m; -}else{ -_6c3=this.styleStart[s]+Math.round((this.styleEnd[s]-this.styleStart[s])*_6c2*1000)/1000+this.units[s]; -this.element.style[s]=_6c3; -} -} -}}); -MochiKit.Visual.fade=function(_6c9,_6ca){ -var s=MochiKit.Style; -var _6cc=s.getStyle(_6c9,"opacity"); -_6ca=MochiKit.Base.update({from:s.getStyle(_6c9,"opacity")||1,to:0,afterFinishInternal:function(_6cd){ -if(_6cd.options.to!==0){ -return; -} -s.hideElement(_6cd.element); -s.setStyle(_6cd.element,{"opacity":_6cc}); -}},_6ca); -return new MochiKit.Visual.Opacity(_6c9,_6ca); -}; -MochiKit.Visual.appear=function(_6ce,_6cf){ -var s=MochiKit.Style; -var v=MochiKit.Visual; -_6cf=MochiKit.Base.update({from:(s.getStyle(_6ce,"display")=="none"?0:s.getStyle(_6ce,"opacity")||0),to:1,afterFinishInternal:function(_6d2){ -v.forceRerendering(_6d2.element); -},beforeSetupInternal:function(_6d3){ -s.setStyle(_6d3.element,{"opacity":_6d3.options.from}); -s.showElement(_6d3.element); -}},_6cf); -return new v.Opacity(_6ce,_6cf); -}; -MochiKit.Visual.puff=function(_6d4,_6d5){ -var s=MochiKit.Style; -var v=MochiKit.Visual; -_6d4=MochiKit.DOM.getElement(_6d4); -var _6d8=MochiKit.Style.getElementDimensions(_6d4,true); -var _6d9={position:s.getStyle(_6d4,"position"),top:_6d4.style.top,left:_6d4.style.left,width:_6d4.style.width,height:_6d4.style.height,opacity:s.getStyle(_6d4,"opacity")}; -_6d5=MochiKit.Base.update({beforeSetupInternal:function(_6da){ -MochiKit.Position.absolutize(_6da.effects[0].element); -},afterFinishInternal:function(_6db){ -s.hideElement(_6db.effects[0].element); -s.setStyle(_6db.effects[0].element,_6d9); -},scaleContent:true,scaleFromCenter:true},_6d5); -return new v.Parallel([new v.Scale(_6d4,200,{sync:true,scaleFromCenter:_6d5.scaleFromCenter,scaleMode:{originalHeight:_6d8.h,originalWidth:_6d8.w},scaleContent:_6d5.scaleContent,restoreAfterFinish:true}),new v.Opacity(_6d4,{sync:true,to:0})],_6d5); -}; -MochiKit.Visual.blindUp=function(_6dc,_6dd){ -var d=MochiKit.DOM; -var s=MochiKit.Style; -_6dc=d.getElement(_6dc); -var _6e0=s.getElementDimensions(_6dc,true); -var _6e1=s.makeClipping(_6dc); -_6dd=MochiKit.Base.update({scaleContent:false,scaleX:false,scaleMode:{originalHeight:_6e0.h,originalWidth:_6e0.w},restoreAfterFinish:true,afterFinishInternal:function(_6e2){ -s.hideElement(_6e2.element); -s.undoClipping(_6e2.element,_6e1); -}},_6dd); -return new MochiKit.Visual.Scale(_6dc,0,_6dd); -}; -MochiKit.Visual.blindDown=function(_6e3,_6e4){ -var d=MochiKit.DOM; -var s=MochiKit.Style; -_6e3=d.getElement(_6e3); -var _6e7=s.getElementDimensions(_6e3,true); -var _6e8; -_6e4=MochiKit.Base.update({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:_6e7.h,originalWidth:_6e7.w},restoreAfterFinish:true,afterSetupInternal:function(_6e9){ -_6e8=s.makeClipping(_6e9.element); -s.setStyle(_6e9.element,{height:"0px"}); -s.showElement(_6e9.element); -},afterFinishInternal:function(_6ea){ -s.undoClipping(_6ea.element,_6e8); -}},_6e4); -return new MochiKit.Visual.Scale(_6e3,100,_6e4); -}; -MochiKit.Visual.switchOff=function(_6eb,_6ec){ -var d=MochiKit.DOM; -var s=MochiKit.Style; -_6eb=d.getElement(_6eb); -var _6ef=s.getElementDimensions(_6eb,true); -var _6f0=s.getStyle(_6eb,"opacity"); -var _6f1; -_6ec=MochiKit.Base.update({duration:0.7,restoreAfterFinish:true,beforeSetupInternal:function(_6f2){ -s.makePositioned(_6eb); -_6f1=s.makeClipping(_6eb); -},afterFinishInternal:function(_6f3){ -s.hideElement(_6eb); -s.undoClipping(_6eb,_6f1); -s.undoPositioned(_6eb); -s.setStyle(_6eb,{"opacity":_6f0}); -}},_6ec); -var v=MochiKit.Visual; -return new v.Sequence([new v.appear(_6eb,{sync:true,duration:0.57*_6ec.duration,from:0,transition:v.Transitions.flicker}),new v.Scale(_6eb,1,{sync:true,duration:0.43*_6ec.duration,scaleFromCenter:true,scaleX:false,scaleMode:{originalHeight:_6ef.h,originalWidth:_6ef.w},scaleContent:false,restoreAfterFinish:true})],_6ec); -}; -MochiKit.Visual.dropOut=function(_6f5,_6f6){ -var d=MochiKit.DOM; -var s=MochiKit.Style; -_6f5=d.getElement(_6f5); -var _6f9={top:s.getStyle(_6f5,"top"),left:s.getStyle(_6f5,"left"),opacity:s.getStyle(_6f5,"opacity")}; -_6f6=MochiKit.Base.update({duration:0.5,distance:100,beforeSetupInternal:function(_6fa){ -s.makePositioned(_6fa.effects[0].element); -},afterFinishInternal:function(_6fb){ -s.hideElement(_6fb.effects[0].element); -s.undoPositioned(_6fb.effects[0].element); -s.setStyle(_6fb.effects[0].element,_6f9); -}},_6f6); -var v=MochiKit.Visual; -return new v.Parallel([new v.Move(_6f5,{x:0,y:_6f6.distance,sync:true}),new v.Opacity(_6f5,{sync:true,to:0})],_6f6); -}; -MochiKit.Visual.shake=function(_6fd,_6fe){ -var d=MochiKit.DOM; -var v=MochiKit.Visual; -var s=MochiKit.Style; -_6fd=d.getElement(_6fd); -var _702={top:s.getStyle(_6fd,"top"),left:s.getStyle(_6fd,"left")}; -_6fe=MochiKit.Base.update({duration:0.5,afterFinishInternal:function(_703){ -s.undoPositioned(_6fd); -s.setStyle(_6fd,_702); -}},_6fe); -return new v.Sequence([new v.Move(_6fd,{sync:true,duration:0.1*_6fe.duration,x:20,y:0}),new v.Move(_6fd,{sync:true,duration:0.2*_6fe.duration,x:-40,y:0}),new v.Move(_6fd,{sync:true,duration:0.2*_6fe.duration,x:40,y:0}),new v.Move(_6fd,{sync:true,duration:0.2*_6fe.duration,x:-40,y:0}),new v.Move(_6fd,{sync:true,duration:0.2*_6fe.duration,x:40,y:0}),new v.Move(_6fd,{sync:true,duration:0.1*_6fe.duration,x:-20,y:0})],_6fe); -}; -MochiKit.Visual.slideDown=function(_704,_705){ -var d=MochiKit.DOM; -var b=MochiKit.Base; -var s=MochiKit.Style; -_704=d.getElement(_704); -if(!_704.firstChild){ -throw new Error("MochiKit.Visual.slideDown must be used on a element with a child"); -} -d.removeEmptyTextNodes(_704); -var _709=s.getStyle(_704.firstChild,"bottom")||0; -var _70a=s.getElementDimensions(_704,true); -var _70b; -_705=b.update({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:_70a.h,originalWidth:_70a.w},restoreAfterFinish:true,afterSetupInternal:function(_70c){ -s.makePositioned(_70c.element); -s.makePositioned(_70c.element.firstChild); -if(/Opera/.test(navigator.userAgent)){ -s.setStyle(_70c.element,{top:""}); -} -_70b=s.makeClipping(_70c.element); -s.setStyle(_70c.element,{height:"0px"}); -s.showElement(_70c.element); -},afterUpdateInternal:function(_70d){ -var _70e=s.getElementDimensions(_70d.element,true); -s.setStyle(_70d.element.firstChild,{bottom:(_70d.dims[0]-_70e.h)+"px"}); -},afterFinishInternal:function(_70f){ -s.undoClipping(_70f.element,_70b); -if(/MSIE/.test(navigator.userAgent)){ -s.undoPositioned(_70f.element); -s.undoPositioned(_70f.element.firstChild); -}else{ -s.undoPositioned(_70f.element.firstChild); -s.undoPositioned(_70f.element); -} -s.setStyle(_70f.element.firstChild,{bottom:_709}); -}},_705); -return new MochiKit.Visual.Scale(_704,100,_705); -}; -MochiKit.Visual.slideUp=function(_710,_711){ -var d=MochiKit.DOM; -var b=MochiKit.Base; -var s=MochiKit.Style; -_710=d.getElement(_710); -if(!_710.firstChild){ -throw new Error("MochiKit.Visual.slideUp must be used on a element with a child"); -} -d.removeEmptyTextNodes(_710); -var _715=s.getStyle(_710.firstChild,"bottom"); -var _716=s.getElementDimensions(_710,true); -var _717; -_711=b.update({scaleContent:false,scaleX:false,scaleMode:{originalHeight:_716.h,originalWidth:_716.w},scaleFrom:100,restoreAfterFinish:true,beforeStartInternal:function(_718){ -s.makePositioned(_718.element); -s.makePositioned(_718.element.firstChild); -if(/Opera/.test(navigator.userAgent)){ -s.setStyle(_718.element,{top:""}); -} -_717=s.makeClipping(_718.element); -s.showElement(_718.element); -},afterUpdateInternal:function(_719){ -var _71a=s.getElementDimensions(_719.element,true); -s.setStyle(_719.element.firstChild,{bottom:(_719.dims[0]-_71a.h)+"px"}); -},afterFinishInternal:function(_71b){ -s.hideElement(_71b.element); -s.undoClipping(_71b.element,_717); -s.undoPositioned(_71b.element.firstChild); -s.undoPositioned(_71b.element); -s.setStyle(_71b.element.firstChild,{bottom:_715}); -}},_711); -return new MochiKit.Visual.Scale(_710,0,_711); -}; -MochiKit.Visual.squish=function(_71c,_71d){ -var d=MochiKit.DOM; -var b=MochiKit.Base; -var s=MochiKit.Style; -var _721=s.getElementDimensions(_71c,true); -var _722; -_71d=b.update({restoreAfterFinish:true,scaleMode:{originalHeight:_721.w,originalWidth:_721.h},beforeSetupInternal:function(_723){ -_722=s.makeClipping(_723.element); -},afterFinishInternal:function(_724){ -s.hideElement(_724.element); -s.undoClipping(_724.element,_722); -}},_71d); -return new MochiKit.Visual.Scale(_71c,/Opera/.test(navigator.userAgent)?1:0,_71d); -}; -MochiKit.Visual.grow=function(_725,_726){ -var d=MochiKit.DOM; -var v=MochiKit.Visual; -var s=MochiKit.Style; -_725=d.getElement(_725); -_726=MochiKit.Base.update({direction:"center",moveTransition:v.Transitions.sinoidal,scaleTransition:v.Transitions.sinoidal,opacityTransition:v.Transitions.full,scaleContent:true,scaleFromCenter:false},_726); -var _72a={top:_725.style.top,left:_725.style.left,height:_725.style.height,width:_725.style.width,opacity:s.getStyle(_725,"opacity")}; -var dims=s.getElementDimensions(_725,true); -var _72c,_72d; -var _72e,_72f; -switch(_726.direction){ -case "top-left": -_72c=_72d=_72e=_72f=0; -break; -case "top-right": -_72c=dims.w; -_72d=_72f=0; -_72e=-dims.w; -break; -case "bottom-left": -_72c=_72e=0; -_72d=dims.h; -_72f=-dims.h; -break; -case "bottom-right": -_72c=dims.w; -_72d=dims.h; -_72e=-dims.w; -_72f=-dims.h; -break; -case "center": -_72c=dims.w/2; -_72d=dims.h/2; -_72e=-dims.w/2; -_72f=-dims.h/2; -break; -} -var _730=MochiKit.Base.update({beforeSetupInternal:function(_731){ -s.setStyle(_731.effects[0].element,{height:"0px"}); -s.showElement(_731.effects[0].element); -},afterFinishInternal:function(_732){ -s.undoClipping(_732.effects[0].element); -s.undoPositioned(_732.effects[0].element); -s.setStyle(_732.effects[0].element,_72a); -}},_726); -return new v.Move(_725,{x:_72c,y:_72d,duration:0.01,beforeSetupInternal:function(_733){ -s.hideElement(_733.element); -s.makeClipping(_733.element); -s.makePositioned(_733.element); -},afterFinishInternal:function(_734){ -new v.Parallel([new v.Opacity(_734.element,{sync:true,to:1,from:0,transition:_726.opacityTransition}),new v.Move(_734.element,{x:_72e,y:_72f,sync:true,transition:_726.moveTransition}),new v.Scale(_734.element,100,{scaleMode:{originalHeight:dims.h,originalWidth:dims.w},sync:true,scaleFrom:/Opera/.test(navigator.userAgent)?1:0,transition:_726.scaleTransition,scaleContent:_726.scaleContent,scaleFromCenter:_726.scaleFromCenter,restoreAfterFinish:true})],_730); -}}); -}; -MochiKit.Visual.shrink=function(_735,_736){ -var d=MochiKit.DOM; -var v=MochiKit.Visual; -var s=MochiKit.Style; -_735=d.getElement(_735); -_736=MochiKit.Base.update({direction:"center",moveTransition:v.Transitions.sinoidal,scaleTransition:v.Transitions.sinoidal,opacityTransition:v.Transitions.none,scaleContent:true,scaleFromCenter:false},_736); -var _73a={top:_735.style.top,left:_735.style.left,height:_735.style.height,width:_735.style.width,opacity:s.getStyle(_735,"opacity")}; -var dims=s.getElementDimensions(_735,true); -var _73c,_73d; -switch(_736.direction){ -case "top-left": -_73c=_73d=0; -break; -case "top-right": -_73c=dims.w; -_73d=0; -break; -case "bottom-left": -_73c=0; -_73d=dims.h; -break; -case "bottom-right": -_73c=dims.w; -_73d=dims.h; -break; -case "center": -_73c=dims.w/2; -_73d=dims.h/2; -break; -} -var _73e; -var _73f=MochiKit.Base.update({beforeStartInternal:function(_740){ -s.makePositioned(_740.effects[0].element); -_73e=s.makeClipping(_740.effects[0].element); -},afterFinishInternal:function(_741){ -s.hideElement(_741.effects[0].element); -s.undoClipping(_741.effects[0].element,_73e); -s.undoPositioned(_741.effects[0].element); -s.setStyle(_741.effects[0].element,_73a); -}},_736); -return new v.Parallel([new v.Opacity(_735,{sync:true,to:0,from:1,transition:_736.opacityTransition}),new v.Scale(_735,/Opera/.test(navigator.userAgent)?1:0,{scaleMode:{originalHeight:dims.h,originalWidth:dims.w},sync:true,transition:_736.scaleTransition,scaleContent:_736.scaleContent,scaleFromCenter:_736.scaleFromCenter,restoreAfterFinish:true}),new v.Move(_735,{x:_73c,y:_73d,sync:true,transition:_736.moveTransition})],_73f); -}; -MochiKit.Visual.pulsate=function(_742,_743){ -var d=MochiKit.DOM; -var v=MochiKit.Visual; -var b=MochiKit.Base; -var _747=MochiKit.Style.getStyle(_742,"opacity"); -_743=b.update({duration:3,from:0,afterFinishInternal:function(_748){ -MochiKit.Style.setStyle(_748.element,{"opacity":_747}); -}},_743); -var _749=_743.transition||v.Transitions.sinoidal; -_743.transition=function(pos){ -return _749(1-v.Transitions.pulse(pos,_743.pulses)); -}; -return new v.Opacity(_742,_743); -}; -MochiKit.Visual.fold=function(_74b,_74c){ -var d=MochiKit.DOM; -var v=MochiKit.Visual; -var s=MochiKit.Style; -_74b=d.getElement(_74b); -var _750=s.getElementDimensions(_74b,true); -var _751={top:_74b.style.top,left:_74b.style.left,width:_74b.style.width,height:_74b.style.height}; -var _752=s.makeClipping(_74b); -_74c=MochiKit.Base.update({scaleContent:false,scaleX:false,scaleMode:{originalHeight:_750.h,originalWidth:_750.w},afterFinishInternal:function(_753){ -new v.Scale(_74b,1,{scaleContent:false,scaleY:false,scaleMode:{originalHeight:_750.h,originalWidth:_750.w},afterFinishInternal:function(_754){ -s.hideElement(_754.element); -s.undoClipping(_754.element,_752); -s.setStyle(_754.element,_751); -}}); -}},_74c); -return new v.Scale(_74b,5,_74c); -}; -MochiKit.Visual.Color=MochiKit.Color.Color; -MochiKit.Visual.getElementsComputedStyle=MochiKit.DOM.computedStyle; -MochiKit.Visual.__new__=function(){ -var m=MochiKit.Base; -m.nameFunctions(this); -this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; -}; -MochiKit.Visual.EXPORT=["roundElement","roundClass","tagifyText","multiple","toggle","Parallel","Sequence","Opacity","Move","Scale","Highlight","ScrollTo","Morph","fade","appear","puff","blindUp","blindDown","switchOff","dropOut","shake","slideDown","slideUp","squish","grow","shrink","pulsate","fold"]; -MochiKit.Visual.EXPORT_OK=["Base","PAIRS"]; -MochiKit.Visual.__new__(); -MochiKit.Base._exportSymbols(this,MochiKit.Visual); -MochiKit.Base._deps("DragAndDrop",["Base","Iter","DOM","Signal","Visual","Position"]); -MochiKit.DragAndDrop.NAME="MochiKit.DragAndDrop"; -MochiKit.DragAndDrop.VERSION="1.4.2"; -MochiKit.DragAndDrop.__repr__=function(){ -return "["+this.NAME+" "+this.VERSION+"]"; -}; -MochiKit.DragAndDrop.toString=function(){ -return this.__repr__(); -}; -MochiKit.DragAndDrop.EXPORT=["Droppable","Draggable"]; -MochiKit.DragAndDrop.EXPORT_OK=["Droppables","Draggables"]; -MochiKit.DragAndDrop.Droppables={drops:[],remove:function(_756){ -this.drops=MochiKit.Base.filter(function(d){ -return d.element!=MochiKit.DOM.getElement(_756); -},this.drops); -},register:function(drop){ -this.drops.push(drop); -},unregister:function(drop){ -this.drops=MochiKit.Base.filter(function(d){ -return d!=drop; -},this.drops); -},prepare:function(_75b){ -MochiKit.Base.map(function(drop){ -if(drop.isAccepted(_75b)){ -if(drop.options.activeclass){ -MochiKit.DOM.addElementClass(drop.element,drop.options.activeclass); -} -drop.options.onactive(drop.element,_75b); -} -},this.drops); -},findDeepestChild:function(_75d){ -deepest=_75d[0]; -for(i=1;i<_75d.length;++i){ -if(MochiKit.DOM.isChildNode(_75d[i].element,deepest.element)){ -deepest=_75d[i]; -} -} -return deepest; -},show:function(_75e,_75f){ -if(!this.drops.length){ -return; -} -var _760=[]; -if(this.last_active){ -this.last_active.deactivate(); -} -MochiKit.Iter.forEach(this.drops,function(drop){ -if(drop.isAffected(_75e,_75f)){ -_760.push(drop); -} -}); -if(_760.length>0){ -drop=this.findDeepestChild(_760); -MochiKit.Position.within(drop.element,_75e.page.x,_75e.page.y); -drop.options.onhover(_75f,drop.element,MochiKit.Position.overlap(drop.options.overlap,drop.element)); -drop.activate(); -} -},fire:function(_762,_763){ -if(!this.last_active){ -return; -} -MochiKit.Position.prepare(); -if(this.last_active.isAffected(_762.mouse(),_763)){ -this.last_active.options.ondrop(_763,this.last_active.element,_762); -} -},reset:function(_764){ -MochiKit.Base.map(function(drop){ -if(drop.options.activeclass){ -MochiKit.DOM.removeElementClass(drop.element,drop.options.activeclass); -} -drop.options.ondesactive(drop.element,_764); -},this.drops); -if(this.last_active){ -this.last_active.deactivate(); -} -}}; -MochiKit.DragAndDrop.Droppable=function(_766,_767){ -var cls=arguments.callee; -if(!(this instanceof cls)){ -return new cls(_766,_767); -} -this.__init__(_766,_767); -}; -MochiKit.DragAndDrop.Droppable.prototype={__class__:MochiKit.DragAndDrop.Droppable,__init__:function(_769,_76a){ -var d=MochiKit.DOM; -var b=MochiKit.Base; -this.element=d.getElement(_769); -this.options=b.update({greedy:true,hoverclass:null,activeclass:null,hoverfunc:b.noop,accept:null,onactive:b.noop,ondesactive:b.noop,onhover:b.noop,ondrop:b.noop,containment:[],tree:false},_76a); -this.options._containers=[]; -b.map(MochiKit.Base.bind(function(c){ -this.options._containers.push(d.getElement(c)); -},this),this.options.containment); -MochiKit.Style.makePositioned(this.element); -MochiKit.DragAndDrop.Droppables.register(this); -},isContained:function(_76e){ -if(this.options._containers.length){ -var _76f; -if(this.options.tree){ -_76f=_76e.treeNode; -}else{ -_76f=_76e.parentNode; -} -return MochiKit.Iter.some(this.options._containers,function(c){ -return _76f==c; -}); -}else{ -return true; -} -},isAccepted:function(_771){ -return ((!this.options.accept)||MochiKit.Iter.some(this.options.accept,function(c){ -return MochiKit.DOM.hasElementClass(_771,c); -})); -},isAffected:function(_773,_774){ -return ((this.element!=_774)&&this.isContained(_774)&&this.isAccepted(_774)&&MochiKit.Position.within(this.element,_773.page.x,_773.page.y)); -},deactivate:function(){ -if(this.options.hoverclass){ -MochiKit.DOM.removeElementClass(this.element,this.options.hoverclass); -} -this.options.hoverfunc(this.element,false); -MochiKit.DragAndDrop.Droppables.last_active=null; -},activate:function(){ -if(this.options.hoverclass){ -MochiKit.DOM.addElementClass(this.element,this.options.hoverclass); -} -this.options.hoverfunc(this.element,true); -MochiKit.DragAndDrop.Droppables.last_active=this; -},destroy:function(){ -MochiKit.DragAndDrop.Droppables.unregister(this); -},repr:function(){ -return "["+this.__class__.NAME+", options:"+MochiKit.Base.repr(this.options)+"]"; -}}; -MochiKit.DragAndDrop.Draggables={drags:[],register:function(_775){ -if(this.drags.length===0){ -var conn=MochiKit.Signal.connect; -this.eventMouseUp=conn(document,"onmouseup",this,this.endDrag); -this.eventMouseMove=conn(document,"onmousemove",this,this.updateDrag); -this.eventKeypress=conn(document,"onkeypress",this,this.keyPress); -} -this.drags.push(_775); -},unregister:function(_777){ -this.drags=MochiKit.Base.filter(function(d){ -return d!=_777; -},this.drags); -if(this.drags.length===0){ -var disc=MochiKit.Signal.disconnect; -disc(this.eventMouseUp); -disc(this.eventMouseMove); -disc(this.eventKeypress); -} -},activate:function(_77a){ -window.focus(); -this.activeDraggable=_77a; -},deactivate:function(){ -this.activeDraggable=null; -},updateDrag:function(_77b){ -if(!this.activeDraggable){ -return; -} -var _77c=_77b.mouse(); -if(this._lastPointer&&(MochiKit.Base.repr(this._lastPointer.page)==MochiKit.Base.repr(_77c.page))){ -return; -} -this._lastPointer=_77c; -this.activeDraggable.updateDrag(_77b,_77c); -},endDrag:function(_77d){ -if(!this.activeDraggable){ -return; -} -this._lastPointer=null; -this.activeDraggable.endDrag(_77d); -this.activeDraggable=null; -},keyPress:function(_77e){ -if(this.activeDraggable){ -this.activeDraggable.keyPress(_77e); -} -},notify:function(_77f,_780,_781){ -MochiKit.Signal.signal(this,_77f,_780,_781); -}}; -MochiKit.DragAndDrop.Draggable=function(_782,_783){ -var cls=arguments.callee; -if(!(this instanceof cls)){ -return new cls(_782,_783); -} -this.__init__(_782,_783); -}; -MochiKit.DragAndDrop.Draggable.prototype={__class__:MochiKit.DragAndDrop.Draggable,__init__:function(_785,_786){ -var v=MochiKit.Visual; -var b=MochiKit.Base; -_786=b.update({handle:false,starteffect:function(_789){ -this._savedOpacity=MochiKit.Style.getStyle(_789,"opacity")||1; -new v.Opacity(_789,{duration:0.2,from:this._savedOpacity,to:0.7}); -},reverteffect:function(_78a,_78b,_78c){ -var dur=Math.sqrt(Math.abs(_78b^2)+Math.abs(_78c^2))*0.02; -return new v.Move(_78a,{x:-_78c,y:-_78b,duration:dur}); -},endeffect:function(_78e){ -new v.Opacity(_78e,{duration:0.2,from:0.7,to:this._savedOpacity}); -},onchange:b.noop,zindex:1000,revert:false,scroll:false,scrollSensitivity:20,scrollSpeed:15,snap:false},_786); -var d=MochiKit.DOM; -this.element=d.getElement(_785); -if(_786.handle&&(typeof (_786.handle)=="string")){ -this.handle=d.getFirstElementByTagAndClassName(null,_786.handle,this.element); -} -if(!this.handle){ -this.handle=d.getElement(_786.handle); -} -if(!this.handle){ -this.handle=this.element; -} -if(_786.scroll&&!_786.scroll.scrollTo&&!_786.scroll.outerHTML){ -_786.scroll=d.getElement(_786.scroll); -this._isScrollChild=MochiKit.DOM.isChildNode(this.element,_786.scroll); -} -MochiKit.Style.makePositioned(this.element); -this.delta=this.currentDelta(); -this.options=_786; -this.dragging=false; -this.eventMouseDown=MochiKit.Signal.connect(this.handle,"onmousedown",this,this.initDrag); -MochiKit.DragAndDrop.Draggables.register(this); -},destroy:function(){ -MochiKit.Signal.disconnect(this.eventMouseDown); -MochiKit.DragAndDrop.Draggables.unregister(this); -},currentDelta:function(){ -var s=MochiKit.Style.getStyle; -return [parseInt(s(this.element,"left")||"0"),parseInt(s(this.element,"top")||"0")]; -},initDrag:function(_791){ -if(!_791.mouse().button.left){ -return; -} -var src=_791.target(); -var _793=(src.tagName||"").toUpperCase(); -if(_793==="INPUT"||_793==="SELECT"||_793==="OPTION"||_793==="BUTTON"||_793==="TEXTAREA"){ -return; -} -if(this._revert){ -this._revert.cancel(); -this._revert=null; -} -var _794=_791.mouse(); -var pos=MochiKit.Position.cumulativeOffset(this.element); -this.offset=[_794.page.x-pos.x,_794.page.y-pos.y]; -MochiKit.DragAndDrop.Draggables.activate(this); -_791.stop(); -},startDrag:function(_796){ -this.dragging=true; -if(this.options.selectclass){ -MochiKit.DOM.addElementClass(this.element,this.options.selectclass); -} -if(this.options.zindex){ -this.originalZ=parseInt(MochiKit.Style.getStyle(this.element,"z-index")||"0"); -this.element.style.zIndex=this.options.zindex; -} -if(this.options.ghosting){ -this._clone=this.element.cloneNode(true); -this.ghostPosition=MochiKit.Position.absolutize(this.element); -this.element.parentNode.insertBefore(this._clone,this.element); -} -if(this.options.scroll){ -if(this.options.scroll==window){ -var _797=this._getWindowScroll(this.options.scroll); -this.originalScrollLeft=_797.left; -this.originalScrollTop=_797.top; -}else{ -this.originalScrollLeft=this.options.scroll.scrollLeft; -this.originalScrollTop=this.options.scroll.scrollTop; -} -} -MochiKit.DragAndDrop.Droppables.prepare(this.element); -MochiKit.DragAndDrop.Draggables.notify("start",this,_796); -if(this.options.starteffect){ -this.options.starteffect(this.element); -} -},updateDrag:function(_798,_799){ -if(!this.dragging){ -this.startDrag(_798); -} -MochiKit.Position.prepare(); -MochiKit.DragAndDrop.Droppables.show(_799,this.element); -MochiKit.DragAndDrop.Draggables.notify("drag",this,_798); -this.draw(_799); -this.options.onchange(this); -if(this.options.scroll){ -this.stopScrolling(); -var p,q; -if(this.options.scroll==window){ -var s=this._getWindowScroll(this.options.scroll); -p=new MochiKit.Style.Coordinates(s.left,s.top); -q=new MochiKit.Style.Coordinates(s.left+s.width,s.top+s.height); -}else{ -p=MochiKit.Position.page(this.options.scroll); -p.x+=this.options.scroll.scrollLeft; -p.y+=this.options.scroll.scrollTop; -p.x+=(window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0); -p.y+=(window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0); -q=new MochiKit.Style.Coordinates(p.x+this.options.scroll.offsetWidth,p.y+this.options.scroll.offsetHeight); -} -var _79d=[0,0]; -if(_799.page.x>(q.x-this.options.scrollSensitivity)){ -_79d[0]=_799.page.x-(q.x-this.options.scrollSensitivity); -}else{ -if(_799.page.x<(p.x+this.options.scrollSensitivity)){ -_79d[0]=_799.page.x-(p.x+this.options.scrollSensitivity); -} -} -if(_799.page.y>(q.y-this.options.scrollSensitivity)){ -_79d[1]=_799.page.y-(q.y-this.options.scrollSensitivity); -}else{ -if(_799.page.y<(p.y+this.options.scrollSensitivity)){ -_79d[1]=_799.page.y-(p.y+this.options.scrollSensitivity); -} -} -this.startScrolling(_79d); -} -if(/AppleWebKit/.test(navigator.appVersion)){ -window.scrollBy(0,0); -} -_798.stop(); -},finishDrag:function(_79e,_79f){ -var dr=MochiKit.DragAndDrop; -this.dragging=false; -if(this.options.selectclass){ -MochiKit.DOM.removeElementClass(this.element,this.options.selectclass); -} -if(this.options.ghosting){ -MochiKit.Position.relativize(this.element,this.ghostPosition); -MochiKit.DOM.removeElement(this._clone); -this._clone=null; -} -if(_79f){ -dr.Droppables.fire(_79e,this.element); -} -dr.Draggables.notify("end",this,_79e); -var _7a1=this.options.revert; -if(_7a1&&typeof (_7a1)=="function"){ -_7a1=_7a1(this.element); -} -var d=this.currentDelta(); -if(_7a1&&this.options.reverteffect){ -this._revert=this.options.reverteffect(this.element,d[1]-this.delta[1],d[0]-this.delta[0]); -}else{ -this.delta=d; -} -if(this.options.zindex){ -this.element.style.zIndex=this.originalZ; -} -if(this.options.endeffect){ -this.options.endeffect(this.element); -} -dr.Draggables.deactivate(); -dr.Droppables.reset(this.element); -},keyPress:function(_7a3){ -if(_7a3.key().string!="KEY_ESCAPE"){ -return; -} -this.finishDrag(_7a3,false); -_7a3.stop(); -},endDrag:function(_7a4){ -if(!this.dragging){ -return; -} -this.stopScrolling(); -this.finishDrag(_7a4,true); -_7a4.stop(); -},draw:function(_7a5){ -var pos=MochiKit.Position.cumulativeOffset(this.element); -var d=this.currentDelta(); -pos.x-=d[0]; -pos.y-=d[1]; -if(this.options.scroll&&(this.options.scroll!=window&&this._isScrollChild)){ -pos.x-=this.options.scroll.scrollLeft-this.originalScrollLeft; -pos.y-=this.options.scroll.scrollTop-this.originalScrollTop; -} -var p=[_7a5.page.x-pos.x-this.offset[0],_7a5.page.y-pos.y-this.offset[1]]; -if(this.options.snap){ -if(typeof (this.options.snap)=="function"){ -p=this.options.snap(p[0],p[1]); -}else{ -if(this.options.snap instanceof Array){ -var i=-1; -p=MochiKit.Base.map(MochiKit.Base.bind(function(v){ -i+=1; -return Math.round(v/this.options.snap[i])*this.options.snap[i]; -},this),p); -}else{ -p=MochiKit.Base.map(MochiKit.Base.bind(function(v){ -return Math.round(v/this.options.snap)*this.options.snap; -},this),p); -} -} -} -var _7ac=this.element.style; -if((!this.options.constraint)||(this.options.constraint=="horizontal")){ -_7ac.left=p[0]+"px"; -} -if((!this.options.constraint)||(this.options.constraint=="vertical")){ -_7ac.top=p[1]+"px"; -} -if(_7ac.visibility=="hidden"){ -_7ac.visibility=""; -} -},stopScrolling:function(){ -if(this.scrollInterval){ -clearInterval(this.scrollInterval); -this.scrollInterval=null; -MochiKit.DragAndDrop.Draggables._lastScrollPointer=null; -} -},startScrolling:function(_7ad){ -if(!_7ad[0]&&!_7ad[1]){ -return; -} -this.scrollSpeed=[_7ad[0]*this.options.scrollSpeed,_7ad[1]*this.options.scrollSpeed]; -this.lastScrolled=new Date(); -this.scrollInterval=setInterval(MochiKit.Base.bind(this.scroll,this),10); -},scroll:function(){ -var _7ae=new Date(); -var _7af=_7ae-this.lastScrolled; -this.lastScrolled=_7ae; -if(this.options.scroll==window){ -var s=this._getWindowScroll(this.options.scroll); -if(this.scrollSpeed[0]||this.scrollSpeed[1]){ -var dm=_7af/1000; -this.options.scroll.scrollTo(s.left+dm*this.scrollSpeed[0],s.top+dm*this.scrollSpeed[1]); -} -}else{ -this.options.scroll.scrollLeft+=this.scrollSpeed[0]*_7af/1000; -this.options.scroll.scrollTop+=this.scrollSpeed[1]*_7af/1000; -} -var d=MochiKit.DragAndDrop; -MochiKit.Position.prepare(); -d.Droppables.show(d.Draggables._lastPointer,this.element); -d.Draggables.notify("drag",this); -if(this._isScrollChild){ -d.Draggables._lastScrollPointer=d.Draggables._lastScrollPointer||d.Draggables._lastPointer; -d.Draggables._lastScrollPointer.x+=this.scrollSpeed[0]*_7af/1000; -d.Draggables._lastScrollPointer.y+=this.scrollSpeed[1]*_7af/1000; -if(d.Draggables._lastScrollPointer.x<0){ -d.Draggables._lastScrollPointer.x=0; -} -if(d.Draggables._lastScrollPointer.y<0){ -d.Draggables._lastScrollPointer.y=0; -} -this.draw(d.Draggables._lastScrollPointer); -} -this.options.onchange(this); -},_getWindowScroll:function(win){ -var vp,w,h; -MochiKit.DOM.withWindow(win,function(){ -vp=MochiKit.Style.getViewportPosition(win.document); -}); -if(win.innerWidth){ -w=win.innerWidth; -h=win.innerHeight; -}else{ -if(win.document.documentElement&&win.document.documentElement.clientWidth){ -w=win.document.documentElement.clientWidth; -h=win.document.documentElement.clientHeight; -}else{ -w=win.document.body.offsetWidth; -h=win.document.body.offsetHeight; -} -} -return {top:vp.y,left:vp.x,width:w,height:h}; -},repr:function(){ -return "["+this.__class__.NAME+", options:"+MochiKit.Base.repr(this.options)+"]"; -}}; -MochiKit.DragAndDrop.__new__=function(){ -MochiKit.Base.nameFunctions(this); -this.EXPORT_TAGS={":common":this.EXPORT,":all":MochiKit.Base.concat(this.EXPORT,this.EXPORT_OK)}; -}; -MochiKit.DragAndDrop.__new__(); -MochiKit.Base._exportSymbols(this,MochiKit.DragAndDrop); -MochiKit.Base._deps("Sortable",["Base","Iter","DOM","Position","DragAndDrop"]); -MochiKit.Sortable.NAME="MochiKit.Sortable"; -MochiKit.Sortable.VERSION="1.4.2"; -MochiKit.Sortable.__repr__=function(){ -return "["+this.NAME+" "+this.VERSION+"]"; -}; -MochiKit.Sortable.toString=function(){ -return this.__repr__(); -}; -MochiKit.Sortable.EXPORT=[]; -MochiKit.Sortable.EXPORT_OK=[]; -MochiKit.Base.update(MochiKit.Sortable,{sortables:{},_findRootElement:function(_7b7){ -while(_7b7.tagName.toUpperCase()!="BODY"){ -if(_7b7.id&&MochiKit.Sortable.sortables[_7b7.id]){ -return _7b7; -} -_7b7=_7b7.parentNode; -} -},_createElementId:function(_7b8){ -if(_7b8.id==null||_7b8.id==""){ -var d=MochiKit.DOM; -var id; -var _7bb=1; -while(d.getElement(id="sortable"+_7bb)!=null){ -_7bb+=1; -} -d.setNodeAttribute(_7b8,"id",id); -} -},options:function(_7bc){ -_7bc=MochiKit.Sortable._findRootElement(MochiKit.DOM.getElement(_7bc)); -if(!_7bc){ -return; -} -return MochiKit.Sortable.sortables[_7bc.id]; -},destroy:function(_7bd){ -var s=MochiKit.Sortable.options(_7bd); -var b=MochiKit.Base; -var d=MochiKit.DragAndDrop; -if(s){ -MochiKit.Signal.disconnect(s.startHandle); -MochiKit.Signal.disconnect(s.endHandle); -b.map(function(dr){ -d.Droppables.remove(dr); -},s.droppables); -b.map(function(dr){ -dr.destroy(); -},s.draggables); -delete MochiKit.Sortable.sortables[s.element.id]; -} -},create:function(_7c3,_7c4){ -_7c3=MochiKit.DOM.getElement(_7c3); -var self=MochiKit.Sortable; -self._createElementId(_7c3); -_7c4=MochiKit.Base.update({element:_7c3,tag:"li",dropOnEmpty:false,tree:false,treeTag:"ul",overlap:"vertical",constraint:"vertical",containment:[_7c3],handle:false,only:false,hoverclass:null,ghosting:false,scroll:false,scrollSensitivity:20,scrollSpeed:15,format:/^[^_]*_(.*)$/,onChange:MochiKit.Base.noop,onUpdate:MochiKit.Base.noop,accept:null},_7c4); -self.destroy(_7c3); -var _7c6={revert:true,ghosting:_7c4.ghosting,scroll:_7c4.scroll,scrollSensitivity:_7c4.scrollSensitivity,scrollSpeed:_7c4.scrollSpeed,constraint:_7c4.constraint,handle:_7c4.handle}; -if(_7c4.starteffect){ -_7c6.starteffect=_7c4.starteffect; -} -if(_7c4.reverteffect){ -_7c6.reverteffect=_7c4.reverteffect; -}else{ -if(_7c4.ghosting){ -_7c6.reverteffect=function(_7c7){ -_7c7.style.top=0; -_7c7.style.left=0; -}; -} -} -if(_7c4.endeffect){ -_7c6.endeffect=_7c4.endeffect; -} -if(_7c4.zindex){ -_7c6.zindex=_7c4.zindex; -} -var _7c8={overlap:_7c4.overlap,containment:_7c4.containment,hoverclass:_7c4.hoverclass,onhover:self.onHover,tree:_7c4.tree,accept:_7c4.accept}; -var _7c9={onhover:self.onEmptyHover,overlap:_7c4.overlap,containment:_7c4.containment,hoverclass:_7c4.hoverclass,accept:_7c4.accept}; -MochiKit.DOM.removeEmptyTextNodes(_7c3); -_7c4.draggables=[]; -_7c4.droppables=[]; -if(_7c4.dropOnEmpty||_7c4.tree){ -new MochiKit.DragAndDrop.Droppable(_7c3,_7c9); -_7c4.droppables.push(_7c3); -} -MochiKit.Base.map(function(e){ -var _7cb=_7c4.handle?MochiKit.DOM.getFirstElementByTagAndClassName(null,_7c4.handle,e):e; -_7c4.draggables.push(new MochiKit.DragAndDrop.Draggable(e,MochiKit.Base.update(_7c6,{handle:_7cb}))); -new MochiKit.DragAndDrop.Droppable(e,_7c8); -if(_7c4.tree){ -e.treeNode=_7c3; -} -_7c4.droppables.push(e); -},(self.findElements(_7c3,_7c4)||[])); -if(_7c4.tree){ -MochiKit.Base.map(function(e){ -new MochiKit.DragAndDrop.Droppable(e,_7c9); -e.treeNode=_7c3; -_7c4.droppables.push(e); -},(self.findTreeElements(_7c3,_7c4)||[])); -} -self.sortables[_7c3.id]=_7c4; -_7c4.lastValue=self.serialize(_7c3); -_7c4.startHandle=MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables,"start",MochiKit.Base.partial(self.onStart,_7c3)); -_7c4.endHandle=MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables,"end",MochiKit.Base.partial(self.onEnd,_7c3)); -},onStart:function(_7cd,_7ce){ -var self=MochiKit.Sortable; -var _7d0=self.options(_7cd); -_7d0.lastValue=self.serialize(_7d0.element); -},onEnd:function(_7d1,_7d2){ -var self=MochiKit.Sortable; -self.unmark(); -var _7d4=self.options(_7d1); -if(_7d4.lastValue!=self.serialize(_7d4.element)){ -_7d4.onUpdate(_7d4.element); -} -},findElements:function(_7d5,_7d6){ -return MochiKit.Sortable.findChildren(_7d5,_7d6.only,_7d6.tree,_7d6.tag); -},findTreeElements:function(_7d7,_7d8){ -return MochiKit.Sortable.findChildren(_7d7,_7d8.only,_7d8.tree?true:false,_7d8.treeTag); -},findChildren:function(_7d9,only,_7db,_7dc){ -if(!_7d9.hasChildNodes()){ -return null; -} -_7dc=_7dc.toUpperCase(); -if(only){ -only=MochiKit.Base.flattenArray([only]); -} -var _7dd=[]; -MochiKit.Base.map(function(e){ -if(e.tagName&&e.tagName.toUpperCase()==_7dc&&(!only||MochiKit.Iter.some(only,function(c){ -return MochiKit.DOM.hasElementClass(e,c); -}))){ -_7dd.push(e); -} -if(_7db){ -var _7e0=MochiKit.Sortable.findChildren(e,only,_7db,_7dc); -if(_7e0&&_7e0.length>0){ -_7dd=_7dd.concat(_7e0); -} -} -},_7d9.childNodes); -return _7dd; -},onHover:function(_7e1,_7e2,_7e3){ -if(MochiKit.DOM.isChildNode(_7e2,_7e1)){ -return; -} -var self=MochiKit.Sortable; -if(_7e3>0.33&&_7e3<0.66&&self.options(_7e2).tree){ -return; -}else{ -if(_7e3>0.5){ -self.mark(_7e2,"before"); -if(_7e2.previousSibling!=_7e1){ -var _7e5=_7e1.parentNode; -_7e1.style.visibility="hidden"; -_7e2.parentNode.insertBefore(_7e1,_7e2); -if(_7e2.parentNode!=_7e5){ -self.options(_7e5).onChange(_7e1); -} -self.options(_7e2.parentNode).onChange(_7e1); -} -}else{ -self.mark(_7e2,"after"); -var _7e6=_7e2.nextSibling||null; -if(_7e6!=_7e1){ -var _7e5=_7e1.parentNode; -_7e1.style.visibility="hidden"; -_7e2.parentNode.insertBefore(_7e1,_7e6); -if(_7e2.parentNode!=_7e5){ -self.options(_7e5).onChange(_7e1); -} -self.options(_7e2.parentNode).onChange(_7e1); -} -} -} -},_offsetSize:function(_7e7,type){ -if(type=="vertical"||type=="height"){ -return _7e7.offsetHeight; -}else{ -return _7e7.offsetWidth; -} -},onEmptyHover:function(_7e9,_7ea,_7eb){ -var _7ec=_7e9.parentNode; -var self=MochiKit.Sortable; -var _7ee=self.options(_7ea); -if(!MochiKit.DOM.isChildNode(_7ea,_7e9)){ -var _7ef; -var _7f0=self.findElements(_7ea,{tag:_7ee.tag,only:_7ee.only}); -var _7f1=null; -if(_7f0){ -var _7f2=self._offsetSize(_7ea,_7ee.overlap)*(1-_7eb); -for(_7ef=0;_7ef<_7f0.length;_7ef+=1){ -if(_7f2-self._offsetSize(_7f0[_7ef],_7ee.overlap)>=0){ -_7f2-=self._offsetSize(_7f0[_7ef],_7ee.overlap); -}else{ -if(_7f2-(self._offsetSize(_7f0[_7ef],_7ee.overlap)/2)>=0){ -_7f1=_7ef+1<_7f0.length?_7f0[_7ef+1]:null; -break; -}else{ -_7f1=_7f0[_7ef]; -break; -} -} -} -} -_7ea.insertBefore(_7e9,_7f1); -self.options(_7ec).onChange(_7e9); -_7ee.onChange(_7e9); -} -},unmark:function(){ -var m=MochiKit.Sortable._marker; -if(m){ -MochiKit.Style.hideElement(m); -} -},mark:function(_7f4,_7f5){ -var d=MochiKit.DOM; -var self=MochiKit.Sortable; -var _7f8=self.options(_7f4.parentNode); -if(_7f8&&!_7f8.ghosting){ -return; -} -if(!self._marker){ -self._marker=d.getElement("dropmarker")||document.createElement("DIV"); -MochiKit.Style.hideElement(self._marker); -d.addElementClass(self._marker,"dropmarker"); -self._marker.style.position="absolute"; -document.getElementsByTagName("body").item(0).appendChild(self._marker); -} -var _7f9=MochiKit.Position.cumulativeOffset(_7f4); -self._marker.style.left=_7f9.x+"px"; -self._marker.style.top=_7f9.y+"px"; -if(_7f5=="after"){ -if(_7f8.overlap=="horizontal"){ -self._marker.style.left=(_7f9.x+_7f4.clientWidth)+"px"; -}else{ -self._marker.style.top=(_7f9.y+_7f4.clientHeight)+"px"; -} -} -MochiKit.Style.showElement(self._marker); -},_tree:function(_7fa,_7fb,_7fc){ -var self=MochiKit.Sortable; -var _7fe=self.findElements(_7fa,_7fb)||[]; -for(var i=0;i<_7fe.length;++i){ -var _800=_7fe[i].id.match(_7fb.format); -if(!_800){ -continue; -} -var _801={id:encodeURIComponent(_800?_800[1]:null),element:_7fa,parent:_7fc,children:[],position:_7fc.children.length,container:self._findChildrenElement(_7fe[i],_7fb.treeTag.toUpperCase())}; -if(_801.container){ -self._tree(_801.container,_7fb,_801); -} -_7fc.children.push(_801); -} -return _7fc; -},_findChildrenElement:function(_802,_803){ -if(_802&&_802.hasChildNodes){ -_803=_803.toUpperCase(); -for(var i=0;i<_802.childNodes.length;++i){ -if(_802.childNodes[i].tagName.toUpperCase()==_803){ -return _802.childNodes[i]; -} -} -} -return null; -},tree:function(_805,_806){ -_805=MochiKit.DOM.getElement(_805); -var _807=MochiKit.Sortable.options(_805); -_806=MochiKit.Base.update({tag:_807.tag,treeTag:_807.treeTag,only:_807.only,name:_805.id,format:_807.format},_806||{}); -var root={id:null,parent:null,children:new Array,container:_805,position:0}; -return MochiKit.Sortable._tree(_805,_806,root); -},setSequence:function(_809,_80a,_80b){ -var self=MochiKit.Sortable; -var b=MochiKit.Base; -_809=MochiKit.DOM.getElement(_809); -_80b=b.update(self.options(_809),_80b||{}); -var _80e={}; -b.map(function(n){ -var m=n.id.match(_80b.format); -if(m){ -_80e[m[1]]=[n,n.parentNode]; -} -n.parentNode.removeChild(n); -},self.findElements(_809,_80b)); -b.map(function(_811){ -var n=_80e[_811]; -if(n){ -n[1].appendChild(n[0]); -delete _80e[_811]; -} -},_80a); -},_constructIndex:function(node){ -var _814=""; -do{ -if(node.id){ -_814="["+node.position+"]"+_814; -} -}while((node=node.parent)!=null); -return _814; -},sequence:function(_815,_816){ -_815=MochiKit.DOM.getElement(_815); -var self=MochiKit.Sortable; -var _816=MochiKit.Base.update(self.options(_815),_816||{}); -return MochiKit.Base.map(function(item){ -return item.id.match(_816.format)?item.id.match(_816.format)[1]:""; -},MochiKit.DOM.getElement(self.findElements(_815,_816)||[])); -},serialize:function(_819,_81a){ -_819=MochiKit.DOM.getElement(_819); -var self=MochiKit.Sortable; -_81a=MochiKit.Base.update(self.options(_819),_81a||{}); -var name=encodeURIComponent(_81a.name||_819.id); -if(_81a.tree){ -return MochiKit.Base.flattenArray(MochiKit.Base.map(function(item){ -return [name+self._constructIndex(item)+"[id]="+encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); -},self.tree(_819,_81a).children)).join("&"); -}else{ -return MochiKit.Base.map(function(item){ -return name+"[]="+encodeURIComponent(item); -},self.sequence(_819,_81a)).join("&"); -} -}}); -MochiKit.Sortable.Sortable=MochiKit.Sortable; -MochiKit.Sortable.__new__=function(){ -MochiKit.Base.nameFunctions(this); -this.EXPORT_TAGS={":common":this.EXPORT,":all":MochiKit.Base.concat(this.EXPORT,this.EXPORT_OK)}; -}; -MochiKit.Sortable.__new__(); -MochiKit.Base._exportSymbols(this,MochiKit.Sortable); -if(typeof (MochiKit)=="undefined"){ -MochiKit={}; -} -if(typeof (MochiKit.MochiKit)=="undefined"){ -MochiKit.MochiKit={}; -} -MochiKit.MochiKit.NAME="MochiKit.MochiKit"; -MochiKit.MochiKit.VERSION="1.4.2"; -MochiKit.MochiKit.__repr__=function(){ -return "["+this.NAME+" "+this.VERSION+"]"; -}; -MochiKit.MochiKit.toString=function(){ -return this.__repr__(); -}; -MochiKit.MochiKit.SUBMODULES=["Base","Iter","Logging","DateTime","Format","Async","DOM","Selector","Style","LoggingPane","Color","Signal","Position","Visual","DragAndDrop","Sortable"]; -if(typeof (JSAN)!="undefined"||typeof (dojo)!="undefined"){ -if(typeof (dojo)!="undefined"){ -dojo.provide("MochiKit.MochiKit"); -(function(lst){ -for(var i=0;i"); -} -} -})(); -} - - diff --git a/kcdc3/paste/evalexception/media/debug.js b/kcdc3/paste/evalexception/media/debug.js deleted file mode 100644 index 57f9df3d..00000000 --- a/kcdc3/paste/evalexception/media/debug.js +++ /dev/null @@ -1,161 +0,0 @@ -function showFrame(anchor) { - var tbid = anchor.getAttribute('tbid'); - var expanded = anchor.expanded; - if (expanded) { - MochiKit.DOM.hideElement(anchor.expandedElement); - anchor.expanded = false; - _swapImage(anchor); - return false; - } - anchor.expanded = true; - if (anchor.expandedElement) { - MochiKit.DOM.showElement(anchor.expandedElement); - _swapImage(anchor); - $('debug_input_'+tbid).focus(); - return false; - } - var url = debug_base - + '/show_frame?tbid=' + tbid - + '&debugcount=' + debug_count; - var d = MochiKit.Async.doSimpleXMLHttpRequest(url); - d.addCallbacks(function (data) { - var el = MochiKit.DOM.DIV({}); - anchor.parentNode.insertBefore(el, anchor.nextSibling); - el.innerHTML = data.responseText; - anchor.expandedElement = el; - _swapImage(anchor); - $('debug_input_'+tbid).focus(); - }, function (error) { - showError(error.req.responseText); - }); - return false; -} - -function _swapImage(anchor) { - var el = anchor.getElementsByTagName('IMG')[0]; - if (anchor.expanded) { - var img = 'minus.jpg'; - } else { - var img = 'plus.jpg'; - } - el.src = debug_base + '/media/' + img; -} - -function submitInput(button, tbid) { - var input = $(button.getAttribute('input-from')); - var output = $(button.getAttribute('output-to')); - var url = debug_base - + '/exec_input'; - var history = input.form.history; - input.historyPosition = 0; - if (! history) { - history = input.form.history = []; - } - history.push(input.value); - var vars = { - tbid: tbid, - debugcount: debug_count, - input: input.value - }; - MochiKit.DOM.showElement(output); - var d = MochiKit.Async.doSimpleXMLHttpRequest(url, vars); - d.addCallbacks(function (data) { - var result = data.responseText; - output.innerHTML += result; - input.value = ''; - input.focus(); - }, function (error) { - showError(error.req.responseText); - }); - return false; -} - -function showError(msg) { - var el = $('error-container'); - if (el.innerHTML) { - el.innerHTML += '
    \n' + msg; - } else { - el.innerHTML = msg; - } - MochiKit.DOM.showElement('error-area'); -} - -function clearError() { - var el = $('error-container'); - el.innerHTML = ''; - MochiKit.DOM.hideElement('error-area'); -} - -function expandInput(button) { - var input = button.form.elements.input; - stdops = { - name: 'input', - style: 'width: 100%', - autocomplete: 'off' - }; - if (input.tagName == 'INPUT') { - var newEl = MochiKit.DOM.TEXTAREA(stdops); - var text = 'Contract'; - } else { - stdops['type'] = 'text'; - stdops['onkeypress'] = 'upArrow(this)'; - var newEl = MochiKit.DOM.INPUT(stdops); - var text = 'Expand'; - } - newEl.value = input.value; - newEl.id = input.id; - MochiKit.DOM.swapDOM(input, newEl); - newEl.focus(); - button.value = text; - return false; -} - -function upArrow(input, event) { - if (window.event) { - event = window.event; - } - if (event.keyCode != 38 && event.keyCode != 40) { - // not an up- or down-arrow - return true; - } - var dir = event.keyCode == 38 ? 1 : -1; - var history = input.form.history; - if (! history) { - history = input.form.history = []; - } - var pos = input.historyPosition || 0; - if (! pos && dir == -1) { - return true; - } - if (! pos && input.value) { - history.push(input.value); - pos = 1; - } - pos += dir; - if (history.length-pos < 0) { - pos = 1; - } - if (history.length-pos > history.length-1) { - input.value = ''; - return true; - } - input.historyPosition = pos; - var line = history[history.length-pos]; - input.value = line; -} - -function expandLong(anchor) { - var span = anchor; - while (span) { - if (span.style && span.style.display == 'none') { - break; - } - span = span.nextSibling; - } - if (! span) { - return false; - } - MochiKit.DOM.showElement(span); - MochiKit.DOM.hideElement(anchor); - return false; -} diff --git a/kcdc3/paste/evalexception/media/minus.jpg b/kcdc3/paste/evalexception/media/minus.jpg deleted file mode 100644 index 05f33065..00000000 Binary files a/kcdc3/paste/evalexception/media/minus.jpg and /dev/null differ diff --git a/kcdc3/paste/evalexception/media/plus.jpg b/kcdc3/paste/evalexception/media/plus.jpg deleted file mode 100644 index a17aa5eb..00000000 Binary files a/kcdc3/paste/evalexception/media/plus.jpg and /dev/null differ diff --git a/kcdc3/paste/evalexception/middleware.py b/kcdc3/paste/evalexception/middleware.py deleted file mode 100644 index 4349b883..00000000 --- a/kcdc3/paste/evalexception/middleware.py +++ /dev/null @@ -1,610 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -Exception-catching middleware that allows interactive debugging. - -This middleware catches all unexpected exceptions. A normal -traceback, like produced by -``paste.exceptions.errormiddleware.ErrorMiddleware`` is given, plus -controls to see local variables and evaluate expressions in a local -context. - -This can only be used in single-process environments, because -subsequent requests must go back to the same process that the -exception originally occurred in. Threaded or non-concurrent -environments both work. - -This shouldn't be used in production in any way. That would just be -silly. - -If calling from an XMLHttpRequest call, if the GET variable ``_`` is -given then it will make the response more compact (and less -Javascripty), since if you use innerHTML it'll kill your browser. You -can look for the header X-Debug-URL in your 500 responses if you want -to see the full debuggable traceback. Also, this URL is printed to -``wsgi.errors``, so you can open it up in another browser window. -""" -import sys -import os -import cgi -import traceback -from cStringIO import StringIO -import pprint -import itertools -import time -import re -from paste.exceptions import errormiddleware, formatter, collector -from paste import wsgilib -from paste import urlparser -from paste import httpexceptions -from paste import registry -from paste import request -from paste import response -import evalcontext - -limit = 200 - -def html_quote(v): - """ - Escape HTML characters, plus translate None to '' - """ - if v is None: - return '' - return cgi.escape(str(v), 1) - -def preserve_whitespace(v, quote=True): - """ - Quote a value for HTML, preserving whitespace (translating - newlines to ``
    `` and multiple spaces to use `` ``). - - If ``quote`` is true, then the value will be HTML quoted first. - """ - if quote: - v = html_quote(v) - v = v.replace('\n', '
    \n') - v = re.sub(r'()( +)', _repl_nbsp, v) - v = re.sub(r'(\n)( +)', _repl_nbsp, v) - v = re.sub(r'^()( +)', _repl_nbsp, v) - return '%s' % v - -def _repl_nbsp(match): - if len(match.group(2)) == 1: - return ' ' - return match.group(1) + ' ' * (len(match.group(2))-1) + ' ' - -def simplecatcher(application): - """ - A simple middleware that catches errors and turns them into simple - tracebacks. - """ - def simplecatcher_app(environ, start_response): - try: - return application(environ, start_response) - except: - out = StringIO() - traceback.print_exc(file=out) - start_response('500 Server Error', - [('content-type', 'text/html')], - sys.exc_info()) - res = out.getvalue() - return ['

    Error

    %s
    ' - % html_quote(res)] - return simplecatcher_app - -def wsgiapp(): - """ - Turns a function or method into a WSGI application. - """ - def decorator(func): - def wsgiapp_wrapper(*args): - # we get 3 args when this is a method, two when it is - # a function :( - if len(args) == 3: - environ = args[1] - start_response = args[2] - args = [args[0]] - else: - environ, start_response = args - args = [] - def application(environ, start_response): - form = wsgilib.parse_formvars(environ, - include_get_vars=True) - headers = response.HeaderDict( - {'content-type': 'text/html', - 'status': '200 OK'}) - form['environ'] = environ - form['headers'] = headers - res = func(*args, **form.mixed()) - status = headers.pop('status') - start_response(status, headers.headeritems()) - return [res] - app = httpexceptions.make_middleware(application) - app = simplecatcher(app) - return app(environ, start_response) - wsgiapp_wrapper.exposed = True - return wsgiapp_wrapper - return decorator - -def get_debug_info(func): - """ - A decorator (meant to be used under ``wsgiapp()``) that resolves - the ``debugcount`` variable to a ``DebugInfo`` object (or gives an - error if it can't be found). - """ - def debug_info_replacement(self, **form): - try: - if 'debugcount' not in form: - raise ValueError('You must provide a debugcount parameter') - debugcount = form.pop('debugcount') - try: - debugcount = int(debugcount) - except ValueError: - raise ValueError('Bad value for debugcount') - if debugcount not in self.debug_infos: - raise ValueError( - 'Debug %s no longer found (maybe it has expired?)' - % debugcount) - debug_info = self.debug_infos[debugcount] - return func(self, debug_info=debug_info, **form) - except ValueError, e: - form['headers']['status'] = '500 Server Error' - return 'There was an error: %s' % html_quote(e) - return debug_info_replacement - -debug_counter = itertools.count(int(time.time())) -def get_debug_count(environ): - """ - Return the unique debug count for the current request - """ - if 'paste.evalexception.debug_count' in environ: - return environ['paste.evalexception.debug_count'] - else: - environ['paste.evalexception.debug_count'] = next = debug_counter.next() - return next - -class EvalException(object): - - def __init__(self, application, global_conf=None, - xmlhttp_key=None): - self.application = application - self.debug_infos = {} - if xmlhttp_key is None: - if global_conf is None: - xmlhttp_key = '_' - else: - xmlhttp_key = global_conf.get('xmlhttp_key', '_') - self.xmlhttp_key = xmlhttp_key - - def __call__(self, environ, start_response): - assert not environ['wsgi.multiprocess'], ( - "The EvalException middleware is not usable in a " - "multi-process environment") - environ['paste.evalexception'] = self - if environ.get('PATH_INFO', '').startswith('/_debug/'): - return self.debug(environ, start_response) - else: - return self.respond(environ, start_response) - - def debug(self, environ, start_response): - assert request.path_info_pop(environ) == '_debug' - next_part = request.path_info_pop(environ) - method = getattr(self, next_part, None) - if not method: - exc = httpexceptions.HTTPNotFound( - '%r not found when parsing %r' - % (next_part, wsgilib.construct_url(environ))) - return exc.wsgi_application(environ, start_response) - if not getattr(method, 'exposed', False): - exc = httpexceptions.HTTPForbidden( - '%r not allowed' % next_part) - return exc.wsgi_application(environ, start_response) - return method(environ, start_response) - - def media(self, environ, start_response): - """ - Static path where images and other files live - """ - app = urlparser.StaticURLParser( - os.path.join(os.path.dirname(__file__), 'media')) - return app(environ, start_response) - media.exposed = True - - def mochikit(self, environ, start_response): - """ - Static path where MochiKit lives - """ - app = urlparser.StaticURLParser( - os.path.join(os.path.dirname(__file__), 'mochikit')) - return app(environ, start_response) - mochikit.exposed = True - - def summary(self, environ, start_response): - """ - Returns a JSON-format summary of all the cached - exception reports - """ - start_response('200 OK', [('Content-type', 'text/x-json')]) - data = []; - items = self.debug_infos.values() - items.sort(lambda a, b: cmp(a.created, b.created)) - data = [item.json() for item in items] - return [repr(data)] - summary.exposed = True - - def view(self, environ, start_response): - """ - View old exception reports - """ - id = int(request.path_info_pop(environ)) - if id not in self.debug_infos: - start_response( - '500 Server Error', - [('Content-type', 'text/html')]) - return [ - "Traceback by id %s does not exist (maybe " - "the server has been restarted?)" - % id] - debug_info = self.debug_infos[id] - return debug_info.wsgi_application(environ, start_response) - view.exposed = True - - def make_view_url(self, environ, base_path, count): - return base_path + '/_debug/view/%s' % count - - #@wsgiapp() - #@get_debug_info - def show_frame(self, tbid, debug_info, **kw): - frame = debug_info.frame(int(tbid)) - vars = frame.tb_frame.f_locals - if vars: - registry.restorer.restoration_begin(debug_info.counter) - local_vars = make_table(vars) - registry.restorer.restoration_end() - else: - local_vars = 'No local vars' - return input_form(tbid, debug_info) + local_vars - - show_frame = wsgiapp()(get_debug_info(show_frame)) - - #@wsgiapp() - #@get_debug_info - def exec_input(self, tbid, debug_info, input, **kw): - if not input.strip(): - return '' - input = input.rstrip() + '\n' - frame = debug_info.frame(int(tbid)) - vars = frame.tb_frame.f_locals - glob_vars = frame.tb_frame.f_globals - context = evalcontext.EvalContext(vars, glob_vars) - registry.restorer.restoration_begin(debug_info.counter) - output = context.exec_expr(input) - registry.restorer.restoration_end() - input_html = formatter.str2html(input) - return ('>>> ' - '%s
    \n%s' - % (preserve_whitespace(input_html, quote=False), - preserve_whitespace(output))) - - exec_input = wsgiapp()(get_debug_info(exec_input)) - - def respond(self, environ, start_response): - if environ.get('paste.throw_errors'): - return self.application(environ, start_response) - base_path = request.construct_url(environ, with_path_info=False, - with_query_string=False) - environ['paste.throw_errors'] = True - started = [] - def detect_start_response(status, headers, exc_info=None): - try: - return start_response(status, headers, exc_info) - except: - raise - else: - started.append(True) - try: - __traceback_supplement__ = errormiddleware.Supplement, self, environ - app_iter = self.application(environ, detect_start_response) - try: - return_iter = list(app_iter) - return return_iter - finally: - if hasattr(app_iter, 'close'): - app_iter.close() - except: - exc_info = sys.exc_info() - for expected in environ.get('paste.expected_exceptions', []): - if isinstance(exc_info[1], expected): - raise - - # Tell the Registry to save its StackedObjectProxies current state - # for later restoration - registry.restorer.save_registry_state(environ) - - count = get_debug_count(environ) - view_uri = self.make_view_url(environ, base_path, count) - if not started: - headers = [('content-type', 'text/html')] - headers.append(('X-Debug-URL', view_uri)) - start_response('500 Internal Server Error', - headers, - exc_info) - environ['wsgi.errors'].write('Debug at: %s\n' % view_uri) - - exc_data = collector.collect_exception(*exc_info) - debug_info = DebugInfo(count, exc_info, exc_data, base_path, - environ, view_uri) - assert count not in self.debug_infos - self.debug_infos[count] = debug_info - - if self.xmlhttp_key: - get_vars = wsgilib.parse_querystring(environ) - if dict(get_vars).get(self.xmlhttp_key): - exc_data = collector.collect_exception(*exc_info) - html = formatter.format_html( - exc_data, include_hidden_frames=False, - include_reusable=False, show_extra_data=False) - return [html] - - # @@: it would be nice to deal with bad content types here - return debug_info.content() - - def exception_handler(self, exc_info, environ): - simple_html_error = False - if self.xmlhttp_key: - get_vars = wsgilib.parse_querystring(environ) - if dict(get_vars).get(self.xmlhttp_key): - simple_html_error = True - return errormiddleware.handle_exception( - exc_info, environ['wsgi.errors'], - html=True, - debug_mode=True, - simple_html_error=simple_html_error) - -class DebugInfo(object): - - def __init__(self, counter, exc_info, exc_data, base_path, - environ, view_uri): - self.counter = counter - self.exc_data = exc_data - self.base_path = base_path - self.environ = environ - self.view_uri = view_uri - self.created = time.time() - self.exc_type, self.exc_value, self.tb = exc_info - __exception_formatter__ = 1 - self.frames = [] - n = 0 - tb = self.tb - while tb is not None and (limit is None or n < limit): - if tb.tb_frame.f_locals.get('__exception_formatter__'): - # Stop recursion. @@: should make a fake ExceptionFrame - break - self.frames.append(tb) - tb = tb.tb_next - n += 1 - - def json(self): - """Return the JSON-able representation of this object""" - return { - 'uri': self.view_uri, - 'created': time.strftime('%c', time.gmtime(self.created)), - 'created_timestamp': self.created, - 'exception_type': str(self.exc_type), - 'exception': str(self.exc_value), - } - - def frame(self, tbid): - for frame in self.frames: - if id(frame) == tbid: - return frame - else: - raise ValueError, ( - "No frame by id %s found from %r" % (tbid, self.frames)) - - def wsgi_application(self, environ, start_response): - start_response('200 OK', [('content-type', 'text/html')]) - return self.content() - - def content(self): - html = format_eval_html(self.exc_data, self.base_path, self.counter) - head_html = (formatter.error_css + formatter.hide_display_js) - head_html += self.eval_javascript() - repost_button = make_repost_button(self.environ) - page = error_template % { - 'repost_button': repost_button or '', - 'head_html': head_html, - 'body': html} - return [page] - - def eval_javascript(self): - base_path = self.base_path + '/_debug' - return ( - '\n' - '\n' - '\n' - % (base_path, base_path, base_path, self.counter)) - -class EvalHTMLFormatter(formatter.HTMLFormatter): - - def __init__(self, base_path, counter, **kw): - super(EvalHTMLFormatter, self).__init__(**kw) - self.base_path = base_path - self.counter = counter - - def format_source_line(self, filename, frame): - line = formatter.HTMLFormatter.format_source_line( - self, filename, frame) - return (line + - '     ' - '    ' - % (frame.tbid, self.base_path)) - -def make_table(items): - if isinstance(items, dict): - items = items.items() - items.sort() - rows = [] - i = 0 - for name, value in items: - i += 1 - out = StringIO() - try: - pprint.pprint(value, out) - except Exception, e: - print >> out, 'Error: %s' % e - value = html_quote(out.getvalue()) - if len(value) > 100: - # @@: This can actually break the HTML :( - # should I truncate before quoting? - orig_value = value - value = value[:100] - value += '...' - value += '%s' % orig_value[100:] - value = formatter.make_wrappable(value) - if i % 2: - attr = ' class="even"' - else: - attr = ' class="odd"' - rows.append('' - '%s%s' - % (attr, html_quote(name), - preserve_whitespace(value, quote=False))) - return '%s
    ' % ( - '\n'.join(rows)) - -def format_eval_html(exc_data, base_path, counter): - short_formatter = EvalHTMLFormatter( - base_path=base_path, - counter=counter, - include_reusable=False) - short_er = short_formatter.format_collected_data(exc_data) - long_formatter = EvalHTMLFormatter( - base_path=base_path, - counter=counter, - show_hidden_frames=True, - show_extra_data=False, - include_reusable=False) - long_er = long_formatter.format_collected_data(exc_data) - text_er = formatter.format_text(exc_data, show_hidden_frames=True) - if short_formatter.filter_frames(exc_data.frames) != \ - long_formatter.filter_frames(exc_data.frames): - # Only display the full traceback when it differs from the - # short version - full_traceback_html = """ -
    - -
    - %s -
    - """ % long_er - else: - full_traceback_html = '' - - return """ - %s - %s -
    - -
    - -
    - """ % (short_er, full_traceback_html, cgi.escape(text_er)) - -def make_repost_button(environ): - url = request.construct_url(environ) - if environ['REQUEST_METHOD'] == 'GET': - return ('
    ' % url) - else: - # @@: I'd like to reconstruct this, but I can't because - # the POST body is probably lost at this point, and - # I can't get it back :( - return None - # @@: Use or lose the following code block - """ - fields = [] - for name, value in wsgilib.parse_formvars( - environ, include_get_vars=False).items(): - if hasattr(value, 'filename'): - # @@: Arg, we'll just submit the body, and leave out - # the filename :( - value = value.value - fields.append( - '' - % (html_quote(name), html_quote(value))) - return ''' -
    -%s - -
    ''' % (url, '\n'.join(fields)) -""" - - -def input_form(tbid, debug_info): - return ''' -
    -
    -
    - - -
    - ''' % {'tbid': tbid} - -error_template = ''' - - - Server Error - %(head_html)s - - - - - -%(repost_button)s - -%(body)s - - - -''' - -def make_eval_exception(app, global_conf, xmlhttp_key=None): - """ - Wraps the application in an interactive debugger. - - This debugger is a major security hole, and should only be - used during development. - - xmlhttp_key is a string that, if present in QUERY_STRING, - indicates that the request is an XMLHttp request, and the - Javascript/interactive debugger should not be returned. (If you - try to put the debugger somewhere with innerHTML, you will often - crash the browser) - """ - if xmlhttp_key is None: - xmlhttp_key = global_conf.get('xmlhttp_key', '_') - return EvalException(app, xmlhttp_key=xmlhttp_key) diff --git a/kcdc3/paste/exceptions/__init__.py b/kcdc3/paste/exceptions/__init__.py deleted file mode 100644 index 813f855f..00000000 --- a/kcdc3/paste/exceptions/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -""" -Package for catching exceptions and displaying annotated exception -reports -""" diff --git a/kcdc3/paste/exceptions/collector.py b/kcdc3/paste/exceptions/collector.py deleted file mode 100644 index 65f9ec06..00000000 --- a/kcdc3/paste/exceptions/collector.py +++ /dev/null @@ -1,526 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Corporation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -## Originally zExceptions.ExceptionFormatter from Zope; -## Modified by Ian Bicking, Imaginary Landscape, 2005 -""" -An exception collector that finds traceback information plus -supplements -""" - -import sys -import traceback -import time -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO -import linecache -from paste.exceptions import serial_number_generator -import warnings - -DEBUG_EXCEPTION_FORMATTER = True -DEBUG_IDENT_PREFIX = 'E-' -FALLBACK_ENCODING = 'UTF-8' - -__all__ = ['collect_exception', 'ExceptionCollector'] - -class ExceptionCollector(object): - - """ - Produces a data structure that can be used by formatters to - display exception reports. - - Magic variables: - - If you define one of these variables in your local scope, you can - add information to tracebacks that happen in that context. This - allows applications to add all sorts of extra information about - the context of the error, including URLs, environmental variables, - users, hostnames, etc. These are the variables we look for: - - ``__traceback_supplement__``: - You can define this locally or globally (unlike all the other - variables, which must be defined locally). - - ``__traceback_supplement__`` is a tuple of ``(factory, arg1, - arg2...)``. When there is an exception, ``factory(arg1, arg2, - ...)`` is called, and the resulting object is inspected for - supplemental information. - - ``__traceback_info__``: - This information is added to the traceback, usually fairly - literally. - - ``__traceback_hide__``: - If set and true, this indicates that the frame should be - hidden from abbreviated tracebacks. This way you can hide - some of the complexity of the larger framework and let the - user focus on their own errors. - - By setting it to ``'before'``, all frames before this one will - be thrown away. By setting it to ``'after'`` then all frames - after this will be thrown away until ``'reset'`` is found. In - each case the frame where it is set is included, unless you - append ``'_and_this'`` to the value (e.g., - ``'before_and_this'``). - - Note that formatters will ignore this entirely if the frame - that contains the error wouldn't normally be shown according - to these rules. - - ``__traceback_reporter__``: - This should be a reporter object (see the reporter module), - or a list/tuple of reporter objects. All reporters found this - way will be given the exception, innermost first. - - ``__traceback_decorator__``: - This object (defined in a local or global scope) will get the - result of this function (the CollectedException defined - below). It may modify this object in place, or return an - entirely new object. This gives the object the ability to - manipulate the traceback arbitrarily. - - The actually interpretation of these values is largely up to the - reporters and formatters. - - ``collect_exception(*sys.exc_info())`` will return an object with - several attributes: - - ``frames``: - A list of frames - ``exception_formatted``: - The formatted exception, generally a full traceback - ``exception_type``: - The type of the exception, like ``ValueError`` - ``exception_value``: - The string value of the exception, like ``'x not in list'`` - ``identification_code``: - A hash of the exception data meant to identify the general - exception, so that it shares this code with other exceptions - that derive from the same problem. The code is a hash of - all the module names and function names in the traceback, - plus exception_type. This should be shown to users so they - can refer to the exception later. (@@: should it include a - portion that allows identification of the specific instance - of the exception as well?) - - The list of frames goes innermost first. Each frame has these - attributes; some values may be None if they could not be - determined. - - ``modname``: - the name of the module - ``filename``: - the filename of the module - ``lineno``: - the line of the error - ``revision``: - the contents of __version__ or __revision__ - ``name``: - the function name - ``supplement``: - an object created from ``__traceback_supplement__`` - ``supplement_exception``: - a simple traceback of any exception ``__traceback_supplement__`` - created - ``traceback_info``: - the str() of any ``__traceback_info__`` variable found in the local - scope (@@: should it str()-ify it or not?) - ``traceback_hide``: - the value of any ``__traceback_hide__`` variable - ``traceback_log``: - the value of any ``__traceback_log__`` variable - - - ``__traceback_supplement__`` is thrown away, but a fixed - set of attributes are captured; each of these attributes is - optional. - - ``object``: - the name of the object being visited - ``source_url``: - the original URL requested - ``line``: - the line of source being executed (for interpreters, like ZPT) - ``column``: - the column of source being executed - ``expression``: - the expression being evaluated (also for interpreters) - ``warnings``: - a list of (string) warnings to be displayed - ``getInfo``: - a function/method that takes no arguments, and returns a string - describing any extra information - ``extraData``: - a function/method that takes no arguments, and returns a - dictionary. The contents of this dictionary will not be - displayed in the context of the traceback, but globally for - the exception. Results will be grouped by the keys in the - dictionaries (which also serve as titles). The keys can also - be tuples of (importance, title); in this case the importance - should be ``important`` (shows up at top), ``normal`` (shows - up somewhere; unspecified), ``supplemental`` (shows up at - bottom), or ``extra`` (shows up hidden or not at all). - - These are used to create an object with attributes of the same - names (``getInfo`` becomes a string attribute, not a method). - ``__traceback_supplement__`` implementations should be careful to - produce values that are relatively static and unlikely to cause - further errors in the reporting system -- any complex - introspection should go in ``getInfo()`` and should ultimately - return a string. - - Note that all attributes are optional, and under certain - circumstances may be None or may not exist at all -- the collector - can only do a best effort, but must avoid creating any exceptions - itself. - - Formatters may want to use ``__traceback_hide__`` as a hint to - hide frames that are part of the 'framework' or underlying system. - There are a variety of rules about special values for this - variables that formatters should be aware of. - - TODO: - - More attributes in __traceback_supplement__? Maybe an attribute - that gives a list of local variables that should also be - collected? Also, attributes that would be explicitly meant for - the entire request, not just a single frame. Right now some of - the fixed set of attributes (e.g., source_url) are meant for this - use, but there's no explicit way for the supplement to indicate - new values, e.g., logged-in user, HTTP referrer, environment, etc. - Also, the attributes that do exist are Zope/Web oriented. - - More information on frames? cgitb, for instance, produces - extensive information on local variables. There exists the - possibility that getting this information may cause side effects, - which can make debugging more difficult; but it also provides - fodder for post-mortem debugging. However, the collector is not - meant to be configurable, but to capture everything it can and let - the formatters be configurable. Maybe this would have to be a - configuration value, or maybe it could be indicated by another - magical variable (which would probably mean 'show all local - variables below this frame') - """ - - show_revisions = 0 - - def __init__(self, limit=None): - self.limit = limit - - def getLimit(self): - limit = self.limit - if limit is None: - limit = getattr(sys, 'tracebacklimit', None) - return limit - - def getRevision(self, globals): - if not self.show_revisions: - return None - revision = globals.get('__revision__', None) - if revision is None: - # Incorrect but commonly used spelling - revision = globals.get('__version__', None) - - if revision is not None: - try: - revision = str(revision).strip() - except: - revision = '???' - return revision - - def collectSupplement(self, supplement, tb): - result = {} - - for name in ('object', 'source_url', 'line', 'column', - 'expression', 'warnings'): - result[name] = getattr(supplement, name, None) - - func = getattr(supplement, 'getInfo', None) - if func: - result['info'] = func() - else: - result['info'] = None - func = getattr(supplement, 'extraData', None) - if func: - result['extra'] = func() - else: - result['extra'] = None - return SupplementaryData(**result) - - def collectLine(self, tb, extra_data): - f = tb.tb_frame - lineno = tb.tb_lineno - co = f.f_code - filename = co.co_filename - name = co.co_name - globals = f.f_globals - locals = f.f_locals - if not hasattr(locals, 'has_key'): - # Something weird about this frame; it's not a real dict - warnings.warn( - "Frame %s has an invalid locals(): %r" % ( - globals.get('__name__', 'unknown'), locals)) - locals = {} - data = {} - data['modname'] = globals.get('__name__', None) - data['filename'] = filename - data['lineno'] = lineno - data['revision'] = self.getRevision(globals) - data['name'] = name - data['tbid'] = id(tb) - - # Output a traceback supplement, if any. - if locals.has_key('__traceback_supplement__'): - # Use the supplement defined in the function. - tbs = locals['__traceback_supplement__'] - elif globals.has_key('__traceback_supplement__'): - # Use the supplement defined in the module. - # This is used by Scripts (Python). - tbs = globals['__traceback_supplement__'] - else: - tbs = None - if tbs is not None: - factory = tbs[0] - args = tbs[1:] - try: - supp = factory(*args) - data['supplement'] = self.collectSupplement(supp, tb) - if data['supplement'].extra: - for key, value in data['supplement'].extra.items(): - extra_data.setdefault(key, []).append(value) - except: - if DEBUG_EXCEPTION_FORMATTER: - out = StringIO() - traceback.print_exc(file=out) - text = out.getvalue() - data['supplement_exception'] = text - # else just swallow the exception. - - try: - tbi = locals.get('__traceback_info__', None) - if tbi is not None: - data['traceback_info'] = str(tbi) - except: - pass - - marker = [] - for name in ('__traceback_hide__', '__traceback_log__', - '__traceback_decorator__'): - try: - tbh = locals.get(name, globals.get(name, marker)) - if tbh is not marker: - data[name[2:-2]] = tbh - except: - pass - - return data - - def collectExceptionOnly(self, etype, value): - return traceback.format_exception_only(etype, value) - - def collectException(self, etype, value, tb, limit=None): - # The next line provides a way to detect recursion. - __exception_formatter__ = 1 - frames = [] - ident_data = [] - traceback_decorators = [] - if limit is None: - limit = self.getLimit() - n = 0 - extra_data = {} - while tb is not None and (limit is None or n < limit): - if tb.tb_frame.f_locals.get('__exception_formatter__'): - # Stop recursion. @@: should make a fake ExceptionFrame - frames.append('(Recursive formatException() stopped)\n') - break - data = self.collectLine(tb, extra_data) - frame = ExceptionFrame(**data) - frames.append(frame) - if frame.traceback_decorator is not None: - traceback_decorators.append(frame.traceback_decorator) - ident_data.append(frame.modname or '?') - ident_data.append(frame.name or '?') - tb = tb.tb_next - n = n + 1 - ident_data.append(str(etype)) - ident = serial_number_generator.hash_identifier( - ' '.join(ident_data), length=5, upper=True, - prefix=DEBUG_IDENT_PREFIX) - - result = CollectedException( - frames=frames, - exception_formatted=self.collectExceptionOnly(etype, value), - exception_type=etype, - exception_value=self.safeStr(value), - identification_code=ident, - date=time.localtime(), - extra_data=extra_data) - if etype is ImportError: - extra_data[('important', 'sys.path')] = [sys.path] - for decorator in traceback_decorators: - try: - new_result = decorator(result) - if new_result is not None: - result = new_result - except: - pass - return result - - def safeStr(self, obj): - try: - return str(obj) - except UnicodeEncodeError: - try: - return unicode(obj).encode(FALLBACK_ENCODING, 'replace') - except UnicodeEncodeError: - # This is when something is really messed up, but this can - # happen when the __str__ of an object has to handle unicode - return repr(obj) - -limit = 200 - -class Bunch(object): - - """ - A generic container - """ - - def __init__(self, **attrs): - for name, value in attrs.items(): - setattr(self, name, value) - - def __repr__(self): - name = '<%s ' % self.__class__.__name__ - name += ' '.join(['%s=%r' % (name, str(value)[:30]) - for name, value in self.__dict__.items() - if not name.startswith('_')]) - return name + '>' - -class CollectedException(Bunch): - """ - This is the result of collection the exception; it contains copies - of data of interest. - """ - # A list of frames (ExceptionFrame instances), innermost last: - frames = [] - # The result of traceback.format_exception_only; this looks - # like a normal traceback you'd see in the interactive interpreter - exception_formatted = None - # The *string* representation of the type of the exception - # (@@: should we give the # actual class? -- we can't keep the - # actual exception around, but the class should be safe) - # Something like 'ValueError' - exception_type = None - # The string representation of the exception, from ``str(e)``. - exception_value = None - # An identifier which should more-or-less classify this particular - # exception, including where in the code it happened. - identification_code = None - # The date, as time.localtime() returns: - date = None - # A dictionary of supplemental data: - extra_data = {} - -class SupplementaryData(Bunch): - """ - The result of __traceback_supplement__. We don't keep the - supplement object around, for fear of GC problems and whatnot. - (@@: Maybe I'm being too superstitious about copying only specific - information over) - """ - - # These attributes are copied from the object, or left as None - # if the object doesn't have these attributes: - object = None - source_url = None - line = None - column = None - expression = None - warnings = None - # This is the *return value* of supplement.getInfo(): - info = None - -class ExceptionFrame(Bunch): - """ - This represents one frame of the exception. Each frame is a - context in the call stack, typically represented by a line - number and module name in the traceback. - """ - - # The name of the module; can be None, especially when the code - # isn't associated with a module. - modname = None - # The filename (@@: when no filename, is it None or '?'?) - filename = None - # Line number - lineno = None - # The value of __revision__ or __version__ -- but only if - # show_revision = True (by defaut it is false). (@@: Why not - # collect this?) - revision = None - # The name of the function with the error (@@: None or '?' when - # unknown?) - name = None - # A SupplementaryData object, if __traceback_supplement__ was found - # (and produced no errors) - supplement = None - # If accessing __traceback_supplement__ causes any error, the - # plain-text traceback is stored here - supplement_exception = None - # The str() of any __traceback_info__ value found - traceback_info = None - # The value of __traceback_hide__ - traceback_hide = False - # The value of __traceback_decorator__ - traceback_decorator = None - # The id() of the traceback scope, can be used to reference the - # scope for use elsewhere - tbid = None - - def get_source_line(self, context=0): - """ - Return the source of the current line of this frame. You - probably want to .strip() it as well, as it is likely to have - leading whitespace. - - If context is given, then that many lines on either side will - also be returned. E.g., context=1 will give 3 lines. - """ - if not self.filename or not self.lineno: - return None - lines = [] - for lineno in range(self.lineno-context, self.lineno+context+1): - lines.append(linecache.getline(self.filename, lineno)) - return ''.join(lines) - -if hasattr(sys, 'tracebacklimit'): - limit = min(limit, sys.tracebacklimit) - -col = ExceptionCollector() - -def collect_exception(t, v, tb, limit=None): - """ - Collection an exception from ``sys.exc_info()``. - - Use like:: - - try: - blah blah - except: - exc_data = collect_exception(*sys.exc_info()) - """ - return col.collectException(t, v, tb, limit=limit) diff --git a/kcdc3/paste/exceptions/errormiddleware.py b/kcdc3/paste/exceptions/errormiddleware.py deleted file mode 100644 index 784414fd..00000000 --- a/kcdc3/paste/exceptions/errormiddleware.py +++ /dev/null @@ -1,460 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php - -""" -Error handler middleware -""" -import sys -import traceback -import cgi -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO -from paste.exceptions import formatter, collector, reporter -from paste import wsgilib -from paste import request - -__all__ = ['ErrorMiddleware', 'handle_exception'] - -class _NoDefault(object): - def __repr__(self): - return '' -NoDefault = _NoDefault() - -class ErrorMiddleware(object): - - """ - Error handling middleware - - Usage:: - - error_catching_wsgi_app = ErrorMiddleware(wsgi_app) - - Settings: - - ``debug``: - If true, then tracebacks will be shown in the browser. - - ``error_email``: - an email address (or list of addresses) to send exception - reports to - - ``error_log``: - a filename to append tracebacks to - - ``show_exceptions_in_wsgi_errors``: - If true, then errors will be printed to ``wsgi.errors`` - (frequently a server error log, or stderr). - - ``from_address``, ``smtp_server``, ``error_subject_prefix``, ``smtp_username``, ``smtp_password``, ``smtp_use_tls``: - variables to control the emailed exception reports - - ``error_message``: - When debug mode is off, the error message to show to users. - - ``xmlhttp_key``: - When this key (default ``_``) is in the request GET variables - (not POST!), expect that this is an XMLHttpRequest, and the - response should be more minimal; it should not be a complete - HTML page. - - Environment Configuration: - - ``paste.throw_errors``: - If this setting in the request environment is true, then this - middleware is disabled. This can be useful in a testing situation - where you don't want errors to be caught and transformed. - - ``paste.expected_exceptions``: - When this middleware encounters an exception listed in this - environment variable and when the ``start_response`` has not - yet occurred, the exception will be re-raised instead of being - caught. This should generally be set by middleware that may - (but probably shouldn't be) installed above this middleware, - and wants to get certain exceptions. Exceptions raised after - ``start_response`` have been called are always caught since - by definition they are no longer expected. - - """ - - def __init__(self, application, global_conf=None, - debug=NoDefault, - error_email=None, - error_log=None, - show_exceptions_in_wsgi_errors=NoDefault, - from_address=None, - smtp_server=None, - smtp_username=None, - smtp_password=None, - smtp_use_tls=False, - error_subject_prefix=None, - error_message=None, - xmlhttp_key=None): - from paste.util import converters - self.application = application - # @@: global_conf should be handled elsewhere in a separate - # function for the entry point - if global_conf is None: - global_conf = {} - if debug is NoDefault: - debug = converters.asbool(global_conf.get('debug')) - if show_exceptions_in_wsgi_errors is NoDefault: - show_exceptions_in_wsgi_errors = converters.asbool(global_conf.get('show_exceptions_in_wsgi_errors')) - self.debug_mode = converters.asbool(debug) - if error_email is None: - error_email = (global_conf.get('error_email') - or global_conf.get('admin_email') - or global_conf.get('webmaster_email') - or global_conf.get('sysadmin_email')) - self.error_email = converters.aslist(error_email) - self.error_log = error_log - self.show_exceptions_in_wsgi_errors = show_exceptions_in_wsgi_errors - if from_address is None: - from_address = global_conf.get('error_from_address', 'errors@localhost') - self.from_address = from_address - if smtp_server is None: - smtp_server = global_conf.get('smtp_server', 'localhost') - self.smtp_server = smtp_server - self.smtp_username = smtp_username or global_conf.get('smtp_username') - self.smtp_password = smtp_password or global_conf.get('smtp_password') - self.smtp_use_tls = smtp_use_tls or converters.asbool(global_conf.get('smtp_use_tls')) - self.error_subject_prefix = error_subject_prefix or '' - if error_message is None: - error_message = global_conf.get('error_message') - self.error_message = error_message - if xmlhttp_key is None: - xmlhttp_key = global_conf.get('xmlhttp_key', '_') - self.xmlhttp_key = xmlhttp_key - - def __call__(self, environ, start_response): - """ - The WSGI application interface. - """ - # We want to be careful about not sending headers twice, - # and the content type that the app has committed to (if there - # is an exception in the iterator body of the response) - if environ.get('paste.throw_errors'): - return self.application(environ, start_response) - environ['paste.throw_errors'] = True - - try: - __traceback_supplement__ = Supplement, self, environ - sr_checker = ResponseStartChecker(start_response) - app_iter = self.application(environ, sr_checker) - return self.make_catching_iter(app_iter, environ, sr_checker) - except: - exc_info = sys.exc_info() - try: - for expect in environ.get('paste.expected_exceptions', []): - if isinstance(exc_info[1], expect): - raise - start_response('500 Internal Server Error', - [('content-type', 'text/html')], - exc_info) - # @@: it would be nice to deal with bad content types here - response = self.exception_handler(exc_info, environ) - return [response] - finally: - # clean up locals... - exc_info = None - - def make_catching_iter(self, app_iter, environ, sr_checker): - if isinstance(app_iter, (list, tuple)): - # These don't raise - return app_iter - return CatchingIter(app_iter, environ, sr_checker, self) - - def exception_handler(self, exc_info, environ): - simple_html_error = False - if self.xmlhttp_key: - get_vars = wsgilib.parse_querystring(environ) - if dict(get_vars).get(self.xmlhttp_key): - simple_html_error = True - return handle_exception( - exc_info, environ['wsgi.errors'], - html=True, - debug_mode=self.debug_mode, - error_email=self.error_email, - error_log=self.error_log, - show_exceptions_in_wsgi_errors=self.show_exceptions_in_wsgi_errors, - error_email_from=self.from_address, - smtp_server=self.smtp_server, - smtp_username=self.smtp_username, - smtp_password=self.smtp_password, - smtp_use_tls=self.smtp_use_tls, - error_subject_prefix=self.error_subject_prefix, - error_message=self.error_message, - simple_html_error=simple_html_error) - -class ResponseStartChecker(object): - def __init__(self, start_response): - self.start_response = start_response - self.response_started = False - - def __call__(self, *args): - self.response_started = True - self.start_response(*args) - -class CatchingIter(object): - - """ - A wrapper around the application iterator that will catch - exceptions raised by the a generator, or by the close method, and - display or report as necessary. - """ - - def __init__(self, app_iter, environ, start_checker, error_middleware): - self.app_iterable = app_iter - self.app_iterator = iter(app_iter) - self.environ = environ - self.start_checker = start_checker - self.error_middleware = error_middleware - self.closed = False - - def __iter__(self): - return self - - def next(self): - __traceback_supplement__ = ( - Supplement, self.error_middleware, self.environ) - if self.closed: - raise StopIteration - try: - return self.app_iterator.next() - except StopIteration: - self.closed = True - close_response = self._close() - if close_response is not None: - return close_response - else: - raise StopIteration - except: - self.closed = True - close_response = self._close() - exc_info = sys.exc_info() - response = self.error_middleware.exception_handler( - exc_info, self.environ) - if close_response is not None: - response += ( - '
    Error in .close():
    %s' - % close_response) - - if not self.start_checker.response_started: - self.start_checker('500 Internal Server Error', - [('content-type', 'text/html')], - exc_info) - - return response - - def close(self): - # This should at least print something to stderr if the - # close method fails at this point - if not self.closed: - self._close() - - def _close(self): - """Close and return any error message""" - if not hasattr(self.app_iterable, 'close'): - return None - try: - self.app_iterable.close() - return None - except: - close_response = self.error_middleware.exception_handler( - sys.exc_info(), self.environ) - return close_response - - -class Supplement(object): - - """ - This is a supplement used to display standard WSGI information in - the traceback. - """ - - def __init__(self, middleware, environ): - self.middleware = middleware - self.environ = environ - self.source_url = request.construct_url(environ) - - def extraData(self): - data = {} - cgi_vars = data[('extra', 'CGI Variables')] = {} - wsgi_vars = data[('extra', 'WSGI Variables')] = {} - hide_vars = ['paste.config', 'wsgi.errors', 'wsgi.input', - 'wsgi.multithread', 'wsgi.multiprocess', - 'wsgi.run_once', 'wsgi.version', - 'wsgi.url_scheme'] - for name, value in self.environ.items(): - if name.upper() == name: - if value: - cgi_vars[name] = value - elif name not in hide_vars: - wsgi_vars[name] = value - if self.environ['wsgi.version'] != (1, 0): - wsgi_vars['wsgi.version'] = self.environ['wsgi.version'] - proc_desc = tuple([int(bool(self.environ[key])) - for key in ('wsgi.multiprocess', - 'wsgi.multithread', - 'wsgi.run_once')]) - wsgi_vars['wsgi process'] = self.process_combos[proc_desc] - wsgi_vars['application'] = self.middleware.application - if 'paste.config' in self.environ: - data[('extra', 'Configuration')] = dict(self.environ['paste.config']) - return data - - process_combos = { - # multiprocess, multithread, run_once - (0, 0, 0): 'Non-concurrent server', - (0, 1, 0): 'Multithreaded', - (1, 0, 0): 'Multiprocess', - (1, 1, 0): 'Multi process AND threads (?)', - (0, 0, 1): 'Non-concurrent CGI', - (0, 1, 1): 'Multithread CGI (?)', - (1, 0, 1): 'CGI', - (1, 1, 1): 'Multi thread/process CGI (?)', - } - -def handle_exception(exc_info, error_stream, html=True, - debug_mode=False, - error_email=None, - error_log=None, - show_exceptions_in_wsgi_errors=False, - error_email_from='errors@localhost', - smtp_server='localhost', - smtp_username=None, - smtp_password=None, - smtp_use_tls=False, - error_subject_prefix='', - error_message=None, - simple_html_error=False, - ): - """ - For exception handling outside of a web context - - Use like:: - - import sys - from paste.exceptions.errormiddleware import handle_exception - try: - do stuff - except: - handle_exception( - sys.exc_info(), sys.stderr, html=False, ...other config...) - - If you want to report, but not fully catch the exception, call - ``raise`` after ``handle_exception``, which (when given no argument) - will reraise the exception. - """ - reported = False - exc_data = collector.collect_exception(*exc_info) - extra_data = '' - if error_email: - rep = reporter.EmailReporter( - to_addresses=error_email, - from_address=error_email_from, - smtp_server=smtp_server, - smtp_username=smtp_username, - smtp_password=smtp_password, - smtp_use_tls=smtp_use_tls, - subject_prefix=error_subject_prefix) - rep_err = send_report(rep, exc_data, html=html) - if rep_err: - extra_data += rep_err - else: - reported = True - if error_log: - rep = reporter.LogReporter( - filename=error_log) - rep_err = send_report(rep, exc_data, html=html) - if rep_err: - extra_data += rep_err - else: - reported = True - if show_exceptions_in_wsgi_errors: - rep = reporter.FileReporter( - file=error_stream) - rep_err = send_report(rep, exc_data, html=html) - if rep_err: - extra_data += rep_err - else: - reported = True - else: - error_stream.write('Error - %s: %s\n' % ( - exc_data.exception_type, exc_data.exception_value)) - if html: - if debug_mode and simple_html_error: - return_error = formatter.format_html( - exc_data, include_hidden_frames=False, - include_reusable=False, show_extra_data=False) - reported = True - elif debug_mode and not simple_html_error: - error_html = formatter.format_html( - exc_data, - include_hidden_frames=True, - include_reusable=False) - head_html = formatter.error_css + formatter.hide_display_js - return_error = error_template( - head_html, error_html, extra_data) - extra_data = '' - reported = True - else: - msg = error_message or ''' - An error occurred. See the error logs for more information. - (Turn debug on to display exception reports here) - ''' - return_error = error_template('', msg, '') - else: - return_error = None - if not reported and error_stream: - err_report = formatter.format_text(exc_data, show_hidden_frames=True) - err_report += '\n' + '-'*60 + '\n' - error_stream.write(err_report) - if extra_data: - error_stream.write(extra_data) - return return_error - -def send_report(rep, exc_data, html=True): - try: - rep.report(exc_data) - except: - output = StringIO() - traceback.print_exc(file=output) - if html: - return """ -

    Additionally an error occurred while sending the %s report: - -

    %s
    -

    """ % ( - cgi.escape(str(rep)), output.getvalue()) - else: - return ( - "Additionally an error occurred while sending the " - "%s report:\n%s" % (str(rep), output.getvalue())) - else: - return '' - -def error_template(head_html, exception, extra): - return ''' - - - Server Error - %s - - -

    Server Error

    - %s - %s - - ''' % (head_html, exception, extra) - -def make_error_middleware(app, global_conf, **kw): - return ErrorMiddleware(app, global_conf=global_conf, **kw) - -doc_lines = ErrorMiddleware.__doc__.splitlines(True) -for i in range(len(doc_lines)): - if doc_lines[i].strip().startswith('Settings'): - make_error_middleware.__doc__ = ''.join(doc_lines[i:]) - break -del i, doc_lines diff --git a/kcdc3/paste/exceptions/formatter.py b/kcdc3/paste/exceptions/formatter.py deleted file mode 100644 index e1fadbec..00000000 --- a/kcdc3/paste/exceptions/formatter.py +++ /dev/null @@ -1,564 +0,0 @@ -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php - -""" -Formatters for the exception data that comes from ExceptionCollector. -""" -# @@: TODO: -# Use this: http://www.zope.org/Members/tino/VisualTraceback/VisualTracebackNews - -import cgi -import re -from paste.util import PySourceColor - -def html_quote(s): - return cgi.escape(str(s), True) - -class AbstractFormatter(object): - - general_data_order = ['object', 'source_url'] - - def __init__(self, show_hidden_frames=False, - include_reusable=True, - show_extra_data=True, - trim_source_paths=()): - self.show_hidden_frames = show_hidden_frames - self.trim_source_paths = trim_source_paths - self.include_reusable = include_reusable - self.show_extra_data = show_extra_data - - def format_collected_data(self, exc_data): - general_data = {} - if self.show_extra_data: - for name, value_list in exc_data.extra_data.items(): - if isinstance(name, tuple): - importance, title = name - else: - importance, title = 'normal', name - for value in value_list: - general_data[(importance, name)] = self.format_extra_data( - importance, title, value) - lines = [] - frames = self.filter_frames(exc_data.frames) - for frame in frames: - sup = frame.supplement - if sup: - if sup.object: - general_data[('important', 'object')] = self.format_sup_object( - sup.object) - if sup.source_url: - general_data[('important', 'source_url')] = self.format_sup_url( - sup.source_url) - if sup.line: - lines.append(self.format_sup_line_pos(sup.line, sup.column)) - if sup.expression: - lines.append(self.format_sup_expression(sup.expression)) - if sup.warnings: - for warning in sup.warnings: - lines.append(self.format_sup_warning(warning)) - if sup.info: - lines.extend(self.format_sup_info(sup.info)) - if frame.supplement_exception: - lines.append('Exception in supplement:') - lines.append(self.quote_long(frame.supplement_exception)) - if frame.traceback_info: - lines.append(self.format_traceback_info(frame.traceback_info)) - filename = frame.filename - if filename and self.trim_source_paths: - for path, repl in self.trim_source_paths: - if filename.startswith(path): - filename = repl + filename[len(path):] - break - lines.append(self.format_source_line(filename or '?', frame)) - source = frame.get_source_line() - long_source = frame.get_source_line(2) - if source: - lines.append(self.format_long_source( - source, long_source)) - etype = exc_data.exception_type - if not isinstance(etype, basestring): - etype = etype.__name__ - exc_info = self.format_exception_info( - etype, - exc_data.exception_value) - data_by_importance = {'important': [], 'normal': [], - 'supplemental': [], 'extra': []} - for (importance, name), value in general_data.items(): - data_by_importance[importance].append( - (name, value)) - for value in data_by_importance.values(): - value.sort() - return self.format_combine(data_by_importance, lines, exc_info) - - def filter_frames(self, frames): - """ - Removes any frames that should be hidden, according to the - values of traceback_hide, self.show_hidden_frames, and the - hidden status of the final frame. - """ - if self.show_hidden_frames: - return frames - new_frames = [] - hidden = False - for frame in frames: - hide = frame.traceback_hide - # @@: It would be nice to signal a warning if an unknown - # hide string was used, but I'm not sure where to put - # that warning. - if hide == 'before': - new_frames = [] - hidden = False - elif hide == 'before_and_this': - new_frames = [] - hidden = False - continue - elif hide == 'reset': - hidden = False - elif hide == 'reset_and_this': - hidden = False - continue - elif hide == 'after': - hidden = True - elif hide == 'after_and_this': - hidden = True - continue - elif hide: - continue - elif hidden: - continue - new_frames.append(frame) - if frames[-1] not in new_frames: - # We must include the last frame; that we don't indicates - # that the error happened where something was "hidden", - # so we just have to show everything - return frames - return new_frames - - def pretty_string_repr(self, s): - """ - Formats the string as a triple-quoted string when it contains - newlines. - """ - if '\n' in s: - s = repr(s) - s = s[0]*3 + s[1:-1] + s[-1]*3 - s = s.replace('\\n', '\n') - return s - else: - return repr(s) - - def long_item_list(self, lst): - """ - Returns true if the list contains items that are long, and should - be more nicely formatted. - """ - how_many = 0 - for item in lst: - if len(repr(item)) > 40: - how_many += 1 - if how_many >= 3: - return True - return False - -class TextFormatter(AbstractFormatter): - - def quote(self, s): - return s - def quote_long(self, s): - return s - def emphasize(self, s): - return s - def format_sup_object(self, obj): - return 'In object: %s' % self.emphasize(self.quote(repr(obj))) - def format_sup_url(self, url): - return 'URL: %s' % self.quote(url) - def format_sup_line_pos(self, line, column): - if column: - return self.emphasize('Line %i, Column %i' % (line, column)) - else: - return self.emphasize('Line %i' % line) - def format_sup_expression(self, expr): - return self.emphasize('In expression: %s' % self.quote(expr)) - def format_sup_warning(self, warning): - return 'Warning: %s' % self.quote(warning) - def format_sup_info(self, info): - return [self.quote_long(info)] - def format_source_line(self, filename, frame): - return 'File %r, line %s in %s' % ( - filename, frame.lineno or '?', frame.name or '?') - def format_long_source(self, source, long_source): - return self.format_source(source) - def format_source(self, source_line): - return ' ' + self.quote(source_line.strip()) - def format_exception_info(self, etype, evalue): - return self.emphasize( - '%s: %s' % (self.quote(etype), self.quote(evalue))) - def format_traceback_info(self, info): - return info - - def format_combine(self, data_by_importance, lines, exc_info): - lines[:0] = [value for n, value in data_by_importance['important']] - lines.append(exc_info) - for name in 'normal', 'supplemental', 'extra': - lines.extend([value for n, value in data_by_importance[name]]) - return self.format_combine_lines(lines) - - def format_combine_lines(self, lines): - return '\n'.join(lines) - - def format_extra_data(self, importance, title, value): - if isinstance(value, str): - s = self.pretty_string_repr(value) - if '\n' in s: - return '%s:\n%s' % (title, s) - else: - return '%s: %s' % (title, s) - elif isinstance(value, dict): - lines = ['\n', title, '-'*len(title)] - items = value.items() - items.sort() - for n, v in items: - try: - v = repr(v) - except Exception, e: - v = 'Cannot display: %s' % e - v = truncate(v) - lines.append(' %s: %s' % (n, v)) - return '\n'.join(lines) - elif (isinstance(value, (list, tuple)) - and self.long_item_list(value)): - parts = [truncate(repr(v)) for v in value] - return '%s: [\n %s]' % ( - title, ',\n '.join(parts)) - else: - return '%s: %s' % (title, truncate(repr(value))) - -class HTMLFormatter(TextFormatter): - - def quote(self, s): - return html_quote(s) - def quote_long(self, s): - return '
    %s
    ' % self.quote(s) - def emphasize(self, s): - return '%s' % s - def format_sup_url(self, url): - return 'URL: %s' % (url, url) - def format_combine_lines(self, lines): - return '
    \n'.join(lines) - def format_source_line(self, filename, frame): - name = self.quote(frame.name or '?') - return 'Module %s:%s in %s' % ( - filename, frame.modname or '?', frame.lineno or '?', - name) - return 'File %r, line %s in %s' % ( - filename, frame.lineno, name) - def format_long_source(self, source, long_source): - q_long_source = str2html(long_source, False, 4, True) - q_source = str2html(source, True, 0, False) - return ('' - '>>  %s' - % (q_long_source, - q_source)) - def format_source(self, source_line): - return '  %s' % self.quote(source_line.strip()) - def format_traceback_info(self, info): - return '
    %s
    ' % self.quote(info) - - def format_extra_data(self, importance, title, value): - if isinstance(value, str): - s = self.pretty_string_repr(value) - if '\n' in s: - return '%s:
    %s
    ' % (title, self.quote(s)) - else: - return '%s: %s' % (title, self.quote(s)) - elif isinstance(value, dict): - return self.zebra_table(title, value) - elif (isinstance(value, (list, tuple)) - and self.long_item_list(value)): - return '%s: [
    \n    %s]
    ' % ( - title, ',
        '.join(map(self.quote, map(repr, value)))) - else: - return '%s: %s' % (title, self.quote(repr(value))) - - def format_combine(self, data_by_importance, lines, exc_info): - lines[:0] = [value for n, value in data_by_importance['important']] - lines.append(exc_info) - for name in 'normal', 'supplemental': - lines.extend([value for n, value in data_by_importance[name]]) - if data_by_importance['extra']: - lines.append( - '\n' + - '
    \n') - lines.extend([value for n, value in data_by_importance['extra']]) - lines.append('
    ') - text = self.format_combine_lines(lines) - if self.include_reusable: - return error_css + hide_display_js + text - else: - # Usually because another error is already on this page, - # and so the js & CSS are unneeded - return text - - def zebra_table(self, title, rows, table_class="variables"): - if isinstance(rows, dict): - rows = rows.items() - rows.sort() - table = ['' % table_class, - '' - % self.quote(title)] - odd = False - for name, value in rows: - try: - value = repr(value) - except Exception, e: - value = 'Cannot print: %s' % e - odd = not odd - table.append( - '' - % (odd and 'odd' or 'even', self.quote(name))) - table.append( - '' - % make_wrappable(self.quote(truncate(value)))) - table.append('
    %s
    %s%s
    ') - return '\n'.join(table) - -hide_display_js = r''' -''' - - -error_css = """ - -""" - -def format_html(exc_data, include_hidden_frames=False, **ops): - if not include_hidden_frames: - return HTMLFormatter(**ops).format_collected_data(exc_data) - short_er = format_html(exc_data, show_hidden_frames=False, **ops) - # @@: This should have a way of seeing if the previous traceback - # was actually trimmed at all - ops['include_reusable'] = False - ops['show_extra_data'] = False - long_er = format_html(exc_data, show_hidden_frames=True, **ops) - text_er = format_text(exc_data, show_hidden_frames=True, **ops) - return """ - %s -
    - -
    - %s -
    -
    - -
    - -
    - """ % (short_er, long_er, cgi.escape(text_er)) - -def format_text(exc_data, **ops): - return TextFormatter(**ops).format_collected_data(exc_data) - -whitespace_re = re.compile(r' +') -pre_re = re.compile(r'') -error_re = re.compile(r'

    ERROR: .*?

    ') - -def str2html(src, strip=False, indent_subsequent=0, - highlight_inner=False): - """ - Convert a string to HTML. Try to be really safe about it, - returning a quoted version of the string if nothing else works. - """ - try: - return _str2html(src, strip=strip, - indent_subsequent=indent_subsequent, - highlight_inner=highlight_inner) - except: - return html_quote(src) - -def _str2html(src, strip=False, indent_subsequent=0, - highlight_inner=False): - if strip: - src = src.strip() - orig_src = src - try: - src = PySourceColor.str2html(src, form='snip') - src = error_re.sub('', src) - src = pre_re.sub('', src) - src = re.sub(r'^[\n\r]{0,1}', '', src) - src = re.sub(r'[\n\r]{0,1}$', '', src) - except: - src = html_quote(orig_src) - lines = src.splitlines() - if len(lines) == 1: - return lines[0] - indent = ' '*indent_subsequent - for i in range(1, len(lines)): - lines[i] = indent+lines[i] - if highlight_inner and i == len(lines)/2: - lines[i] = '%s' % lines[i] - src = '
    \n'.join(lines) - src = whitespace_re.sub( - lambda m: ' '*(len(m.group(0))-1) + ' ', src) - return src - -def truncate(string, limit=1000): - """ - Truncate the string to the limit number of - characters - """ - if len(string) > limit: - return string[:limit-20]+'...'+string[-17:] - else: - return string - -def make_wrappable(html, wrap_limit=60, - split_on=';?&@!$#-/\\"\''): - # Currently using , maybe should use ​ - # http://www.cs.tut.fi/~jkorpela/html/nobr.html - if len(html) <= wrap_limit: - return html - words = html.split() - new_words = [] - for word in words: - wrapped_word = '' - while len(word) > wrap_limit: - for char in split_on: - if char in word: - first, rest = word.split(char, 1) - wrapped_word += first+char+'' - word = rest - break - else: - for i in range(0, len(word), wrap_limit): - wrapped_word += word[i:i+wrap_limit]+'' - word = '' - wrapped_word += word - new_words.append(wrapped_word) - return ' '.join(new_words) - -def make_pre_wrappable(html, wrap_limit=60, - split_on=';?&@!$#-/\\"\''): - """ - Like ``make_wrappable()`` but intended for text that will - go in a ``
    `` block, so wrap on a line-by-line basis.
    -    """
    -    lines = html.splitlines()
    -    new_lines = []
    -    for line in lines:
    -        if len(line) > wrap_limit:
    -            for char in split_on:
    -                if char in line:
    -                    parts = line.split(char)
    -                    line = ''.join(parts)
    -                    break
    -        new_lines.append(line)
    -    return '\n'.join(lines)
    diff --git a/kcdc3/paste/exceptions/reporter.py b/kcdc3/paste/exceptions/reporter.py
    deleted file mode 100644
    index 95e31ba9..00000000
    --- a/kcdc3/paste/exceptions/reporter.py
    +++ /dev/null
    @@ -1,141 +0,0 @@
    -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
    -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
    -
    -from email.MIMEText import MIMEText
    -from email.MIMEMultipart import MIMEMultipart
    -import smtplib
    -import time
    -try:
    -    from socket import sslerror
    -except ImportError:
    -    sslerror = None
    -from paste.exceptions import formatter
    -
    -class Reporter(object):
    -
    -    def __init__(self, **conf):
    -        for name, value in conf.items():
    -            if not hasattr(self, name):
    -                raise TypeError(
    -                    "The keyword argument %s was not expected"
    -                    % name)
    -            setattr(self, name, value)
    -        self.check_params()
    -
    -    def check_params(self):
    -        pass
    -
    -    def format_date(self, exc_data):
    -        return time.strftime('%c', exc_data.date)
    -
    -    def format_html(self, exc_data, **kw):
    -        return formatter.format_html(exc_data, **kw)
    -
    -    def format_text(self, exc_data, **kw):
    -        return formatter.format_text(exc_data, **kw)
    -
    -class EmailReporter(Reporter):
    -
    -    to_addresses = None
    -    from_address = None
    -    smtp_server = 'localhost'
    -    smtp_username = None
    -    smtp_password = None
    -    smtp_use_tls = False
    -    subject_prefix = ''
    -
    -    def report(self, exc_data):
    -        msg = self.assemble_email(exc_data)
    -        server = smtplib.SMTP(self.smtp_server)
    -        if self.smtp_use_tls:
    -            server.ehlo()
    -            server.starttls()
    -            server.ehlo()
    -        if self.smtp_username and self.smtp_password:
    -            server.login(self.smtp_username, self.smtp_password)
    -        server.sendmail(self.from_address,
    -                        self.to_addresses, msg.as_string())
    -        try:
    -            server.quit()
    -        except sslerror:
    -            # sslerror is raised in tls connections on closing sometimes
    -            pass
    -
    -    def check_params(self):
    -        if not self.to_addresses:
    -            raise ValueError("You must set to_addresses")
    -        if not self.from_address:
    -            raise ValueError("You must set from_address")
    -        if isinstance(self.to_addresses, (str, unicode)):
    -            self.to_addresses = [self.to_addresses]
    -
    -    def assemble_email(self, exc_data):
    -        short_html_version = self.format_html(
    -            exc_data, show_hidden_frames=False)
    -        long_html_version = self.format_html(
    -            exc_data, show_hidden_frames=True)
    -        text_version = self.format_text(
    -            exc_data, show_hidden_frames=False)
    -        msg = MIMEMultipart()
    -        msg.set_type('multipart/alternative')
    -        msg.preamble = msg.epilogue = ''
    -        text_msg = MIMEText(text_version)
    -        text_msg.set_type('text/plain')
    -        text_msg.set_param('charset', 'ASCII')
    -        msg.attach(text_msg)
    -        html_msg = MIMEText(short_html_version)
    -        html_msg.set_type('text/html')
    -        # @@: Correct character set?
    -        html_msg.set_param('charset', 'UTF-8')
    -        html_long = MIMEText(long_html_version)
    -        html_long.set_type('text/html')
    -        html_long.set_param('charset', 'UTF-8')
    -        msg.attach(html_msg)
    -        msg.attach(html_long)
    -        subject = '%s: %s' % (exc_data.exception_type,
    -                              formatter.truncate(str(exc_data.exception_value)))
    -        msg['Subject'] = self.subject_prefix + subject
    -        msg['From'] = self.from_address
    -        msg['To'] = ', '.join(self.to_addresses)
    -        return msg
    -
    -class LogReporter(Reporter):
    -
    -    filename = None
    -    show_hidden_frames = True
    -
    -    def check_params(self):
    -        assert self.filename is not None, (
    -            "You must give a filename")
    -
    -    def report(self, exc_data):
    -        text = self.format_text(
    -            exc_data, show_hidden_frames=self.show_hidden_frames)
    -        f = open(self.filename, 'a')
    -        try:
    -            f.write(text + '\n' + '-'*60 + '\n')
    -        finally:
    -            f.close()
    -
    -class FileReporter(Reporter):
    -
    -    file = None
    -    show_hidden_frames = True
    -
    -    def check_params(self):
    -        assert self.file is not None, (
    -            "You must give a file object")
    -
    -    def report(self, exc_data):
    -        text = self.format_text(
    -            exc_data, show_hidden_frames=self.show_hidden_frames)
    -        self.file.write(text + '\n' + '-'*60 + '\n')
    -
    -class WSGIAppReporter(Reporter):
    -
    -    def __init__(self, exc_data):
    -        self.exc_data = exc_data
    -
    -    def __call__(self, environ, start_response):
    -        start_response('500 Server Error', [('Content-type', 'text/html')])
    -        return [formatter.format_html(self.exc_data)]
    diff --git a/kcdc3/paste/exceptions/serial_number_generator.py b/kcdc3/paste/exceptions/serial_number_generator.py
    deleted file mode 100644
    index 02896638..00000000
    --- a/kcdc3/paste/exceptions/serial_number_generator.py
    +++ /dev/null
    @@ -1,123 +0,0 @@
    -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
    -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
    -
    -"""
    -Creates a human-readable identifier, using numbers and digits,
    -avoiding ambiguous numbers and letters.  hash_identifier can be used
    -to create compact representations that are unique for a certain string
    -(or concatenation of strings)
    -"""
    -
    -try:
    -    from hashlib import md5
    -except ImportError:
    -    from md5 import md5
    -
    -good_characters = "23456789abcdefghjkmnpqrtuvwxyz"
    -
    -base = len(good_characters)
    -
    -def make_identifier(number):
    -    """
    -    Encodes a number as an identifier.
    -    """
    -    if not isinstance(number, (int, long)):
    -        raise ValueError(
    -            "You can only make identifiers out of integers (not %r)"
    -            % number)
    -    if number < 0:
    -        raise ValueError(
    -            "You cannot make identifiers out of negative numbers: %r"
    -            % number)
    -    result = []
    -    while number:
    -        next = number % base
    -        result.append(good_characters[next])
    -        # Note, this depends on integer rounding of results:
    -        number = number / base
    -    return ''.join(result)
    -
    -def hash_identifier(s, length, pad=True, hasher=md5, prefix='',
    -                    group=None, upper=False):
    -    """
    -    Hashes the string (with the given hashing module), then turns that
    -    hash into an identifier of the given length (using modulo to
    -    reduce the length of the identifier).  If ``pad`` is False, then
    -    the minimum-length identifier will be used; otherwise the
    -    identifier will be padded with 0's as necessary.
    -
    -    ``prefix`` will be added last, and does not count towards the
    -    target length.  ``group`` will group the characters with ``-`` in
    -    the given lengths, and also does not count towards the target
    -    length.  E.g., ``group=4`` will cause a identifier like
    -    ``a5f3-hgk3-asdf``.  Grouping occurs before the prefix.
    -    """
    -    if not callable(hasher):
    -        # Accept sha/md5 modules as well as callables
    -        hasher = hasher.new
    -    if length > 26 and hasher is md5:
    -        raise ValueError, (
    -            "md5 cannot create hashes longer than 26 characters in "
    -            "length (you gave %s)" % length)
    -    if isinstance(s, unicode):
    -        s = s.encode('utf-8')
    -    h = hasher(str(s))
    -    bin_hash = h.digest()
    -    modulo = base ** length
    -    number = 0
    -    for c in list(bin_hash):
    -        number = (number * 256 + ord(c)) % modulo
    -    ident = make_identifier(number)
    -    if pad:
    -        ident = good_characters[0]*(length-len(ident)) + ident
    -    if group:
    -        parts = []
    -        while ident:
    -            parts.insert(0, ident[-group:])
    -            ident = ident[:-group]
    -        ident = '-'.join(parts)
    -    if upper:
    -        ident = ident.upper()
    -    return prefix + ident
    -
    -# doctest tests:
    -__test__ = {
    -    'make_identifier': """
    -    >>> make_identifier(0)
    -    ''
    -    >>> make_identifier(1000)
    -    'c53'
    -    >>> make_identifier(-100)
    -    Traceback (most recent call last):
    -        ...
    -    ValueError: You cannot make identifiers out of negative numbers: -100
    -    >>> make_identifier('test')
    -    Traceback (most recent call last):
    -        ...
    -    ValueError: You can only make identifiers out of integers (not 'test')
    -    >>> make_identifier(1000000000000)
    -    'c53x9rqh3'
    -    """,
    -    'hash_identifier': """
    -    >>> hash_identifier(0, 5)
    -    'cy2dr'
    -    >>> hash_identifier(0, 10)
    -    'cy2dr6rg46'
    -    >>> hash_identifier('this is a test of a long string', 5)
    -    'awatu'
    -    >>> hash_identifier(0, 26)
    -    'cy2dr6rg46cx8t4w2f3nfexzk4'
    -    >>> hash_identifier(0, 30)
    -    Traceback (most recent call last):
    -        ...
    -    ValueError: md5 cannot create hashes longer than 26 characters in length (you gave 30)
    -    >>> hash_identifier(0, 10, group=4)
    -    'cy-2dr6-rg46'
    -    >>> hash_identifier(0, 10, group=4, upper=True, prefix='M-')
    -    'M-CY-2DR6-RG46'
    -    """}
    -
    -if __name__ == '__main__':
    -    import doctest
    -    doctest.testmod()
    -    
    diff --git a/kcdc3/paste/fileapp.py b/kcdc3/paste/fileapp.py
    deleted file mode 100644
    index 84325116..00000000
    --- a/kcdc3/paste/fileapp.py
    +++ /dev/null
    @@ -1,354 +0,0 @@
    -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
    -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
    -# (c) 2005 Ian Bicking, Clark C. Evans and contributors
    -# This module is part of the Python Paste Project and is released under
    -# the MIT License: http://www.opensource.org/licenses/mit-license.php
    -"""
    -This module handles sending static content such as in-memory data or
    -files.  At this time it has cache helpers and understands the
    -if-modified-since request header.
    -"""
    -
    -import os, time, mimetypes, zipfile, tarfile
    -from paste.httpexceptions import *
    -from paste.httpheaders import *
    -
    -CACHE_SIZE = 4096
    -BLOCK_SIZE = 4096 * 16
    -
    -__all__ = ['DataApp', 'FileApp', 'DirectoryApp', 'ArchiveStore']
    -
    -class DataApp(object):
    -    """
    -    Returns an application that will send content in a single chunk,
    -    this application has support for setting cache-control and for
    -    responding to conditional (or HEAD) requests.
    -
    -    Constructor Arguments:
    -
    -        ``content``     the content being sent to the client
    -
    -        ``headers``     the headers to send /w the response
    -
    -        The remaining ``kwargs`` correspond to headers, where the
    -        underscore is replaced with a dash.  These values are only
    -        added to the headers if they are not already provided; thus,
    -        they can be used for default values.  Examples include, but
    -        are not limited to:
    -
    -            ``content_type``
    -            ``content_encoding``
    -            ``content_location``
    -
    -    ``cache_control()``
    -
    -        This method provides validated construction of the ``Cache-Control``
    -        header as well as providing for automated filling out of the
    -        ``EXPIRES`` header for HTTP/1.0 clients.
    -
    -    ``set_content()``
    -
    -        This method provides a mechanism to set the content after the
    -        application has been constructed.  This method does things
    -        like changing ``Last-Modified`` and ``Content-Length`` headers.
    -
    -    """
    -
    -    allowed_methods = ('GET', 'HEAD')
    -
    -    def __init__(self, content, headers=None, allowed_methods=None,
    -                 **kwargs):
    -        assert isinstance(headers, (type(None), list))
    -        self.expires = None
    -        self.content = None
    -        self.content_length = None
    -        self.last_modified = 0
    -        if allowed_methods is not None:
    -            self.allowed_methods = allowed_methods
    -        self.headers = headers or []
    -        for (k, v) in kwargs.items():
    -            header = get_header(k)
    -            header.update(self.headers, v)
    -        ACCEPT_RANGES.update(self.headers, bytes=True)
    -        if not CONTENT_TYPE(self.headers):
    -            CONTENT_TYPE.update(self.headers)
    -        if content is not None:
    -            self.set_content(content)
    -
    -    def cache_control(self, **kwargs):
    -        self.expires = CACHE_CONTROL.apply(self.headers, **kwargs) or None
    -        return self
    -
    -    def set_content(self, content, last_modified=None):
    -        assert content is not None
    -        if last_modified is None:
    -            self.last_modified = time.time()
    -        else:
    -            self.last_modified = last_modified
    -        self.content = content
    -        self.content_length = len(content)
    -        LAST_MODIFIED.update(self.headers, time=self.last_modified)
    -        return self
    -
    -    def content_disposition(self, **kwargs):
    -        CONTENT_DISPOSITION.apply(self.headers, **kwargs)
    -        return self
    -
    -    def __call__(self, environ, start_response):
    -        method = environ['REQUEST_METHOD'].upper()
    -        if method not in self.allowed_methods:
    -            exc = HTTPMethodNotAllowed(
    -                'You cannot %s a file' % method,
    -                headers=[('Allow', ','.join(self.allowed_methods))])
    -            return exc(environ, start_response)
    -        return self.get(environ, start_response)
    -
    -    def calculate_etag(self):
    -        return '"%s-%s"' % (self.last_modified, self.content_length)
    -
    -    def get(self, environ, start_response):
    -        headers = self.headers[:]
    -        current_etag = self.calculate_etag()
    -        ETAG.update(headers, current_etag)
    -        if self.expires is not None:
    -            EXPIRES.update(headers, delta=self.expires)
    -
    -        try:
    -            client_etags = IF_NONE_MATCH.parse(environ)
    -            if client_etags:
    -                for etag in client_etags:
    -                    if etag == current_etag or etag == '*':
    -                        # horribly inefficient, n^2 performance, yuck!
    -                        for head in list_headers(entity=True):
    -                            head.delete(headers)
    -                        start_response('304 Not Modified', headers)
    -                        return ['']
    -        except HTTPBadRequest, exce:
    -            return exce.wsgi_application(environ, start_response)
    -
    -        # If we get If-None-Match and If-Modified-Since, and
    -        # If-None-Match doesn't match, then we should not try to
    -        # figure out If-Modified-Since (which has 1-second granularity
    -        # and just isn't as accurate)
    -        if not client_etags:
    -            try:
    -                client_clock = IF_MODIFIED_SINCE.parse(environ)
    -                if client_clock >= int(self.last_modified):
    -                    # horribly inefficient, n^2 performance, yuck!
    -                    for head in list_headers(entity=True):
    -                        head.delete(headers)
    -                    start_response('304 Not Modified', headers)
    -                    return [''] # empty body
    -            except HTTPBadRequest, exce:
    -                return exce.wsgi_application(environ, start_response)
    -
    -        (lower, upper) = (0, self.content_length - 1)
    -        range = RANGE.parse(environ)
    -        if range and 'bytes' == range[0] and 1 == len(range[1]):
    -            (lower, upper) = range[1][0]
    -            upper = upper or (self.content_length - 1)
    -            if upper >= self.content_length or lower > upper:
    -                return HTTPRequestRangeNotSatisfiable((
    -                  "Range request was made beyond the end of the content,\r\n"
    -                  "which is %s long.\r\n  Range: %s\r\n") % (
    -                     self.content_length, RANGE(environ))
    -                ).wsgi_application(environ, start_response)
    -
    -        content_length = upper - lower + 1
    -        CONTENT_RANGE.update(headers, first_byte=lower, last_byte=upper,
    -                            total_length = self.content_length)
    -        CONTENT_LENGTH.update(headers, content_length)
    -        if content_length == self.content_length:
    -            start_response('200 OK', headers)
    -        else:
    -            start_response('206 Partial Content', headers)
    -        if self.content is not None:
    -            return [self.content[lower:upper+1]]
    -        return (lower, content_length)
    -
    -class FileApp(DataApp):
    -    """
    -    Returns an application that will send the file at the given
    -    filename.  Adds a mime type based on ``mimetypes.guess_type()``.
    -    See DataApp for the arguments beyond ``filename``.
    -    """
    -
    -    def __init__(self, filename, headers=None, **kwargs):
    -        self.filename = filename
    -        content_type, content_encoding = self.guess_type()
    -        if content_type and 'content_type' not in kwargs:
    -            kwargs['content_type'] = content_type
    -        if content_encoding and 'content_encoding' not in kwargs:
    -            kwargs['content_encoding'] = content_encoding
    -        DataApp.__init__(self, None, headers, **kwargs)
    -
    -    def guess_type(self):
    -        return mimetypes.guess_type(self.filename)
    -
    -    def update(self, force=False):
    -        stat = os.stat(self.filename)
    -        if not force and stat.st_mtime == self.last_modified:
    -            return
    -        self.last_modified = stat.st_mtime
    -        if stat.st_size < CACHE_SIZE:
    -            fh = open(self.filename,"rb")
    -            self.set_content(fh.read(), stat.st_mtime)
    -            fh.close()
    -        else:
    -            self.content = None
    -            self.content_length = stat.st_size
    -            # This is updated automatically if self.set_content() is
    -            # called
    -            LAST_MODIFIED.update(self.headers, time=self.last_modified)
    -
    -    def get(self, environ, start_response):
    -        is_head = environ['REQUEST_METHOD'].upper() == 'HEAD'
    -        if 'max-age=0' in CACHE_CONTROL(environ).lower():
    -            self.update(force=True) # RFC 2616 13.2.6
    -        else:
    -            self.update()
    -        if not self.content:
    -            if not os.path.exists(self.filename):
    -                exc = HTTPNotFound(
    -                    'The resource does not exist',
    -                    comment="No file at %r" % self.filename)
    -                return exc(environ, start_response)
    -            try:
    -                file = open(self.filename, 'rb')
    -            except (IOError, OSError), e:
    -                exc = HTTPForbidden(
    -                    'You are not permitted to view this file (%s)' % e)
    -                return exc.wsgi_application(
    -                    environ, start_response)
    -        retval = DataApp.get(self, environ, start_response)
    -        if isinstance(retval, list):
    -            # cached content, exception, or not-modified
    -            if is_head:
    -                return ['']
    -            return retval
    -        (lower, content_length) = retval
    -        if is_head:
    -            return ['']
    -        file.seek(lower)
    -        file_wrapper = environ.get('wsgi.file_wrapper', None)
    -        if file_wrapper:
    -            return file_wrapper(file, BLOCK_SIZE)
    -        else:
    -            return _FileIter(file, size=content_length)
    -
    -class _FileIter(object):
    -
    -    def __init__(self, file, block_size=None, size=None):
    -        self.file = file
    -        self.size = size
    -        self.block_size = block_size or BLOCK_SIZE
    -
    -    def __iter__(self):
    -        return self
    -
    -    def next(self):
    -        chunk_size = self.block_size
    -        if self.size is not None:
    -            if chunk_size > self.size:
    -                chunk_size = self.size
    -            self.size -= chunk_size
    -        data = self.file.read(chunk_size)
    -        if not data:
    -            raise StopIteration
    -        return data
    -
    -    def close(self):
    -        self.file.close()
    -
    -
    -class DirectoryApp(object):
    -    """
    -    Returns an application that dispatches requests to corresponding FileApps based on PATH_INFO.
    -    FileApp instances are cached. This app makes sure not to serve any files that are not in a subdirectory.
    -    To customize FileApp creation override ``DirectoryApp.make_fileapp``
    -    """
    -
    -    def __init__(self, path):
    -        self.path = os.path.abspath(path)
    -        if not self.path.endswith(os.path.sep):
    -            self.path += os.path.sep
    -        assert os.path.isdir(self.path)
    -        self.cached_apps = {}
    -
    -    make_fileapp = FileApp
    -
    -    def __call__(self, environ, start_response):
    -        path_info = environ['PATH_INFO']
    -        app = self.cached_apps.get(path_info)
    -        if app is None:
    -            path = os.path.join(self.path, path_info.lstrip('/'))
    -            if not os.path.normpath(path).startswith(self.path):
    -                app = HTTPForbidden()
    -            elif os.path.isfile(path):
    -                app = self.make_fileapp(path)
    -                self.cached_apps[path_info] = app
    -            else:
    -                app = HTTPNotFound(comment=path)
    -        return app(environ, start_response)
    -
    -
    -class ArchiveStore(object):
    -    """
    -    Returns an application that serves up a DataApp for items requested
    -    in a given zip or tar archive.
    -
    -    Constructor Arguments:
    -
    -        ``filepath``    the path to the archive being served
    -
    -    ``cache_control()``
    -
    -        This method provides validated construction of the ``Cache-Control``
    -        header as well as providing for automated filling out of the
    -        ``EXPIRES`` header for HTTP/1.0 clients.
    -    """
    -
    -    def __init__(self, filepath):
    -        if zipfile.is_zipfile(filepath):
    -            self.archive = zipfile.ZipFile(filepath,"r")
    -        elif tarfile.is_tarfile(filepath):
    -            self.archive = tarfile.TarFileCompat(filepath,"r")
    -        else:
    -            raise AssertionError("filepath '%s' is not a zip or tar " % filepath)
    -        self.expires = None
    -        self.last_modified = time.time()
    -        self.cache = {}
    -
    -    def cache_control(self, **kwargs):
    -        self.expires = CACHE_CONTROL.apply(self.headers, **kwargs) or None
    -        return self
    -
    -    def __call__(self, environ, start_response):
    -        path = environ.get("PATH_INFO","")
    -        if path.startswith("/"):
    -            path = path[1:]
    -        application = self.cache.get(path)
    -        if application:
    -            return application(environ, start_response)
    -        try:
    -            info = self.archive.getinfo(path)
    -        except KeyError:
    -            exc = HTTPNotFound("The file requested, '%s', was not found." % path)
    -            return exc.wsgi_application(environ, start_response)
    -        if info.filename.endswith("/"):
    -            exc = HTTPNotFound("Path requested, '%s', is not a file." % path)
    -            return exc.wsgi_application(environ, start_response)
    -        content_type, content_encoding = mimetypes.guess_type(info.filename)
    -        # 'None' is not a valid content-encoding, so don't set the header if
    -        # mimetypes.guess_type returns None
    -        if content_encoding is not None:
    -            app = DataApp(None, content_type = content_type,
    -                                content_encoding = content_encoding)
    -        else:
    -            app = DataApp(None, content_type = content_type)
    -        app.set_content(self.archive.read(path),
    -                time.mktime(info.date_time + (0,0,0)))
    -        self.cache[path] = app
    -        app.expires = self.expires
    -        return app(environ, start_response)
    -
    diff --git a/kcdc3/paste/fixture.py b/kcdc3/paste/fixture.py
    deleted file mode 100644
    index 242a1dea..00000000
    --- a/kcdc3/paste/fixture.py
    +++ /dev/null
    @@ -1,1725 +0,0 @@
    -# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
    -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
    -"""
    -Routines for testing WSGI applications.
    -
    -Most interesting is the `TestApp `_
    -for testing WSGI applications, and the `TestFileEnvironment
    -`_ class for testing the
    -effects of command-line scripts.
    -"""
    -
    -import sys
    -import random
    -import urllib
    -import urlparse
    -import mimetypes
    -import time
    -import cgi
    -import os
    -import shutil
    -import smtplib
    -import shlex
    -from Cookie import BaseCookie
    -try:
    -    from cStringIO import StringIO
    -except ImportError:
    -    from StringIO import StringIO
    -import re
    -try:
    -    import subprocess
    -except ImportError:
    -    from paste.util import subprocess24 as subprocess
    -
    -from paste import wsgilib
    -from paste import lint
    -from paste.response import HeaderDict
    -
    -def tempnam_no_warning(*args):
    -    """
    -    An os.tempnam with the warning turned off, because sometimes
    -    you just need to use this and don't care about the stupid
    -    security warning.
    -    """
    -    return os.tempnam(*args)
    -
    -class NoDefault(object):
    -    pass
    -
    -def sorted(l):
    -    l = list(l)
    -    l.sort()
    -    return l
    -
    -class Dummy_smtplib(object):
    -
    -    existing = None
    -
    -    def __init__(self, server):
    -        import warnings
    -        warnings.warn(
    -            'Dummy_smtplib is not maintained and is deprecated',
    -            DeprecationWarning, 2)
    -        assert not self.existing, (
    -            "smtplib.SMTP() called again before Dummy_smtplib.existing.reset() "
    -            "called.")
    -        self.server = server
    -        self.open = True
    -        self.__class__.existing = self
    -
    -    def quit(self):
    -        assert self.open, (
    -            "Called %s.quit() twice" % self)
    -        self.open = False
    -
    -    def sendmail(self, from_address, to_addresses, msg):
    -        self.from_address = from_address
    -        self.to_addresses = to_addresses
    -        self.message = msg
    -
    -    def install(cls):
    -        smtplib.SMTP = cls
    -
    -    install = classmethod(install)
    -
    -    def reset(self):
    -        assert not self.open, (
    -            "SMTP connection not quit")
    -        self.__class__.existing = None
    -
    -class AppError(Exception):
    -    pass
    -
    -class TestApp(object):
    -
    -    # for py.test
    -    disabled = True
    -
    -    def __init__(self, app, namespace=None, relative_to=None,
    -                 extra_environ=None, pre_request_hook=None,
    -                 post_request_hook=None):
    -        """
    -        Wraps a WSGI application in a more convenient interface for
    -        testing.
    -
    -        ``app`` may be an application, or a Paste Deploy app
    -        URI, like ``'config:filename.ini#test'``.
    -
    -        ``namespace`` is a dictionary that will be written to (if
    -        provided).  This can be used with doctest or some other
    -        system, and the variable ``res`` will be assigned everytime
    -        you make a request (instead of returning the request).
    -
    -        ``relative_to`` is a directory, and filenames used for file
    -        uploads are calculated relative to this.  Also ``config:``
    -        URIs that aren't absolute.
    -
    -        ``extra_environ`` is a dictionary of values that should go
    -        into the environment for each request.  These can provide a
    -        communication channel with the application.
    -
    -        ``pre_request_hook`` is a function to be called prior to
    -        making requests (such as ``post`` or ``get``). This function
    -        must take one argument (the instance of the TestApp).
    -
    -        ``post_request_hook`` is a function, similar to
    -        ``pre_request_hook``, to be called after requests are made.
    -        """
    -        if isinstance(app, (str, unicode)):
    -            from paste.deploy import loadapp
    -            # @@: Should pick up relative_to from calling module's
    -            # __file__
    -            app = loadapp(app, relative_to=relative_to)
    -        self.app = app
    -        self.namespace = namespace
    -        self.relative_to = relative_to
    -        if extra_environ is None:
    -            extra_environ = {}
    -        self.extra_environ = extra_environ
    -        self.pre_request_hook = pre_request_hook
    -        self.post_request_hook = post_request_hook
    -        self.reset()
    -
    -    def reset(self):
    -        """
    -        Resets the state of the application; currently just clears
    -        saved cookies.
    -        """
    -        self.cookies = {}
    -
    -    def _make_environ(self):
    -        environ = self.extra_environ.copy()
    -        environ['paste.throw_errors'] = True
    -        return environ
    -
    -    def get(self, url, params=None, headers=None, extra_environ=None,
    -            status=None, expect_errors=False):
    -        """
    -        Get the given url (well, actually a path like
    -        ``'/page.html'``).
    -
    -        ``params``:
    -            A query string, or a dictionary that will be encoded
    -            into a query string.  You may also include a query
    -            string on the ``url``.
    -
    -        ``headers``:
    -            A dictionary of extra headers to send.
    -
    -        ``extra_environ``:
    -            A dictionary of environmental variables that should
    -            be added to the request.
    -
    -        ``status``:
    -            The integer status code you expect (if not 200 or 3xx).
    -            If you expect a 404 response, for instance, you must give
    -            ``status=404`` or it will be an error.  You can also give
    -            a wildcard, like ``'3*'`` or ``'*'``.
    -
    -        ``expect_errors``:
    -            If this is not true, then if anything is written to
    -            ``wsgi.errors`` it will be an error.  If it is true, then
    -            non-200/3xx responses are also okay.
    -
    -        Returns a `response object
    -        `_
    -        """
    -        if extra_environ is None:
    -            extra_environ = {}
    -        # Hide from py.test:
    -        __tracebackhide__ = True
    -        if params:
    -            if not isinstance(params, (str, unicode)):
    -                params = urllib.urlencode(params, doseq=True)
    -            if '?' in url:
    -                url += '&'
    -            else:
    -                url += '?'
    -            url += params
    -        environ = self._make_environ()
    -        url = str(url)
    -        if '?' in url:
    -            url, environ['QUERY_STRING'] = url.split('?', 1)
    -        else:
    -            environ['QUERY_STRING'] = ''
    -        self._set_headers(headers, environ)
    -        environ.update(extra_environ)
    -        req = TestRequest(url, environ, expect_errors)
    -        return self.do_request(req, status=status)
    -
    -    def _gen_request(self, method, url, params='', headers=None, extra_environ=None,
    -             status=None, upload_files=None, expect_errors=False):
    -        """
    -        Do a generic request.
    -        """
    -        if headers is None:
    -            headers = {}
    -        if extra_environ is None:
    -            extra_environ = {}
    -        environ = self._make_environ()
    -        # @@: Should this be all non-strings?
    -        if isinstance(params, (list, tuple, dict)):
    -            params = urllib.urlencode(params)
    -        if hasattr(params, 'items'):
    -            # Some other multi-dict like format
    -            params = urllib.urlencode(params.items())
    -        if upload_files:
    -            params = cgi.parse_qsl(params, keep_blank_values=True)
    -            content_type, params = self.encode_multipart(
    -                params, upload_files)
    -            environ['CONTENT_TYPE'] = content_type
    -        elif params:
    -            environ.setdefault('CONTENT_TYPE', 'application/x-www-form-urlencoded')
    -        if '?' in url:
    -            url, environ['QUERY_STRING'] = url.split('?', 1)
    -        else:
    -            environ['QUERY_STRING'] = ''
    -        environ['CONTENT_LENGTH'] = str(len(params))
    -        environ['REQUEST_METHOD'] = method
    -        environ['wsgi.input'] = StringIO(params)
    -        self._set_headers(headers, environ)
    -        environ.update(extra_environ)
    -        req = TestRequest(url, environ, expect_errors)
    -        return self.do_request(req, status=status)
    -
    -    def post(self, url, params='', headers=None, extra_environ=None,
    -             status=None, upload_files=None, expect_errors=False):
    -        """
    -        Do a POST request.  Very like the ``.get()`` method.
    -        ``params`` are put in the body of the request.
    -
    -        ``upload_files`` is for file uploads.  It should be a list of
    -        ``[(fieldname, filename, file_content)]``.  You can also use
    -        just ``[(fieldname, filename)]`` and the file content will be
    -        read from disk.
    -
    -        Returns a `response object
    -        `_
    -        """
    -        return self._gen_request('POST', url, params=params, headers=headers,
    -                                 extra_environ=extra_environ,status=status,
    -                                 upload_files=upload_files,
    -                                 expect_errors=expect_errors)
    -
    -    def put(self, url, params='', headers=None, extra_environ=None,
    -             status=None, upload_files=None, expect_errors=False):
    -        """
    -        Do a PUT request.  Very like the ``.get()`` method.
    -        ``params`` are put in the body of the request.
    -
    -        ``upload_files`` is for file uploads.  It should be a list of
    -        ``[(fieldname, filename, file_content)]``.  You can also use
    -        just ``[(fieldname, filename)]`` and the file content will be
    -        read from disk.
    -
    -        Returns a `response object
    -        `_
    -        """
    -        return self._gen_request('PUT', url, params=params, headers=headers,
    -                                 extra_environ=extra_environ,status=status,
    -                                 upload_files=upload_files,
    -                                 expect_errors=expect_errors)
    -
    -    def delete(self, url, params='', headers=None, extra_environ=None,
    -               status=None, expect_errors=False):
    -        """
    -        Do a DELETE request.  Very like the ``.get()`` method.
    -        ``params`` are put in the body of the request.
    -
    -        Returns a `response object
    -        `_
    -        """
    -        return self._gen_request('DELETE', url, params=params, headers=headers,
    -                                 extra_environ=extra_environ,status=status,
    -                                 upload_files=None, expect_errors=expect_errors)
    -
    -
    -
    -
    -    def _set_headers(self, headers, environ):
    -        """
    -        Turn any headers into environ variables
    -        """
    -        if not headers:
    -            return
    -        for header, value in headers.items():
    -            if header.lower() == 'content-type':
    -                var = 'CONTENT_TYPE'
    -            elif header.lower() == 'content-length':
    -                var = 'CONTENT_LENGTH'
    -            else:
    -                var = 'HTTP_%s' % header.replace('-', '_').upper()
    -            environ[var] = value
    -
    -    def encode_multipart(self, params, files):
    -        """
    -        Encodes a set of parameters (typically a name/value list) and
    -        a set of files (a list of (name, filename, file_body)) into a
    -        typical POST body, returning the (content_type, body).
    -        """
    -        boundary = '----------a_BoUnDaRy%s$' % random.random()
    -        lines = []
    -        for key, value in params:
    -            lines.append('--'+boundary)
    -            lines.append('Content-Disposition: form-data; name="%s"' % key)
    -            lines.append('')
    -            lines.append(value)
    -        for file_info in files:
    -            key, filename, value = self._get_file_info(file_info)
    -            lines.append('--'+boundary)
    -            lines.append('Content-Disposition: form-data; name="%s"; filename="%s"'
    -                         % (key, filename))
    -            fcontent = mimetypes.guess_type(filename)[0]
    -            lines.append('Content-Type: %s' %
    -                         fcontent or 'application/octet-stream')
    -            lines.append('')
    -            lines.append(value)
    -        lines.append('--' + boundary + '--')
    -        lines.append('')
    -        body = '\r\n'.join(lines)
    -        content_type = 'multipart/form-data; boundary=%s' % boundary
    -        return content_type, body
    -
    -    def _get_file_info(self, file_info):
    -        if len(file_info) == 2:
    -            # It only has a filename
    -            filename = file_info[1]
    -            if self.relative_to:
    -                filename = os.path.join(self.relative_to, filename)
    -            f = open(filename, 'rb')
    -            content = f.read()
    -            f.close()
    -            return (file_info[0], filename, content)
    -        elif len(file_info) == 3:
    -            return file_info
    -        else:
    -            raise ValueError(
    -                "upload_files need to be a list of tuples of (fieldname, "
    -                "filename, filecontent) or (fieldname, filename); "
    -                "you gave: %r"
    -                % repr(file_info)[:100])
    -
    -    def do_request(self, req, status):
    -        """
    -        Executes the given request (``req``), with the expected
    -        ``status``.  Generally ``.get()`` and ``.post()`` are used
    -        instead.
    -        """
    -        if self.pre_request_hook:
    -            self.pre_request_hook(self)
    -        __tracebackhide__ = True
    -        if self.cookies:
    -            c = BaseCookie()
    -            for name, value in self.cookies.items():
    -                c[name] = value
    -            hc = '; '.join(['='.join([m.key, m.value]) for m in c.values()])
    -            req.environ['HTTP_COOKIE'] = hc
    -        req.environ['paste.testing'] = True
    -        req.environ['paste.testing_variables'] = {}
    -        app = lint.middleware(self.app)
    -        old_stdout = sys.stdout
    -        out = CaptureStdout(old_stdout)
    -        try:
    -            sys.stdout = out
    -            start_time = time.time()
    -            raise_on_wsgi_error = not req.expect_errors
    -            raw_res = wsgilib.raw_interactive(
    -                app, req.url,
    -                raise_on_wsgi_error=raise_on_wsgi_error,
    -                **req.environ)
    -            end_time = time.time()
    -        finally:
    -            sys.stdout = old_stdout
    -            sys.stderr.write(out.getvalue())
    -        res = self._make_response(raw_res, end_time - start_time)
    -        res.request = req
    -        for name, value in req.environ['paste.testing_variables'].items():
    -            if hasattr(res, name):
    -                raise ValueError(
    -                    "paste.testing_variables contains the variable %r, but "
    -                    "the response object already has an attribute by that "
    -                    "name" % name)
    -            setattr(res, name, value)
    -        if self.namespace is not None:
    -            self.namespace['res'] = res
    -        if not req.expect_errors:
    -            self._check_status(status, res)
    -            self._check_errors(res)
    -        res.cookies_set = {}
    -        for header in res.all_headers('set-cookie'):
    -            c = BaseCookie(header)
    -            for key, morsel in c.items():
    -                self.cookies[key] = morsel.value
    -                res.cookies_set[key] = morsel.value
    -        if self.post_request_hook:
    -            self.post_request_hook(self)
    -        if self.namespace is None:
    -            # It's annoying to return the response in doctests, as it'll
    -            # be printed, so we only return it is we couldn't assign
    -            # it anywhere
    -            return res
    -
    -    def _check_status(self, status, res):
    -        __tracebackhide__ = True
    -        if status == '*':
    -            return
    -        if isinstance(status, (list, tuple)):
    -            if res.status not in status:
    -                raise AppError(
    -                    "Bad response: %s (not one of %s for %s)\n%s"
    -                    % (res.full_status, ', '.join(map(str, status)),
    -                       res.request.url, res.body))
    -            return
    -        if status is None:
    -            if res.status >= 200 and res.status < 400:
    -                return
    -            raise AppError(
    -                "Bad response: %s (not 200 OK or 3xx redirect for %s)\n%s"
    -                % (res.full_status, res.request.url,
    -                   res.body))
    -        if status != res.status:
    -            raise AppError(
    -                "Bad response: %s (not %s)" % (res.full_status, status))
    -
    -    def _check_errors(self, res):
    -        if res.errors:
    -            raise AppError(
    -                "Application had errors logged:\n%s" % res.errors)
    -
    -    def _make_response(self, (status, headers, body, errors), total_time):
    -        return TestResponse(self, status, headers, body, errors,
    -                            total_time)
    -
    -class CaptureStdout(object):
    -
    -    def __init__(self, actual):
    -        self.captured = StringIO()
    -        self.actual = actual
    -
    -    def write(self, s):
    -        self.captured.write(s)
    -        self.actual.write(s)
    -
    -    def flush(self):
    -        self.actual.flush()
    -
    -    def writelines(self, lines):
    -        for item in lines:
    -            self.write(item)
    -
    -    def getvalue(self):
    -        return self.captured.getvalue()
    -
    -class TestResponse(object):
    -
    -    # for py.test
    -    disabled = True
    -
    -    """
    -    Instances of this class are return by `TestApp
    -    `_
    -    """
    -
    -    def __init__(self, test_app, status, headers, body, errors,
    -                 total_time):
    -        self.test_app = test_app
    -        self.status = int(status.split()[0])
    -        self.full_status = status
    -        self.headers = headers
    -        self.header_dict = HeaderDict.fromlist(self.headers)
    -        self.body = body
    -        self.errors = errors
    -        self._normal_body = None
    -        self.time = total_time
    -        self._forms_indexed = None
    -
    -    def forms__get(self):
    -        """
    -        Returns a dictionary of ``Form`` objects.  Indexes are both in
    -        order (from zero) and by form id (if the form is given an id).
    -        """
    -        if self._forms_indexed is None:
    -            self._parse_forms()
    -        return self._forms_indexed
    -
    -    forms = property(forms__get,
    -                     doc="""
    -                     A list of 
    s found on the page (instances of - `Form `_) - """) - - def form__get(self): - forms = self.forms - if not forms: - raise TypeError( - "You used response.form, but no forms exist") - if 1 in forms: - # There is more than one form - raise TypeError( - "You used response.form, but more than one form exists") - return forms[0] - - form = property(form__get, - doc=""" - Returns a single `Form - `_ instance; it - is an error if there are multiple forms on the - page. - """) - - _tag_re = re.compile(r'<(/?)([:a-z0-9_\-]*)(.*?)>', re.S|re.I) - - def _parse_forms(self): - forms = self._forms_indexed = {} - form_texts = [] - started = None - for match in self._tag_re.finditer(self.body): - end = match.group(1) == '/' - tag = match.group(2).lower() - if tag != 'form': - continue - if end: - assert started, ( - " unexpected at %s" % match.start()) - form_texts.append(self.body[started:match.end()]) - started = None - else: - assert not started, ( - "Nested form tags at %s" % match.start()) - started = match.start() - assert not started, ( - "Danging form: %r" % self.body[started:]) - for i, text in enumerate(form_texts): - form = Form(self, text) - forms[i] = form - if form.id: - forms[form.id] = form - - def header(self, name, default=NoDefault): - """ - Returns the named header; an error if there is not exactly one - matching header (unless you give a default -- always an error - if there is more than one header) - """ - found = None - for cur_name, value in self.headers: - if cur_name.lower() == name.lower(): - assert not found, ( - "Ambiguous header: %s matches %r and %r" - % (name, found, value)) - found = value - if found is None: - if default is NoDefault: - raise KeyError( - "No header found: %r (from %s)" - % (name, ', '.join([n for n, v in self.headers]))) - else: - return default - return found - - def all_headers(self, name): - """ - Gets all headers by the ``name``, returns as a list - """ - found = [] - for cur_name, value in self.headers: - if cur_name.lower() == name.lower(): - found.append(value) - return found - - def follow(self, **kw): - """ - If this request is a redirect, follow that redirect. It - is an error if this is not a redirect response. Returns - another response object. - """ - assert self.status >= 300 and self.status < 400, ( - "You can only follow redirect responses (not %s)" - % self.full_status) - location = self.header('location') - type, rest = urllib.splittype(location) - host, path = urllib.splithost(rest) - # @@: We should test that it's not a remote redirect - return self.test_app.get(location, **kw) - - def click(self, description=None, linkid=None, href=None, - anchor=None, index=None, verbose=False): - """ - Click the link as described. Each of ``description``, - ``linkid``, and ``url`` are *patterns*, meaning that they are - either strings (regular expressions), compiled regular - expressions (objects with a ``search`` method), or callables - returning true or false. - - All the given patterns are ANDed together: - - * ``description`` is a pattern that matches the contents of the - anchor (HTML and all -- everything between ```` and - ````) - - * ``linkid`` is a pattern that matches the ``id`` attribute of - the anchor. It will receive the empty string if no id is - given. - - * ``href`` is a pattern that matches the ``href`` of the anchor; - the literal content of that attribute, not the fully qualified - attribute. - - * ``anchor`` is a pattern that matches the entire anchor, with - its contents. - - If more than one link matches, then the ``index`` link is - followed. If ``index`` is not given and more than one link - matches, or if no link matches, then ``IndexError`` will be - raised. - - If you give ``verbose`` then messages will be printed about - each link, and why it does or doesn't match. If you use - ``app.click(verbose=True)`` you'll see a list of all the - links. - - You can use multiple criteria to essentially assert multiple - aspects about the link, e.g., where the link's destination is. - """ - __tracebackhide__ = True - found_html, found_desc, found_attrs = self._find_element( - tag='a', href_attr='href', - href_extract=None, - content=description, - id=linkid, - href_pattern=href, - html_pattern=anchor, - index=index, verbose=verbose) - return self.goto(found_attrs['uri']) - - def clickbutton(self, description=None, buttonid=None, href=None, - button=None, index=None, verbose=False): - """ - Like ``.click()``, except looks for link-like buttons. - This kind of button should look like - ``