Redesigning the main view: including a new features list, reformatted login and signup form.

This commit is contained in:
Samuel Clay 2010-06-12 21:20:06 -04:00
parent ccc2b73bf5
commit ee61967a2b
9 changed files with 412 additions and 86 deletions

View file

@ -21,18 +21,23 @@ def opml_upload(request):
xml_opml = None
message = "OK"
code = 1
payload = {}
if request.method == 'POST':
if 'file' in request.FILES:
file = request.FILES['file']
xml_opml = file.read()
opml_importer = OPMLImporter(xml_opml, request.user)
folders = opml_importer.process()
feeds = UserSubscription.objects.filter(user=request.user).values()
data = json.encode(dict(message=message, code=code, payload=dict(folders=folders, feeds=feeds)))
opml_importer = OPMLImporter(xml_opml, request.user)
folders = opml_importer.process()
feeds = UserSubscription.objects.filter(user=request.user).values()
payload = dict(folders=folders, feeds=feeds)
else:
message = "Attach an .opml file."
code = -1
data = json.encode(dict(message=message, code=code, payload=payload))
return HttpResponse(data, mimetype='text/plain')
class OPMLImporter:

View file

@ -2,6 +2,7 @@ from django import forms
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from apps.reader.models import Feature
class LoginForm(forms.Form):
username = forms.CharField(label=_("Username"), max_length=30,
@ -66,3 +67,11 @@ class SignupForm(forms.Form):
new_user = authenticate(username=self.cleaned_data['signup_username'], password=self.cleaned_data['signup_password'])
return new_user
class FeatureForm(forms.Form):
description = forms.CharField(required=True)
def save(self):
feature = Feature(description=self.cleaned_data['description'])
feature.save()
return feature

View file

@ -0,0 +1,143 @@
from south.db import db
from django.db import models
from apps.reader.models import *
class Migration:
def forwards(self, orm):
# Adding model 'Feature'
db.create_table('reader_feature', (
('id', orm['reader.feature:id']),
('description', orm['reader.feature:description']),
('date', orm['reader.feature:date']),
))
db.send_create_signal('reader', ['Feature'])
def backwards(self, orm):
# Deleting model 'Feature'
db.delete_table('reader_feature')
models = {
'auth.group': {
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'})
},
'auth.permission': {
'Meta': {'unique_together': "(('content_type', 'codename'),)"},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'reader.feature': {
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'reader.userstory': {
'Meta': {'unique_together': "(('user', 'feed', 'story'),)"},
'feed': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.Feed']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'opinion': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'read_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'story': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.Story']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'reader.usersubscription': {
'Meta': {'unique_together': "(('user', 'feed'),)"},
'feed': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.Feed']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_read_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 29, 20, 26, 1, 340435)'}),
'mark_read_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 29, 20, 26, 1, 340483)'}),
'needs_unread_recalc': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'unread_count_negative': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'unread_count_neutral': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'unread_count_positive': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'unread_count_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2000, 1, 1, 0, 0)'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'reader.usersubscriptionfolders': {
'folders': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'rss_feeds.feed': {
'Meta': {'db_table': "'feeds'"},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'creation': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'days_to_trim': ('django.db.models.fields.IntegerField', [], {'default': '90'}),
'etag': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
'feed_address': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '255'}),
'feed_link': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200'}),
'feed_tagline': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'feed_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_load_time': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'last_modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'last_update': ('django.db.models.fields.DateTimeField', [], {'default': '0', 'auto_now': 'True', 'blank': 'True'}),
'min_to_decay': ('django.db.models.fields.IntegerField', [], {'default': '15'}),
'next_scheduled_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'num_subscribers': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'page_data': ('StoryField', [], {'null': 'True', 'blank': 'True'}),
'stories_per_month': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'rss_feeds.story': {
'Meta': {'db_table': "'stories'"},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'story_author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.StoryAuthor']"}),
'story_content': ('StoryField', [], {'null': 'True', 'blank': 'True'}),
'story_content_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'story_date': ('django.db.models.fields.DateTimeField', [], {}),
'story_feed': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'stories'", 'to': "orm['rss_feeds.Feed']"}),
'story_guid': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
'story_guid_hash': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'story_original_content': ('StoryField', [], {'null': 'True', 'blank': 'True'}),
'story_past_trim_date': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'story_permalink': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
'story_tags': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
'story_title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['rss_feeds.Tag']"})
},
'rss_feeds.storyauthor': {
'author_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'feed': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.Feed']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'rss_feeds.tag': {
'feed': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.Feed']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['reader']

View file

@ -7,6 +7,7 @@ from apps.rss_feeds.models import Feed, Story, Tag
from utils import feedparser, object_manager, json
from apps.analyzer.models import ClassifierFeed, ClassifierAuthor, ClassifierTag, ClassifierTitle
from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags
from utils.compressed_textfield import StoryField
DAYS_OF_UNREAD = 14
@ -137,4 +138,14 @@ class UserSubscriptionFolders(models.Model):
class Meta:
verbose_name_plural = "folders"
verbose_name = "folder"
verbose_name = "folder"
class Feature(models.Model):
description = models.TextField(default="")
date = models.DateTimeField(default=datetime.datetime.now)
def __unicode__(self):
return "[%s] %s" % (self.date, self.description[:50])
class Meta:
ordering = ["-date"]

View file

@ -19,4 +19,5 @@ urlpatterns = patterns('',
url(r'^delete_feed', views.delete_feed, name='delete-feed'),
url(r'^add_url', views.add_url),
url(r'^add_folder', views.add_folder),
url(r'^add_feature', views.add_feature, name='add-feature'),
)

View file

@ -13,12 +13,12 @@ from django.db.models.aggregates import Count
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.contrib.auth import login as login_user
from django.http import HttpResponse, HttpRequest, HttpResponseRedirect
from django.http import HttpResponse, HttpRequest, HttpResponseRedirect, HttpResponseForbidden
from apps.analyzer.models import ClassifierFeed, ClassifierAuthor, ClassifierTag, ClassifierTitle
from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags
from apps.analyzer.models import get_classifiers_for_user
from apps.reader.models import UserSubscription, UserSubscriptionFolders, UserStory
from apps.reader.forms import SignupForm, LoginForm
from apps.reader.models import UserSubscription, UserSubscriptionFolders, UserStory, Feature
from apps.reader.forms import SignupForm, LoginForm, FeatureForm
try:
from apps.rss_feeds.models import Feed, Story, Tag, StoryAuthor
except:
@ -40,10 +40,17 @@ def index(request):
else:
login_form = LoginForm(prefix='login')
signup_form = SignupForm(prefix='signup')
features = Feature.objects.all()
feature_form = None
if request.user.is_staff:
feature_form = FeatureForm()
return render_to_response('reader/feeds.xhtml', {
'login_form': login_form,
'signup_form': signup_form,
'feature_form': feature_form,
'features': features
}, context_instance=RequestContext(request))
@never_cache
@ -409,4 +416,21 @@ def delete_feed(request):
user_sub_folders_object.save()
data = json.encode(dict(code=1))
return HttpResponse(data)
return HttpResponse(data)
@login_required
def add_feature(request):
if not request.user.is_staff:
return HttpResponseForbidden()
code = -1
form = FeatureForm(request.POST)
if form.is_valid():
form.save()
code = 1
return HttpResponseRedirect(reverse('index'))
data = json.encode(dict(code=code))
return HttpResponse(data)

View file

@ -44,24 +44,46 @@ a img {
.NB-account {
position: absolute;
right: 50px;
top: 40px;
width: 186px;
right: 0px;
top: 0px;
width: 478px;
z-index: 1;
overflow: hidden;
}
.NB-account .NB-module-header-login {
width: 186px;
margin: 0 50px 0 0;
float: left;
}
.NB-account .NB-module-header-signup {
width: 186px;
margin: 0;
float: left;
}
.NB-account .NB-login {
padding: 0 0 64px 0;
margin: 0 0 24px 0;
margin: 0 50px 24px 12px;
width: 186px;
float: left;
background: transparent url('../img/reader/separator.png') no-repeat bottom center;
}
.NB-account .NB-signup {
float: left;
width: 186px;
padding: 0;
margin: 0;
}
.NB-account input[type=text],
.NB-account input[type=password] {
border:1px solid #D3D5DE;
display:block;
font-size:13px;
margin:0 0 5px;
margin:0 0 12px;
padding:5px;
width:100%;
}
@ -100,7 +122,7 @@ a img {
.NB-account label,
.NB-account .NB-account-label {
margin-top: 2px;
margin: 2px 0 2px;
color:#A0B0C0;
font-size:12px;
display: block;
@ -1281,10 +1303,13 @@ form.opml_import_form input {
font-size: 130%;
}
.NB-modal h5 {
.NB-modal h5,
.NB-module h5 {
margin: 0;
padding: 8px 8px 6px;
background-color: #D9DDF9;
text-shadow: 0 1px 0 #F6F6F6;
border-bottom: 1px solid #A0A0A0;
padding: 18px 0 0px;
margin: 0 0 6px 0;
color:#505050;
font-size: 14px;
font-family: "Gill Sans", inherit;
@ -1350,13 +1375,6 @@ form.opml_import_form input {
overflow: hidden;
}
.NB-modal h5 {
margin: 0;
padding: 0;
padding: 8px 8px 6px;
background-color: #D9DDF9;
text-shadow: 0 1px 0 #F6F6F6;
}
/* ============== */
/* = Classifier = */
@ -1735,4 +1753,71 @@ background: transparent;
height: 15px;
z-index: 10;
cursor: pointer;
}
/* ============= */
/* = Main Page = */
/* ============= */
.NB-account .NB-module {
margin: 12px 0 36px;
padding: 12px 0;
width: 478px;
clear: both;
}
.NB-account .NB-module a {
text-decoration: none;
color: #3E4773;
}
.NB-account .NB-module a:hover {
color: #0E1763;
}
.NB-account .NB-module h5 {
margin: 0 0 12px;
padding: 8px 12px 6px;
}
.NB-account .NB-module .NB-module-header-right {
font-size: 13px;
line-height: 16px;
font-weight: normal;
float: right;
}
/* ============ */
/* = Features = */
/* ============ */
.NB-module-features {
list-style: none;
padding: 0;
margin: 0 12px;
}
.NB-module-features .NB-module-feature {
margin: 0 0 12px;
}
.NB-module-features .NB-module-feature .NB-module-feature-date {
float: left;
text-transform: uppercase;
padding: 0 8px 12px 0;
color: #134160;
font-size: 12px;
font-weight: bold;
line-height: 16px;
white-space: nowrap;
}
.NB-module-features .NB-module-feature .NB-module-feature-description {
line-height: 16px;
font-size: 13px;
padding: 0 4px 12px 0;
}
#add-feature-form {
display: none;
}

View file

@ -364,10 +364,12 @@ NEWSBLUR.AssetModel.Reader.prototype = {
}
NEWSBLUR.Preferences.view_settings[preference] = value;
this.make_request('/profile/set', {
'preference': preference,
'value': value
}, callback, null);
if (NEWSBLUR.Globals.is_authenticated) {
this.make_request('/profile/set', {
'preference': preference,
'value': value
}, callback, null);
}
}
};

View file

@ -8,69 +8,115 @@
<div id="NB-splash">
<div class="NB-splash-info">
{% if user.is_authenticated %}
<div class="NB-user">
Welcome, {{ user.username}}.
(<a href="{% url logout %}?next=/">Logout</a>)
</div>
{% endif %}
{% if not user.is_authenticated %}
<div class="NB-account">
<div class="NB-account">
{% if not user.is_authenticated %}
<div class="NB-login">
{% if login_form.errors %}
{% for field, error in login_form.errors.items %}
{{ error }}
{% endfor %}
{% endif %}
<!--
<div class="NB-account-label">Want an account?</div>
<div class="NB-account-text">Talk to <a href="http://twitter.com/samuelclay">@samuelclay</a>.</div>
-->
<form method="post" action="{% url login %}">
<div>
{{ login_form.username.label_tag }}
{{ login_form.username }}
</div>
<div>
{{ login_form.password.label_tag }}
{{ login_form.password }}
</div>
<div class="NB-module">
<h5>
<div class="NB-module-header-login">Login</div>
<div class="NB-module-header-signup">Signup</div>
</h5>
<input name="submit" type="submit" value="login" />
<input type="hidden" name="next" value="/" />
</form>
<div class="NB-login">
{% if login_form.errors %}
{% for field, error in login_form.errors.items %}
{{ error }}
{% endfor %}
{% endif %}
<!--
<div class="NB-account-label">Want an account?</div>
<div class="NB-account-text">Talk to <a href="http://twitter.com/samuelclay">@samuelclay</a>.</div>
-->
<form method="post" action="{% url login %}">
<div>
{{ login_form.username.label_tag }}
{{ login_form.username }}
</div>
<div>
{{ login_form.password.label_tag }}
{{ login_form.password }}
</div>
<input name="submit" type="submit" value="login" />
<input type="hidden" name="next" value="/" />
</form>
</div>
<div class="NB-signup">
{% if signup_form.errors %}
{% for field, error in signup_form.errors.items %}
{{ error }}
{% endfor %}
{% endif %}
<form method="post" action="{% url signup %}">
<div>
{{ signup_form.signup_username.label_tag }}
{{ signup_form.signup_username }}
</div>
<div>
{{ signup_form.signup_password.label_tag }}
{{ signup_form.signup_password }}
</div>
<div>
{{ signup_form.email.label_tag }}
{{ signup_form.email }}
</div>
<input name="submit" type="submit" value="create account" />
<input type="hidden" name="next" value="/" />
</form>
</div>
</div>
<div class="NB-signup">
{% if signup_form.errors %}
{% for field, error in signup_form.errors.items %}
{{ error }}
{% endfor %}
{% endif %}
<form method="post" action="{% url signup %}">
<div>
{{ signup_form.signup_username.label_tag }}
{{ signup_form.signup_username }}
{% else %}
<div class="NB-module NB-module-stats">
<h5 class="NB-module-header">
Welcome, {{ user.username }}
<div class="NB-module-header-right">
<a href="{% url logout %}?next=/">Logout</a>
</div>
<div>
{{ signup_form.signup_password.label_tag }}
{{ signup_form.signup_password }}
</div>
<div>
{{ signup_form.email.label_tag }}
{{ signup_form.email }}
</div>
<input name="submit" type="submit" value="create account" />
<input type="hidden" name="next" value="/" />
</form>
</h5>
</div>
{% endif %}
<div class="NB-module NB-module-stats">
<h5 class="NB-module-header">
New Features
<div class="NB-module-header-right">
{% if user.is_staff %}
<a href="#" id="add-feature-button">Add</a>
<script>
$(document).ready(function() {
$('#add-feature-button').click(function(e) {
e.preventDefault();
$('#add-feature-form').fadeIn(500);
});
});
</script>
{% endif %}
</div>
</h5>
<table class="NB-module-features">
{% for feature in features %}
<tr class="NB-module-feature">
<td class="NB-module-feature-date">{{ feature.date|date:"M d, Y" }}</div>
<td class="NB-module-feature-description">{{ feature.description }}
</tr>
{% endfor %}
</table>
{% if user.is_staff %}
<form action="{% url add-feature %}" id="add-feature-form" method="post">
{{ feature_form.description }}
<input type="submit" value="Add feature" />
</form>
{% endif %}
</div>
{% endif %}
</div>
</div>
<div class="NB-splash-info NB-splash-info-about">