widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #13047
[Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
kaputtnik has proposed merging lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website.
Commit message:
Updated to fit Django 1.11.12
Requested reviews:
Widelands Developers (widelands-dev)
Related bugs:
Bug #1760400 in Widelands Website: "Django v 1.8 is EOL"
https://bugs.launchpad.net/widelands-website/+bug/1760400
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands-website/django1_11/+merge/343065
Major update of the website code to Django 1.11.12. I have gone through all release notes form Django 1.8 until Django 1.11.
- Added migrations for wlscreens
- Fixed creation of noticetypes by converting signals post_syncdb to post_migrate. This is done by applying some files called 'app.db'. This makes it also possible to have our own notification types for django_messages.
- Replaced linaro-django-pagination (outdated, not maintained) with dj-pagination
- Removed using of context_instance (deprecated since Django 1.8)
- Added 'options' for setting DATABASES when using mysql version > 5.6
- updated third party app django-registration
- Replaced setting MIDDLE_CLASSES with MIDDLEWARE
- Added setting PASSWORD_HASHERS which is needed because some old password hashers are removed. Doing so old password stored with an old hasher will be converted automatically.
- removed some unused code
- moved mainpage urls from urls.py to mainpage/urls.py
- Removed app 'tracking' and replaced it with a single middleware, including some own modifications
- Fixes getting a list of usernames for JavaScript. This may need a review from a security perspective.
Will add some links to specific revisions for a code review later on.
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website.
=== modified file 'check_input/__init__.py'
--- check_input/__init__.py 2017-11-24 11:11:43 +0000
+++ check_input/__init__.py 2018-04-12 06:55:31 +0000
@@ -1,1 +0,0 @@
-default_app_config = 'check_input.apps.CheckInput'
=== added directory 'django_messages_wl'
=== added file 'django_messages_wl/__init__.py'
=== added file 'django_messages_wl/apps.py'
--- django_messages_wl/apps.py 1970-01-01 00:00:00 +0000
+++ django_messages_wl/apps.py 2018-04-12 06:55:31 +0000
@@ -0,0 +1,12 @@
+from django_messages.apps import DjangoMessagesConfig
+from django.db.models import signals
+
+
+class WLDjangoMessagesConfig(DjangoMessagesConfig):
+
+ #name = 'django_messages'
+ #verbose_name = 'Messages'
+
+ def ready(self):
+ from django_messages_wl.management import create_notice_types
+ signals.post_migrate.connect(create_notice_types, sender=self)
\ No newline at end of file
=== added file 'django_messages_wl/management.py'
--- django_messages_wl/management.py 1970-01-01 00:00:00 +0000
+++ django_messages_wl/management.py 2018-04-12 06:55:31 +0000
@@ -0,0 +1,16 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from django.conf import settings
+from django.utils.translation import ugettext_lazy as _
+
+
+if "notification" in settings.INSTALLED_APPS and getattr(settings, 'DJANGO_MESSAGES_NOTIFY', True):
+ from notification import models as notification
+
+ def create_notice_types(sender, **kwargs):
+ print("Creating wl specific noticetypes for django-messages ...")
+ notification.create_notice_type("messages_received", _("Message Received"), _("you have received a message"), default=2)
+ notification.create_notice_type("messages_reply_received", _("Reply Received"), _("you have received a reply to a message"), default=2)
+else:
+ print("Skipping creation of NoticeTypes as notification app not found")
\ No newline at end of file
=== modified file 'djangoratings/fields.py'
--- djangoratings/fields.py 2016-12-13 18:28:51 +0000
+++ djangoratings/fields.py 2018-04-12 06:55:31 +0000
@@ -115,7 +115,7 @@
key=self.field.key,
)
- if not (user and user.is_authenticated()):
+ if not (user and user.is_authenticated):
if not ip_address:
raise ValueError('``user`` or ``ip_address`` must be present.')
kwargs['user__isnull'] = True
@@ -169,7 +169,7 @@
raise InvalidRating('%s is not a valid choice for %s' %
(score, self.field.name))
- is_anonymous = (user is None or not user.is_authenticated())
+ is_anonymous = (user is None or not user.is_authenticated)
if is_anonymous and not self.field.allow_anonymous:
raise AuthRequired("user must be a user, not '%r'" % (user,))
=== modified file 'djangoratings/management/commands/update_recommendations.py'
--- djangoratings/management/commands/update_recommendations.py 2016-12-13 18:28:51 +0000
+++ djangoratings/management/commands/update_recommendations.py 2018-04-12 06:55:31 +0000
@@ -1,9 +1,9 @@
-from django.core.management.base import NoArgsCommand, CommandError
+from django.core.management.base import BaseCommand, CommandError
from djangoratings.models import SimilarUser
-class Command(NoArgsCommand):
+class Command(BaseCommand):
- def handle_noargs(self, **options):
+ def handle(self, *args, **options):
SimilarUser.objects.update_recommendations()
=== modified file 'djangoratings/templatetags/ratings.py'
--- djangoratings/templatetags/ratings.py 2016-12-13 18:28:51 +0000
+++ djangoratings/templatetags/ratings.py 2018-04-12 06:55:31 +0000
@@ -20,8 +20,8 @@
def render(self, context):
try:
- request = template.resolve_variable(self.request, context)
- obj = template.resolve_variable(self.obj, context)
+ request = django.template.Variable(self.request).resolve(context)
+ obj = django.template.Variable(self.obj).resolve(context)
field = getattr(obj, self.field_name)
except (template.VariableDoesNotExist, AttributeError):
return ''
@@ -63,8 +63,8 @@
def render(self, context):
try:
- user = template.resolve_variable(self.request, context)
- obj = template.resolve_variable(self.obj, context)
+ user = django.template.Variable(self.request).resolve(context)
+ obj = django.template.Variable(self.obj).resolve(context)
field = getattr(obj, self.field_name)
except template.VariableDoesNotExist:
return ''
=== modified file 'local_settings.py.sample'
--- local_settings.py.sample 2017-11-25 11:11:17 +0000
+++ local_settings.py.sample 2018-04-12 06:55:31 +0000
@@ -27,6 +27,13 @@
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
+ # Next is only used for mysql. Explanations:
+ # https://docs.djangoproject.com/en/1.11/ref/databases/#connecting-to-the-database
+ # 'init_command': must be set for mysql >= 5.6
+ # 'OPTIONS': {
+ # 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
+ # 'isolation_level': 'read committed',
+ #},
}
}
=== modified file 'local_urls.py.sample'
--- local_urls.py.sample 2016-06-04 14:17:40 +0000
+++ local_urls.py.sample 2018-04-12 06:55:31 +0000
@@ -1,14 +1,14 @@
from django.conf.urls import *
from django.conf import settings
-
+from django.views.static import serve
local_urlpatterns = [
url(r'^wlmedia/(?P<path>.*)$',
- 'django.views.static.serve',
+ serve,
{'document_root': settings.STATIC_MEDIA_PATH},
name='static_media'),
url(r'^media/(?P<path>.*)$',
- 'django.views.static.serve',
+ serve,
{'document_root': settings.STATIC_MEDIA_PATH},
name='static_media_pybb'),
]
=== removed file 'mainpage/templatetags/online_users.py'
--- mainpage/templatetags/online_users.py 2016-12-13 18:28:51 +0000
+++ mainpage/templatetags/online_users.py 1970-01-01 00:00:00 +0000
@@ -1,32 +0,0 @@
-#!/usr/bin/env python -tt
-# encoding: utf-8
-#
-# File: online_user.py
-#
-# Created by Holger Rapp on 2009-02-19.
-# Copyright (c) 2009 HolgerRapp@xxxxxxx. All rights reserved.
-#
-# Last Modified: 2009-02-20 22:37:16
-#
-
-from django import template
-from django.contrib.auth.models import User
-from django.db.models import Count
-import datetime
-from tracking.models import Visitor
-
-register = template.Library()
-
-
-@register.inclusion_tag('mainpage/online_users.html')
-def online_users(num):
- """Show user that has been login an hour ago."""
- users = [l.user for l in Visitor.objects.active().exclude(user=None)]
-
- # There might still be duplicates, so we make a set and order it into
- # a list again
- users = sorted(list(set(users)), key=lambda user: user.username)
-
- return {
- 'users': users,
- }
=== modified file 'mainpage/templatetags/wl_extras.py'
--- mainpage/templatetags/wl_extras.py 2018-02-17 11:22:19 +0000
+++ mainpage/templatetags/wl_extras.py 2018-04-12 06:55:31 +0000
@@ -2,6 +2,7 @@
# encoding: utf-8
from django import template
+from django.utils.safestring import mark_safe
register = template.Library()
@@ -23,8 +24,16 @@
@register.simple_tag
+@mark_safe
def all_users():
- """Provide a list of all users."""
+ """Provide a list of all users.
+
+ Only used for providing/filtering usernames with JS when writing PMs.
+ This is marked as 'safe', otherwise the list contains
+ 'Username' instead of 'Username' (replaced apostrophes) and JS
+ can't handle '.
+
+ """
from django.contrib.auth.models import User
return [str(u.username) for u in User.objects.all()]
=== modified file 'mainpage/urls.py'
--- mainpage/urls.py 2016-12-13 18:28:51 +0000
+++ mainpage/urls.py 2018-04-12 06:55:31 +0000
@@ -1,8 +1,12 @@
from django.conf.urls import *
-from widelands.mainpage import views
-
-urlpatterns = patterns('',
- # Example:
- url(r'^$', views.mainpage, name='mainpage'),
-
- )
+from mainpage import views
+
+urlpatterns = [
+ url(r'^$', views.mainpage, name='mainpage'),
+ url(r'^locale/$', views.view_locale),
+ url(r'^changelog/$', views.changelog, name='changelog'),
+ url(r'^developers/$', views.developers, name='developers'),
+ url(r'^legal_notice/$', views.legal_notice, name='legal_notice'),
+ url(r'^legal_notice_thanks/$', views.legal_notice_thanks,
+ name='legal_notice_thanks'),
+]
=== modified file 'mainpage/views.py'
--- mainpage/views.py 2018-02-11 13:50:52 +0000
+++ mainpage/views.py 2018-04-12 06:55:31 +0000
@@ -1,4 +1,3 @@
-from django.template import RequestContext
from settings import WIDELANDS_SVN_DIR, INQUIRY_RECIPIENTS
from templatetags.wl_markdown import do_wl_markdown
from operator import itemgetter
=== modified file 'news/feeds.py'
--- news/feeds.py 2016-12-13 18:28:51 +0000
+++ news/feeds.py 2018-04-12 06:55:31 +0000
@@ -1,6 +1,6 @@
from django.contrib.syndication.views import Feed, FeedDoesNotExist
from django.core.exceptions import ObjectDoesNotExist
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from news.models import Post, Category
# Validated through http://validator.w3.org/feed/
=== modified file 'news/models.py'
--- news/models.py 2017-09-12 18:04:34 +0000
+++ news/models.py 2018-04-12 06:55:31 +0000
@@ -4,7 +4,7 @@
from django.contrib.auth.models import User
from tagging.fields import TagField
from news.managers import PublicManager
-from django.core.urlresolvers import reverse
+from django.urls import reverse
import datetime
import settings
import tagging
=== modified file 'notification/management/commands/emit_notices.py'
--- notification/management/commands/emit_notices.py 2017-04-20 20:22:28 +0000
+++ notification/management/commands/emit_notices.py 2018-04-12 06:55:31 +0000
@@ -1,15 +1,15 @@
import logging
-from django.core.management.base import NoArgsCommand
+from django.core.management.base import BaseCommand
from notification.engine import send_all
-class Command(NoArgsCommand):
+class Command(BaseCommand):
help = 'Emit queued notices.'
- def handle_noargs(self, **options):
+ def handle(self, *args, **options):
# Franku: Uncomment for debugging purposes
# logging.basicConfig(level=logging.DEBUG, format='%(message)s')
logging.info('-' * 72)
=== modified file 'notification/models.py'
--- notification/models.py 2018-02-05 12:32:22 +0000
+++ notification/models.py 2018-04-12 06:55:31 +0000
@@ -8,8 +8,7 @@
from django.db import models
from django.db.models.query import QuerySet
from django.conf import settings
-from django.core.urlresolvers import reverse
-from django.template import Context
+from django.urls import reverse
from django.template.loader import render_to_string
from django.core.exceptions import ImproperlyConfigured
@@ -132,7 +131,7 @@
def create_notice_type(label, display, description, default=2, verbosity=1):
"""Creates a new NoticeType.
- This is intended to be used by other apps as a post_syncdb
+ This is intended to be used by other apps as a post_migrate
manangement step.
"""
@@ -186,15 +185,17 @@
"""
format_templates = {}
+
for format in formats:
# conditionally turn off autoescaping for .txt extensions in format
- if format.endswith('.txt'):
- context.autoescape = False
- else:
- context.autoescape = True
+ # if format.endswith('.txt'):
+ # context.autoescape = False
+ # else:
+ # context.autoescape = True
format_templates[format] = render_to_string((
'notification/%s/%s' % (label, format),
- 'notification/%s' % format), context_instance=context)
+ 'notification/%s' % format), context)
+
return format_templates
@@ -250,13 +251,12 @@
activate(language)
# update context with user specific translations
- context = Context({
+ context = {
'user': user,
- 'notices_url': notices_url,
'current_site': current_site,
'subject': notice_type.display,
'description': notice_type.description,
- })
+ }
context.update(extra_context)
# get prerendered format messages
@@ -265,20 +265,21 @@
# Strip newlines from subject
subject = ''.join(render_to_string('notification/email_subject.txt', {
'message': messages['short.txt'],
- }, context).splitlines())
-
+ }).splitlines())
+
# Strip leading newlines. Make writing the email templates easier:
# Each linebreak in the templates results in a linebreak in the emails
# If the first line in a template contains only template tags the
# email will contain an empty line at the top.
body = render_to_string('notification/email_body.txt', {
'message': messages['full.txt'],
- }, context).lstrip()
+ 'notices_url': notices_url,
+ }).lstrip()
if should_send(user, notice_type, '1') and user.email: # Email
recipients.append(user.email)
send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, recipients)
-
+
# reset environment to original language
activate(current_language)
except NoticeType.DoesNotExist:
=== modified file 'notification/views.py'
--- notification/views.py 2017-05-03 19:12:49 +0000
+++ notification/views.py 2018-04-12 06:55:31 +0000
@@ -1,5 +1,4 @@
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from collections import OrderedDict
from notification.models import *
@@ -28,7 +27,7 @@
app_tables[app].append({'notice_type': notice_type, 'html_values': checkbox_values})
- return render_to_response('notification/notice_settings.html', {
+ return render(request, 'notification/notice_settings.html', {
'column_headers': [medium_display for medium_id, medium_display in NOTICE_MEDIA],
'app_tables': OrderedDict(sorted(app_tables.items(), key=lambda t: t[0]))
- }, context_instance=RequestContext(request))
+ })
=== added file 'online_users_middleware.py'
--- online_users_middleware.py 1970-01-01 00:00:00 +0000
+++ online_users_middleware.py 2018-04-12 06:55:31 +0000
@@ -0,0 +1,56 @@
+from django.conf import settings
+from django.core.cache import cache
+from django.contrib.auth.models import User
+from django.utils.deprecation import MiddlewareMixin
+from django.contrib.auth import user_logged_out
+from django.dispatch import receiver
+
+ONLINE_THRESHOLD = getattr(settings, 'ONLINE_THRESHOLD', 60 * 15)
+ONLINE_MAX = getattr(settings, 'ONLINE_MAX', 50)
+
+
+def get_online_now(self):
+ return User.objects.filter(id__in=self.online_now_ids or [])
+
+
+@receiver(user_logged_out)
+def logout(sender, **kwargs):
+ cache.delete('online-%s' % kwargs['user'].id)
+
+
+class OnlineNowMiddleware(MiddlewareMixin):
+ """Maintains a list of users who have interacted with the website recently.
+
+ Their user IDs are available as ``online_now_ids`` on the request
+ object, and their corresponding users are available (lazily) as the
+ ``online_now`` property on the request object.
+
+ """
+
+ def process_request(self, request):
+ # First get the index
+ uids = cache.get('online-now', [])
+
+ # Perform the multiget on the individual online uid keys
+ online_keys = ['online-%s' % (u,) for u in uids]
+ fresh = cache.get_many(online_keys).keys()
+ online_now_ids = [int(k.replace('online-', '')) for k in fresh]
+
+ # If the user is authenticated, add their id to the list
+ if request.user.is_authenticated():
+ uid = request.user.id
+ # If their uid is already in the list, we want to bump it
+ # to the top, so we remove the earlier entry.
+ if uid in online_now_ids:
+ online_now_ids.remove(uid)
+ online_now_ids.append(uid)
+ if len(online_now_ids) > ONLINE_MAX:
+ del online_now_ids[0]
+
+ # Attach our modifications to the request object
+ request.__class__.online_now_ids = online_now_ids
+ request.__class__.online_now = property(get_online_now)
+
+ # Set the new cache
+ cache.set('online-%s' % (request.user.pk,), True, ONLINE_THRESHOLD)
+ cache.set('online-now', online_now_ids, ONLINE_THRESHOLD)
=== modified file 'pip_requirements.txt'
--- pip_requirements.txt 2018-01-12 19:52:31 +0000
+++ pip_requirements.txt 2018-04-12 06:55:31 +0000
@@ -1,15 +1,15 @@
# Python requirements for widelands-website at 22.06.2017
BeautifulSoup==3.2.0
-Django==1.8
+Django==1.11.12
django-contrib-comments==1.8.0
django-haystack==2.6.1
# django-messages is very old on pypi
# Do not install newer versions because our notifications app is affected
-e git://github.com/arneb/django-messages.git@2d8dabb755e0b5ace876bde25f45d07c2051ac37#egg=django_messages
django-nocaptcha-recaptcha==0.0.19
--e git://github.com/zyga/django-pagination.git#egg=linaro_django_pagination
-django-registration==2.2
+dj-pagination==2.3.2
+django-registration==2.4.1
django-tagging==0.4.5
gunicorn==19.7.1
Markdown==2.6.8
=== added file 'pybb/apps.py'
--- pybb/apps.py 1970-01-01 00:00:00 +0000
+++ pybb/apps.py 2018-04-12 06:55:31 +0000
@@ -0,0 +1,12 @@
+from django.apps import AppConfig
+from django.db.models import signals
+
+
+class PybbConfig(AppConfig):
+
+ name = 'pybb'
+ verbose_name = 'Pybb'
+
+ def ready(self):
+ from pybb.management.pybb_notifications import create_notice_types
+ signals.post_migrate.connect(create_notice_types, sender=self)
=== modified file 'pybb/feeds.py'
--- pybb/feeds.py 2016-12-13 18:28:51 +0000
+++ pybb/feeds.py 2018-04-12 06:55:31 +0000
@@ -1,5 +1,5 @@
from django.contrib.syndication.views import Feed
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.core.exceptions import ObjectDoesNotExist
from django.utils.feedgenerator import Atom1Feed
from pybb.models import Post, Topic, Forum
=== modified file 'pybb/forms.py'
--- pybb/forms.py 2017-11-23 16:12:01 +0000
+++ pybb/forms.py 2018-04-12 06:55:31 +0000
@@ -28,13 +28,6 @@
self.ip = kwargs.pop('ip', None)
super(AddPostForm, self).__init__(*args, **kwargs)
- # TODO(Franku): This doesn't work anymore with django 1.8 Use 'field_order'
- # with django 1.9
- self.fields.keyOrder = ['name',
- 'body',
- 'markup',
- 'attachment']
-
if self.topic:
self.fields['name'].widget = forms.HiddenInput()
self.fields['name'].required = False
=== modified file 'pybb/management/commands/pybb_resave_post.py'
--- pybb/management/commands/pybb_resave_post.py 2016-12-13 18:28:51 +0000
+++ pybb/management/commands/pybb_resave_post.py 2018-04-12 06:55:31 +0000
@@ -1,4 +1,3 @@
-from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
=== modified file 'pybb/management/pybb_notifications.py'
--- pybb/management/pybb_notifications.py 2017-04-29 19:52:28 +0000
+++ pybb/management/pybb_notifications.py 2018-04-12 06:55:31 +0000
@@ -1,11 +1,11 @@
-from django.db.models import signals
from django.utils.translation import ugettext_noop as _
try:
from notification import models as notification
- def create_notice_types(app, created_models, verbosity, **kwargs):
+ def create_notice_types(sender, **kwargs):
+ print("Creating noticetypes for pybb ...")
notification.create_notice_type('forum_new_topic',
_('Forum New Topic'),
_('a new topic has been added to the forum'),
@@ -13,10 +13,5 @@
notification.create_notice_type('forum_new_post',
_('Forum New Post'),
_('a new comment has been posted to a topic you observe'))
-
- # TODO (Franku): post_syncdb is deprecated since Django 1.7
- # See: https://docs.djangoproject.com/en/1.8/ref/signals/#post-syncdb
- signals.post_syncdb.connect(create_notice_types,
- sender=notification)
except ImportError:
print 'Skipping creation of NoticeTypes as notification app not found'
=== modified file 'pybb/models.py'
--- pybb/models.py 2017-09-12 18:04:34 +0000
+++ pybb/models.py 2018-04-12 06:55:31 +0000
@@ -5,7 +5,7 @@
from django.db import models
from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.utils.html import strip_tags
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
=== modified file 'pybb/templatetags/pybb_extras.py'
--- pybb/templatetags/pybb_extras.py 2017-06-22 06:33:22 +0000
+++ pybb/templatetags/pybb_extras.py 2018-04-12 06:55:31 +0000
@@ -5,9 +5,7 @@
from pprint import pprint
from django import template
-from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
-from django.template import RequestContext
from django.template.defaultfilters import stringfilter
from django.utils.encoding import smart_unicode
from django.utils.html import escape
@@ -132,7 +130,7 @@
else:
return topic.updated <= read.time
- if not user.is_authenticated():
+ if not user.is_authenticated:
return False
else:
if isinstance(topic, Topic):
=== modified file 'pybb/unread.py'
--- pybb/unread.py 2016-12-13 18:28:51 +0000
+++ pybb/unread.py 2018-04-12 06:55:31 +0000
@@ -2,7 +2,7 @@
def cache_unreads(qs, user):
- if not len(qs) or not user.is_authenticated():
+ if not len(qs) or not user.is_authenticated:
return qs
if isinstance(qs[0], Topic):
reads = Read.objects.filter(topic__pk__in=set(x.id for x in qs),
=== modified file 'pybb/urls.py'
--- pybb/urls.py 2016-12-13 18:28:51 +0000
+++ pybb/urls.py 2018-04-12 06:55:31 +0000
@@ -48,7 +48,7 @@
url('^api/post_ajax_preview/$', views.post_ajax_preview,
name='pybb_post_ajax_preview'),
- # Subsciption
+ # Subscription
url('^topic/(?P<topic_id>\d+)/subscribe/$',
views.add_subscription, name='pybb_add_subscription'),
url('^topic/(?P<topic_id>\d+)/unsubscribe/$',
=== removed file 'pybb/urls.py.orig'
--- pybb/urls.py.orig 2009-02-25 16:55:36 +0000
+++ pybb/urls.py.orig 1970-01-01 00:00:00 +0000
@@ -1,21 +0,0 @@
-from django.conf.urls.defaults import *
-from django.views.generic.simple import redirect_to
-from django.conf import settings
-import django.views.static
-
-from django.contrib import admin
-admin.autodiscover()
-
-urlpatterns = patterns('',
- (r'^$', redirect_to, {'url': '/forum/'}),
- (r'^admin/(.*)', admin.site.root),
- (r'', include('account.urls')),
- # (r'^forum/', include('pybb.urls')),
- # (r'^forum/', include('forum.urls')),
-)
-
-if (settings.DEBUG):
- urlpatterns += patterns('',
- (r'^%s(?P<path>.*)$' % settings.MEDIA_URL.lstrip('/'),
- django.views.static.serve, {'document_root': settings.MEDIA_ROOT}),
- )
=== modified file 'pybb/util.py'
--- pybb/util.py 2017-08-21 19:13:19 +0000
+++ pybb/util.py 2018-04-12 06:55:31 +0000
@@ -5,8 +5,7 @@
from BeautifulSoup import BeautifulSoup
from datetime import datetime
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.shortcuts import render
from django.http import HttpResponse
from django.utils.functional import Promise
from django.utils.translation import check_for_language
@@ -32,7 +31,7 @@
output = func(request, *args, **kwargs)
if not isinstance(output, dict):
return output
- kwargs = {'context_instance': RequestContext(request)}
+
# TODO(Franku): 'MIME_TYPE' is never in output as i can see for now.
# But if, this should maybe 'content_type' instead
@@ -43,7 +42,7 @@
else:
template = template_path
- return render_to_response(template, output, **kwargs)
+ return render(request, template, output)
return wrapper
return decorator
=== modified file 'pybb/views.py'
--- pybb/views.py 2017-12-10 13:18:47 +0000
+++ pybb/views.py 2018-04-12 06:55:31 +0000
@@ -7,7 +7,7 @@
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.conf import settings
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.db import connection
from django.utils import translation
from django.shortcuts import render
@@ -88,7 +88,7 @@
topic.views += 1
topic.save()
- if request.user.is_authenticated():
+ if request.user.is_authenticated:
topic.update_read(request.user)
if pybb_settings.FREEZE_FIRST_POST:
@@ -98,13 +98,13 @@
last_post = topic.posts.order_by('-created')[0]
initial = {}
- if request.user.is_authenticated():
+ if request.user.is_authenticated:
initial = {'markup': 'markdown'}
form = AddPostForm(topic=topic, initial=initial)
moderator = (request.user.is_superuser or
request.user in topic.forum.moderators.all())
- subscribed = (request.user.is_authenticated() and
+ subscribed = (request.user.is_authenticated and
request.user in topic.subscribers.all())
posts = topic.posts.exclude(hidden=True).select_related()
=== modified file 'settings.py'
--- settings.py 2017-12-23 09:15:02 +0000
+++ settings.py 2018-04-12 06:55:31 +0000
@@ -67,7 +67,7 @@
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]
-INSTALLED_APPS = (
+INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
@@ -78,8 +78,6 @@
'django.contrib.humanize',
'django.contrib.sitemaps',
'nocaptcha_recaptcha',
- # Thirdparty apps, but need preload
- 'tracking', # included as wlapp
# Our own apps
'wiki.templatetags.restructuredtext',
@@ -91,43 +89,42 @@
'wlsearch',
'wlpoll',
'wlevents',
- 'wlmaps',
+ 'wlmaps.apps.WlMapsConfig',
'wlscreens',
'wlggz',
'wlscheduling',
- 'check_input',
+ 'check_input.apps.CheckInput',
'haystack', # search engine; see option HAYSTACK_CONNECTIONS
# Modified 3rd party apps
- 'wiki', # This is based on wikiapp, but has some local modifications
+ 'wiki.apps.WikiConfig', # This is based on wikiapp, but has some local modifications
'news', # This is based on simple-blog, but has some local modifications
'news.managers',
- 'pybb', # Feature enriched version of pybb
+ 'pybb.apps.PybbConfig', # Feature enriched version of pybb
# Thirdparty apps
'threadedcomments', # included as wlapp
'notification', # included as wlapp
- 'django_messages',
- 'linaro_django_pagination',
+ 'django_messages_wl.apps.WLDjangoMessagesConfig',
+ 'dj_pagination',
'tagging',
'djangoratings', # included as wlapp
'sphinxdoc', # included as wlapp
-)
+]
-MIDDLEWARE_CLASSES = (
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
- 'django.middleware.security.SecurityMiddleware',
-
- 'linaro_django_pagination.middleware.PaginationMiddleware',
- 'tracking.middleware.VisitorTrackingMiddleware',
- 'tracking.middleware.VisitorCleanUpMiddleware',
-)
+
+ # Foreign middleware
+ 'dj_pagination.middleware.PaginationMiddleware',
+ 'online_users_middleware.OnlineNowMiddleware',
+]
TEMPLATES = [
{
@@ -161,6 +158,13 @@
DEFAULT_FROM_EMAIL = 'noreply@xxxxxxxxxxxxx'
ACCOUNT_ACTIVATION_DAYS = 2 # Days an activation token keeps active
+# Franku: SHA1 Needed as compatibility for old passwords
+# https://docs.djangoproject.com/en/1.11/releases/1.10/#removed-weak-password-hashers-from-the-default-password-hashers-setting
+PASSWORD_HASHERS = [
+ 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
+ 'django.contrib.auth.hashers.SHA1PasswordHasher'
+]
+
######################
# Wiki configuration #
######################
@@ -253,11 +257,6 @@
},
}
-############
-# Tracking #
-############
-TRACKING_CLEANUP_TIMEOUT = 48
-
###########################
# Widelands SVN directory #
###########################
@@ -311,12 +310,21 @@
BLEACH_ALLOWED_ATTRIBUTES = {'img': ['src', 'alt'], 'a': [
'href'], 'td': ['align'], '*': ['class', 'id', 'title']}
-################################
-# Pagination settings #
-# for linaro-django-pagination #
-################################
+##########################
+# Pagination settings #
+# for dj-pagination #
+##########################
PAGINATION_DEFAULT_WINDOW = 2
+###########################
+# Settings for displaying #
+# online users #
+###########################
+
+# Time in seconds how long a user will be shown online
+ONLINE_THRESHOLD = 60 * 30
+# Number of stored users
+ONLINE_MAX = 25
try:
from local_settings import *
=== modified file 'sphinxdoc/models.py'
--- sphinxdoc/models.py 2016-12-13 18:28:51 +0000
+++ sphinxdoc/models.py 2018-04-12 06:55:31 +0000
@@ -4,6 +4,7 @@
"""
from django.db import models
+from django.urls import reverse
class App(models.Model):
@@ -15,9 +16,8 @@
def __unicode__(self):
return self.name
- @models.permalink
def get_absolute_url(self):
- return ('doc-index', (), {'slug': self.slug})
+ return reverse('doc-index', kwargs={'slug': self.slug})
class Meta:
app_label = 'sphinxdoc'
=== modified file 'sphinxdoc/views.py'
--- sphinxdoc/views.py 2016-12-13 18:28:51 +0000
+++ sphinxdoc/views.py 2018-04-12 06:55:31 +0000
@@ -4,8 +4,8 @@
import os.path
from django.http import Http404
-from django.shortcuts import get_object_or_404, render_to_response
-from django.template import RequestContext
+from django.shortcuts import get_object_or_404, render
+#from django.template import RequestContext
import json
from django.views import static
@@ -52,8 +52,7 @@
if 'title' not in data['doc']:
data['doc']['title'] = SPECIAL_TITLES[page_name]
- return render_to_response(templates, data,
- context_instance=RequestContext(request))
+ return render(request, templates, data)
def search(request, slug):
=== modified file 'static_sitemap.py'
--- static_sitemap.py 2016-11-22 19:21:00 +0000
+++ static_sitemap.py 2018-04-12 06:55:31 +0000
@@ -1,5 +1,5 @@
from django.contrib.sitemaps import Sitemap
-from django.core.urlresolvers import reverse
+from django.urls import reverse
class StaticViewSitemap(Sitemap):
=== modified file 'templates/django_messages/inbox.html'
--- templates/django_messages/inbox.html 2017-11-07 18:04:38 +0000
+++ templates/django_messages/inbox.html 2018-04-12 06:55:31 +0000
@@ -31,7 +31,7 @@
</td>
<td>{{ message.sent_at|custom_date:user }}</td>
<td>
- <a href="{% url 'django_messages.views.delete' message.id %}?next={{ request.path|iriencode }}">
+ <a href="{% url 'messages_delete' message.id %}?next={{ request.path|iriencode }}">
<img src="{{ MEDIA_URL }}img/delete.png" alt="delete" title="delete" />
</a>
</td>
=== modified file 'templates/django_messages/outbox.html'
--- templates/django_messages/outbox.html 2017-11-07 18:04:38 +0000
+++ templates/django_messages/outbox.html 2018-04-12 06:55:31 +0000
@@ -27,7 +27,7 @@
</td>
<td>{{ message.sent_at|custom_date:user }}</td>
<td>
- <a href="{% url 'django_messages.views.delete' message.id %}?next={{ request.path|iriencode }}">
+ <a href="{% url 'messages_delete' message.id %}?next={{ request.path|iriencode }}">
<img src="{{ MEDIA_URL }}img/delete.png" alt="delete" title="delete" />
</a>
</td>
=== removed file 'templates/mainpage/online_users.html'
--- templates/mainpage/online_users.html 2016-06-05 20:58:46 +0000
+++ templates/mainpage/online_users.html 1970-01-01 00:00:00 +0000
@@ -1,18 +0,0 @@
-{% load wlprofile_extras %}
-
-{% if users %}
-<div class="columnModule">
- <h3>Currently Online</h3>
- <div class="columnModuleBox">
- {% if users %}
- <ul class="player">
- {% for user in users %}
- <li><a href="{% url 'profile_view' user %}">{{user.username}}</a></li>
- {% endfor %}
- </ul>
- {% else %}
- <p>Currently nobody is online.</p>
- {% endif %}
- </div>
-</div>
-{% endif %}
=== modified file 'templates/notification/forum_new_topic/full.txt'
--- templates/notification/forum_new_topic/full.txt 2018-03-18 10:48:37 +0000
+++ templates/notification/forum_new_topic/full.txt 2018-04-12 06:55:31 +0000
@@ -1,9 +1,9 @@
+{% autoescape off %}
{% load i18n %}{% blocktrans with topic.get_absolute_url as topic_url and post.body as txt %}The Forum topic "{{ topic }}" has been created by {{ user }}.
-
{{ user }} wrote:
{{ txt }}
-------------------------
Link to topic: http://{{ current_site }}{{ topic_url }}
-{% endblocktrans %}
+{% endblocktrans %}{% endautoescape %}
=== modified file 'templates/right_boxes.html'
--- templates/right_boxes.html 2016-06-05 20:58:46 +0000
+++ templates/right_boxes.html 2018-04-12 06:55:31 +0000
@@ -109,7 +109,18 @@
<!-- Logged in users -->
-{% online_users 10 %}
+{% if request.online_now %}
+<div class="columnModule">
+ <h3>Currently Online</h3>
+ <div class="columnModuleBox">
+ <ul class="player">
+ {% for user in request.online_now|slice:":10" %}
+ <li><a href="{% url 'profile_view' user %}">{{ user.username }}</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+</div>
+{% endif %}
<!-- Latest Post -->
{% pybb_last_posts %}
=== removed file 'templates/wiki/article_teaser.html'
--- templates/wiki/article_teaser.html 2016-03-07 20:38:39 +0000
+++ templates/wiki/article_teaser.html 1970-01-01 00:00:00 +0000
@@ -1,20 +0,0 @@
-
-{% load wiki_extras %}
-{% load avatar_tags %}
-{% load custom_date %}
-
-<tr class="{% cycle odd,even %}">
- <td class="meta">
- <div class="avatar">{% avatar article.latest_changeset.editor 40 %}</div>
- <div class="details">
- <a href="{% url 'profiles.views.profile' article.latest_changeset.editor.username %}">
- {{ article.latest_changeset.editor }}
- </a>
- </div>
- {{ article.last_update|custom_date:user }}
- </td>
- <td>
- <h2><a href="{{ article.get_absolute_url }}">{{ article.title }}</a></h2>
- <div class="body">{% render_content article 'summary' %}</div>
- </td>
-</tr>
=== modified file 'templates/wlmaps/index.html'
--- templates/wlmaps/index.html 2017-11-12 16:15:08 +0000
+++ templates/wlmaps/index.html 2018-04-12 06:55:31 +0000
@@ -22,7 +22,7 @@
<table class="maps">
{% for map in object_list %}
<tr class="{% cycle "odd" "even" %}">
- <td class="first-column"><a href="{{ map.get_absolute_url }}"><img class="minimap" src="{{ MEDIA_URL }}{{ map.minimap.url }}" alt="{{ map.name }}" /></a></td>
+ <td class="first-column"><a href="{{ map.get_absolute_url }}"><img class="minimap" src="{{ MEDIA_URL }}{{ map.minimap }}" alt="{{ map.name }}" /></a></td>
<td>
<h3><a class="invertedColor" href="{{ map.get_absolute_url }}">{{ map.name }}</a></h3>
<table>
=== modified file 'templates/wlmaps/map_detail.html'
--- templates/wlmaps/map_detail.html 2017-11-12 16:15:08 +0000
+++ templates/wlmaps/map_detail.html 2018-04-12 06:55:31 +0000
@@ -38,7 +38,7 @@
<div>
<a href="{% url 'wlmaps_index' %}">Maps</a> » {{ map.name }}
</div>
- <img class="posLeft map" style="float: left" src="{{ MEDIA_URL }}{{ map.minimap.url }}" alt="{{ map.name }}" />
+ <img class="posLeft map" style="float: left" src="{{ MEDIA_URL }}{{ map.minimap }}" alt="{{ map.name }}" />
<div>
<h3>Description:</h3>
<p>{{ map.descr|wl_markdown:"bleachit" }}</p>
=== modified file 'threadedcomments/templatetags/gravatar.py'
--- threadedcomments/templatetags/gravatar.py 2016-12-13 18:28:51 +0000
+++ threadedcomments/templatetags/gravatar.py 2018-04-12 06:55:31 +0000
@@ -3,7 +3,7 @@
from django.template.defaultfilters import stringfilter
from django.utils.encoding import smart_str
from django.utils.safestring import mark_safe
-from django.utils.hashcompat import md5_constructor
+from hashlib import md5 as md5_constructor
import urllib
GRAVATAR_MAX_RATING = getattr(settings, 'GRAVATAR_MAX_RATING', 'R')
=== modified file 'threadedcomments/templatetags/threadedcommentstags.py'
--- threadedcomments/templatetags/threadedcommentstags.py 2017-01-24 11:53:11 +0000
+++ threadedcomments/templatetags/threadedcommentstags.py 2018-04-12 06:55:31 +0000
@@ -1,7 +1,7 @@
import re
from django import template
from django.contrib.contenttypes.models import ContentType
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from threadedcomments.models import ThreadedComment, FreeThreadedComment
=== removed directory 'threadedcomments/tests'
=== removed file 'threadedcomments/tests/__init__.py'
--- threadedcomments/tests/__init__.py 2016-05-15 14:41:54 +0000
+++ threadedcomments/tests/__init__.py 1970-01-01 00:00:00 +0000
@@ -1,8 +0,0 @@
-from views_tests import *
-from templatetags_tests import *
-try:
- import comment_utils
-except ImportError:
- pass
-else:
- from moderator_tests import *
=== removed file 'threadedcomments/tests/moderator_tests.py'
--- threadedcomments/tests/moderator_tests.py 2016-12-13 18:28:51 +0000
+++ threadedcomments/tests/moderator_tests.py 1970-01-01 00:00:00 +0000
@@ -1,403 +0,0 @@
-from django.core import mail
-from django.test import TestCase
-
-from django.contrib.auth.models import User
-
-from threadedcomments.moderation import moderator, CommentModerator
-from threadedcomments.models import FreeThreadedComment, ThreadedComment, TestModel
-from threadedcomments.models import MARKDOWN, TEXTILE, REST, PLAINTEXT
-
-
-__all__ = ('ModeratorTestCase',)
-
-
-class ModeratorTestCase(TestCase):
-
- def test_threadedcomment(self):
- topic = TestModel.objects.create(name='Test')
- user = User.objects.create_user(
- 'user', 'floguy@xxxxxxxxx', password='password')
- user2 = User.objects.create_user(
- 'user2', 'floguy@xxxxxxxxx', password='password')
-
- comment1 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1',
- comment='This is fun! This is very fun!',
- )
- comment2 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1',
- comment='This is stupid! I hate it!',
- )
- comment3 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment2,
- comment='I agree, the first comment was wrong and you are right!',
- )
- comment4 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1',
- comment='What are we talking about?',
- )
- comment5 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment3,
- comment="I'm a fanboy!",
- )
- comment6 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment1,
- comment='What are you talking about?',
- )
-
- class Moderator1(CommentModerator):
- enable_field = 'is_public'
- auto_close_field = 'date'
- close_after = 15
- moderator.register(TestModel, Moderator1)
-
- comment7 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1',
- comment='Post moderator addition. Does it still work?',
- )
-
- topic.is_public = False
- topic.save()
-
- comment8 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment7,
- comment='This should not appear, due to enable_field',
- )
-
- moderator.unregister(TestModel)
-
- comment9 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1',
- comment='This should appear again, due to unregistration',
- )
-
- self.assertEquals(len(mail.outbox), 0)
-
- ##################
-
- class Moderator2(CommentModerator):
- enable_field = 'is_public'
- auto_close_field = 'date'
- close_after = 15
- akismet = False
- email_notification = True
- moderator.register(TestModel, Moderator2)
-
- comment10 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1',
- comment='This should not appear again, due to registration with a new manager.',
- )
-
- topic.is_public = True
- topic.save()
-
- comment11 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment1,
- comment='This should appear again.',
- )
-
- self.assertEquals(len(mail.outbox), 1)
- mail.outbox = []
-
- topic.date = topic.date - datetime.timedelta(days=20)
- topic.save()
-
- comment12 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment7,
- comment="This shouldn't appear, due to close_after=15.",
- )
-
- topic.date = topic.date + datetime.timedelta(days=20)
- topic.save()
-
- moderator.unregister(TestModel)
-
- class Moderator3(CommentModerator):
- max_comment_length = 10
- moderator.register(TestModel, Moderator3)
-
- comment13 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment7,
- comment="This shouldn't appear because it has more than 10 chars.",
- )
-
- comment14 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment7,
- comment='<10chars',
- )
-
- moderator.unregister(TestModel)
-
- class Moderator4(CommentModerator):
- allowed_markup = [REST, ]
- moderator.register(TestModel, Moderator4)
-
- comment15 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment7,
- comment='INVALID Markup. Should not show up.', markup=TEXTILE
- )
-
- comment16 = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', parent=comment7,
- comment='VALID Markup. Should show up.', markup=REST
- )
-
- moderator.unregister(TestModel)
-
- tree = ThreadedComment.public.get_tree(topic)
- output = []
- for comment in tree:
- output.append('%s %s' % (' ' * comment.depth, comment.comment))
- self.assertEquals('\n'.join(output),
- """
-This is fun! This is very fun!
- What are you talking about?
- This should appear again.
-This is stupid! I hate it!
- I agree, the first comment was wrong and you are right!
- I'm a fanboy!
-What are we talking about?
-Post moderator addition. Does it still work?
- <10chars
- VALID Markup. Should show up.
-This should appear again, due to unregistration
-""".lstrip())
-
- tree = ThreadedComment.objects.get_tree(topic)
- output = []
- for comment in tree:
- output.append('%s %s' % (' ' * comment.depth, comment.comment))
- self.assertEquals('\n'.join(output),
- """
-This is fun! This is very fun!
- What are you talking about?
- This should appear again.
-This is stupid! I hate it!
- I agree, the first comment was wrong and you are right!
- I'm a fanboy!
-What are we talking about?
-Post moderator addition. Does it still work?
- This shouldn't appear because it has more than 10 chars.
- <10chars
- VALID Markup. Should show up.
-This should appear again, due to unregistration
-""".lstrip())
-
- tree = ThreadedComment.objects.get_tree(topic, root=comment2)
- output = []
- for comment in tree:
- output.append('%s %s' % (' ' * comment.depth, comment.comment))
- self.assertEquals('\n'.join(output),
- """
-This is stupid! I hate it!
- I agree, the first comment was wrong and you are right!
- I'm a fanboy!
-""".lstrip())
-
- tree = ThreadedComment.objects.get_tree(topic, root=comment2.id)
- for comment in tree:
- output.append('%s %s' % (' ' * comment.depth, comment.comment))
- self.assertEquals('\n'.join(output),
- """
-This is stupid! I hate it!
- I agree, the first comment was wrong and you are right!
- I'm a fanboy!
-""".lstrip())
-
- def test_freethreadedcomment(self):
-
- ###########################
- ### FreeThreadedComment ###
- ###########################
-
- fcomment1 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1',
- comment='This is fun! This is very fun!',
- )
- fcomment2 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1',
- comment='This is stupid! I hate it!',
- )
- fcomment3 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment2,
- comment='I agree, the first comment was wrong and you are right!',
- )
- fcomment4 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1',
- website='http://www.eflorenzano.com/', email='floguy@xxxxxxxxx',
- comment='What are we talking about?',
- )
- fcomment5 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment3,
- comment="I'm a fanboy!",
- )
- fcomment6 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment1,
- comment='What are you talking about?',
- )
-
- moderator.register(TestModel, Moderator1)
-
- fcomment7 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1',
- comment='Post moderator addition. Does it still work?',
- )
-
- topic.is_public = False
- topic.save()
-
- fcomment8 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment7,
- comment='This should not appear, due to enable_field',
- )
-
- moderator.unregister(TestModel)
-
- fcomment9 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1',
- comment='This should appear again, due to unregistration',
- )
-
- self.assertEquals(len(mail.outbox), 0)
-
- moderator.register(TestModel, Moderator2)
-
- fcomment10 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1',
- comment='This should not appear again, due to registration with a new manager.',
- )
-
- topic.is_public = True
- topic.save()
-
- fcomment11 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment1,
- comment='This should appear again.',
- )
-
- self.assertEquals(len(mail.outbox), 1)
-
- mail.outbox = []
-
- topic.date = topic.date - datetime.timedelta(days=20)
- topic.save()
-
- fcomment12 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment7,
- comment="This shouldn't appear, due to close_after=15.",
- )
-
- topic.date = topic.date + datetime.timedelta(days=20)
- topic.save()
-
- moderator.unregister(TestModel)
- moderator.register(TestModel, Moderator3)
-
- fcomment13 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment7,
- comment="This shouldn't appear because it has more than 10 chars.",
- )
-
- fcomment14 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment7,
- comment='<10chars',
- )
-
- moderator.unregister(TestModel)
-
- class Moderator5(CommentModerator):
- allowed_markup = [REST, ]
- max_depth = 3
- moderator.register(TestModel, Moderator5)
-
- fcomment15 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment7,
- comment='INVALID Markup. Should not show up.', markup=TEXTILE
- )
-
- fcomment16 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=None,
- comment='VALID Markup. Should show up.', markup=REST
- )
-
- fcomment17 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment16,
- comment='Building Depth...Should Show Up.', markup=REST
- )
-
- fcomment18 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment17,
- comment='More Depth...Should Show Up.', markup=REST
- )
-
- fcomment19 = FreeThreadedComment.objects.create_for_object(
- topic, name='Eric', ip_address='127.0.0.1', parent=fcomment18,
- comment='Too Deep..Should NOT Show UP', markup=REST
- )
-
- moderator.unregister(TestModel)
-
- tree = FreeThreadedComment.public.get_tree(topic)
- output = []
- for comment in tree:
- output.append('%s %s' % (' ' * comment.depth, comment.comment))
- self.assertEquals('\n'.join(output),
- """
-This is fun! This is very fun!
- What are you talking about?
- This should appear again.
-This is stupid! I hate it!
- I agree, the first comment was wrong and you are right!
- I'm a fanboy!
-What are we talking about?
-Post moderator addition. Does it still work?
- <10chars
-This should appear again, due to unregistration
-VALID Markup. Should show up.
- Building Depth...Should Show Up.
- More Depth...Should Show Up.
-""".lstrip())
-
- tree = FreeThreadedComment.objects.get_tree(topic)
- output = []
- for comment in tree:
- output.append('%s %s' % (' ' * comment.depth, comment.comment))
- self.assertEquals('\n'.join(output),
- """
-This is fun! This is very fun!
- What are you talking about?
- This should appear again.
-This is stupid! I hate it!
- I agree, the first comment was wrong and you are right!
- I'm a fanboy!
-What are we talking about?
-Post moderator addition. Does it still work?
- This shouldn't appear because it has more than 10 chars.
- <10chars
-This should appear again, due to unregistration
-VALID Markup. Should show up.
- Building Depth...Should Show Up.
- More Depth...Should Show Up.
-""".lstrip())
-
- tree = FreeThreadedComment.objects.get_tree(topic, root=comment2)
- output = []
- for comment in tree:
- output.append('%s %s' % (' ' * comment.depth, comment.comment))
- self.assertEquals('\n'.join(output),
- """
-This is stupid! I hate it!
- I agree, the first comment was wrong and you are right!
- I'm a fanboy!
-""".lstrip())
-
- tree = FreeThreadedComment.objects.get_tree(topic, root=comment2.id)
- output = []
- for comment in tree:
- output.append('%s %s' % (' ' * comment.depth, comment.comment))
- self.assertEquals('\n'.join(output),
- """
-This is stupid! I hate it!
- I agree, the first comment was wrong and you are right!
- I'm a fanboy!
-""".lstrip())
=== removed file 'threadedcomments/tests/templatetags_tests.py'
--- threadedcomments/tests/templatetags_tests.py 2016-12-13 18:28:51 +0000
+++ threadedcomments/tests/templatetags_tests.py 1970-01-01 00:00:00 +0000
@@ -1,561 +0,0 @@
-import datetime
-
-from xml.dom.minidom import parseString
-
-from django.core import mail
-from django.core.urlresolvers import reverse
-from django.template import Context, Template
-from django.test import TestCase
-from django.utils.simplejson import loads
-
-from django.contrib.auth.models import User
-from django.contrib.contenttypes.models import ContentType
-
-from threadedcomments.models import FreeThreadedComment, ThreadedComment, TestModel
-from threadedcomments.models import MARKDOWN, TEXTILE, REST, PLAINTEXT
-from threadedcomments.templatetags import threadedcommentstags as tags
-
-
-__all__ = ('TemplateTagTestCase',)
-
-
-class TemplateTagTestCase(TestCase):
- urls = 'threadedcomments.tests.threadedcomments_urls'
-
- def test_get_comment_url(self):
-
- user = User.objects.create_user(
- 'user', 'floguy@xxxxxxxxx', password='password')
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- comment = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address='127.0.0.1',
- comment='My test comment!',
- )
-
- c = Context({
- 'topic': topic,
- 'parent': comment
- })
- sc = {
- 'ct': content_type.pk,
- 'id': topic.pk,
- 'pid': comment.pk,
- }
-
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_comment_url topic %}').render(
- c), u'/comment/%(ct)s/%(id)s/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_comment_url topic parent %}').render(
- c), u'/comment/%(ct)s/%(id)s/%(pid)s/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_comment_url_json topic %}').render(
- c), u'/comment/%(ct)s/%(id)s/json/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_comment_url_xml topic %}').render(
- c), u'/comment/%(ct)s/%(id)s/xml/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_comment_url_json topic parent %}').render(
- c), u'/comment/%(ct)s/%(id)s/%(pid)s/json/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_comment_url_xml topic parent %}').render(
- c), u'/comment/%(ct)s/%(id)s/%(pid)s/xml/' % sc)
-
- def test_get_free_comment_url(self):
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- comment = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='My test free comment!',
- )
-
- c = Context({
- 'topic': topic,
- 'parent': comment,
- })
- sc = {
- 'ct': content_type.pk,
- 'id': topic.pk,
- 'pid': comment.pk,
- }
-
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_free_comment_url topic %}').render(
- c), u'/freecomment/%(ct)s/%(id)s/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_free_comment_url topic parent %}').render(
- c), u'/freecomment/%(ct)s/%(id)s/%(pid)s/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_free_comment_url_json topic %}').render(
- c), u'/freecomment/%(ct)s/%(id)s/json/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_free_comment_url_xml topic %}').render(
- c), u'/freecomment/%(ct)s/%(id)s/xml/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_free_comment_url_json topic parent %}').render(
- c), u'/freecomment/%(ct)s/%(id)s/%(pid)s/json/' % sc)
- self.assertEquals(Template('{% load threadedcommentstags %}{% get_free_comment_url_xml topic parent %}').render(
- c), u'/freecomment/%(ct)s/%(id)s/%(pid)s/xml/' % sc)
-
- def test_get_comment_count(self):
-
- user = User.objects.create_user(
- 'user', 'floguy@xxxxxxxxx', password='password')
-
- topic = TestModel.objects.create(name='Test2')
-
- comment = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address='127.0.0.1',
- comment='My test comment!',
- )
-
- c = Context({
- 'topic': topic,
- })
-
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% get_comment_count for topic as count %}{{ count }}').render(c),
- u'1'
- )
-
- def test_get_free_comment_count(self):
-
- topic = TestModel.objects.create(name='Test2')
-
- comment = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='My test free comment!',
- )
-
- c = Context({
- 'topic': topic,
- })
-
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% get_free_comment_count for topic as count %}{{ count }}').render(c),
- u'1'
- )
-
- def test_get_threaded_comment_form(self):
- self.assertEquals(
- Template('{% load threadedcommentstags %}{% get_threaded_comment_form as form %}{{ form }}').render(
- Context({})),
- u'<tr><th><label for="id_comment">comment:</label></th><td><textarea id="id_comment" rows="10" cols="40" name="comment"></textarea></td></tr>\n<tr><th><label for="id_markup">Markup:</label></th><td><select name="markup" id="id_markup">\n<option value="">---------</option>\n<option value="1">markdown</option>\n<option value="2">textile</option>\n<option value="3">restructuredtext</option>\n<option value="5" selected="selected">plaintext</option>\n</select></td></tr>'
- )
-
- def test_get_latest_comments(self):
-
- user = User.objects.create_user(
- 'user', 'floguy@xxxxxxxxx', password='password')
-
- topic = TestModel.objects.create(name='Test2')
- old_topic = topic
- content_type = ContentType.objects.get_for_model(topic)
-
- ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address='127.0.0.1',
- comment='Test 1',
- )
- ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address='127.0.0.1',
- comment='Test 2',
- )
- ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address='127.0.0.1',
- comment='Test 3',
- )
-
- self.assertEquals(
- Template('{% load threadedcommentstags %}{% get_latest_comments 2 as comments %}{{ comments }}').render(
- Context({})),
- u'[<ThreadedComment: Test 3>, <ThreadedComment: Test 2>]'
- )
-
- def test_get_latest_free_comments(self):
-
- topic = TestModel.objects.create(name='Test2')
-
- FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='Test 1',
- )
- FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='Test 2',
- )
- FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='Test 3',
- )
-
- self.assertEquals(
- Template('{% load threadedcommentstags %}{% get_latest_free_comments 2 as comments %}{{ comments }}').render(
- Context({})),
- u'[<FreeThreadedComment: Test 3>, <FreeThreadedComment: Test 2>]'
- )
-
- def test_get_threaded_comment_tree(self):
-
- user = User.objects.create_user(
- 'user', 'floguy@xxxxxxxxx', password='password')
-
- topic = TestModel.objects.create(name='Test2')
-
- parent1 = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address='127.0.0.1',
- comment='test1',
- )
- ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address='127.0.0.1',
- comment='test2',
- parent=parent1,
- )
- parent2 = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address='127.0.0.1',
- comment='test3',
- )
- ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address='127.0.0.1',
- comment='test4',
- parent=parent2,
- )
-
- c = Context({
- 'topic': topic,
- })
-
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% get_threaded_comment_tree for topic as tree %}[{% for item in tree %}({{ item.depth }}){{ item.comment }},{% endfor %}]').render(c),
- u'[(0)test1,(1)test2,(0)test3,(1)test4,]'
- )
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% get_threaded_comment_tree for topic 3 as tree %}[{% for item in tree %}({{ item.depth }}){{ item.comment }},{% endfor %}]').render(c),
- u'[(0)test3,(1)test4,]'
- )
-
- def test_get_free_threaded_comment_tree(self):
-
- topic = TestModel.objects.create(name='Test2')
-
- parent1 = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='test1',
- )
- FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='test2',
- parent=parent1,
- )
- parent2 = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='test3',
- )
- FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='test4',
- parent=parent2,
- )
-
- c = Context({
- 'topic': topic,
- })
-
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% get_free_threaded_comment_tree for topic as tree %}[{% for item in tree %}({{ item.depth }}){{ item.comment }},{% endfor %}]').render(c),
- u'[(0)test1,(1)test2,(0)test3,(1)test4,]'
- )
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% get_free_threaded_comment_tree for topic 3 as tree %}[{% for item in tree %}({{ item.depth }}){{ item.comment }},{% endfor %}]').render(c),
- u'[(0)test3,(1)test4,]'
- )
-
- def test_user_comment_tags(self):
-
- user1 = User.objects.create_user(
- 'eric', 'floguy@xxxxxxxxx', password='password')
- user2 = User.objects.create_user(
- 'brian', 'brosner@xxxxxxxxx', password='password')
-
- topic = TestModel.objects.create(name='Test2')
-
- ThreadedComment.objects.create_for_object(topic,
- user=user1,
- ip_address='127.0.0.1',
- comment='Eric comment',
- )
- ThreadedComment.objects.create_for_object(topic,
- user=user2,
- ip_address='127.0.0.1',
- comment='Brian comment',
- )
-
- c = Context({
- 'user': user1,
- })
-
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% get_user_comments for user as comments %}{{ comments }}').render(c),
- u'[<ThreadedComment: Eric comment>]'
- )
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% get_user_comment_count for user as comment_count %}{{ comment_count }}').render(c),
- u'1',
- )
-
- def test_markdown_comment(self):
-
- user = User.objects.create_user(
- 'user', 'floguy@xxxxxxxxx', password='password')
- topic = TestModel.objects.create(name='Test2')
-
- markdown_txt = '''
-A First Level Header
-====================
-
-A Second Level Header
----------------------
-
-Now is the time for all good men to come to
-the aid of their country. This is just a
-regular paragraph.
-
-The quick brown fox jumped over the lazy
-dog's back.
-
-### Header 3
-
-> This is a blockquote.
->
-> This is the second paragraph in the blockquote.
->
-> ## This is an H2 in a blockquote
-'''
-
- comment_markdown = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', markup=MARKDOWN,
- comment=markdown_txt,
- )
-
- c = Context({
- 'comment': comment_markdown,
- })
- s = Template('{% load threadedcommentstags %}{% auto_transform_markup comment %}').render(
- c).replace('\\n', '')
- self.assertEquals(s.startswith(u"<h1>"), True)
-
- def test_textile_comment(self):
-
- user = User.objects.create_user(
- 'user', 'floguy@xxxxxxxxx', password='password')
- topic = TestModel.objects.create(name='Test2')
-
- textile_txt = '''
-h2{color:green}. This is a title
-
-h3. This is a subhead
-
-p{color:red}. This is some text of dubious character. Isn't the use of "quotes" just lazy ... writing -- and theft of 'intellectual property' besides? I think the time has come to see a block quote.
-
-bq[fr]. This is a block quote. I'll admit it's not the most exciting block quote ever devised.
-
-Simple list:
-
-#{color:blue} one
-# two
-# three
-
-Multi-level list:
-
-# one
-## aye
-## bee
-## see
-# two
-## x
-## y
-# three
-
-Mixed list:
-
-* Point one
-* Point two
-## Step 1
-## Step 2
-## Step 3
-* Point three
-** Sub point 1
-** Sub point 2
-
-
-Well, that went well. How about we insert an <a href="/" title="watch out">old-fashioned ... hypertext link</a>? Will the quote marks in the tags get messed up? No!
-
-"This is a link (optional title)":http://www.textism.com
-
-table{border:1px solid black}.
-|_. this|_. is|_. a|_. header|
-<{background:gray}. |\2. this is|{background:red;width:200px}. a|^<>{height:200px}. row|
-|this|<>{padding:10px}. is|^. another|(bob#bob). row|
-
-An image:
-
-!/common/textist.gif(optional alt text)!
-
-# Librarians rule
-# Yes they do
-# But you knew that
-
-Some more text of dubious character. Here is a noisome string of CAPITAL letters. Here is ... something we want to _emphasize_.
-That was a linebreak. And something to indicate *strength*. Of course I could use <em>my ... own HTML tags</em> if I <strong>felt</strong> like it.
-
-h3. Coding
-
-This <code>is some code, "isn't it"</code>. Watch those quote marks! Now for some preformatted text:
-
-<pre>
-<code>
- $text = str_replace("<p>%::%</p>","",$text);
- $text = str_replace("%::%</p>","",$text);
- $text = str_replace("%::%","",$text);
-
-</code>
-</pre>
-
-This isn't code.
-
-
-So you see, my friends:
-
-* The time is now
-* The time is not later
-* The time is not yesterday
-* We must act
-'''
-
- comment_textile = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', markup=TEXTILE,
- comment=textile_txt,
- )
- c = Context({
- 'comment': comment_textile
- })
- s = Template(
- '{% load threadedcommentstags %}{% auto_transform_markup comment %}').render(c)
- self.assertEquals('<h3>' in s, True)
-
- def test_rest_comment(self):
-
- user = User.objects.create_user(
- 'user', 'floguy@xxxxxxxxx', password='password')
- topic = TestModel.objects.create(name='Test2')
-
- rest_txt = '''
-FooBar Header
-=============
-reStructuredText is **nice**. It has its own webpage_.
-
-A table:
-
-===== ===== ======
- Inputs Output
------------- ------
- A B A or B
-===== ===== ======
-False False False
-True False True
-False True True
-True True True
-===== ===== ======
-
-RST TracLinks
--------------
-
-See also ticket `#42`::.
-
-.. _webpage: http://docutils.sourceforge.net/rst.html
-'''
-
- comment_rest = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', markup=REST,
- comment=rest_txt,
- )
- c = Context({
- 'comment': comment_rest
- })
- s = Template(
- '{% load threadedcommentstags %}{% auto_transform_markup comment %}').render(c)
- self.assertEquals(s.startswith('<p>reStructuredText is'), True)
-
- def test_plaintext_comment(self):
-
- user = User.objects.create_user(
- 'user', 'floguy@xxxxxxxxx', password='password')
- topic = TestModel.objects.create(name='Test2')
-
- comment_plaintext = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', markup=PLAINTEXT,
- comment='<b>This is Funny</b>',
- )
- c = Context({
- 'comment': comment_plaintext
- })
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% auto_transform_markup comment %}').render(c),
- u'<b>This is Funny</b>'
- )
-
- comment_plaintext = ThreadedComment.objects.create_for_object(
- topic, user=user, ip_address='127.0.0.1', markup=PLAINTEXT,
- comment='<b>This is Funny</b>',
- )
- c = Context({
- 'comment': comment_plaintext
- })
- self.assertEquals(
- Template(
- '{% load threadedcommentstags %}{% auto_transform_markup comment as abc %}{{ abc }}').render(c),
- u'<b>This is Funny</b>'
- )
-
- def test_gravatar_tags(self):
- c = Context({
- 'email': 'floguy@xxxxxxxxx',
- 'rating': 'G',
- 'size': 30,
- 'default': 'overridectx',
- })
- self.assertEquals(
- Template(
- '{% load gravatar %}{% get_gravatar_url for email %}').render(c),
- u'http://www.gravatar.com/avatar.php?gravatar_id=04d6b8e8d3c68899ac88eb8623392150&rating=R&size=80&default=img%3Ablank'
- )
- self.assertEquals(
- Template(
- '{% load gravatar %}{% get_gravatar_url for email as var %}Var: {{ var }}').render(c),
- u'Var: http://www.gravatar.com/avatar.php?gravatar_id=04d6b8e8d3c68899ac88eb8623392150&rating=R&size=80&default=img%3Ablank'
- )
- self.assertEquals(
- Template(
- '{% load gravatar %}{% get_gravatar_url for email size 30 rating "G" default override as var %}Var: {{ var }}').render(c),
- u'Var: http://www.gravatar.com/avatar.php?gravatar_id=04d6b8e8d3c68899ac88eb8623392150&rating=G&size=30&default=override'
- )
- self.assertEquals(
- Template(
- '{% load gravatar %}{% get_gravatar_url for email size size rating rating default default as var %}Var: {{ var }}').render(c),
- u'Var: http://www.gravatar.com/avatar.php?gravatar_id=04d6b8e8d3c68899ac88eb8623392150&rating=G&size=30&default=overridectx'
- )
- self.assertEquals(
- Template('{% load gravatar %}{{ email|gravatar }}').render(c),
- u'http://www.gravatar.com/avatar.php?gravatar_id=04d6b8e8d3c68899ac88eb8623392150&rating=R&size=80&default=img%3Ablank'
- )
=== removed file 'threadedcomments/tests/threadedcomments_urls.py'
--- threadedcomments/tests/threadedcomments_urls.py 2016-12-13 18:28:51 +0000
+++ threadedcomments/tests/threadedcomments_urls.py 1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
-from django.conf.urls.defaults import *
-
-
-urlpatterns = patterns('',
- url(r"", include('threadedcomments.urls')),
- )
=== removed file 'threadedcomments/tests/views_tests.py'
--- threadedcomments/tests/views_tests.py 2016-12-13 18:28:51 +0000
+++ threadedcomments/tests/views_tests.py 1970-01-01 00:00:00 +0000
@@ -1,856 +0,0 @@
-import datetime
-
-from xml.dom.minidom import parseString
-
-from django.core import mail
-from django.core.urlresolvers import reverse
-from django.template import Context, Template
-from django.test import TestCase
-from django.utils.simplejson import loads
-
-from django.contrib.auth.models import User
-from django.contrib.contenttypes.models import ContentType
-
-from threadedcomments.models import FreeThreadedComment, ThreadedComment, TestModel
-from threadedcomments.models import MARKDOWN, TEXTILE, REST, PLAINTEXT
-from threadedcomments.templatetags import threadedcommentstags as tags
-
-
-__all__ = ('ViewsTestCase',)
-
-
-class ViewsTestCase(TestCase):
- urls = 'threadedcomments.tests.threadedcomments_urls'
-
- def test_freecomment_create(self):
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- url = reverse('tc_free_comment', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id
- })
- response = self.client.post(url, {
- 'comment': 'test1',
- 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx',
- 'next': '/'
- })
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://www.eflorenzano.com/',
- 'comment': u'test1',
- 'name': u'eric',
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'floguy@xxxxxxxxx',
- 'is_approved': False
- })
-
- def test_freecomment_preview(self):
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- url = reverse('tc_free_comment', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id
- })
-
- response = self.client.post(url, {
- 'comment': 'test1',
- 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx',
- 'next': '/',
- 'preview': 'True'
- })
- self.assertEquals(len(response.content) > 0, True)
-
- def test_freecomment_edit(self):
-
- topic = TestModel.objects.create(name='Test2')
-
- comment = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='My test free comment!',
- )
-
- url = reverse('tc_free_comment_edit', kwargs={
- 'edit_id': comment.pk
- })
-
- response = self.client.post(url, {
- 'comment': 'test1_edited',
- 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx',
- 'next': '/'
- })
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://www.eflorenzano.com/',
- 'comment': u'test1_edited',
- 'name': u'eric',
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'floguy@xxxxxxxxx',
- 'is_approved': False
- })
-
- def test_freecomment_edit_with_preview(self):
-
- topic = TestModel.objects.create(name='Test2')
-
- comment = FreeThreadedComment.objects.create_for_object(topic,
- website='http://oebfare.com/',
- comment='My test free comment!',
- ip_address='127.0.0.1',
- )
-
- url = reverse('tc_free_comment_edit', kwargs={
- 'edit_id': comment.pk
- })
-
- response = self.client.post(url, {
- 'comment': 'test1_edited',
- 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx',
- 'next': '/',
- 'preview': 'True'
- })
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://oebfare.com/',
- 'comment': u'My test free comment!',
- 'name': u'',
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'',
- 'is_approved': False
- })
- self.assertEquals(len(response.content) > 0, True)
-
- def test_freecomment_json_create(self):
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- url = reverse('tc_free_comment_ajax', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'ajax': 'json'
- })
-
- response = self.client.post(url, {
- 'comment': 'test2',
- 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx'
- })
- tmp = loads(response.content)
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://www.eflorenzano.com/',
- 'comment': u'test2',
- 'name': u'eric',
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'floguy@xxxxxxxxx',
- 'is_approved': False
- })
-
- def test_freecomment_json_edit(self):
-
- topic = TestModel.objects.create(name='Test2')
-
- comment = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='My test free comment!',
- )
-
- url = reverse('tc_free_comment_edit_ajax', kwargs={
- 'edit_id': comment.pk,
- 'ajax': 'json'
- })
-
- response = self.client.post(url, {
- 'comment': 'test2_edited',
- 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx'
- })
- tmp = loads(response.content)
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://www.eflorenzano.com/',
- 'comment': u'test2_edited',
- 'name': u'eric',
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'floguy@xxxxxxxxx',
- 'is_approved': False
- })
-
- def test_freecomment_xml_create(self):
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- url = reverse('tc_free_comment_ajax', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'ajax': 'xml'
- })
-
- response = self.client.post(url, {'comment': 'test3', 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/', 'email': 'floguy@xxxxxxxxx', 'next': '/'})
- tmp = parseString(response.content)
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://www.eflorenzano.com/',
- 'comment': u'test3',
- 'name': u'eric',
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'floguy@xxxxxxxxx',
- 'is_approved': False
- })
-
- def test_freecomment_xml_edit(self):
-
- topic = TestModel.objects.create(name='Test2')
-
- comment = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='My test free comment!',
- )
-
- url = reverse('tc_free_comment_edit_ajax', kwargs={
- 'edit_id': comment.pk,
- 'ajax': 'xml'
- })
-
- response = self.client.post(url, {
- 'comment': 'test2_edited',
- 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx'
- })
- tmp = parseString(response.content)
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://www.eflorenzano.com/',
- 'comment': u'test2_edited',
- 'name': u'eric',
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'floguy@xxxxxxxxx',
- 'is_approved': False
- })
-
- def test_freecomment_child_create(self):
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- parent = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='My test free comment!',
- )
-
- url = reverse('tc_free_comment_parent', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'parent_id': parent.id
- })
- response = self.client.post(url, {
- 'comment': 'test4',
- 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx',
- 'next': '/'
- })
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://www.eflorenzano.com/',
- 'comment': u'test4',
- 'name': u'eric',
- 'parent': parent,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'floguy@xxxxxxxxx',
- 'is_approved': False
- })
-
- def test_freecomment_child_json_create(self):
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- parent = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='My test free comment!',
- )
-
- url = reverse('tc_free_comment_parent_ajax', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'parent_id': parent.id,
- 'ajax': 'json'
- })
-
- response = self.client.post(url, {
- 'comment': 'test5',
- 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx'
- })
- tmp = loads(response.content)
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://www.eflorenzano.com/',
- 'comment': u'test5',
- 'name': u'eric',
- 'parent': parent,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'floguy@xxxxxxxxx',
- 'is_approved': False
- })
-
- def test_freecomment_child_xml_create(self):
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- parent = FreeThreadedComment.objects.create_for_object(topic,
- ip_address='127.0.0.1',
- comment='My test free comment!',
- )
-
- url = reverse('tc_free_comment_parent_ajax', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'parent_id': parent.id,
- 'ajax': 'xml'
- })
-
- response = self.client.post(url, {
- 'comment': 'test6', 'name': 'eric',
- 'website': 'http://www.eflorenzano.com/',
- 'email': 'floguy@xxxxxxxxx'
- })
- tmp = parseString(response.content)
- o = FreeThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'website': u'http://www.eflorenzano.com/',
- 'comment': u'test6',
- 'name': u'eric',
- 'parent': parent,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- 'email': u'floguy@xxxxxxxxx',
- 'is_approved': False
- })
-
- def create_user_and_login(self):
- user = User.objects.create_user(
- 'testuser',
- 'testuser@xxxxxxxxx',
- 'password',
- )
- user.is_active = True
- user.save()
- self.client.login(username='testuser', password='password')
- return user
-
- def test_comment_create(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- url = reverse('tc_comment', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id
- })
-
- response = self.client.post(url, {
- 'comment': 'test7',
- 'next': '/'
- })
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'test7',
- 'is_approved': False,
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_comment_preview(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- url = reverse('tc_comment', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id
- })
-
- response = self.client.post(url, {
- 'comment': 'test7',
- 'next': '/',
- 'preview': 'True'
- })
- self.assertEquals(len(response.content) > 0, True)
-
- def test_comment_edit(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- comment = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address=u'127.0.0.1',
- comment='My test comment!',
- )
-
- url = reverse('tc_comment_edit', kwargs={
- 'edit_id': comment.pk,
- })
-
- response = self.client.post(url, {
- 'comment': 'test7_edited',
- 'next': '/',
- })
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'test7_edited',
- 'is_approved': False,
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_comment_edit_with_preview(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- comment = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address=u'127.0.0.1',
- comment='My test comment!',
- )
-
- url = reverse('tc_comment_edit', kwargs={
- 'edit_id': comment.pk,
- })
-
- response = self.client.post(url, {
- 'comment': 'test7_edited',
- 'next': '/',
- 'preview': 'True'
- })
-
- self.assertEquals(len(response.content) > 0, True)
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'My test comment!',
- 'is_approved': False,
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_comment_json_create(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- url = reverse('tc_comment_ajax', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'ajax': 'json'
- })
-
- response = self.client.post(url, {
- 'comment': 'test8'
- })
- tmp = loads(response.content)
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'test8',
- 'is_approved': False,
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_comment_json_edit(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- comment = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address=u'127.0.0.1',
- comment='My test comment!',
- )
-
- url = reverse('tc_comment_edit_ajax', kwargs={
- 'edit_id': comment.pk,
- 'ajax': 'json',
- })
-
- response = self.client.post(url, {
- 'comment': 'test8_edited'
- })
- tmp = loads(response.content)
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'test8_edited',
- 'is_approved': False,
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_comment_xml_create(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- url = reverse('tc_comment_ajax', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'ajax': 'xml'
- })
-
- response = self.client.post(url, {
- 'comment': 'test9'
- })
- tmp = parseString(response.content)
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'test9',
- 'is_approved': False,
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_comment_xml_edit(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- comment = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address=u'127.0.0.1',
- comment='My test comment!',
- )
-
- url = reverse('tc_comment_edit_ajax', kwargs={
- 'edit_id': comment.pk,
- 'ajax': 'xml',
- })
-
- response = self.client.post(url, {
- 'comment': 'test8_edited'
- })
- tmp = parseString(response.content)
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'test8_edited',
- 'is_approved': False,
- 'parent': None,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_comment_child_create(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- parent = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address=u'127.0.0.1',
- comment='My test comment!',
- )
-
- url = reverse('tc_comment_parent', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'parent_id': parent.id
- })
-
- response = self.client.post(url, {
- 'comment': 'test10',
- 'next': '/'
- })
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'test10',
- 'is_approved': False,
- 'parent': parent,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_comment_child_json_create(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- parent = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address=u'127.0.0.1',
- comment='My test comment!',
- )
-
- url = reverse('tc_comment_parent_ajax', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'parent_id': parent.id,
- 'ajax': 'json'
- })
-
- response = self.client.post(url, {
- 'comment': 'test11'
- })
- tmp = loads(response.content)
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'test11',
- 'is_approved': False,
- 'parent': parent,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_comment_child_xml_create(self):
-
- user = self.create_user_and_login()
-
- topic = TestModel.objects.create(name='Test2')
- content_type = ContentType.objects.get_for_model(topic)
-
- parent = ThreadedComment.objects.create_for_object(topic,
- user=user,
- ip_address=u'127.0.0.1',
- comment='My test comment!',
- )
-
- url = reverse('tc_comment_parent_ajax', kwargs={
- 'content_type': content_type.id,
- 'object_id': topic.id,
- 'parent_id': parent.id,
- 'ajax': 'xml'
- })
-
- response = self.client.post(url, {
- 'comment': 'test12'
- })
- tmp = parseString(response.content)
- o = ThreadedComment.objects.latest(
- 'date_submitted').get_base_data(show_dates=False)
- self.assertEquals(o, {
- 'comment': u'test12',
- 'is_approved': False,
- 'parent': parent,
- 'markup': u'plaintext',
- 'content_object': topic,
- 'user': user,
- 'is_public': True,
- 'ip_address': u'127.0.0.1',
- })
-
- def test_freecomment_delete(self):
-
- user = User.objects.create_user(
- 'testuser',
- 'testuser@xxxxxxxxx',
- 'password',
- )
- user.is_active = True
- user.save()
- self.client.login(username='testuser', password='password')
-
- topic = TestModel.objects.create(name='Test2')
-
- comment = FreeThreadedComment.objects.create_for_object(topic,
- ip_address=u'127.0.0.1',
- comment='My test comment!',
- )
- deleted_id = comment.id
-
- url = reverse('tc_free_comment_delete', kwargs={
- 'object_id': comment.id,
- })
-
- response = self.client.post(url, {'next': '/'})
- o = response['Location'].split(
- '?')[-1] == 'next=/freecomment/%d/delete/' % deleted_id
- self.assertEquals(o, True)
-
- # become super user and try deleting comment
- user.is_superuser = True
- user.save()
-
- response = self.client.post(url, {'next': '/'})
- self.assertEquals(response['Location'], 'http://testserver/')
- self.assertRaises(
- FreeThreadedComment.DoesNotExist,
- lambda: FreeThreadedComment.objects.get(id=deleted_id)
- )
-
- # re-create comment
- comment.save()
-
- response = self.client.get(url, {'next': '/'})
- self.assertEquals(len(response.content) > 0, True)
-
- o = FreeThreadedComment.objects.get(id=deleted_id) != None
- self.assertEquals(o, True)
-
- def test_comment_delete(self):
-
- some_other_guy = User.objects.create_user(
- 'some_other_guy',
- 'somewhere@xxxxxxxxxxxxxxx',
- 'password1',
- )
- user = User.objects.create_user(
- 'testuser',
- 'testuser@xxxxxxxxx',
- 'password',
- )
- user.is_active = True
- user.save()
- self.client.login(username='testuser', password='password')
-
- topic = TestModel.objects.create(name='Test2')
-
- comment = ThreadedComment.objects.create_for_object(topic,
- user=some_other_guy,
- ip_address=u'127.0.0.1',
- comment='My test comment!',
- )
- deleted_id = comment.id
-
- url = reverse('tc_comment_delete', kwargs={
- 'object_id': comment.id,
- })
- response = self.client.post(url, {'next': '/'})
- self.assertEquals(response['Location'].split(
- '?')[-1], 'next=/comment/%s/delete/' % deleted_id)
-
- user.is_superuser = True
- user.save()
-
- response = self.client.post(url, {'next': '/'})
- self.assertEquals(response['Location'], 'http://testserver/')
- self.assertRaises(
- ThreadedComment.DoesNotExist,
- lambda: ThreadedComment.objects.get(id=deleted_id)
- )
-
- # re-create comment
- comment.save()
-
- response = self.client.get(url, {'next': '/'})
- self.assertEquals(len(response.content) > 0, True)
-
- o = ThreadedComment.objects.get(id=deleted_id) != None
- self.assertEquals(o, True)
=== modified file 'threadedcomments/views.py'
--- threadedcomments/views.py 2016-12-13 18:28:51 +0000
+++ threadedcomments/views.py 2018-04-12 06:55:31 +0000
@@ -1,7 +1,7 @@
from django.http import HttpResponseRedirect, Http404
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
-from django.shortcuts import get_object_or_404, render_to_response
+from django.shortcuts import get_object_or_404, render
from django.template import RequestContext, Context, Template
from django.utils.http import urlquote
from django.conf import settings
@@ -53,11 +53,10 @@
context['comment'] = new_comment
else:
context['comment'] = None
- return render_to_response(
- 'threadedcomments/preview_comment.html',
- extra_context,
- context_instance=RequestContext(request, context, context_processors)
- )
+ return render(request,
+ 'threadedcomments/preview_comment.html',
+ extra_context,
+ )
def free_comment(request, content_type=None, object_id=None, edit_id=None, parent_id=None, add_messages=False, ajax=False, model=FreeThreadedComment, form_class=FreeThreadedCommentForm, context_processors=[], extra_context={}):
@@ -151,7 +150,7 @@
return True
return False
-
+# Todo: Next one is not used so far and may need adjustments to the render()
def comment_delete(request, object_id, model=ThreadedComment, extra_context={}, context_processors=[], permission_callback=can_delete_comment):
"""Deletes the specified comment, which can be either a
``FreeThreadedComment`` or a ``ThreadedComment``.
@@ -175,17 +174,15 @@
else:
is_free_threaded_comment = True
is_threaded_comment = False
- return render_to_response(
- 'threadedcomments/confirm_delete.html',
- extra_context,
- context_instance=RequestContext(
- request,
- {
- 'comment': tc,
- 'is_free_threaded_comment': is_free_threaded_comment,
- 'is_threaded_comment': is_threaded_comment,
- 'next': _get_next(request),
- },
- context_processors
+
+ extra_context.update(
+ {'comment': tc,
+ 'is_free_threaded_comment': is_free_threaded_comment,
+ 'is_threaded_comment': is_threaded_comment,
+ 'next': _get_next(request),
+ }
)
- )
+ return render(request,
+ 'threadedcomments/confirm_delete.html',
+ extra_context,
+ )
=== removed directory 'tracking'
=== removed file 'tracking/AUTHORS'
--- tracking/AUTHORS 2016-06-05 20:58:46 +0000
+++ tracking/AUTHORS 1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
-django-tracking was written by Josh VanderLinden
-
-There have been many contributions, including:
-
-ramusus (https://github.com/ramusus/django-tracking)
-jezdez (https://bitbucket.org/jezdez/django-tracking)
=== removed file 'tracking/CHANGES'
--- tracking/CHANGES 2016-06-05 20:58:46 +0000
+++ tracking/CHANGES 1970-01-01 00:00:00 +0000
@@ -1,44 +0,0 @@
-=======================
-django-tracking changes
-=======================
-
-0.3.5
------
-
-- Using Django's GIS utilities a little better
-- Added caching in the middleware to reduce hits to the database
-- Added some logging
-
-0.3.3
------
-
-- Improved the setup.py script
-
-0.3.2
------
-
-- Merged changes from ramusus to better deal with the unicode problems
-
-0.3.1
------
-
-- Trying to handle some unicode problems
-- Added a sample base.html template
-- Code cleaning
-
-0.3.0
------
-
-- Fixed several bugs dealing with performance
-- Improved stability
-- Added some German translations
-- Removed dependency on GeoIP C API and Python API in favor of Django's
- built-in GIS utilities
-- Tweaked the active users map
-
-0.2.5
------
-
-- Did some code optimization
-- Added the ability to have a dynamically updating Google Map to show where your
- visitors are coming from
=== removed file 'tracking/LICENSE'
--- tracking/LICENSE 2016-06-05 20:58:46 +0000
+++ tracking/LICENSE 1970-01-01 00:00:00 +0000
@@ -1,19 +0,0 @@
-Copyright (c) 2008-2009 Josh VanderLinden
-
-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.
\ No newline at end of file
=== removed file 'tracking/__init__.py'
--- tracking/__init__.py 2016-12-13 18:28:51 +0000
+++ tracking/__init__.py 1970-01-01 00:00:00 +0000
@@ -1,8 +0,0 @@
-import listeners
-
-VERSION = (0, 4, 0)
-
-
-def get_version():
- 'Returns the version as a human-format string.'
- return '.'.join([str(i) for i in VERSION])
=== removed file 'tracking/admin.py'
--- tracking/admin.py 2016-12-13 18:28:51 +0000
+++ tracking/admin.py 1970-01-01 00:00:00 +0000
@@ -1,5 +0,0 @@
-from django.contrib import admin
-from tracking.models import BannedIP, UntrackedUserAgent
-
-admin.site.register(BannedIP)
-admin.site.register(UntrackedUserAgent)
=== removed file 'tracking/listeners.py'
--- tracking/listeners.py 2016-12-13 18:28:51 +0000
+++ tracking/listeners.py 1970-01-01 00:00:00 +0000
@@ -1,35 +0,0 @@
-import logging
-
-log = logging.getLogger('tracking.listeners')
-
-try:
- from django.core.cache import cache
- from django.db.models.signals import post_save, post_delete
-
- from tracking.models import UntrackedUserAgent, BannedIP
-except ImportError:
- pass
-else:
-
- def refresh_untracked_user_agents(sender, instance, created=False, **kwargs):
- """Updates the cache of user agents that we don't track."""
-
- log.debug('Updating untracked user agents cache')
- cache.set('_tracking_untracked_uas',
- UntrackedUserAgent.objects.all(),
- 3600)
-
- def refresh_banned_ips(sender, instance, created=False, **kwargs):
- """Updates the cache of banned IP addresses."""
-
- log.debug('Updating banned IP cache')
- cache.set('_tracking_banned_ips',
- [b.ip_address for b in BannedIP.objects.all()],
- 3600)
-
- post_save.connect(refresh_untracked_user_agents, sender=UntrackedUserAgent)
- post_delete.connect(refresh_untracked_user_agents,
- sender=UntrackedUserAgent)
-
- post_save.connect(refresh_banned_ips, sender=BannedIP)
- post_delete.connect(refresh_banned_ips, sender=BannedIP)
=== removed directory 'tracking/locale'
=== removed directory 'tracking/locale/de'
=== removed directory 'tracking/locale/de/LC_MESSAGES'
=== removed file 'tracking/locale/de/LC_MESSAGES/django.mo'
Binary files tracking/locale/de/LC_MESSAGES/django.mo 2016-06-05 20:58:46 +0000 and tracking/locale/de/LC_MESSAGES/django.mo 1970-01-01 00:00:00 +0000 differ
=== removed file 'tracking/locale/de/LC_MESSAGES/django.po'
--- tracking/locale/de/LC_MESSAGES/django.po 2016-06-05 20:58:46 +0000
+++ tracking/locale/de/LC_MESSAGES/django.po 1970-01-01 00:00:00 +0000
@@ -1,110 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-03-24 12:45+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: Jannis Leidel <jannis@xxxxxxxxxxx>\n"
-"Language-Team: LANGUAGE <LL@xxxxxx>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: models.py:77
-msgid "unknown"
-msgstr "unbekannt"
-
-#: models.py:121
-msgid "keyword"
-msgstr "Schlagwort"
-
-#: models.py:121
-msgid ""
-"Part or all of a user-agent string. For example, \"Googlebot\" here will be "
-"found in \"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/"
-"bot.html)\" and that visitor will not be tracked."
-msgstr ""
-"Ein Teil oder der ganze Name eines User Agent. \"Googlebot\" zum Beispiel "
-"wird auch in \"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google."
-"com/bot.html)\" gefunden werden und verhindern, dass dieser \"User\" "
-"getrackt wird."
-
-#: models.py:128
-msgid "Untracked User-Agent"
-msgstr "Nicht-getrackter User-Agent"
-
-#: models.py:129
-msgid "Untracked User-Agents"
-msgstr "Nicht-getrackte User-Agents"
-
-#: models.py:132
-msgid "The IP address that should be banned"
-msgstr "Die IP-Adresse ist schon verboten"
-
-#: models.py:139
-msgid "Banned IP"
-msgstr "Verbotene IP"
-
-#: models.py:140
-msgid "Banned IPs"
-msgstr "Verbotene IPs"
-
-#: views.py:79
-#, python-format
-msgid "%(minutes)i minute"
-msgid_plural "%(minutes)i minutes"
-msgstr[0] "%(minutes)i Minute"
-msgstr[1] "%(minutes)i Minuten"
-
-#: views.py:85
-#, python-format
-msgid "%(seconds)i second"
-msgid_plural "%(seconds)i seconds"
-msgstr[0] "%(seconds)i Sekunde"
-msgstr[1] "%(seconds)i Sekunden"
-
-#: templates/tracking/_active_users.html:2
-#, python-format
-msgid "1 active user"
-msgid_plural "%(counter)s active users"
-msgstr[0] "1 aktiver Benutzer"
-msgstr[1] "%(counter)s aktive Benutzer"
-
-#: templates/tracking/_active_users.html:5
-#, python-format
-msgid "1 guest user"
-msgid_plural "%(counter)s guest users"
-msgstr[0] "1 Gastbenutzer"
-msgstr[1] "%(counter)s Gastbenutzer"
-
-#: templates/tracking/_active_users.html:15
-#, python-format
-msgid "1 registered user"
-msgid_plural "%(counter)s registered users"
-msgstr[0] "1 registrierter Benutzer"
-msgstr[1] "%(counter)s registrierte Benutzer"
-
-#: templates/tracking/visitor_map.html:4
-msgid "Active Visitors Map"
-msgstr "Aktive Benutzer Karte"
-
-#: templates/tracking/visitor_map.html:15
-msgid "Active Users"
-msgstr "Aktive Benutzer"
-
-#: templates/tracking/visitor_map.html:18
-msgid ""
-"Below is a map and a list of recently active users on this site. It updates "
-"itself every 5 seconds or so, adding and removing pins in the map as "
-"necessary. Stick around for a few minutes to see it in action!"
-msgstr ""
-"Unten sehen Sie eine Karte und eine Liste der Benutzer, die gerade auf "
-"dieser Seite aktiv sind. Ungefähr alle 5 Sekunden werden die Daten "
-"aktualisert und rendern die Karte neu. Bleiben Sie ein paar Minuten, um es "
-"live in Aktion zu erleben!"
=== removed directory 'tracking/media'
=== removed directory 'tracking/media/tracking'
=== removed directory 'tracking/media/tracking/img'
=== removed file 'tracking/media/tracking/img/Flags.zip'
Binary files tracking/media/tracking/img/Flags.zip 2016-06-05 20:58:46 +0000 and tracking/media/tracking/img/Flags.zip 1970-01-01 00:00:00 +0000 differ
=== removed file 'tracking/media/tracking/img/famfamfam_flag_icons.zip'
Binary files tracking/media/tracking/img/famfamfam_flag_icons.zip 2016-06-05 20:58:46 +0000 and tracking/media/tracking/img/famfamfam_flag_icons.zip 1970-01-01 00:00:00 +0000 differ
=== removed directory 'tracking/media/tracking/js'
=== removed file 'tracking/media/tracking/js/jquery-1.4.4.min.js'
--- tracking/media/tracking/js/jquery-1.4.4.min.js 2016-06-05 20:58:46 +0000
+++ tracking/media/tracking/js/jquery-1.4.4.min.js 1970-01-01 00:00:00 +0000
@@ -1,167 +0,0 @@
-/*!
- * jQuery JavaScript Library v1.4.4
- * http://jquery.com/
- *
- * Copyright 2010, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- * Copyright 2010, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- *
- * Date: Thu Nov 11 19:04:53 2010 -0500
- */
-(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
-h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;k<J.length;k++){h=J[k];h.origType.replace(X,"")===a.type?f.push(h.selector):J.splice(k--,1)}f=c(a.target).closest(f,a.currentTarget);o=0;for(x=f.length;o<x;o++){r=f[o];for(k=0;k<J.length;k++){h=J[k];if(r.selector===h.selector&&(!A||A.test(h.namespace))){l=r.elem;e=null;if(h.preType==="mouseenter"||
-h.preType==="mouseleave"){a.type=h.preType;e=c(a.relatedTarget).closest(h.selector)[0]}if(!e||e!==l)C.push({elem:l,handleObj:h,level:r.level})}}}o=0;for(x=C.length;o<x;o++){f=C[o];if(d&&f.level>d)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La,
-"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,
-e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,
-"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+
-a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,
-C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j,
-s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,
-j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length},
-toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j===
--1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false;
-if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K<Q;K++)if((j=arguments[K])!=null)for(s in j){v=G[s];z=j[s];if(G!==z)if(ga&&z&&(b.isPlainObject(z)||(H=b.isArray(z)))){if(H){H=false;v=v&&b.isArray(v)?v:[]}else v=v&&b.isPlainObject(v)?v:{};G[s]=b.extend(ga,v,z)}else if(z!==B)G[s]=z}return G};b.extend({noConflict:function(j){E.$=e;if(j)E.jQuery=d;return b},isReady:false,readyWait:1,ready:function(j){j===true&&b.readyWait--;
-if(!b.readyWait||j!==true&&!b.isReady){if(!t.body)return setTimeout(b.ready,1);b.isReady=true;if(!(j!==true&&--b.readyWait>0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
-b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
-!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
-l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H<G;){if(s.apply(j[H++],v)===false)break}else if(K)for(z in j){if(s.call(j[z],
-z,j[z])===false)break}else for(v=j[0];H<G&&s.call(v,H,v)!==false;v=j[++H]);return j},trim:O?function(j){return j==null?"":O.call(j)}:function(j){return j==null?"":j.toString().replace(k,"").replace(o,"")},makeArray:function(j,s){var v=s||[];if(j!=null){var z=b.type(j);j.length==null||z==="string"||z==="function"||z==="regexp"||b.isWindow(j)?M.call(v,j):b.merge(v,j)}return v},inArray:function(j,s){if(s.indexOf)return s.indexOf(j);for(var v=0,z=s.length;v<z;v++)if(s[v]===j)return v;return-1},merge:function(j,
-s){var v=j.length,z=0;if(typeof s.length==="number")for(var H=s.length;z<H;z++)j[v++]=s[z];else for(;s[z]!==B;)j[v++]=s[z++];j.length=v;return j},grep:function(j,s,v){var z=[],H;v=!!v;for(var G=0,K=j.length;G<K;G++){H=!!s(j[G],G);v!==H&&z.push(j[G])}return z},map:function(j,s,v){for(var z=[],H,G=0,K=j.length;G<K;G++){H=s(j[G],G,v);if(H!=null)z[z.length]=H}return z.concat.apply([],z)},guid:1,proxy:function(j,s,v){if(arguments.length===2)if(typeof s==="string"){v=j;j=v[s];s=B}else if(s&&!b.isFunction(s)){v=
-s;s=B}if(!s&&j)s=function(){return j.apply(v||this,arguments)};if(j)s.guid=j.guid=j.guid||s.guid||b.guid++;return s},access:function(j,s,v,z,H,G){var K=j.length;if(typeof s==="object"){for(var Q in s)b.access(j,Q,s[Q],z,H,v);return j}if(v!==B){z=!G&&z&&b.isFunction(v);for(Q=0;Q<K;Q++)H(j[Q],s,z?v.call(j[Q],Q,H(j[Q],s)):v,G);return j}return K?H(j[0],s):B},now:function(){return(new Date).getTime()},uaMatch:function(j){j=j.toLowerCase();j=L.exec(j)||g.exec(j)||i.exec(j)||j.indexOf("compatible")<0&&n.exec(j)||
-[];return{browser:j[1]||"",version:j[2]||"0"}},browser:{}});b.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(j,s){R["[object "+s+"]"]=s.toLowerCase()});m=b.uaMatch(m);if(m.browser){b.browser[m.browser]=true;b.browser.version=m.version}if(b.browser.webkit)b.browser.safari=true;if(D)b.inArray=function(j,s){return D.call(s,j)};if(!/\s/.test("\u00a0")){k=/^[\s\xA0]+/;o=/[\s\xA0]+$/}f=b(t);if(t.addEventListener)u=function(){t.removeEventListener("DOMContentLoaded",u,
-false);b.ready()};else if(t.attachEvent)u=function(){if(t.readyState==="complete"){t.detachEvent("onreadystatechange",u);b.ready()}};return E.jQuery=E.$=b}();(function(){c.support={};var a=t.documentElement,b=t.createElement("script"),d=t.createElement("div"),e="script"+c.now();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"),
-k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false,
-scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent=
-false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom=
-1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="<div style='width:4px;'></div>";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display=
-"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h=
-c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);
-else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h<l;h++){f=e[h].name;if(f.indexOf("data-")===0){f=f.substr(5);ka(this[0],f,d[f])}}}return d}else if(typeof a==="object")return this.each(function(){c.data(this,
-a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(b===B){d=this.triggerHandler("getData"+k[1]+"!",[k[0]]);if(d===B&&this.length){d=c.data(this[0],a);d=ka(this[0],a,d)}return d===B&&k[1]?this.data(k[0]):d}else return this.each(function(){var o=c(this),x=[k[0],b];o.triggerHandler("setData"+k[1]+"!",x);c.data(this,a,b);o.triggerHandler("changeData"+k[1]+"!",x)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var e=
-c.data(a,b);if(!d)return e||[];if(!e||c.isArray(d))e=c.data(a,b,c.makeArray(d));else e.push(d);return e}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),e=d.shift();if(e==="inprogress")e=d.shift();if(e){b==="fx"&&d.unshift("inprogress");e.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===B)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,
-a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var sa=/[\n\t]/g,ha=/\s+/,Sa=/\r/g,Ta=/^(?:href|src|style)$/,Ua=/^(?:button|input)$/i,Va=/^(?:button|input|object|select|textarea)$/i,Wa=/^a(?:rea)?$/i,ta=/^(?:radio|checkbox)$/i;c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",
-colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};c.fn.extend({attr:function(a,b){return c.access(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(x){var r=c(this);r.addClass(a.call(this,x,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===
-1)if(f.className){for(var h=" "+f.className+" ",l=f.className,k=0,o=b.length;k<o;k++)if(h.indexOf(" "+b[k]+" ")<0)l+=" "+b[k];f.className=c.trim(l)}else f.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var x=c(this);x.removeClass(a.call(this,o,x.attr("class")))});if(a&&typeof a==="string"||a===B)for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===1&&f.className)if(a){for(var h=(" "+f.className+" ").replace(sa," "),
-l=0,k=b.length;l<k;l++)h=h.replace(" "+b[l]+" "," ");f.className=c.trim(h)}else f.className=""}return this},toggleClass:function(a,b){var d=typeof a,e=typeof b==="boolean";if(c.isFunction(a))return this.each(function(f){var h=c(this);h.toggleClass(a.call(this,f,h.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var f,h=0,l=c(this),k=b,o=a.split(ha);f=o[h++];){k=e?k:!l.hasClass(f);l[k?"addClass":"removeClass"](f)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,
-"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(sa," ").indexOf(a)>-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";
-if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h<e;h++){var l=f[h];if(l.selected&&(c.support.optDisabled?!l.disabled:l.getAttribute("disabled")===null)&&(!l.parentNode.disabled||!c.nodeName(l.parentNode,"optgroup"))){a=c(l).val();if(b)return a;d.push(a)}}return d}if(ta.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Sa,"")}return B}var k=c.isFunction(a);return this.each(function(o){var x=c(this),r=a;if(this.nodeType===1){if(k)r=
-a.call(this,o,x.val());if(r==null)r="";else if(typeof r==="number")r+="";else if(c.isArray(r))r=c.map(r,function(C){return C==null?"":C+""});if(c.isArray(r)&&ta.test(this.type))this.checked=c.inArray(x.val(),r)>=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},
-attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&
-b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0};
-c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,
-arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid=
-d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+
-c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h<A.length;h++){C=A[h];if(d.guid===C.guid){if(k||x.test(C.namespace)){e==null&&A.splice(h--,1);r.remove&&r.remove.call(a,C)}if(e!=null)break}}if(A.length===0||e!=null&&A.length===1){if(!r.teardown||r.teardown.call(a,o)===false)c.removeEvent(a,f,w.handle);delete I[f]}}else for(h=0;h<A.length;h++){C=A[h];if(k||x.test(C.namespace)){c.event.remove(a,r,C.handler,h);A.splice(h--,1)}}}if(c.isEmptyObject(I)){if(b=
-w.handle)b.elem=null;delete w.events;delete w.handle;if(typeof w==="function")c.removeData(a,J);else c.isEmptyObject(w)&&c.removeData(a)}}}}},trigger:function(a,b,d,e){var f=a.type||a;if(!e){a=typeof a==="object"?a[c.expando]?a:c.extend(c.Event(f),a):c.Event(f);if(f.indexOf("!")>=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===
-8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k===
-"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+
-d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f<l;f++){var k=d[f];if(b||e.test(k.namespace)){a.handler=k.handler;a.data=k.data;a.handleObj=k;k=k.handler.apply(this,h);if(k!==B){a.result=k;if(k===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
-fix:function(a){if(a[c.expando])return a;var b=a;a=c.Event(b);for(var d=this.props.length,e;d;){e=this.props[--d];a[e]=b[e]}if(!a.target)a.target=a.srcElement||t;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=t.documentElement;d=t.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
-d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(a.which==null&&(a.charCode!=null||a.keyCode!=null))a.which=a.charCode!=null?a.charCode:a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==B)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,Y(a.origType,a.selector),c.extend({},a,{handler:Ka,guid:a.handler.guid}))},remove:function(a){c.event.remove(this,
-Y(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,d){if(c.isWindow(this))this.onbeforeunload=d},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.removeEvent=t.removeEventListener?function(a,b,d){a.removeEventListener&&a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent&&a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=
-c.now();this[c.expando]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=ca;var a=this.originalEvent;if(a)if(a.preventDefault)a.preventDefault();else a.returnValue=false},stopPropagation:function(){this.isPropagationStopped=ca;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=ca;this.stopPropagation()},isDefaultPrevented:U,isPropagationStopped:U,isImmediatePropagationStopped:U};
-var va=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},wa=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?wa:va,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?wa:va)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(){if(this.nodeName.toLowerCase()!==
-"form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length){a.liveFired=B;return la("submit",this,arguments)}});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13){a.liveFired=B;return la("submit",this,arguments)}})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};if(!c.support.changeBubbles){var V,
-xa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired=
-B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type===
-"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]===
-0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h<k;h++)c.event.add(this[h],d,l,e)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&!a.preventDefault)for(var d in a)this.unbind(d,
-a[d]);else{d=0;for(var e=this.length;d<e;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,e){return this.live(b,d,e,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){var d=c.Event(a);d.preventDefault();d.stopPropagation();c.event.trigger(d,b,this[0]);return d.result}},toggle:function(a){for(var b=arguments,d=
-1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(e){var f=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,f+1);e.preventDefault();return b[f].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var ya={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,e,f,h){var l,k=0,o,x,r=h||this.selector;h=h?this:c(this.context);if(typeof d===
-"object"&&!d.preventDefault){for(l in d)h[b](l,e,d[l],r);return this}if(c.isFunction(e)){f=e;e=B}for(d=(d||"").split(" ");(l=d[k++])!=null;){o=X.exec(l);x="";if(o){x=o[0];l=l.replace(X,"")}if(l==="hover")d.push("mouseenter"+x,"mouseleave"+x);else{o=l;if(l==="focus"||l==="blur"){d.push(ya[l]+x);l+=x}else l=(ya[l]||l)+x;if(b==="live"){x=0;for(var A=h.length;x<A;x++)c.event.add(h[x],"live."+Y(l,r),{data:e,selector:r,handler:f,origType:l,origHandler:f,preType:o})}else h.unbind("live."+Y(l,r),f)}}return this}});
-c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d,e){if(e==null){e=d;d=null}return arguments.length>0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
-(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1&&!q){y.sizcache=n;y.sizset=p}if(y.nodeName.toLowerCase()===i){F=y;break}y=y[g]}m[p]=F}}}function b(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1){if(!q){y.sizcache=n;y.sizset=p}if(typeof i!=="string"){if(y===i){F=true;break}}else if(k.filter(i,
-[y]).length>0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3];
-break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr,
-q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h=
-l;g.sort(w);if(h)for(var i=1;i<g.length;i++)g[i]===g[i-1]&&g.splice(i--,1)}return g};k.matches=function(g,i){return k(g,null,null,i)};k.matchesSelector=function(g,i){return k(i,null,null,[g]).length>0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p<q;p++){var u,y=o.order[p];if(u=o.leftMatch[y].exec(g)){var F=u[1];u.splice(1,1);if(F.substr(F.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");m=o.find[y](u,i,n);if(m!=null){g=g.replace(o.match[y],"");break}}}}m||(m=i.getElementsByTagName("*"));
-return{set:m,expr:g}};k.filter=function(g,i,n,m){for(var p,q,u=g,y=[],F=i,M=i&&i[0]&&k.isXML(i[0]);g&&i.length;){for(var N in o.filter)if((p=o.leftMatch[N].exec(g))!=null&&p[2]){var O,D,R=o.filter[N];D=p[1];q=false;p.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(F===y)y=[];if(o.preFilter[N])if(p=o.preFilter[N](p,F,n,y,m,M)){if(p===true)continue}else q=O=true;if(p)for(var j=0;(D=F[j])!=null;j++)if(D){O=R(D,p,j,F);var s=m^!!O;if(n&&O!=null)if(s)q=true;else F[j]=false;else if(s){y.push(D);q=true}}if(O!==
-B){n||(F=y);g=g.replace(o.match[N],"");if(!q)return[];break}}}if(g===u)if(q==null)k.error(g);else break;u=g}return F};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var o=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
-POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,i){var n=typeof i==="string",m=n&&!/\W/.test(i);n=n&&!m;if(m)i=i.toLowerCase();m=0;for(var p=g.length,q;m<p;m++)if(q=g[m]){for(;(q=q.previousSibling)&&q.nodeType!==1;);g[m]=n||q&&q.nodeName.toLowerCase()===
-i?q||false:q===i}n&&k.filter(i,g,true)},">":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p<q;p++){if(n=g[p]){n=n.parentNode;g[p]=n.nodeName.toLowerCase()===i?n:false}}else{for(;p<q;p++)if(n=g[p])g[p]=m?n.parentNode:n.parentNode===i;m&&k.filter(i,g,true)}},"":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=i=i.toLowerCase();q=a}q("parentNode",i,p,g,m,n)},"~":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=
-i=i.toLowerCase();q=a}q("previousSibling",i,p,g,m,n)}},find:{ID:function(g,i,n){if(typeof i.getElementById!=="undefined"&&!n)return(g=i.getElementById(g[1]))&&g.parentNode?[g]:[]},NAME:function(g,i){if(typeof i.getElementsByName!=="undefined"){for(var n=[],m=i.getElementsByName(g[1]),p=0,q=m.length;p<q;p++)m[p].getAttribute("name")===g[1]&&n.push(m[p]);return n.length===0?null:n}},TAG:function(g,i){return i.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,i,n,m,p,q){g=" "+g[1].replace(/\\/g,
-"")+" ";if(q)return g;q=0;for(var u;(u=i[q])!=null;q++)if(u)if(p^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n,
-m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===
-true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===
-g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return i<n[3]-0},gt:function(g,i,n){return i>n[3]-0},nth:function(g,i,n){return n[3]-
-0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n<m;n++)if(i[n]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+p)},CHILD:function(g,i){var n=i[1],m=g;switch(n){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(n===
-"first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":n=i[2];var p=i[3];if(n===1&&p===0)return true;var q=i[0],u=g.parentNode;if(u&&(u.sizcache!==q||!g.nodeIndex)){var y=0;for(m=u.firstChild;m;m=m.nextSibling)if(m.nodeType===1)m.nodeIndex=++y;u.sizcache=q}m=g.nodeIndex-p;return n===0?m===0:m%n===0&&m/n>=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===
-i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]];
-if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m,
-g);else if(typeof g.length==="number")for(var p=g.length;n<p;n++)m.push(g[n]);else for(;g[n];n++)m.push(g[n]);return m}}var w,I;if(t.documentElement.compareDocumentPosition)w=function(g,i){if(g===i){h=true;return 0}if(!g.compareDocumentPosition||!i.compareDocumentPosition)return g.compareDocumentPosition?-1:1;return g.compareDocumentPosition(i)&4?-1:1};else{w=function(g,i){var n,m,p=[],q=[];n=g.parentNode;m=i.parentNode;var u=n;if(g===i){h=true;return 0}else if(n===m)return I(g,i);else if(n){if(!m)return 1}else return-1;
-for(;u;){p.unshift(u);u=u.parentNode}for(u=m;u;){q.unshift(u);u=u.parentNode}n=p.length;m=q.length;for(u=0;u<n&&u<m;u++)if(p[u]!==q[u])return I(p[u],q[u]);return u===n?I(g,q[u],-1):I(p[u],i,1)};I=function(g,i,n){if(g===i)return n;for(g=g.nextSibling;g;){if(g===i)return-1;g=g.nextSibling}return 1}}k.getText=function(g){for(var i="",n,m=0;g[m];m++){n=g[m];if(n.nodeType===3||n.nodeType===4)i+=n.nodeValue;else if(n.nodeType!==8)i+=k.getText(n.childNodes)}return i};(function(){var g=t.createElement("div"),
-i="script"+(new Date).getTime(),n=t.documentElement;g.innerHTML="<a name='"+i+"'/>";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g);
-n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&&
-function(){var g=k,i=t.createElement("div");i.innerHTML="<p class='TEST'></p>";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F||
-p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g=
-t.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition?
-function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n<u;n++)k(g,q[n],m);return k.filter(p,m)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=k.getText;c.isXMLDoc=k.isXML;
-c.contains=k.contains})();var Za=/Until$/,$a=/^(?:parents|prevUntil|prevAll)/,ab=/,/,Na=/^.[^:#\[\.,]*$/,bb=Array.prototype.slice,cb=c.expr.match.POS;c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,e=0,f=this.length;e<f;e++){d=b.length;c.find(a,this[e],b);if(e>0)for(var h=d;h<b.length;h++)for(var l=0;l<d;l++)if(b[l]===b[h]){b.splice(h--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,e=b.length;d<e;d++)if(c.contains(this,b[d]))return true})},
-not:function(a){return this.pushStack(ma(this,a,false),"not",a)},filter:function(a){return this.pushStack(ma(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e<f;e++){l=a[e];k[l]||(k[l]=c.expr.match.POS.test(l)?c(l,b||this.context):l)}for(;h&&h.ownerDocument&&h!==b;){for(l in k){e=k[l];if(e.jquery?e.index(h)>-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h=
-h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e<f;e++)for(h=this[e];h;)if(l?l.index(h)>-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context):
-c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,
-2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,
-b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&
-e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/<tbody/i,eb=/<|&#?\w+;/,Ca=/<(?:script|object|embed|option|style)/i,Da=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/\=([^="'>\s]+\/)>/g,P={option:[1,
-"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
-c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
-wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
-prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
-this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
-return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null;
-else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1></$2>");try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(e){this.empty().append(a)}}else c.isFunction(a)?this.each(function(f){var h=c(this);h.html(a.call(this,f,h.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=
-c(this),e=d.html();d.replaceWith(a.call(this,b,e))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){var e,f,h,l=a[0],k=[];if(!c.support.checkClone&&arguments.length===3&&typeof l==="string"&&Da.test(l))return this.each(function(){c(this).domManip(a,
-b,d,true)});if(c.isFunction(l))return this.each(function(x){var r=c(this);a[0]=l.call(this,x,b?r.html():B);r.domManip(a,b,d)});if(this[0]){e=l&&l.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:c.buildFragment(a,this,k);h=e.fragment;if(f=h.childNodes.length===1?h=h.firstChild:h.firstChild){b=b&&c.nodeName(f,"tr");f=0;for(var o=this.length;f<o;f++)d.call(b?c.nodeName(this[f],"table")?this[f].getElementsByTagName("tbody")[0]||this[f].appendChild(this[f].ownerDocument.createElement("tbody")):
-this[f]:this[f],f>0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",
-prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f<h;f++){var l=(f>0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument||
-b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1></$2>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]==="<table>"&&!x?r.childNodes:[];for(o=k.length-
-1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));
-d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i,
-jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,
-zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),
-h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b);
-if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=
-d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left;
-e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
-ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b===
-"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("<div>").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&
-!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})},
-getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html",
-script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data||
-!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache=
-false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset;
-A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type",
-b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&&
-c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d||
-c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]=
-encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess",
-[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),
-e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}});
-if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show",
-3),a,b,d);else{d=0;for(var e=this.length;d<e;d++){a=this[d];b=a.style.display;if(!c.data(a,"olddisplay")&&b==="none")b=a.style.display="";b===""&&c.css(a,"display")==="none"&&c.data(a,"olddisplay",qa(a.nodeName))}for(d=0;d<e;d++){a=this[d];b=a.style.display;if(b===""||b==="none")a.style.display=c.data(a,"olddisplay")||""}return this}},hide:function(a,b,d){if(a||a===0)return this.animate(S("hide",3),a,b,d);else{a=0;for(b=this.length;a<b;a++){d=c.css(this[a],"display");d!=="none"&&c.data(this[a],"olddisplay",
-d)}for(a=0;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b,d){var e=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||e?this.each(function(){var f=e?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(S("toggle",3),a,b,d);return this},fadeTo:function(a,b,d,e){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d,e)},animate:function(a,b,d,e){var f=c.speed(b,
-d,e);if(c.isEmptyObject(a))return this.each(f.complete);return this[f.queue===false?"each":"queue"](function(){var h=c.extend({},f),l,k=this.nodeType===1,o=k&&c(this).is(":hidden"),x=this;for(l in a){var r=c.camelCase(l);if(l!==r){a[r]=a[l];delete a[l];l=r}if(a[l]==="hide"&&o||a[l]==="show"&&!o)return h.complete.call(this);if(k&&(l==="height"||l==="width")){h.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(c.css(this,"display")==="inline"&&c.css(this,"float")==="none")if(c.support.inlineBlockNeedsLayout)if(qa(this.nodeName)===
-"inline")this.style.display="inline-block";else{this.style.display="inline";this.style.zoom=1}else this.style.display="inline-block"}if(c.isArray(a[l])){(h.specialEasing=h.specialEasing||{})[l]=a[l][1];a[l]=a[l][0]}}if(h.overflow!=null)this.style.overflow="hidden";h.curAnim=c.extend({},a);c.each(a,function(A,C){var J=new c.fx(x,h,A);if(vb.test(C))J[C==="toggle"?o?"show":"hide":C](a);else{var w=wb.exec(C),I=J.cur()||0;if(w){var L=parseFloat(w[2]),g=w[3]||"px";if(g!=="px"){c.style(x,A,(L||1)+g);I=(L||
-1)/J.cur()*I;c.style(x,A,I+g)}if(w[1])L=(w[1]==="-="?-1:1)*L+I;J.custom(I,L,g)}else J.custom(I,C,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var e=d.length-1;e>=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b,
-d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a*
-Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)}
-var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;
-this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide||
-this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=
-c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},interval:13,stop:function(){clearInterval(ba);ba=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===
-b.elem}).length};var xb=/^t(?:able|d|h)$/i,Ia=/^(?:body|html)$/i;c.fn.offset="getBoundingClientRect"in t.documentElement?function(a){var b=this[0],d;if(a)return this.each(function(l){c.offset.setOffset(this,a,l)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);try{d=b.getBoundingClientRect()}catch(e){}var f=b.ownerDocument,h=f.documentElement;if(!d||!c.contains(h,b))return d||{top:0,left:0};b=f.body;f=fa(f);return{top:d.top+(f.pageYOffset||c.support.boxModel&&
-h.scrollTop||b.scrollTop)-(h.clientTop||b.clientTop||0),left:d.left+(f.pageXOffset||c.support.boxModel&&h.scrollLeft||b.scrollLeft)-(h.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(x){c.offset.setOffset(this,a,x)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d,e=b.offsetParent,f=b.ownerDocument,h=f.documentElement,l=f.body;d=(f=f.defaultView)?f.getComputedStyle(b,null):b.currentStyle;
-for(var k=b.offsetTop,o=b.offsetLeft;(b=b.parentNode)&&b!==l&&b!==h;){if(c.offset.supportsFixedPosition&&d.position==="fixed")break;d=f?f.getComputedStyle(b,null):b.currentStyle;k-=b.scrollTop;o-=b.scrollLeft;if(b===e){k+=b.offsetTop;o+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&xb.test(b.nodeName))){k+=parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}e=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"){k+=
-parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}d=d}if(d.position==="relative"||d.position==="static"){k+=l.offsetTop;o+=l.offsetLeft}if(c.offset.supportsFixedPosition&&d.position==="fixed"){k+=Math.max(h.scrollTop,l.scrollTop);o+=Math.max(h.scrollLeft,l.scrollLeft)}return{top:k,left:o}};c.offset={initialize:function(){var a=t.body,b=t.createElement("div"),d,e,f,h=parseFloat(c.css(a,"marginTop"))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",
-height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);d=b.firstChild;e=d.firstChild;f=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=e.offsetTop!==5;this.doesAddBorderForTableAndCells=
-f.offsetTop===5;e.style.position="fixed";e.style.top="20px";this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15;e.style.position=e.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==h;a.removeChild(b);c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.css(a,
-"marginTop"))||0;d+=parseFloat(c.css(a,"marginLeft"))||0}return{top:b,left:d}},setOffset:function(a,b,d){var e=c.css(a,"position");if(e==="static")a.style.position="relative";var f=c(a),h=f.offset(),l=c.css(a,"top"),k=c.css(a,"left"),o=e==="absolute"&&c.inArray("auto",[l,k])>-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a,
-e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&&
-c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();
-c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+
-b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window);
=== removed file 'tracking/middleware.py'
--- tracking/middleware.py 2016-12-13 18:28:51 +0000
+++ tracking/middleware.py 1970-01-01 00:00:00 +0000
@@ -1,195 +0,0 @@
-from datetime import datetime, timedelta
-import logging
-import re
-import traceback
-
-from django.conf import settings
-from django.contrib.auth.models import AnonymousUser
-from django.core.cache import cache
-from django.core.urlresolvers import reverse, NoReverseMatch
-from django.db.utils import DatabaseError
-from django.http import Http404
-
-from tracking import utils
-from tracking.models import Visitor, UntrackedUserAgent, BannedIP
-
-title_re = re.compile('<title>(.*?)</title>')
-log = logging.getLogger('tracking.middleware')
-
-
-class VisitorTrackingMiddleware(object):
- """Keeps track of your active users. Anytime a visitor accesses a valid
- URL, their unique record will be updated with the page they're on and the
- last time they requested a page.
-
- Records are considered to be unique when the session key and IP
- address are unique together. Sometimes the same user used to have
- two different records, so I added a check to see if the session key
- had changed for the same IP and user agent in the last 5 minutes
-
- """
-
- @property
- def prefixes(self):
- """Returns a list of URL prefixes that we should not track."""
-
- if not hasattr(self, '_prefixes'):
- self._prefixes = getattr(settings, 'NO_TRACKING_PREFIXES', [])
-
- if not getattr(settings, '_FREEZE_TRACKING_PREFIXES', False):
- for name in ('MEDIA_URL', 'STATIC_URL'):
- url = getattr(settings, name)
- if url and url != '/':
- self._prefixes.append(url)
-
- try:
- # finally, don't track requests to the tracker update pages
- self._prefixes.append(
- reverse('tracking-refresh-active-users'))
- except NoReverseMatch:
- # django-tracking hasn't been included in the URLconf if we
- # get here, which is not a bad thing
- pass
-
- settings.NO_TRACKING_PREFIXES = self._prefixes
- settings._FREEZE_TRACKING_PREFIXES = True
-
- return self._prefixes
-
- def process_request(self, request):
- # don't process AJAX requests
- if request.is_ajax():
- return
-
- # create some useful variables
- ip_address = utils.get_ip(request)
- user_agent = unicode(request.META.get(
- 'HTTP_USER_AGENT', '')[:255], errors='ignore')
-
- # retrieve untracked user agents from cache
- ua_key = '_tracking_untracked_uas'
- untracked = cache.get(ua_key)
- if untracked is None:
- log.info('Updating untracked user agent cache')
- untracked = UntrackedUserAgent.objects.all()
- cache.set(ua_key, untracked, 3600)
-
- # see if the user agent is not supposed to be tracked
- for ua in untracked:
- # if the keyword is found in the user agent, stop tracking
- if user_agent.find(ua.keyword) != -1:
- log.debug('Not tracking UA "%s" because of keyword: %s' %
- (user_agent, ua.keyword))
- return
-
- if hasattr(request, 'session') and request.session.session_key:
- # use the current session key if we can
- session_key = request.session.session_key
- else:
- # otherwise just fake a session key
- session_key = '%s:%s' % (ip_address, user_agent)
- session_key = session_key[:40]
-
- # ensure that the request.path does not begin with any of the prefixes
- for prefix in self.prefixes:
- if request.path.startswith(prefix):
- log.debug('Not tracking request to: %s' % request.path)
- return
-
- # if we get here, the URL needs to be tracked
- # determine what time it is
- now = datetime.now()
-
- attrs = {
- 'session_key': session_key,
- 'ip_address': ip_address
- }
-
- # for some reason, Visitor.objects.get_or_create was not working here
- try:
- visitor = Visitor.objects.get(**attrs)
- except Visitor.DoesNotExist:
- # see if there's a visitor with the same IP and user agent
- # within the last 5 minutes
- cutoff = now - timedelta(minutes=5)
- visitors = Visitor.objects.filter(
- ip_address=ip_address,
- user_agent=user_agent,
- last_update__gte=cutoff
- )
-
- if len(visitors):
- visitor = visitors[0]
- visitor.session_key = session_key
- log.debug('Using existing visitor for IP %s / UA %s: %s' %
- (ip_address, user_agent, visitor.id))
- else:
- # it's probably safe to assume that the visitor is brand new
- visitor = Visitor(**attrs)
- log.debug('Created a new visitor: %s' % attrs)
- except:
- return
-
- # determine whether or not the user is logged in
- user = request.user
- if isinstance(user, AnonymousUser):
- user = None
-
- # update the tracking information
- visitor.user = user
- visitor.user_agent = user_agent
-
- # if the visitor record is new, or the visitor hasn't been here for
- # at least an hour, update their referrer URL
- one_hour_ago = now - timedelta(hours=1)
- if not visitor.last_update or visitor.last_update <= one_hour_ago:
- visitor.referrer = utils.u_clean(
- request.META.get('HTTP_REFERER', 'unknown')[:255])
-
- # reset the number of pages they've been to
- visitor.page_views = 0
- visitor.session_start = now
-
- visitor.url = request.path
- visitor.page_views += 1
- visitor.last_update = now
- try:
- visitor.save()
- except DatabaseError:
- log.error('There was a problem saving visitor information:\n%s\n\n%s' % (
- traceback.format_exc(), locals()))
-
-
-class VisitorCleanUpMiddleware:
- """Clean up old visitor tracking records in the database."""
-
- def process_request(self, request):
- timeout = utils.get_cleanup_timeout()
-
- if str(timeout).isdigit():
- log.debug('Cleaning up visitors older than %s hours' % timeout)
- timeout = datetime.now() - timedelta(hours=int(timeout))
- Visitor.objects.filter(last_update__lte=timeout).delete()
-
-
-class BannedIPMiddleware:
- """Raises an Http404 error for any page request from a banned IP. IP
- addresses may be added to the list of banned IPs via the Django admin.
-
- The banned users do not actually receive the 404 error--instead they get
- an "Internal Server Error", effectively eliminating any access to the site.
-
- """
-
- def process_request(self, request):
- key = '_tracking_banned_ips'
- ips = cache.get(key)
- if ips is None:
- # compile a list of all banned IP addresses
- log.info('Updating banned IPs cache')
- ips = [b.ip_address for b in BannedIP.objects.all()]
- cache.set(key, ips, 3600)
-
- # check to see if the current user's IP address is in that list
- if utils.get_ip(request) in ips:
- raise Http404
=== removed directory 'tracking/migrations'
=== removed file 'tracking/migrations/0001_initial.py'
--- tracking/migrations/0001_initial.py 2016-12-13 18:28:51 +0000
+++ tracking/migrations/0001_initial.py 1970-01-01 00:00:00 +0000
@@ -1,65 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import models, migrations
-from django.conf import settings
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ]
-
- operations = [
- migrations.CreateModel(
- name='BannedIP',
- fields=[
- ('id', models.AutoField(verbose_name='ID',
- serialize=False, auto_created=True, primary_key=True)),
- ('ip_address', models.GenericIPAddressField(
- help_text='The IP address that should be banned', verbose_name=b'IP Address')),
- ],
- options={
- 'ordering': ('ip_address',),
- 'verbose_name': 'Banned IP',
- 'verbose_name_plural': 'Banned IPs',
- },
- ),
- migrations.CreateModel(
- name='UntrackedUserAgent',
- fields=[
- ('id', models.AutoField(verbose_name='ID',
- serialize=False, auto_created=True, primary_key=True)),
- ('keyword', models.CharField(help_text='Part or all of a user-agent string. For example, "Googlebot" here will be found in "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" and that visitor will not be tracked.', max_length=100, verbose_name='keyword')),
- ],
- options={
- 'ordering': ('keyword',),
- 'verbose_name': 'Untracked User-Agent',
- 'verbose_name_plural': 'Untracked User-Agents',
- },
- ),
- migrations.CreateModel(
- name='Visitor',
- fields=[
- ('id', models.AutoField(verbose_name='ID',
- serialize=False, auto_created=True, primary_key=True)),
- ('session_key', models.CharField(max_length=40)),
- ('ip_address', models.CharField(max_length=20)),
- ('user_agent', models.CharField(max_length=255)),
- ('referrer', models.CharField(max_length=255)),
- ('url', models.CharField(max_length=255)),
- ('page_views', models.PositiveIntegerField(default=0)),
- ('session_start', models.DateTimeField()),
- ('last_update', models.DateTimeField()),
- ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, null=True)),
- ],
- options={
- 'ordering': ('-last_update',),
- },
- ),
- migrations.AlterUniqueTogether(
- name='visitor',
- unique_together=set([('session_key', 'ip_address')]),
- ),
- ]
=== removed file 'tracking/migrations/__init__.py'
=== removed file 'tracking/models.py'
--- tracking/models.py 2016-12-13 18:28:51 +0000
+++ tracking/models.py 1970-01-01 00:00:00 +0000
@@ -1,133 +0,0 @@
-from datetime import datetime, timedelta
-import logging
-import traceback
-
-from django.contrib.gis.geoip import HAS_GEOIP
-
-if HAS_GEOIP:
- from django.contrib.gis.geoip import GeoIP, GeoIPException
-
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.db import models
-from django.utils.translation import ugettext, ugettext_lazy as _
-from tracking import utils
-
-USE_GEOIP = getattr(settings, 'TRACKING_USE_GEOIP', False)
-CACHE_TYPE = getattr(settings, 'GEOIP_CACHE_TYPE', 4)
-
-log = logging.getLogger('tracking.models')
-
-
-class VisitorManager(models.Manager):
-
- def active(self, timeout=None):
- """Retrieves only visitors who have been active within the timeout
- period."""
- if not timeout:
- timeout = utils.get_timeout()
-
- now = datetime.now()
- cutoff = now - timedelta(minutes=timeout)
-
- return self.get_queryset().filter(last_update__gte=cutoff)
-
-
-class Visitor(models.Model):
- session_key = models.CharField(max_length=40)
- ip_address = models.CharField(max_length=20)
- user = models.ForeignKey(User, null=True)
- user_agent = models.CharField(max_length=255)
- referrer = models.CharField(max_length=255)
- url = models.CharField(max_length=255)
- page_views = models.PositiveIntegerField(default=0)
- session_start = models.DateTimeField()
- last_update = models.DateTimeField()
-
- objects = VisitorManager()
-
- def _time_on_site(self):
- """Attempts to determine the amount of time a visitor has spent on the
- site based upon their information that's in the database."""
- if self.session_start:
- seconds = (self.last_update - self.session_start).seconds
-
- hours = seconds / 3600
- seconds -= hours * 3600
- minutes = seconds / 60
- seconds -= minutes * 60
-
- return u'%i:%02i:%02i' % (hours, minutes, seconds)
- else:
- return ugettext(u'unknown')
- time_on_site = property(_time_on_site)
-
- def _get_geoip_data(self):
- """Attempts to retrieve MaxMind GeoIP data based upon the visitor's
- IP."""
-
- if not HAS_GEOIP or not USE_GEOIP:
- # go no further when we don't need to
- log.debug('Bailing out. HAS_GEOIP: %s; TRACKING_USE_GEOIP: %s' % (
- HAS_GEOIP, USE_GEOIP))
- return None
-
- if not hasattr(self, '_geoip_data'):
- self._geoip_data = None
- try:
- gip = GeoIP(cache=CACHE_TYPE)
- self._geoip_data = gip.city(self.ip_address)
- except GeoIPException:
- # don't even bother...
- log.error('Error getting GeoIP data for IP "%s": %s' %
- (self.ip_address, traceback.format_exc()))
-
- return self._geoip_data
-
- geoip_data = property(_get_geoip_data)
-
- def _get_geoip_data_json(self):
- """Cleans out any dirty unicode characters to make the geoip data safe
- for JSON encoding."""
- clean = {}
- if not self.geoip_data:
- return {}
-
- for key, value in self.geoip_data.items():
- clean[key] = utils.u_clean(value)
- return clean
-
- geoip_data_json = property(_get_geoip_data_json)
-
- class Meta:
- ordering = ('-last_update',)
- unique_together = ('session_key', 'ip_address',)
- app_label = 'tracking'
-
-
-class UntrackedUserAgent(models.Model):
- keyword = models.CharField(_('keyword'), max_length=100, help_text=_(
- 'Part or all of a user-agent string. For example, "Googlebot" here will be found in "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" and that visitor will not be tracked.'))
-
- def __unicode__(self):
- return self.keyword
-
- class Meta:
- ordering = ('keyword',)
- verbose_name = _('Untracked User-Agent')
- verbose_name_plural = _('Untracked User-Agents')
- app_label = 'tracking'
-
-
-class BannedIP(models.Model):
- ip_address = models.GenericIPAddressField(
- 'IP Address', help_text=_('The IP address that should be banned'))
-
- def __unicode__(self):
- return self.ip_address
-
- class Meta:
- ordering = ('ip_address',)
- verbose_name = _('Banned IP')
- verbose_name_plural = _('Banned IPs')
- app_label = 'tracking'
=== removed directory 'tracking/templates'
=== removed file 'tracking/templates/base.html'
--- tracking/templates/base.html 2016-06-05 20:58:46 +0000
+++ tracking/templates/base.html 1970-01-01 00:00:00 +0000
@@ -1,21 +0,0 @@
-<html>
-<head>
-<title>My Site - {% block title %}Welcome{% endblock %}</title>
-<script type="text/javascript" src="{{ MEDIA_URL }}js/jquery-1.4.4.min.js"></script>
-<style type="text/css">
-#active-users-map {
- height: 400px;
- width: 600px;
-}
-</style>
-{% block extra-head %}{% endblock %}
-</head>
-<body>
-<h1><a href="/">My Site</a></h1>
-{% block content %}{% endblock %}
-
-{% block footer %}
-<p>Copyright © {% now "Y" %} Me. All rights reserved.</p>
-{% endblock %}
-</body>
-</html>
=== removed directory 'tracking/templates/tracking'
=== removed file 'tracking/templates/tracking/_active_users.html'
--- tracking/templates/tracking/_active_users.html 2016-06-05 20:58:46 +0000
+++ tracking/templates/tracking/_active_users.html 1970-01-01 00:00:00 +0000
@@ -1,21 +0,0 @@
-{% load i18n %}<div class="total">
- {% blocktrans count active.count as counter %}1 active user{% plural %}{{ counter }} active users{% endblocktrans %}
-</div>
-<div class="guests">
- {% blocktrans count guests.count as counter %}1 guest user{% plural %}{{ counter }} guest users{% endblocktrans %}
- {% if user.is_superuser %}
- {% for visitor in guests %}
- {% if forloop.first %}<ul class="active-guests">{% endif %}
- <li>{{ visitor.ip_address }}</li>
- {% if forloop.last %}</ul>{% endif %}
- {% endfor %}
- {% endif %}
-</div>
-<div class="registered">
- {% blocktrans count registered.count as counter %}1 registered user{% plural %}{{ counter }} registered users{% endblocktrans %}
- {% for visitor in registered %}
- {% if forloop.first %}<ul class="active-users">{% endif %}
- <li>{{ visitor.user }}</li>
- {% if forloop.last %}</ul>{% endif %}
- {% endfor %}
-</div>
\ No newline at end of file
=== removed file 'tracking/templates/tracking/_active_users.js'
--- tracking/templates/tracking/_active_users.js 2016-06-06 18:26:47 +0000
+++ tracking/templates/tracking/_active_users.js 1970-01-01 00:00:00 +0000
@@ -1,155 +0,0 @@
-//<![CDATA[
-var AUmap;
-var markerList = new Array();
-var blurbs = new Array();
-
-$(document).ready(function () {
- if (GBrowserIsCompatible()) {
- // set up Google Maps
- origin = new GLatLng(35.232253,-95.273437);
- AUmap = new GMap2(document.getElementById("active-users-map"));
- AUmap.setCenter(origin, 2);
- AUmap.addControl(new GSmallMapControl());
- AUmap.addControl(new GMapTypeControl());
- AUmap.enableScrollWheelZoom();
-
- // now fetch the active users
- createMarkers();
-
- // refresh the markers every 5 seconds or so
- setInterval('createMarkers()', 5000);
-
- $('.active-user h3').live('click', function () {
- var p = $(this).parent().get(0);
- var num = parseInt(p.id.replace('au-', ''));
- var marker = markerList[num];
-
- AUmap.openInfoWindowHtml(marker.getLatLng(), blurbs[num]);
- AUmap.panTo(marker.getLatLng());
- });
- } else {
- alert('This page requires a modern browser which supports Google Maps!');
- }
-});
-$(document).unload(GUnload);
-
-$('html').ajaxSend(function(event, xhr, settings) {
- function getCookie(name) {
- var cookieValue = null;
- if (document.cookie && document.cookie != '') {
- var cookies = document.cookie.split(';');
- for (var i = 0; i < cookies.length; i++) {
- var cookie = jQuery.trim(cookies[i]);
- // Does this cookie string begin with the name we want?
- if (cookie.substring(0, name.length + 1) == (name + '=')) {
- cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
- break;
- }
- }
- }
- return cookieValue;
- }
- if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
- // Only send the token to relative URLs i.e. locally.
- xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
- }
-});
-
-function createMarkers() {
- // Pull back all active users and create markers for them
- $.ajax({
- url : "{% url 'tracking-get-active-users' %}",
- type : 'POST',
- dataType : 'json',
- data: {},
- success : function (json) {
- var userList = $('#active-users-list');
-
- $.each(json.users, function (i, user) {
- var url = '<a href="' + user.url + '">' + user.url + '</a>';
- if (!markerList[user.id] && user.geoip && user.geoip.city && user.geoip.city != 'None') {
- // determine which flag to use
- var img = '<img src="/static/images/flags/' +
- user.geoip.country_code.toLowerCase() +
- '.png" class="flag" />';
-
- var ref = '';
- if (user.referrer != 'unknown') {
- ref = '<div><strong>From</strong> <a href="' + user.referrer +
- '">' + user.referrer + '</a></div>';
- }
-
- // come up with some HTML to put in the list
- var listHtml = '<div id="au-' + user.id + '" ' +
- 'class="active-user location-info"><h3>' +
- user.geoip.city + '</h3><div>' + img +
- user.geoip.region + ', ' +
- user.geoip.country_name + '</div>' +
- '<div><strong>Viewing</strong> <span id="auu-' + user.id +'">' +
- url + '</span></div>' +
- '<div><strong>Using</strong> ' + user.user_agent + '</div>' + ref +
- '<div><strong>Has viewed</strong> <span id="pv-' + user.id +
- '">' + user.page_views + '</span> page(s)</div>' +
- '<div><strong>Updated</strong> <span id="lu-' + user.id + '">' +
- user.friendly_time + '</span> ago</div></div>';
- userList.prepend(listHtml);
-
- // add a marker to the map
- var point = new GLatLng(user.geoip.latitude,
- user.geoip.longitude);
-
- AUmap.addOverlay(createMarker(point, user, img));
- } else {
- $('#auu-' + user.id).html(url);
- $('#lu-' + user.id).html(user.friendly_time);
- $('#pv-' + user.id).html(user.page_views);
-
- // Send recently-active users to the top of the list
- if (user.last_update <= 10) {
- $('#au-' + user.id).prependTo(userList);
- }
- }
- });
-
- // Clean up old markers
- $.each(markerList, function (i, marker) {
- if (marker) {
- var inList = false;
- $.each(json.users, function (j, juser) {
- if (i == juser.id) {
- inList = true;
- return;
- }
- });
- if (!inList) {
- AUmap.removeOverlay(marker);
- $('#au-' + i).remove();
- markerList[i] = null;
- }
- }
- });
- }
- });
-}
-
-function createMarker(point, user, img) {
- // Add a marker overlay to the map and store various bits of info about it
- var marker = new GMarker(point);
- marker.value = user.id;
-
- var myHtml = '<div class="mapOverlay"><h3>' + user.geoip.city + '</h3>';
- myHtml += '<div>' + img + user.geoip.region;
- myHtml += ', ' + user.geoip.country_name + '</div></div>';
-
- // Add a listener to pop up an info box when the mouse goes over a marker
- GEvent.addListener(marker, "mouseover", function() {
- AUmap.openInfoWindowHtml(point, myHtml);
- });
-
- // Keep track of the marker's data
- markerList[user.id] = marker;
- blurbs[user.id] = myHtml;
-
- return marker;
-}
-//]]>
=== removed file 'tracking/templates/tracking/refresh_active_users.js'
--- tracking/templates/tracking/refresh_active_users.js 2016-06-05 20:58:46 +0000
+++ tracking/templates/tracking/refresh_active_users.js 1970-01-01 00:00:00 +0000
@@ -1,26 +0,0 @@
-var trackingUsers;
-var refreshTimeout = 5000;
-
-$(document).ready(function () {
- trackingUsers = $('#tracking-active-users');
- if (trackingUsers) {
- refreshActiveUsers();
- }
-});
-
-function refreshActiveUsers() {
- $.ajax({
- 'url': updateActiveURL,
- 'type': 'GET',
- 'data': {},
- 'dataType': 'json',
- 'error': function (xhr, status, msg) {
- // do nothing
- },
- 'success': function (json) {
- trackingUsers.html(json.users)
- }
- });
-
- setTimeout(refreshActiveUsers, refreshTimeout);
-}
\ No newline at end of file
=== removed file 'tracking/templates/tracking/visitor_map.html'
--- tracking/templates/tracking/visitor_map.html 2016-06-05 20:58:46 +0000
+++ tracking/templates/tracking/visitor_map.html 1970-01-01 00:00:00 +0000
@@ -1,24 +0,0 @@
-{% extends template %}
-{% load i18n %}
-
-{% block title %}{% trans "Active Visitors Map" %}{% endblock %}
-
-{% block extra-head %}
-{{ block.super }}
-<script src="http://maps.google.com/maps?file=api&v=2&key={{ GOOGLE_MAPS_KEY }}" type="text/javascript"></script>
-<script type="text/javascript">
-{% include 'tracking/_active_users.js' %}
-</script>
-{% endblock %}
-
-{% block content %}
-<h2>{% trans "Active Users" %}</h2>
-
-<p>
- {% trans "Below is a map and a list of recently active users on this site. It updates itself every 5 seconds or so, adding and removing pins in the map as necessary. Stick around for a few minutes to see it in action!" %}
-</p>
-
-<div id="active-users-map"></div>
-
-<div id="active-users-list"></div>
-{% endblock %}
=== removed directory 'tracking/templatetags'
=== removed file 'tracking/templatetags/__init__.py'
=== removed file 'tracking/templatetags/tracking_tags.py'
--- tracking/templatetags/tracking_tags.py 2016-12-13 18:28:51 +0000
+++ tracking/templatetags/tracking_tags.py 1970-01-01 00:00:00 +0000
@@ -1,53 +0,0 @@
-from django import template
-from tracking.models import Visitor
-
-register = template.Library()
-
-
-class VisitorsOnSite(template.Node):
- """Injects the number of active users on your site as an integer into the
- context."""
-
- def __init__(self, varname, same_page=False):
- self.varname = varname
- self.same_page = same_page
-
- def render(self, context):
- if self.same_page:
- try:
- request = context['request']
- count = Visitor.objects.active().filter(url=request.path).count()
- except KeyError:
- raise template.TemplateSyntaxError(
- "Please add 'django.core.context_processors.request' to your TEMPLATE_CONTEXT_PROCESSORS if you want to see how many users are on the same page.")
- else:
- count = Visitor.objects.active().count()
-
- context[self.varname] = count
- return ''
-
-
-def visitors_on_site(parser, token):
- """Determines the number of active users on your site and puts it into the
- context."""
- try:
- tag, a, varname = token.split_contents()
- except ValueError:
- raise template.TemplateSyntaxError(
- 'visitors_on_site usage: {% visitors_on_site as visitors %}')
-
- return VisitorsOnSite(varname)
-register.tag(visitors_on_site)
-
-
-def visitors_on_page(parser, token):
- """Determines the number of active users on the same page and puts it into
- the context."""
- try:
- tag, a, varname = token.split_contents()
- except ValueError:
- raise template.TemplateSyntaxError(
- 'visitors_on_page usage: {% visitors_on_page as visitors %}')
-
- return VisitorsOnSite(varname, same_page=True)
-register.tag(visitors_on_page)
=== removed file 'tracking/urls.py'
--- tracking/urls.py 2016-12-13 18:28:51 +0000
+++ tracking/urls.py 1970-01-01 00:00:00 +0000
@@ -1,16 +0,0 @@
-from django.conf.urls.defaults import *
-from django.conf import settings
-from tracking import views
-
-urlpatterns = patterns('',
- url(r'^refresh/$', views.update_active_users,
- name='tracking-refresh-active-users'),
- url(r'^refresh/json/$', views.get_active_users,
- name='tracking-get-active-users'),
- )
-
-if getattr(settings, 'TRACKING_USE_GEOIP', False):
- urlpatterns += patterns('',
- url(r'^map/$', views.display_map,
- name='tracking-visitor-map'),
- )
=== removed file 'tracking/utils.py'
--- tracking/utils.py 2016-12-13 18:28:51 +0000
+++ tracking/utils.py 1970-01-01 00:00:00 +0000
@@ -1,74 +0,0 @@
-from django.conf import settings
-import re
-import unicodedata
-
-# this is not intended to be an all-knowing IP address regex
-IP_RE = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
-
-
-def get_ip(request):
- """Retrieves the remote IP address from the request data.
-
- If the user is
- behind a proxy, they may have a comma-separated list of IP addresses, so
- we need to account for that. In such a case, only the first IP in the
- list will be retrieved. Also, some hosts that use a proxy will put the
- REMOTE_ADDR into HTTP_X_FORWARDED_FOR. This will handle pulling back the
- IP from the proper place.
-
- """
-
- # if neither header contain a value, just use local loopback
- ip_address = request.META.get('HTTP_X_FORWARDED_FOR',
- request.META.get('REMOTE_ADDR', '127.0.0.1'))
- if ip_address:
- # make sure we have one and only one IP
- try:
- ip_address = IP_RE.match(ip_address)
- if ip_address:
- ip_address = ip_address.group(0)
- else:
- # no IP, probably from some dirty proxy or other device
- # throw in some bogus IP
- ip_address = '10.0.0.1'
- except IndexError:
- pass
-
- return ip_address
-
-
-def get_timeout():
- """Gets any specified timeout from the settings file, or use 10 minutes by
- default."""
- return getattr(settings, 'TRACKING_TIMEOUT', 10)
-
-
-def get_cleanup_timeout():
- """
- Gets any specified visitor clean-up timeout from the settings file, or
- use 24 hours by default
- """
- return getattr(settings, 'TRACKING_CLEANUP_TIMEOUT', 24)
-
-
-def u_clean(s):
- """A strange attempt at cleaning up unicode."""
-
- uni = ''
- try:
- # try this first
- uni = str(s).decode('iso-8859-1')
- except UnicodeDecodeError:
- try:
- # try utf-8 next
- uni = str(s).decode('utf-8')
- except UnicodeDecodeError:
- # last resort method... one character at a time (ugh)
- if s and type(s) in (str, unicode):
- for c in s:
- try:
- uni += unicodedata.normalize('NFKC', unicode(c))
- except UnicodeDecodeError:
- uni += '-'
-
- return uni.encode('ascii', 'xmlcharrefreplace')
=== removed file 'tracking/views.py'
--- tracking/views.py 2016-12-13 18:28:51 +0000
+++ tracking/views.py 1970-01-01 00:00:00 +0000
@@ -1,115 +0,0 @@
-from datetime import datetime
-import logging
-import traceback
-
-from django.conf import settings
-from django.http import Http404, HttpResponse
-from django.shortcuts import render_to_response
-from django.template import RequestContext, Context, loader
-from django.utils.simplejson import JSONEncoder
-from django.utils.translation import ungettext
-from django.views.decorators.cache import never_cache
-from tracking.models import Visitor
-from tracking.utils import u_clean as uc
-
-DEFAULT_TRACKING_TEMPLATE = getattr(settings, 'DEFAULT_TRACKING_TEMPLATE',
- 'tracking/visitor_map.html')
-log = logging.getLogger('tracking.views')
-
-
-def update_active_users(request):
- """Returns a list of all active users."""
- if request.is_ajax():
- active = Visitor.objects.active()
- user = getattr(request, 'user', None)
-
- info = {
- 'active': active,
- 'registered': active.filter(user__isnull=False),
- 'guests': active.filter(user__isnull=True),
- 'user': user
- }
-
- # render the list of active users
- t = loader.get_template('tracking/_active_users.html')
- c = Context(info)
- users = {'users': t.render(c)}
-
- return HttpResponse(content=JSONEncoder().encode(users))
-
- # if the request was not made via AJAX, raise a 404
- raise Http404
-
-
-@never_cache
-def get_active_users(request):
- """Retrieves a list of active users which is returned as plain JSON for
- easier manipulation with JavaScript."""
- if request.is_ajax():
- active = Visitor.objects.active().reverse()
- now = datetime.now()
-
- # we don't put the session key or IP address here for security reasons
- try:
- data = {'users': [{
- 'id': v.id,
- #'user': uc(v.user),
- 'user_agent': uc(v.user_agent),
- 'referrer': uc(v.referrer),
- 'url': uc(v.url),
- 'page_views': v.page_views,
- 'geoip': v.geoip_data_json,
- 'last_update': (now - v.last_update).seconds,
- 'friendly_time': ', '.join(friendly_time((now - v.last_update).seconds)),
- } for v in active]}
- except:
- log.error('There was a problem putting all of the visitor data together:\n%s\n\n%s' % (
- traceback.format_exc(), locals()))
- return HttpResponse(content='{}', mimetype='text/javascript')
-
- response = HttpResponse(content=JSONEncoder().encode(data),
- mimetype='text/javascript')
- response['Content-Length'] = len(response.content)
-
- return response
-
- # if the request was not made via AJAX, raise a 404
- raise Http404
-
-
-def friendly_time(last_update):
- minutes = last_update / 60
- seconds = last_update % 60
-
- friendly_time = []
- if minutes > 0:
- friendly_time.append(ungettext(
- '%(minutes)i minute',
- '%(minutes)i minutes',
- minutes
- ) % {'minutes': minutes})
- if seconds > 0:
- friendly_time.append(ungettext(
- '%(seconds)i second',
- '%(seconds)i seconds',
- seconds
- ) % {'seconds': seconds})
-
- return friendly_time or 0
-
-
-def display_map(request, template_name=DEFAULT_TRACKING_TEMPLATE,
- extends_template='base.html'):
- """Displays a map of recently active users.
-
- Requires a Google Maps API key and GeoIP in order to be most
- effective.
-
- """
-
- GOOGLE_MAPS_KEY = getattr(settings, 'GOOGLE_MAPS_KEY', None)
-
- return render_to_response(template_name,
- {'GOOGLE_MAPS_KEY': GOOGLE_MAPS_KEY,
- 'template': extends_template},
- context_instance=RequestContext(request))
=== modified file 'urls.py'
--- urls.py 2017-12-23 09:15:02 +0000
+++ urls.py 2018-04-12 06:55:31 +0000
@@ -4,7 +4,6 @@
from django.contrib import admin
admin.autodiscover()
-from mainpage.views import mainpage
from news.feeds import NewsPostsFeed
from django.views.generic.base import RedirectView
from django.views.generic import TemplateView
@@ -28,7 +27,7 @@
url(r'^accounts/', include('registration.backends.hmac.urls')),
url('^', include('django.contrib.auth.urls')),
- # Feed for Mainpage
+ # Feed for news
url(r'^feeds/news/$', NewsPostsFeed()),
# Formerly 3rd party
@@ -47,15 +46,9 @@
url(r'^forum/', include('pybb.urls')),
# WL specific:
- url(r'^$', mainpage, name='mainpage'),
- url(r'^locale/$', 'mainpage.views.view_locale'),
- url(r'^changelog/$', 'mainpage.views.changelog', name='changelog'),
- url(r'^developers/$', 'mainpage.views.developers', name='developers'),
- url(r'^legal_notice/$', 'mainpage.views.legal_notice', name='legal_notice'),
- url(r'^legal_notice_thanks/$', 'mainpage.views.legal_notice_thanks',
- name='legal_notice_thanks'),
+ url(r'^', include('mainpage.urls')),
url(r'^help/(?P<path>.*)', RedirectView.as_view(url='/encyclopedia/%(path)s',
- permanent=True)), # to not break old links
+ permanent=True)), # to not break old links
url(r'^encyclopedia/', include('wlhelp.urls')),
url(r'^webchat/', include('wlwebchat.urls')),
url(r'^images/', include('wlimages.urls')),
=== modified file 'wiki/__init__.py'
--- wiki/__init__.py 2016-02-26 19:00:22 +0000
+++ wiki/__init__.py 2018-04-12 06:55:31 +0000
@@ -1,3 +0,0 @@
-#from wiki.templatetags.restructuredtext import restructuredtext
-
-#restructuredtext('`Available as 1.0 since September, 2007 <http://www.modwsgi.org/>`')
=== added file 'wiki/apps.py'
--- wiki/apps.py 1970-01-01 00:00:00 +0000
+++ wiki/apps.py 2018-04-12 06:55:31 +0000
@@ -0,0 +1,12 @@
+from django.apps import AppConfig
+from django.db.models import signals
+
+
+class WikiConfig(AppConfig):
+
+ name = 'wiki'
+ verbose_name = 'Widelands Wiki'
+
+ def ready(self):
+ from wiki.management import create_notice_types
+ signals.post_migrate.connect(create_notice_types, sender=self)
=== modified file 'wiki/management.py'
--- wiki/management.py 2017-04-26 19:54:03 +0000
+++ wiki/management.py 2018-04-12 06:55:31 +0000
@@ -1,11 +1,11 @@
-from django.db.models import signals
from django.utils.translation import ugettext_noop as _
try:
from notification import models as notification
- def create_notice_types(app, created_models, verbosity, **kwargs):
+ def create_notice_types(sender, **kwargs):
+ print("Creating noticetypes for wiki ...")
notification.create_notice_type('wiki_revision_reverted',
_('Article Revision Reverted'),
_('your revision has been reverted'))
@@ -13,9 +13,5 @@
_('Observed Article Changed'),
_('an article you observe has changed'))
- # TODO (Franku): post_syncdb is deprecated since Django 1.7
- # See: https://docs.djangoproject.com/en/1.8/ref/signals/#post-syncdb
- signals.post_syncdb.connect(create_notice_types,
- sender=notification)
except ImportError:
print 'Skipping creation of NoticeTypes as notification app not found'
=== modified file 'wiki/models.py'
--- wiki/models.py 2017-12-10 13:18:47 +0000
+++ wiki/models.py 2018-04-12 06:55:31 +0000
@@ -1,5 +1,5 @@
from datetime import datetime
-from django.core.urlresolvers import reverse
+from django.urls import reverse
# Google Diff Match Patch library
# http://code.google.com/p/google-diff-match-patch
=== removed file 'wiki/static_urls.py'
--- wiki/static_urls.py 2016-12-13 18:28:51 +0000
+++ wiki/static_urls.py 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
-from django.conf.urls import *
-from django.conf import settings
-
-
-urlpatterns = patterns('',
- url(r'^site_media/(?P<path>.*)$',
- 'django.views.static.serve',
- {'document_root': settings.STATIC_MEDIA_PATH},
- name='wiki_static_media'),
- )
=== modified file 'wiki/templatetags/switchcase.py'
--- wiki/templatetags/switchcase.py 2016-12-13 18:28:51 +0000
+++ wiki/templatetags/switchcase.py 2018-04-12 06:55:31 +0000
@@ -72,8 +72,8 @@
# Resolve the value; if it's a non-existant variable don't even bother
# checking the values of the cases since they'll never match.
try:
- value = template.resolve_variable(self.value, context)
- except VariableDoesNotExist:
+ value = template.Variable(self.value).resolve(context)
+ except template.VariableDoesNotExist:
return ''
# Check each case, and if it matches return the rendered content
@@ -99,8 +99,8 @@
"""
try:
- return template.resolve_variable(self.value, context) == otherval
- except VariableDoesNotExist:
+ return template.Variable(self.value).resolve(context) == otherval
+ except template.VariableDoesNotExist:
# If the variable doesn't exist, it doesn't equal anything.
return False
=== modified file 'wiki/templatetags/wiki_extras.py'
--- wiki/templatetags/wiki_extras.py 2017-02-24 20:12:28 +0000
+++ wiki/templatetags/wiki_extras.py 2018-04-12 06:55:31 +0000
@@ -52,18 +52,3 @@
'content': getattr(article, content_attr),
'markup': getattr(article, markup_attr)
}
-
-
-@register.inclusion_tag('wiki/article_teaser.html')
-def show_teaser(article):
- """Show a teaser box for the summary of the article."""
- return {'article': article}
-
-
-@register.inclusion_tag('wiki/wiki_title.html')
-def wiki_title(group):
- """Display a <h1> title for the wiki, with a link to the group main
- page."""
- return {'group_name': group.name,
- 'group_type': group._meta.verbose_name.title(),
- 'group_url': group.get_absolute_url()}
=== modified file 'wiki/views.py'
--- wiki/views.py 2017-12-10 19:04:12 +0000
+++ wiki/views.py 2018-04-12 06:55:31 +0000
@@ -5,10 +5,10 @@
from django.conf import settings
from django.core.cache import cache
from django.template import RequestContext
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.http import (Http404, HttpResponseRedirect,
HttpResponseNotAllowed, HttpResponse, HttpResponseForbidden)
-from django.shortcuts import get_object_or_404, render_to_response, redirect
+from django.shortcuts import get_object_or_404, render, redirect
from django.contrib.contenttypes.models import ContentType
from django.contrib import messages
from wiki.forms import ArticleForm
@@ -155,9 +155,8 @@
if extra_context is not None:
template_params.update(extra_context)
- return render_to_response('/'.join([template_dir, template_name]),
- template_params,
- context_instance=RequestContext(request))
+ return render(request, '/'.join([template_dir, template_name]),
+ template_params,)
return HttpResponseNotAllowed(['GET'])
@@ -224,9 +223,8 @@
if extra_context is not None:
template_params.update(extra_context)
- return render_to_response('/'.join([template_dir, template_name]),
- template_params,
- context_instance=RequestContext(request))
+ return render(request, '/'.join([template_dir, template_name]),
+ template_params,)
return HttpResponseNotAllowed(['GET'])
@@ -279,7 +277,7 @@
form.cache_old_content()
if form.is_valid():
- if request.user.is_authenticated():
+ if request.user.is_authenticated:
form.editor = request.user
if ((article is None) and (group_slug is not None)):
@@ -337,9 +335,8 @@
if extra_context is not None:
template_params.update(extra_context)
- return render_to_response('/'.join([template_dir, template_name]),
- template_params,
- context_instance=RequestContext(request))
+ return render(request, '/'.join([template_dir, template_name]),
+ template_params,)
def view_changeset(request, title, revision,
@@ -403,9 +400,8 @@
if extra_context is not None:
template_params.update(extra_context)
- return render_to_response('/'.join([template_dir, template_name]),
- template_params,
- context_instance=RequestContext(request))
+ return render(request, '/'.join([template_dir, template_name]),
+ template_params,)
return HttpResponseNotAllowed(['GET'])
@@ -449,9 +445,8 @@
if extra_context is not None:
template_params.update(extra_context)
- return render_to_response('/'.join([template_dir, template_name]),
- template_params,
- context_instance=RequestContext(request))
+ return render(request, '/'.join([template_dir, template_name]),
+ template_params,)
return HttpResponseNotAllowed(['GET'])
@@ -497,7 +492,7 @@
art = Article.objects.exclude(pk=article.pk).get(title=old_title)
except Article.DoesNotExist:
# No existing article found -> reverting possible
- if request.user.is_authenticated():
+ if request.user.is_authenticated:
article.revert_to(revision, get_real_ip(request), request.user)
else:
article.revert_to(revision, get_real_ip(request))
@@ -541,9 +536,8 @@
if extra_context is not None:
template_params.update(extra_context)
- return render_to_response('/'.join([template_dir, template_name]),
- template_params,
- context_instance=RequestContext(request))
+ return render(request, '/'.join([template_dir, template_name]),
+ template_params,)
return HttpResponseNotAllowed(['GET'])
@@ -680,6 +674,5 @@
context = {'found_links': found_links,
'found_old_links': found_old_links,
'name': title}
- return render_to_response('wiki/backlinks.html',
- context,
- context_instance=RequestContext(request))
+ return render(request, 'wiki/backlinks.html',
+ context,)
=== modified file 'wl_utils.py'
--- wl_utils.py 2016-12-13 18:28:51 +0000
+++ wl_utils.py 2018-04-12 06:55:31 +0000
@@ -13,23 +13,15 @@
# Initial implemenation details about AutoOneToOneField:
# http://softwaremaniacs.org/blog/2007/03/07/auto-one-to-one-field/
#
-# This doesn't worked anymore with django 1.8
-# changed according to:
-# https://github.com/skorokithakis/django-annoying/issues/36
-
-
-# SingleRelatedObjectDescriptor gets renamed with Django 1.9
-try:
- from django.db.models.fields.related import SingleRelatedObjectDescriptor
-except ImportError:
- from django.db.models.fields.related_descriptors import ReverseOneToOneDescriptor as SingleRelatedObjectDescriptor
+
+
+from django.db.models.fields.related_descriptors import ReverseOneToOneDescriptor
from django.db.models import OneToOneField
-from django.db.models.fields.related import SingleRelatedObjectDescriptor
from django.db import models
-class AutoSingleRelatedObjectDescriptor(SingleRelatedObjectDescriptor):
+class AutoReverseOneToOneDescriptor(ReverseOneToOneDescriptor):
"""The descriptor that handles the object creation for an
AutoOneToOneField."""
@@ -37,7 +29,7 @@
model = getattr(self.related, 'related_model', self.related.model)
try:
- return (super(AutoSingleRelatedObjectDescriptor, self)
+ return (super(AutoReverseOneToOneDescriptor, self)
.__get__(instance, instance_type))
except model.DoesNotExist:
obj = model(**{self.related.field.name: instance})
@@ -47,7 +39,7 @@
# Don't return obj directly, otherwise it won't be added
# to Django's cache, and the first 2 calls to obj.relobj
# will return 2 different in-memory objects
- return (super(AutoSingleRelatedObjectDescriptor, self)
+ return (super(AutoReverseOneToOneDescriptor, self)
.__get__(instance, instance_type))
@@ -57,4 +49,4 @@
def contribute_to_related_class(self, cls, related):
setattr(cls, related.get_accessor_name(),
- AutoSingleRelatedObjectDescriptor(related))
+ AutoReverseOneToOneDescriptor(related))
=== modified file 'wlggz/views.py'
--- wlggz/views.py 2016-12-13 18:28:51 +0000
+++ wlggz/views.py 2018-04-12 06:55:31 +0000
@@ -1,10 +1,9 @@
# Create your views here.
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.contrib.auth.decorators import login_required
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.shortcuts import render
from django.http import HttpResponseRedirect
from forms import EditGGZForm
@@ -30,6 +29,5 @@
'ggz_form': form,
}
- return render_to_response('wlggz/edit_ggz.html',
- template_params,
- context_instance=RequestContext(request))
+ return render(request, 'wlggz/edit_ggz.html',
+ template_params)
=== modified file 'wlimages/tests.py'
--- wlimages/tests.py 2016-12-13 18:28:51 +0000
+++ wlimages/tests.py 2018-04-12 06:55:31 +0000
@@ -24,7 +24,7 @@
from django.contrib.auth.models import User
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test.client import Client
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.db import IntegrityError
from models import Image
=== modified file 'wlimages/views.py'
--- wlimages/views.py 2016-12-13 18:28:51 +0000
+++ wlimages/views.py 2018-04-12 06:55:31 +0000
@@ -1,8 +1,7 @@
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponse, HttpResponseRedirect
-from django.shortcuts import get_object_or_404, render_to_response
-from django.template import RequestContext
+from django.shortcuts import get_object_or_404, render
from models import Image
from wl_utils import get_real_ip
@@ -42,7 +41,7 @@
# Get the current object's name (provided by __unicode__()) from this model
name = app.get_object_for_this_type(id=object_id)
- return render_to_response('wlimages/upload.html', {
+ return render(request, 'wlimages/upload.html', {
'upload_form': form,
- 'referer': name,
- }, context_instance=RequestContext(request))
+ 'referer': name,}
+ )
=== added file 'wlmaps/apps.py'
--- wlmaps/apps.py 1970-01-01 00:00:00 +0000
+++ wlmaps/apps.py 2018-04-12 06:55:31 +0000
@@ -0,0 +1,13 @@
+from django.apps import AppConfig
+from django.db.models import signals
+
+
+class WlMapsConfig(AppConfig):
+
+ name = 'wlmaps'
+ verbose_name = 'Widelands Maps'
+
+ def ready(self):
+ from wlmaps.management import create_notice_types
+ signals.post_migrate.connect(create_notice_types, sender=self)
+
=== modified file 'wlmaps/management.py'
--- wlmaps/management.py 2017-05-03 19:12:49 +0000
+++ wlmaps/management.py 2018-04-12 06:55:31 +0000
@@ -1,18 +1,13 @@
-from django.db.models import signals
from django.utils.translation import ugettext_noop as _
try:
from notification import models as notification
- def create_notice_types(app, created_models, verbosity, **kwargs):
+ def create_notice_types(sender, **kwargs):
+ print('Creating notice types for maps ...')
notification.create_notice_type('maps_new_map',
_('A new Map is available'),
- _('a new map is available for download'),1)
-
- # TODO (Franku): post_syncdb is deprecated since Django 1.7
- # See: https://docs.djangoproject.com/en/1.8/ref/signals/#post-syncdb
- signals.post_syncdb.connect(create_notice_types,
- sender=notification)
+ _('a new map is available for download'), 1)
except ImportError:
print 'Skipping creation of NoticeTypes as notification app not found'
=== modified file 'wlmaps/models.py'
--- wlmaps/models.py 2018-02-22 08:20:16 +0000
+++ wlmaps/models.py 2018-04-12 06:55:31 +0000
@@ -4,6 +4,7 @@
from django.db import models
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
+from django.urls import reverse
import datetime
try:
from notification import models as notification
@@ -43,9 +44,8 @@
ordering = ('-pub_date',)
get_latest_by = 'pub_date'
- @models.permalink
def get_absolute_url(self):
- return ('wlmaps_view', None, {'map_slug': self.slug})
+ return reverse('wlmaps_view', kwargs={'map_slug': self.slug})
def __unicode__(self):
return u'%s by %s' % (self.name, self.author)
=== modified file 'wlmaps/tests/test_views.py'
--- wlmaps/tests/test_views.py 2017-01-23 13:01:31 +0000
+++ wlmaps/tests/test_views.py 2018-04-12 06:55:31 +0000
@@ -3,7 +3,7 @@
#
from django.test import TestCase as DjangoTest, Client
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.contrib.auth.models import User
# TODO(Franku): Not used, but should be replaced with python json because it gets removed in django 1.7
#from django.utils import simplejson as json
=== modified file 'wlmaps/views.py'
--- wlmaps/views.py 2017-06-20 20:13:59 +0000
+++ wlmaps/views.py 2018-04-12 06:55:31 +0000
@@ -3,11 +3,10 @@
#
from forms import UploadMapForm, EditCommentForm
-from django.shortcuts import render_to_response, get_object_or_404
-from django.template import RequestContext
+from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect, HttpResponseNotAllowed, HttpResponse, HttpResponseBadRequest
-from django.core.urlresolvers import reverse
+from django.urls import reverse
import models
from settings import MAPS_PER_PAGE
from wl_utils import get_real_ip
@@ -19,11 +18,10 @@
#########
def index(request):
maps = models.Map.objects.all()
- return render_to_response('wlmaps/index.html',
+ return render(request, 'wlmaps/index.html',
{'maps': maps,
'maps_per_page': MAPS_PER_PAGE,
- },
- context_instance=RequestContext(request))
+ })
def rate(request, map_slug):
@@ -75,9 +73,8 @@
context = {
'map': map,
}
- return render_to_response('wlmaps/map_detail.html',
- context,
- context_instance=RequestContext(request))
+ return render(request, 'wlmaps/map_detail.html',
+ context)
@login_required
@@ -94,9 +91,8 @@
context = {'form': form, 'map': map}
- return render_to_response('wlmaps/edit_comment.html',
- context,
- context_instance=RequestContext(request))
+ return render(request, 'wlmaps/edit_comment.html',
+ context)
@login_required
@@ -112,6 +108,5 @@
form = UploadMapForm()
context = {'form': form, }
- return render_to_response('wlmaps/upload.html',
- context,
- context_instance=RequestContext(request))
+ return render(request, 'wlmaps/upload.html',
+ context)
=== modified file 'wlpoll/models.py'
--- wlpoll/models.py 2017-01-21 12:39:23 +0000
+++ wlpoll/models.py 2018-04-12 06:55:31 +0000
@@ -1,5 +1,6 @@
from django.db import models
from django.contrib.auth.models import User
+from django.urls import reverse
import datetime
@@ -33,9 +34,8 @@
return False
return self.closed_date < datetime.datetime.now()
- @models.permalink
def get_absolute_url(self):
- return ('wlpoll_detail', (), {'pk': self.id})
+ return reverse('wlpoll_detail', kwargs={'pk': self.id})
def __unicode__(self):
return self.name
=== modified file 'wlpoll/views.py'
--- wlpoll/views.py 2016-12-13 18:28:51 +0000
+++ wlpoll/views.py 2018-04-12 06:55:31 +0000
@@ -1,8 +1,8 @@
-from django.shortcuts import render_to_response, get_object_or_404
+from django.shortcuts import get_object_or_404
from django.contrib.auth.decorators import login_required
from django.template import RequestContext
from django.http import HttpResponseNotAllowed, HttpResponseRedirect, HttpResponseForbidden
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from models import Poll, Choice, Vote
from django.views import generic
=== modified file 'wlprofile/management/commands/profile_fetch_gravatars.py'
--- wlprofile/management/commands/profile_fetch_gravatars.py 2016-12-13 18:28:51 +0000
+++ wlprofile/management/commands/profile_fetch_gravatars.py 2018-04-12 06:55:31 +0000
@@ -1,4 +1,3 @@
-from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
=== modified file 'wlprofile/templatetags/custom_date.py'
--- wlprofile/templatetags/custom_date.py 2016-12-13 18:28:51 +0000
+++ wlprofile/templatetags/custom_date.py 2018-04-12 06:55:31 +0000
@@ -11,7 +11,7 @@
from django.utils.translation import ugettext as _
from django import template
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.utils.safestring import mark_safe
from django.template.defaultfilters import date as django_date
from django.core.exceptions import ObjectDoesNotExist
@@ -131,7 +131,7 @@
def custom_date(date, user):
"""If this user is logged in, return his representation, otherwise, return
a sane default."""
- if not user.is_authenticated():
+ if not user.is_authenticated:
return do_custom_date(DEFAULT_TIME_DISPLAY, date, float(DEFAULT_TIME_ZONE))
try:
userprofile = User.objects.get(username=user).wlprofile
=== modified file 'wlprofile/templatetags/wlprofile_extras.py'
--- wlprofile/templatetags/wlprofile_extras.py 2016-12-13 18:28:51 +0000
+++ wlprofile/templatetags/wlprofile_extras.py 2018-04-12 06:55:31 +0000
@@ -10,7 +10,7 @@
#
from django import template
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.utils.safestring import mark_safe
register = template.Library()
=== modified file 'wlprofile/views.py'
--- wlprofile/views.py 2016-12-13 18:28:51 +0000
+++ wlprofile/views.py 2018-04-12 06:55:31 +0000
@@ -1,11 +1,10 @@
# Create your views here.
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.shortcuts import render
from django.http import HttpResponseRedirect
from forms import EditProfileForm
@@ -31,9 +30,8 @@
'profile': profile,
}
- return render_to_response('wlprofile/view_profile.html',
- template_params,
- context_instance=RequestContext(request))
+ return render(request, 'wlprofile/view_profile.html',
+ template_params)
@login_required
@@ -54,6 +52,5 @@
'profile': instance,
'profile_form': form,
}
- return render_to_response('wlprofile/edit_profile.html',
- template_params,
- context_instance=RequestContext(request))
+ return render(request, 'wlprofile/edit_profile.html',
+ template_params)
=== added directory 'wlscreens/migrations'
=== added file 'wlscreens/migrations/0001_initial.py'
--- wlscreens/migrations/0001_initial.py 1970-01-01 00:00:00 +0000
+++ wlscreens/migrations/0001_initial.py 2018-04-12 06:55:31 +0000
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2018-04-05 20:49
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import wlscreens.models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Category',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255)),
+ ('slug', models.SlugField(blank=True, max_length=255, unique=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Screenshot',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255)),
+ ('screenshot', models.ImageField(storage=wlscreens.models.OverwriteStorage(), upload_to=wlscreens.models.screenshot_path)),
+ ('thumbnail', models.ImageField(editable=False, storage=wlscreens.models.OverwriteStorage(), upload_to=wlscreens.models.thumbnail_path)),
+ ('comment', models.TextField(blank=True, null=True)),
+ ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='screenshots', to='wlscreens.Category')),
+ ],
+ ),
+ migrations.AlterUniqueTogether(
+ name='screenshot',
+ unique_together=set([('name', 'category')]),
+ ),
+ ]
=== added file 'wlscreens/migrations/__init__.py'
=== modified file 'wlscreens/models.py'
--- wlscreens/models.py 2016-12-13 18:28:51 +0000
+++ wlscreens/models.py 2018-04-12 06:55:31 +0000
@@ -8,13 +8,14 @@
from django.core.files.storage import FileSystemStorage
import os
from settings import THUMBNAIL_SIZE, MEDIA_ROOT
+from django.urls import reverse
+
# Taken from django snippet 976
-
class OverwriteStorage(FileSystemStorage):
- def get_available_name(self, name):
+ def get_available_name(self, name, max_length=None):
"""Returns a filename that's free on the target storage system, and
available for new content to be written to."""
# If the filename already exists, remove it as if it was a true file
@@ -23,8 +24,6 @@
os.remove(os.path.join(MEDIA_ROOT, name))
return name
-# Create your models here.
-
class Category(models.Model):
name = models.CharField(max_length=255)
@@ -36,25 +35,33 @@
return super(Category, self).save(*args, **kwargs)
- @models.permalink
def get_absolute_url(self):
- return ('wlscreens_category', None, {'category_slug': self.slug})
+ return reverse('wlscreens_category', kwargs={'category_slug': self.slug})
def __unicode__(self):
return u"%s" % self.name
+def screenshot_path(instance, filename):
+ return 'wlscreens/screens/%s/%s.%s' % (
+ instance.category, instance.name, filename.rsplit('.', 1)[-1].lower()
+ )
+
+
+def thumbnail_path(instance, filename):
+ return 'wlscreens/thumbs/%s/%s.png' % (
+ instance.category, instance.name)
+
+
class Screenshot(models.Model):
name = models.CharField(max_length=255)
screenshot = models.ImageField(
- upload_to=lambda i, n: 'wlscreens/screens/%s/%s.%s' % (
- i.category, i.name, n.rsplit('.', 1)[-1].lower()),
+ upload_to=screenshot_path,
storage=OverwriteStorage(),
)
thumbnail = models.ImageField(
- upload_to=lambda i, n: 'wlscreens/thumbs/%s/%s.png' % (
- i.category, i.name),
+ upload_to=thumbnail_path,
editable=False,
storage=OverwriteStorage(),
)
=== modified file 'wlscreens/tests/test_views.py'
--- wlscreens/tests/test_views.py 2016-12-13 18:28:51 +0000
+++ wlscreens/tests/test_views.py 2018-04-12 06:55:31 +0000
@@ -3,7 +3,7 @@
#
from django.test import TestCase as DjangoTest, Client
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.contrib.auth.models import User
from wlscreens.models import *
=== modified file 'wlscreens/views.py'
--- wlscreens/views.py 2016-12-13 18:28:51 +0000
+++ wlscreens/views.py 2018-04-12 06:55:31 +0000
@@ -1,17 +1,16 @@
# Create your views here.
from models import Category, Screenshot
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.shortcuts import render
from django.http import Http404
def index(request):
c = Category.objects.order_by('-name')
- return render_to_response('wlscreens/index.html',
- {'categories': c, },
- RequestContext(request))
+ return render(request, 'wlscreens/index.html',
+ {'categories': c, }
+ )
def category(request, category_slug):
=== modified file 'wlsearch/urls.py'
--- wlsearch/urls.py 2017-09-20 09:29:28 +0000
+++ wlsearch/urls.py 2018-04-12 06:55:31 +0000
@@ -13,5 +13,5 @@
from views import search
urlpatterns = [
- url(r'^/?$', search, name='search'),
+ url(r'^$', search, name='search'),
]
=== modified file 'wlsearch/views.py'
--- wlsearch/views.py 2017-10-05 21:59:03 +0000
+++ wlsearch/views.py 2018-04-12 06:55:31 +0000
@@ -1,4 +1,4 @@
-from django.core.urlresolvers import reverse
+from django.urls import reverse
from django.shortcuts import render
from django.http import HttpResponseRedirect
from forms import WlSearchForm
=== modified file 'wlwebchat/views.py'
--- wlwebchat/views.py 2016-12-13 18:28:51 +0000
+++ wlwebchat/views.py 2018-04-12 06:55:31 +0000
@@ -1,7 +1,6 @@
# Create your views here.
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.shortcuts import render
def webchat(request):
- return render_to_response('wlwebchat/index.html', context_instance=RequestContext(request))
+ return render(request, 'wlwebchat/index.html')
Follow ups
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-27
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: GunChleoc, 2018-04-27
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-26
-
[Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: noreply, 2018-04-26
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-25
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-25
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-21
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: GunChleoc, 2018-04-20
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-20
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: GunChleoc, 2018-04-20
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-19
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-18
-
[Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-18
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-18
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: GunChleoc, 2018-04-18
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-18
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: GunChleoc, 2018-04-18
-
[Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-16
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-16
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-15
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: SirVer, 2018-04-15
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-15
-
Re: [Merge] lp:~widelands-dev/widelands-website/django1_11 into lp:widelands-website
From: kaputtnik, 2018-04-15