Fix for tinymce in event admin

This commit is contained in:
fergal.moran
2012-08-15 12:26:28 +01:00
parent a171478e9d
commit 9642716feb
40 changed files with 330 additions and 53 deletions

1
core/widgets/__init__.py Normal file
View File

@@ -0,0 +1 @@
__author__ = 'fergalm'

4
core/widgets/upload.py Normal file
View File

@@ -0,0 +1,4 @@
from django.forms.widgets import ClearableFileInput
class FileUploadWidget(ClearableFileInput):
pass

View File

@@ -15,7 +15,7 @@ ADMINS = (
)
MANAGERS = ADMINS
AUTH_PROFILE_MODULE = 'spa.models.UserProfile'
AUTH_PROFILE_MODULE = 'spa.UserProfile'
DATABASES = {
'default': {
@@ -125,6 +125,7 @@ INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.admindocs',
'djcelery',
'crispy_forms',
'pipeline',
'avatar',
'notification',

1
initial_data.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -13,5 +13,6 @@ django-socialauth>=0.1.2c
django-tastypie>=0.9.11
django-tinymce>=1.5.1b2
git+git://github.com/PaulUithol/backbone-tastypie.git#egg=backbone-tastypie
git+git://github.com/maraujop/django-crispy-forms.git#django-crispy-forms
django-grappelli
humanize

View File

@@ -1,4 +1,5 @@
from django.contrib import admin
from spa.models.Recurrence import Recurrence
from spa.models.Release import Release
from spa.models.Event import Event
from spa.models.Label import Label
@@ -20,3 +21,4 @@ admin.site.register(Release, DefaultAdmin)
admin.site.register(ReleaseAudio)
admin.site.register(Venue)
admin.site.register(Event)
admin.site.register(Recurrence)

View File

@@ -32,5 +32,5 @@ class CommentResource(BackboneCompatibleResource):
def dehydrate(self, bundle):
bundle.data['avatar_image'] = bundle.obj.user.get_profile().get_avatar_image(150)
bundle.data['user_url'] = bundle.obj.user.get_absolute_url()
bundle.data['user_name'] = bundle.obj.user.get_profile().nice_name()
bundle.data['user_name'] = bundle.obj.user.get_profile().nice_name() or bundle.obj.user.get_profile().display_name
return bundle

View File

@@ -0,0 +1,21 @@
from django.conf.urls import url
from tastypie.authorization import Authorization
from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource
from spa.models import UserProfile
class UserResource(BackboneCompatibleResource):
class Meta:
queryset = UserProfile.objects.all()
authorization = Authorization()
def dehydrate(self, bundle):
bundle.data['display_name'] = bundle.obj.display_name
bundle.data['first_name'] = bundle.obj.first_name
bundle.data['last_name'] = bundle.obj.last_name
bundle.data['email'] = bundle.obj.email
return bundle
def override_urls(self):
return [
url(r"^(?P<resource_name>%s)/(?P<username>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
]

73
spa/forms.py Normal file
View File

@@ -0,0 +1,73 @@
from crispy_forms.bootstrap import FormActions
from crispy_forms.layout import Submit, Fieldset, Layout
from django import forms
from django.contrib.auth.models import User
from django.forms.models import ModelForm
from crispy_forms.helper import FormHelper
from spa.models.UserProfile import UserProfile
class UserForm(ModelForm):
avatar_image_select = forms.ChoiceField(
choices=(
('gravatar', "Use gravatar image."),
('social', "Use Twitter/Facebook image."),
('custom', "Use custom image (upload below).")
),
label="Avatar Image",
widget=forms.RadioSelect,
initial='option_gravatar',
help_text="Select the source of your avatar image."
)
avatar_image = forms.ImageField(
label="",
required=False
)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name')
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
'User details',
'display_name',
'email',
'first_name',
'last_name',
'avatar_image_select',
'avatar_image'
),
FormActions(
Submit('save_changes', 'Save changes', css_class="btn-primary"),
Submit('cancel', 'Cancel'),
)
)
self.helper.form_class = 'form-horizontal'
self.helper.form_id = 'id-new-user-form'
self.helper.form_method = 'post'
self.helper.form_error_title = 'Ooopsies'
self.helper.formset_error_title = 'Ooopsies'
self.helper.form_show_errors = True
def save(self, *args, **kwargs):
user = super(UserForm, self).save(*args, **kwargs)
profile = UserProfile.objects.filter(user=user)[0]
if profile is None:
profile = UserProfile()
profile.user = user
profile.avatar_type = self.cleaned_data['avatar_image_select']
profile.avatar_image = self.cleaned_data['avatar_image']
# and so on with the remaining fields
profile.save()
return profile

View File

@@ -1,11 +1,10 @@
from django.contrib.auth.models import User
from django.db import models
from spa.models.__BaseModel import __BaseModel
from spa.models._BaseModel import _BaseModel
from spa.models.Mix import Mix
class Comment(__BaseModel):
class Comment(_BaseModel):
class Meta:
db_table = 'www_comment'
app_label = 'spa'
user = models.ForeignKey(User, editable=False)

View File

@@ -2,11 +2,11 @@ from datetime import datetime
from django.contrib.auth.models import User
from django.db import models
from tinymce import models as tinymce_models
from spa.models import Recurrence
from spa.models.Venue import Venue
class Event(models.Model):
class Meta:
db_table = 'www_event'
app_label = 'spa'
event_venue = models.ForeignKey(Venue)
@@ -17,6 +17,7 @@ class Event(models.Model):
date_created = models.DateField(default=datetime.now())
event_title = models.CharField(max_length=250)
event_description = tinymce_models.HTMLField()
event_recurrence = models.ForeignKey(Recurrence)
attendees = models.ManyToManyField(User, related_name='attendees')

View File

@@ -1,9 +1,8 @@
from django.db import models
from spa.models.__BaseModel import __BaseModel
from spa.models._BaseModel import _BaseModel
class Label(__BaseModel):
class Label(_BaseModel):
class Meta:
db_table = 'www_label'
app_label = 'spa'
name = models.CharField(max_length=100)

View File

@@ -5,7 +5,7 @@ import os
from core.utils.file import generate_save_file_name
from dss import settings
from spa.models.UserProfile import UserProfile
from spa.models.__BaseModel import __BaseModel
from spa.models._BaseModel import _BaseModel
from tasks.waveform import create_waveform_task
from django.db import models
@@ -15,9 +15,8 @@ def mix_file_name(instance, filename):
def mix_image_name(instance, filename):
return generate_save_file_name('mix-images', filename)
class Mix(__BaseModel):
class Mix(_BaseModel):
class Meta:
db_table = 'www_mix'
app_label = 'spa'
title = models.CharField(max_length=50)

View File

@@ -1,10 +1,9 @@
from django.db import models
from spa.models.Mix import Mix
from spa.models.__Activity import __Activity
from spa.models._Activity import _Activity
class MixLike(__Activity):
class MixLike(_Activity):
class Meta:
db_table = 'www_like'
app_label = 'spa'
mix = models.ForeignKey(Mix, related_name='likes')

View File

@@ -1,10 +1,9 @@
from django.db import models
from spa.models.Mix import Mix
from spa.models.__Activity import __Activity
from spa.models._Activity import _Activity
class MixPlay(__Activity):
class MixPlay(_Activity):
class Meta:
db_table = 'www_play'
app_label = 'spa'
mix = models.ForeignKey(Mix, related_name='plays')

4
spa/models/Recurrence.py Normal file
View File

@@ -0,0 +1,4 @@
from spa.models._Lookup import _Lookup
class Recurrence(_Lookup):
pass

View File

@@ -4,7 +4,7 @@ from core.utils.file import generate_save_file_name
from dss import settings
from spa.models.Label import Label
from spa.models.UserProfile import UserProfile
from spa.models.__BaseModel import __BaseModel
from spa.models._BaseModel import _BaseModel
def release_image_name(instance, filename):
return generate_save_file_name('release-images', filename)
@@ -12,9 +12,8 @@ def release_image_name(instance, filename):
def release_file_name(instance, filename):
return generate_save_file_name('release-audio', filename)
class Release(__BaseModel):
class Release(_BaseModel):
class Meta:
db_table = 'www_release'
app_label = 'spa'
release_artist = models.CharField(max_length=100)
@@ -44,9 +43,8 @@ class Release(__BaseModel):
return qs
class ReleaseAudio(__BaseModel):
class ReleaseAudio(_BaseModel):
class Meta:
db_table = 'www_releaseaudio'
app_label = 'spa'
def __unicode__(self):

View File

@@ -6,17 +6,17 @@ from django.db import models
from django.db.models.signals import post_save
from django_gravatar.helpers import has_gravatar, get_gravatar_url
from dss import settings
from spa.models.__BaseModel import __BaseModel
from spa.models._BaseModel import _BaseModel
class UserProfile(__BaseModel):
class UserProfile(_BaseModel):
class Meta:
db_table = 'www_userprofile'
app_label = 'spa'
# This field is required.
user = models.ForeignKey(User, unique=True)
avatar_type = models.CharField(max_length=15)
avatar_image = models.ImageField(blank=True, upload_to='/avatars/')
display_name = models.CharField(blank=True, max_length=35)
def create_user_profile(sender, instance, created, **kwargs):
if created:
@@ -43,7 +43,7 @@ class UserProfile(__BaseModel):
return reverse('user_details', kwargs={'user_name': self.user.username})
def nice_name(self):
return self.first_name + ' ' + self.last_name
return self.display_name or self.first_name + ' ' + self.last_name
def get_avatar_image(self, size=150):
avatar_type = self.avatar_type
@@ -62,4 +62,4 @@ class UserProfile(__BaseModel):
elif avatar_type == 'custom' or avatar_type:
return self.avatar_image.url
return urlparse.urljoin(settings.STAT, "img/default-avatar-32.png")
return urlparse.urljoin(settings.STATIC_URL, "img/default-avatar-32.png")

View File

@@ -7,7 +7,6 @@ def venue_image_name(instance, filename):
class Venue(models.Model):
class Meta:
db_table = 'www_venue'
app_label = 'spa'
user = models.ForeignKey(User)

View File

@@ -1,7 +1,7 @@
from django.contrib.auth.models import User
from django.db import models
from spa.models.__BaseModel import __BaseModel
from spa.models._BaseModel import _BaseModel
class __Activity(__BaseModel):
class _Activity(_BaseModel):
date = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, null=True )

View File

@@ -1,7 +1,7 @@
import logging
from django.db import models
class __BaseModel(models.Model):
class _BaseModel(models.Model):
logger = logging.getLogger(__name__)
class Meta:
abstract = True

8
spa/models/_Lookup.py Normal file
View File

@@ -0,0 +1,8 @@
from django.db import models
from _BaseModel import _BaseModel
class _Lookup(_BaseModel):
description = models.CharField(max_length=100)
def __unicode__(self):
return self.description

View File

@@ -1,6 +1,7 @@
from __BaseModel import __BaseModel
from _BaseModel import _BaseModel
from UserProfile import UserProfile
from __Activity import __Activity
from _Activity import _Activity
from Recurrence import Recurrence
from Comment import Comment
from Venue import Venue
from Event import Event
@@ -9,3 +10,16 @@ from Mix import Mix
from MixLike import MixLike
from MixPlay import MixPlay
from Release import Release
from django.db.models import signals
from django.contrib.auth.management import create_superuser
from django.contrib.auth import models as auth_app
# Prevent interactive question about wanting a superuser created. (This
# code has to go in this otherwise empty "models" module so that it gets
# processed by the "syncdb" command during database creation.)
signals.post_syncdb.disconnect(
create_superuser,
sender=auth_app,
dispatch_uid = "django.contrib.auth.management.create_superuser")

View File

@@ -1,9 +1,16 @@
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from spa.forms import UserForm
__author__ = 'fergalm'
def get_template(request, template_name):
return render_to_response(
'views/%s.html' % template_name,
context_instance=RequestContext(request))
context_instance=RequestContext(request))
def get_template_ex(request, template_name):
html = render_to_response(
'views/%s.html' % template_name,
context_instance=RequestContext(request, {'form': UserForm() }))
return html

View File

@@ -1,3 +1,4 @@
import urlparse
from allauth.socialaccount.models import SocialAccount
from django import template
from django.contrib.auth.models import User
@@ -26,14 +27,14 @@ def avatar_image(user, size=150):
gravatar_exists = has_gravatar(user.email)
if gravatar_exists:
return get_gravatar_url(user.email, size)
elif avatar_type == 'social':
elif avatar_type == 'social' or avatar_type == '':
try:
social_account = SocialAccount.objects.filter(user = user)[0]
if social_account:
provider = social_account.get_provider_account()
return provider.get_avatar_url()
except:
pass
return urlparse.urljoin(settings.STATIC_URL, "img/default-avatar-32.png")
elif avatar_type == 'custom':
return profile.avatar_image.url

View File

@@ -8,6 +8,7 @@ from spa.api.v1.MixResource import MixResource
from spa.api.v1.ReleaseAudioResource import ReleaseAudioResource
from spa.api.v1.ReleaseResource import ReleaseResource
import spa
from spa.api.v1.UserResource import UserResource
v1_api = Api(api_name='v1')
v1_api.register(MixResource())
@@ -15,12 +16,14 @@ v1_api.register(CommentResource())
v1_api.register(ReleaseResource())
v1_api.register(ReleaseAudioResource())
v1_api.register(EventResource())
v1_api.register(UserResource())
ajax = AjaxHandler()
urlpatterns = django.conf.urls.patterns(
'',
url(r'^$', 'spa.views.app', name='home'),
url(r'^tpl/(?P<template_name>\w+)/$', 'spa.templates.get_template'),
url(r'^tplex/(?P<template_name>\w+)/$', 'spa.templates.get_template_ex'),
(r'^ajax/', include(ajax.urls)),
(r'^api/', include(v1_api.urls)),
)

View File

@@ -8,6 +8,8 @@ body {
padding-bottom: 40px;
padding-right: 32px;
}
/* IE/Chrome image fix */
img.event-content {width: auto; height: auto;}
#header {
margin-top: 20px;
@@ -21,6 +23,24 @@ body {
border-bottom: 3px solid #CEC3B3;
}
.page-header{
-moz-border-bottom-colors: none;
-moz-border-image: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
border-color: -moz-use-text-color -moz-use-text-color #E5E5E5;
border-style: none none solid;
border-width: 0 0 1px;
color: #333333;
display: block;
font-size: 19.5px;
line-height: 36px;
margin-bottom: 27px;
padding: 0;
width: 100%;
}
#mix-comments-list ul {
list-style-type: none;
}

View File

View File

@@ -9,7 +9,8 @@ var AppRouter = Backbone.Router.extend({
"/events":"eventList",
"/event/:id":"eventDetails",
"/accounts/login/":"login",
"/accounts/logout/":"logout"
"/accounts/logout/":"logout",
"/me":"userDetails"
},
initialize:function () {
this.headerView = new HeaderView();
@@ -105,11 +106,19 @@ var AppRouter = Backbone.Router.extend({
},
logout:function () {
window.utils.showAlert("Success", "You are now logged out", "alert-success");
},
userDetails: function(){
var user = new User();
$('#site-content-fill').html('');
user.fetch({success:function () {
var content = new UserView({model:user}).el;
$('#content').html(content);
}});
}
});
utils.loadTemplate([
'HeaderView', 'SidebarView',
'HeaderView', 'SidebarView', 'UserView',
'MixListView', 'MixListItemView', 'MixView',
'CommentListView', 'CommentListItemView',
'ReleaseListView', 'ReleaseListItemView', 'ReleaseItemView', 'ReleaseView', 'ReleaseAudioListView', 'ReleaseAudioItemView',

View File

@@ -0,0 +1,3 @@
var User = TastypieModel.extend({
urlRoot:window.appSettings.urlRoot + "user/"
});

View File

@@ -0,0 +1,25 @@
window.UserView = Backbone.View.extend({
events:{
"click #save-changes":"saveChanges",
"change input" :"changed",
"change select" :"changed"
},
initialize:function () {
this.render();
},
render:function () {
$(this.el).html(this.template({"item":this.model.toJSON()}));
return this;
},
saveChanges: function(){
this.model.save();
return false;
},
changed:function(evt) {
var changed = evt.currentTarget;
var value = $("#"+changed.id).val();
var obj = "{\""+changed.id +"\":\""+value+"\"}";
var objInst = JSON.parse(obj);
this.model.set(objInst);
}
});

1
static/js/libs/backbone-forms.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -19,9 +19,8 @@
<link rel="stylesheet" href="{{ STATIC_URL }}css/bootstrap-responsive.css">
<link rel="stylesheet" href="{{ STATIC_URL }}css/jasny/jasny-bootstrap.css">
<link rel="stylesheet" href="{{ STATIC_URL }}css/jasny/jasny-bootstrap-responsive.css">
<link rel="stylesheet" href="{{ STATIC_URL }}css/style.css">
<link rel="stylesheet" href="{{ STATIC_URL }}css/sup.css">
<link rel="stylesheet" href="{{ STATIC_URL }}css/colorbox.css">
<link rel="stylesheet" href="{{ STATIC_URL }}css/style.css">
<script src="{{ STATIC_URL }}js/libs/modernizr-2.5.3-respond-1.1.0.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
@@ -78,11 +77,13 @@
<script src="{{ STATIC_URL }}js/libs/jquery.colorbox.js"></script>
<!-- backbone application -->
<script src="{{ STATIC_URL }}js/libs/backbone-forms.min.js"></script>
<script src="{{ STATIC_URL }}js/libs/backbone-tastypie.js"></script>
<script src="{{ STATIC_URL }}js/app/site.js"></script>
<script src="{{ STATIC_URL }}js/app/utils.js"></script>
<script src="{{ STATIC_URL }}js/app/settings.js"></script>
<script src="{{ STATIC_URL }}js/app/models/mix.js"></script>
<script src="{{ STATIC_URL }}js/app/models/user.js"></script>
<script src="{{ STATIC_URL }}js/app/models/comment.js"></script>
<script src="{{ STATIC_URL }}js/app/models/release.js"></script>
<script src="{{ STATIC_URL }}js/app/models/release_audio.js"></script>
@@ -90,6 +91,7 @@
<script src="{{ STATIC_URL }}js/app/views/header.js"></script>
<script src="{{ STATIC_URL }}js/app/views/sidebar.js"></script>
<script src="{{ STATIC_URL }}js/app/views/mix.js"></script>
<script src="{{ STATIC_URL }}js/app/views/user.js"></script>
<script src="{{ STATIC_URL }}js/app/views/comment.js"></script>
<script src="{{ STATIC_URL }}js/app/views/release.js"></script>
<script src="{{ STATIC_URL }}js/app/views/release_audio.js"></script>

View File

@@ -1,17 +1,28 @@
<style type="text/css">
div.event-content img {
width: auto;
height: auto;
max-width: none;
}
div.event-content td {
padding: 12px;
}
</style>
<div class="hero-unit">
<div class="row">
<div id="event-header">
<div id="event-header" class="bordered">
<h3><i class="icon-time"></i>
<%= item.event_date %>
&nbsp;::&nbsp;
<%= item.event_title %>
&nbsp;-&nbsp;
<%= item.venue %>
&nbsp;-&nbsp;
&nbsp;::&nbsp;
<%= item.event_title %>
</h3>
</div>
</div>
<div class="row">
<div class="row event-content">
<%= item.event_description %>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<div id="title-area">
<h2 class="bordered">Upcoming Events</h2>
<p class="page-header">Upcoming Events</p>
</div>
<div id="layout-area" class="hero-well">
<table id="event-table" class="table table-bordered table-striped">

View File

@@ -41,7 +41,7 @@
</a>
<li>
<li>
<a href="">{% user_display user %}</a>
<a href="#/me">{% user_display user %}</a>
</li>
</ul>
</div>

View File

@@ -1,7 +1,4 @@
{% load socialaccount_tags %}
<div class="well">
<h2>Login to Deep South Sounds</h2>
<div>

View File

@@ -1,6 +1,6 @@
<div class="hero-unit">
<div class="row">
<div id="release-header">
<div id="release-header" class="bordered">
<h3><i class="icon-time"></i>
<%= item.release_date %>
&nbsp;::&nbsp;

View File

@@ -1,5 +1,5 @@
<div id="title-area">
<h2 class="bordered">Upcoming Releases</h2>
<p class="page-header">Upcoming Releases</p>
</div>
<div id="layout-area" class="hero-well">
<table id="release-table" class="table table-bordered table-striped">

View File

@@ -0,0 +1,75 @@
{% load account_tags %}
{% block scripts %}
<script type="text/javascript">
$(document).ready(function () {
$('#avatar_image').hide();
$('input:radio[name=avatar_image_select]').bind('click', function (data) {
var selected = $('input:radio[name=avatar_image_select]:checked').val();
if (selected === 'custom') {
$('#avatar_image').show();
} else {
$('#avatar_image').hide();
}
});
});
</script>
{% endblock %}
{% block content %}
<form enctype="multipart/form-data" method="post" class="form-horizontal" id="id-new-user-form">
{% csrf_token %}
<fieldset>
<legend>User details</legend>
<div class="clearfix control-group" id="div_display_name">
<label class="control-label" for="display_name">Display name</label>
<div class="controls">
<input type="text" maxlength="75" name="email" class="textinput textInput" id="display_name" value="<%= item.display_name %>">
</div>
</div>
<div class="clearfix control-group" id="div_email">
<label class="control-label" for="email">E-mail address</label>
<div class="controls">
<input type="text" maxlength="75" name="email" class="textinput textInput" id="email" value="{{ user.email }}">
</div>
</div>
<div class="clearfix control-group" id="div_first_name">
<label class="control-label " for="first_name">First name</label>
<div class="controls">
<input type="text" maxlength="30" name="first_name" class="textinput textInput" id="first_name" value="{{ user.first_name }}">
</div>
</div>
<div class="clearfix control-group" id="div_last_name">
<label class="control-label " for="last_name">Last name</label>
<div class="controls">
<input type="text" maxlength="30" name="last_name" class="textinput textInput" id="last_name" value="{{ user.last_name }}">
</div>
</div>
<div class="clearfix control-group" id="div_avatar_image_select">
<label class="control-label requiredField" for="avatar_image_select_0">Avatar Image<span class="asteriskField">*</span></label>
<div class="controls">
<label class="radio">
Use gravatar image.
<input type="radio" value="gravatar" id="avatar_image_select_1" name="avatar_image_select">
</label>
<label class="radio">
Use Twitter/Facebook image.
<input type="radio" value="social" id="avatar_image_select_2" name="avatar_image_select">
</label>
<label class="radio">
Use custom image (upload below).
<input type="radio" value="custom" id="avatar_image_select_3" name="avatar_image_select">
</label>
</div>
</div>
<div class="clearfix control-group" id="div_avatar_image">
<div class="controls">
<input type="file" name="avatar_image" class="clearablefileinput" id="avatar_image" style="display: none;">
</div>
</div>
</fieldset>
<div class="form-actions">
<input type="submit" id="save-changes" class="btn btn-primary" value="Save changes" name="save_changes">&zwnj;
</div>
</form>
{% endblock %}