Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added project files

  • Loading branch information...
commit 351c6fffd84570cd79a39db0b54d3bc21cae7e51 0 parents
@geaden authored
Showing with 12,930 additions and 0 deletions.
  1. 0  bookmarks/__init__.py
  2. BIN  bookmarks/__init__.pyc
  3. +25 −0 bookmarks/admin.py
  4. BIN  bookmarks/admin.pyc
  5. +38 −0 bookmarks/feeds.py
  6. BIN  bookmarks/feeds.pyc
  7. +53 −0 bookmarks/forms.py
  8. BIN  bookmarks/forms.pyc
  9. +48 −0 bookmarks/forms.py~
  10. +77 −0 bookmarks/models.py
  11. BIN  bookmarks/models.pyc
  12. +33 −0 bookmarks/models.py~
  13. 0  bookmarks/templatetags/__init__.py
  14. BIN  bookmarks/templatetags/__init__.pyc
  15. +6 −0 bookmarks/templatetags/bookmark_filter.py
  16. BIN  bookmarks/templatetags/bookmark_filter.pyc
  17. +6 −0 bookmarks/templatetags/bookmarks_filters.py
  18. +1 −0  bookmarks/test_data.json
  19. +46 −0 bookmarks/tests.py
  20. BIN  bookmarks/tests.pyc
  21. +331 −0 bookmarks/views.py
  22. +163 −0 bookmarks/views.py.noajax
  23. BIN  bookmarks/views.pyc
  24. +230 −0 bookmarks/views.py~
  25. 0  django_bookmarks/__init__.py
  26. BIN  django_bookmarks/__init__.pyc
  27. BIN  django_bookmarks/locale/ru/LC_MESSAGES/django.mo
  28. +131 −0 django_bookmarks/locale/ru/LC_MESSAGES/django.po
  29. +192 −0 django_bookmarks/settings.py
  30. BIN  django_bookmarks/settings.pyc
  31. +162 −0 django_bookmarks/settings.py~
  32. +33 −0 django_bookmarks/site_media/bookmark_edit.js
  33. +33 −0 django_bookmarks/site_media/bookmark_edit.js~
  34. +48 −0 django_bookmarks/site_media/jquery.autocomplete.css
  35. +808 −0 django_bookmarks/site_media/jquery.autocomplete.js
  36. +13 −0 django_bookmarks/site_media/jquery.autocomplete.min.js
  37. +12 −0 django_bookmarks/site_media/jquery.autocomplete.pack.js
  38. +9,404 −0 django_bookmarks/site_media/jquery.js
  39. +11 −0 django_bookmarks/site_media/search.js
  40. +53 −0 django_bookmarks/site_media/style.css
  41. +32 −0 django_bookmarks/site_media/style.css~
  42. +6 −0 django_bookmarks/site_media/tag_autocomplete.js
  43. +10 −0 django_bookmarks/templates/admin/base_site.html
  44. +78 −0 django_bookmarks/templates/admin/change_form.html
  45. +100 −0 django_bookmarks/templates/admin/change_list.html
  46. +44 −0 django_bookmarks/templates/admin/delete_confirmation.html
  47. +49 −0 django_bookmarks/templates/base.html
  48. +26 −0 django_bookmarks/templates/base.html~
  49. +43 −0 django_bookmarks/templates/bookmark_list.html
  50. +32 −0 django_bookmarks/templates/bookmark_list.html~
  51. +23 −0 django_bookmarks/templates/bookmark_page.html
  52. +23 −0 django_bookmarks/templates/bookmark_page.html~
  53. +14 −0 django_bookmarks/templates/bookmark_save.html
  54. +14 −0 django_bookmarks/templates/bookmark_save.html~
  55. +5 −0 django_bookmarks/templates/bookmark_save_form.html
  56. +5 −0 django_bookmarks/templates/bookmark_save_form.html~
  57. +16 −0 django_bookmarks/templates/comments/form.html
  58. +17 −0 django_bookmarks/templates/comments/form.html~
  59. +9 −0 django_bookmarks/templates/comments/posted.html
  60. +9 −0 django_bookmarks/templates/comments/posted.html~
  61. 0  django_bookmarks/templates/feeds/recent_description.html
  62. +1 −0  django_bookmarks/templates/feeds/recent_title.html
  63. 0  django_bookmarks/templates/feeds/user_description.html
  64. +1 −0  django_bookmarks/templates/feeds/user_title.html
  65. +12 −0 django_bookmarks/templates/friend_invite.html
  66. +21 −0 django_bookmarks/templates/friends_page.html
  67. +5 −0 django_bookmarks/templates/invitation_email.txt
  68. +20 −0 django_bookmarks/templates/main_page.html
  69. +15 −0 django_bookmarks/templates/main_page.html~
  70. +6 −0 django_bookmarks/templates/popular_page.html
  71. +1 −0  django_bookmarks/templates/popular_page.html~
  72. +17 −0 django_bookmarks/templates/registration/login.html
  73. +10 −0 django_bookmarks/templates/registration/register.html
  74. +9 −0 django_bookmarks/templates/registration/register_success.html
  75. +17 −0 django_bookmarks/templates/search.html
  76. +17 −0 django_bookmarks/templates/search.html~
  77. +18 −0 django_bookmarks/templates/shared_bookmark_list.html
  78. +18 −0 django_bookmarks/templates/shared_bookmark_list.html~
  79. +10 −0 django_bookmarks/templates/tag_cloud_page.html
  80. +10 −0 django_bookmarks/templates/tag_cloud_page.html~
  81. +6 −0 django_bookmarks/templates/tag_page.html
  82. +6 −0 django_bookmarks/templates/tag_page.html~
  83. +21 −0 django_bookmarks/templates/user_page.html
  84. +11 −0 django_bookmarks/templates/user_page.html~
  85. +75 −0 django_bookmarks/urls.py
  86. BIN  django_bookmarks/urls.pyc
  87. +53 −0 django_bookmarks/urls.py~
  88. +28 −0 django_bookmarks/wsgi.py
  89. BIN  django_bookmarks/wsgi.pyc
  90. +10 −0 manage.py
0  bookmarks/__init__.py
No changes.
BIN  bookmarks/__init__.pyc
Binary file not shown
25 bookmarks/admin.py
@@ -0,0 +1,25 @@
+from bookmarks.models import Bookmark, Tag, Link, SharedBookmark, Friendship, Invitation
+from django.contrib import admin
+
+class BookmarkAdmin(admin.ModelAdmin):
+ fields = ['title', 'link', 'user']
+ list_display = ('title', 'link', 'user')
+ list_filter = ('user', )
+ ordering = ('title', )
+ search_fields = ('title', )
+
+admin.site.register(Bookmark, BookmarkAdmin)
+
+admin.site.register(Tag)
+admin.site.register(Link)
+admin.site.register(SharedBookmark)
+
+class FriendshipAdmin(admin.ModelAdmin):
+ list_filter = ('from_friend', )
+
+admin.site.register(Friendship, FriendshipAdmin)
+
+class InvitationAdmin(admin.ModelAdmin):
+ ordering = ('email', )
+
+admin.site.register(Invitation, InvitationAdmin)
BIN  bookmarks/admin.pyc
Binary file not shown
38 bookmarks/feeds.py
@@ -0,0 +1,38 @@
+from django.contrib.syndication.views import Feed
+from bookmarks.models import Bookmark
+from django.core.exceptions import ObjectDoesNotExist
+from django.contrib.auth.models import User
+from django.contrib.syndication.views import FeedDoesNotExist
+from django.shortcuts import get_object_or_404
+
+class RecentBookmarks(Feed):
+ title = 'Django Bookmarks | Recent Bookmarks'
+ link = '/feed/recent'
+ description = 'Recent bookmarks posted to Django Bookmarks'
+ description_template = 'feeds/recent_description.html'
+ title_template = 'feeds/recent_title.html'
+
+ def items(self):
+ return Bookmark.objects.order_by('-id')[:10]
+
+
+class UserBookmarks(Feed):
+ description_template = 'feeds/user_description.html'
+ title_template = 'feeds/user_title.html'
+
+ def get_object(self, request, username):
+ return get_object_or_404(User, username=username)
+
+
+ def title(self, user):
+ return 'Django Bookmarks | Bookmarks for %s' % user.username
+
+
+ def link(self, user):
+ return '/feeds/user/%s/' % user.username
+
+ def description(self, user):
+ return 'Recent bookmarks posted by %s' % user.username
+
+ def items(self, user):
+ return user.bookmark_set.order_by('-id')[:10]
BIN  bookmarks/feeds.pyc
Binary file not shown
53 bookmarks/forms.py
@@ -0,0 +1,53 @@
+import re
+from django.contrib.auth.models import User
+from django.core.exceptions import ObjectDoesNotExist
+from django import forms
+from django.utils.translation import gettext_lazy as _
+
+class RegistrationForm(forms.Form):
+ username = forms.CharField(label='Username', max_length=30)
+ email = forms.EmailField(label='Email')
+ password = forms.CharField(label='Password', widget=forms.PasswordInput())
+ verify = forms.CharField(label='Password (Again)', widget=forms.PasswordInput())
+
+ def clean_verify(self):
+ if 'password' in self.cleaned_data:
+ password = self.cleaned_data['password']
+ verify = self.cleaned_data['verify']
+ if password == verify:
+ return verify
+ raise forms.ValidationError('Passwords do not match.')
+
+ def clean_username(self):
+ username = self.cleaned_data['username']
+ if not re.search(r'^\w+$', username):
+ raise forms.ValidationError('Username can only contain alphanumeric characters and the underscore.')
+ try:
+ User.objects.get(username=username)
+ except ObjectDoesNotExist:
+ return username
+ raise forms.ValidationError('Username is already taken.')
+
+
+class BookmarkSaveForm(forms.Form):
+ url = forms.URLField(label='URL',
+ widget=forms.TextInput(attrs={'size': 64}))
+ title = forms.CharField(label='Title',
+ widget=forms.TextInput(attrs={'size': 64}))
+ tags = forms.CharField(label='Tags',
+ required=False,
+ widget=forms.TextInput(attrs={'size': 64}))
+ share = forms.BooleanField(label='Share on the main page',
+ required=False)
+
+
+class SearchForm(forms.Form):
+ query = forms.CharField(label='Enter a keyword to search for',
+ widget=forms.TextInput(attrs={'size': 32})
+ )
+
+
+class FriendInvitationForm(forms.Form):
+ name = forms.CharField(label=_('Friend\'s Name'), localize=True)
+ email = forms.EmailField(label=_('Friend\'s Email'), localize=True)
+
BIN  bookmarks/forms.pyc
Binary file not shown
48 bookmarks/forms.py~
@@ -0,0 +1,48 @@
+import re
+from django.contrib.auth.models import User
+from django.core.exceptions import ObjectDoesNotExist
+from django import forms
+
+class RegistrationForm(forms.Form):
+ username = forms.CharField(label='Username', max_length=30)
+ email = forms.EmailField(label='Email')
+ password = forms.CharField(label='Password', widget=forms.PasswordInput())
+ verify = forms.CharField(label='Password (Again)', widget=forms.PasswordInput())
+
+ def clean_verify(self):
+ if 'password' in self.cleaned_data:
+ password = self.cleaned_data['password']
+ verify = self.cleaned_data['verify']
+ if password == verify:
+ return verify
+ raise forms.ValidationError('Passwords do not match.')
+
+ def clean_username(self):
+ username = self.cleaned_data['username']
+ if not re.search(r'^\w+$', username):
+ raise forms.ValidationError('Username can only contain alphanumeric characters and the underscore.')
+ try:
+ User.objects.get(username=username)
+ except ObjectDoesNotExist:
+ return username
+ raise forms.ValidationError('Username is already taken.')
+
+
+class BookmarkSaveForm(forms.Form):
+ url = forms.URLField(label='URL',
+ widget=forms.TextInput(attrs={'size': 64}))
+ title = forms.CharField(label='Title',
+ widget=forms.TextInput(attrs={'size': 64}))
+ tags = forms.CharField(label='Tags',
+ required=False,
+ widget=forms.TextInput(attrs={'size': 64}))
+ share = forms.BooleanFiels(label='Share on the main page',
+ required=False)
+
+
+class SearchForm(forms.Form):
+ query = forms.CharField(label='Enter a keyword to search for',
+ widget=forms.TextInput(attrs={'size': 32})
+ )
+
+
77 bookmarks/models.py
@@ -0,0 +1,77 @@
+from django.db import models
+from django.contrib.auth.models import User
+from django.core.mail import send_mail
+from django.template import Context
+from django.conf import settings
+from django.template.loader import get_template
+
+class Link(models.Model):
+ url = models.URLField(unique=True)
+
+ def __unicode__(self):
+ return self.url
+
+
+class Bookmark(models.Model):
+ user = models.ForeignKey(User)
+ link = models.ForeignKey(Link)
+ title = models.CharField(max_length=200)
+
+ def __unicode__(self):
+ return '%s,%s' % (self.user.username, self.link.url)
+
+ def get_absolute_url(self):
+ return self.link.url
+
+
+class Tag(models.Model):
+ name = models.CharField(max_length=64, unique=True)
+ bookmarks = models.ManyToManyField(Bookmark)
+
+ def __unicode__(self):
+ return self.name
+
+
+class SharedBookmark(models.Model):
+ bookmark = models.ForeignKey(Bookmark, unique=True)
+ date = models.DateTimeField(auto_now_add=True)
+ votes = models.IntegerField(default=1)
+ user_voted = models.ManyToManyField(User)
+
+ def __unicode__(self):
+ return "%s,%s" % (self.bookmark,self.votes)
+
+
+class Friendship(models.Model):
+ from_friend = models.ForeignKey(User, related_name='friend_set')
+ to_friend = models.ForeignKey(User, related_name='to_friend_set')
+
+ def __unicode__(self):
+ return "%s, %s" % (self.from_friend.username, self.to_friend.username)
+
+ class Meta:
+ unique_together = (('to_friend', 'from_friend'), )
+ permissions = (('can_list_friend_bookmarks', 'Can list friend bookmarks'), )
+
+class Invitation(models.Model):
+ name = models.CharField(max_length=50)
+ email = models.EmailField()
+ code = models.CharField(max_length=20)
+ sender = models.ForeignKey(User)
+
+ def __unicode__(self):
+ return "%s, %s" % (self.sender.username, self.email)
+
+ def send(self):
+ subject = 'Invitation to join Django Bookmarks'
+ link = '%s/friend/accept/%s/' % (settings.SITE_HOST, self.code)
+ template = get_template('invitation_email.txt')
+ context = Context({
+ 'name': self.name,
+ 'link': link,
+ 'sender': self.sender.username,
+ })
+ message = template.render(context)
+ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [self.email])
+
+
BIN  bookmarks/models.pyc
Binary file not shown
33 bookmarks/models.py~
@@ -0,0 +1,33 @@
+from django.db import models
+from django.contrib.auth.models import User
+
+class Link(models.Model):
+ url = models.URLField(unique=True)
+
+ def __unicode__(self):
+ return self.url
+
+class Bookmark(models.Model):
+ user = models.ForeignKey(User)
+ link = models.ForeignKey(Link)
+ title = models.CharField(max_length=200)
+
+ def __unicode__(self):
+ return '%s,%s' % (self.user.username, self.link.url)
+
+class Tag(models.Model):
+ name = models.CharField(max_length=64, unique=True)
+ bookmarks = models.ManyToManyField(Bookmark)
+
+ def __unicode__(self):
+ return self.name
+
+class SharedBookmark(models.Model):
+ bookmark = models.ForeignKey(Bookmark, unique=True)
+ date = models.DateTimeField(auto_now_add=True)
+ votes = models.IntegerField(default=1)
+ user_voted = models.ManyToManyField(User)
+
+ def __unicode__(self):
+ return "%s,%s" % (self.bookmark, self.votes)
+
0  bookmarks/templatetags/__init__.py
No changes.
BIN  bookmarks/templatetags/__init__.pyc
Binary file not shown
6 bookmarks/templatetags/bookmark_filter.py
@@ -0,0 +1,6 @@
+from django import template
+register = template.Library()
+
+@register.filter
+def capitalize(value):
+ return value.capitalize()
BIN  bookmarks/templatetags/bookmark_filter.pyc
Binary file not shown
6 bookmarks/templatetags/bookmarks_filters.py
@@ -0,0 +1,6 @@
+from django import template
+register = template.Library()
+
+@register.filter
+def capitalize(value):
+ return value.capitalize()
1  bookmarks/test_data.json
@@ -0,0 +1 @@
+[{"pk": 8, "model": "contenttypes.contenttype", "fields": {"model": "bookmark", "name": "bookmark", "app_label": "bookmarks"}}, {"pk": 11, "model": "contenttypes.contenttype", "fields": {"model": "comment", "name": "comment", "app_label": "comments"}}, {"pk": 12, "model": "contenttypes.contenttype", "fields": {"model": "commentflag", "name": "comment flag", "app_label": "comments"}}, {"pk": 4, "model": "contenttypes.contenttype", "fields": {"model": "contenttype", "name": "content type", "app_label": "contenttypes"}}, {"pk": 14, "model": "contenttypes.contenttype", "fields": {"model": "friendship", "name": "friendship", "app_label": "bookmarks"}}, {"pk": 2, "model": "contenttypes.contenttype", "fields": {"model": "group", "name": "group", "app_label": "auth"}}, {"pk": 15, "model": "contenttypes.contenttype", "fields": {"model": "invitation", "name": "invitation", "app_label": "bookmarks"}}, {"pk": 7, "model": "contenttypes.contenttype", "fields": {"model": "link", "name": "link", "app_label": "bookmarks"}}, {"pk": 13, "model": "contenttypes.contenttype", "fields": {"model": "logentry", "name": "log entry", "app_label": "admin"}}, {"pk": 1, "model": "contenttypes.contenttype", "fields": {"model": "permission", "name": "permission", "app_label": "auth"}}, {"pk": 5, "model": "contenttypes.contenttype", "fields": {"model": "session", "name": "session", "app_label": "sessions"}}, {"pk": 10, "model": "contenttypes.contenttype", "fields": {"model": "sharedbookmark", "name": "shared bookmark", "app_label": "bookmarks"}}, {"pk": 6, "model": "contenttypes.contenttype", "fields": {"model": "site", "name": "site", "app_label": "sites"}}, {"pk": 9, "model": "contenttypes.contenttype", "fields": {"model": "tag", "name": "tag", "app_label": "bookmarks"}}, {"pk": 3, "model": "contenttypes.contenttype", "fields": {"model": "user", "name": "user", "app_label": "auth"}}, {"pk": "bec47f28093f7250699572826c5b1a10", "model": "sessions.session", "fields": {"expire_date": "2012-07-26T18:23:41.118Z", "session_data": "YmUyNmMzZjg4ZGNhMjk3MDBmYzFkYWM2OWE2YjkwZjgyYjExNDhmMjqAAn1xAShVDV9hdXRoX3Vz\nZXJfaWRLAVUSX2F1dGhfdXNlcl9iYWNrZW5kVSlkamFuZ28uY29udHJpYi5hdXRoLmJhY2tlbmRz\nLk1vZGVsQmFja2VuZFUKaW52aXRhdGlvbnECSwN1Lg==\n"}}, {"pk": "6d5e3f4e4612305205646ad56416551c", "model": "sessions.session", "fields": {"expire_date": "2012-07-28T09:38:51.960Z", "session_data": "MzRiODNhZDBmMzBkMmNiZjQxOTg0MDNmZWQ2YzczN2JlNGYzZDJjYTqAAn1xAShVEl9hdXRoX3Vz\nZXJfYmFja2VuZFUpZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmRVDV9h\ndXRoX3VzZXJfaWRLAVUPZGphbmdvX2xhbmd1YWdlWAIAAABydXECdS4=\n"}}, {"pk": "cd963dfb316d49b99be856ad50376cc7", "model": "sessions.session", "fields": {"expire_date": "2012-07-23T17:55:19.992Z", "session_data": "YmEyMjJjMWMxMjdkZDI4OWMzMDQwYmZmOWFjZmUzMGE5MjY5YWJiYTqAAn1xAShVEl9hdXRoX3Vz\nZXJfYmFja2VuZHECVSlkamFuZ28uY29udHJpYi5hdXRoLmJhY2tlbmRzLk1vZGVsQmFja2VuZHED\nVQ1fYXV0aF91c2VyX2lkcQRLAXUu\n"}}, {"pk": "18251784508627fa48e70c5c51545ce2", "model": "sessions.session", "fields": {"expire_date": "2012-07-24T17:10:18.638Z", "session_data": "ZmE4MGIzZTA4NGUyNWYzZmZmNDUyMWY0OGI3MDIyOTRhZjVkMzVhNjqAAn1xAVUKdGVzdGNvb2tp\nZXECVQZ3b3JrZWRxA3Mu\n"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "bookmarks.link", "fields": {"url": "http://packtpub.com/"}}, {"pk": 3, "model": "bookmarks.link", "fields": {"url": "http://www.google.com/"}}, {"pk": 4, "model": "bookmarks.link", "fields": {"url": "http://www.example.com/"}}, {"pk": 5, "model": "bookmarks.link", "fields": {"url": "http://www.nba.com/"}}, {"pk": 6, "model": "bookmarks.link", "fields": {"url": "http://sports.yahoo.com/nba"}}, {"pk": 7, "model": "bookmarks.link", "fields": {"url": "http://slamdunk.ru/"}}, {"pk": 8, "model": "bookmarks.link", "fields": {"url": "http://www.yandex.ru/"}}, {"pk": 9, "model": "bookmarks.link", "fields": {"url": "http://www.djangoproject.com/"}}, {"pk": 10, "model": "bookmarks.link", "fields": {"url": "http://www.amazon.com/"}}, {"pk": 11, "model": "bookmarks.link", "fields": {"url": "http://developers.android.com/"}}, {"pk": 12, "model": "bookmarks.link", "fields": {"url": "http://www.adandzo.com/"}}, {"pk": 13, "model": "bookmarks.link", "fields": {"url": "http://www.mobile-review.com/"}}, {"pk": 14, "model": "bookmarks.link", "fields": {"url": "http://www.droider.ru/"}}, {"pk": 15, "model": "bookmarks.link", "fields": {"url": "http://www.auchan.fr/"}}, {"pk": 16, "model": "bookmarks.link", "fields": {"url": "https://81.95.132.133/"}}, {"pk": 17, "model": "bookmarks.link", "fields": {"url": "https://oasisweb.uga.edu/html/applet.html"}}, {"pk": 18, "model": "bookmarks.link", "fields": {"url": "http://ru.wikipedia.org/wiki/JavaScript"}}, {"pk": 19, "model": "bookmarks.link", "fields": {"url": "http://www.nczonline.net/blog/2009/09/29/web-definitions-dom-ajax-and-more/"}}, {"pk": 20, "model": "bookmarks.link", "fields": {"url": "http://googlerussiablog.blogspot.com/2009/07/google-chrome.html"}}, {"pk": 21, "model": "bookmarks.link", "fields": {"url": "http://www.w3schools.com/js/"}}, {"pk": 22, "model": "bookmarks.link", "fields": {"url": "http://job-blog.bullgare.ru/2011/02"}}, {"pk": 1, "model": "bookmarks.tag", "fields": {"bookmarks": [9, 1], "name": "book"}}, {"pk": 2, "model": "bookmarks.tag", "fields": {"bookmarks": [1], "name": "publisher"}}, {"pk": 3, "model": "bookmarks.tag", "fields": {"bookmarks": [7, 2], "name": "search"}}, {"pk": 4, "model": "bookmarks.tag", "fields": {"bookmarks": [7, 2], "name": "engine"}}, {"pk": 5, "model": "bookmarks.tag", "fields": {"bookmarks": [3], "name": "example"}}, {"pk": 6, "model": "bookmarks.tag", "fields": {"bookmarks": [5, 6, 4], "name": "basketball"}}, {"pk": 7, "model": "bookmarks.tag", "fields": {"bookmarks": [21, 8], "name": "django"}}, {"pk": 8, "model": "bookmarks.tag", "fields": {"bookmarks": [], "name": "android,"}}, {"pk": 9, "model": "bookmarks.tag", "fields": {"bookmarks": [10], "name": "developers"}}, {"pk": 10, "model": "bookmarks.tag", "fields": {"bookmarks": [10, 12, 13], "name": "android"}}, {"pk": 11, "model": "bookmarks.tag", "fields": {"bookmarks": [5, 4], "name": "nba"}}, {"pk": 12, "model": "bookmarks.tag", "fields": {"bookmarks": [10], "name": "jelly"}}, {"pk": 13, "model": "bookmarks.tag", "fields": {"bookmarks": [10], "name": "bean"}}, {"pk": 14, "model": "bookmarks.tag", "fields": {"bookmarks": [1], "name": "ebook"}}, {"pk": 15, "model": "bookmarks.tag", "fields": {"bookmarks": [11], "name": "logistics"}}, {"pk": 16, "model": "bookmarks.tag", "fields": {"bookmarks": [11], "name": "adandzo"}}, {"pk": 17, "model": "bookmarks.tag", "fields": {"bookmarks": [12], "name": "mobile"}}, {"pk": 18, "model": "bookmarks.tag", "fields": {"bookmarks": [12, 13], "name": "review"}}, {"pk": 19, "model": "bookmarks.tag", "fields": {"bookmarks": [12], "name": "eldar"}}, {"pk": 20, "model": "bookmarks.tag", "fields": {"bookmarks": [12], "name": "murtazin"}}, {"pk": 21, "model": "bookmarks.tag", "fields": {"bookmarks": [14], "name": "auchan"}}, {"pk": 22, "model": "bookmarks.tag", "fields": {"bookmarks": [15], "name": "motiw"}}, {"pk": 23, "model": "bookmarks.tag", "fields": {"bookmarks": [16], "name": "applet"}}, {"pk": 24, "model": "bookmarks.tag", "fields": {"bookmarks": [17, 19, 18], "name": "JavaScript"}}, {"pk": 25, "model": "bookmarks.tag", "fields": {"bookmarks": [20, 18], "name": "Web"}}, {"pk": 26, "model": "bookmarks.tag", "fields": {"bookmarks": [19], "name": "google"}}, {"pk": 27, "model": "bookmarks.tag", "fields": {"bookmarks": [19], "name": "chrome"}}, {"pk": 28, "model": "bookmarks.tag", "fields": {"bookmarks": [20], "name": "tutorial"}}, {"pk": 29, "model": "bookmarks.tag", "fields": {"bookmarks": [21], "name": "internationalization"}}, {"pk": 38, "model": "auth.permission", "fields": {"codename": "add_logentry", "name": "Can add log entry", "content_type": 13}}, {"pk": 39, "model": "auth.permission", "fields": {"codename": "change_logentry", "name": "Can change log entry", "content_type": 13}}, {"pk": 40, "model": "auth.permission", "fields": {"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 13}}, {"pk": 4, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 2}}, {"pk": 5, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 2}}, {"pk": 6, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 2}}, {"pk": 1, "model": "auth.permission", "fields": {"codename": "add_permission", "name": "Can add permission", "content_type": 1}}, {"pk": 2, "model": "auth.permission", "fields": {"codename": "change_permission", "name": "Can change permission", "content_type": 1}}, {"pk": 3, "model": "auth.permission", "fields": {"codename": "delete_permission", "name": "Can delete permission", "content_type": 1}}, {"pk": 7, "model": "auth.permission", "fields": {"codename": "add_user", "name": "Can add user", "content_type": 3}}, {"pk": 8, "model": "auth.permission", "fields": {"codename": "change_user", "name": "Can change user", "content_type": 3}}, {"pk": 9, "model": "auth.permission", "fields": {"codename": "delete_user", "name": "Can delete user", "content_type": 3}}, {"pk": 22, "model": "auth.permission", "fields": {"codename": "add_bookmark", "name": "Can add bookmark", "content_type": 8}}, {"pk": 23, "model": "auth.permission", "fields": {"codename": "change_bookmark", "name": "Can change bookmark", "content_type": 8}}, {"pk": 24, "model": "auth.permission", "fields": {"codename": "delete_bookmark", "name": "Can delete bookmark", "content_type": 8}}, {"pk": 41, "model": "auth.permission", "fields": {"codename": "add_friendship", "name": "Can add friendship", "content_type": 14}}, {"pk": 44, "model": "auth.permission", "fields": {"codename": "can_list_friend_bookmarks", "name": "Can list friend bookmarks", "content_type": 14}}, {"pk": 42, "model": "auth.permission", "fields": {"codename": "change_friendship", "name": "Can change friendship", "content_type": 14}}, {"pk": 43, "model": "auth.permission", "fields": {"codename": "delete_friendship", "name": "Can delete friendship", "content_type": 14}}, {"pk": 45, "model": "auth.permission", "fields": {"codename": "add_invitation", "name": "Can add invitation", "content_type": 15}}, {"pk": 46, "model": "auth.permission", "fields": {"codename": "change_invitation", "name": "Can change invitation", "content_type": 15}}, {"pk": 47, "model": "auth.permission", "fields": {"codename": "delete_invitation", "name": "Can delete invitation", "content_type": 15}}, {"pk": 19, "model": "auth.permission", "fields": {"codename": "add_link", "name": "Can add link", "content_type": 7}}, {"pk": 20, "model": "auth.permission", "fields": {"codename": "change_link", "name": "Can change link", "content_type": 7}}, {"pk": 21, "model": "auth.permission", "fields": {"codename": "delete_link", "name": "Can delete link", "content_type": 7}}, {"pk": 28, "model": "auth.permission", "fields": {"codename": "add_sharedbookmark", "name": "Can add shared bookmark", "content_type": 10}}, {"pk": 29, "model": "auth.permission", "fields": {"codename": "change_sharedbookmark", "name": "Can change shared bookmark", "content_type": 10}}, {"pk": 30, "model": "auth.permission", "fields": {"codename": "delete_sharedbookmark", "name": "Can delete shared bookmark", "content_type": 10}}, {"pk": 25, "model": "auth.permission", "fields": {"codename": "add_tag", "name": "Can add tag", "content_type": 9}}, {"pk": 26, "model": "auth.permission", "fields": {"codename": "change_tag", "name": "Can change tag", "content_type": 9}}, {"pk": 27, "model": "auth.permission", "fields": {"codename": "delete_tag", "name": "Can delete tag", "content_type": 9}}, {"pk": 31, "model": "auth.permission", "fields": {"codename": "add_comment", "name": "Can add comment", "content_type": 11}}, {"pk": 34, "model": "auth.permission", "fields": {"codename": "can_moderate", "name": "Can moderate comments", "content_type": 11}}, {"pk": 32, "model": "auth.permission", "fields": {"codename": "change_comment", "name": "Can change comment", "content_type": 11}}, {"pk": 33, "model": "auth.permission", "fields": {"codename": "delete_comment", "name": "Can delete comment", "content_type": 11}}, {"pk": 35, "model": "auth.permission", "fields": {"codename": "add_commentflag", "name": "Can add comment flag", "content_type": 12}}, {"pk": 36, "model": "auth.permission", "fields": {"codename": "change_commentflag", "name": "Can change comment flag", "content_type": 12}}, {"pk": 37, "model": "auth.permission", "fields": {"codename": "delete_commentflag", "name": "Can delete comment flag", "content_type": 12}}, {"pk": 10, "model": "auth.permission", "fields": {"codename": "add_contenttype", "name": "Can add content type", "content_type": 4}}, {"pk": 11, "model": "auth.permission", "fields": {"codename": "change_contenttype", "name": "Can change content type", "content_type": 4}}, {"pk": 12, "model": "auth.permission", "fields": {"codename": "delete_contenttype", "name": "Can delete content type", "content_type": 4}}, {"pk": 13, "model": "auth.permission", "fields": {"codename": "add_session", "name": "Can add session", "content_type": 5}}, {"pk": 14, "model": "auth.permission", "fields": {"codename": "change_session", "name": "Can change session", "content_type": 5}}, {"pk": 15, "model": "auth.permission", "fields": {"codename": "delete_session", "name": "Can delete session", "content_type": 5}}, {"pk": 16, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 6}}, {"pk": 17, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 6}}, {"pk": 18, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 6}}, {"pk": 1, "model": "auth.group", "fields": {"name": "Moderator", "permissions": [19, 20, 21, 25, 26, 27]}}, {"pk": 2, "model": "auth.user", "fields": {"username": "severe", "first_name": "", "last_name": "", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-07-08T16:38:42.025Z", "groups": [], "user_permissions": [], "password": "pbkdf2_sha256$10000$J2lKr8ZcYmfx$wFzLgtZmGxWRYxFnEK79pdnulUmX7nhwKIH3otT9j6s=", "email": "severe@adandzo.com", "date_joined": "2012-07-08T16:38:42.025Z"}}, {"pk": 6, "model": "auth.user", "fields": {"username": "dave", "first_name": "", "last_name": "", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-07-08T17:16:15.124Z", "groups": [], "user_permissions": [], "password": "pbkdf2_sha256$10000$GX401Uc5ze81$i9r8R6Y0KSld9Ifxn/Xq10y7UHEdqr/M+hx1leCHxpA=", "email": "dave@example.com", "date_joined": "2012-07-08T17:16:15.124Z"}}, {"pk": 9, "model": "auth.user", "fields": {"username": "bill", "first_name": "", "last_name": "", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-07-08T17:23:40.478Z", "groups": [], "user_permissions": [], "password": "pbkdf2_sha256$10000$hVXNYawvqtdj$EeVSXKXbvtGRWD84gwQKifxTOwUlOi59BEKSLi2GyMg=", "email": "severe@adandzo.com", "date_joined": "2012-07-08T17:23:40.478Z"}}, {"pk": 5, "model": "auth.user", "fields": {"username": "mark", "first_name": "", "last_name": "", "is_active": true, "is_superuser": false, "is_staff": true, "last_login": "2012-07-11T20:13:35.840Z", "groups": [1], "user_permissions": [19, 20, 21], "password": "pbkdf2_sha256$10000$GNDoZe59aPUB$NBXNEC80XkJEgUwueBHUDZrdXgTmPzPbKFGpc6/EyEI=", "email": "mark@fm.com", "date_joined": "2012-07-08T17:15:13Z"}}, {"pk": 10, "model": "auth.user", "fields": {"username": "denisovgena", "first_name": "", "last_name": "", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-07-12T18:22:19.661Z", "groups": [], "user_permissions": [], "password": "pbkdf2_sha256$10000$PJLmc9K5DP5V$+5KbezW1GnBs0q/N9Zp38GPuSuZldJBILLRVvir10MI=", "email": "denisov@adandzo.com", "date_joined": "2012-07-12T18:22:19.661Z"}}, {"pk": 1, "model": "auth.user", "fields": {"username": "geaden", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": true, "last_login": "2012-07-12T18:22:36.892Z", "groups": [], "user_permissions": [], "password": "pbkdf2_sha256$10000$Lt82daqWm2kd$XCto9EdB4geD4xp/nGQ527J1KIq0x9VS1O+BgD0TBDA=", "email": "denisovgena@gmail.com", "date_joined": "2012-07-05T14:22:30.044Z"}}, {"pk": 4, "model": "admin.logentry", "fields": {"action_flag": 2, "action_time": "2012-07-11T15:39:52.114Z", "object_repr": "mark", "object_id": "5", "change_message": "Changed password and groups.", "user": 1, "content_type": 3}}, {"pk": 3, "model": "admin.logentry", "fields": {"action_flag": 1, "action_time": "2012-07-11T15:39:08.720Z", "object_repr": "Moderator", "object_id": "1", "change_message": "", "user": 1, "content_type": 2}}, {"pk": 2, "model": "admin.logentry", "fields": {"action_flag": 2, "action_time": "2012-07-11T15:37:37.112Z", "object_repr": "mark", "object_id": "5", "change_message": "Changed password and is_staff.", "user": 1, "content_type": 3}}, {"pk": 1, "model": "admin.logentry", "fields": {"action_flag": 2, "action_time": "2012-07-11T15:36:49.694Z", "object_repr": "mark", "object_id": "5", "change_message": "Changed password and user_permissions.", "user": 1, "content_type": 3}}, {"pk": 15, "model": "bookmarks.bookmark", "fields": {"link": 16, "user": 1, "title": "Motiw"}}, {"pk": 16, "model": "bookmarks.bookmark", "fields": {"link": 17, "user": 1, "title": "Applet is no loading"}}, {"pk": 7, "model": "bookmarks.bookmark", "fields": {"link": 8, "user": 1, "title": "Yandex"}}, {"pk": 17, "model": "bookmarks.bookmark", "fields": {"link": 18, "user": 1, "title": "Wikipedia about JavaScript"}}, {"pk": 19, "model": "bookmarks.bookmark", "fields": {"link": 20, "user": 1, "title": "JavaScript and Google Chrome"}}, {"pk": 3, "model": "bookmarks.bookmark", "fields": {"link": 4, "user": 1, "title": "Example"}}, {"pk": 2, "model": "bookmarks.bookmark", "fields": {"link": 3, "user": 1, "title": "Google"}}, {"pk": 20, "model": "bookmarks.bookmark", "fields": {"link": 21, "user": 1, "title": "JavaScript Tutorial"}}, {"pk": 18, "model": "bookmarks.bookmark", "fields": {"link": 19, "user": 1, "title": "Web definitions"}}, {"pk": 21, "model": "bookmarks.bookmark", "fields": {"link": 22, "user": 1, "title": "Django Internationalization"}}, {"pk": 5, "model": "bookmarks.bookmark", "fields": {"link": 6, "user": 1, "title": "Yahoo! Sports"}}, {"pk": 10, "model": "bookmarks.bookmark", "fields": {"link": 11, "user": 1, "title": "Android"}}, {"pk": 11, "model": "bookmarks.bookmark", "fields": {"link": 12, "user": 1, "title": "Consulting in logistics"}}, {"pk": 12, "model": "bookmarks.bookmark", "fields": {"link": 13, "user": 1, "title": "Mobile Review"}}, {"pk": 9, "model": "bookmarks.bookmark", "fields": {"link": 10, "user": 1, "title": "Amazon"}}, {"pk": 1, "model": "bookmarks.bookmark", "fields": {"link": 1, "user": 1, "title": "Pack Publishing"}}, {"pk": 14, "model": "bookmarks.bookmark", "fields": {"link": 15, "user": 5, "title": "Auchan"}}, {"pk": 6, "model": "bookmarks.bookmark", "fields": {"link": 7, "user": 1, "title": "Slamdunk.ru"}}, {"pk": 4, "model": "bookmarks.bookmark", "fields": {"link": 5, "user": 1, "title": "NBA"}}, {"pk": 8, "model": "bookmarks.bookmark", "fields": {"link": 9, "user": 1, "title": "Django project"}}, {"pk": 13, "model": "bookmarks.bookmark", "fields": {"link": 14, "user": 1, "title": "Droider"}}, {"pk": 2, "model": "bookmarks.sharedbookmark", "fields": {"bookmark": 13, "date": "2012-07-10T18:31:56.346Z", "votes": 2, "user_voted": [5, 1]}}, {"pk": 1, "model": "bookmarks.sharedbookmark", "fields": {"bookmark": 12, "date": "2012-07-10T18:24:01.638Z", "votes": 3, "user_voted": [5, 1]}}, {"pk": 3, "model": "bookmarks.sharedbookmark", "fields": {"bookmark": 14, "date": "2012-07-10T20:35:58.096Z", "votes": 2, "user_voted": [5, 1]}}, {"pk": 6, "model": "bookmarks.sharedbookmark", "fields": {"bookmark": 20, "date": "2012-07-11T18:10:20.313Z", "votes": 2, "user_voted": [5, 1]}}, {"pk": 5, "model": "bookmarks.sharedbookmark", "fields": {"bookmark": 18, "date": "2012-07-11T18:07:29.119Z", "votes": 2, "user_voted": [5, 1]}}, {"pk": 4, "model": "bookmarks.sharedbookmark", "fields": {"bookmark": 17, "date": "2012-07-11T18:06:28.833Z", "votes": 2, "user_voted": [5, 1]}}, {"pk": 7, "model": "bookmarks.sharedbookmark", "fields": {"bookmark": 21, "date": "2012-07-12T20:28:54.869Z", "votes": 1, "user_voted": [1]}}, {"pk": 1, "model": "bookmarks.friendship", "fields": {"to_friend": 2, "from_friend": 1}}, {"pk": 2, "model": "bookmarks.friendship", "fields": {"to_friend": 5, "from_friend": 1}}, {"pk": 3, "model": "bookmarks.friendship", "fields": {"to_friend": 1, "from_friend": 2}}, {"pk": 4, "model": "bookmarks.friendship", "fields": {"to_friend": 1, "from_friend": 5}}, {"pk": 5, "model": "bookmarks.friendship", "fields": {"to_friend": 1, "from_friend": 10}}, {"pk": 6, "model": "bookmarks.friendship", "fields": {"to_friend": 10, "from_friend": 1}}, {"pk": 1, "model": "bookmarks.invitation", "fields": {"code": "uCVMEF7sHDnN3thqrxUG", "sender": 1, "name": "Mark", "email": "denisovgena@gmail.com"}}, {"pk": 3, "model": "bookmarks.invitation", "fields": {"code": "skGqR8ZSrzYp3y8dYaMN", "sender": 1, "name": "Jordan", "email": "denisovgena@gmail.com"}}, {"pk": 4, "model": "bookmarks.invitation", "fields": {"code": "zcA9K9WHez9VDWZFvmWw", "sender": 1, "name": "Michael", "email": "denisovgena@gmail.com"}}, {"pk": 5, "model": "bookmarks.invitation", "fields": {"code": "8sqK7Nw3jU9QxfAQZ9sP", "sender": 1, "name": "Dwarf", "email": "denisovgena@gmail.com"}}, {"pk": 6, "model": "bookmarks.invitation", "fields": {"code": "N5M78V4wzyzvjJqFHskE", "sender": 1, "name": "man", "email": "denisovgena@gmail.com"}}, {"pk": 7, "model": "bookmarks.invitation", "fields": {"code": "u6PkbxnQMucCu8jPN9tx", "sender": 1, "name": "comon", "email": "denisovgena@gmail.com"}}, {"pk": 8, "model": "bookmarks.invitation", "fields": {"code": "hJvrfxNj9N52RxkJFtuf", "sender": 1, "name": "Geaden", "email": "denisovgena@gmail.com"}}, {"pk": 1, "model": "comments.comment", "fields": {"comment": "Awesome!", "user_url": "", "submit_date": "2012-07-10T20:30:48.596Z", "ip_address": "127.0.0.1", "object_pk": "2", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 2, "model": "comments.comment", "fields": {"comment": "Cool!", "user_url": "", "submit_date": "2012-07-10T20:33:17.259Z", "ip_address": "127.0.0.1", "object_pk": "2", "site": 1, "is_removed": false, "user": 5, "content_type": 10, "is_public": true, "user_name": "mark", "user_email": "mark@fm.com"}}, {"pk": 3, "model": "comments.comment", "fields": {"comment": "Test", "user_url": "", "submit_date": "2012-07-10T20:34:51.287Z", "ip_address": "127.0.0.1", "object_pk": "2", "site": 1, "is_removed": false, "user": 5, "content_type": 10, "is_public": true, "user_name": "mark", "user_email": "mark@fm.com"}}, {"pk": 4, "model": "comments.comment", "fields": {"comment": "What's that about?", "user_url": "", "submit_date": "2012-07-10T20:36:51.925Z", "ip_address": "127.0.0.1", "object_pk": "3", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 5, "model": "comments.comment", "fields": {"comment": "C'mon!", "user_url": "", "submit_date": "2012-07-10T20:43:24.740Z", "ip_address": "127.0.0.1", "object_pk": "3", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 6, "model": "comments.comment", "fields": {"comment": "Eldar is good!", "user_url": "", "submit_date": "2012-07-11T13:46:47.760Z", "ip_address": "127.0.0.1", "object_pk": "1", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 7, "model": "comments.comment", "fields": {"comment": "Test again", "user_url": "", "submit_date": "2012-07-11T13:50:08.758Z", "ip_address": "127.0.0.1", "object_pk": "1", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 8, "model": "comments.comment", "fields": {"comment": "What's up?", "user_url": "", "submit_date": "2012-07-11T14:11:48.527Z", "ip_address": "127.0.0.1", "object_pk": "1", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 9, "model": "comments.comment", "fields": {"comment": "Ouch!", "user_url": "", "submit_date": "2012-07-11T14:13:27.871Z", "ip_address": "127.0.0.1", "object_pk": "2", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 10, "model": "comments.comment", "fields": {"comment": "Let's try again!", "user_url": "", "submit_date": "2012-07-11T14:24:18.704Z", "ip_address": "127.0.0.1", "object_pk": "3", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 11, "model": "comments.comment", "fields": {"comment": "Oh, no!", "user_url": "", "submit_date": "2012-07-11T14:46:09.222Z", "ip_address": "127.0.0.1", "object_pk": "2", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 12, "model": "comments.comment", "fields": {"comment": "Let's try again!", "user_url": "", "submit_date": "2012-07-11T14:47:18.982Z", "ip_address": "127.0.0.1", "object_pk": "2", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 13, "model": "comments.comment", "fields": {"comment": "Hope it works!", "user_url": "", "submit_date": "2012-07-11T14:50:16.170Z", "ip_address": "127.0.0.1", "object_pk": "2", "site": 1, "is_removed": false, "user": 1, "content_type": 10, "is_public": true, "user_name": "geaden", "user_email": "denisovgena@gmail.com"}}, {"pk": 14, "model": "comments.comment", "fields": {"comment": "Thanks for the resource!", "user_url": "", "submit_date": "2012-07-11T20:14:31.128Z", "ip_address": "127.0.0.1", "object_pk": "6", "site": 1, "is_removed": false, "user": 5, "content_type": 10, "is_public": true, "user_name": "mark", "user_email": "mark@fm.com"}}]
46 bookmarks/tests.py
@@ -0,0 +1,46 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+from django.test.client import Client
+
+class ViewTest(TestCase):
+ fixtures = ['test_data.json']
+
+ def setUp(self):
+ self.client = Client()
+
+ # def test_register_page(self):
+ # data = {
+ # 'username': 'test_user',
+ # 'email': 'test@example.com',
+ # 'password': 'pass123',
+ # 'verify': 'pass123'
+ # }
+ # response = self.client.post('/register/', data)
+ # self.assertEqual(response.status_code, 302)
+
+ def test_bookmark_save(self):
+ response = self.client.login('/save/', 'geaden', 'sFrancis')
+ self.assertTrue(response)
+ data = {
+ 'url': 'http://www.example.com/',
+ 'title': 'Test URL',
+ 'tags': 'test-tag'
+ }
+ response = self.client.post('/save/', data)
+ self.assertEqual(response.status_code, 302)
+ response = self.client.get('/user/geaden/')
+ self.assertTrue('Test URL' in response.content)
+ self.assertTrue('test-tag' in response.content)
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.assertEqual(1 + 1, 2)
BIN  bookmarks/tests.pyc
Binary file not shown
331 bookmarks/views.py
@@ -0,0 +1,331 @@
+from django.views.decorators.csrf import csrf_protect, csrf_exempt, ensure_csrf_cookie
+from django.http import HttpResponse, Http404
+from django.contrib.auth.models import User
+from django.template import Context
+from django.shortcuts import render_to_response, get_object_or_404
+from django.http import HttpResponseRedirect
+from django.contrib.auth import logout
+from django.template import RequestContext
+from bookmarks.forms import *
+from bookmarks.models import *
+from django.contrib.auth.decorators import login_required
+from django.core.exceptions import ObjectDoesNotExist
+from datetime import datetime, timedelta
+from django.db.models import Q
+from django.core.paginator import Paginator
+from django.contrib import messages
+from django.utils.translation import gettext as _
+from django.views.decorators.cache import cache_page
+
+
+ITEMS_PER_PAGE = 10
+
+def main_page(request):
+ language = request.LANGUAGE_CODE
+ shared_bookmarks = SharedBookmark.objects.order_by('-date')[:10]
+ variables = RequestContext(request,
+ {
+ 'head_title': 'Welcome to Django Bookmarks',
+ 'page_title': 'Welcome to Django Bookmarks',
+ 'page_body': 'Here you can store and share bookmarks!',
+ 'shared_bookmarks': shared_bookmarks,
+ 'language': language,
+ })
+ return render_to_response('main_page.html', variables)
+
+@ensure_csrf_cookie
+def user_page(request, username):
+ user = get_object_or_404(User, username=username)
+ query_set = user.bookmark_set.order_by('-id')
+ paginator = Paginator(query_set, ITEMS_PER_PAGE)
+ is_friend = Friendship.objects.filter(from_friend=request.user, to_friend=user)
+ try:
+ page = int(request.GET['page'])
+ except:
+ page = 1
+ try:
+ bookmarks = paginator.page(page).object_list
+ except:
+ raise Http404
+ variables = RequestContext(request, {
+ 'username': username,
+ 'bookmarks': bookmarks,
+ 'show_tags': True,
+ 'show_edit': username == request.user.username,
+ 'show_paginator': paginator.num_pages > 1,
+ 'has_prev': paginator.page(page).has_previous(),
+ 'has_next': paginator.page(page).has_next(),
+ 'page': page,
+ 'pages': paginator.num_pages,
+ 'next_page': page + 1,
+ 'prev_page': page - 1,
+ 'is_friend': is_friend
+ })
+ return render_to_response('user_page.html', variables)
+
+def logout_page(request):
+ logout(request)
+ return HttpResponseRedirect('/')
+
+@csrf_protect
+def register_page(request):
+ if request.method == 'POST':
+ form = RegistrationForm(request.POST)
+ if form.is_valid():
+ user = User.objects.create_user(username=form.cleaned_data['username'],
+ password=form.cleaned_data['password'],
+ email=form.cleaned_data['email'])
+ if 'invitation' in request.session:
+ #Retrieve the invitation object.
+ invitation = Invitation.objects.get(id=request.session['invitation'])
+ #Create the friendship from user to sender.
+ friendship = Friendship(from_friend = user, to_friend = invitation.sender)
+ friendship.save()
+ #Create friendship from sender to user
+ friendship = Friendship(from_friend = invitation.sender, to_friend = user)
+ friendship.save()
+ #Delete invitation from the database and session.
+ invitation.delete()
+ del request.session['invitation']
+ return HttpResponseRedirect('/register/success/')
+ else:
+ form = RegistrationForm()
+ variables = RequestContext(request, {'form': form})
+ return render_to_response('registration/register.html', variables)
+
+
+def _bookmark_save(request, form):
+ #Create or get link
+ link, dummy = Link.objects.get_or_create(url=form.cleaned_data['url'])
+ #Create or get bookmark
+ bookmark, created = Bookmark.objects.get_or_create(user=request.user,
+ link=link)
+ #Update bookmark title
+ bookmark.title = form.cleaned_data['title']
+ #If the bookmark is being updated, clear old tag list.
+ if not created:
+ bookmark.tag_set.clear()
+ #Create a new tag list
+ tag_names = form.cleaned_data['tags'].split()
+ for tag_name in tag_names:
+ tag, dummy = Tag.objects.get_or_create(name=tag_name)
+ bookmark.tag_set.add(tag)
+ #Share on the main page if request.
+ if form.cleaned_data['share']:
+ shared_bookmark, created = SharedBookmark.objects.get_or_create(bookmark=bookmark)
+ if created:
+ shared_bookmark.user_voted.add(request.user)
+ shared_bookmark.save()
+ #Save bookmark to database
+ bookmark.save()
+ return bookmark
+
+
+@ensure_csrf_cookie
+@csrf_exempt
+@login_required(login_url='/login/')
+def bookmark_save_page(request):
+ ajax = request.GET.has_key('ajax')
+ if request.method == 'POST':
+ form = BookmarkSaveForm(request.POST)
+ if form.is_valid():
+ bookmark = _bookmark_save(request, form)
+ if ajax:
+ variables = RequestContext(request, {
+ 'bookmarks': [bookmark],
+ 'show_edit': True,
+ 'show_tags': True
+ })
+ return render_to_response('bookmark_list.html', variables)
+ else:
+ return HttpResponseRedirect('/user/%s/' % request.user.username)
+ else:
+ if ajax:
+ return HttpResponse('failure')
+ elif request.GET.has_key('url'):
+ url = request.GET['url']
+ title = ''
+ tags = ''
+ try:
+ link = Link.objects.get(url=url)
+ bookmark = Bookmark.objects.get(link=link,
+ user=request.user
+ )
+ title = bookmark.title
+ tags = ' '.join(tag.name for tag in bookmark.tag_set.all())
+ except ObjectDoesNotExist:
+ pass
+ form = BookmarkSaveForm({
+ 'url': url,
+ 'title': title,
+ 'tags': tags
+ })
+ else:
+ form = BookmarkSaveForm()
+ variables = RequestContext(request, {'form': form})
+ if ajax:
+ return render_to_response('bookmark_save_form.html', variables)
+ else:
+ return render_to_response('bookmark_save.html', variables)
+
+
+def tag_page(request, tag_name):
+ tag = get_object_or_404(Tag, name=tag_name)
+ bookmarks = tag.bookmarks.order_by('-id')
+ variables = RequestContext(request, {
+ 'bookmarks': bookmarks,
+ 'tag_name': tag_name,
+ 'show_tags': True,
+ 'show_user': True
+ })
+ return render_to_response('tag_page.html', variables)
+
+@cache_page
+def tag_cloud_page(request):
+ MAX_WEIGHT = 5
+ tags = Tag.objects.order_by('name')
+ #Calculate tag, min and max counts
+ min_count = max_count = tags[0].bookmarks.count()
+ for tag in tags:
+ tag.count = tag.bookmarks.count()
+ if tag.count < min_count:
+ min_count = tag.count
+ if max_count < tag.count:
+ max_count = tag.count
+ #Calculate count range. Avoid dividing by zero
+ range = float(max_count - min_count)
+ if range == 0.0:
+ range = 1.0
+ #Calculate tag weights
+ for tag in tags:
+ tag.weight = int(MAX_WEIGHT*(tag.count - min_count) / range)
+ variables = RequestContext(request, {
+ 'tags': tags
+ })
+ return render_to_response('tag_cloud_page.html', variables)
+
+def search_page(request):
+ form = SearchForm()
+ bookmarks = []
+ show_results = False
+ if request.GET.has_key('query'):
+ show_results = True
+ query = request.GET['query'].strip()
+ if query:
+ keywords = query.split()
+ q = Q()
+ for keyword in keywords:
+ q = q & Q(title__icontains=keyword)
+ form = SearchForm({'query': query})
+ bookmarks = Bookmark.objects.filter(q)[:10]
+ variables = RequestContext(request,{
+ 'form': form,
+ 'bookmarks': bookmarks,
+ 'show_results': show_results,
+ 'show_tags': True,
+ 'show_user': True
+ })
+ if request.GET.has_key('ajax'):
+ return render_to_response('bookmark_list.html', variables)
+ else:
+ return render_to_response('search.html', variables)
+
+@ensure_csrf_cookie
+def ajax_tag_autocomplete(request):
+ if request.GET.has_key('q'):
+ tags = Tag.objects.filter(name__istartswith=request.GET['q'])[:10]
+ return HttpResponse('\n'.join(tag.name for tag in tags))
+ return HttpResponse()
+
+@login_required
+def bookmark_vote_page(request):
+ if request.GET.has_key('id'):
+ try:
+ id = request.GET['id']
+ shared_bookmark = SharedBookmark.objects.get(id=id)
+ user_voted = shared_bookmark.user_voted.filter(username=request.user.username)
+ if not user_voted:
+ shared_bookmark.votes += 1
+ shared_bookmark.user_voted.add(request.user)
+ shared_bookmark.save()
+ except ObjectDoesNotExist:
+ raise Http404('Bookmark not found.')
+ if request.META.has_key('HTTP_REFERER'):
+ return HttpResponseRedirect(request.META['HTTP_REFERER'])
+ return HttpResponseRedirect('/')
+
+def popular_page(request):
+ today = datetime.today()
+ yesterday = today - timedelta(1)
+ shared_bookmarks = SharedBookmark.objects.filter(date__gt=yesterday)
+ shared_bookmarks = shared_bookmarks.order_by('-votes')[:10]
+ variables = RequestContext(request, {
+ 'shared_bookmarks': shared_bookmarks})
+ return render_to_response('popular_page.html', variables)
+
+@csrf_protect
+def bookmark_page(request, bookmark_id):
+ shared_bookmark = get_object_or_404(SharedBookmark,
+ id=bookmark_id
+ )
+ variables = RequestContext(request, {
+ 'shared_bookmark': shared_bookmark
+ })
+ return render_to_response('bookmark_page.html', variables)
+
+
+def friend_page(request, username):
+ user = get_object_or_404(User, username=username)
+ friends = [friendship.to_friend for friendship in user.friend_set.all()]
+ friend_bookmarks = Bookmark.objects.filter(user__in=friends).order_by('-id')
+ variables = RequestContext(request, {
+ 'username': username,
+ 'friends': friends,
+ 'bookmarks': friend_bookmarks[:10],
+ 'show_tags': True,
+ 'show_user': True
+ })
+ return render_to_response('friends_page.html', variables)
+
+@login_required
+def friend_add(request):
+ if request.GET.has_key('username'):
+ friend = get_object_or_404(User, username=request.GET['username'])
+ friendship = Friendship(from_friend=request.user, to_friend=friend)
+ try:
+ friendship.save()
+ messages.info(request, '%s was added to your friend list.' % friend.username)
+ except:
+ messages.info(request, '%s is already a friend of yours.' % friend.username)
+ return HttpResponseRedirect('/friends/%s/' % request.user.username)
+ else:
+ raise Http404
+
+@csrf_protect
+@login_required
+def friend_invite(request):
+ if request.method == 'POST':
+ form = FriendInvitationForm(request.POST)
+ if form.is_valid():
+ invitation = Invitation(
+ name = form.cleaned_data['name'],
+ email = form.cleaned_data['email'],
+ code = User.objects.make_random_password(20),
+ sender = request.user)
+ invitation.save()
+ try:
+ invitation.send()
+ messages.info(request, _('An invitation was sent to %s.') % {'email': invitation.email})
+ except:
+ messages.info(request, _('There was an error while sending the invitation.'))
+ return HttpResponseRedirect('/friend/invite/')
+ else:
+ form = FriendInvitationForm()
+ variables = RequestContext(request, {'form': form})
+ return render_to_response('friend_invite.html', variables)
+
+
+def friend_accept(request, code):
+ invitation = get_object_or_404(Invitation, code__exact = code)
+ request.session['invitation'] = invitation.id
+ return HttpResponseRedirect('/register/')
163 bookmarks/views.py.noajax
@@ -0,0 +1,163 @@
+from django.views.decorators.csrf import csrf_protect
+from django.http import HttpResponse, Http404
+from django.contrib.auth.models import User
+from django.template import Context
+from django.shortcuts import render_to_response, get_object_or_404
+from django.http import HttpResponseRedirect
+from django.contrib.auth import logout
+from django.template import RequestContext
+from bookmarks.forms import *
+from bookmarks.models import *
+from django.contrib.auth.decorators import login_required
+from django.core.exceptions import ObjectDoesNotExist
+
+def main_page(request):
+ return render_to_response('main_page.html', RequestContext(request,
+ {
+ 'head_title': 'Welcome to Django Bookmarks',
+ 'page_title': 'Welcome to Django Bookmarks',
+ 'page_body': 'Here you can store and share bookmarks!'
+ }))
+
+def user_page(request, username):
+ user = get_object_or_404(User, username=username)
+ bookmarks = user.bookmark_set.order_by('-id')
+ variables = RequestContext(request, {
+ 'username': username,
+ 'bookmarks': bookmarks,
+ 'show_tags': True,
+ 'show_edit': username == request.user.username
+ })
+
+ return render_to_response('user_page.html', variables)
+
+def logout_page(request):
+ logout(request)
+ return HttpResponseRedirect('/')
+
+@csrf_protect
+def register_page(request):
+ if request.method == 'POST':
+ form = RegistrationForm(request.POST)
+ if form.is_valid():
+ user = User.objects.create_user(username=form.cleaned_data['username'],
+ password=form.cleaned_data['password'],
+ email=form.cleaned_data['email'])
+ return HttpResponseRedirect('/register/success/')
+ else:
+ form = RegistrationForm()
+ variables = RequestContext(request, {'form': form})
+ return render_to_response('registration/register.html', variables)
+
+
+def _bookmark_save(request, form):
+ #Create or get link
+ link, dummy = Link.objects.get_or_create(url=form.cleaned_data['url'])
+ #Create or get bookmark
+ bookmark, created = Bookmark.objects.get_or_create(user=request.user,
+ link=link)
+ #Update bookmark title
+ bookmark.title = form.cleaned_data['title']
+ #If the bookmark is being updated, clear old tag list.
+ if not created:
+ bookmark.tag_set.clear()
+ #Create a new tag list
+ tag_names = form.cleaned_data['tags'].split()
+ for tag_name in tag_names:
+ tag, dummy = Tag.objects.get_or_create(name=tag_name)
+ bookmark.tag_set.add(tag)
+ #Save bookmark to database
+ bookmark.save()
+ return bookmark
+
+
+@csrf_protect
+@login_required(login_url='/login/')
+def bookmark_save_page(request):
+ ajax = request.GET.has_key('ajax')
+ if request.method == 'POST':
+ form = BookmarkSaveForm(request.POST)
+ if form.is_valid():
+ bookmark = _bookmark_save(request, form)
+ return HttpResponseRedirect('/user/%s/' % request.user.username)
+
+ elif request.GET.has_key('url'):
+ url = request.GET['url']
+ title = ''
+ tags = ''
+ try:
+ link = Link.objects.get(url=url)
+ bookmark = Bookmark.objects.get(link=link,
+ user=request.user
+ )
+ title = bookmark.title
+ tags = ' '.join(tag.name for tag in bookmark.tag_set.all())
+ except ObjectDoesNotExist:
+ pass
+ form = BookmarkSaveForm({
+ 'url': url,
+ 'title': title,
+ 'tags': tags
+ })
+ else:
+ form = BookmarkSaveForm()
+ variables = RequestContext(request, {'form': form})
+ return render_to_response('bookmark_save.html', variables)
+
+
+def tag_page(request, tag_name):
+ tag = get_object_or_404(Tag, name=tag_name)
+ bookmarks = tag.bookmarks.order_by('-id')
+ variables = RequestContext(request, {
+ 'bookmarks': bookmarks,
+ 'tag_name': tag_name,
+ 'show_tags': True,
+ 'show_user': True
+ })
+ return render_to_response('tag_page.html', variables)
+
+
+def tag_cloud_page(request):
+ MAX_WEIGHT = 5
+ tags = Tag.objects.order_by('name')
+ #Calculate tag, min and max counts
+ min_count = max_count = tags[0].bookmarks.count()
+ for tag in tags:
+ tag.count = tag.bookmarks.count()
+ if tag.count < min_count:
+ min_count = tag.count
+ if max_count < tag.count:
+ max_count = tag.count
+ #Calculate count range. Avoid dividing by zero
+ range = float(max_count - min_count)
+ if range == 0.0:
+ range = 1.0
+ #Calculate tag weights
+ for tag in tags:
+ tag.weight = int(MAX_WEIGHT*(tag.count - min_count) / range)
+ variables = RequestContext(request, {
+ 'tags': tags
+ })
+ return render_to_response('tag_cloud_page.html', variables)
+
+def search_page(request):
+ form = SearchForm()
+ bookmarks = []
+ show_results = False
+ if request.GET.has_key('query'):
+ show_results = True
+ query = request.GET['query'].strip()
+ if query:
+ form = SearchForm({'query': query})
+ bookmarks = Bookmark.objects.filter(title__icontains=query)[:10]
+ variables = RequestContext(request,{
+ 'form': form,
+ 'bookmarks': bookmarks,
+ 'show_results': show_results,
+ 'show_tags': True,
+ 'show_user': True
+ })
+ if request.GET.has_key('ajax'):
+ return render_to_response('bookmark_list.html', variables)
+ else:
+ return render_to_response('search.html', variables)
BIN  bookmarks/views.pyc
Binary file not shown
230 bookmarks/views.py~
@@ -0,0 +1,230 @@
+from django.views.decorators.csrf import csrf_protect, csrf_exempt, ensure_csrf_cookie
+from django.http import HttpResponse, Http404
+from django.contrib.auth.models import User
+from django.template import Context
+from django.shortcuts import render_to_response, get_object_or_404
+from django.http import HttpResponseRedirect
+from django.contrib.auth import logout
+from django.template import RequestContext
+from bookmarks.forms import *
+from bookmarks.models import *
+from django.contrib.auth.decorators import login_required
+from django.core.exceptions import ObjectDoesNotExist
+from datetime import datetime, timedelta
+
+def main_page(request):
+ shared_bookmarks = SharedBookmark.objects.order_by('-date')[:10]
+ variables = RequestContext(request,
+ {
+ 'head_title': 'Welcome to Django Bookmarks',
+ 'page_title': 'Welcome to Django Bookmarks',
+ 'page_body': 'Here you can store and share bookmarks!',
+ 'shared_bookmarks': shared_bookmarks
+ })
+ return render_to_response('main_page.html', variables)
+
+@ensure_csrf_cookie
+def user_page(request, username):
+ user = get_object_or_404(User, username=username)
+ bookmarks = user.bookmark_set.order_by('-id')
+ variables = RequestContext(request, {
+ 'username': username,
+ 'bookmarks': bookmarks,
+ 'show_tags': True,
+ 'show_edit': username == request.user.username
+ })
+
+ return render_to_response('user_page.html', variables)
+
+def logout_page(request):
+ logout(request)
+ return HttpResponseRedirect('/')
+
+@csrf_protect
+def register_page(request):
+ if request.method == 'POST':
+ form = RegistrationForm(request.POST)
+ if form.is_valid():
+ user = User.objects.create_user(username=form.cleaned_data['username'],
+ password=form.cleaned_data['password'],
+ email=form.cleaned_data['email'])
+ return HttpResponseRedirect('/register/success/')
+ else:
+ form = RegistrationForm()
+ variables = RequestContext(request, {'form': form})
+ return render_to_response('registration/register.html', variables)
+
+
+def _bookmark_save(request, form):
+ #Create or get link
+ link, dummy = Link.objects.get_or_create(url=form.cleaned_data['url'])
+ #Create or get bookmark
+ bookmark, created = Bookmark.objects.get_or_create(user=request.user,
+ link=link)
+ #Update bookmark title
+ bookmark.title = form.cleaned_data['title']
+ #If the bookmark is being updated, clear old tag list.
+ if not created:
+ bookmark.tag_set.clear()
+ #Create a new tag list
+ tag_names = form.cleaned_data['tags'].split()
+ for tag_name in tag_names:
+ tag, dummy = Tag.objects.get_or_create(name=tag_name)
+ bookmark.tag_set.add(tag)
+ #Share on the main page if requeste.
+ if form.cleaned_data['share']:
+ shared_bookmark, created = SharedBookmark.objects.get_or_create(bookmark=bookmark)
+ if created:
+ shared_bookmark.user_voted.add(request.user)
+ shared_bookmark.save()
+ #Save bookmark to database
+ bookmark.save()
+ return bookmark
+
+
+@ensure_csrf_cookie
+@csrf_exempt
+@login_required(login_url='/login/')
+def bookmark_save_page(request):
+ ajax = request.GET.has_key('ajax')
+ if request.method == 'POST':
+ form = BookmarkSaveForm(request.POST)
+ if form.is_valid():
+ bookmark = _bookmark_save(request, form)
+ if ajax:
+ variables = RequestContext(request, {
+ 'bookmarks': [bookmark],
+ 'show_edit': True,
+ 'show_tags': True
+ })
+ return render_to_response('bookmark_list.html', variables)
+ else:
+ return HttpResponseRedirect('/user/%s/' % request.user.username)
+ else:
+ if ajax:
+ return HttpResponse('failure')
+ elif request.GET.has_key('url'):
+ url = request.GET['url']
+ title = ''
+ tags = ''
+ try:
+ link = Link.objects.get(url=url)
+ bookmark = Bookmark.objects.get(link=link,
+ user=request.user
+ )
+ title = bookmark.title
+ tags = ' '.join(tag.name for tag in bookmark.tag_set.all())
+ except ObjectDoesNotExist:
+ pass
+ form = BookmarkSaveForm({
+ 'url': url,
+ 'title': title,
+ 'tags': tags
+ })
+ else:
+ form = BookmarkSaveForm()
+ variables = RequestContext(request, {'form': form})
+ if ajax:
+ return render_to_response('bookmark_save_form.html', variables)
+ else:
+ return render_to_response('bookmark_save.html', variables)
+
+
+def tag_page(request, tag_name):
+ tag = get_object_or_404(Tag, name=tag_name)
+ bookmarks = tag.bookmarks.order_by('-id')
+ variables = RequestContext(request, {
+ 'bookmarks': bookmarks,
+ 'tag_name': tag_name,
+ 'show_tags': True,
+ 'show_user': True
+ })
+ return render_to_response('tag_page.html', variables)
+
+
+def tag_cloud_page(request):
+ MAX_WEIGHT = 5
+ tags = Tag.objects.order_by('name')
+ #Calculate tag, min and max counts
+ min_count = max_count = tags[0].bookmarks.count()
+ for tag in tags:
+ tag.count = tag.bookmarks.count()
+ if tag.count < min_count:
+ min_count = tag.count
+ if max_count < tag.count:
+ max_count = tag.count
+ #Calculate count range. Avoid dividing by zero
+ range = float(max_count - min_count)
+ if range == 0.0:
+ range = 1.0
+ #Calculate tag weights
+ for tag in tags:
+ tag.weight = int(MAX_WEIGHT*(tag.count - min_count) / range)
+ variables = RequestContext(request, {
+ 'tags': tags
+ })
+ return render_to_response('tag_cloud_page.html', variables)
+
+def search_page(request):
+ form = SearchForm()
+ bookmarks = []
+ show_results = False
+ if request.GET.has_key('query'):
+ show_results = True
+ query = request.GET['query'].strip()
+ if query:
+ form = SearchForm({'query': query})
+ bookmarks = Bookmark.objects.filter(title__icontains=query)[:10]
+ variables = RequestContext(request,{
+ 'form': form,
+ 'bookmarks': bookmarks,
+ 'show_results': show_results,
+ 'show_tags': True,
+ 'show_user': True
+ })
+ if request.GET.has_key('ajax'):
+ return render_to_response('bookmark_list.html', variables)
+ else:
+ return render_to_response('search.html', variables)
+
+@ensure_csrf_cookie
+def ajax_tag_autocomplete(request):
+ if request.GET.has_key('q'):
+ tags = Tag.objects.filter(name__istartswith=request.GET['q'])[:10]
+ return HttpResponse('\n'.join(tag.name for tag in tags))
+ return HttpResponse()
+
+@login_required
+def bookmark_vote_page(request):
+ if request.GET.has_key('id'):
+ try:
+ id = request.GET['id']
+ shared_bookmark = SharedBookmark.objects.get(id=id)
+ user_voted = shared_bookmark.user_voted.filter(username=request.user.username)
+ if not user_voted:
+ shared_bookmark.votes += 1
+ shared_bookmark.user_voted.add(request.user)
+ shared_bookmark.save()
+ except ObjectDoesNotExist:
+ raise Http404('Bookmark not found.')
+ if request.META.has_key('HTTP_REFERER'):
+ return HttpResponseRedirect(request.META['HTTP_REFERER'])
+ return HttpResponseRedirect('/')
+
+def popular_page(request):
+ today = datetime.today()
+ yesterday = today - timedelta(1)
+ shared_bookmarks = SharedBookmark.objects.filter(date__gt=yesterday)
+ shared_bookmarks = shared_bookmarks.order_by('-votes')[:10]
+ variables = RequestContext(request, {
+ 'shard_bookmarks': shared_bookmarks})
+ return render_to_response('popular_page.html', variables)
+
+def bookmark_page(request, bookmark_id):
+ shared_bookmark = get_object_or_404(SharedBookmark,
+ id=bookmark_id
+ )
+ variables = RequestContext(request, {
+ 'shared_bookmark': shared_bookmark
+ })
+ return render_to_response('bookmark_page.html', variables)
0  django_bookmarks/__init__.py
No changes.
BIN  django_bookmarks/__init__.pyc
Binary file not shown
BIN  django_bookmarks/locale/ru/LC_MESSAGES/django.mo
Binary file not shown
131 django_bookmarks/locale/ru/LC_MESSAGES/django.po
@@ -0,0 +1,131 @@
+# 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: 2012-07-14 13:09+0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: bookmarks/forms.py:51
+msgid "Friend's Name"
+msgstr "Имя друга"
+
+#: bookmarks/forms.py:52
+msgid "Friend's Email"
+msgstr "Адрес электронной почты друга"
+
+#: bookmarks/views.py:315
+#, python-format
+msgid "An invitation was sent to %s."
+msgstr "Приглашение было отправлено на %s."
+
+#: bookmarks/views.py:317
+msgid "There was an error while sending the invitation."
+msgstr "Возникла ошибка при отправке приглашения."
+
+#: django_bookmarks/templates/base.html:43
+msgid "Change language"
+msgstr "Изменить язык"
+
+#: django_bookmarks/templates/friend_invite.html:3
+#: django_bookmarks/templates/friend_invite.html:4
+msgid "Invite A Friend"
+msgstr "Пригласить друга"
+
+#: django_bookmarks/templates/friend_invite.html:6
+msgid ""
+"Enter your friend name and email below, and click \"send invite\" to invite "
+"your friend to join the site:"
+msgstr ""
+"Введите имя Вашего друга и адрес его электронной почты ниже, и нажмите "
+"\"Отправить приглашение\", чтобы пригласить Вашего друга присоединиться к "
+"сайту:"
+
+#: django_bookmarks/templates/friend_invite.html:10
+msgid "send invite"
+msgstr "Отправить приглашение"
+
+#: django_bookmarks/templates/admin/base_site.html:4
+msgid "Django Bookmarks site admin"
+msgstr ""
+
+#: django_bookmarks/templates/admin/base_site.html:7
+msgid "Django Bookmarks administration"
+msgstr ""
+
+#: django_bookmarks/templates/admin/change_form.html:21
+#: django_bookmarks/templates/admin/change_list.html:43
+#: django_bookmarks/templates/admin/delete_confirmation.html:8
+msgid "Home"
+msgstr ""
+
+#: django_bookmarks/templates/admin/change_form.html:24
+msgid "Add"
+msgstr ""
+
+#: django_bookmarks/templates/admin/change_form.html:34
+msgid "History"
+msgstr ""
+
+#: django_bookmarks/templates/admin/change_form.html:35
+msgid "View on site"
+msgstr ""
+
+#: django_bookmarks/templates/admin/change_form.html:46
+#: django_bookmarks/templates/admin/change_list.html:69
+msgid "Please correct the error below."
+msgid_plural "Please correct the errors below."
+msgstr[0] ""
+msgstr[1] ""
+
+#: django_bookmarks/templates/admin/change_list.html:60
+#, python-format
+msgid "Add %(name)s"
+msgstr ""
+
+#: django_bookmarks/templates/admin/change_list.html:80
+msgid "Filter"
+msgstr ""
+
+#: django_bookmarks/templates/admin/delete_confirmation.html:12
+msgid "Delete"
+msgstr ""
+
+#: django_bookmarks/templates/admin/delete_confirmation.html:19
+#, python-format
+msgid ""
+"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting "
+"related objects, but your account doesn't have permission to delete the "
+"following types of objects:"
+msgstr ""
+
+#: django_bookmarks/templates/admin/delete_confirmation.html:27
+#, python-format
+msgid ""
+"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the "
+"following protected related objects:"
+msgstr ""
+
+#: django_bookmarks/templates/admin/delete_confirmation.html:35
+#, python-format
+msgid ""
+"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? "
+"All of the following related items will be deleted:"
+msgstr ""
+
+#: django_bookmarks/templates/admin/delete_confirmation.html:40
+msgid "Yes, I'm sure"
+msgstr ""
192 django_bookmarks/settings.py
@@ -0,0 +1,192 @@
+# -*- coding: utf-8 -*-
+# Django settings for django_bookmarks project.
+import os.path
+import django.contrib.auth
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+SITE_HOST = '127.0.0.1:8000'
+DEFAULT_FROM_EMAIL = 'Django Bookmarks <denisovgena@mail.ru>'
+EMAIL_HOST = 'smtp.mail.ru'
+EMAIL_PORT = ''
+EMAIL_HOST_USER = 'denisovgena@mail.ru'
+EMAIL_HOST_PASSWORD = 'sFrancis'
+
+ADMINS = (
+ # ('Your Name', 'your_email@example.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.postgresql_psycopg2', #'mysql', 'sqlite3' or 'oracle'.
+ 'NAME': 'djangobookmarks', # Or path to database file if using sqlite3.
+ 'USER': 'postgres', # Not used with sqlite3.
+ 'PASSWORD': '12345', # 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.
+ }
+}
+
+CACHE_BACKEND = 'file:///tmp/djnago_cache'
+CACHE_MIDDLEWARE_SECONDS = 60 * 5
+
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Europe/Moscow'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en'
+
+LANGUAGES = (
+ ('en', 'English'),
+ ('ru', 'Русский'),
+)
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale.
+USE_L10N = True
+
+# If you set this to False, Django will not use timezone-aware datetimes.
+USE_TZ = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = ''
+
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = ''
+
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
+
+# Additional locations of static files
+STATICFILES_DIRS = (
+ # Put strings here, like "/home/html/static" or "C:/www/django/static".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+)
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+ 'django.contrib.staticfiles.finders.FileSystemFinder',
+ 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'k!w)q86&amp;(y83bw&amp;jd3f%4-k*wkhya!0fad-4&amp;d^exf62(pqj+w'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader',
+# 'django.template.loaders.eggs.Loader',
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+ "django.contrib.auth.context_processors.auth",
+ "django.core.context_processors.debug",
+ "django.core.context_processors.i18n",
+ "django.core.context_processors.media",
+ "django.core.context_processors.static",
+ "django.contrib.messages.context_processors.messages",
+ "django.core.context_processors.i18n", )
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ #'django.middleware.cache.CacheMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.locale.LocaleMiddleware',
+ #'django.middleware.csrf.CsrfResponseMiddleware',
+ # Uncomment the next line for simple clickjacking protection:
+ # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+)
+
+ROOT_URLCONF = 'django_bookmarks.urls'
+
+# Python dotted path to the WSGI application used by Django's runserver.
+WSGI_APPLICATION = 'django_bookmarks.wsgi.application'
+
+TEMPLATE_DIRS = (
+ # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+ os.path.join(os.path.dirname(__file__), 'templates'),
+)
+
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.admin',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'bookmarks',
+ 'django.contrib.comments',
+ # Uncomment the next line to enable the admin:
+ # 'django.contrib.admin',
+ # Uncomment the next line to enable admin documentation:
+ # 'django.contrib.admindocs',
+)
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error when DEBUG=False.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'filters': {
+ 'require_debug_false': {
+ '()': 'django.utils.log.RequireDebugFalse'
+ }
+ },
+ 'handlers': {
+ 'mail_admins': {
+ 'level': 'ERROR',
+ 'filters': ['require_debug_false'],
+ 'class': 'django.utils.log.AdminEmailHandler'
+ }
+ },
+ 'loggers': {
+ 'django.request': {
+ 'handlers': ['mail_admins'],
+ 'level': 'ERROR',
+ 'propagate': True,
+ },
+ }
+}
+
+#Custom login page
+#django.contrib.auth.LOGIN_URL = '/login/'
BIN  django_bookmarks/settings.pyc
Binary file not shown
162 django_bookmarks/settings.py~
@@ -0,0 +1,162 @@
+# Django settings for django_bookmarks project.
+import os.path
+import django.contrib.auth
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+ # ('Your Name', 'your_email@example.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.postgresql_psycopg2', #'mysql', 'sqlite3' or 'oracle'.
+ 'NAME': 'djangobookmarks', # Or path to database file if using sqlite3.
+ 'USER': 'postgres', # Not used with sqlite3.
+ 'PASSWORD': '12345', # Not used with sqlite3.
+ 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
+ 'PORT': '', # Set to empty string for default. Not used with sqlite3.
+ }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale.
+USE_L10N = True
+
+# If you set this to False, Django will not use timezone-aware datetimes.
+USE_TZ = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = ''
+
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = ''
+
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
+
+# Additional locations of static files
+STATICFILES_DIRS = (
+ # Put strings here, like "/home/html/static" or "C:/www/django/static".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+)
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+ 'django.contrib.staticfiles.finders.FileSystemFinder',
+ 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'k!w)q86&amp;(y83bw&amp;jd3f%4-k*wkhya!0fad-4&amp;d^exf62(pqj+w'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader',
+# 'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ #'django.middleware.csrf.CsrfResponseMiddleware',
+ # Uncomment the next line for simple clickjacking protection:
+ # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+)
+
+ROOT_URLCONF = 'django_bookmarks.urls'
+
+# Python dotted path to the WSGI application used by Django's runserver.
+WSGI_APPLICATION = 'django_bookmarks.wsgi.application'
+
+TEMPLATE_DIRS = (
+ # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+ os.path.join(os.path.dirname(__file__), 'templates'),
+)
+
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'bookmarks',
+ # Uncomment the next line to enable the admin:
+ # 'django.contrib.admin',
+ # Uncomment the next line to enable admin documentation:
+ # 'django.contrib.admindocs',
+)
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error when DEBUG=False.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'filters': {
+ 'require_debug_false': {
+ '()': 'django.utils.log.RequireDebugFalse'
+ }
+ },
+ 'handlers': {
+ 'mail_admins': {
+ 'level': 'ERROR',
+ 'filters': ['require_debug_false'],
+ 'class': 'django.utils.log.AdminEmailHandler'
+ }
+ },
+ 'loggers': {
+ 'django.request': {
+ 'handlers': ['mail_admins'],
+ 'level': 'ERROR',
+ 'propagate': True,
+ },
+ }
+}
+
+#Custom login page
+#django.contrib.auth.LOGIN_URL = '/login/'
33 django_bookmarks/site_media/bookmark_edit.js
@@ -0,0 +1,33 @@
+function bookmark_edit() {
+ var item = $(this).parent();
+ var url = item.find(".title").attr("href");
+ item.load("/save/?ajax&url=" + escape(url), null, function () {
+ $("#save-form").submit(bookmark_save);});
+ return false;
+}
+
+$(document).ready(function () {
+ $("a.edit").click(bookmark_edit);
+});
+
+function bookmark_save() {
+ var item = $(this).parent();
+ var data = {
+ url: item.find("#id_url").val(),
+ title: item.find("#id_title").val(),
+ tags: item.find("#id_tags").val()
+ };
+ $.post("/save/?ajax", data, function (result) {
+ if (result != "failure") {
+ item.before($("li", result).get(0));
+ item.remove();
+ $("a.edit").click(bookmark_edit);
+ }
+ else {
+ alert("Failed to validate bookmark before saving.");
+ }
+ });
+ return false;
+}
+
+
33 django_bookmarks/site_media/bookmark_edit.js~
@@ -0,0 +1,33 @@
+function bookmark_edit() {
+ var item = $(this).parent();
+ var url = item.find(".title").attr("href");
+ item.load("/save/?ajax&url=" + escape(url), null, function () {
+ $("#save-form").submit(bookmark_save);});
+ return false;
+}
+
+$(document).ready(function () {
+ $("a.edit").click(bookmark_edit);
+});
+
+function bookmark_save() {
+ var item = $(this).parent();
+ var data = {
+ url: item.find("#id_url").val(),
+ title: item.find("#id_title").val(),
+ tags: item.find("#id_tags").val()
+ };
+ $.post("/save/?ajax", data, function (result) {
+ if (result != "failure") {
+ item.before($("li", result).get(0));
+ item.remove();
+ $("ul.bookmarks.edit").click(bookmark_edit);
+ }
+ else {
+ alert("Failed to validate bookmark before saving.");
+ }
+ });
+ return false;
+}
+
+
48 django_bookmarks/site_media/jquery.autocomplete.css
@@ -0,0 +1,48 @@
+.ac_results {
+ padding: 0px;
+ border: 1px solid black;
+ background-color: white;
+ overflow: hidden;
+ z-index: 99999;
+}
+
+.ac_results ul {
+ width: 100%;
+ list-style-position: outside;
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.ac_results li {
+ margin: 0px;
+ padding: 2px 5px;
+ cursor: default;
+ display: block;
+ /*
+ if width will be 100% horizontal scrollbar will apear
+ when scroll mode will be used
+ */
+ /*width: 100%;*/
+ font: menu;
+ font-size: 12px;
+ /*
+ it is very important, if line-height not setted or setted
+ in relative units scroll will be broken in firefox
+ */
+ line-height: 16px;
+ overflow: hidden;
+}
+
+.ac_loading {
+ background: white url('indicator.gif') right center no-repeat;
+}
+
+.ac_odd {
+ background-color: #eee;
+}
+
+.ac_over {
+ background-color: #0A246A;
+ color: white;
+}
808 django_bookmarks/site_media/jquery.autocomplete.js
@@ -0,0 +1,808 @@
+/*
+ * jQuery Autocomplete plugin 1.1
+ *
+ * Copyright (c) 2009 Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
+ */
+
+;(function($) {
+
+$.fn.extend({
+ autocomplete: function(urlOrData, options) {
+ var isUrl = typeof urlOrData == "string";
+ options = $.extend({}, $.Autocompleter.defaults, {
+ url: isUrl ? urlOrData : null,
+ data: isUrl ? null : urlOrData,
+ delay: isUrl ? $.Autocompleter.defaults.delay : 10,
+ max: options && !options.scroll ? 10 : 150
+ }, options);
+
+ // if highlight is set to false, replace it with a do-nothing function
+ options.highlight = options.highlight || function(value) { return value; };
+
+ // if the formatMatch option is not specified, then use formatItem for backwards compatibility
+ options.formatMatch = options.formatMatch || options.formatItem;
+
+ return this.each(function() {
+ new $.Autocompleter(this, options);
+ });
+ },
+ result: function(handler) {
+ return this.bind("result", handler);
+ },
+ search: function(handler) {
+ return this.trigger("search", [handler]);
+ },
+ flushCache: function() {
+ return this.trigger("flushCache");
+ },
+ setOptions: function(options){
+ return this.trigger("setOptions", [options]);
+ },
+ unautocomplete: function() {
+ return this.trigger("unautocomplete");
+ }
+});
+
+$.Autocompleter = function(input, options) {
+
+ var KEY = {
+ UP: 38,
+ DOWN: 40,
+ DEL: 46,
+ TAB: 9,
+ RETURN: 13,
+ ESC: 27,
+ COMMA: 188,
+ PAGEUP: 33,
+ PAGEDOWN: 34,
+ BACKSPACE: 8
+ };
+
+ // Create $ object for input element
+ var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
+
+ var timeout;
+ var previousValue = "";
+ var cache = $.Autocompleter.Cache(options);
+ var hasFocus = 0;
+ var lastKeyPressCode;
+ var config = {
+ mouseDownOnSelect: false
+ };
+ var select = $.Autocompleter.Select(options, input, selectCurrent, config);
+
+ var blockSubmit;
+
+ // prevent form submit in opera when selecting with return key
+ $.browser.opera && $(input.form).bind("submit.autocomplete", function() {
+ if (blockSubmit) {
+ blockSubmit = false;
+ return false;
+ }
+ });
+
+ // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
+ $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
+ // a keypress means the input has focus
+ // avoids issue where input had focus before the autocomplete was applied
+ hasFocus = 1;
+ // track last key pressed
+ lastKeyPressCode = event.keyCode;
+ switch(event.keyCode) {
+
+ case KEY.UP:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.prev();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ case KEY.DOWN:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.next();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ case KEY.PAGEUP:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.pageUp();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ case KEY.PAGEDOWN:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.pageDown();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ // matches also semicolon
+ case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
+ case KEY.TAB:
+ case KEY.RETURN:
+ if( selectCurrent() ) {
+ // stop default to prevent a form submit, Opera needs special handling
+ event.preventDefault();
+ blockSubmit = true;
+ return false;
+ }
+ break;
+
+ case KEY.ESC:
+ select.hide();
+ break;
+
+ default:
+ clearTimeout(timeout);
+ timeout = setTimeout(onChange, options.delay);
+ break;
+ }
+ }).focus(function(){
+ // track whether the field has focus, we shouldn't process any
+ // results if the field no longer has focus
+ hasFocus++;
+ }).blur(function() {
+ hasFocus = 0;
+ if (!config.mouseDownOnSelect) {
+ hideResults();
+ }
+ }).click(function() {
+ // show select when clicking in a focused field
+ if ( hasFocus++ > 1 && !select.visible() ) {
+ onChange(0, true);
+ }
+ }).bind("search", function() {
+ // TODO why not just specifying both arguments?
+ var fn = (arguments.length > 1) ? arguments[1] : null;
+ function findValueCallback(q, data) {
+ var result;
+ if( data && data.length ) {
+ for (var i=0; i < data.length; i++) {
+ if( data[i].result.toLowerCase() == q.toLowerCase() ) {
+ result = data[i];
+ break;
+ }
+ }
+ }
+ if( typeof fn == "function" ) fn(result);
+ else $input.trigger("result", result && [result.data, result.value]);
+ }
+ $.each(trimWords($input.val()), function(i, value) {
+ request(value, findValueCallback, findValueCallback);
+ });
+ }).bind("flushCache", function() {
+ cache.flush();
+ }).bind("setOptions", function() {
+ $.extend(options, arguments[1]);
+ // if we've updated the data, repopulate
+ if ( "data" in arguments[1] )
+ cache.populate();
+ }).bind("unautocomplete", function() {
+ select.unbind();
+ $input.unbind();
+ $(input.form).unbind(".autocomplete");
+ });
+
+
+ function selectCurrent() {
+ var selected = select.selected();
+ if( !selected )
+ return false;
+
+ var v = selected.result;
+ previousValue = v;
+
+ if ( options.multiple ) {
+ var words = trimWords($input.val());
+ if ( words.length > 1 ) {
+ var seperator = options.multipleSeparator.length;
+ var cursorAt = $(input).selection().start;
+ var wordAt, progress = 0;
+ $.each(words, function(i, word) {
+ progress += word.length;
+ if (cursorAt <= progress) {
+ wordAt = i;
+ return false;
+ }
+ progress += seperator;
+ });
+ words[wordAt] = v;
+ // TODO this should set the cursor to the right position, but it gets overriden somewhere
+ //$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
+ v = words.join( options.multipleSeparator );
+ }
+ v += options.multipleSeparator;
+ }
+
+ $input.val(v);
+ hideResultsNow();
+ $input.trigger("result", [selected.data, selected.value]);
+ return true;
+ }
+
+ function onChange(crap, skipPrevCheck) {
+ if( lastKeyPressCode == KEY.DEL ) {
+ select.hide();
+ return;
+ }
+
+ var currentValue = $input.val();
+
+ if ( !skipPrevCheck && currentValue == previousValue )
+ return;
+
+ previousValue = currentValue;
+
+ currentValue = lastWord(currentValue);
+ if ( currentValue.length >= options.minChars) {
+ $input.addClass(options.loadingClass);
+ if (!options.matchCase)
+ currentValue = currentValue.toLowerCase();
+ request(currentValue, receiveData, hideResultsNow);
+ } else {
+ stopLoading();
+ select.hide();
+ }
+ };
+
+ function trimWords(value) {
+ if (!value)
+ return [""];
+ if (!options.multiple)
+ return [$.trim(value)];
+ return $.map(value.split(options.multipleSeparator), function(word) {
+ return $.trim(value).length ? $.trim(word) : null;
+ });
+ }
+
+ function lastWord(value) {
+ if ( !options.multiple )
+ return value;
+ var words = trimWords(value);
+ if (words.length == 1)
+ return words[0];
+ var cursorAt = $(input).selection().start;
+ if (cursorAt == value.length) {
+ words = trimWords(value)
+ } else {
+ words = trimWords(value.replace(value.substring(cursorAt), ""));
+ }
+ return words[words.length - 1];
+ }
+
+ // fills in the input box w/the first match (assumed to be the best match)
+ // q: the term entered
+ // sValue: the first matching result
+ function autoFill(q, sValue){
+ // autofill in the complete box w/the first match as long as the user hasn't entered in more data
+ // if the last user key pressed was backspace, don't autofill
+ if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
+ // fill in the value (keep the case the user has typed)
+ $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
+ // select the portion of the value not typed by the user (so the next character will erase)
+ $(input).selection(previousValue.length, previousValue.length + sValue.length);
+ }
+ };
+
+ function hideResults() {
+ clearTimeout(timeout);
+ timeout = setTimeout(hideResultsNow, 200);
+ };
+
+ function hideResultsNow() {
+ var wasVisible = select.visible();
+ select.hide();
+ clearTimeout(timeout);
+ stopLoading();
+ if (options.mustMatch) {
+ // call search and run callback
+ $input.search(
+ function (result){
+ // if no value found, clear the input box
+ if( !result ) {
+ if (options.multiple) {
+ var words = trimWords($input.val()).slice(0, -1);
+ $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
+ }
+ else {
+ $input.val( "" );
+ $input.trigger("result", null);
+ }
+ }
+ }
+ );
+ }
+ };
+
+ function receiveData(q, data) {
+ if ( data && data.length && hasFocus ) {
+ stopLoading();
+ select.display(data, q);
+ autoFill(q, data[0].value);
+ select.show();
+ } else {
+ hideResultsNow();
+ }
+ };
+
+ function request(term, success, failure) {
+ if (!options.matchCase)
+ term = term.toLowerCase();
+ var data = cache.load(term);
+ // recieve the cached data
+ if (data && data.length) {
+ success(term, data);
+ // if an AJAX url has been supplied, try loading the data now
+ } else if( (typeof options.url == "string") && (options.url.length > 0) ){
+
+ var extraParams = {
+ timestamp: +new Date()
+ };
+ $.each(options.extraParams, function(key, param) {
+ extraParams[key] = typeof param == "function" ? param() : param;
+ });
+
+ $.ajax({
+ // try to leverage ajaxQueue plugin to abort previous requests
+ mode: "abort",
+ // limit abortion to this input
+ port: "autocomplete" + input.name,
+ dataType: options.dataType,
+ url: options.url,
+ data: $.extend({
+ q: lastWord(term),
+ limit: options.max
+ }, extraParams),
+ success: function(data) {
+ var parsed = options.parse && options.parse(data) || parse(data);
+ cache.add(term, parsed);
+ success(term, parsed);
+ }
+ });
+ } else {
+ // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
+ select.emptyList();
+ failure(term);
+ }
+ };
+
+ function parse(data) {
+ var parsed = [];
+ var rows = data.split("\n");
+ for (var i=0; i < rows.length; i++) {
+ var row = $.trim(rows[i]);
+ if (row) {
+ row = row.split("|");
+ parsed[parsed.length] = {
+ data: row,
+ value: row[0],
+ result: options.formatResult && options.formatResult(row, row[0]) || row[0]
+ };
+ }
+ }
+ return parsed;
+ };
+
+ function stopLoading() {
+ $input.removeClass(options.loadingClass);
+ };
+
+};
+
+$.Autocompleter.defaults = {
+ inputClass: "ac_input",
+ resultsClass: "ac_results",
+ loadingClass: "ac_loading",
+ minChars: 1,
+ delay: 400,
+ matchCase: false,
+ matchSubset: true,
+ matchContains: false,
+ cacheLength: 10,
+ max: 100,
+ mustMatch: false,
+ extraParams: {},
+ selectFirst: true,
+ formatItem: function(row) { return row[0]; },
+ formatMatch: null,
+ autoFill: false,
+ width: 0,
+ multiple: false,
+ multipleSeparator: ", ",
+ highlight: function(value, term) {
+ return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
+ },
+ scroll: true,
+ scrollHeight: 180
+};
+
+$.Autocompleter.Cache = function(options) {
+
+ var data = {};
+ var length = 0;
+
+ function matchSubset(s, sub) {
+ if (!options.matchCase)
+ s = s.toLowerCase();
+ var i = s.indexOf(sub);
+ if (options.matchContains == "word"){
+ i = s.toLowerCase().search("\\b" + sub.toLowerCase());
+ }
+ if (i == -1) return false;
+ return i == 0 || options.matchContains;
+ };
+
+ function add(q, value) {
+ if (length > options.cacheLength){
+ flush();
+ }
+ if (!data[q]){
+ length++;
+ }
+ data[q] = value;
+ }
+
+ function populate(){
+ if( !options.data ) return false;
+ // track the matches
+ var stMatchSets = {},
+ nullData = 0;
+
+ // no url was specified, we need to adjust the cache length to make sure it fits the local data store
+ if( !options.url ) options.cacheLength = 1;
+
+ // track all options for minChars = 0
+ stMatchSets[""] = [];
+
+ // loop through the array and create a lookup structure
+ for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
+ var rawValue = options.data[i];
+ // if rawValue is a string, make an array otherwise just reference the array
+ rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
+
+ var value = options.formatMatch(rawValue, i+1, options.data.length);
+ if ( value === false )
+ continue;
+
+ var firstChar = value.charAt(0).toLowerCase();
+ // if no lookup array for this character exists, look it up now
+ if( !stMatchSets[firstChar] )
+ stMatchSets[firstChar] = [];
+
+ // if the match is a string
+ var row = {
+ value: value,
+ data: rawValue,
+ result: options.formatResult && options.formatResult(rawValue) || value
+ };
+
+ // push the current match into the set list
+ stMatchSets[firstChar].push(row);
+
+ // keep track of minChars zero items
+ if ( nullData++ < options.max ) {
+ stMatchSets[""].push(row);
+ }
+ };
+
+ // add the data items to the cache
+ $.each(stMatchSets, function(i, value) {
+ // increase the cache size
+ options.cacheLength++;
+ // add to the cache
+ add(i, value);
+ });
+ }
+
+ // populate any existing data
+ setTimeout(populate, 25);
+
+ function flush(){
+ data = {};
+ length = 0;
+ }
+
+ return {
+ flush: flush,
+ add: add,
+ populate: populate,
+ load: function(q) {
+ if (!options.cacheLength || !length)
+ return null;
+ /*
+ * if dealing w/local data and matchContains than we must make sure
+ * to loop through all the data collections looking for matches
+ */
+ if( !options.url && options.matchContains ){
+ // track all matches
+ var csub = [];
+ // loop through all the data grids for matches
+ for( var k in data ){
+ // don't search through the stMatchSets[""] (minChars: 0) cache
+ // this prevents duplicates
+ if( k.length > 0 ){
+ var c = data[k];
+ $.each(c, function(i, x) {
+ // if we've got a match, add it to the array
+ if (matchSubset(x.value, q)) {
+ csub.push(x);
+ }
+ });
+ }
+ }
+ return csub;
+ } else
+ // if the exact item exists, use it
+ if (data[q]){
+ return data[q];
+ } else
+ if (options.matchSubset) {
+ for (var i = q.length - 1; i >= options.minChars; i--) {
+ var c = data[q.substr(0, i)];
+ if (c) {
+ var csub = [];
+ $.each(c, function(i, x) {
+ if (matchSubset(x.value, q)) {
+ csub[csub.length] = x;
+ }
+ });
+ return csub;
+ }
+ }
+ }
+ return null;
+ }
+ };
+};
+
+$.Autocompleter.Select = function (options, input, select, config) {
+ var CLASSES = {
+ ACTIVE: "ac_over"
+ };
+
+ var listItems,
+ active = -1,
+ data,
+ term = "",
+ needsInit = true,
+ element,
+ list;
+
+ // Create results
+ function init() {
+ if (!needsInit)
+ return;
+ element = $("<div/>")
+ .hide()
+ .addClass(options.resultsClass)
+ .css("position", "absolute")
+ .appendTo(document.body);
+
+ list = $("<ul/>").appendTo(element).mouseover( function(event) {
+ if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
+ active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
+ $(target(event)).addClass(CLASSES.ACTIVE);
+ }
+ }).click(function(event) {
+ $(target(event)).addClass(CLASSES.ACTIVE);
+ select();
+ // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
+ input.focus();
+ return false;
+ }).mousedown(function() {
+ config.mouseDownOnSelect = true;
+ }).mouseup(function() {
+ config.mouseDownOnSelect = false;
+ });
+
+ if( options.width > 0 )
+ element.css("width", options.width);
+
+ needsInit = false;
+ }
+
+ function target(event) {
+ var element = event.target;
+ while(element && element.tagName != "LI")
+ element = element.parentNode;
+ // more fun with IE, sometimes event.target is empty, just ignore it then
+ if(!element)
+ return [];
+ return element;
+ }
+
+ function moveSelect(step) {
+ listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
+ movePosition(step);
+ var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
+ if(options.scroll) {
+ var offset = 0;
+ listItems.slice(0, active).each(function() {
+ offset += this.offsetHeight;
+ });
+ if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
+ list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
+ } else if(offset < list.scrollTop()) {
+ list.scrollTop(offset);
+ }
+ }
+ };
+
+ function movePosition(step) {
+ active += step;
+ if (active < 0) {
+ active = listItems.size() - 1;
+ } else if (active >= listItems.size()) {
+ active = 0;
+ }
+ }
+
+ function limitNumberOfItems(available) {
+ return options.max && options.max < available
+ ? options.max
+ : available;
+ }
+
+ function fillList() {
+ list.empty();
+ var max = limitNumberOfItems(data.length);
+ for (var i=0; i < max; i++) {
+ if (!data[i])
+ continue;
+ var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);