create and edit Library objects, which now have names and owners

pull/1/head
eric 2013-11-27 22:14:52 -05:00
parent 19179f695d
commit ccb1f0dd86
18 changed files with 321 additions and 36 deletions

View File

@ -155,6 +155,9 @@ Not needed on newly designed home page?
<li><a href="{{ rhtoolsurl }}">Rights Holder Tools</a></li> <li><a href="{{ rhtoolsurl }}">Rights Holder Tools</a></li>
<li><a href="{{ privacyurl }}">Privacy</a></li> <li><a href="{{ privacyurl }}">Privacy</a></li>
<li><a href="{{ termsurl }}">Terms of Use</a></li> <li><a href="{{ termsurl }}">Terms of Use</a></li>
{% for library in user.libraries.all %}
<li><a href="{% url library_admin library.id %}">{{ library }} Administration</a></li>
{% endfor %}
{% if user.is_staff %} {% if user.is_staff %}
<li><a href="{{ adminurl }}">Unglue.it Administration</a></li> <li><a href="{{ adminurl }}">Unglue.it Administration</a></li>
<li><a href="{{ editionurl }}">Create New Editions</a></li> <li><a href="{{ editionurl }}">Create New Editions</a></li>

View File

@ -11,7 +11,7 @@
{% if not request.user.is_anonymous %} {% if not request.user.is_anonymous %}
<li class="first"><a href="/"><span>My Books</span></a></li> <li class="first"><a href="/"><span>My Books</span></a></li>
{% for library in request.user.profile.libraries %} {% for library in request.user.profile.libraries %}
<li><a href="{% url library library %}"><span>{{ library }}</span></a></li> <li><a href="{% url library library.user %}"><span>{{ library }}</span></a></li>
{% endfor %} {% endfor %}
<li> <li>
{% else %} {% else %}
@ -55,7 +55,7 @@
</a></li> </a></li>
{% endfor %} {% endfor %}
{% if library %} {% if library %}
<li class="first"><a href="{% url library_users library %}"><span>More from {{ library }}</span></a></li> <li class="first"><a href="{% url library_users library.id %}"><span>More from {{ library }}</span></a></li>
{% endif %} {% endif %}
</ul> </ul>
</li> </li>

View File

@ -25,16 +25,21 @@
<p>We love libraries because we're from the library world. And because we live in a real world that still needs libraries. We've built Unglue.it in part because we've seen that the ebook system we have now really doesn't work for libraries, and we believe in making things better.</p> <p>We love libraries because we're from the library world. And because we live in a real world that still needs libraries. We've built Unglue.it in part because we've seen that the ebook system we have now really doesn't work for libraries, and we believe in making things better.</p>
<p>We started out with "Pledge" campaigns to help books become Creative Commons licensed ebooks. <p>We started out with "Pledge" campaigns to help books become Creative Commons licensed ebooks.
Weve added "Buy-to-Unglue" campaigns to so that libraries can lend ebooks on the road to being unglued. </p> Weve added "Buy-to-Unglue" campaigns to so that libraries can lend ebooks on the road to being unglued. We've built a <i>free distribution platform</i> to help libraries distribute these books. </p>
<p><strong>We need your help, though.</strong> We can't unglue ebooks all by ourselves; we need lots of people to pitch in. If you'd like to share free, unlimited, no-DRM, privacy-respecting ebooks with your patrons, here are some ways you can help:</p> <p><strong>We need your help, though.</strong> We can't unglue ebooks all by ourselves; we need lots of people to pitch in. If you'd like to share free, unlimited, no-DRM, privacy-respecting ebooks with your patrons, here are some ways you can help:</p>
<dl> <dl>
<dt><a href="{% url registration_register %}?next={% request.get_full_path|urlencode }}">Sign up.</a></dt> <dt><a href="{% url registration_register %}?next={% request.get_full_path|urlencode }}">Sign up.</a> It's Free!</dt>
<dd>Starting an account, for yourself or your library, is free, and lets you add books to your wishlist, comment on them, and support campaigns. <a href="{{ request.get_full_path|urlencode}}">Sign up here.</a> It's the first step towards becoming an Unglue.it participating library. </dd> <dd>Starting an account, for yourself or your library, is free. Use it to help us distribute unglued and ungluing ebooks. </dd>
<dt>Become an Unglue.it participating library</dt> <dt>Become an Unglue.it participating library</dt>
<dd>Review our <a href="https://www.docracy.com/0_uyw26qv9c/unglue-it-library-license-agreement">LIBRARY LICENCE AGREEMENT</a>. Once an agreement has been signed, you and your patrons can buy Library Licenses for any ebook with a Buy-to-Unglue campaign. You can lend these ebooks to your patrons immediately using our free distribution platform. <dd><ol><li><a href="{% url library_create %}">Make your library page</a> on Unglue.it. It takes just a few minutes. It's the first step towards becoming an Unglue.it participating library.</li>
The library license gives download access to one library member at a time for 14 days each, in multiple formats, from the our platform or yours, if you have one. <li>Review our <a href="https://www.docracy.com/0_uyw26qv9c/unglue-it-library-license-agreement">LIBRARY LICENCE AGREEMENT</a>. </li>
<li>Once an agreement has been signed, we'll "approve" your library; you and your patrons can then buy Library Licenses for any ebook with a Buy-to-Unglue campaign. You can lend these ebooks to your patrons immediately using our free distribution platform.
The library license gives download access to one library member at a time for 14 days each, in multiple formats, from our platform or yours, if you have one. </li>
<li>Log in as the library, and add unglued and ungluing books to your library list. Buy licenses to our buy-to-unglue titles. </li>
<li>Let your users know about Unglue.it!</li>
</ol>
</dd> </dd>
<dt>Stay in touch.</dt> <dt>Stay in touch.</dt>
<dd>You can follow us on Twitter (<a href="http://twitter.com/unglueit">@unglueit</a>), <a href="http://facebook/com/unglueit">Facebook</a>, and our <a href="http://blog.unglue.it">blog</a>, and <a href="http://eepurl.com/fKLfI">subscribe to our newsletter</a> (1-2 emails per month).</dd> <dd>You can follow us on Twitter (<a href="http://twitter.com/unglueit">@unglueit</a>), <a href="http://facebook/com/unglueit">Facebook</a>, and our <a href="http://blog.unglue.it">blog</a>, and <a href="http://eepurl.com/fKLfI">subscribe to our newsletter</a> (1-2 emails per month).</dd>

View File

@ -0,0 +1,39 @@
{% extends "basedocumentation.html" %}
{% block title %} Library Admin {% endblock %}
{% block extra_extra_head %}
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/ui-lightness/jquery-ui.css" type="text/css" media="screen">
{{ form.media.css }}
<script type="text/javascript" src="{{ jquery_ui_home }}" ></script>
{{ form.media.js }}
{% endblock %}
{% block doccontent %}
{% if library.pk %}
<h2>Edit Library</h2>
<ul><li> Go to <a href="{% url library library.user %}">{{ library }} Page</a></li>
<li><a href="{% url library_login library.id %}">Log in</a> as the library user </li>
{% else %}
<h2>Create New Library</h2>
{% endif %}
<p>{{ status }}</p>
<form method="POST" action="#" class="std_form">
{% csrf_token %}
{{ form.work }}
{{ form.non_field_errors }}
<div>
<p><b>Library Name</b>: {{ form.name.errors }}{{ form.name }}</p>
{% if form.instance.user %}
<p><b>Library Username</b>: {{ form.instance.user }}</p>
<p><b>Library Email</b>: {{ form.instance.user.email }}</p>
{% else %}
<p><b>Library Username</b>: {{ form.username.errors }}{{ form.username }} This will be the Unglue.it username for the library.</p>
<p><b>Library Email</b>: {{ form.email.errors }}{{ form.email }} The notification address for the library.</p>
{% endif %}
<p><b>Library Authentication Method</b>: {{ form.backend.errors }}{{ form.backend }}</p>
</div>
<input type="submit" name="create_new_library" value="{% if library.pk %}Save Edits{% else %}Create A Library{% endif %}" id="submit">
</form>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load endless %} {% load endless %}
{% load truncatechars %} {% load truncatechars %}
{% block title %} &#8212; {{ library.user.username }}{% endblock %} {% block title %} &#8212; {{ library }}{% endblock %}
{% block extra_css %} {% block extra_css %}
<link type="text/css" rel="stylesheet" href="/static/css/supporter_layout.css" /> <link type="text/css" rel="stylesheet" href="/static/css/supporter_layout.css" />
<link type="text/css" rel="stylesheet" href="/static/css/searchandbrowse2.css" /> <link type="text/css" rel="stylesheet" href="/static/css/searchandbrowse2.css" />
@ -37,7 +37,7 @@ function highlightTarget(targetdiv) {
{% block topsection %} {% block topsection %}
<div id="locationhash">{% ifequal activetab '#3' %}#1{% else %}{{ activetab }}{% endifequal %}</div> <div id="locationhash">{% ifequal activetab '#3' %}#1{% else %}{{ activetab }}{% endifequal %}</div>
{% ifequal supporter request.user %} {% ifequal supporter request.user %}
<div class="launch_top pale">You are logged in as the administrator of {{ library.user.username }} <div class="launch_top pale">You are logged in as the administrator of {{ library }}
</div> </div>
{% endifequal %} {% endifequal %}
@ -52,7 +52,7 @@ function highlightTarget(targetdiv) {
<div class="block-inner"> <div class="block-inner">
<img class="user-avatar" src="{{ library.user.profile.avatar_url }}" height="50" width="50" alt="Avatar for {{ supporter }}" title="Avatar" /> <img class="user-avatar" src="{{ library.user.profile.avatar_url }}" height="50" width="50" alt="Avatar for {{ supporter }}" title="Avatar" />
<span class="user-name"> <span class="user-name">
<a href="#"><span itemprop="name">{{ library.user.username }}</span></a> <a href="#"><span itemprop="name">{{ library }}</span></a>
</span> </span>
</div> </div>
<span class="user-badges"> <span class="user-badges">
@ -157,7 +157,7 @@ function highlightTarget(targetdiv) {
</div> </div>
{% else %} {% else %}
<div class="empty-wishlist"> <div class="empty-wishlist">
It looks like {{ library.user.username }} is just getting started, and isn't offering any books just yet.<br /><br /> It looks like {{ library }} is just getting started, and isn't offering any books just yet.<br /><br />
{% endifequal %} {% endifequal %}
{% else %} {% else %}

View File

@ -47,12 +47,12 @@
{% for library in libraries %} {% for library in libraries %}
<div class="items {% cycle 'row1' 'row2' %}{% if library.group in request.user.groups.all %} joined{% endif %}"> <div class="items {% cycle 'row1' 'row2' %}{% if library.group in request.user.groups.all %} joined{% endif %}">
<div class="avatar"> <div class="avatar">
<a href="{% url library library %}"> <a href="{% url library library.user %}">
<img class="user-avatar" src="{{ library.user.profile.avatar_url }}" height="50" width="50" alt="Avatar for {{ library }}" title="{{ library }}" /> <img class="user-avatar" src="{{ library.user.profile.avatar_url }}" height="50" width="50" alt="Avatar for {{ library }}" title="{{ library }}" />
</a> </a>
</div> </div>
<div class="nonavatar"> <div class="nonavatar">
<div class="libname"><a href="{% url library library %}">{{ library }}</a> {{ library.user.tagline }}</div> <div class="libname"><a href="{% url library library.user %}">{{ library }}</a> {{ library.user.tagline }}</div>
<div class="libstat">{% if library.group in request.user.groups.all %}<em>joined</em>{% else %}&nbsp;{% endif %}</div> <div class="libstat">{% if library.group in request.user.groups.all %}<em>joined</em>{% else %}&nbsp;{% endif %}</div>
<div class="libstat">{{ library.library_users.count }} Unglue.it users</div> <div class="libstat">{{ library.library_users.count }} Unglue.it users</div>
<div class="libstat">{{ library.user.acqs.count }} Unglue.it holdings</div> <div class="libstat">{{ library.user.acqs.count }} Unglue.it holdings</div>

View File

@ -19,7 +19,7 @@
<div id="user-block1"> <div id="user-block1">
<div id="block-intro-text"><span class="special-user-name">{{ library }} </span> </div> <div id="block-intro-text"><span class="special-user-name">{{ library }} </span> </div>
</div> </div>
<div class="user-block24"><span class="user-short-info">These ungluers are are using <a href="{% url library library %}">{{ library }}</a>. </span> <div class="user-block24"><span class="user-short-info">These ungluers are are using <a href="{% url library library.user %}">{{ library }}</a>. </span>
</div> </div>
</div> </div>
</div> </div>
@ -67,7 +67,7 @@
No users yet. No users yet.
{% endfor %} {% endfor %}
{% else %} {% else %}
You need to be a member of {{ library }} to see all its members. They can borrow and donate books. Perhaps you'd like to <a href="{% url join_library library %}">join them</a>? You need to be a member of {{ library }} to see all its members. They can borrow and donate books. Perhaps you'd like to <a href="{% url join_library library.id %}">join them</a>?
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@
Your request to join {{ library }} has been accepted. Welcome! Your request to join {{ library }} has been accepted. Welcome!
{{ library }} is participating in Unglue.it. That means you can borrow Unglue.it ebooks owned by {{ library }} (when available). A list of the ebooks available to {{ library }} members is available here: {{ library }} is participating in Unglue.it. That means you can borrow Unglue.it ebooks owned by {{ library }} (when available). A list of the ebooks available to {{ library }} members is available here:
https://{{ current_site.domain }}{% url library library %} https://{{ current_site.domain }}{% url library library.user %}
When you buy an ebook through Unglue.it, you'll be offered the option to by a library license in stead of an individual license. That means that {{ library }} will own the ebook, but you'll be the first in line to borrow it. After two weeks, the book will be available to other members of {{ library }}. When you buy an ebook through Unglue.it, you'll be offered the option to by a library license in stead of an individual license. That means that {{ library }} will own the ebook, but you'll be the first in line to borrow it. After two weeks, the book will be available to other members of {{ library }}.
@ -14,7 +14,7 @@ eBooks available for purchase through Unglue.it will eventually be free to the w
{{ user.username }}'s request to join {{ library }} has been accepted. {{ user.username }}'s request to join {{ library }} has been accepted.
Manage your users at Manage your users at
https://{{ current_site.domain }}{% url library_users library %} https://{{ current_site.domain }}{% url library_users library.id %}
the Unglue.it team the Unglue.it team
{% endifequal %} {% endifequal %}

View File

@ -1,6 +1,6 @@
{% extends "notification/notice_template.html" %} {% extends "notification/notice_template.html" %}
{% block comments_book %} {% block comments_book %}
<a href="{% url library library %}"><img src="{{ library.user.profile.avatar_url }}" alt="avatar for {{ library }}" /></a> <a href="{% url library library.user %}"><img src="{{ library.user.profile.avatar_url }}" alt="avatar for {{ library }}" /></a>
{% endblock %} {% endblock %}
{% block comments_graphical %} {% block comments_graphical %}
@ -13,7 +13,7 @@
{% block comments_textual %} {% block comments_textual %}
{% ifequal user recipient %} {% ifequal user recipient %}
<p>{{ library }} is participating in Unglue.it. That means you can borrow Unglue.it ebooks owned by {{ library }} (when available). Here's <a href="{% url library library %}">a list of the ebooks available</a> to {{ library }} members. <p> <p>{{ library }} is participating in Unglue.it. That means you can borrow Unglue.it ebooks owned by {{ library }} (when available). Here's <a href="{% url library library.user %}">a list of the ebooks available</a> to {{ library }} members. <p>
<p>When you buy an ebook through unglue.it, you'll be offered the option to by a library license in stead of an individual license. That means that {{ library }} will own the ebook, but you'll be the first in line to borrow it. After two weeks, the book will be available to other members of {{ library }}. </p> <p>When you buy an ebook through unglue.it, you'll be offered the option to by a library license in stead of an individual license. That means that {{ library }} will own the ebook, but you'll be the first in line to borrow it. After two weeks, the book will be available to other members of {{ library }}. </p>
@ -22,6 +22,6 @@
<p>{{ library }} and the Unglue.it team <p>{{ library }} and the Unglue.it team
</p> </p>
{% else %} {% else %}
<p>You can manage your users <a href="{% url library_users library %}">on this page</a>.</p> <p>You can manage your users <a href="{% url library_users library.id %}">on this page</a>.</p>
{% endifequal %} {% endifequal %}
{% endblock %} {% endblock %}

View File

@ -73,7 +73,7 @@ function highlightTarget(targetdiv) {
{% block topsection %} {% block topsection %}
<div id="locationhash">{{ activetab }}</div> <div id="locationhash">{{ activetab }}</div>
{% if supporter.library %} {% if supporter.library %}
<div class="launch_top pale">{{ supporter.library }} is a Library participating in Unglue.it. <a href="{% url join_library supporter.username %}">Click here</a> to use {{ supporter.library }}'s books. <div class="launch_top pale">{{ supporter.library }} is a Library participating in Unglue.it. <a href="{% url join_library supporter.library.id %}">Click here</a> to use {{ supporter.library }}'s books.
</div> </div>
{% endif %} {% endif %}

View File

@ -1,4 +1,9 @@
from django import forms
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from .models import Library
class AuthForm(AuthenticationForm): class AuthForm(AuthenticationForm):
def __init__(self, request=None, *args, **kwargs): def __init__(self, request=None, *args, **kwargs):
@ -8,3 +13,38 @@ class AuthForm(AuthenticationForm):
else: else:
super(AuthForm, self).__init__(*args, **kwargs) super(AuthForm, self).__init__(*args, **kwargs)
class NewLibraryForm(forms.ModelForm):
username = forms.RegexField(
label=_("Library Username"),
max_length=30,
regex=r'^[\w.@+-]+$',
help_text = _("30 characters or fewer."),
error_messages = {
'invalid': _("This value may contain only letters, numbers and @/./+/-/_ characters.")
},
initial = '',
)
email = forms.EmailField(
label=_("notification email address for library"),
max_length=100,
error_messages={'required': 'Please enter an email address for the library.'},
)
def clean_username(self):
username= self.cleaned_data['username']
try:
user = User.objects.get(username=username)
raise forms.ValidationError(_("That username is already in use, please choose another."))
except User.DoesNotExist:
self.instance.user = User(username=username)
return username
class Meta:
model = Library
fields = 'name', 'backend', 'email', 'username'
widgets = {'name':forms.TextInput(attrs={'size':'40'})}
class LibraryForm(forms.ModelForm):
class Meta:
model = Library
fields = 'name', 'backend',

View File

@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Library.name'
db.add_column('libraryauth_library', 'name',
self.gf('django.db.models.fields.CharField')(default='', max_length=80),
keep_default=False)
# Adding field 'Library.approved'
db.add_column('libraryauth_library', 'approved',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
# Adding field 'Library.owner'
db.add_column('libraryauth_library', 'owner',
self.gf('django.db.models.fields.related.ForeignKey')(default=10, related_name='libraries', to=orm['auth.User']),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Library.name'
db.delete_column('libraryauth_library', 'name')
# Deleting field 'Library.approved'
db.delete_column('libraryauth_library', 'approved')
# Deleting field 'Library.owner'
db.delete_column('libraryauth_library', 'owner_id')
models = {
'auth.group': {
'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'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': {
'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'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']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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'})
},
'libraryauth.block': {
'Meta': {'ordering': "['lower']", 'object_name': 'Block'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blocks'", 'to': "orm['libraryauth.Library']"}),
'lower': ('regluit.libraryauth.models.IPAddressModelField', [], {'unique': 'True', 'db_index': 'True'}),
'upper': ('regluit.libraryauth.models.IPAddressModelField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
},
'libraryauth.cardpattern': {
'Meta': {'object_name': 'CardPattern'},
'checksum': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'card_patterns'", 'to': "orm['libraryauth.Library']"}),
'pattern': ('django.db.models.fields.CharField', [], {'max_length': '20'})
},
'libraryauth.emailpattern': {
'Meta': {'object_name': 'EmailPattern'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'email_patterns'", 'to': "orm['libraryauth.Library']"}),
'pattern': ('django.db.models.fields.CharField', [], {'max_length': '20'})
},
'libraryauth.library': {
'Meta': {'object_name': 'Library'},
'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'backend': ('django.db.models.fields.CharField', [], {'default': "'ip'", 'max_length': '10'}),
'group': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'library'", 'unique': 'True', 'null': 'True', 'to': "orm['auth.Group']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '80'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'libraries'", 'to': "orm['auth.User']"}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'library'", 'unique': 'True', 'to': "orm['auth.User']"})
},
'libraryauth.libraryuser': {
'Meta': {'object_name': 'LibraryUser'},
'credential': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'library_users'", 'to': "orm['libraryauth.Library']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_libraries'", 'to': "orm['auth.User']"})
}
}
complete_apps = ['libraryauth']

View File

@ -9,6 +9,7 @@ from django.db.models import Q
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.forms import IPAddressField as BaseIPAddressField from django.forms import IPAddressField as BaseIPAddressField
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
class Library(models.Model): class Library(models.Model):
''' '''
@ -21,10 +22,13 @@ class Library(models.Model):
('cardnum', 'Library Card Number check'), ('cardnum', 'Library Card Number check'),
('email', 'e-mail pattern check'), ('email', 'e-mail pattern check'),
),default='ip') ),default='ip')
name = models.CharField(max_length=80, default='')
approved = models.BooleanField(default=False)
owner = models.ForeignKey(User, related_name="libraries")
credential = None credential = None
def __unicode__(self): def __unicode__(self):
return self.user.username return unicode(self.name)
def add_user(self, user): def add_user(self, user):
user.groups.add(self.group) user.groups.add(self.group)
@ -38,6 +42,9 @@ class Library(models.Model):
@property @property
def join_template(self): def join_template(self):
return 'libraryauth/' + self.backend + '_join.html' return 'libraryauth/' + self.backend + '_join.html'
def get_absolute_url(self):
return reverse('library', args=[self.user.username])
def add_group(sender, created, instance, **kwargs): def add_group(sender, created, instance, **kwargs):
if created: if created:

View File

@ -1,5 +1,5 @@
<div class="p_form">To use {{ library }}...{{authenticator.form.non_field_errors}}<br /> <div class="p_form">To use {{ library }}...{{authenticator.form.non_field_errors}}<br />
<form action="{% url join_library library %}#" method="POST" class="std_form " id="join_form"> <form action="{% url join_library library.id %}#" method="POST" class="std_form " id="join_form">
{% csrf_token %} {% csrf_token %}
{{ authenticator.form.credential.label_tag }}: {{ authenticator.form.credential }} {{ authenticator.form.credential.label_tag }}: {{ authenticator.form.credential }}
{% if authenticator.form.credential.errors %} {% if authenticator.form.credential.errors %}

View File

@ -1,6 +1,6 @@
<br /> <br />
{% if authenticator.allowed %} {% if authenticator.allowed %}
<a href="{% url join_library authenticator.library %}?next={% url join_library authenticator.library %}" class="fakeinput">Make this my Library</a> <a href="{% url join_library authenticator.library.id %}?next={% url join_library authenticator.library.id %}" class="fakeinput">Make this my Library</a>
{% else %} {% else %}
Based on your account's email address, you can't join {{ authenticator.library }}. You can <a href="{% url email_change %}"> change your email address</a> if you need to. Based on your account's email address, you can't join {{ authenticator.library }}. You can <a href="{% url email_change %}"> change your email address</a> if you need to.
{% endif %} {% endif %}

View File

@ -1,6 +1,6 @@
<br /> <br />
{% if authenticator.allowed %} {% if authenticator.allowed %}
<a href="{% url join_library authenticator.library %}?next={% url join_library authenticator.library %}" class="fakeinput">Make this my Library</a> <a href="{% url join_library authenticator.library.id %}?next={% url join_library authenticator.library.id %}" class="fakeinput">Make this my Library</a>
{% else %} {% else %}
You can't join {{ authenticator.library }} at your current location. You can't join {{ authenticator.library }} at your current location.
{% endif %} {% endif %}

View File

@ -1,16 +1,20 @@
from django.conf.urls.defaults import * from django.conf.urls.defaults import *
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.views.generic.simple import direct_to_template from django.views.generic.simple import direct_to_template
from django.contrib.auth.decorators import login_required
from . import views, models from . import views, models
urlpatterns = patterns( urlpatterns = patterns(
"", "",
url(r"^libraryauth/(?P<library>[^/]+)/join/$", views.join_library, name="join_library"), url(r"^libraryauth/(?P<library_id>\d+)/join/$", views.join_library, name="join_library"),
url(r"^libraryauth/(?P<library>[^/]+)/deny/$", direct_to_template, {'template':'libraryauth/denied.html'}, name="bad_library"), url(r"^libraryauth/(?P<library_id>\d+)/deny/$", direct_to_template, {'template':'libraryauth/denied.html'}, name="bad_library"),
url(r"^libraryauth/(?P<library>[^/]+)/users/$", views.library, {'template':'libraryauth/users.html'}, name="library_users"), url(r"^libraryauth/(?P<library_id>\d+)/users/$", views.library, {'template':'libraryauth/users.html'}, name="library_users"),
url(r"^libraryauth/(?P<library_id>\d+)/admin/$", login_required(views.UpdateLibraryView.as_view()), name="library_admin"),
url(r"^libraryauth/(?P<library_id>\d+)/login/$", views.login_as_library, name="library_login"),
url(r"^libraryauth/create/$", login_required(views.CreateLibraryView.as_view()), name="library_create"),
url(r"^libraryauth/list/$", direct_to_template, { url(r"^libraryauth/list/$", direct_to_template, {
'template':'libraryauth/list.html', 'template':'libraryauth/list.html',
'extra_context':{'libraries':models.Library.objects.order_by('user__username')} 'extra_context':{'libraries':models.Library.objects.order_by('name')}
}, name="library_list"), }, name="library_list"),
url(r'^accounts/superlogin/$', views.superlogin, name='superlogin'), url(r'^accounts/superlogin/$', views.superlogin, name='superlogin'),
) )

View File

@ -1,20 +1,31 @@
import logging import logging
from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.contrib.auth.views import login from django.contrib.auth.views import login
from django.contrib.auth import login as login_to_user
from django.contrib.auth import load_backend
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.views.generic.edit import FormView, CreateView, UpdateView
from . import backends from . import backends
from .models import Library from .models import Library
from .forms import AuthForm from .forms import AuthForm, LibraryForm, NewLibraryForm
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def library(request, library, def get_library_or_404(library=None, library_id=None):
if library_id:
return get_object_or_404(Library, id=library_id)
else:
return get_object_or_404(Library, user__username=library)
def library(request, library=None, library_id=None,
extra_context={}, extra_context={},
template='libraryauth/library.html', template='libraryauth/library.html',
**kwargs): **kwargs):
library=get_object_or_404(Library, user__username=library) library=get_library_or_404(library=library, library_id=library_id)
context={ 'library':library, context={ 'library':library,
'is_admin': request.user.is_staff or request.user==library.user, 'is_admin': request.user.is_staff or request.user==library.user,
'is_member': request.user.is_staff or library.has_user(request.user), 'is_member': request.user.is_staff or library.has_user(request.user),
@ -22,11 +33,11 @@ def library(request, library,
context.update(extra_context) context.update(extra_context)
return render(request, template, context) return render(request, template, context)
def join_library(request, library): def join_library(request, library_id):
library=get_object_or_404(Library, user__username=library) library=get_library_or_404(library_id=library_id)
return Authenticator(request,library).process( return Authenticator(request,library).process(
reverse('library',args=[str(library)]), reverse('library',args=[library.user]),
reverse('bad_library',args=[str(library)]), reverse('bad_library',args=[library.id]),
) )
def superlogin(request, extra_context=None, **kwargs): def superlogin(request, extra_context=None, **kwargs):
@ -78,3 +89,64 @@ class Authenticator:
def allowed(self): def allowed(self):
backend_test= getattr(backends, self.library.backend + '_authenticate') backend_test= getattr(backends, self.library.backend + '_authenticate')
return backend_test(self.request, self.library) return backend_test(self.request, self.library)
class CreateLibraryView(CreateView):
model = Library
template_name="libraryauth/edit.html"
form_class = NewLibraryForm
def get_initial(self):
return {'email': self.request.user.email}
def form_valid(self, form):
form.instance.owner = self.request.user
user = form.instance.user
user.email = form.cleaned_data['email']
user.save()
form.instance.user = user
form.instance.save()
context_data = self.get_context_data(form=form)
context_data['status'] = 'Library Updated'
return HttpResponseRedirect(reverse('library_admin',args=[form.instance.id]))
class UpdateLibraryView(UpdateView):
model = Library
pk_url_kwarg = 'library_id'
template_name="libraryauth/edit.html"
form_class = LibraryForm
def form_valid(self, form):
if self.request.user in [form.instance.owner, form.instance.user]:
form.instance.save()
context_data = self.get_context_data(form=form)
context_data['status'] = 'Library Updated.'
else:
context_data['status'] = 'You\'re not permitted to edit this library.'
return self.render_to_response(context_data)
@login_required
def login_as_library(request, library_id):
library=get_library_or_404(library_id=library_id)
if request.user == library.owner:
login_user(request, library.user)
return HttpResponseRedirect(reverse('library',args=[library.user]))
else:
return HttpResponseRedirect(reverse('library_admin',args=[library.user]))
def login_user(request, user):
"""
Log in a user without requiring credentials (using ``login`` from
``django.contrib.auth``, first finding a matching backend).
magic from https://djangosnippets.org/snippets/1547/
"""
if not hasattr(user, 'backend'):
for backend in settings.AUTHENTICATION_BACKENDS:
if user == load_backend(backend).get_user(user.pk):
user.backend = backend
break
if hasattr(user, 'backend'):
return login_to_user(request, user)