fmartingr
/
shelfzilla
Archived
1
0
Fork 0
This commit is contained in:
Juan Manuel Parrilla 2015-04-13 21:06:10 +02:00
commit 67a9f0f382
59 changed files with 1160 additions and 158 deletions

View File

@ -1,5 +1,5 @@
shelfzilla
==========
===========
## Prepare environment for local development
@ -21,3 +21,4 @@ fab runserver
- grunt-cli installed as global resource
- bower installed as a global resource
Enjoy!

View File

@ -1,3 +1,4 @@
-r ../requirements.txt
gunicorn==18.0
toml==0.8.2
toml==0.9.0
opbeat==2.1

View File

@ -1,5 +1,5 @@
# Base
Django==1.7.1
Django==1.7.2
# Admin
django-suit==0.2.11
@ -9,6 +9,9 @@ django-import-export==0.2.6
# Fixes
# Mailing
django-mailgun==0.2.2
# Statics
django-compressor==1.4
@ -17,7 +20,13 @@ dj-database-url==0.3.0
psycopg2==2.5.4
# Files
django-mptt==0.6.1
django-filer==0.9.8
# Blog
django-ckeditor-updated==4.4.4
# API
djoser==0.2.1
djangorestframework==3.1.1
django-cors-headers==1.0.0

2
fabfile.py vendored
View File

@ -54,7 +54,7 @@ def setup_virtualenv():
Creates or updates a virtualenv
"""
print(yellow('Create virtualenv'))
local('virtualenv-2.7 .virtualenv')
local('virtualenv .virtualenv')
with virtualenv():
print(yellow('Installing requirements'))

View File

@ -29,7 +29,7 @@ function validations() {
PID_PATH=/var/run/shelfzilla
PID_FILE=${PID_PATH}/${INSTANCE}.pid
P_USER="shelfzilla"
LOG_PATH=/var/log/shefzilla
LOG_PATH=/var/log/shelfzilla
LOG_FILE=shelfzilla.log
FCGI_PORT=8000
FCGI_IP=127.0.0.1

View File

@ -61,20 +61,20 @@ cp -r %{_gitdir}/rpm/scripts/shelfzilla $RPM_BUILD_ROOT%{_app_dir}/init/
#rmdir %{_app_dir}/init/
## Npm install
#cd %{_app_dir} && npm install --production
cd %{_app_dir} && npm install --production
## pip install
#pip install -r %{_app_dir}/config/production/requirements.txt
pip install -r %{_app_dir}/config/production/requirements.txt
## Migrate
#python2.7 %{_app_dir}/manage.py migrate --no-initial-data
python2.7 %{_app_dir}/manage.py migrate --no-initial-data
## Bower
#cd %{_app_dir}
#bower install --allow-root
cd %{_app_dir}
bower install --allow-root
## Collect static
#python2.7 manage.py collectstatic --clear --noinput
python2.7 manage.py collectstatic --clear --noinput
# -------------------------------------------------------------------------------------------- #
# pre-uninstall section:
@ -101,5 +101,3 @@ rm -rf $RPM_BUILD_ROOT
%{_app_dir}/*
%{_app_dir}/.bowerrc
%{_init_path}/shelfzilla

View File

@ -103,3 +103,17 @@ class UserAdmin(DjangoUserAdmin):
# Now register the new UserAdmin...
admin.site.register(models.User, UserAdmin)
admin.site.register(Permission)
class AccessCodeAdmin(admin.ModelAdmin):
list_display = ('code', 'max_uses', 'expiration', 'active', 'uses',
'usable')
def uses(self, obj):
return obj.uses
def usable(self, obj):
return obj.usable
usable.boolean = True
admin.site.register(models.AccessCode, AccessCodeAdmin)

View File

View File

@ -0,0 +1,12 @@
# coding: utf-8
# django
# third party
from rest_framework import serializers
# own
class FeedSerializer(serializers.Serializer):
pass

View File

@ -0,0 +1,12 @@
# coding: utf-8
# third
from rest_framework.routers import DefaultRouter
# own
from .views import FeedViewSet
router = DefaultRouter(trailing_slash=False)
router.register(r'feed', FeedViewSet, base_name='feed')
urlpatterns = router.urls

View File

@ -0,0 +1,43 @@
# coding: utf-8
# python
from itertools import chain
import json
# third
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
# own
from shelfzilla.apps.manga.models import (
UserReadVolume, UserHaveVolume, UserWishlistVolume
)
class FeedViewSet(viewsets.ViewSet):
"""
"""
permission_classes = (IsAuthenticated,)
def list(self, request):
owned_list = UserHaveVolume.objects.filter(user=request.user)
wishlisted_list = UserWishlistVolume.objects.filter(user=request.user)
read_list = UserReadVolume.objects.filter(user=request.user)
timeline = sorted(
chain(owned_list, wishlisted_list, read_list),
key=lambda model: model.date,
reverse=True
)[:20]
result = []
for item in timeline:
event = {
'date': item.date,
'message': item.timeline_message,
'type': item.event_type,
}
result.append(event)
return Response(result)

View File

@ -0,0 +1,15 @@
# coding: utf-8
# py3
from __future__ import absolute_import
# django
from django.apps import AppConfig
class AccountConfig(AppConfig):
name = 'account'
verbose_name = "Account"
def ready(self):
from . import signals

View File

@ -1,10 +1,13 @@
from django import forms
from django.contrib.auth import authenticate
from django.db import transaction
from django.contrib.auth.forms import (
PasswordChangeForm as DjangoPasswordChangeForm
)
from django.utils.translation import ugettext_lazy as _
from . import models
class LoginForm(forms.Form):
username = forms.CharField(max_length=75, label=_('Username'))
@ -43,3 +46,51 @@ class LoginForm(forms.Form):
class PasswordChangeForm(DjangoPasswordChangeForm):
pass
class RegistrationForm(forms.ModelForm):
"""
Custom for for registering an user
"""
password1 = forms.CharField(label=_('Password'),
widget=forms.PasswordInput)
password2 = forms.CharField(label=_('Repeat password'),
widget=forms.PasswordInput)
access_code = forms.CharField(label=_('Invitation code'), required=True)
class Meta:
model = models.User
fields = ('email', 'username', )
def get_access_code(self):
try:
return models.AccessCode.objects.get(
code=self.cleaned_data['access_code'])
except models.AccessCode.DoesNotExist:
return False
def clean_access_code(self):
code = self.get_access_code()
if not code or (code and not code.usable):
raise forms.ValidationError(_('Invitation code is not valid'))
return self.cleaned_data['access_code']
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
with transaction.atomic():
user = super(RegistrationForm, self).save(commit=False)
access_code = self.get_access_code()
user.access_code = access_code
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user

View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('account', '0002_auto_20141111_1208'),
]
operations = [
migrations.CreateModel(
name='AccessCode',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('code', models.CharField(max_length=128, verbose_name='Code')),
('max_uses', models.IntegerField(default=1, verbose_name='Number of uses')),
('expiration', models.DateTimeField(default=None, null=True, verbose_name='Expires')),
('active', models.BooleanField(default=True, verbose_name='Active')),
('user', models.ForeignKey(related_name='access_codes', blank=True, to=settings.AUTH_USER_MODEL, null=True)),
],
options={
'verbose_name': 'Access code',
'verbose_name_plural': 'Access codes',
},
bases=(models.Model,),
),
migrations.AddField(
model_name='user',
name='access_code',
field=models.ForeignKey(related_name='used_by', blank=True, to='account.AccessCode', null=True),
preserve_default=True,
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('account', '0003_auto_20150110_1056'),
]
operations = [
migrations.AlterField(
model_name='accesscode',
name='expiration',
field=models.DateTimeField(default=None, null=True, verbose_name='Expires', blank=True),
preserve_default=True,
),
]

View File

@ -92,6 +92,11 @@ class User(AbstractBaseUser,
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ('email',)
# Access codes / Invitations
access_code = models.ForeignKey('account.AccessCode',
null=True, blank=True,
related_name='used_by')
objects = UserManager()
class Meta:
@ -132,3 +137,40 @@ class User(AbstractBaseUser,
return today.year - birthdate.year - (
(today.month, today.day) < (birthdate.month, birthdate_day)
)
class AccessCode(models.Model):
code = models.CharField(_('Code'), max_length=128)
max_uses = models.IntegerField(_('Number of uses'), default=1)
user = models.ForeignKey(User, null=True, blank=True,
related_name='access_codes')
expiration = models.DateTimeField(_('Expires'), null=True, blank=True,
default=None)
active = models.BooleanField(_('Active'), default=True)
class Meta:
verbose_name = _('Access code')
verbose_name_plural = _('Access codes')
def __unicode__(self):
return self.code
@property
def uses(self):
return self.used_by.count()
@property
def usable(self):
# Check if active
if not self.active:
return False
# Check if expired
if self.expiration and (timezone.now() >= self.expiration):
return False
# Check if it someone already used it
if self.used_by.count() == self.max_uses:
return False
return True

View File

@ -0,0 +1,15 @@
# coding: utf-8
# django
from django.dispatch import Signal
from django.dispatch import receiver
user_registered = Signal(providing_args=["user"])
@receiver(user_registered)
def send_email_new_user(sender, **kwargs):
from shelfzilla.apps.mailing.emails import RegistrationEmail
mail = RegistrationEmail({"user": kwargs.get('user')})
mail.send()

View File

@ -1,10 +1,13 @@
from django.conf.urls import patterns, url
from .views import LoginView, LogoutView, UserProfileView, AccountView
from .views import (
LoginView, LogoutView, UserProfileView, AccountView, RegisterView
)
urlpatterns = patterns(
'',
url(r'^login/$', LoginView.as_view(), name="login"),
url(r'^register/$', RegisterView.as_view(), name="register"),
url(r'^logout/$', LogoutView.as_view(), name="logout"),
url(
r'^user/(?P<username>[\w\d\-\.]+)/$',

View File

@ -2,7 +2,7 @@ from itertools import chain
from django.views.generic import View
from django.template import RequestContext
from django.shortcuts import render_to_response, get_object_or_404
from django.contrib.auth import logout
from django.contrib.auth import logout, authenticate, login
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _
@ -11,8 +11,9 @@ from django.contrib import messages
from django.contrib.auth import login
from django.core.urlresolvers import reverse
from .forms import LoginForm, PasswordChangeForm
from .forms import LoginForm, PasswordChangeForm, RegistrationForm
from .models import User
from .signals import user_registered
from shelfzilla.apps.manga.models import (
UserReadVolume, UserHaveVolume, UserWishlistVolume
)
@ -140,3 +141,35 @@ class AccountView(View):
ctx = RequestContext(request, data)
return render_to_response(self.template, context_instance=ctx)
class RegisterView(View):
template = 'account/register.html'
form_class = RegistrationForm
def get(self, request):
form_data = {}
if 'code' in request.GET:
form_data['access_code'] = request.GET['code']
data = {
'form': self.form_class(initial=form_data)
}
ctx = RequestContext(request, data)
return render_to_response(self.template, context_instance=ctx)
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
form.save()
user = authenticate(username=request.POST.get('username'),
password=request.POST.get('password1'))
login(request, user)
messages.success(request, _('Welcome to the community! :)'))
user_registered.send(sender=self.__class__, user=user)
return HttpResponseRedirect(reverse('homepage'))
ctx = RequestContext(request, {'form': form})
return render_to_response(self.template, context_instance=ctx)

View File

View File

@ -0,0 +1,17 @@
# coding: utf-8
# django
from django.contrib import admin
# app
from .models import QuestionAnswerCategory, QuestionAnswer
class QuestionAnswerInline(admin.TabularInline):
model = QuestionAnswer
class QuestionAnswerCategoryAdmin(admin.ModelAdmin):
inlines = (QuestionAnswerInline, )
admin.site.register(QuestionAnswerCategory, QuestionAnswerCategoryAdmin)

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='QuestionAnswer',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('ord', models.PositiveIntegerField(default=1)),
('title_es', models.CharField(max_length=256)),
('answer_es', models.TextField()),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='QuestionAnswerCategory',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name_es', models.CharField(max_length=32)),
],
options={
},
bases=(models.Model,),
),
migrations.AddField(
model_name='questionanswer',
name='category',
field=models.ForeignKey(to='faq.QuestionAnswerCategory'),
preserve_default=True,
),
]

View File

@ -0,0 +1,47 @@
# coding: utf-8
# django
from django.db import models
from django.utils.translation import get_language, ugettext_lazy as _
class QuestionAnswerCategory(models.Model):
name_es = models.CharField(max_length=32)
class Meta:
ordering = ('name_es', )
verbose_name = _('Category')
verbose_name_plural = _('Categories')
def __unicode__(self):
return self.name
@property
def name(self):
return getattr(self, u'name_{}'.format(get_language()), u'')
class QuestionAnswer(models.Model):
category = models.ForeignKey(QuestionAnswerCategory,
related_name='questions')
ord = models.PositiveIntegerField(default=1)
# Spanish
title_es = models.CharField(max_length=256)
answer_es = models.TextField()
class Meta:
ordering = ('ord', )
verbose_name = _('Question')
verbose_name_plural = _('Questions')
def __unicode__(self):
return self.title
@property
def title(self):
return getattr(self, u'title_{}'.format(get_language()), u'')
@property
def answer(self):
return getattr(self, u'answer_{}'.format(get_language()), u'')

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,12 @@
# coding: utf-8
# django
from django.conf.urls import patterns, url
# app
from .views import FaqListView
urlpatterns = patterns(
'',
url(r'^$', FaqListView.as_view(), name='faq.list'),
)

View File

@ -0,0 +1,26 @@
# coding: utf-8
# django
from django.views.generic import View
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.db.models import Count
from django.contrib.auth import get_user_model
# shelfzilla.faq
from .models import QuestionAnswerCategory
class FaqListView(View):
template = 'faq/list.html'
def get(self, request):
data = {
'categories': QuestionAnswerCategory.objects.all(),
'navigation': {
'section': 'faqs',
},
}
ctx = RequestContext(request, data)
return render_to_response(self.template, context_instance=ctx)

View File

View File

@ -0,0 +1,16 @@
# coding: utf-8
from django.utils.translation import ugettext_lazy as _
from .models import Email
class RegistrationEmail(Email):
template = 'mailing/registration.html'
subject = _('Bienvenido a Shelfzilla')
# Context requires:
# - user: <User model>
def prepare(self):
self.recipients.append(self.context['user'].email)

View File

@ -0,0 +1,53 @@
# coding: utf-8
from django.template import Context, Template
from django.template.loader import get_template
from django.utils.html import strip_tags
from django.core.mail import EmailMultiAlternatives
from django.conf import settings
class Email(object):
template = ''
context = {}
subject = ''
from_email = None
recipients = []
text = ''
def __init__(self, context={}):
self.from_email = getattr(settings, 'FROM_EMAIL', 'root@localhost')
self.context = context
self.recipients = []
self.prepare()
def prepare(self):
pass
def compile_template(self):
tmpl = get_template(self.template)
self.html = tmpl.render(Context(self.context))
self.text = strip_tags(self.html)
def send(self):
if self.template:
self.compile_template()
message = EmailMultiAlternatives(self.subject,
self.text,
self.from_email,
self.recipients)
if self.template:
message.attach_alternative(self.html, "text/html")
message.send()
# subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
# text_content = 'This is an important message.'
# html_content = '<p>This is an <strong>important</strong> message.</p>'
# msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
# msg.attach_alternative(html_content, "text/html")
# msg.send()

View File

@ -88,11 +88,13 @@ admin.site.register(Publisher, PublisherAdmin)
class SeriesSummaryInline(admin.TabularInline):
model = SeriesSummary
fields = ('summary', 'language', )
suit_classes = 'suit-tab suit-tab-summaries'
class SeriesPublisherInline(admin.TabularInline):
model = SeriesPublisher
fields = ('publisher', 'status', 'actual_publisher')
suit_classes = 'suit-tab suit-tab-publishers'
class SeriesAdmin(ImportExportModelAdmin, reversion.VersionAdmin):
@ -107,7 +109,9 @@ class SeriesAdmin(ImportExportModelAdmin, reversion.VersionAdmin):
suit_form_tabs = (
('general', _('General')),
('publishers', _('Publishers')),
('volumes', _('Volumes')),
('summaries', _('Summary')),
('review', _('Review')),
('advanced', _('Advanced')),
)
@ -198,6 +202,7 @@ admin.site.register(Volume, VolumeAdmin)
class PersonAdmin(ImportExportModelAdmin, reversion.VersionAdmin):
resource_class = PersonResource
search_fields = ('name', )
suit_form_tabs = (
('general', _('General')),
('review', _('Review')),

View File

View File

@ -0,0 +1,59 @@
# coding: utf-8
# django
from django.conf import settings
# third party
from easy_thumbnails.files import get_thumbnailer
from rest_framework import serializers
# own
from ..models import Volume, Series, Publisher, Person, Language
class LanguageSerializer(serializers.ModelSerializer):
class Meta:
model = Language
fields = ('name', )
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = ('name',)
class PublisherSerializer(serializers.ModelSerializer):
class Meta:
model = Publisher
fields = ('id', 'name', 'url', )
class SeriesSerializer(serializers.ModelSerializer):
original_publisher = PublisherSerializer()
art = PersonSerializer(many=True)
story = PersonSerializer(many=True)
class Meta:
model = Series
fields = ('id', 'name', 'status', 'art', 'story', 'original_publisher')
class VolumeSerializer(serializers.ModelSerializer):
series = SeriesSerializer()
publisher = PublisherSerializer()
language = LanguageSerializer()
cover = serializers.SerializerMethodField('get_cover_thumbnail')
def get_cover_thumbnail(self, obj):
if obj.cover:
url = get_thumbnailer(obj.cover).get_thumbnail({
'size': (100, 100), 'crop': 'scale', 'autocrop': True,
}).url
return url
return None
class Meta:
model = Volume
fields = ('id', 'series', 'number', 'name', 'retail_price',
'release_date', 'publisher', 'cover', 'language')

View File

@ -0,0 +1,13 @@
# coding: utf-8
# third
from rest_framework.routers import DefaultRouter
# own
from .views import VolumesViewSet
router = DefaultRouter(trailing_slash=False)
router.register(r'volumes', VolumesViewSet)
urlpatterns = router.urls

View File

@ -0,0 +1,22 @@
# coding: utf-8
# third
from rest_framework import viewsets, filters
from rest_framework.permissions import IsAuthenticated
# own
from .serializers import VolumeSerializer
from ..models import Volume
class VolumesViewSet(viewsets.ReadOnlyModelViewSet):
"""
"""
# permission_classes = (IsAuthenticated,)
serializer_class = VolumeSerializer
queryset = Volume.objects.filter(hidden=False)
paginate_by = 20
filter_fields = ('series', )
filter_backends = (filters.SearchFilter,)
search_fields = ('name', 'number', 'series__name', )

View File

@ -261,7 +261,7 @@ class UserHaveVolume(models.Model):
return self._timeline_message % {'volume': self.volume}
def __unicode__(self):
return "{} {} {}".format(
return u"{} {} {}".format(
self.user.username,
_('have'),
self.volume
@ -289,7 +289,7 @@ class UserWishlistVolume(models.Model):
return self._timeline_message % {'volume': self.volume}
def __unicode__(self):
return "{} {} {}".format(
return u"{} {} {}".format(
self.user.username,
_('wants'),
self.volume
@ -317,7 +317,7 @@ class UserReadVolume(models.Model):
return self._timeline_message % {'volume': self.volume}
def __unicode__(self):
return "{} {} {}".format(
return u"{} {} {}".format(
self.user.username,
_('have read'),
self.volume
@ -386,8 +386,11 @@ def volume_check_filer(sender, instance, created, **kwargs):
def series_delete_folder(sender, instance, using, **kwargs):
if instance.folder:
instance.folder.delete()
try:
if instance.folder:
instance.folder.delete()
except:
pass
def volume_delete_cover(sender, instance, **kwargs):

View File

@ -8,9 +8,9 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-09-09 01:10+0200\n"
"PO-Revision-Date: 2014-09-09 01:11+0200\n"
"Last-Translator: Felipe Martin <fmartingr@me.com>\n"
"POT-Creation-Date: 2015-01-26 14:45+0100\n"
"PO-Revision-Date: 2015-01-26 14:45+0100\n"
"Last-Translator: <admin@admin.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -19,18 +19,6 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Translated-Using: django-rosetta 0.7.4\n"
#: models.py:7
msgid "For review"
msgstr "Para revisión"
#: models.py:9
msgid "Review comment"
msgstr "Comentario de revisión"
#: models.py:11
msgid "Hidden"
msgstr "Oculto"
#: apps/_admin/views.py:44 apps/_admin/views.py:81
msgid "Volume series changed"
msgstr "Serie de los volúmenes cambiada"
@ -43,6 +31,160 @@ msgstr "No se encontró la URL de la carátula para actualizar."
msgid "Volume not found."
msgstr "Volumen no encontrado."
#: apps/account/admin.py:85
#, fuzzy
#| msgid "Person"
msgid "Personal info"
msgstr "Persona"
#: apps/account/admin.py:87
msgid "Permissions"
msgstr "Permisos"
#: apps/account/admin.py:88
msgid "Information"
msgstr "Información"
#: apps/account/forms.py:13 apps/account/models.py:40
msgid "Username"
msgstr "Usuario"
#: apps/account/forms.py:15 apps/account/forms.py:55
msgid "Password"
msgstr "Contraseña"
#: apps/account/forms.py:37
msgid "This account is disabled."
msgstr "Esta cuenta está desactivada."
#: apps/account/forms.py:41
msgid "User with those credentials was not found."
msgstr "No se ha encontrado un usuario con esos credenciales."
#: apps/account/forms.py:57
msgid "Repeat password"
msgstr "Repetir contraseña"
#: apps/account/forms.py:59
msgid "Invitation code"
msgstr "Código de invitacion"
#: apps/account/forms.py:75
msgid "Invitation code is not valid"
msgstr "El código de invitación no es válido"
#: apps/account/models.py:19
msgid "Male"
msgstr "Hombre"
#: apps/account/models.py:20
msgid "Female"
msgstr "Mujer"
#: apps/account/models.py:32
msgid "Email address"
msgstr "Correo electrónico"
#: apps/account/models.py:36
msgid "An user email that should be verified."
msgstr "Una dirección de correo electrónico que será verificada"
#: apps/account/models.py:48
msgid "First name"
msgstr "Nombre"
#: apps/account/models.py:53
msgid "Last name"
msgstr "Apellidos"
#: apps/account/models.py:58
msgid "Birthdate"
msgstr "Fecha de nacimiento"
#: apps/account/models.py:65
msgid "Gender"
msgstr "Sexo"
#: apps/account/models.py:73
msgid "Date joined"
msgstr "Fecha de registro"
#: apps/account/models.py:79
msgid "Active status"
msgstr ""
#: apps/account/models.py:81
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
msgstr ""
#: apps/account/models.py:86
msgid "Staff status"
msgstr ""
#: apps/account/models.py:88
msgid "Designates whether the user can log into this admin site."
msgstr ""
#: apps/account/models.py:103
msgid "User"
msgstr "Usuario"
#: apps/account/models.py:143 apps/manga/models.py:232
msgid "Code"
msgstr "Código"
#: apps/account/models.py:144
msgid "Number of uses"
msgstr "Número de usos"
#: apps/account/models.py:147
msgid "Expires"
msgstr "Expira"
#: apps/account/models.py:149
msgid "Active"
msgstr "Activo"
#: apps/account/models.py:152
msgid "Access code"
msgstr "Código de acceso"
#: apps/account/models.py:153
msgid "Access codes"
msgstr "Códigos de acceso"
#: apps/account/views.py:48
msgid "Logged in successfully."
msgstr "Has accedido correctamente."
#: apps/account/views.py:67
msgid "Logged out successfully"
msgstr "Sesión finalizada."
#: apps/account/views.py:134
msgid "Password changed."
msgstr "Contraseña cambiada."
#: apps/account/views.py:165
msgid "Welcome to the community! :)"
msgstr "¡Bienvenido a la comunidad! :)"
#: apps/blog/admin.py:24 apps/manga/admin.py:61 apps/manga/admin.py:109
#: apps/manga/admin.py:154 apps/manga/admin.py:202 apps/manga/admin.py:229
#: apps/manga/admin.py:254
msgid "General"
msgstr "General"
#: apps/blog/admin.py:25
msgid "Content"
msgstr "Contenido"
#: apps/blog/templatetags/datetime.py:17
msgid "%B %e, %Y"
msgstr ""
#: apps/config/models.py:13 apps/config/models.py:16 apps/config/models.py:17
msgid "Site Configuration"
msgstr "Configuración del sitio"
@ -51,6 +193,10 @@ msgstr "Configuración del sitio"
msgid "Social Configuration"
msgstr "Configuración social"
#: apps/mailing/emails.py:10
msgid "Bienvenido a Shelfzilla"
msgstr ""
#: apps/manga/admin.py:44
msgid "Items marked for review"
msgstr "Marcar items para revisión"
@ -59,11 +205,6 @@ msgstr "Marcar items para revisión"
msgid "Items unmarked for review"
msgstr "Desmarcar items para revisión"
#: apps/manga/admin.py:61 apps/manga/admin.py:109 apps/manga/admin.py:154
#: apps/manga/admin.py:202 apps/manga/admin.py:229 apps/manga/admin.py:254
msgid "General"
msgstr "General"
#: apps/manga/admin.py:62 apps/manga/admin.py:111 apps/manga/admin.py:156
#: apps/manga/admin.py:203 apps/manga/admin.py:255
msgid "Review"
@ -75,7 +216,7 @@ msgid "Advanced"
msgstr "Avanzado"
#: apps/manga/admin.py:83 apps/manga/models.py:134 apps/manga/models.py:135
#: themes/bootflat/templates/_layout.html:40
#: themes/bootflat/templates/_layout.html:39
#: themes/bootflat/templates/homepage/home.html:72
#: themes/bootflat/templates/manga/publishers/detail.html:19
#: themes/bootflat/templates/manga/series/list.html:4
@ -120,7 +261,7 @@ msgstr "URL"
msgid "Publisher"
msgstr "Editorial"
#: apps/manga/models.py:54 themes/bootflat/templates/_layout.html:43
#: apps/manga/models.py:54 themes/bootflat/templates/_layout.html:42
#: themes/bootflat/templates/manga/publishers/list.html:4
#: themes/bootflat/templates/manga/series/detail.html:55
msgid "Publishers"
@ -206,10 +347,6 @@ msgstr "Persona"
msgid "Persons"
msgstr "Personas"
#: apps/manga/models.py:232
msgid "Code"
msgstr "Código"
#: apps/manga/models.py:239
#: themes/bootflat/templates/manga/series/detail.html:106
msgid "Language"
@ -219,7 +356,7 @@ msgstr "Idioma"
msgid "Languages"
msgstr "Idiomas"
#: apps/manga/models.py:250 apps/manga/models.py:273 apps/manga/models.py:296
#: apps/manga/models.py:250 apps/manga/models.py:278 apps/manga/models.py:306
msgid "Date"
msgstr "Fecha"
@ -228,32 +365,32 @@ msgstr "Fecha"
msgid "%(volume)s added to collection"
msgstr "%(volume)s añadido a la colección"
#: apps/manga/models.py:261
#: apps/manga/models.py:266
msgid "have"
msgstr "tiene"
#: apps/manga/models.py:275
#: apps/manga/models.py:280
#, python-format
msgid "%(volume)s wishlisted"
msgstr "%(volume)s añadido a deseados"
#: apps/manga/models.py:284
#: apps/manga/models.py:294
msgid "wants"
msgstr "quiere"
#: apps/manga/models.py:298
#: apps/manga/models.py:308
#, python-format
msgid "%(volume)s marked as read"
msgstr "%(volume)s marcado como leído"
#: apps/manga/models.py:307
#: apps/manga/models.py:322
msgid "have read"
msgstr "ha leído"
#: apps/manga/views/search.py:11 apps/manga/views/search.py:13
#: themes/bootflat/templates/_layout.html:80
#: themes/bootflat/templates/_admin/volumes/includes/cover.html:6
#: themes/bootflat/templates/_admin/volumes/includes/cover.html:15
#: themes/bootflat/templates/_layout.html:83
msgid "Search"
msgstr "Buscar"
@ -285,31 +422,19 @@ msgstr "{} marcado como no leído"
msgid "{} marked as read!"
msgstr "¡{} marcado como leído!"
#: apps/users/forms.py:7
msgid "Username"
msgstr "Usuario"
#: models.py:7
msgid "For review"
msgstr "Para revisión"
#: apps/users/forms.py:9
msgid "Password"
msgstr "Contraseña"
#: models.py:9
msgid "Review comment"
msgstr "Comentario de revisión"
#: apps/users/forms.py:31
msgid "This account is disabled."
msgstr "Esta cuenta está desactivada."
#: models.py:11
msgid "Hidden"
msgstr "Oculto"
#: apps/users/forms.py:35
msgid "User with those credentials was not found."
msgstr "No se ha encontrado un usuario con esos credenciales."
#: apps/users/views.py:44
msgid "Logged in successfully."
msgstr "Has accedido correctamente."
#: apps/users/views.py:63
msgid "Logged out successfully"
msgstr "Sesión finalizada."
#: settings/base.py:128
#: settings/base.py:131
msgid "Spanish"
msgstr "Español"
@ -329,25 +454,6 @@ msgstr ""
"Hubo un error interno del servidor. Puede ser temporal, pero si el problema "
"persiste, contacta con nosotros."
#: themes/bootflat/templates/_layout.html:27
msgid "Toggle navigation"
msgstr "Mostrar/Ocultar navegación"
#: themes/bootflat/templates/_layout.html:49
#: themes/bootflat/templates/_layout.html:57
#: themes/bootflat/templates/users/profile-pjax.html:4
#: themes/bootflat/templates/users/profile.html:4
msgid "Profile"
msgstr "Perfil"
#: themes/bootflat/templates/_layout.html:62
msgid "Logout"
msgstr "Cerrar sesión"
#: themes/bootflat/templates/_layout.html:69
msgid "Log in"
msgstr "Entrar"
#: themes/bootflat/templates/_admin/manga/series/includes/volumes.html:13
msgid "Edit"
msgstr "Editar"
@ -361,6 +467,7 @@ msgid "Change to: "
msgstr "Cambiar a:"
#: themes/bootflat/templates/_admin/volumes/change_series.html:28
#: themes/bootflat/templates/account/main.html:61
msgid "Change"
msgstr "Cambiar"
@ -372,6 +479,41 @@ msgstr "Carátula actual:"
msgid "Update with this"
msgstr "Actualizar con esta"
#: themes/bootflat/templates/_layout.html:27
msgid "Toggle navigation"
msgstr "Mostrar/Ocultar navegación"
#: themes/bootflat/templates/_layout.html:47
#: themes/bootflat/templates/_layout.html:55
#: themes/bootflat/templates/users/profile-pjax.html:4
#: themes/bootflat/templates/users/profile.html:4
msgid "Profile"
msgstr "Perfil"
#: themes/bootflat/templates/_layout.html:60
msgid "Logout"
msgstr "Cerrar sesión"
#: themes/bootflat/templates/_layout.html:67
#: themes/bootflat/templates/account/register.html:15
#: themes/bootflat/templates/account/register.html:46
msgid "Register"
msgstr "Registrarse"
#: themes/bootflat/templates/_layout.html:72
msgid "Log in"
msgstr "Entrar"
#: themes/bootflat/templates/account/_layout.html:4
msgid "Account"
msgstr ""
#: themes/bootflat/templates/account/main.html:31
#, fuzzy
#| msgid "Change to: "
msgid "Change password"
msgstr "Cambiar a:"
#: themes/bootflat/templates/homepage/home.html:10
msgid "Upcoming volumes"
msgstr "Futuros lanzamientos"
@ -397,6 +539,16 @@ msgstr "Estadísticas"
msgid "Users"
msgstr "Usuarios"
#: themes/bootflat/templates/manga/publishers/detail.html:10
#: themes/bootflat/templates/manga/series/detail.html:10
#: themes/bootflat/templates/manga/series/includes/volume.html:52
msgid "Edit in admin"
msgstr "Editar en el admin"
#: themes/bootflat/templates/manga/publishers/detail.html:26
msgid "Homepage"
msgstr "Página principal"
#: themes/bootflat/templates/manga/search.html:8
msgid "Looking for..."
msgstr "Buscando..."
@ -407,16 +559,6 @@ msgstr "Buscando..."
msgid "No results"
msgstr "Sin resultados"
#: themes/bootflat/templates/manga/publishers/detail.html:10
#: themes/bootflat/templates/manga/series/detail.html:10
#: themes/bootflat/templates/manga/series/includes/volume.html:47
msgid "Edit in admin"
msgstr "Editar en el admin"
#: themes/bootflat/templates/manga/publishers/detail.html:26
msgid "Homepage"
msgstr "Página principal"
#: themes/bootflat/templates/manga/series/detail.html:26
msgid "Art"
msgstr "Arte"
@ -443,20 +585,20 @@ msgstr "Todos"
msgid "Filter"
msgstr "Filtrar"
#: themes/bootflat/templates/manga/series/list.html:17
msgid "other"
msgstr "otros"
#: themes/bootflat/templates/manga/series/includes/volume.html:39
#: themes/bootflat/templates/manga/series/includes/volume.html:44
#, python-format
msgid "%(pages)s pages"
msgstr "%(pages)s páginas"
#: themes/bootflat/templates/users/login.html:24
#: themes/bootflat/templates/manga/series/list.html:17
msgid "other"
msgstr "otros"
#: themes/bootflat/templates/users/login.html:27
msgid "Access the site"
msgstr "Accede al sitio"
#: themes/bootflat/templates/users/login.html:41
#: themes/bootflat/templates/users/login.html:45
msgid "Login"
msgstr "Entrar"
@ -469,10 +611,6 @@ msgstr "Lista de deseados"
msgid "Edit my profile"
msgstr "Editar mi perfil"
#: themes/bootflat/templates/users/profile.html:50
msgid "My preferences"
msgstr "Mis preferencias"
#: themes/bootflat/templates/users/profile/achievements.html:4
msgid "Achievements"
msgstr "Logros"
@ -494,6 +632,9 @@ msgstr "Volúmenes"
msgid "Volumes wishlisted"
msgstr "Deseados"
#~ msgid "My preferences"
#~ msgstr "Mis preferencias"
#~ msgid "Requires review"
#~ msgstr "Requiere revisión"

View File

@ -65,10 +65,18 @@ INSTALLED_APPS = (
'shelfzilla.apps._admin',
'shelfzilla.apps.config',
'shelfzilla.apps.homepage',
'shelfzilla.apps.landing',
# 'shelfzilla.apps.landing',
'shelfzilla.apps.mailing',
'shelfzilla.apps.manga',
'shelfzilla.apps.blog',
'shelfzilla.apps.faq',
'shelfzilla.apps.pjax',
# API
'corsheaders',
'rest_framework',
'rest_framework.authtoken',
'djoser',
)
TEMPLATE_CONTEXT_PROCESSORS = (
@ -93,6 +101,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
MIDDLEWARE_CLASSES = (
'reversion.middleware.RevisionMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
@ -255,7 +264,7 @@ SUIT_CONFIG = {
{
'label': 'Authorization',
'icon': 'icon-lock',
'models': ('account.user', 'auth.group')
'models': ('account.user', 'auth.group', 'account.accesscode', )
},
{
'app': 'config',
@ -272,6 +281,11 @@ SUIT_CONFIG = {
'label': 'Manga',
'icon': 'icon-book',
},
{
'app': 'faq',
'label': 'FAQs',
'icon': 'icon-book',
},
{
'label': 'Files',
'icon': 'icon-file',
@ -296,3 +310,24 @@ CKEDITOR_CONFIGS = {
# AUTH
#
AUTH_USER_MODEL = 'account.User'
#
# API
#
CORS_ORIGIN_ALLOW_ALL = True
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
}
DJOSER = {
'DOMAIN': 'shelfzilla.com',
'SITE_NAME': 'Shelfzilla',
'PASSWORD_RESET_CONFIRM_URL': '#/password/reset/confirm/{uid}/{token}',
'ACTIVATION_URL': '#/activate/{uid}/{token}',
'LOGIN_AFTER_ACTIVATION': True,
'SEND_ACTIVATION_EMAIL': False,
}

View File

@ -14,6 +14,17 @@ with open(os.environ['APP_CONFIGFILE']) as conffile:
# Installed Apps
INSTALLED_APPS += tuple(config['global']['installed_apps'])
# Middleware classes
if 'middleware_classes' in config['global']:
if 'prepend' in config['global']['middleware_classes']:
MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + tuple(
config['global']['middleware_classes']['prepend']
)
if 'append' in config['global']['middleware_classes']:
MIDDLEWARE_CLASSES += tuple(
config['global']['middleware_classes']['append']
)
# Database
DATABASES = {
'default': dj_database_url.parse(config['global']['database_url'])
@ -75,13 +86,27 @@ FILER_STORAGES = {
# Logging
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'disable_existing_loggers': True,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
},
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': config['log']['logfile'],
},
'opbeat': {
'level': 'WARNING',
'class': 'opbeat.contrib.django.handlers.OpbeatHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose'
}
},
'loggers': {
'django.request': {
@ -89,5 +114,21 @@ LOGGING = {
'level': 'DEBUG',
'propagate': True,
},
'django.db.backends': {
'level': 'ERROR',
'handlers': ['console'],
'propagate': False,
},
'shelfzilla': {
'level': 'WARNING',
'handlers': ['opbeat'],
'propagate': False,
},
# Log errors from the Opbeat module to the console (recommended)
'opbeat.errors': {
'level': 'ERROR',
'handlers': ['console'],
'propagate': False,
},
},
}

View File

@ -30,3 +30,8 @@ FILER_DUMP_PAYLOAD = True
MEDIA_URL = '/media/'
STATIC_URL = '/static/'
EMAIL_BACKEND = 'django_mailgun.MailgunBackend'
MAILGUN_ACCESS_KEY = 'key-fdfc57f2bfb35a4ba5f9c1e3c30af373'
MAILGUN_SERVER_NAME = 'sandbox2dd21e486d144dc59742738b15e494ee.mailgun.org'
FROM_EMAIL = 'info@shelfzilla.com'

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

View File

@ -1,4 +1,9 @@
{% load i18n %}
{% if original %}
<a class="btn btn-info pull-right" href="{% url "admin:manga_volume_add" %}?series={{ original.pk }}">{% trans "Add" %} {% trans "Volume" %}</a>
<br />
<br />
<table class="table table-striped table-bordered table-hover table-condensed">
<thead>
<th>&nbsp;</th>
@ -15,4 +20,4 @@
{% endfor %}
</tbody>
</table>
{% endif %}

View File

@ -1,9 +1,12 @@
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '{{ social_config.google_analytics }}']);
_gaq.push(['_trackPageview']);
ga('create', '{{ social_config.google_analytics }}', '{{ request.get_host }}');
ga('send', 'pageview');
(function() {
var ga = document.createElement('script');
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
ga.setAttribute('async', 'true');
document.documentElement.firstChild.appendChild(ga);
})();
</script>

View File

@ -15,6 +15,9 @@
{% endblock %}
{% endcompress %}
<title>{% block page_title %}ShelfZilla{% endblock %}</title>
{% if social_config.google_analytics %}
{% include "_includes/google_analytics.html" %}
{% endif %}
</head>
<body>
{% block navigation_bar %}
@ -34,7 +37,6 @@
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="navbar-collapse">
{% if user.is_authenticated %}
<ul class="nav navbar-nav">
<li data-pjax-nav {% if navigation.section == "series" %}class="active"{% endif %}>
<a data-pjax href="{% url "series.list" %}">{% trans "Series" %}</a>
@ -42,8 +44,12 @@
<li data-pjax-nav {% if navigation.section == "publishers" %}class="active"{% endif %}>
<a data-pjax href="{% url 'publishers.list' %}">{% trans "Publishers" %}</a>
</li>
<!--
<li data-pjax-nav {% if navigation.section == "faqs" %}class="active"{% endif %}>
<a data-pjax href="{% url 'faq.list' %}">{% trans "Faq" %}</a>
</li>
-->
</ul>
{% endif %}
<ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %}
<li><a href="{% url 'profile' user.username %}" title="{% trans "Profile" %}" data-toggle="tooltip" data-placement="bottom">
@ -64,6 +70,11 @@
</a>
</li>
{% else %}
<li>
<a href="{% url "register" %}">
<i class="glyphicon glyphicon-pencil"></i> {% trans "Register" %}
</a>
</li>
<li>
<a href="{% url "login" %}">
<i class="glyphicon glyphicon-log-in"></i> {% trans "Log in" %}
@ -100,7 +111,7 @@
var USE_PJAX = {{ site_config.use_pjax|lower }};
</script>
<script type="text/javascript" src="{% static "bower/jquery/dist/jquery.js" %}"></script>
<script type="text/javascript" src="{% static "bower/Bootflat/js/bootstrap.min.js" %}"></script>
<script type="text/javascript" src="{% static "bower/bootflatv2/js/bootstrap.min.js" %}"></script>
<script type="text/javascript" src="{% static "bower/jquery-pjax/jquery.pjax.js" %}"></script>
<script type="text/javascript" src="{% static "bower/nprogress/nprogress.js" %}"></script>
<script type="text/javascript" src="{% static "bower/toastr/toastr.js" %}"></script>
@ -113,9 +124,5 @@
{% if not USER_CONFIG.use_pjax %}
<script type="text/javascript">$(function() { window.updateMessages(); });</script>
{% endif %}
{% if social_config.google_analytics %}
{% include "_includes/google_analytics.html" %}
{% endif %}
</body>
</html>

View File

@ -0,0 +1,55 @@
{% extends "_layout.html" %}
{% load i18n %}
{% block main_content %}
<div class="container">
<div class="alert alert-info text-center">
Para registrarte en Shelfzilla necesitarás un <strong>código de invitación</strong>. Los registros abiertos no están disponibles por el momento.<br />
Permanece atento a nuestras redes sociales, ¡pronto empezaremos a repatir!
</div>
<div class="row">
<div class="col-sm-3"></div>
<div class="col-sm-6" >
<div class="panel panel-primary">
<div class="panel-heading">
<h1 class="panel-title">{% trans "Register" %}</h1>
</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="post" action=".">
{% csrf_token %}
{% for field in form %}
<div class="has-error text-right">
{% for error in field.errors %}
<span class="badge badge-danger">{{ error }}</span>
{% endfor %}
</div>
<div class="form-group {% if field.errors %}has-error has-feedback{% endif %}">
<label for="input_{{ field.name }}" class="col-sm-4 control-label">
{{ field.label }}
</label>
<div class="col-sm-8">
<input type="{{ field.field.widget.input_type }}"
class="form-control"
id="input_{{ field.name }}"
name="{{ field.html_name }}"
value="{% if field.field.widget.input_type != "password" and field.value %}{{ field.value }}{% endif %}">
{% if field.errors %}
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
{% endif %}
</div>
</div>
{% endfor %}
<div class="form-group">
<div class="col-sm-12 text-right">
<button type="submit"
class="btn btn-primary">
{% trans "Register" %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1 @@
{% extends "_layout.html" %}

View File

@ -0,0 +1,20 @@
{% extends "faq/layout.html" %}
{% block main_content %}
<div class="container">
<h1>FAQs</h1>
{% for cat in categories %}
<h3>{{ cat.name }}</h3>
<ul class="media-list">
{% for item in cat.questions.all %}
<li class="media well">
<div class="media-body">
<h2 class="media-heading">{{ item.title }}</h2>
<p>{{ item.answer|linebreaks }}</p>
</div>
</li>
{% endfor %}
</ul>
{% endfor %}
</div>
{% endblock %}

View File

@ -13,7 +13,7 @@
<div class="row">
{% for volume in FUTURE_RELEASES %}
<div class="col-sm-6 latest-manga">
{% include "manga/series/includes/volume.html" with volume=volume user=user show_publisher=True type='slim' %}
{% include "manga/series/includes/volume.html" with volume=volume user=user show_publisher=True show_language=True type='slim' %}
</div>
{% if forloop.counter|divisibleby:2 %}
</div><div class="row">
@ -28,7 +28,7 @@
<div class="row">
{% for volume in LATEST_MANGA_ADDED %}
<div class="col-sm-6 latest-manga">
{% include "manga/series/includes/volume.html" with volume=volume user=user show_publisher=True type='slim' %}
{% include "manga/series/includes/volume.html" with volume=volume user=user show_publisher=True show_language=True type='slim' %}
</div>
{% if forloop.counter|divisibleby:2 %}
</div><div class="row">

View File

@ -0,0 +1,8 @@
<p>¡Hola {{ user.username }}!</p>
<p>Ahora formas parte de una comunidad creciente de coleccionistas de comics, esperamos que te sientas a gusto y que nos ayudes a todos a mejorarla y sobretodo que sea útil para ti.</p>
<p><a href="http://shelfzilla.com">Entrar en Shelfzilla</a></p>
<p>Un saludo,<br />
el equipo.</p>

View File

@ -19,7 +19,12 @@
{% endif %}
{% if type == 'slim' %}
<a href="{{ volume.series.get_absolute_url }}" data-pjax>
<h4 class="media-heading">{{ volume }}</h4>
<h4 class="media-heading">
{% if show_language and volume.language %}
<img src="{% static "images/flags/"|add:volume.language.code|add:'.gif' code %}" />
{% endif %}
{{ volume }}
</h4>
</a>
{% else %}
<div class="volume-number">

View File

@ -1,7 +1,6 @@
{% extends '_layout.html' %}
{% load i18n %}
{% block navigation_bar %}{% endblock %}
{% block page_title %}{{ block.super }} | Login{% endblock %}
{% block extra_js %}
@ -19,27 +18,40 @@ $(function(){
{% endblock %}
{% block main_content %}
<div class="panel panel-primary panel-login">
<div class="panel-heading">
<h3 class="panel-title">{% trans "Access the site" %}</h3>
</div>
<div class="panel-body">
<form method="post" class="form">
{% csrf_token %}
{% for field in login_form %}
{% for error in field.errors %}
<p><span class="label label-danger">{{ error|striptags }}</span></p>
{% endfor %}
<div class="form-group {% if field.errors %}has-error has-feedback{% endif %}">
<label class="control-label">{{ field.label }}</label>
<input type="{{ field.field.widget.input_type }}" name="{{ field.html_name }}" class="form-control {{ field.classes }}" />
{% if field.errors %}
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
{% endif %}
<div class="container">
<div class="row">
<div class="col-sm-3"></div>
<div class="col-sm-6">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">{% trans "Access the site" %}</h3>
</div>
{% endfor %}
<button class="btn btn-primary" type="submit">{% trans "Login" %}</button>
</form>
<div class="panel-body">
<form method="post" class="form">
{% csrf_token %}
{% for field in login_form %}
{% for error in field.errors %}
<p><span class="label label-danger">{{ error|striptags }}</span></p>
{% endfor %}
<div class="form-group {% if field.errors %}has-error has-feedback{% endif %}">
<label class="control-label">{{ field.label }}</label>
<input type="{{ field.field.widget.input_type }}" name="{{ field.html_name }}" class="form-control {{ field.classes }}" />
{% if field.errors %}
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
{% endif %}
</div>
{% endfor %}
<div class="text-center">
<button class="btn btn-primary" type="submit">{% trans "Login" %}</button>
</div>
</form>
<hr />
<div class="text-center">
<a href="{% url 'register' %}">¿No tienes cuenta? Regístrate aquí</a>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -14,6 +14,7 @@ urlpatterns = patterns(
url(r'^$', include('shelfzilla.apps.homepage.urls')),
url(r'^', include('shelfzilla.apps.landing.urls')),
url(r'^', include('shelfzilla.apps.account.urls')),
url(r'^faqs/', include('shelfzilla.apps.faq.urls')),
url(r'^blog/', include('shelfzilla.apps.blog.urls', namespace='blog')),
url(r'^series/', include('shelfzilla.apps.manga.urls.series')),
url(r'^volumes/', include('shelfzilla.apps.manga.urls.volumes')),
@ -21,6 +22,14 @@ urlpatterns = patterns(
url(r'^search/', include('shelfzilla.apps.manga.urls.search')),
url(r'^_admin/', include('shelfzilla.apps._admin.urls')),
url(r'^admin/', include(admin.site.urls)),
# url(r'^feedback/',
# include('object_feedback.urls', namespace="object_feedback")),
)
# API
urlpatterns += patterns(
'',
url(r'^api/v1/', include('shelfzilla.urls_api', namespace='api')),
)
if settings.DEBUG:

21
shelfzilla/urls_api.py Normal file
View File

@ -0,0 +1,21 @@
# coding: utf-8
# django
from django.conf.urls import patterns, include, url
# app
from .views import BlockedView
# API
urlpatterns = patterns(
'',
# Manually blocked API endpoints
url(r'^auth/register/', BlockedView.as_view()),
# /auth
url(r'^auth/', include('djoser.urls')),
# /feed
url(r'^', include('shelfzilla.apps.account.api.urls')),
# /volumes
url(r'^', include('shelfzilla.apps.manga.api.urls')),
)

View File

@ -1,9 +1,15 @@
# coding: utf-8
# python
import json
# django
from django.views.generic import View as DjangoView
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.contrib import messages
from django.core.exceptions import PermissionDenied
class View(DjangoView):
@ -46,3 +52,8 @@ class MessagesView(View):
)
return result
class BlockedView(View):
def dispatch(self, *args, **kwargs):
raise PermissionDenied