Skip to content
Browse files

move to GitLab

  • Loading branch information...
1 parent 4098afe commit 72af1ac166b2c2103b4f3c38f5506a00b4cbfab9 @mechanicalgirl committed Apr 27, 2014
Showing with 6 additions and 2,332 deletions.
  1. 0 README
  2. +6 −0 README.md
  3. 0 __init__.py
  4. 0 contact/__init__.py
  5. +0 −14 contact/forms.py
  6. 0 contact/models.py
  7. +0 −46 contact/templates/contact.html
  8. +0 −7 contact/templates/contact_notification.txt
  9. +0 −10 contact/urls.py
  10. +0 −40 contact/views.py
  11. +0 −16 feeds.py
  12. +0 −81 local_settings.py
  13. +0 −11 manage.py
  14. 0 message/__init__.py
  15. +0 −6 message/admin.py
  16. +0 −18 message/models.py
  17. +0 −17 message/templates/messages.html
  18. +0 −8 message/urls.py
  19. +0 −28 message/views.py
  20. 0 pages/__init__.py
  21. +0 −38 pages/templates/about.html
  22. +0 −8 pages/urls.py
  23. 0 playlist/__init__.py
  24. +0 −16 playlist/admin.py
  25. +0 −42 playlist/forms.py
  26. +0 −108 playlist/models.py
  27. +0 −18 playlist/templates/archive.html
  28. +0 −21 playlist/templates/archive_list.html
  29. +0 −20 playlist/templates/home.html
  30. +0 −33 playlist/templates/main_player.html
  31. +0 −6 playlist/templates/message_notification.txt
  32. +0 −51 playlist/templates/playlist_comments.html
  33. +0 −91 playlist/templates/playlist_save.html
  34. +0 −52 playlist/templates/single.html
  35. +0 −27 playlist/templates/song_add.html
  36. +0 −20 playlist/urls.py
  37. +0 −207 playlist/views.py
  38. 0 profiles/__init__.py
  39. +0 −7 profiles/admin.py
  40. +0 −2 profiles/data/__init__.py
  41. +0 −297 profiles/data/choices.py
  42. +0 −19 profiles/forms.py
  43. +0 −95 profiles/models.py
  44. +0 −50 profiles/templates/edit_profile.html
  45. +0 −22 profiles/templates/search_results.html
  46. +0 −36 profiles/templates/view_another_users_profile.html
  47. +0 −46 profiles/templates/view_profile.html
  48. +0 −16 profiles/urls.py
  49. +0 −144 profiles/views.py
  50. +0 −90 settings.py
  51. +0 −84 site_media/css/fivesongs.css
  52. BIN site_media/flash/player_mp3_multi.swf
  53. BIN site_media/images/fivesongs_icon_rss.jpg
  54. BIN site_media/images/fivesongs_icon_twitter.jpg
  55. +0 −20 templates/accounts/login/login.html
  56. +0 −13 templates/admin/base_site.html
  57. +0 −47 templates/base.html
  58. +0 −12 templates/comments/posted.html
  59. +0 −39 templates/comments/preview.html
  60. +0 −12 templates/footer.html
  61. +0 −39 templates/header.html
  62. +0 −10 templates/registration/logged_out.html
  63. +0 −14 templates/registration/password_change_done.html
  64. +0 −26 templates/registration/password_change_form.html
  65. +0 −13 templates/registration/password_reset_complete.html
  66. +0 −33 templates/registration/password_reset_confirm.html
  67. +0 −11 templates/registration/password_reset_done.html
  68. +0 −15 templates/registration/password_reset_email.html
  69. +0 −24 templates/registration/password_reset_form.html
  70. +0 −36 urls.py
View
0 README
No changes.
View
6 README.md
@@ -0,0 +1,6 @@
+fivesongsdaily
+===============
+
+NOTE: As of April 27, 2014, all development on this repository is happening over at GitLab:
+
+https://gitlab.com/mechanicalgirl/fivesongsdaily
View
0 __init__.py
No changes.
View
0 contact/__init__.py
No changes.
View
14 contact/forms.py
@@ -1,14 +0,0 @@
-from django import forms
-
-class ContactForm(forms.Form):
-
- TOPIC_CHOICES = (
- ('', ''),
- ('PLY', 'Suggest A Playlist'),
- ('BUG', 'Report A Bug'),
- ('OTR', 'Other'),
- )
-
- topic = forms.CharField(label='Topics', max_length=3, widget=forms.Select(choices=TOPIC_CHOICES))
- message = forms.CharField(label='Your Message', widget=forms.Textarea, error_messages={'required': 'Please enter a message.'})
-
View
0 contact/models.py
No changes.
View
46 contact/templates/contact.html
@@ -1,46 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}
- : Contact
-{% endblock %}
-
-{% block content_bigcol %}
-
- {% if message %}
-
- <div class="page">
- <div class="page_title">contact</div>
- <div class="page_content">
- <h2>Your message has been sent:</h2>
- {{ message|linebreaks }}
- <br /><br />
- <p><a style="font-weight: bold; text-decoration: none;" href="/">home &rsaquo</a></p>
- </div>
-
- {% else %}
-
- <div class="page">
- <div class="page_title">contact</div>
- <div class="page_content">
-
- <div class="form_box">
- <form action="" method="POST">
- <div class="fieldwrapper">
- {{ form.topic.as_widget }}<br />
- </div>
- <div class="fieldwrapper">
- {{ form.message.as_widget }}<br />
- </div>
- <div class="left"><input type="submit" value="Send" /></div>
- <div class="right">
- {{ form.message.errors }}<br />
- </div>
- <div style="clear: both;"></div>
- </form>
- </div>
-
- </div>
-
- {% endif %}
-
-{% endblock %}
View
7 contact/templates/contact_notification.txt
@@ -1,7 +0,0 @@
-Date: {{ date }}
-
-Sender: {{ sender }}
-
-Message: {{ message }}
-
-
View
10 contact/urls.py
@@ -1,10 +0,0 @@
-from django.conf.urls.defaults import *
-from django.contrib.comments.models import Comment
-from django.views.generic.simple import direct_to_template
-
-from fivesongs.contact import views
-
-urlpatterns = patterns('',
- url(r'^$', views.contact, name='contract_contact'),
-)
-
View
40 contact/views.py
@@ -1,40 +0,0 @@
-import logging
-import datetime
-
-from django.conf import settings
-from django.contrib.sites.models import Site
-from django.http import Http404, HttpResponse, HttpResponseRedirect
-from django.shortcuts import render_to_response
-from django.template import RequestContext
-from django.template.loader import render_to_string
-
-from fivesongs.contact.forms import ContactForm
-
-def contact(request):
- context = {}
- if request.method == 'POST':
- form = ContactForm(request.POST)
- if form.is_valid():
- sender = request.user.email
- topic = form.cleaned_data['topic']
- message = form.cleaned_data['message']
- # request.POST['message']
- from django.core.mail import send_mail
- email_dict = { 'sender': sender, 'message': message, 'date': 'today' }
- email_dict['site'] = Site.objects.get_current()
- subject = "Contact message sent from " + str(email_dict['site']) + " : " + topic
- body = render_to_string('contact_notification.txt', email_dict)
- sent = send_mail(subject, body, settings.ADMIN_EMAIL, [settings.ADMIN_EMAIL])
- # context['sender'] = sender
- context['message'] = message
- # return HttpResponseRedirect('/')
- else:
- form = ContactForm() # An unbound form
-
- logging.debug('*******************')
- logging.debug(form)
-
- context['form'] = form
-
- return render_to_response('contact.html', context, context_instance=RequestContext(request))
-
View
16 feeds.py
@@ -1,16 +0,0 @@
-import datetime
-from time import strftime
-
-from django.contrib.syndication.feeds import Feed
-
-from fivesongs.playlist.models import Playlist
-
-class LatestPlaylists(Feed):
- title = "Five Songs Daily"
- link = "http://www.fivesongsdaily.com/"
- description = "Latest playlists on Five Songs Daily"
-
- def items(self):
- todaysdate = datetime.datetime.now().strftime("%Y-%m-%d")
- return Playlist.objects.filter(active=True).filter(play_date__lte=todaysdate).order_by('-play_date')[:5]
-
View
81 local_settings.py
@@ -1,81 +0,0 @@
-# Django settings for the fivesongs project.
-
-import os, logging
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-ADMINS = (
- ('', ''),
-)
-ADMIN_EMAIL = ''
-MANAGERS = ADMINS
-DEFAULT_FROM_EMAIL = ''
-
-DATABASE_ENGINE = '' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-DATABASE_NAME = '' # Or path to database file if using sqlite3.
-DATABASE_USER = '' # Not used with sqlite3.
-DATABASE_PASSWORD = '' # Not used with sqlite3.
-DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
-DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
-
-TIME_ZONE = 'America/Los Angeles'
-
-LANGUAGE_CODE = 'en-us'
-
-SITE_ID = 1
-
-USE_I18N = False
-
-MEDIA_ROOT = '/fivesongs/site_media/'
-
-MEDIA_URL = '/site_media/'
-
-ADMIN_MEDIA_PREFIX = '/media/'
-
-SECRET_KEY = ''
-
-TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.load_template_source',
- 'django.template.loaders.app_directories.load_template_source',
-# 'django.template.loaders.eggs.load_template_source',
-)
-
-MIDDLEWARE_CLASSES = (
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'middleware.project_logging.LoggingMiddleware',
-)
-
-ROOT_URLCONF = 'fivesongs.urls'
-
-TEMPLATE_DIRS = (
- '/fivesongs/templates/'
-)
-
-INSTALLED_APPS = (
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.admin',
- 'django.contrib.comments',
- 'fivesongs.playlist',
- 'fivesongs.profiles',
- 'fivesongs.pages',
- 'fivesongs.contact',
-)
-
-AKISMET_API_KEY = ''
-
-LOGOUT_URL = '/accounts/login/'
-
-LOGGING_LEVEL = (logging.DEBUG if DEBUG else logging.WARNING)
-LOGGING_LOGFILE = '/logs/'+DATABASE_NAME+'.log'
-LOGGING_FORMAT = "%(asctime)s [%(levelname)s] %(message)s"
-LOGGING_DATEFMT = "%m-%d %H:%M:%S"
-
-logging.basicConfig(level=LOGGING_LEVEL, format=LOGGING_FORMAT,
- datefmt=LOGGING_DATEFMT, filename=LOGGING_LOGFILE, filemode="a")
-
View
11 manage.py
@@ -1,11 +0,0 @@
-#!/usr/bin/env python
-from django.core.management import execute_manager
-try:
- import settings # Assumed to be in the same directory.
-except ImportError:
- import sys
- sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
- sys.exit(1)
-
-if __name__ == "__main__":
- execute_manager(settings)
View
0 message/__init__.py
No changes.
View
6 message/admin.py
@@ -1,6 +0,0 @@
-from fivesongs.message.models import Message
-
-from django.contrib import admin
-
-admin.site.register(Message)
-
View
18 message/models.py
@@ -1,18 +0,0 @@
-import logging
-
-from django.db import models
-from django.http import Http404
-from django.template.loader import render_to_string
-from django.utils.translation import ugettext_lazy as _
-
-class Message(models.Model):
- message = models.TextField()
- active = models.BooleanField(default=True)
- created_at = models.DateTimeField(auto_now_add=True)
-
- def __str__(self):
- return u"%s" %self.message
-
- class Meta:
- ordering = ['-created_at']
-
View
17 message/templates/messages.html
@@ -1,17 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}
- : Messages
-{% endblock %}
-
-{% block content_bigcol %}
-
- {% if messages %}
- {% for message in messages %}
- <p><b>{{ message.created_at|date:"M d, Y" }}</b></p>
- <div class="post_body">{% autoescape off %}{{ message.message|linebreaks }}{% endautoescape %}</div>
- <hr>
- {% endfor %}
- {% endif %}
-
-{% endblock %}
View
8 message/urls.py
@@ -1,8 +0,0 @@
-from django.conf.urls.defaults import *
-
-from fivesongs.message import views
-
-urlpatterns = patterns('',
- url(r'^$', views.show_all, name='messages_all'),
-)
-
View
28 message/views.py
@@ -1,28 +0,0 @@
-import logging
-import datetime
-from time import strftime
-
-from django.conf import settings
-from django.contrib.auth import authenticate
-from django.contrib.auth.decorators import login_required
-from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
-from django.http import Http404, HttpResponse, HttpResponseRedirect
-from django.shortcuts import render_to_response
-from django.template import RequestContext
-from django.template.loader import render_to_string
-
-from fivesongs.message.models import Message
-
-@login_required
-def show_all(request):
- template_name = 'messages.html'
- context = {}
-
- try:
- messages = Message.objects.filter(active=True).order_by('-created_at')[:5]
- except ObjectDoesNotExist:
- messages = None
- context['messages'] = messages
-
- return render_to_response(template_name, context, context_instance=RequestContext(request))
-
View
0 pages/__init__.py
No changes.
View
38 pages/templates/about.html
@@ -1,38 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}
- : About This Site
-{% endblock %}
-
-{% block content_bigcol %}
- {% if user.id %}
-
- <div class="page">
- <div class="page_title">about this site</div>
- <div class="page_content">
- <p>If you're reading this, it's because I know you, or you've been recommended by someone I know.</p>
-
- <p>For a few years now, I've been posting daily playlists privately to my LiveJournal account, and more recently to Facebook. Originally, they started as links to five songs, then I transitioned to a Flex player that could play the songs in your browser but still allow you to download the files.</p>
-
- <p>But posting the links in different formats got a little tedious. So I've decided to move the whole operation to this site.</p>
-
- <p>Make no mistake, this is a private network. I've always tried to be conscious of making the downloads public - I know that there are a lot of music blogs out there that post music freely, but I don't particularly feel like wrangling with the RIAA - or anyone else who takes a dim view of file sharing, for that matter.</p>
-
- <p>So access to this site is always going to be limited to a small group of users. There won't be any open registration - I'll be setting up all the user accounts myself.</p>
-
- <p>Five new songs will be posted daily, but once that day is over, access to the mp3s goes away - you'll still be able to see the list of songs posted on previous days, but the download links will be gone. That may change in the future, but for now I'm imposing that limit so that I don't have to worry about storing a lot of media.</p>
-
- <p>
- And for now, you'll have to log in to get each day's playlist. You can subscribe to the Twitter or rss feeds to get the latest updates.
- Some time in the coming weeks, I'll be adding an optional mailing list to notify you when each day's playlist becomes available (you can join the mailing list by going to your profile and selecting "Email notify")<br />
- </p>
-
- <p>Also coming soon - a little farther in the future - you'll be able to suggest playlists of your own and upload the files to go with them.</p>
-
- <p>For now, just enjoy the music.</p>
-
- </div>
-
- {% endif %}
-{% endblock %}
-
View
8 pages/urls.py
@@ -1,8 +0,0 @@
-from django.conf.urls.defaults import *
-from django.views.generic.simple import direct_to_template
-
-urlpatterns = patterns('django.views.generic.simple',
- (r'^about/$', 'direct_to_template', {'template': 'about.html'}),
- (r'^$', 'direct_to_template', {'template': 'about.html'}),
-)
-
View
0 playlist/__init__.py
No changes.
View
16 playlist/admin.py
@@ -1,16 +0,0 @@
-from fivesongs.playlist.models import Song, Playlist
-
-from django.contrib import admin
-
-class SongAdmin(admin.ModelAdmin):
- list_display = ('title', 'artist', 'user', 'active',)
- list_filter = ('artist',)
-
-class PlaylistAdmin(admin.ModelAdmin):
- list_display = ('play_date', 'user', 'active', 'song1', 'song2', 'song3', 'song4', 'song5',)
- list_filter = ('active', 'user',)
-
-admin.site.register(Playlist, PlaylistAdmin)
-admin.site.register(Song, SongAdmin)
-
-
View
42 playlist/forms.py
@@ -1,42 +0,0 @@
-import logging
-
-from django import forms
-from django.forms import ModelForm
-from django.utils.translation import ugettext_lazy as _
-from django.contrib.auth.models import User
-
-from fivesongs.playlist.models import Song, Playlist, Comment
-
-class CommentForm(ModelForm):
- fullname = forms.CharField(label='Your Name', error_messages={'required': 'Please enter your name.'})
- email = forms.CharField(label='Your Email Address', error_messages={'required': 'Please enter a valid email address.'})
- website = forms.URLField(label='Your Web Site', required=False)
- body = forms.CharField(widget=forms.Textarea, error_messages={'required': 'Please enter a comment.'})
-
- class Meta:
- model = Comment
-
-class SongForm(ModelForm):
- artist = forms.CharField(label='Artist:', widget=forms.TextInput(attrs={' class': 'input_short' }), required=True)
- title = forms.CharField(label='Title:', widget=forms.TextInput(attrs={' class': 'input_short' }), required=True)
- filepath = forms.FileField(label='File:', required=True)
-
- class Meta:
- model = Song
- exclude = ('user',)
-
-class PlaylistForm(ModelForm):
- def clean(self):
- data = {}
- for k,v in self.cleaned_data.iteritems():
- if type(v) is str or type(v) is unicode:
- data[k] = v
- logging.debug('CLEANED DATA ================ %s' %data)
- # if self.cleaned_data['song1'] == (self.cleaned_data['song2'] or self.cleaned_data['song3'] or self.cleaned_data['song4'] or self.cleaned_data['song5']):
- # raise forms.ValidationError(_(u'You have selected the same song more than once - try again.'))
- return self.cleaned_data
-
- class Meta:
- model = Playlist
- exclude = ('user',)
-
View
108 playlist/models.py
@@ -1,108 +0,0 @@
-import logging
-
-from django.db import models
-from django.http import Http404
-from django.template.loader import render_to_string
-from django.utils.translation import ugettext_lazy as _
-from django.contrib.auth.models import User
-
-class Song(models.Model):
- """
- """
- user = models.ForeignKey(User, editable=True)
- artist = models.CharField(max_length=255)
- title = models.CharField(max_length=255)
- album = models.CharField(max_length=255, blank=True)
- filepath = models.FileField('file upload', upload_to='mp3/', blank=True)
- active = models.BooleanField(default=True)
- created_at = models.DateTimeField(auto_now_add=True)
-
- def __str__(self):
- return u"%s - %s" %(self.artist, self.title)
-
- class Meta:
- ordering = ['-created_at']
- unique_together = ("artist", "title",)
-
-class Playlist(models.Model):
- """
- """
- user = models.ForeignKey(User, editable=True)
- play_date = models.DateField()
- song1 = models.ForeignKey(Song, related_name="song1")
- song2 = models.ForeignKey(Song, related_name="song2")
- song3 = models.ForeignKey(Song, related_name="song3")
- song4 = models.ForeignKey(Song, related_name="song4")
- song5 = models.ForeignKey(Song, related_name="song5")
- active = models.BooleanField(default=True)
- created_at = models.DateTimeField(auto_now_add=True)
-
- def __str__(self):
- return u"%s" % self.play_date
-
- def get_absolute_url(self):
- return "/playlist/%s/" % self.id
- # maybe make this by date?
-
- class Meta:
- ordering = ['-play_date']
- unique_together = ("song1", "song2", "song3", "song4", "song5",)
-
-class Comment(models.Model):
- """
- """
- post = models.ForeignKey(Playlist)
- fullname = models.CharField(max_length=60)
- email = models.CharField(max_length=60)
- website = models.URLField(verify_exists=True, max_length=255, blank=True)
- body = models.TextField()
- publish = models.BooleanField(default=True)
- created_at = models.DateTimeField(auto_now_add=True)
-
- def __str__(self):
- return u"%s" % self.id
-
- class Meta:
- ordering = ['-created_at']
-
- # custom method to clean website - make sure it's a complete url
-
-from django.contrib.comments.signals import comment_was_posted
-
-def on_comment_was_posted(sender, comment, request, *args, **kwargs):
- # spam checking can be enabled/disabled per the comment's target Model
- #if comment.content_type.model_class() != Entry:
- # return
-
- from django.contrib.sites.models import Site
- from django.conf import settings
-
- try:
- from akismet import Akismet
- except:
- return
-
- ak = Akismet(
- key=settings.AKISMET_API_KEY,
- blog_url='http://%s/' % Site.objects.get(pk=settings.SITE_ID).domain
- )
-
- if ak.verify_key():
- data = {
- 'user_ip': request.META.get('REMOTE_ADDR', '127.0.0.1'),
- 'user_agent': request.META.get('HTTP_USER_AGENT', ''),
- 'referrer': request.META.get('HTTP_REFERER', ''),
- 'comment_type': 'comment',
- 'comment_author': comment.user_name.encode('utf-8'),
- }
-
- if ak.comment_check(comment.comment.encode('utf-8'), data=data, build_data=True):
- comment.flags.create(
- user=comment.content_object.author,
- flag='spam'
- )
- comment.is_public = False
- comment.save()
-
-comment_was_posted.connect(on_comment_was_posted)
-
View
18 playlist/templates/archive.html
@@ -1,18 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}
- : All Entries
-{% endblock %}
-
-{% block content_bigcol %}
- {% include "archive_list.html" %}
-
- <div class="pagination" align="center">
- {% for page in page_range %}
- <a href="/playlist/all/?page={{ page }}">{{ page }}</a>
- {% endfor %}
- </div>
-
-{% endblock %}
-
-
View
21 playlist/templates/archive_list.html
@@ -1,21 +0,0 @@
- {% load comments %}
-
- {% if all_playlists %}
- <b>Here's what you missed yesterday, and the day before that, and the day before that ...</b><br />
- {% for playlist in all_playlists %}
- <div class="archive_list">
- <p>
- <a class="playlist_date" href="/id/{{ playlist.id }}">Playlist for {{ playlist.play_date|date:"M d, Y" }}</a>
- <a class="comment_count" href="/id/{{ playlist.id }}">{% get_comment_count for playlist as comment_count %} ({{ comment_count }} comments)</a><br />
- {{ playlist.song1 }}<br />
- {{ playlist.song2 }}<br />
- {{ playlist.song3 }}<br />
- {{ playlist.song4 }}<br />
- {{ playlist.song5 }}<br />
- </p>
- </div>
- {% endfor %}
- {% else %}
- No playlists yet.<br />
- {% endif %}
-
View
20 playlist/templates/home.html
@@ -1,20 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}
- : All Entries
-{% endblock %}
-
-{% block page_js %}
- <script language="javascript" src="/site_media/js/jquery-1.3.2.js"></script>
- <script language="javascript" src="/site_media/js/postComment.js"></script>
-{% endblock %}
-
-{% block content_bigcol %}
- {% include "main_player.html" %}
-{% endblock %}
-
-{% block content_smallcol %}
- {% include "archive_list.html" %}
- <div class="more"><a href="/playlist/all/">all the rest &rsaquo</a></div>
-{% endblock %}
-
View
33 playlist/templates/main_player.html
@@ -1,33 +0,0 @@
- {% if playlist %}
-
- <div align="center">
- <div style="margin-bottom: 20px; width: 450px;">
-
- <p><b>Today's Playlist ({{ playlist.play_date|date:"M d Y" }}) : </b></p>
-
- <div style="float: left">
- <object type="application/x-shockwave-flash" data="/site_media/flash/player_mp3_multi.swf" width="400" height="100" align="center">
- <param name="movie" value="/site_media/flash/player_mp3_multi.swf" />
- <param name="FlashVars" value="mp3=/site_media/{{ playlist.song1.filepath }}|/site_media/{{ playlist.song2.filepath }}|/site_media/{{ playlist.song3.filepath }}|/site_media/{{ playlist.song4.filepath }}|/site_media/{{ playlist.song5.filepath }}&amp;title={{ playlist.song1 }}|{{ playlist.song2 }}|{{ playlist.song3 }}|{{ playlist.song4 }}|{{ playlist.song5 }}&amp;width=400&amp;height=100" />
- </object>
- </div>
-
- <div style="float: left; width: 50px;">
- <a href="/site_media/{{ playlist.song1.filepath }}" title="right click to download">mp3</a><br />
- <a href="/site_media/{{ playlist.song2.filepath }}" title="right click to download">mp3</a><br />
- <a href="/site_media/{{ playlist.song3.filepath }}" title="right click to download">mp3</a><br />
- <a href="/site_media/{{ playlist.song4.filepath }}" title="right click to download">mp3</a><br />
- <a href="/site_media/{{ playlist.song5.filepath }}" title="right click to download">mp3</a><br />
- </div>
- <div class="clear"></div>
-
- <p>Posted by: <a href="/profile/{{ playlist.user }}/">{{ playlist.user }}</a></p>
-
- </div></div>
-
- {% include "playlist_comments.html" %}
-
- {% else %}
- No playlist today.<br />
- {% endif %}
-
View
6 playlist/templates/message_notification.txt
@@ -1,6 +0,0 @@
-New playlist uploaded by: {{ user }}
-
-Approve or delete here: http://{{ site }}/admin/playlist/playlist/?user__id__exact={{ user_id }}&active__exact=0
-
-
-
View
51 playlist/templates/playlist_comments.html
@@ -1,51 +0,0 @@
-
- {% load comments %}
-
- {% get_comment_form for playlist as form %}
- <div id="comment_form">
- <form action="{% comment_form_target %}" method="POST">
- {{ form.comment }}
- <p style="display: none;">{{ form.honeypot }}</p>
- {{ form.content_type }}
- {{ form.object_pk }}
- {{ form.timestamp }}
- {{ form.security_hash }}
- <p><input type="submit" value="Leave a comment" id="id_submit" /></p>
- </form>
- </div>
-
- {% get_comment_list for playlist as cmt_list %}
- {% for comment in cmt_list %}
- <div class="comment">
- <a href="/profile/{{ comment.user.username }}/">{{ comment.user.username }}</a><br />
- {{ comment.submit_date|date:"M d Y" }}
- {{ comment.comment|linebreaks }}
- </div>
- {% endfor %}
-
- <script type="text/javascript" charset="utf-8">
- function bindPostCommentHandler() {
- $('#comment_form form input.submit-preview').remove();
- $('#comment_form form').submit(function() {
- $.ajax({
- type: "POST",
- data: $('#comment_form form').serialize(),
- url: "{% comment_form_target %}",
- cache: false,
- dataType: "html",
- success: function(html, textStatus) {
- $('#comment_form form').replaceWith('Your comment was posted successfully. Reload the page to see it.');
- bindPostCommentHandler();
- },
- error: function (XMLHttpRequest, textStatus, errorThrown) {
- $('#comment_form form').replaceWith('Your comment was unable to be posted at this time. We apologise for the inconvenience.');
- }
- });
- return false;
- });
- }
- $(document).ready(function() {
- bindPostCommentHandler();
- });
- </script>
-
View
91 playlist/templates/playlist_save.html
@@ -1,91 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}
- : Add Your Own Playlist
-{% endblock %}
-
-{% block content_bigcol %}
-
- <div class="page_title">{{ user.username }}'s playlists</div>
-
- <table>
- <tr valign="top">
- <form enctype="multipart/form-data" action="/playlist/song/add/" method="post">
- <td>{{ form_song.artist.label }} {{ form_song.artist }}</td>
- <td>{{ form_song.title.label }} {{ form_song.title }}</td>
- <td>{{ form_song.filepath.label }} {{ form_song.filepath }}</td>
- <td>
- <input type="submit" value="Add a song" /><br />
- (Limit: 5 MB)<br />
- </td>
- </form>
- </tr>
- </table>
- <div class="right" style="color: red;">
- {% if upload_message %}{{ upload_message }}{% endif %}
- {% if form_song.non_field_errors %}<ul class="errorlist">{{ form_song.non_field_errors.as_ul }}</ul>{% endif %}
- {% if form_song.field.errors %}<ul class="errorlist">{{ form_song.field.errors.as_ul }}</ul>{% endif %}
- </div>
- <div class="clear"></div>
-
- <br /><br />
-
- {% if song_select %}
- <!-- ({{ song_select|length }} songs available)<br /> -->
- <form name="user_playlist" action="." method="post">
- <label><b>Song 1:</b></label> <select name="song1" id="id_song1"><option value="" selected="selected">---------</option>
- {% for song in song_select %}<option value="{{ song.id }}">{{ song }}</option>{% endfor %}
- </select><br />
-
- <label><b>Song 2:</b></label> <select name="song2" id="id_song2"><option value="" selected="selected">---------</option>
- {% for song in song_select %}<option value="{{ song.id }}">{{ song }}</option>{% endfor %}
- </select><br />
-
- <label><b>Song 3:</b></label> <select name="song3" id="id_song3"><option value="" selected="selected">---------</option>
- {% for song in song_select %}<option value="{{ song.id }}">{{ song }}</option>{% endfor %}
- </select><br />
-
- <label><b>Song 4:</b></label> <select name="song4" id="id_song4"><option value="" selected="selected">---------</option>
- {% for song in song_select %}<option value="{{ song.id }}">{{ song }}</option>{% endfor %}
- </select><br />
-
- <label><b>Song 5:</b></label> <select name="song5" id="id_song5"><option value="" selected="selected">---------</option>
- {% for song in song_select %}<option value="{{ song.id }}">{{ song }}</option>{% endfor %}
- </select><br />
-
- <input type="hidden" name="user" id="id_user" value="{{ user.id }}">
- <input type="hidden" name="active" id="id_active" value="False">
- <input type="hidden" name="play_date" id="id_play_date" value="1999-01-01">
- <p align="center"><input type="submit" value="Save Your Playlist" /></p>
- </form>
- <div class="right" style="color: red;">
- {% if form.non_field_errors %}<ul class="errorlist">{{ form.non_field_errors.as_ul }}</ul>{% endif %}
- {% if form.field.errors %}<ul class="errorlist">{{ form.field.errors.as_ul }}</ul>{% endif %}
- </div>
- <div class="clear"></div>
-
- {% endif %}
-
- {% if user_queued_playlists %}
- <br /><br />
- <b>Your queued playlists:</b><br />
- {% for playlist in user_queued_playlists %}
- <p><b>{{ playlist.play_date }}</b> | {{ playlist.song1 }} | {{ playlist.song2 }} | {{ playlist.song3 }} | {{ playlist.song4 }} | {{ playlist.song5 }}<br />
- {% endfor %}
- {% endif %}
- {% if user_pending_playlists %}
- <br /><br />
- <b>Your playlists pending review:</b><br />
- {% for playlist in user_pending_playlists %}
- <p>{{ playlist.song1 }} | {{ playlist.song2 }} | {{ playlist.song3 }} | {{ playlist.song4 }} | {{ playlist.song5 }}<br />
- {% endfor %}
- {% endif %}
-
-{% endblock %}
-
-{% block content_smallcol %}
-
- <p>(upload requirements)</p>
-
-{% endblock %}
-
View
52 playlist/templates/single.html
@@ -1,52 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}
- : Single Entry
-{% endblock %}
-
-{% block page_js %}
- <script language="javascript" src="/site_media/js/jquery-1.3.2.js"></script>
-{% endblock %}
-
-{% block content_bigcol %}
-
- {% if playlist %}
-
- <div align="center"><div style="text-align: left; margin-bottom: 20px; width: 450px;">
-
- <p><b>Playlist for {{ playlist.play_date|date:"M d, Y" }}:</b></p>
-
- <div style="float: left">
- {{ playlist.song1 }}<br />
- {{ playlist.song2 }}<br />
- {{ playlist.song3 }}<br />
- {{ playlist.song4 }}<br />
- {{ playlist.song5 }}<br />
- </div>
-
- {% if playlist.song1.filepath %}
- <div style="float: left; margin-left: 50px; width: 50px;">
- <a href="/site_media/{{ playlist.song1.filepath }}" title="right click to download">mp3</a><br />
- <a href="/site_media/{{ playlist.song2.filepath }}" title="right click to download">mp3</a><br />
- <a href="/site_media/{{ playlist.song3.filepath }}" title="right click to download">mp3</a><br />
- <a href="/site_media/{{ playlist.song4.filepath }}" title="right click to download">mp3</a><br />
- <a href="/site_media/{{ playlist.song5.filepath }}" title="right click to download">mp3</a><br />
- </div>
- {% endif %}
-
- <div class="clear"></div>
-
- </div></div>
-
- {% include "playlist_comments.html" %}
-
- {% else %}
- <div>No entries available.</div>
- {% endif %}
-
-{% endblock %}
-
-{% block content_smallcol %}
- {% include "archive_list.html" %}
-{% endblock %}
-
View
27 playlist/templates/song_add.html
@@ -1,27 +0,0 @@
-{% block page_title %}
- : Add A Song
-{% endblock %}
-
-{% block content_bigcol %}
-
- <table>
- <tr valign="top">
- <form enctype="multipart/form-data" action="/playlist/song/add/" method="post">
- <td>{{ form.artist.label }} {{ form.artist }}</td>
- <td>{{ form.title.label }} {{ form.title }}</td>
- <td>{{ form.filepath.label }} {{ form.filepath }}</td>
- <td valign="bottom">
- <input type="hidden" name="song_number" id="id_song_number" value="">
- <input type="submit" value="Add song" /><br />
- </td>
- </form>
- </tr>
- </table>
-
- <form name="user_playlist" action="." method="post">
-
- <p align="center"><input type="submit" value="Save Your Playlist" /><br /></p>
- </form>
-
-{% endblock %}
-
View
20 playlist/urls.py
@@ -1,20 +0,0 @@
-from django.conf.urls.defaults import *
-from django.contrib.comments.models import Comment
-from django.views.generic.simple import direct_to_template
-
-from fivesongs.playlist import views
-
-urlpatterns = patterns('',
- url(r'^preview/(?P<id>\w+)/*$', views.preview_id, name='playlist_previewbyid'),
-
- url(r'^id/(?P<id>\w+)/*$', views.show_id, name='playlist_byid'),
- url(r'^all/*$', views.show_all, name='playlist_all'),
-
- url(r'^song/add/*$', views.user_song_upload, name='playlist_song_upload'),
- url(r'^upload/(?P<message>.*?)/*$', views.user_playlist, name='playlist_save'),
- url(r'^upload/*$', views.user_playlist, name='playlist_save'),
-
- url(r'^(?P<id>\w+)/*$', views.show_id, name='playlist_byid'),
- url(r'^$', views.show_home, name='playlist_home'),
-)
-
View
207 playlist/views.py
@@ -1,207 +0,0 @@
-import logging
-import time, datetime
-from datetime import timedelta
-from time import strftime
-
-from django.conf import settings
-from django.contrib.auth import authenticate
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth.models import User
-from django.contrib.sites.models import Site
-from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
-from django.http import Http404, HttpResponse, HttpResponseRedirect
-from django.shortcuts import render_to_response
-from django.template import RequestContext
-from django.template.loader import render_to_string
-
-from fivesongs.playlist.models import Song, Playlist
-from fivesongs.playlist.forms import SongForm, PlaylistForm
-from fivesongs.profiles.models import Avatar
-
-@login_required
-def show_home(request):
- template_name = 'home.html'
- context = {}
-
- todaysdate = datetime.datetime.now().strftime("%Y-%m-%d")
- try:
- playlist = Playlist.objects.get(play_date=todaysdate, active=True)
- except ObjectDoesNotExist:
- playlist = None
-
- try:
- all_playlists = Playlist.objects.filter(active=True).filter(play_date__lt=todaysdate).order_by('-play_date')[:4]
- except ObjectDoesNotExist:
- all_playlists = None
-
- context['playlist'] = playlist
- context['all_playlists'] = all_playlists
-
- return render_to_response(template_name, context, context_instance=RequestContext(request))
-
-@login_required
-def show_all(request):
- template_name = 'archive.html'
- context = {}
- per_page = 5
- page = int(request.GET.get('page', '1'))
-
- todaysdate = datetime.datetime.now().strftime("%Y-%m-%d")
- try:
- all_playlists = Playlist.objects.filter(active=True).filter(play_date__lt=todaysdate).order_by('-play_date')
- except ObjectDoesNotExist:
- all_playlists = None
-
- total_entries = all_playlists.count()
- total_pages = (total_entries/per_page)+1
- context['page_range'] = range(1, total_pages+1)
-
- offset = (page * per_page) - per_page
- limit = offset + per_page
- all_playlists = all_playlists[offset:limit]
- context['all_playlists'] = all_playlists
- context['today'] = todaysdate
-
- return render_to_response(template_name, context, context_instance=RequestContext(request))
-
-@login_required
-def show_id(request, id):
- """
- """
- template_name = 'single.html'
- context = {}
-
- todaysdate = datetime.date.today()
- try:
- playlist = Playlist.objects.get(pk=id, active=True, play_date__lt=todaysdate)
- last_week = todaysdate - datetime.timedelta(days=7)
- if playlist.play_date < last_week:
- playlist.song1.filepath = None
- playlist.song2.filepath = None
- playlist.song3.filepath = None
- playlist.song4.filepath = None
- playlist.song5.filepath = None
- except ObjectDoesNotExist:
- playlist = None
- return HttpResponseRedirect('/')
-
- if playlist.play_date == todaysdate:
- template_name = 'home.html'
-
- try:
- all_playlists = Playlist.objects.filter(active=True).filter(play_date__lt=todaysdate).order_by('-play_date')[:4]
- except ObjectDoesNotExist:
- all_playlists = None
-
- if request.method == 'POST': # new comment
- form = form(request.POST)
- logging.debug('form')
- logging.debug(form)
- if form.is_valid():
- form.post_id = entry.id
- form.publish = True
- form.save()
- return HttpResponseRedirect('/id/%s/' % entry.id)
- else:
- form = form()
-
- context['today'] = todaysdate
- context['playlist'] = playlist
- context['all_playlists'] = all_playlists
- return render_to_response(template_name, context, context_instance=RequestContext(request))
-
-@login_required
-def preview_id(request, id):
- template_name = 'preview.html'
- context = {}
-
- if request.user.is_superuser:
- try:
- playlist = Playlist.objects.get(pk=id, active=True)
- except ObjectDoesNotExist:
- playlist = None
- return HttpResponseRedirect('/')
- else:
- playlist = None
- return HttpResponseRedirect('/')
-
- context['playlist'] = playlist
- return render_to_response(template_name, context, context_instance=RequestContext(request))
-
-@login_required
-def user_song_upload(request):
- template_name = 'song_add.html'
- context = {}
- form_class = SongForm
- if request.method == 'POST':
- form = form_class(request.POST, request.FILES)
- logging.debug('******* file size ***********')
- logging.debug(request.FILES['filepath'].size)
- if request.FILES['filepath'].size < 5457045 and form.is_valid():
- song = form.save(commit=False)
- song.active = True
- song.user = request.user
- song.save()
- return HttpResponseRedirect('/playlist/upload/success/')
- else:
- logging.debug('********* file too large ***************')
- return HttpResponseRedirect('/playlist/upload/error/')
-
-@login_required
-def user_playlist(request, message):
- template_name = 'playlist_save.html'
- context = {}
- form_class = PlaylistForm
-
- try:
- user_songs = Song.objects.filter(user=request.user)
- except ObjectDoesNotExist:
- user_songs = None
- context['song_select'] = user_songs
-
- try:
- user_pending_playlists = Playlist.objects.filter(user=request.user, active=False).order_by('-created_at')
- except ObjectDoesNotExist:
- user_pending_playlists = None
- context['user_pending_playlists'] = user_pending_playlists
-
- try:
- user_queued_playlists = Playlist.objects.filter(user=request.user, active=True).order_by('-created_at')
- except ObjectDoesNotExist:
- user_queued_playlists = None
- context['user_queued_playlists'] = user_queued_playlists
-
- if request.method == 'POST':
- form = form_class(request.POST)
- if form.is_valid():
- playlist = form.save(commit=False)
- playlist.active = False
- playlist.user = request.user
- playlist.save()
-
- # notify admin of new list
- from django.core.mail import send_mail
- email_dict = {'user': request.user, 'user_id': request.user.id}
- email_dict['site'] = Site.objects.get_current()
- subject = "New playlist uploaded by " + request.user.username
- body = render_to_string('message_notification.txt', email_dict)
- sent = send_mail(subject, body, settings.ADMIN_EMAIL, [settings.ADMIN_EMAIL])
- logging.debug('SENT: %s' %sent)
-
- else:
- logging.debug('*************** not valid %s', form.errors)
- else:
- form = form_class()
-
- context['form'] = form
- context['form_song'] = SongForm()
-
- if message == 'error':
- context['upload_message'] = 'Sorry, that file was too large.'
- if message == 'success':
- context['upload_message'] = 'Your file was uploaded successfully.'
- logging.debug('*************** message **************')
- logging.debug(message)
-
- return render_to_response(template_name, context, context_instance=RequestContext(request))
-
View
0 profiles/__init__.py
No changes.
View
7 profiles/admin.py
@@ -1,7 +0,0 @@
-from fivesongs.profiles.models import UserProfile, Avatar
-
-from django.contrib import admin
-
-admin.site.register(UserProfile)
-admin.site.register(Avatar)
-
View
2 profiles/data/__init__.py
@@ -1,2 +0,0 @@
-
-
View
297 profiles/data/choices.py
@@ -1,297 +0,0 @@
-states = (
- (" ", " "),
- ("AL" , "ALABAMA"),
- ("AK" , "ALASKA"),
- ("AZ" , "ARIZONA"),
- ("AR" , "ARKANSAS"),
- ("CA" , "CALIFORNIA"),
- ("CO" , "COLORADO"),
- ("CT" , "CONNECTICUT"),
- ("DE" , "DELAWARE"),
- ("DC" , "DISTRICT OF COLUMBIA"),
- ("FL" , "FLORIDA"),
- ("GA" , "GEORGIA"),
- ("HI" , "HAWAII"),
- ("ID" , "IDAHO"),
- ("IL" , "ILLINOIS"),
- ("IN" , "INDIANA"),
- ("IA" , "IOWA"),
- ("KS" , "KANSAS"),
- ("KY" , "KENTUCKY"),
- ("LA" , "LOUISIANA"),
- ("ME" , "MAINE"),
- ("MD" , "MARYLAND"),
- ("MA" , "MASSACHUSETTS"),
- ("MI" , "MICHIGAN"),
- ("MN" , "MINNESOTA"),
- ("MS" , "MISSISSIPPI"),
- ("MO" , "MISSOURI"),
- ("MT" , "MONTANA"),
- ("NE" , "NEBRASKA"),
- ("NV" , "NEVADA"),
- ("NH" , "NEW HAMPSHIRE"),
- ("NJ" , "NEW JERSEY"),
- ("NM" , "NEW MEXICO"),
- ("NY" , "NEW YORK"),
- ("NC" , "NORTH CAROLINA"),
- ("ND" , "NORTH DAKOTA"),
- ("OH" , "OHIO"),
- ("OK" , "OKLAHOMA"),
- ("OR" , "OREGON"),
- ("PA" , "PENNSYLVANIA"),
- ("RI" , "RHODE ISLAND"),
- ("SC" , "SOUTH CAROLINA"),
- ("SD" , "SOUTH DAKOTA"),
- ("TN" , "TENNESSEE"),
- ("TX" , "TEXAS"),
- ("UT" , "UTAH"),
- ("VT" , "VERMONT"),
- ("VA" , "VIRGINIA"),
- ("WA" , "WASHINGTON"),
- ("WV" , "WEST VIRGINIA"),
- ("WI" , "WISCONSIN"),
- ("WY" , "WYOMING"),
-)
-
-countries = (
- ("", "Select a country"),
- ("AF", "AFGHANISTAN"),
- ("AL", "ALBANIA"),
- ("DZ", "ALGERIA"),
- ("AS", "AMERICAN SAMOA"),
- ("AD", "ANDORRA"),
- ("AO", "ANGOLA"),
- ("AI", "ANGUILLA"),
- ("AQ", "ANTARCTICA"),
- ("AG", "ANTIGUA AND BARBUDA"),
- ("AZ", "AZERBAIJAN"),
- ("AR", "ARGENTINA"),
- ("AM", "ARMENIA"),
- ("AW", "ARUBA"),
- ("AU", "AUSTRALIA"),
- ("AT", "AUSTRIA"),
- ("BS", "BAHAMAS"),
- ("BH", "BAHRAIN"),
- ("BD", "BANGLADESH"),
- ("BB", "BARBADOS"),
- ("BE", "BELGIUM"),
- ("BZ", "BELIZE"),
- ("BY", "BELARUS"),
- ("BJ", "BENIN"),
- ("BM", "BERMUDA"),
- ("BT", "BHUTAN"),
- ("BO", "BOLIVIA"),
- ("BA", "BOSNIA AND HERZEGOWI"),
- ("BW", "BOTSWANA"),
- ("BV", "BOUVET ISLAND"),
- ("BR", "BRAZIL"),
- ("IO", "BRITISH INDIAN OCEAN"),
- ("BN", "BRUNEI DARUSSALAM"),
- ("BG", "BULGARIA"),
- ("BF", "BURKINA FASO"),
- ("BI", "BURUNDI"),
- ("CA", "CANADA"),
- ("KH", "CAMBODIA"),
- ("CM", "CAMEROON"),
- ("CV", "CAPE VERDE"),
- ("CF", "CENTRAL AFRICAN REPU"),
- ("TD", "CHAD"),
- ("CL", "CHILE"),
- ("CN", "CHINA"),
- ("CX", "CHRISTMAS ISLAND"),
- ("CC", "COCOS (KEELING) ISLA"),
- ("CO", "COLOMBIA"),
- ("KM", "COMOROS"),
- ("CG", "CONGO"),
- ("CD", "CONGO, THE DEMOCRATI"),
- ("CK", "COOK ISLANDS"),
- ("CR", "COSTA RICA"),
- ("CI", "COTE D&#39;IVOIRE"),
- ("HR", "CROATIA (localname:H"),
- ("CU", "CUBA"),
- ("CY", "CYPRUS"),
- ("CZ", "CZECH REPUBLIC"),
- ("DE", "GERMANY"),
- ("DK", "DENMARK"),
- ("DJ", "DJIBOUTI"),
- ("DM", "DOMINICA"),
- ("DO", "DOMINICAN REPUBLIC"),
- ("TP", "EAST TIMOR"),
- ("EC", "ECUADOR"),
- ("EG", "EGYPT"),
- ("SV", "ELSALVADOR"),
- ("GQ", "EQUATORIAL GUINEA"),
- ("ER", "ERITREA"),
- ("EE", "ESTONIA"),
- ("ET", "ETHIOPIA"),
- ("FK", "FALKLAND ISLANDS (MA"),
- ("FO", "FAROE ISLANDS"),
- ("FJ", "FIJI"),
- ("FI", "FINLAND"),
- ("FR", "FRANCE"),
- ("FX", "FRANCE, METROPOLITAN"),
- ("TF", "FRENCH SOUTHERN TERR"),
- ("GF", "FRENCH GUIANA"),
- ("PF", "FRENCH POLYNESIA"),
- ("GM", "GAMBIA"),
- ("GE", "GEORGIA"),
- ("GA", "GABON"),
- ("GH", "GHANA"),
- ("GI", "GIBRALTAR"),
- ("GL", "GREENLAND"),
- ("GR", "GREECE"),
- ("GD", "GRENADA"),
- ("GP", "GUADELOUPE"),
- ("GT", "GUATEMALA"),
- ("GU", "GUAM"),
- ("GN", "GUINEA"),
- ("GW", "GUINEA-BISSAU"),
- ("GY", "GUYANA"),
- ("HT", "HAITI"),
- ("HM", "HEARD AND MCDONALD I"),
- ("HK", "HONGKONG"),
- ("HN", "HONDURAS"),
- ("HU", "HUNGARY"),
- ("IS", "ICELAND"),
- ("ID", "INDONESIA"),
- ("IL", "ISRAEL"),
- ("IN", "INDIA"),
- ("IQ", "IRAQ"),
- ("IR", "IRAN (ISLAMIC REPUBL"),
- ("IE", "IRELAND"),
- ("IT", "ITALY"),
- ("JM", "JAMAICA"),
- ("JP", "JAPAN"),
- ("JO", "JORDAN"),
- ("KZ", "KAZAKHSTAN"),
- ("KE", "KENYA"),
- ("KY", "CAYMAN ISLANDS"),
- ("KI", "KIRIBATI"),
- ("KP", "KOREA, DEMOCRATIC PE"),
- ("KR", "KOREA, REPUBLIC OF"),
- ("KW", "KUWAIT"),
- ("KG", "KYRGYZSTAN"),
- ("LV", "LATVIA"),
- ("LB", "LEBANON"),
- ("LS", "LESOTHO"),
- ("LY", "LIBYAN ARAB JAMAHIRI"),
- ("LR", "LIBERIA"),
- ("LI", "LIECHTENSTEIN"),
- ("LT", "LITHUANIA"),
- ("LA", "LAO PEOPLE&#39;S DEM"),
- ("LU", "LUXEMBOURG"),
- ("MK", "MACEDONIA, THE FORME"),
- ("MG", "MADAGASCAR"),
- ("ML", "MALI"),
- ("MW", "MALAWI"),
- ("MV", "MALDIVES"),
- ("MY", "MALAYSIA"),
- ("MD", "MOLDOVA, REPUBLIC OF"),
- ("MQ", "MARTINIQUE"),
- ("MH", "MARSHALL ISLANDS"),
- ("MP", "NORTHERN MARIANA ISL"),
- ("MA", "MOROCCO"),
- ("MR", "MAURITANIA"),
- ("MU", "MAURITIUS"),
- ("YT", "MAYOTTE"),
- ("MX", "MEXICO"),
- ("FM", "MICRONESIA, FEDERATE"),
- ("MN", "MONGOLIA"),
- ("MO", "MACAU"),
- ("MS", "MONTSERRAT"),
- ("MT", "MALTA"),
- ("MC", "MONACO"),
- ("MZ", "MOZAMBIQUE"),
- ("MM", "MYANMAR"),
- ("NR", "NAURU"),
- ("NE", "NIGER"),
- ("NP", "NEPAL"),
- ("NL", "NETHERLANDS"),
- ("AN", "NETHERLANDS ANTILLES"),
- ("NF", "NORFOLK ISLAND"),
- ("NZ", "NEW ZEALAND"),
- ("NI", "NICARAGUA"),
- ("NG", "NIGERIA"),
- ("NU", "NIUE"),
- ("NO", "NORWAY"),
- ("NC", "NEW CALEDONIA"),
- ("NA", "NAMIBIA"),
- ("OM", "OMAN"),
- ("PK", "PAKISTAN"),
- ("PS", "PALESTINIAN TERRITOR"),
- ("PW", "PALAU"),
- ("PG", "PAPUA NEW GUINEA"),
- ("PY", "PARAGUAY"),
- ("PA", "PANAMA"),
- ("PH", "PHILIPPINES"),
- ("PN", "PITCAIRN"),
- ("PL", "POLAND"),
- ("PT", "PORTUGAL"),
- ("PE", "PERU"),
- ("PR", "PUERTO RICO"),
- ("QA", "QATAR"),
- ("RE", "REUNION"),
- ("RO", "ROMANIA"),
- ("RU", "RUSSIAN FEDERATION"),
- ("RW", "RWANDA"),
- ("VC", "SAINT VINCENT AND TH"),
- ("LC", "SAINT LUCIA"),
- ("KN", "SAINT KITTS AND NEVI"),
- ("WS", "SAMOA"),
- ("SM", "SANMARINO"),
- ("ST", "SAO TOME AND PRINCIP"),
- ("SK", "SLOVAKIA (Slovak Rep"),
- ("SA", "SAUDI ARABIA"),
- ("SN", "SENEGAL"),
- ("SL", "SIERRA LEONE"),
- ("SG", "SINGAPORE"),
- ("SI", "SLOVENIA"),
- ("SB", "SOLOMON ISLANDS"),
- ("SO", "SOMALIA"),
- ("ZA", "SOUTH AFRICA"),
- ("GS", "SOUTH GEORGIA AND TH"),
- ("SC", "SEYCHELLES"),
- ("SD", "SUDAN"),
- ("ES", "SPAIN"),
- ("LK", "SRI LANKA"),
- ("PM", "ST.PIERRE AND MIQUEL"),
- ("SH", "ST.HELENA"),
- ("SR", "SURINAME"),
- ("SJ", "SVALBARD AND JANMAYE"),
- ("SZ", "SWAZILAND"),
- ("SE", "SWEDEN"),
- ("CH", "SWITZERLAND"),
- ("SY", "SYRIAN ARAB REPUBLIC"),
- ("TW", "TAIWAN"),
- ("TJ", "TAJIKISTAN"),
- ("TZ", "TANZANIA, UNITED REP"),
- ("TN", "TUNISIA"),
- ("TH", "THAILAND"),
- ("TG", "TOGO"),
- ("TK", "TOKELAU"),
- ("TO", "TONGA"),
- ("TT", "TRINIDAD AND TOBAGO"),
- ("TR", "TURKEY"),
- ("TC", "TURKS AND CAICOS ISL"),
- ("TM", "TURKMENISTAN"),
- ("TV", "TUVALU"),
- ("UG", "UGANDA"),
- ("UA", "UKRAINE"),
- ("GB", "UNITED KINGDOM"),
- ("AE", "UNITED ARAB EMIRATES"),
- ("UM", "UNITED STATES OUTLYI"),
- ("UY", "URUGUAY"),
- ("UZ", "UZBEKISTAN"),
- ("VU", "VANUATU"),
- ("VE", "VENEZUELA"),
- ("VN", "VIETNAM"),
- ("VI", "VIRGIN ISLANDS (U.S."),
- ("VG", "VIRGIN ISLANDS (BRIT"),
- ("VA", "HOLY SEE (VATICAN CI"),
- ("WF", "WALLIS AND FUTUNA IS"),
- ("EH", "WESTERN SAHARA"),
- ("YE", "YEMEN"),
- ("YU", "YUGOSLAVIA"),
- ("ZM", "ZAMBIA"),
- ("ZW", "ZIMBABWE"),
-)
View
19 profiles/forms.py
@@ -1,19 +0,0 @@
-from django import forms
-from django.forms import ModelForm
-from django.utils.translation import ugettext_lazy as _
-from django.contrib.auth.models import User
-
-from fivesongs.profiles.models import UserProfile
-from fivesongs.profiles.data.choices import states, countries
-
-class UserProfileForm(ModelForm):
- state = forms.ChoiceField(label='State', widget=forms.Select(attrs={'class':'input_select'}), choices=states, required=False)
- country = forms.ChoiceField(label='Country', widget=forms.Select(attrs={'class':'input_select'}), choices=countries, required=False)
- favorite_bands = forms.CharField(label='Favorite bands (comma-separated)', widget=forms.widgets.Textarea(), required=False)
-
- class Meta:
- model = UserProfile
- exclude = ('user',)
-
-
-
View
95 profiles/models.py
@@ -1,95 +0,0 @@
-import datetime
-import os.path
-try:
- from PIL import Image, ImageFilter
-except ImportError:
- import Image, ImageFilter
-
-from django.db import models
-from django.http import Http404
-from django.template.loader import render_to_string
-from django.utils.translation import ugettext_lazy as _
-from django.contrib.auth.models import User
-from django.core.files.storage import default_storage
-
-class UserProfile(models.Model):
- """
- user contact data, photo, personal information, etc.
- A basic profile which stores user information after the account has been activated.
- Use this model as the value of the ``AUTH_PROFILE_MODULE`` setting
- """
- user = models.ForeignKey(User, editable=False)
- first_name = models.CharField(max_length=40, blank=True)
- last_name = models.CharField(max_length=40, blank=True)
- city = models.CharField(max_length=100, blank=True)
- state = models.CharField(max_length=2, blank=True)
- zip_code = models.IntegerField(max_length=5, blank=True, null=True)
- twitter = models.CharField(max_length=100, blank=True)
- livejournal = models.CharField(max_length=100, blank=True)
- website = models.CharField(max_length=100, blank=True)
- favorite_bands = models.TextField("Favorite bands", blank=True)
- email_notify = models.BooleanField(default=False)
- visible = models.BooleanField(default=False)
- active = models.BooleanField(default=False)
- created_at = models.DateTimeField(auto_now_add=True)
-
- def __unicode__(self):
- return u"%s %s" % (self.first_name, self.last_name)
-
- def fullname(self):
- return "%s, %s" %(self.last_name,self.first_name)
- fullname.short_description = 'Full Name'
-
- class Meta:
- ordering = ['last_name']
-
-
-class Avatar(models.Model):
- """
- """
- image = models.ImageField(upload_to="avatars/%Y/%b/%d", storage=default_storage)
- user = models.ForeignKey(User)
- date = models.DateTimeField(auto_now_add=True)
- valid = models.BooleanField()
-
- class Meta:
- unique_together = (('user', 'valid'),)
-
- def __unicode__(self):
- return _("%s's Avatar") % self.user
-
- def delete(self):
- if hasattr(settings, "AWS_SECRET_ACCESS_KEY"):
- path = urllib.unquote(self.image.name)
- else:
- path = self.image.path
-
- base, filename = os.path.split(path)
- name, extension = os.path.splitext(filename)
- for key in AVATAR_SIZES:
- try:
- storage.delete(os.path.join(base, "%s.%s%s" % (name, key, extension)))
- except:
- pass
-
- super(Avatar, self).delete()
-
- def save(self):
- for avatar in Avatar.objects.filter(user=self.user, valid=self.valid).exclude(id=self.id):
- if hasattr(settings, "AWS_SECRET_ACCESS_KEY"):
- path = urllib.unquote(self.image.name)
- else:
- path = avatar.image.path
-
- base, filename = os.path.split(path)
- name, extension = os.path.splitext(filename)
- for key in AVATAR_SIZES:
- try:
- storage.delete(os.path.join(base, "%s.%s%s" % (name, key, extension)))
- except:
- pass
- avatar.delete()
-
- super(Avatar, self).save()
-
-
View
50 profiles/templates/edit_profile.html
@@ -1,50 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}Your Profile{% endblock %}
-{% block title %}Hello, {{ user.username }}{% endblock %}
-
-{% block content_bigcol %}
-
-{% if error_message %}
-
- You've reached this page in error:<br />
- {{ error_message }}
-
-{% else %}
-
-{% if form.errors %}
- {% for field in form %}
- {% if field.errors %}
- <div class="form_error_field">
- {{ field.label }}
- {{ field.errors }}
- </div>
- {% endif %}
- {% endfor %}
-{% endif %}
-
-<form enctype="multipart/form-data" action="." method="post">
-
-<p><label>Email Address:</label><input type="text" name="email" value="{{ form.email }}"><br /></p>
-<p>{{ form.first_name.label_tag }} {{ form.first_name.as_widget }}<br /></p>
-<p>{{ form.last_name.label_tag }} {{ form.last_name.as_widget }}<br /></p>
-<p>{{ form.city.label_tag }} {{ form.city.as_widget }}<br /></p>
-<p>{{ form.state.label_tag }} {{ form.state.as_widget }}<br /></p>
-<p>{{ form.zip_code.label_tag }} {{ form.zip_code.as_widget }}<br /></p>
-<p>{{ form.twitter.label_tag }} {{ form.twitter.as_widget }}<br /></p>
-<p>{{ form.livejournal.label_tag }} {{ form.livejournal.as_widget }}<br /></p>
-<p>{{ form.website.label_tag }} {{ form.website.as_widget }}<br /></p>
-<p>{{ form.favorite_bands.label_tag }} {{ form.favorite_bands.as_widget }}<br /></p>
-<p>{{ form.visible.label_tag }} {{ form.visible.as_widget }}<br /></p>
-<p>{{ form.email_notify.label_tag }} {{ form.email_notify.as_widget }}<br /></p>
-<p align="center"><input type="submit" value="Save Your Profile" /><br /></p>
-
-</form>
-
-<p style="margin-left: 25px; font-weight: bold;">
- <a href="/password_reset/">Reset Your Password</a></p>
-
-{% endif %}
-
-{% endblock %}
-
View
22 profiles/templates/search_results.html
@@ -1,22 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}Search Results{% endblock %}
-
-{% block content_bigcol %}
-
-{% if results %}
-
- <h3>Members who like {{ band }}:</h3>
- <ul>
- {% for result in results %}
- <a href="/profile/{{ result.user.username }}/">{{ result.user.username }}</a><br />
- {% endfor %}
- </ul>
-
-{% else %}
-
- <h3>No one else seems to like {{ band }} ...</h3>
-
-{% endif %}
-
-{% endblock %}
View
36 profiles/templates/view_another_users_profile.html
@@ -1,36 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}Your Profile{% endblock %}
-
-{% block content_bigcol %}
-
-{% if error_message %}
-
- <div class="required">
- Error: {{ error_message }}
- </div>
-
-{% else %}
-
- <p><b>Profile for: {{ profile.user.username }}</b></p>
- <p><br /></p>
- <p><label>Name</label>{{ profile.first_name }} {{ profile.last_name }}<br /></p>
- <p><label>Email</label>{{ profile.user.email }}<br /></p>
- <p><label>On Twitter</label>{{ profile.twitter }}<br /></p>
- <p><label>On LiveJournal</label>{{ profile.livejournal }}<br /></p>
- <p><label>Web Site</label>{{ profile.website }}<br /></p>
- <p><label>Favorite Bands</label>
- {% if bands %}
- <div style="position: relative; float: left; width: 400px;">
- {% for band in bands %}
- <a href="/profile/band/{{ band }}/">{{ band }}</a>
- {% endfor %}
- </div>
- <div class="clear"></div>
- {% endif %}
- </p>
- <p><br /></p>
-
-{% endif %}
-
-{% endblock %}
View
46 profiles/templates/view_profile.html
@@ -1,46 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_title %}Your Profile{% endblock %}
-
-{% block content_bigcol %}
-
-{% if error_message %}
-
- <div class="required">
- Error: {{ error_message }}
- </div>
-
-{% else %}
-
- <p><b>Profile for: {{ profile.user.username }} (that's you!)</b><img src="/site_media/{{ avatar.image }}" height="50" width="50" align="right" hspace="100"></p>
- <p></p>
- <p><label>Email Address:</label> {{ profile.user.email }}<br /></p>
- <p><label>First Name:</label> {{ profile.first_name }}<br /></p>
- <p><label>Last Name:</label> {{ profile.last_name }}<br /></p>
- <p><label>City:</label> {{ profile.city }}<br /></p>
- <p><label>State:</label> {{ profile.state }}<br /></p>
- <p><label>Zip Code:</label> {{ profile.zip_code }}<br /></p>
- <p><label>Twitter ID:</p> {{ profile.twitter }}<br /></p>
- <p><label>LiveJournal ID:</label> {{ profile.livejournal }}<br /></p>
- <p><label>Your web site:</label> {{ profile.website }}<br /></p>
- <p><label>Favorite Bands</label>
- {% if bands %}
- <div style="position: relative; float: left; width: 400px;">
- {% for band in bands %}
- <a href="/profile/band/{{ band }}/">{{ band }}</a>
- {% endfor %}
- </div>
- <div class="clear"></div>
- {% endif %}
- </p>
- <p><label>Let other users see your profile?:</label> {{ profile.visible|yesno:"sure,nah,maybe" }}<br /></p>
- <p><label>Receive email notifications?:</label> {{ profile.email_notify|yesno:"yeah,nope,maybe" }}<br /></p>
- <p><br /></p>
-
- <p style="margin-left: 25px; font-weight: bold;">
- <a href="/profile/edit/{{ user }}/">Edit Your Profile</a> |
- <a href="/password_reset/">Reset Your Password</a></p>
-
-{% endif %}
-
-{% endblock %}
View
16 profiles/urls.py
@@ -1,16 +0,0 @@
-from django.conf.urls.defaults import *
-from django.views.generic.simple import direct_to_template
-from django.contrib.auth import views
-
-from fivesongs.profiles import views
-
-urlpatterns = patterns('',
- url(r'^(?P<username>\w+)/$', views.view, name='profiles_view'),
- url(r'^(?P<username>\w+)$', views.view, name='profiles_view'),
-
- url(r'^edit/(?P<username>\w+)/$', views.edit, name='profiles_edit'),
- url(r'^edit/(?P<username>\w+)$', views.edit, name='profiles_edit'),
-
- url(r'^band/(?P<bandname>.*?)/$', views.searchband, name='profiles_searchband'),
- url(r'^band/(?P<bandname>.*?)$', views.searchband, name='profiles_searchband'),
-)
View
144 profiles/views.py
@@ -1,144 +0,0 @@
-import logging
-
-from django.contrib.auth import authenticate, login, logout
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth.models import User
-
-from django.core.exceptions import ObjectDoesNotExist
-from django.core.urlresolvers import reverse
-from django.http import HttpResponseNotFound, Http404, HttpResponse, HttpResponseRedirect
-from django import forms
-from django.shortcuts import render_to_response
-from django.template import RequestContext
-
-from fivesongs.profiles.models import UserProfile, Avatar
-from fivesongs.profiles.forms import UserProfileForm
-
-@login_required
-def searchband(request, bandname):
- """
- """
- template_name = 'search_results.html'
- context ={}
- try:
- profiles = UserProfile.objects.filter(visible=True, active=True, favorite_bands__contains=bandname)
- except ObjectDoesNotExist:
- profiles = None
- context['results'] = profiles
- context['band'] = bandname
- return render_to_response(template_name, context, context_instance=RequestContext(request))
-
-@login_required
-def view(request, username):
- """
- View the profile if it exists; return the create template if it doesn't.
- If the username in the request doesn't match the logged in user, return an error page.
- """
- template_name = 'view_profile.html'
- context = {}
-
- try:
- requested_user = User.objects.get(username=username)
- profile = UserProfile.objects.get(user=requested_user.id, active=True)
- except ObjectDoesNotExist:
- context['error_message'] = 'That user does not exist.'
- profile = None
-
- logging.debug(profile)
-
- permission = has_permission(request.user, request.user.username, username)
- if permission is True:
- logging.debug('**** OK - viewing your own profile')
- if not profile:
- logging.debug('***** you havent created a profile yet, redir to edit')
- return HttpResponseRedirect('/profile/edit/%s/' % request.user.username)
- else:
- if profile and profile.visible:
- logging.debug('***** this user has allowed their profile to be visible to other users')
- template_name = 'view_another_users_profile.html'
- else:
- logging.debug('***** its not your profile, and the user has not made it visible to all')
- context['error_message'] = 'You do not have permission to view this profile.'
-
- if profile:
- try:
- avatar = Avatar.objects.get(user=requested_user.id)
- except ObjectDoesNotExist:
- avatar = None
- context['avatar'] = avatar
-
- try:
- favorite_bands = profile.favorite_bands
- except ObjectDoesNotExist:
- favorite_bands = None
-
- if favorite_bands:
- bands = []
- band_list = favorite_bands.split (',')
- for band in band_list:
- bands.append(band.strip())
- context['bands'] = bands
-
- context['profile'] = profile
-
- return render_to_response(template_name, context, context_instance=RequestContext(request))
-
-@login_required
-def edit(request, username):
- """
- Edit a user profile if it exists; create a new one if it doesn't.
- """
- context = {}
- template_name = 'edit_profile.html'
- form_class = UserProfileForm
-
- permission = has_permission(request.user, request.user.username, username)
- if permission is not True:
- context['error_message'] = 'You do not have permission to create/edit this profile.'
-
- try:
- profile = UserProfile.objects.get(user=request.user.id, active=True)
- except ObjectDoesNotExist:
- profile = None
-
- if request.method == 'POST':
- update_user = User.objects.get(username='%s' %username)
- update_user.email = request.POST['email']
- if profile:
- form = form_class(request.POST, request.FILES, instance=profile)
- if form.is_valid():
- if update_user.email: update_user.save()
- profile = form.save(commit=False)
- profile.active = True
- profile.save()
- logging.debug('first save')
- logging.debug(profile.save())
- # form.save()
- return HttpResponseRedirect('/profile/%s/' % request.user.username)
- else:
- form = form_class(request.POST)
- if form.is_valid():
- profile = form.save(commit=False)
- profile.user_id = request.user.id
- profile.active = True
- profile.save()
- logging.debug('save')
- logging.debug(profile.save())
- return HttpResponseRedirect('/profile/%s/' % request.user.username)
- else:
- form = form_class(instance=profile)
- form.email = request.user.email
-
- context['form'] = form
- context['profile'] = profile
-
- return render_to_response(template_name, context, context_instance=RequestContext(request))
-
-def has_permission(request_user, request_username, username):
- """
- Username in the request must match the logged in user.
- """
- if cmp(request_username, username) != 0:
- return False
- return True
-
View
90 settings.py
@@ -1,90 +0,0 @@
-import os, logging, sys
-
-# Django settings for the fivesongs project.
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-ADMINS = (
- ('', ''),
-)
-ADMIN_EMAIL = ''
-MANAGERS = ADMINS
-DEFAULT_FROM_EMAIL = ''
-
-EMAIL_HOST = ''
-EMAIL_HOST_USER = ''
-EMAIL_HOST_PASSWORD = ''
-
-DATABASE_ENGINE = '' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-DATABASE_NAME = '' # Or path to database file if using sqlite3.
-DATABASE_USER = '' # Not used with sqlite3.
-DATABASE_PASSWORD = '' # Not used with sqlite3.
-DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
-DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
-
-TIME_ZONE = 'America/Los Angeles'
-
-LANGUAGE_CODE = 'en-us'
-
-SITE_ID = 1
-
-USE_I18N = False
-
-MEDIA_ROOT = '/fivesongs/site_media/'
-
-MEDIA_URL = '/site_media/'
-
-ADMIN_MEDIA_PREFIX = '/media/'
-
-SECRET_KEY = ''
-
-TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.load_template_source',
- 'django.template.loaders.app_directories.load_template_source',
-# 'django.template.loaders.eggs.load_template_source',
-)
-
-MIDDLEWARE_CLASSES = (
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'middleware.project_logging.LoggingMiddleware',
-)
-
-ROOT_URLCONF = 'fivesongs.urls'
-
-TEMPLATE_ROOT = ''
-
-TEMPLATE_DIRS = (
- '/fivesongs/templates/',
- '/django/contrib/admin/templates/',
-)
-
-INSTALLED_APPS = (
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.admin',
- 'django.contrib.comments',
- 'fivesongs.playlist',
- 'fivesongs.profiles',
- 'fivesongs.pages',
- 'fivesongs.contact',
-)
-
-LOGOUT_URL = '/accounts/login/'
-
-AKISMET_API_KEY = ''
-
-try:
- from local_settings import *
-except ImportError:
- try:
- from mod_python import apache
- apache.log_error( "local_settings.py not set; using default settings", apache.APLOG_NOTICE )
- except ImportError:
- import sys
- sys.stderr.write( "local_settings.py not set; using default settings\n" )
-
View
84 site_media/css/fivesongs.css
@@ -1,84 +0,0 @@
-html,body { width: 100%; height: 100%; font: 12px Helvetica, Arial, sans-serif; letter-spacing: 0px; line-height: 20px; }
-body { background-color: #fff; position:relative; margin: 0; padding: 0; }
-/* background-color: #f9d3e2; */
-
-a img { border: none; }
-a { color: #003366 }
-
-/* global effects */
-.clear { clear: both; }
-.left { float: left; }
-.right { float: right; }
-.visible { display: block; }
-.hidden { display: none; }
-.hand { cursor: pointer; }
-.half { width: 49%; }
-.invisible { opacity: 0; }
-.inactive { color: #333; }
-
-h1 { font-size: 20px; }
-h2, h3, h4 { font-size: 16px; }
-
-.required { color: #ff4400; }
-
-.form { border: 0 solid; }
-fieldset { padding: 0; margin-top: 5px;}
-textarea { height: 100px; width: 400px; background-color: #e1e1e1; }
<