Merge branch 'master' of github.com:Gluejar/regluit into payment

pull/1/head
Raymond Yee 2012-01-03 09:53:51 -05:00
commit 81fc04ef12
31 changed files with 709 additions and 242 deletions

View File

@ -104,7 +104,10 @@ class ApiHelpView(TemplateView):
campaigns = models.Campaign.objects.all()
if len(campaigns):
c = campaigns[0]
try:
isbn = c.work.editions.all()[0].isbn_13
except IndexError:
isbn = ''
context["campaign"] = campaigns[0]
context["campaign_isbn"] = isbn

View File

@ -196,6 +196,7 @@ class Campaign(models.Model):
return self
def supporters(self):
"""nb: returns (distinct) supporter IDs, not supporter objects"""
translist = self.transactions().values_list('user', flat=True).distinct()
return translist
@ -224,7 +225,10 @@ class Work(models.Model):
@property
def googlebooks_id(self):
# may want to denormalize this at some point to avoid an extra query
try:
return self.editions.all()[0].googlebooks_id
except IndexError:
return ''
@property
def googlebooks_url(self):
@ -248,10 +252,16 @@ class Work(models.Model):
return "http://openlibrary.org" + self.openlibrary_id
def cover_image_small(self):
try:
return self.editions.all()[0].cover_image_small()
except IndexError:
return "/static/images/generic_cover_larger.png"
def cover_image_thumbnail(self):
try:
return self.editions.all()[0].cover_image_thumbnail()
except IndexError:
return "/static/images/generic_cover_larger.png"
def author(self):
authors = list(Author.objects.filter(editions__work=self).all())

View File

@ -3,6 +3,7 @@ from django import forms
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
from django.core.validators import validate_email
from django.utils.translation import ugettext_lazy as _
from django.forms.extras.widgets import SelectDateWidget
@ -192,10 +193,10 @@ class CampaignAdminForm(forms.Form):
pass
class EmailShareForm(forms.Form):
recipient = forms.EmailField()
sender = forms.EmailField(widget=forms.HiddenInput())
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea())
sender = forms.EmailField()
recipient = forms.EmailField()
# allows us to return user to original page by passing it as hidden form input
# we can't rely on POST or GET since the emailshare view handles both
# and may iterate several times as it catches user errors, losing URL info

View File

@ -6,6 +6,7 @@
{% url rightsholders as rhtoolsurl %}
{% url faq as faqurl %}
{% url rh_admin as adminurl %}
{% load truncatechars %}
<html>
<head>
@ -45,7 +46,7 @@
<div class="js-topmenu">
<ul class="menu">
{% if user.is_authenticated %}
<li class="first"><a href="/supporter/{{user.username}}"><span id="welcome">Welcome, {{ user.username }}</span></a></li>
<li class="first"><a href="/supporter/{{user.username}}"><span id="welcome">Welcome, {{ user.username|truncatechars:20 }}</span></a></li>
<li><a href="{% url auth_logout %}"><span>Sign Out</span></a></li>
{% else %}
<li class="first"><a href="{% url auth_login %}?next={% firstof request.path '/' %}"><span>Sign In</span></a></li>

View File

@ -1,6 +1,10 @@
{% extends "base.html" %}
{% block extra_head %}
<link href="/static/css/documentation.css" rel="stylesheet" type="text/css" />
{% block extra_js %}
<script type="text/javascript" src="/static/js/definitions.js"></script>
{% endblock %}
{% block extra_extra_head %}
<!-- extra head content in descendants goes in extra_extra_head, not extra_head, to avoid overwriting the documentation.css include -->
{% endblock %}
@ -9,31 +13,7 @@
{% block title %}{% endblock %}
{% block topsection %}
<div id="js-topsection">
<div class="js-main">
<div class="js-topnews">
<div class="user-block">
<div class="user-block1">
<div class="block-inner">
<div class="block-intro-text">With your help we raise money to buy book rights. The <span class="typo">unglued</span> books are free to download, here.</div>
<a class="my-setting readon"><span>Learn more</span></a>
</div>
</div>
<div class="user-block2">
<div class="block-inner">
<label class="title">Spread the Word</label>
<a href="https://www.facebook.com/sharer.php?u={{request.build_absolute_uri}}{{ request.path|urlencode:"" }}"><img src="/static/images/icons/facebook.png" alt="Facebook" title="Facebook" /></a>
<a href="https://twitter.com/intent/tweet?url={{request.build_absolute_uri}}{{ request.path|urlencode:"" }}&text=Unglue%ebooks%21"><img src="/static/images/icons/twitter.png" alt="Twitter" title="Twitter" /></a>
<a href="#"><img src="/static/images/icons/email.png" alt="email" title="email" /></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% include "learn_more.html" %}
{% endblock %}
{% block content %}

View File

@ -50,6 +50,11 @@
</div>
<!-- status of book vis-a-vis user's wishlist -->
{% if request.user.id in work.last_campaign.supporters %}
<div class="moreinfo on-wishlist">
<span>Pledged!</span>
</div>
{% else %}
{% ifequal supporter request.user %}
<div class="moreinfo remove-wishlist">
<span id="{{ work.id }}">Remove This</span>
@ -66,7 +71,7 @@
<div class="moreinfo add-wishlist">
<span id="{{ googlebooks_id }}">Add&nbsp;to&nbsp;Wishlist</span>
</div>
{% endif %}{% endif %}{% endifequal %}
{% endif %}{% endif %}{% endifequal %}{% endif %}
<!-- bibliographic data -->
<div class="white_text">
@ -90,6 +95,11 @@
</div>
<div class="listview author {{ work.author }}">{{ work.author }}</div>
</div>
{% if request.user.id in work.last_campaign.supporters %}
<div class="listview panelfront side1 on-wishlist">
<span>Pledged!</span>
</div>
{% else %}
{% ifequal supporter request.user %}
<div class="listview panelfront side1 remove-wishlist">
<span id="{{ work.id }}">Remove This</span>
@ -106,12 +116,11 @@
<div class="listview panelfront side1 add-wishlist">
<span id="{{ googlebooks_id }}">Add to Wishlist</span>
</div>
{% endif %}{% endif %}{% endifequal %}
{% endif %}{% endif %}{% endifequal %}{% endif %}
<div class="listview panelfront side1 booklist-status">
<span class="booklist-status-label">Status:&nbsp;</span><span class="booklist-status-text">{{ status }}</span>
</div>
<div class="listview panelfront side1 icons">
{% if fromsupport %}
{% if status == 'No campaign yet' or status == 'INITIALIZED' %}
<span class="rounded"><span class="grey"><span class="panelnope">Wished by&nbsp;</span>{{ work.wished_by.count }}</span></span>
{% else %}
@ -120,12 +129,6 @@
</div>
<div class="booklist-status-label">{{ work.percent_unglued_number }}%</div>
{% endif %}
{% else %}
<div class="booklist-status-img">
<img src="/static/images/images/icon-book-37by25-{{ work.percent_unglued }}.png" title="book list status" alt="book list status" />
</div>
<div class="booklist-status-label">{{ work.percent_unglued_number }}%</div>
{% endif %}
<div class="right_add"><img src="/static/images/book-panel/add_gray.png" alt="Add"/></div>
</div>
<div class="listview panelfront side1 ebooks">

View File

@ -0,0 +1,20 @@
{% load comments i18n %}
<form action="{% comment_form_target %}" method="post">{% csrf_token %}
{% if next %}<div><input type="hidden" name="next" value="{{ next }}" /></div>{% endif %}
{% for field in form %}
{% if field.is_hidden %}
<div>{{ field }}</div>
{% else %}
{% if field.errors %}{{ field.errors }}{% endif %}
{% ifequal field.name "honeypot" %}<p style="display:none;"{% if field.errors %} class="error"{% endif %}>{{ field.label_tag }} {{ field }}</p>{% endifequal %}
{% ifequal field.name "name" %}<div><input id="id_name" type="hidden" name="name" value="" /></div>{% endifequal %}
{% ifequal field.name "email" %}<div><input id="id_email" type="hidden" name="email" value="" /></div>{% endifequal %}
{% ifequal field.name "url" %}<div><input id="id_url" type="hidden" name="url" value="" /></div>{% endifequal %}
{% ifequal field.name "comment" %}<p{% if field.errors %} class="error"{% endif %}>Add your comment:<br /> <textarea id="id_comment" rows="4" cols="60" name="comment"></textarea></p>{% endifequal %}
{% endif %}
{% endfor %}
<input type="hidden" name="next" value="{{ request.get_full_path }}#" />
<p class="submit">
<input type="submit" name="post" class="submit-post" value="{% trans "Post" %}" />
</p>
</form>

View File

@ -0,0 +1,15 @@
<div id="comments">
{% for comment in comment_list %}
<div class="work_supporter">
<a href="/supporter/{{comment.user.username}}">
<div class="work_supporter_avatar">
{% if comment.user.profile.pic_url %}
<img class="user-avatar" src="{{ comment.user.profile.pic_url }}" height="50" width="50" alt="Picture of {{ comment.user }}" title="{{ comment.user }}" />
{% else %}
<img class="user-avatar" src="/static/images/header/avatar.png" height="50" width="50" alt="Generic Ungluer Avatar" title="Ungluer" />
{% endif %}
</div>
<span class="comment_username">{{comment.user.username }}</span></a> <span>({{ comment.submit_date }})</span> <br /><span class="comment">{{ comment.comment }}</span>
</div>
{% endfor %}
</div>

View File

@ -1,3 +1,4 @@
{% load truncatechars %}
<div class="jsmodule">
<h3 class="jsmod-title"><span>Explore</span></h3>
<div class="jsmod-content">
@ -25,7 +26,7 @@
{% else %}
<img src="/static/images/header/avatar.png" height="30" width="30" alt="Generic Ungluer Avatar" />
{% endif %}
<span class="ungluer-name">{{ungluer}}</span>
<span class="ungluer-name">{{ungluer|truncatechars:20}}</span>
</a></li>
{% endfor %}
</ul>

View File

@ -1,4 +1,5 @@
{% extends "base.html" %}
{% load truncatechars %}
{% block extra_css %}
<link type="text/css" rel="stylesheet" href="/static/css/landingpage.css" />
@ -9,29 +10,14 @@
<script src="/static/js/jquery-1.6.3.min.js"></script>
<!-- expands/collapses the learn more section -->
<script type="text/javascript">
var $j = jQuery.noConflict();
$j(document).ready(function(){
$j('.user-block-hide').hide();
$j('.user-block1 a').click(
function() {
$j(this).toggleClass("active");
$j(".user-block-hide").slideToggle(300);
$j("a.readon").toggleClass("down");
}
);
});
</script>
<!-- toggle to panelview state instead of listview default -->
<script type="application/x-javascript">
<script type="text/javascript">
jQuery(document).ready(function($) {
$('.listview').addClass("panelview").removeClass("listview");
});
</script>
<script type="text/javascript" src="/static/js/definitions.js"></script>
<script type="text/javascript" src="/static/js/greenpanel.js"></script>
<script src="/static/js/slides.min.jquery.js"></script>
@ -60,35 +46,7 @@ var $j = jQuery.noConflict();
{% endblock %}
{% block topsection %}
<div id="js-topsection">
<div class="js-main">
<div class="js-topnews">
<div class="user-block">
<div class="user-block1">
<div class="block-inner">
<div class="block-intro-text">With your help we raise money to buy book rights. The <span class="typo">unglued</span> books are free to download, here.</div>
<a class="my-setting readon"><span>Learn more</span></a>
</div>
</div>
<div class="user-block2">
<div class="block-inner">
<label class="title">Spread the Word</label>
<a href="https://www.facebook.com/sharer/sharer.php?src=bm&u={{request.build_absolute_uri}}"><img src="/static/images/supporter_icons/facebook_square.png" alt="Facebook" title="Facebook" /></a>
<a href="https://twitter.com/share"><img src="/static/images/supporter_icons/twitter_square.png" alt="tweeter" title="Twitter" /></a>
</div>
</div>
</div>
<div class="user-block-hide">
<div class="quicktour"><span class="highlight">We all have books we love so much, we'd like to give them to the world.</span> We want to share them, but also reward their creators. With digital books, it can be hard to do both.</div>
<div class="movingrightalong"></div>
<div class="quicktour"><span class="highlight">Unglue.it offers a win-win solution: Crowdfunding.</span> We run pledge campaigns for books; you chip in. When, together, we've reached the goal, we'll reward the book's creators and issue an unglued ebook.</div>
<div class="movingrightalong"></div>
<div class="quicktour last"><a href="https://creativecommons.org/">Creative Commons</a> licensing means everyone, everywhere can read and share the unglued book — freely and legally. <span class="highlight">You've given your favorite book to the world.</span></div>
</div>
</div>
</div>
</div>
{% include "learn_more.html" %}
{% endblock %}
{% block content %}
@ -201,7 +159,7 @@ var $j = jQuery.noConflict();
<li>
<div class="user-avatar"><a href="{% url supporter event.wishlist.user.username %}"><img src="{% if event.wishlist.user.picurl %}{{ event.wishlist.user.picurl}}{% else %}/static/images/landingpage/user-avatar.png{% endif %}" width="43" height="43" title="{{event.wishlist.user.username}}" alt="{{event.wishlist.user.username}} avatar" /></a></div>
<div class="user-book-info">
<p class="user-book-info"><a href="{% url supporter event.wishlist.user.username %}">{{event.wishlist.user.username}}</a> is Wishing For</p>
<p class="user-book-info"><a href="{% url supporter event.wishlist.user.username %}">{{event.wishlist.user.username|truncatechars:20}}</a> is Wishing For</p>
<a class="user-book-name" href="{% url work event.work.id %}">{{ event.work.title }}</a>
</div>
<div class="user-book-thumb"><a class="user-book-name" href="{% url work event.work.id %}"><img src="{{ event.work.cover_image_thumbnail }}" width="29" height="43" title="{{ event.work.title }}" alt="Very Long Book Title" /></a></div>

View File

@ -0,0 +1,36 @@
<div id="js-topsection">
<div class="js-main">
<div class="js-topnews">
<div class="user-block">
<div class="user-block1">
<div class="block-inner">
<div class="block-intro-text">
<div><span class="def">unglue</span> (v. t.) 1. To pay an author or publisher in full, up front, for publishing a Creative Commons ebook.</div>
<div><span class="def">unglue</span> (v. t.) 2. To make a digital book free to read and use, worldwide.</div>
<div><span class="def">unglue</span> (v. t.) 3. To make it clearly legal for a digital book to be used, distributed, archived and preserved by libraries.</div>
<div><span class="def">unglue</span> (v. t.) 4. For an author or publisher, to accept a fixed amount of money from the public for its unlimited use of an ebook.</div>
<div><span class="def">unglue</span> (v. t.) 5. To give your favorite books to everyone on earth.</div>
<div><span class="def">unglue</span> (v. t.) 6. To reward authors and publishers for sharing books with the world.</div>
</div>
<a class="my-setting readon"><span>Learn more</span></a>
</div>
</div>
<div class="user-block2">
<div class="block-inner">
<label class="title">Spread the Word</label>
<a href="https://www.facebook.com/sharer/sharer.php?src=bm&u={{request.build_absolute_uri}}"><img src="/static/images/supporter_icons/facebook_square.png" alt="Facebook" title="Facebook" /></a>
<a href="https://twitter.com/share"><img src="/static/images/supporter_icons/twitter_square.png" alt="tweeter" title="Twitter" /></a>
</div>
</div>
</div>
<div class="user-block-hide">
<div class="quicktour"><span class="highlight">We all have books we love so much, we'd like to give them to the world.</span> We want to share them, but also reward their creators. With digital books, it can be hard to do both.</div>
<div class="movingrightalong"></div>
<div class="quicktour"><span class="highlight">Unglue.it offers a win-win solution: Crowdfunding.</span> We run pledge campaigns for books; you chip in. When, together, we've reached the goal, we'll reward the book's creators and issue an unglued ebook.</div>
<div class="movingrightalong"></div>
<div class="quicktour last"><a href="https://creativecommons.org/">Creative Commons</a> licensing means everyone, everywhere can read and share the unglued book — freely and legally. <span class="highlight">You've given your favorite book to the world.</span></div>
</div>
</div>
</div>
</div>

View File

@ -2,8 +2,6 @@
{% block extra_css %}
<link rel="stylesheet" href="/static/css/book_list.css">
<link rel="stylesheet" href="/static/css/book_panel.css">
<!-- important! search.css goes last -->
<link rel="stylesheet" href="/static/css/search.css">
{% endblock %}
{% block extra_head %}
<script type="text/javascript" src="/static/js/wishlist.js"></script>
@ -43,8 +41,10 @@
{% for work in results %}
<div class="{% cycle 'row1' 'row2' %}">
{% with work.googlebooks_id as googlebooks_id %}
{% with work.last_campaign_status as status %}
{% with work.last_campaign.deadline as deadline %}
{% include "book_panel.html" %}
{% endwith %}
{% endwith %}{% endwith %}{% endwith %}
</div>
{% empty %}
<h2>Sorry, couldn't find that!</h2>

View File

@ -1,5 +1,6 @@
{% extends "base.html" %}
{% load endless %}
{% load truncatechars %}
{% block title %} &#8212; {{ supporter.username }}{% endblock %}
{% block extra_css %}
@ -61,7 +62,7 @@ how do I integrate the your wishlist thing with the tabs thing?
{% else %}
<img class="user-avatar" src="/static/images/header/avatar.png" height="50" width="50" alt="Generic Ungluer Avatar" title="Ungluer" />
{% endif %}
<span class="user-name"><a href="#">{{ supporter.username }}</a></span>
<span class="user-name"><a href="#">{{ supporter.username|truncatechars:20 }}</a></span>
<span class="user-date">{{ date }}</span>
<span class="user-short-info">{{ supporter.profile.tagline }}</span>
</div>

View File

@ -1,4 +1,5 @@
{% extends "base.html" %}
{% load comments %}
{% block title %}&#151; {% if work.last_campaign_status == 'ACTIVE' %}Campaign to unglue {% endif %}{{ work.title }}{% endblock %}
{% block extra_css %}
@ -111,6 +112,10 @@ jQuery(document).ready(function(){
<div class="create-account">
<span title="{% url work work.id %}">Login to Add</span>
</div>
{% else %}{% if request.user.id in work.last_campaign.supporters %}
<div class="add-wishlist">
<span class="on-wishlist">Pledged!</span>
</div>
{% else %}{% if work in request.user.wishlist.works.all %}
<div class="remove-wishlist-workpage">
<span id="{{ work.id }}">Remove This</span>
@ -119,15 +124,15 @@ jQuery(document).ready(function(){
<div class="add-wishlist">
<span id="{{ work.googlebooks_id }}">Add to Wishlist</span>
</div>
{% endif %}{% endif %}
{% endif %}{% endif %}{% endif %}
</div>
</div>
</div>
{% get_comment_count for work as comment_count %}
<div class="content-block-heading" id="tabs">
<ul class="tabs">
<li class="tabs1 active"><a href="#">Campaign</a></li>
<li class="tabs2"><a href="#">Community </a></li>
<li class="tabs1 active"><a href="#">{% if status == 'ACTIVE' %}Campaign{% else %}Description{% endif %}</a></li>
<li class="tabs2"><a href="#">Comments ({{comment_count}})</a></li>
<li class="tabs3"><a href="#">Supporters</a></li>
<li class="tabs4"><a href="#">Details</a></li>
</ul>
@ -145,7 +150,7 @@ jQuery(document).ready(function(){
{{ claim.rights_holder.rights_holder_name }}
{% endif %}
{% endfor %}
has agreed to release <i>{{work.title}}</i> to the world as a Creative Commons licensed ebook if ungluers can join together to raise ${{ work.last_campaign.target }} by {{ work.last_campaign.deadline }}.
, has agreed to release <i>{{work.title}}</i> to the world as a Creative Commons licensed ebook if ungluers can join together to raise ${{ work.last_campaign.target }} by {{ work.last_campaign.deadline }}.
You can help!</p>
{{ work.last_campaign.description|safe }}
{% else %}
@ -157,13 +162,14 @@ jQuery(document).ready(function(){
</div>
<div id="tabs-2" class="tabs">
<div class="tabs-content">
Being Unglued infomation
{% render_comment_list for work %}
{% if user.is_authenticated %}{% render_comment_form for work %}{% endif %}
</div>
</div>
<div id="tabs-3" class="tabs">
<div class="tabs-content">
{% for supporter in work.wished_by %}
<div class="work_supporter">
<div class="work_supporter_nocomment">
<a href="/supporter/{{supporter}}">
<div class="work_supporter_avatar">
{% if supporter.profile.pic_url %}
@ -233,11 +239,11 @@ jQuery(document).ready(function(){
<h3 class="jsmod-title"><span>Share</span></h3>
<div class="jsmod-content">
<ul class="social menu">
<a href="https://www.facebook.com/sharer.php?u={{request.build_absolute_uri}}{{ request.path|urlencode:"" }}"><li class="facebook first"><span>Facebook</span></li></a>
<a href="https://twitter.com/intent/tweet?url={{request.build_absolute_uri}}{{ request.path|urlencode:"" }}&text=I'm%20ungluing%20{{ work.title|urlencode }}%20at%20%40unglueit"><li class="twitter"><span>Twitter</span></li></a>
<a href="{% url emailshare %}?next={% firstof request.path '/' %}"><li class="email"><span>Email</span></li></a>
<a href="https://www.facebook.com/sharer.php?u={{request.build_absolute_uri|urlencode:"" }}"><li class="facebook first"><span>Facebook</span></li></a>
<a href="https://twitter.com/intent/tweet?url={{request.build_absolute_uri|urlencode:"" }}&text=I'm%20ungluing%20{{ work.title|urlencode }}%20at%20%40unglueit"><li class="twitter"><span>Twitter</span></li></a>
{% if request.user.is_authenticated %}<a href="{% url emailshare %}?next={{request.build_absolute_uri|urlencode:""}}"><li class="email"><span>Email</span></li></a>{% endif %}
<a href="#" id="embed"><li class="embed"><span>Embed</span></li></a>
<div id="widgetcode">Copy/paste this into your site:<br /><textarea rows="7" cols="22">&lt;iframe src="{{request.build_absolute_uri}}/api/widget/{{work.editions.all.0.isbn_13}}/" width="152" height="325" frameborder="0"&gt;&lt;/iframe&gt;</textarea></div>
<div id="widgetcode">Copy/paste this into your site:<br /><textarea rows="7" cols="22">&lt;iframe src="https://{{request.META.HTTP_HOST}}/api/widget/{{work.editions.all.0.isbn_13}}/" width="152" height="325" frameborder="0"&gt;&lt;/iframe&gt;</textarea></div>
</ul>
</div>
</div>

View File

View File

@ -0,0 +1,185 @@
"""
The truncatechars filter is part of Django dev, but we're on 1.3.1
The following is the filter and its dependencies
To use this filter, put "{% load truncatechars %}" at the beginning of your template,
then {{ myvariable|truncatechars:num }}
"""
import unicodedata
from django import template
from django.template.base import Library
from django.template.defaultfilters import stringfilter
from django.utils.encoding import force_unicode
from django.utils.functional import allow_lazy, SimpleLazyObject
from django.utils.translation import pgettext
register = Library()
class Truncator(SimpleLazyObject):
"""
An object used to truncate text, either by characters or words.
"""
def __init__(self, text):
super(Truncator, self).__init__(lambda: force_unicode(text))
def add_truncation_text(self, text, truncate=None):
if truncate is None:
truncate = pgettext(
'String to return when truncating text',
u'%(truncated_text)s...')
truncate = force_unicode(truncate)
if '%(truncated_text)s' in truncate:
return truncate % {'truncated_text': text}
# The truncation text didn't contain the %(truncated_text)s string
# replacement argument so just append it to the text.
if text.endswith(truncate):
# But don't append the truncation text if the current text already
# ends in this.
return text
return '%s%s' % (text, truncate)
def chars(self, num, truncate=None):
"""
Returns the text truncated to be no longer than the specified number
of characters.
Takes an optional argument of what should be used to notify that the
string has been truncated, defaulting to a translatable string of an
ellipsis (...).
"""
length = int(num)
uniself = unicode(self._wrapped)
text = unicodedata.normalize('NFC', uniself)
# Calculate the length to truncate to (max length - end_text length)
truncate_len = length
for char in self.add_truncation_text('', truncate):
if not unicodedata.combining(char):
truncate_len -= 1
if truncate_len == 0:
break
s_len = 0
end_index = None
for i, char in enumerate(text):
if unicodedata.combining(char):
# Don't consider combining characters
# as adding to the string length
continue
s_len += 1
if end_index is None and s_len > truncate_len:
end_index = i
if s_len > length:
# Return the truncated string
return self.add_truncation_text(text[:end_index or 0],
truncate)
# Return the original string since no truncation was necessary
return text
chars = allow_lazy(chars)
def words(self, num, truncate=None, html=False):
"""
Truncates a string after a certain number of words. Takes an optional
argument of what should be used to notify that the string has been
truncated, defaulting to ellipsis (...).
"""
length = int(num)
if html:
return self._html_words(length, truncate)
return self._text_words(length, truncate)
words = allow_lazy(words)
def _text_words(self, length, truncate):
"""
Truncates a string after a certain number of words.
Newlines in the string will be stripped.
"""
words = self._wrapped.split()
if len(words) > length:
words = words[:length]
return self.add_truncation_text(u' '.join(words), truncate)
return u' '.join(words)
def _html_words(self, length, truncate):
"""
Truncates HTML to a certain number of words (not counting tags and
comments). Closes opened tags if they were correctly closed in the
given HTML.
Newlines in the HTML are preserved.
"""
if length <= 0:
return u''
html4_singlets = (
'br', 'col', 'link', 'base', 'img',
'param', 'area', 'hr', 'input'
)
# Count non-HTML words and keep note of open tags
pos = 0
end_text_pos = 0
words = 0
open_tags = []
while words <= length:
m = re_words.search(self._wrapped, pos)
if not m:
# Checked through whole string
break
pos = m.end(0)
if m.group(1):
# It's an actual non-HTML word
words += 1
if words == length:
end_text_pos = pos
continue
# Check for tag
tag = re_tag.match(m.group(0))
if not tag or end_text_pos:
# Don't worry about non tags or tags after our truncate point
continue
closing_tag, tagname, self_closing = tag.groups()
# Element names are always case-insensitive
tagname = tagname.lower()
if self_closing or tagname in html4_singlets:
pass
elif closing_tag:
# Check for match in open tags list
try:
i = open_tags.index(tagname)
except ValueError:
pass
else:
# SGML: An end tag closes, back to the matching start tag,
# all unclosed intervening start tags with omitted end tags
open_tags = open_tags[i + 1:]
else:
# Add it to the start of the open tags list
open_tags.insert(0, tagname)
if words <= length:
# Don't try to close tags if we don't need to truncate
return self._wrapped
out = self._wrapped[:end_text_pos]
truncate_text = self.add_truncation_text('', truncate)
if truncate_text:
out += truncate_text
# Close any tags still open
for tag in open_tags:
out += '</%s>' % tag
# Return string
return out
# django dev uses filter(is_safe=True) syntax here, but that's not yet available in 1.3.1
@register.filter()
@stringfilter
def truncatechars(value, arg):
"""
Truncates a string after a certain number of characters.
Argument: Number of characters to truncate after.
"""
try:
length = int(arg)
except ValueError: # Invalid literal for int().
return value # Fail silently.
return Truncator(value).chars(length)
truncatechars.is_safe = True

View File

@ -18,6 +18,7 @@ from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.core.exceptions import ObjectDoesNotExist
from django.core.mail import send_mail
from django.contrib import messages
from django.forms import Select
from django.forms.models import modelformset_factory
from django.http import HttpResponseRedirect
@ -80,8 +81,10 @@ def work(request, work_id, action='display'):
work = get_object_or_404(models.Work, id=work_id)
editions = work.editions.all().order_by('-publication_date')
campaign = work.last_campaign()
try:
pubdate = work.editions.all()[0].publication_date[:4]
except IndexError:
pubdate = 'unknown'
if not request.user.is_anonymous():
claimform = UserClaimForm( request.user, data={'work':work_id, 'user': request.user.id})
else:
@ -631,30 +634,20 @@ def search(request):
# flag search result as on wishlist as appropriate
if not request.user.is_anonymous():
# get a list of all the googlebooks_ids for works on the user's wishlist
wishlist = request.user.wishlist
editions = models.Edition.objects.filter(work__wishlists__in=[wishlist])
googlebooks_ids = [e['googlebooks_id'] for e in editions.values('googlebooks_id')]
ungluers = userlists.other_users(request.user, 5)
# if the results is on their wishlist flag it
for result in results:
if result['googlebooks_id'] in googlebooks_ids:
result['on_wishlist'] = True
else:
result['on_wishlist'] = False
else:
ungluers = userlists.other_users(None, 5)
# also urlencode some parameters we'll need to pass to workstub in the title links
# needs to be done outside the if condition
works=[]
for result in results:
result['urlimage'] = urllib.quote_plus(sub('^https?:\/\/','', result['cover_image_thumbnail']).encode("utf-8"), safe='')
result['urlauthor'] = urllib.quote_plus(result['author'].encode("utf-8"), safe='')
result['urltitle'] = urllib.quote_plus(result['title'].encode("utf-8"), safe='')
try:
edition = models.Edition.objects.get(googlebooks_id=result['googlebooks_id'])
works.append(edition.work)
except models.Edition.DoesNotExist:
works.append(result)
context = {
"q": q,
"results": results,
"results": works,
"ungluers": ungluers
}
return render(request, 'search.html', context)
@ -1017,6 +1010,7 @@ def work_goodreads(request, work_id):
url = "http://www.goodreads.com/search?" + q
return HttpResponseRedirect(url)
@login_required
def emailshare(request):
if request.method == 'POST':
form=EmailShareForm(request.POST)
@ -1037,6 +1031,10 @@ def emailshare(request):
next = request.GET['next']
except:
next = ''
form = EmailShareForm(initial={'next':next})
if request.user.is_authenticated():
sender = request.user.email
else:
sender = ''
form = EmailShareForm(initial={'next':next, 'message':"I'm ungluing books at unglue.it. Here's one of my favorites: "+next, "sender":sender})
return render(request, "emailshare.html", {'form':form})

View File

@ -269,7 +269,7 @@ class PaymentManager( object ):
pledged_list = []
if authorized:
authorized_list = Transaction.objects.filter(type=PAYMENT_TYPE_AUTHORIZATION,
authorized_list = transaction_list.filter(type=PAYMENT_TYPE_AUTHORIZATION,
status="ACTIVE")
else:
authorized_list = []

View File

@ -88,6 +88,7 @@ TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
join(PROJECT_DIR, "frontend", "templates"),
join(PROJECT_DIR, "frontend", "templates", "registration"),
)
@ -98,6 +99,7 @@ INSTALLED_APPS = (
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.comments',
'south',
'django_extensions',
'regluit.frontend',
@ -110,6 +112,7 @@ INSTALLED_APPS = (
'djcelery',
'endless_pagination',
'selectable',
'regluit.frontend.templatetags',
# this must appear *after* django.frontend or else it overrides the
# registration templates in frontend/templates/registration

View File

@ -261,6 +261,9 @@ div.content-block-content {
padding: 5px;
border: solid 5px #EDF3F4;
}
.tabs-content form {
margin-left: -5px;
}
ul.social li {
padding: 5px 0 5px 30px;
height: 28px;
@ -348,20 +351,19 @@ ul.support li:hover {
ul.support li:hover a {
color: #fff;
}
.work_supporter {
.work_supporter_nocomment {
height: 50px;
margin-top: 5px;
vertical-align: middle;
margin-left: -5px;
}
.work_supporter .work_supporter_avatar {
.work_supporter_avatar {
float: left;
}
.work_supporter .work_supporter_name {
.work_supporter_name {
height: 50px;
line-height: 50px;
float: left;
margin-left: 5px;
}
/* this line differs from sitewide.css. should it? */
a {
@ -417,3 +419,13 @@ a {
.editions a:hover {
text-decoration: underline;
}
.work_supporter {
height: auto;
min-height: 50px;
margin-top: 5px;
vertical-align: middle;
margin-left: -5px;
}
.work_supporter_avatar {
margin-right: 5px;
}

View File

@ -13,14 +13,62 @@
border-style: solid none;
border-color: #FFFFFF;
}
.user-block {
width: 100%;
clear: both;
/* variables and mixins used in multiple less files go here */
.header-text {
height: 36px;
line-height: 36px;
display: block;
text-decoration: none;
font-weight: bold;
font-size: 13px;
letter-spacing: -0.05em;
}
.panelborders {
border-width: 1px 0px;
border-style: solid none;
border-color: #FFFFFF;
}
.user-block-hide {
float: left;
width: 100%;
clear: both;
border-top: solid 1px #8ac3d7;
margin-top: 20px;
}
.user-block-hide .quicktour {
width: 270px;
float: left;
font-style: italic;
line-height: 20px;
font-size: 13px;
margin-top: 20px;
}
.user-block-hide .quicktour .highlight {
font-weight: bold;
}
.user-block-hide .quicktour.last {
padding-right: 0px;
width: 270px;
background: url("/static/images/landingpage/signmeup-arrow.png") no-repeat center bottom;
padding-bottom: 42px;
}
.user-block-hide .movingrightalong {
background: url("/static/images/landingpage/quicktour-arrow.png") no-repeat center;
height: 100px;
width: 75px;
float: left;
margin-top: 20px;
}
.block-intro-text div {
display: none;
}
.block-intro-text div#active {
display: inherit;
}
/* Learn More area (not already styles by learnmore.less) */
.user-block {
width: 100%;
clear: both;
}
.user-block1, .user-block2 {
float: left;
@ -69,6 +117,9 @@
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
}
.have-right #js-rightcol .jsmodule {
border-bottom: 1px solid #3c4e52;
@ -102,6 +153,9 @@
-moz-border-radius: 14px 14px 14px 14px;
-webkit-border-radius: 14px 14px 14px 14px;
border-radius: 14px 14px 14px 14px;
-moz-border-radius: 14px 14px 14px 14px;
-webkit-border-radius: 14px 14px 14px 14px;
border-radius: 14px 14px 14px 14px;
padding: 10px;
background: #edf3f4;
}
@ -110,6 +164,9 @@
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
padding: 10px;
font-style: italic;
}
@ -118,6 +175,9 @@ dt {
-moz-border-radius: 14px 14px 14px 14px;
-webkit-border-radius: 14px 14px 14px 14px;
border-radius: 14px 14px 14px 14px;
-moz-border-radius: 14px 14px 14px 14px;
-webkit-border-radius: 14px 14px 14px 14px;
border-radius: 14px 14px 14px 14px;
padding: 10px 0px 10px 7px;
background: #edf3f4;
}

View File

@ -13,6 +13,58 @@
border-style: solid none;
border-color: #FFFFFF;
}
/* variables and mixins used in multiple less files go here */
.header-text {
height: 36px;
line-height: 36px;
display: block;
text-decoration: none;
font-weight: bold;
font-size: 13px;
letter-spacing: -0.05em;
}
.panelborders {
border-width: 1px 0px;
border-style: solid none;
border-color: #FFFFFF;
}
.user-block-hide {
float: left;
width: 100%;
clear: both;
border-top: solid 1px #8ac3d7;
margin-top: 20px;
}
.user-block-hide .quicktour {
width: 270px;
float: left;
font-style: italic;
line-height: 20px;
font-size: 13px;
margin-top: 20px;
}
.user-block-hide .quicktour .highlight {
font-weight: bold;
}
.user-block-hide .quicktour.last {
padding-right: 0px;
width: 270px;
background: url("/static/images/landingpage/signmeup-arrow.png") no-repeat center bottom;
padding-bottom: 42px;
}
.user-block-hide .movingrightalong {
background: url("/static/images/landingpage/quicktour-arrow.png") no-repeat center;
height: 100px;
width: 75px;
float: left;
margin-top: 20px;
}
.block-intro-text div {
display: none;
}
.block-intro-text div#active {
display: inherit;
}
#expandable {
display: none;
}
@ -45,37 +97,6 @@
width: 100%;
clear: both;
}
.user-block-hide {
float: left;
width: 100%;
clear: both;
border-top: solid 1px #8ac3d7;
margin-top: 20px;
}
.user-block-hide .quicktour {
width: 270px;
float: left;
font-style: italic;
line-height: 20px;
font-size: 13px;
margin-top: 20px;
}
.user-block-hide .quicktour .highlight {
font-weight: bold;
}
.user-block-hide .quicktour.last {
padding-right: 0px;
width: 270px;
background: url("/static/images/landingpage/signmeup-arrow.png") no-repeat center bottom;
padding-bottom: 42px;
}
.user-block-hide .movingrightalong {
background: url("/static/images/landingpage/quicktour-arrow.png") no-repeat center;
height: 100px;
width: 75px;
float: left;
margin-top: 20px;
}
.user-block1, .user-block2 {
float: left;
}
@ -106,6 +127,8 @@
font-size: 20px;
height: 30px;
line-height: 30px;
height: 30px;
line-height: 30px;
color: #3d4e53;
padding-right: 15px;
}
@ -113,10 +136,15 @@
float: right;
height: 24px;
line-height: 24px;
height: 24px;
line-height: 24px;
width: 24px;
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
border: solid 4px #3d4e53;
text-align: center;
font-size: 17px;
@ -136,6 +164,9 @@
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
margin-bottom: 10px;
padding: 0 10px 10px 10px;
}
@ -144,12 +175,17 @@
padding-bottom: 10px;
}
#js-rightcol .jsmodule input, #js-rightcol2 .jsmodule input {
-moz-border-radius: 32px 32px 32px 32px;
-webkit-border-radius: 32px 32px 32px 32px;
border-radius: 32px 32px 32px 32px;
-moz-border-radius: 32px 32px 32px 32px;
-webkit-border-radius: 32px 32px 32px 32px;
border-radius: 32px 32px 32px 32px;
border: none;
height: 36px;
line-height: 36px;
height: 36px;
line-height: 36px;
width: 90%;
outline: none;
padding-left: 16px;
@ -227,6 +263,9 @@ div.typo2 {
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
-moz-border-radius: 12px 12px 12px 12px;
-webkit-border-radius: 12px 12px 12px 12px;
border-radius: 12px 12px 12px 12px;
padding: 10px;
font-style: italic;
}
@ -242,6 +281,8 @@ div.signup_btn a {
font-weight: bold;
height: 36px;
line-height: 36px;
height: 36px;
line-height: 36px;
letter-spacing: 1px;
text-decoration: none;
text-transform: capitalize;
@ -295,6 +336,9 @@ h2.page-heading {
margin-top: 55px;
}
#js-maincontainer-bot-block #js-search {
-moz-border-radius: 64px 64px 64px 64px;
-webkit-border-radius: 64px 64px 64px 64px;
border-radius: 64px 64px 64px 64px;
-moz-border-radius: 64px 64px 64px 64px;
-webkit-border-radius: 64px 64px 64px 64px;
border-radius: 64px 64px 64px 64px;
@ -327,6 +371,8 @@ h2.page-heading {
color: #66942e;
height: 26px;
line-height: 26px;
height: 26px;
line-height: 26px;
font-size: 13px;
float: left;
padding: 0;
@ -375,6 +421,9 @@ h2.page-heading {
-moz-border-radius: 10px 10px 0 0;
-webkit-border-radius: 10px 10px 0 0;
border-radius: 10px 10px 0 0;
-moz-border-radius: 10px 10px 0 0;
-webkit-border-radius: 10px 10px 0 0;
border-radius: 10px 10px 0 0;
font-size: 18px;
overflow: hidden;
display: inline-block;
@ -389,6 +438,8 @@ h3.module-title {
padding: 14px 0;
}
.google_signup div {
height: 32px;
line-height: 32px;
height: 32px;
line-height: 32px;
float: left;

52
static/css/learnmore.css Normal file
View File

@ -0,0 +1,52 @@
/* variables and mixins used in multiple less files go here */
.header-text {
height: 36px;
line-height: 36px;
display: block;
text-decoration: none;
font-weight: bold;
font-size: 13px;
letter-spacing: -0.05em;
}
.panelborders {
border-width: 1px 0px;
border-style: solid none;
border-color: #FFFFFF;
}
.user-block-hide {
float: left;
width: 100%;
clear: both;
border-top: solid 1px #8ac3d7;
margin-top: 20px;
}
.user-block-hide .quicktour {
width: 270px;
float: left;
font-style: italic;
line-height: 20px;
font-size: 13px;
margin-top: 20px;
}
.user-block-hide .quicktour .highlight {
font-weight: bold;
}
.user-block-hide .quicktour.last {
padding-right: 0px;
width: 270px;
background: url("/static/images/landingpage/signmeup-arrow.png") no-repeat center bottom;
padding-bottom: 42px;
}
.user-block-hide .movingrightalong {
background: url("/static/images/landingpage/quicktour-arrow.png") no-repeat center;
height: 100px;
width: 75px;
float: left;
margin-top: 20px;
}
.block-intro-text div {
display: none;
}
.block-intro-text div#active {
display: inherit;
}

View File

@ -88,6 +88,13 @@ ul.menu {
padding: 0;
margin: 0;
}
/* Learn More menu */
.block-intro-text {
padding-right: 10px;
}
.block-intro-text span.def {
font-style: italic;
}
a.readon {
background: url("/static/images/learnmore-downarrow.png") right center no-repeat;
color: #fff;
@ -149,6 +156,8 @@ a.readon span {
font-weight: bold;
font-size: 13px;
letter-spacing: -0.05em;
overflow: auto;
max-width: 240px;
}
.js-topmenu ul li.last a {
background: url("/static/images/bg.png") right top no-repeat;

30
static/js/definitions.js Normal file
View File

@ -0,0 +1,30 @@
// expand or collapse the learn more section
var $j = jQuery.noConflict();
$j(document).ready(function(){
$j('.user-block-hide').hide();
$j('.user-block1 a').click(
function() {
$j(this).toggleClass("active");
$j(".user-block-hide").slideToggle(300);
$j("a.readon").toggleClass("down");
}
);
});
// make a random ungluing definition active onload
$j(document).ready(function() {
var length = $j(".block-intro-text div").length;
var ran = Math.floor(Math.random()*length)+1;
$j(".block-intro-text div:nth-child(" + ran + ")").attr('id', 'active');
});
// change the ungluing def onclick
$j(document).delegate(".block-intro-text", "click", function() {
var length = $j(".block-intro-text div").length;
// minus one because length includes THIS div and we want the set of its siblings only
var ran = Math.floor(Math.random()*length)-1;
// make sure our next active div is not the current one!
$j(this).children("#active").siblings().eq(ran).attr('id', 'foo');
$j(this).children("#active").removeAttr('id');
$j(this).children("#foo").attr('id', 'active');
});

View File

@ -303,6 +303,10 @@ div.content-block-content {
iframe {
.mediaborder;
}
form {
margin-left: -5px;
}
}
ul.social li {
@ -361,21 +365,20 @@ ul.support li {
}
}
.work_supporter {
.work_supporter_nocomment {
height: 50px;
margin-top: 5px;
vertical-align: middle;
margin-left:-5px;
}
.work_supporter_avatar {
.work_supporter_avatar {
float: left;
}
}
.work_supporter_name {
.work_supporter_name {
.height(50px);
float: left;
margin-left: 5px;
}
}
/* this line differs from sitewide.css. should it? */
@ -426,3 +429,14 @@ a{ color:#3d4e53; font-size:12px;}
text-decoration: underline;
}
}
.work_supporter {
height: auto;
min-height: 50px;
margin-top: 5px;
vertical-align: middle;
margin-left:-5px;
}
.work_supporter_avatar {
margin-right: 5px;
}

View File

@ -1,17 +1,12 @@
// Styles basedocumentation.html and its descendants.
@import "variables.less";
@import "learnmore.less";
// Learn More area
/* Learn More area (not already styles by learnmore.less) */
.user-block {
width:100%; clear:both;
}
.user-block-hide {
float: left;
width:100%;
clear:both;
}
.user-block1, .user-block2 {
float:left;
}

View File

@ -1,4 +1,5 @@
@import "variables.less";
@import "learnmore.less";
.clickyarrows() {
text-indent:-10000px;
@ -48,42 +49,6 @@
clear:both;
}
.user-block-hide {
float: left;
width:100%;
clear:both;
border-top: solid 1px @bright-blue;
margin-top: 20px;
.quicktour {
width: 270px;
float: left;
font-style: italic;
line-height:20px;
font-size:13px;
margin-top: 20px;
.highlight {
font-weight: bold;
}
&.last {
padding-right:0px;
width:270px;
background: url("@{image-base}landingpage/signmeup-arrow.png") no-repeat center bottom;
padding-bottom: 42px;
}
}
.movingrightalong {
background: url("@{image-base}landingpage/quicktour-arrow.png") no-repeat center;
height: 100px;
width: 75px;
float: left;
margin-top: 20px;
}
}
.user-block1, .user-block2 {
float:left;
}

View File

@ -0,0 +1,45 @@
@import "variables.less";
.user-block-hide {
float: left;
width:100%;
clear:both;
border-top: solid 1px @bright-blue;
margin-top: 20px;
.quicktour {
width: 270px;
float: left;
font-style: italic;
line-height:20px;
font-size:13px;
margin-top: 20px;
.highlight {
font-weight: bold;
}
&.last {
padding-right:0px;
width:270px;
background: url("@{image-base}landingpage/signmeup-arrow.png") no-repeat center bottom;
padding-bottom: 42px;
}
}
.movingrightalong {
background: url("@{image-base}landingpage/quicktour-arrow.png") no-repeat center;
height: 100px;
width: 75px;
float: left;
margin-top: 20px;
}
}
.block-intro-text div {
display: none;
&#active {
display: inherit;
}
}

View File

@ -96,6 +96,15 @@ ul.menu{
margin:0;
}
/* Learn More menu */
.block-intro-text {
padding-right: 10px;
span.def {
font-style: italic;
}
}
a.readon {
background:url("@{image-base}learnmore-downarrow.png") right center no-repeat;
color:#fff;
@ -150,6 +159,9 @@ a.readon {
span#welcome {
color:@green;
.header-text;
overflow:auto;
//emergency backstop to prevent usernames too long for truncate filter to force header into content area
max-width: 240px;
}
&.last {

View File

@ -14,4 +14,5 @@ urlpatterns = patterns('',
(r'', include('regluit.payment.urls')),
(r'^selectable/', include('selectable.urls')),
url(r'^admin/', include(admin_site.urls)),
(r'^comments/', include('django.contrib.comments.urls')),
)