Translation of UI (german and english)

Bugfix for feeds
    - removed categories related and up
    - load new books now working
    - category random now working
login page is free of non accessible elements
boolean custom column is vivible in UI
books with only with certain languages can be shown
book shelfs can be deleted from UI
Anonymous user view is more resticted
Added browse of series in sidebar
Dependencys in vendor folder are updated to newer versions (licencs files are now present)
Bugfix editing Authors names
Made upload on windows working
This commit is contained in:
OzzieIsaacs
2016-11-09 19:24:33 +01:00
parent a6b6700a73
commit bbf6d9b026
1690 changed files with 149106 additions and 35697 deletions
+18 -11
View File
@@ -52,23 +52,26 @@ books_languages_link = Table('books_languages_link', Base.metadata,
cc = conn.execute("SELECT id, datatype FROM custom_columns")
cc_ids = []
cc_exceptions = ['bool', 'datetime', 'int', 'comments', 'float', 'composite','series' ]
cc_exceptions = [ 'datetime', 'int', 'comments', 'float', 'composite','series' ]
books_custom_column_links = {}
cc_classes = {}
for row in cc:
if row.datatype not in cc_exceptions:
books_custom_column_links[row.id] = Table('books_custom_column_' + str(row.id) + '_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('value', Integer, ForeignKey('custom_column_' + str(row.id) + '.id'), primary_key=True)
)
#books_custom_column_links[row.id]=
cc_ids.append(row.id)
cc_classes = {}
for id in cc_ids:
ccdict={'__tablename__':'custom_column_' + str(id),
'id':Column(Integer, primary_key=True),
'value':Column(String)}
cc_classes[id] = type('Custom_Column_' + str(id), (Base,), ccdict)
cc_ids.append([row.id,row.datatype])
if row.datatype == 'bool':
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
'id': Column(Integer, primary_key=True),
'book': Column(Integer,ForeignKey('books.id')),
'value': Column(Boolean)}
else:
ccdict={'__tablename__':'custom_column_' + str(row.id),
'id':Column(Integer, primary_key=True),
'value':Column(String)}
cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict)
class Comments(Base):
__tablename__ = 'comments'
@@ -182,6 +185,7 @@ class Books(Base):
last_modified = Column(String)
path = Column(String)
has_cover = Column(Integer)
uuid = Column(String)
authors = relationship('Authors', secondary=books_authors_link, backref='books')
tags = relationship('Tags', secondary=books_tags_link, backref='books')
@@ -205,7 +209,10 @@ class Books(Base):
def __repr__(self):
return u"<Books('{0},{1}{2}{3}{4}{5}{6}{7}{8}')>".format(self.title, self.sort, self.author_sort, self.timestamp, self.pubdate, self.series_index, self.last_modified ,self.path, self.has_cover)
for id in cc_ids:
setattr(Books, 'custom_column_' + str(id), relationship(cc_classes[id], secondary=books_custom_column_links[id], backref='books'))
if id[1] == 'bool':
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]], primaryjoin=(Books.id==cc_classes[id[0]].book), backref='books'))
else:
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]], secondary=books_custom_column_links[id[0]], backref='books'))
class Custom_Columns(Base):
__tablename__ = 'custom_columns'
+9 -8
View File
@@ -18,6 +18,7 @@ from email.MIMEBase import MIMEBase
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.generator import Generator
from flask_babel import gettext as _
import subprocess
def update_download(book_id, user_id):
@@ -72,8 +73,8 @@ def send_mail(book_id, kindle_mail):
msg = MIMEMultipart()
msg['From'] = settings["mail_from"]
msg['To'] = kindle_mail
msg['Subject'] = 'Send to Kindle'
text = 'This email has been sent via calibre web.'
msg['Subject'] = _('Send to Kindle')
text = _('This email has been sent via calibre web.')
msg.attach(MIMEText(text))
use_ssl = settings.get('mail_use_ssl', 0)
@@ -95,7 +96,7 @@ def send_mail(book_id, kindle_mail):
formats["pdf"] = os.path.join(config.DB_ROOT, book.path, entry.name + ".pdf")
if len(formats) == 0:
return "Could not find any formats suitable for sending by email"
return _("Could not find any formats suitable for sending by email")
if 'mobi' in formats:
msg.attach(get_attachment(formats['mobi']))
@@ -104,13 +105,13 @@ def send_mail(book_id, kindle_mail):
if filepath is not None:
msg.attach(get_attachment(filepath))
elif filepath is None:
return "Could not convert epub to mobi"
return _("Could not convert epub to mobi")
elif 'pdf' in formats:
msg.attach(get_attachment(formats['pdf']))
elif 'pdf' in formats:
msg.attach(get_attachment(formats['pdf']))
else:
return "Could not find any formats suitable for sending by email"
return _("Could not find any formats suitable for sending by email")
# convert MIME message to string
fp = StringIO()
@@ -134,7 +135,7 @@ def send_mail(book_id, kindle_mail):
mailserver.quit()
except (socket.error, smtplib.SMTPRecipientsRefused, smtplib.SMTPException), e:
app.logger.error(traceback.print_exc())
return "Failed to send mail: %s" % str(e)
return _("Failed to send mail: %s" % str(e))
return None
@@ -154,8 +155,8 @@ def get_attachment(file_path):
return attachment
except IOError:
traceback.print_exc()
message = ('The requested file could not be read. Maybe wrong '
'permissions?')
message = (_('The requested file could not be read. Maybe wrong '\
'permissions?'))
return None
def get_valid_filename(value, replace_whitespace=True):
+1
View File
@@ -6,6 +6,7 @@
}
body{background:#f2f2f2}body h2{font-weight:normal;color:#444}
body { margin-bottom: 40px;}
a{color: #45b29d}a:hover{color: #444;}
.navigation .nav-head{text-transform:uppercase;color:#999;margin:20px 0}.navigation .nav-head:nth-child(1n+2){border-top:1px solid #ccc;padding-top:20px}
.navigation li a{color:#444;text-decoration:none;display:block;padding:10px}.navigation li a:hover{background:rgba(153,153,153,0.4);border-radius:5px}
+46
View File
@@ -131,3 +131,49 @@
}
)
});
var languages = new Bloodhound({
name: 'languages',
datumTokenizer: function(datum) {
return [datum.name];
},
queryTokenizer: function(query) {
return [query];
},
remote: {
url: '/get_languages_json?q=',
replace: function(url, query) {
url_query = url+encodeURIComponent(query);
return url_query;
}
}
});
function language_source(query, cb) {
var bh_adapter = languages.ttAdapter();
var tokens = query.split(",");
var current_language = tokens[tokens.length-1].trim();
tokens.splice(tokens.length-1, 1); // remove last element
var prefix = "";
for (var i = 0; i < tokens.length; i++) {
var tag = tokens[i].trim();
prefix += tag + ", ";
}
prefixed_source(prefix, current_language, cb, bh_adapter);
}
var promise = languages.initialize();
promise.done(function(){
$("#languages").typeahead(
{
highlight: true, minLength: 0,
hint: true
}, {
name: 'languages', displayKey: 'name',
source: language_source
}
)
});
-11
View File
@@ -1,11 +0,0 @@
{% extends "layout.html" %}
{% block body %}
<div class="discover">
<h1>{{title}}</h1>
<ul class="list-unstyled">
{% for entry in entries %}
<li><a href="{{url_for('author', name=entry.name)}}">{{entry.sort}}</a></li>
{% endfor %}
</ul>
</div>
{% endblock %}
-11
View File
@@ -1,11 +0,0 @@
{% extends "layout.html" %}
{% block body %}
<div class="discover">
<h1>{{title}}</h1>
<ul class="list-unstyled">
{% for entry in entries %}
<li><a href="{{url_for('category', name=entry.name)}}">{{entry.name}}</a></li>
{% endfor %}
</ul>
</div>
{% endblock %}
+18 -10
View File
@@ -35,13 +35,13 @@
{% endif %}
{% if entry.series|length > 0 %}
<p>Book {{entry.series_index}} of <a href="{{url_for('series', name=entry.series[0].name)}}">{{entry.series[0].name}}</a></p>
<p>{{_('Book')}} {{entry.series_index}} {{_('of')}} <a href="{{url_for('series', name=entry.series[0].name)}}">{{entry.series[0].name}}</a></p>
{% endif %}
{% if entry.languages.__len__() > 0 %}
<div class="languages">
<p>
<span class="label label-default">language: {{entry.languages[0].lang_code}}</span>
<span class="label label-default">{{_('language')}}: {% for language in entry.languages %} {{language.language_name}}{% if not loop.last %},{% endif %}{% endfor %} </span>
</p>
</div>
{% endif %}
@@ -69,7 +69,15 @@
{% if c.datatype == 'rating' %}
{{ '%d' % (column.value / 2) }}
{% else %}
{{ column.value }}
{% if c.datatype == 'bool' %}
{% if column.value == true %}
<span class="glyphicon glyphicon-ok"></span>
{% else %}
<span class="glyphicon glyphicon-remove"></span>
{% endif %}
{% else %}
{{ column.value }}
{% endif %}
{% endif %}
{% endfor %}
<br />
@@ -81,18 +89,18 @@
{% if entry.comments|length > 0 %}
<h3>Description:</h3>
<h3>{{_('Description:')}}</h3>
{{entry.comments[0].text|safe}}
{% endif %}
{% if g.user.is_authenticated() %}
{% if g.user.is_authenticated %}
<div class="more-stuff">
<div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group" aria-label="Download, send to Kindle, reading">
<div class="btn-group" role="group">
<button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-download"></span> Download
<span class="glyphicon glyphicon-download"></span> {{_('Download')}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
@@ -102,11 +110,11 @@
</ul>
</div>
{% if g.user.kindle_mail %}
<a href="{{url_for('send_to_kindle', book_id=entry.id)}}" id="sendbtn" class="btn btn-primary" role="button"><span class="glyphicon glyphicon-send"></span> Send to Kindle</a>
<a href="{{url_for('send_to_kindle', book_id=entry.id)}}" id="sendbtn" class="btn btn-primary" role="button"><span class="glyphicon glyphicon-send"></span> {{_('Send to Kindle')}}</a>
{% endif %}
<div class="btn-group" role="group">
<button id="btnGroupDrop2" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-eye-open"></span> Read in browser
<span class="glyphicon glyphicon-eye-open"></span> {{_('Read in browser')}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop2">
@@ -125,7 +133,7 @@
<div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group" aria-label="Add to shelves">
<button id="btnGroupDrop2" type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-list"></span> Add to shelf
<span class="glyphicon glyphicon-list"></span> {{_('Add to shelf')}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop2">
@@ -165,7 +173,7 @@
{% if g.user.role_edit() %}
<div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group" aria-label="Edit/Delete book">
<a href="{{ url_for('edit_book', book_id=entry.id) }}" class="btn btn-sm btn-warning" role="button"><span class="glyphicon glyphicon-edit"></span> Edit metadata</a>
<a href="{{ url_for('edit_book', book_id=entry.id) }}" class="btn btn-sm btn-warning" role="button"><span class="glyphicon glyphicon-edit"></span> {{_('Edit metadata')}}</a>
<!-- <a href="{{ url_for('edit_book', book_id=entry.id) }}" class="btn btn-sm btn-danger" role="button"><span class="glyphicon glyphicon-trash"></span> Delete</a> -->
</div>
{% endif %}
+26 -13
View File
@@ -11,42 +11,55 @@
<div class="col-sm-8">
<form role="form" action="{{ url_for('edit_book', book_id=book.id) }}" method="post">
<div class="form-group">
<label for="book_title">Book Title</label>
<label for="book_title">{{_('Book Title')}}</label>
<input type="text" class="form-control" name="book_title" id="book_title" value="{{book.title}}">
</div>
<div class="form-group">
<label for="bookAuthor">Author</label>
<label for="bookAuthor">{{_('Author')}}</label>
<input type="text" class="form-control typeahead" name="author_name" id="bookAuthor" value="{{' & '.join(authors)}}" autocomplete="off">
</div>
<div class="form-group">
<label for="description">Description</label>
<label for="description">{{_('Description')}}</label>
<textarea class="form-control" name="description" id="description" rows="7">{% if book.comments %}{{book.comments[0].text}}{%endif%}</textarea>
</div>
<div class="form-group">
<label for="tags">Tags</label>
<input type="text" class="form-control" name="tags" id="tags" value="{% for tag in book.tags %}{{tag.name.strip()}}, {% endfor %}">
<label for="tags">{{_('Tags')}}</label>
<input type="text" class="form-control typeahead" name="tags" id="tags" value="{% for tag in book.tags %}{{tag.name.strip()}}, {% endfor %}">
</div>
<div class="form-group">
<label for="series">Series</label>
<input type="text" class="form-control" name="series" id="series" value="{% if book.series %}{{book.series[0].name}}{% endif %}">
<label for="series">{{_('Series')}}</label>
<input type="text" class="form-control typeahead" name="series" id="series" value="{% if book.series %}{{book.series[0].name}}{% endif %}">
</div>
<div class="form-group">
<label for="series_index">Series id</label>
<label for="series_index">{{_('Series id')}}</label>
<input type="text" class="form-control" name="series_index" id="series_index" value="{{book.series_index}}">
</div>
<div class="form-group">
<label for="rating">Rating</label>
<label for="rating">{{_('Rating')}}</label>
<input type="number" min="1" max="5" step="1" class="form-control" name="rating" id="rating" value="{% if book.ratings %}{{book.ratings[0].rating / 2}}{% endif %}">
</div>
<div class="form-group">
<label for="cover_url">Cover URL (jpg)</label>
<label for="cover_url">{{_('Cover URL (jpg)')}}</label>
<input type="text" class="form-control" name="cover_url" id="cover_url" value="">
</div>
<div class="form-group">
<label for="languages">{{_('Language')}}</label>
<input type="text" class="form-control typeahead" name="languages" id="languages" value="{% for language in book.languages %}{{language.language_name.strip()}}, {% endfor %}">
</div>
{% if cc|length > 0 %}
{% for c in cc %}
<div class="form-group">
<label for="{{ 'custom_column_' ~ c.id }}">{{ c.name }}</label>
{% if c.datatype == 'bool' %}
<select name="{{ 'custom_column_' ~ c.id }}" id="{{ 'custom_column_' ~ c.id }}" class="form-control">
<option value="None" {% if book['custom_column_' ~ c.id]|length == 0 %} selected {% endif %}></option>
<option value="True" {% if book['custom_column_' ~ c.id]|length > 0 %}{% if book['custom_column_' ~ c.id][0].value ==true %}selected{% endif %}{% endif %} >{{_('Yes')}}</option>
<option value="False" {% if book['custom_column_' ~ c.id]|length > 0 %}{% if book['custom_column_' ~ c.id][0].value ==false %}selected{% endif %}{% endif %}>{{_('No')}}</option>
</select>
{% endif %}
{% if c.datatype in ['text', 'series'] and not c.is_multiple %}
<input type="text" class="form-control" name="{{ 'custom_column_' ~ c.id }}" id="{{ 'custom_column_' ~ c.id }}"
{% if book['custom_column_' ~ c.id]|length > 0 %}
@@ -72,7 +85,7 @@
{% endfor %}
</select>
{% endif %}
{% if c.datatype == 'rating' %}
<input type="number" min="1" max="5" step="1" class="form-control" name="{{ 'custom_column_' ~ c.id }}" id="{{ 'custom_column_' ~ c.id }}"
{% if book['custom_column_' ~ c.id]|length > 0 %}
@@ -86,10 +99,10 @@
<div class="checkbox">
<label>
<input name="detail_view" type="checkbox" checked> view book after edit
<input name="detail_view" type="checkbox" checked> {{_('view book after edit')}}
</label>
</div>
<button type="submit" class="btn btn-default">Submit</button>
<button type="submit" class="btn btn-default">{{_('Submit')}}</button>
</form>
</div>
{% endif %}
+7 -7
View File
@@ -4,30 +4,30 @@
<h1>{{title}}</h1>
<form role="form" method="POST">
<div class="form-group">
<label for="mail_server">SMTP hostname</label>
<label for="mail_server">{{_('SMTP hostname')}}</label>
<input type="text" class="form-control" name="mail_server" id="mail_server" value="{{content.mail_server}}">
</div>
<div class="form-group">
<label for="mail_port">SMTP port (usually 25 for plain SMTP and 587 for SSL)</label>
<label for="mail_port">{{_('SMTP port (usually 25 for plain SMTP and 587 for SSL)')}}</label>
<input type="text" class="form-control" name="mail_port" id="mail_port" value="{{content.mail_port}}">
</div>
<div class="form-group">
<label for="mail_use_ssl">Server uses SSL (StartTLS)</label>
<label for="mail_use_ssl">{{_('Server uses SSL (StartTLS)')}}</label>
<input type="checkbox" name="mail_use_ssl" id="mail_use_ssl" {% if content.mail_use_ssl %}checked{% endif %}>
</div>
<div class="form-group">
<label for="mail_login">SMTP login</label>
<label for="mail_login">{{_('SMTP login')}}</label>
<input type="text" class="form-control" name="mail_login" id="mail_login" value="{{content.mail_login}}">
</div>
<div class="form-group">
<label for="mail_password">SMTP password</label>
<label for="mail_password">{{_('SMTP password')}}</label>
<input type="password" class="form-control" name="mail_password" id="mail_password" value="{{content.mail_password}}">
</div>
<div class="form-group">
<label for="mail_from">From e-mail</label>
<label for="mail_from">{{_('From e-mail')}}</label>
<input type="text" class="form-control" name="mail_from" id="mail_from" value="{{content.mail_from}}">
</div>
<button type="submit" class="btn btn-default">Submit</button>
<button type="submit" class="btn btn-default">{{_('Submit')}}</button>
</form>
</div>
+29 -12
View File
@@ -7,17 +7,17 @@
<link rel="start"
href="{{url_for('feed_index')}}"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="related"
href="{{url_for('feed_index')}}"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<link rel="up"
href="{{url_for('feed_index')}}"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="next"
title="{{_('Next')}}"
href="{{ next_url }}"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="search"
href="{{url_for('feed_osd')}}"
type="application/opensearchdescription+xml"/>
<title>Calibre Web</title>
<updated>2010-01-10T10:03:10Z</updated>
<author>
<name>Calibre Web</name>
<uri>https://github.com/janeczku/calibre-web</uri>
@@ -27,7 +27,7 @@
<entry>
<title>{{entry.title}}</title>
<id>{{entry.uuid}}</id>
<updated>{{entry.last_modified}}</updated>
<updated>{{entry.timestamp}}</updated>
<author>
<name>{{entry.authors[0].name}}</name>
<uri>{{entry.authors[0].name}}</uri>
@@ -41,18 +41,13 @@
<summary>{% if entry.comments[0] %}{{entry.comments[0].text|striptags}}{% endif %}</summary>
{% if entry.has_cover %}
<link rel="http://opds-spec.org/image"
href="{{ url_for('get_cover', cover_path=entry.path) }}"
href="{{ url_for('feed_get_cover', cover_path=entry.path) }}"
type="image/jpg"/>
<link rel="http://opds-spec.org/image/thumbnail"
href="{{ url_for('get_cover', cover_path=entry.path) }}"
href="{{ url_for('feed_get_cover', cover_path=entry.path) }}"
type="image/jpg"/>
{% endif %}
<link rel="alternate"
href="/opds-catalogs/entries/4571.complete.xml"
type="application/atom+xml;type=entry;profile=opds-catalog"
title="Complete Catalog Entry for Bob, Son of Bob"/>
{% for format in entry.data %}
<link rel="http://opds-spec.org/acquisition"
href="{{ url_for('get_opds_download_link', book_id=entry.id, format=format.format|lower)}}"{% if format.format|lower == "epub" %}
@@ -63,4 +58,26 @@
{% endfor %}
</entry>
{% endfor %}
{% for author in authors %}
<entry>
<title>{{author.name}}</title>
<!--content type="text">{{author.name}}</content-->
<id>{{ url_for('feed_author', name=author.name) }}</id>
<link href="{{ url_for('feed_author', name=author.name)}}" type="application/atom+xml;profile=opds-catalog;kind=navigation" rel="subsection"/>
</entry>
{% endfor %}
{% for entry in categorys %}
<entry>
<title>{{entry.name}}</title>
<id>{{ url_for('feed_category', name=entry.name) }}</id>
<link href="{{ url_for('feed_category', name=entry.name)}}" type="application/atom+xml;profile=opds-catalog;kind=navigation" rel="subsection"/>
</entry>
{% endfor %}
{% for entry in series %}
<entry>
<title>{{entry.name}}</title>
<id>{{ url_for('feed_series', name=entry.name) }}</id>
<link href="{{ url_for('feed_series', name=entry.name)}}" type="application/atom+xml;profile=opds-catalog;kind=navigation" rel="subsection"/>
</entry>
{% endfor %}
</feed>
+2 -4
View File
@@ -1,8 +1,8 @@
{% extends "layout.html" %}
{% block body %}
{% if random.count() > 0 %}
{% if g.user.show_random_books() %}
<div class="discover">
<h2>Discover (Random Books)</h2>
<h2>{{_('Discover (Random Books)')}}</h2>
<div class="row">
{% for entry in random %}
@@ -31,7 +31,6 @@
{% endif %}
</div>
</div>
<!-- <p><a href="{{ url_for('edit_book', book_id=entry.id) }}">{{entry.authors[0].name}}: {{entry.title}}</a></p> -->
{% endfor %}
</div>
</div>
@@ -72,7 +71,6 @@
{% endif %}
</div>
</div>
<!-- <p><a href="{{ url_for('edit_book', book_id=entry.id) }}">{{entry.authors[0].name}}: {{entry.title}}</a></p> -->
{% endfor %}
</div>
</div>
+37 -21
View File
@@ -5,19 +5,14 @@
href="{{url_for('feed_index')}}"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="start"
href="{{url_for('feed_index')}}"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="related"
href="{{url_for('feed_index')}}"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<link rel="up"
title="{{_('Start')}}"
href="{{url_for('feed_index')}}"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<link rel="search"
title="{{_('Search')}}"
href="{{url_for('feed_osd')}}"
type="application/opensearchdescription+xml"/>
<title>Calibre Web</title>
<updated>2010-01-10T10:03:10Z</updated>
<author>
<name>Calibre Web</name>
<uri>https://github.com/janeczku/calibre-web</uri>
@@ -25,32 +20,53 @@
<entry>
<title>Hot Books</title>
<title>{{_('Hot Books')}}</title>
<link rel="http://opds-spec.org/sort/popular"
href="{{url_for('feed_hot')}}"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>2010-01-10T10:01:01Z</updated>
<id>urn:uuid:d49e8018-a0e0-499e-9423-7c175fa0c56e</id>
<content type="text">Popular publications from this catalog based on Rating.</content>
<id>{{url_for('feed_hot')}}</id>
<content type="text">{{_('Popular publications from this catalog based on Rating.')}}</content>
</entry>
<entry>
<title>New Books</title>
<link rel="http://opds-spec.org/sort/popular"
<title>{{_('New Books')}}</title>
<link rel="http://opds-spec.org/sort/new"
href="{{url_for('feed_new')}}"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>2010-01-10T10:01:01Z</updated>
<id>urn:uuid:d49e8018-a0e0-499e-9423-7c175fa0c56e</id>
<content type="text">The latest Books</content>
<id>{{url_for('feed_new')}}</id>
<content type="text">{{_('The latest Books')}}</content>
</entry>
<entry>
<title>Random Books</title>
<link rel="http://opds-spec.org/sort/random"
<title>{{_('Random Books')}}</title>
<link rel="http://opds-spec.org/featured"
href="{{url_for('feed_discover')}}"
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
<updated>2010-01-10T10:01:01Z</updated>
<id>urn:uuid:d49e8018-a0e0-499e-9423-7c175fa0c56e</id>
<content type="text">Show Random Books</content>
<id>{{url_for('feed_discover')}}</id>
<content type="text">{{_('Show Random Books')}}</content>
</entry>
<entry>
<title>{{_('Authors')}}</title>
<link rel="subsection"
href="{{url_for('feed_authorindex')}}"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<id>{{url_for('feed_authorindex')}}</id>
<content type="text">{{_('Books ordered by Author')}}</content>
</entry>
<entry>
<title>{{_('Category list')}}</title>
<link rel="subsection"
href="{{url_for('feed_categoryindex')}}"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<id>{{url_for('feed_categoryindex')}}</id>
<content type="text">{{_('Books ordered by category')}}</content>
</entry>
<entry>
<title>{{_('Series list')}}</title>
<link rel="subsection"
href="{{url_for('feed_seriesindex')}}"
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
<id>{{url_for('feed_seriesindex')}}</id>
<content type="text">{{_('Books ordered by series')}}</content>
</entry>
</feed>
+12
View File
@@ -0,0 +1,12 @@
{% extends "layout.html" %}
{% block body %}
<h1>{{title}}</h1>
<div class="container">
{% for lang in languages %}
<div class="row">
<div class="col-xs-1" align="left"><span class="badge">{{lang_counter[loop.index0].bookcount}}</span></div>
<div class="col-xs-6"><a href="{{url_for('language', name=lang.lang_code)}}">{{lang.name}}</a></div>
</div>
{% endfor %}
</div>
{% endblock %}
+51 -32
View File
@@ -43,7 +43,7 @@
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="sr-only">{{_('Toggle navigation')}}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
@@ -51,41 +51,44 @@
<a class="navbar-brand" href="{{url_for('index')}}">Calibre Web</a>
</div>
<div class="navbar-collapse collapse">
{% if g.user.is_authenticated or g.user.is_anonymous() %}
<ul class="nav navbar-nav ">
<li>
<form class="navbar-form navbar-left" role="search" action="{{url_for('search')}}" method="GET">
<div class="form-group">
<input type="text" class="form-control" name="query" placeholder="Search">
<input type="text" class="form-control" name="query" placeholder="{{_('Search')}}">
</div>
<button type="submit" class="btn btn-default">Go!</button>
<button type="submit" class="btn btn-default">{{_('Go!')}}</button>
</form>
</li>
<li><a href="{{url_for('advanced_search')}}"><span class="glyphicon glyphicon-search"></span> Advanced Search</a></li>
<li><a href="{{url_for('advanced_search')}}"><span class="glyphicon glyphicon-search"></span> {{_('Advanced Search')}}</a></li>
</ul>
{% endif %}
<ul class="nav navbar-nav navbar-right" id="main-nav">
{% if g.user.is_authenticated() %}
{% if g.user.is_authenticated or g.user.is_anonymous() %}
{% if g.user.role_upload() or g.user.role_admin()%}
{% if g.allow_upload %}
<li>
<form id="form-upload" class="navbar-form" action="{{ url_for('upload') }}" method="post" enctype="multipart/form-data">
<div class="form-group">
<span class="btn btn-default btn-file">Upload <input id="btn-upload" name="btn-upload" type="file"></span>
</div>
</form>
</li>
{% endif %}
{% if g.allow_upload %}
<li>
<form id="form-upload" class="navbar-form" action="{{ url_for('upload') }}" method="post" enctype="multipart/form-data">
<div class="form-group">
<span class="btn btn-default btn-file">{{_('Upload')}} <input id="btn-upload" name="btn-upload" type="file"></span>
</div>
</form>
</li>
{% endif %}
{% endif %}
{% if g.user.role_admin() %}
<li><a href="{{url_for('user_list')}}"><span class="glyphicon glyphicon-dashboard"></span> Admin</a></li>
<li><a href="{{url_for('user_list')}}"><span class="glyphicon glyphicon-dashboard"></span> {{_('Admin')}}</a></li>
{% endif %}
<li><a href="{{url_for('profile')}}"><span class="glyphicon glyphicon-user"></span> {{g.user.nickname}}</a></li>
<li><a href="{{url_for('logout', next='%s%s' % (request.script_root, request.path))}}"><span class="glyphicon glyphicon-log-out"></span> Logout</a></li>
{% else %}
<li><a href="{{url_for('login', next='%s%s' % (request.script_root, request.path))}}"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
{% if g.allow_registration %}
<li><a href="{{url_for('register')}}"><span class="glyphicon glyphicon-user"></span> Register</a></li>
{% if not g.user.is_anonymous() %}
<li><a href="{{url_for('logout', next='%s%s' % (request.script_root, request.path))}}"><span class="glyphicon glyphicon-log-out"></span> {{_('Logout')}}</a></li>
{% endif %}
{% endif %}
{% if g.allow_registration and not g.user.is_authenticated %}
<li><a href="{{url_for('login', next='%s%s' % (request.script_root, request.path))}}"><span class="glyphicon glyphicon-log-in"></span> {{_('Login')}}</a></li>
<li><a href="{{url_for('register')}}"><span class="glyphicon glyphicon-user"></span> {{_('Register')}}</a></li>
{% endif %}
</ul>
</div><!--/.nav-collapse -->
</div>
@@ -109,30 +112,46 @@
{% endfor %}
<div class="container-fluid">
<div class="row-fluid">
{% if g.user.is_authenticated or g.user.is_anonymous() %}
<div class="col-sm-2">
<nav class="navigation">
<ul class="list-unstyled" id="scnd-nav" intent in-standard-append="nav.navigation" in-mobile-after="#main-nav" in-mobile-class="nav navbar-nav">
<li class="nav-head hidden-xs">Browse</li>
<li><a href="{{url_for('index')}}"><span class="glyphicon glyphicon-book"></span> New Books</a></li>
<li><a href="{{url_for('hot_books')}}"><span class="glyphicon glyphicon-fire"></span> Hot Books</a></li>
<li><a href="{{url_for('discover')}}"><span class="glyphicon glyphicon-random"></span> Discover</a></li>
<li><a href="{{url_for('category_list')}}"><span class="glyphicon glyphicon-inbox"></span> Categories</a></li>
<li><a href="{{url_for('author_list')}}"><span class="glyphicon glyphicon-user"></span> Authors</a></li>
{% if g.user.is_authenticated() %}
<li class="nav-head hidden-xs">Public Shelves</li>
<li class="nav-head hidden-xs">{{_('Browse')}}</li>
<li><a href="{{url_for('index')}}"><span class="glyphicon glyphicon-book"></span> {{_('New Books')}}</a></li>
{% if g.user.show_hot_books() %}
<li><a href="{{url_for('hot_books')}}"><span class="glyphicon glyphicon-fire"></span> {{_('Hot Books')}}</a></li>
{%endif%}
{% if g.user.show_random_books() %}
<li><a href="{{url_for('discover')}}"><span class="glyphicon glyphicon-random"></span> {{_('Discover')}}</a></li>
{%endif%}
{% if g.user.show_category() %}
<li><a href="{{url_for('category_list')}}"><span class="glyphicon glyphicon-inbox"></span> {{_('Categories')}}</a></li>
{%endif%}
{% if g.user.show_series() %}
<li><a href="{{url_for('series_list')}}"><span class="glyphicon glyphicon-bookmark"></span> {{_('Series')}}</a></li>
{%endif%}
<li><a href="{{url_for('author_list')}}"><span class="glyphicon glyphicon-user"></span> {{_('Authors')}}</a></li>
{% if g.user.filter_language() == 'all' and g.user.show_language() %}
<li><a href="{{url_for('language_overview')}}"><span class="glyphicon glyphicon-flag"></span> {{_('Languages')}} </a></li>
{%endif%}
{% if g.user.is_authenticated or g.user.is_anonymous() %}
<li class="nav-head hidden-xs">{{_('Public Shelves')}}</li>
{% for shelf in g.public_shelfes %}
<li><a href="{{url_for('show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list"></span> {{shelf.name}}</a></li>
{% endfor %}
<li class="nav-head hidden-xs">Your Shelves</li>
<li class="nav-head hidden-xs">{{_('Your Shelves')}}</li>
{% for shelf in g.user.shelf %}
<li><a href="{{url_for('show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list"></span> {{shelf.name}}</a></li>
{% endfor %}
<li class="create-shelf"><a href="{{url_for('create_shelf')}}">Create a Shelf</a></li>
{% if not g.user.is_anonymous() %}
<li class="create-shelf"><a href="{{url_for('create_shelf')}}">{{_('Create a Shelf')}}</a></li>
{% endif %}
{% endif %}
<li><a href="{{url_for('stats')}}"><span class="glyphicon glyphicon-info-sign"></span> {{_('About')}}</a></li>
</ul>
</nav>
</div>
{% endif %}
<div class="col-sm-10">
{% block body %}{% endblock %}
{% if pagination %}
@@ -159,4 +178,4 @@
</div>
{% block js %}{% endblock %}
</body>
</html>
</html>
+12
View File
@@ -0,0 +1,12 @@
{% extends "layout.html" %}
{% block body %}
<h1>{{title}}</h1>
<div class="container">
{% for entry in entries %}
<div class="row">
<div class="col-xs-1" align="left"><span class="badge">{{entry.count}}</span></div>
<div class="col-xs-6"><a href="{{url_for(folder, name=entry[0].name)}}">{{entry[0].name}}</a></div>
</div>
{% endfor %}
</div>
{% endblock %}
+7 -7
View File
@@ -1,22 +1,22 @@
{% extends "layout.html" %}
{% block body %}
<div class="well col-sm-6 col-sm-offset-2">
<h2 style="margin-top: 0">Login</h2>
<h2 style="margin-top: 0">{{_('Login')}}</h2>
<form method="POST" role="form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username" name="username" placeholder="Username">
<label for="username">{{_('Username')}}</label>
<input type="text" class="form-control" id="username" name="username" placeholder="{{_('Username')}}">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="Password">
<label for="password">{{_('Password')}}</label>
<input type="password" class="form-control" id="password" name="password" placeholder="{{_('Password')}}">
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="remember_me" checked> Remember me
<input type="checkbox" name="remember_me" checked> {{_('Remember me')}}
</label>
</div>
<button type="submit" class="btn btn-default">Submit</button>
<button type="submit" class="btn btn-default">{{_('Submit')}}</button>
</form>
</div>
{% if error %}
+2 -2
View File
@@ -23,9 +23,9 @@
<Image height="16" width="16" type="image/vnd.microsoft.icon">http://example.com/websearch.ico</Image>
-->
<Query role="example" searchTerms="shakespeare hamlet" />
<!--Query role="example" searchTerms="shakespeare hamlet" />
<Query role="example" searchTerms="doyle detective" />
<Query role="example" searchTerms="love stories" />
<Query role="example" searchTerms="love stories" /-->
<Attribution>Search Data Copyright 1971-2012, Project Gutenberg, All Rights Reserved.</Attribution>
<SyndicationRight>open</SyndicationRight>
+1 -1
View File
@@ -133,7 +133,7 @@
<h3>Settings</h3>
<div>
<p>
<input type="checkbox" id="sidebarReflow" name="sidebarReflow">Reflow text when sidebars are open.
<input type="checkbox" id="sidebarReflow" name="sidebarReflow">{{_(Reflow text when sidebars are open.)}}
</p>
</div>
<div class="closer icon-cancel-circled"></div>
+1 -1
View File
@@ -26,7 +26,7 @@ See https://github.com/adobe-type-tools/cmap-resources
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>PDF.js viewer</title>
<title>{{_(PDF.js viewer)}}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/viewer.css') }}">
+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Basic txt Reader</title>
<title>{{_(Basic txt Reader)}}</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<meta name="apple-mobile-web-app-capable" content="yes">
+8 -8
View File
@@ -1,21 +1,21 @@
{% extends "layout.html" %}
{% block body %}
<div class="well col-sm-6 col-sm-offset-2">
<h2 style="margin-top: 0">Register a new account</h2>
<h2 style="margin-top: 0">{{_('Register a new account')}}</h2>
<form method="POST" role="form">
<div class="form-group required">
<label for="nickname">Username</label>
<input type="text" class="form-control" id="nickname" name="nickname" placeholder="Choose a username" required>
<label for="nickname">{{_('Username')}}</label>
<input type="text" class="form-control" id="nickname" name="nickname" placeholder="{{_('Choose a username')}}" required>
</div>
<div class="form-group required">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="Choose a password" required>
<label for="password">{{_('Password')}}</label>
<input type="password" class="form-control" id="password" name="password" placeholder="{{_('Choose a password')}}" required>
</div>
<div class="form-group required">
<label for="email">Email address</label>
<input type="email" class="form-control" id="email" name="email" placeholder="Your email address" required>
<label for="email">{{_('Email address')}}</label>
<input type="email" class="form-control" id="email" name="email" placeholder="{{_('Your email address')}}" required>
</div>
<button type="submit" class="btn btn-primary">Register</button>
<button type="submit" class="btn btn-primary">{{_('Register')}}</button>
</form>
</div>
{% if error %}
+3 -3
View File
@@ -3,10 +3,10 @@
<div class="discover">
{% if entries|length < 1 %}
<h2>No Results for: {{searchterm}}</h2>
<p>Please try a diffrent Search</p>
<h2>{{_('No Results for:')}} {{searchterm}}</h2>
<p>{{_('Please try a diffrent Search')}}</p>
{% else %}
<h2>{{entries|length}} Results for: {{searchterm}}</h2>
<h2>{{entries|length}} {{_('Results for:')}} {{searchterm}}</h2>
{%endif%}
<div class="row">
+7 -7
View File
@@ -3,14 +3,14 @@
<div class="col-sm-8">
<form role="form" action="{{ url_for('advanced_search') }}" method="GET">
<div class="form-group">
<label for="book_title">Book Title</label>
<label for="book_title">{{_('Book Title')}}</label>
<input type="text" class="form-control" name="book_title" id="book_title" value="">
</div>
<div class="form-group">
<label for="bookAuthor">Author</label>
<label for="bookAuthor">{{_('Author')}}</label>
<input type="text" class="form-control typeahead" name="author_name" id="bookAuthor" value="" autocomplete="off">
</div>
<label for="Tags">Include Tags</label>
<label for="Tags">{{_('Tags')}}</label>
<div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for tag in tags %}
@@ -20,7 +20,7 @@
{% endfor %}
</div>
</div>
<label for="Tags">Exclude Tags</label>
<label for="Tags">{{_('Exclude Tags')}}</label>
<div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for tag in tags %}
@@ -30,7 +30,7 @@
{% endfor %}
</div>
</div>
<button type="submit" class="btn btn-default">Submit</button>
<button type="submit" class="btn btn-default">{{_('Submit')}}</button>
</form>
</div>
{% endblock %}
@@ -39,8 +39,8 @@
<script src="{{ url_for('static', filename='js/typeahead.bundle.js') }}"></script>
<script src="{{ url_for('static', filename='js/edit_books.js') }}"></script>
<script>
$('form').on('change input typeahead:selected', function() {
form = $('form').serialize();
$('form')}}.on('change input typeahead:selected', function() {
form = $('form')}}.serialize();
$.getJSON( "{{ url_for('get_matching_tags') }}", form, function( data ) {
$('.tags_click').each(function() {
if ($.inArray(parseInt($(this).children('input').first().val(), 10), data.tags) == -1 ) {
+3
View File
@@ -2,6 +2,9 @@
{% block body %}
<div class="discover">
<h2>{{title}}</h2>
{% if g.user.is_authenticated %}
<a href=" {{ url_for('delete_shelf', shelf_id=shelf.id) }} " class="btn btn-danger">{{ _('Delete this Shelf') }} </a>
{% endif %}
<div class="row">
{% for entry in entries %}
+3 -3
View File
@@ -4,15 +4,15 @@
<h1>{{title}}</h1>
<form role="form" method="POST">
<div class="form-group">
<label for="title">Title</label>
<label for="title">{{_('Title')}}</label>
<input type="text" class="form-control" name="title" id="title" value="">
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="is_public"> should the shelf be public?
<input type="checkbox" name="is_public"> {{_('should the shelf be public?')}}
</label>
</div>
<button type="submit" class="btn btn-default">Submit</button>
<button type="submit" class="btn btn-default">{{_('Submit')}}</button>
</form>
</div>
{% endblock %}
+2 -1
View File
@@ -1,6 +1,7 @@
{% extends "layout.html" %}
{% block body %}
<div class="discover">
<h1>{{counter}} Books in this Library</h1>
<h2>{{bookcounter}} {{_('Books in this Library')}}</h2>
<h2>{{authorcounter}} {{_('Authors in this Library')}}</h2>
</div>
{% endblock %}
+50 -12
View File
@@ -5,58 +5,96 @@
<form role="form" method="POST" autocomplete="off">
{% if g.user and g.user.role_admin() and new_user %}
<div class="form-group required">
<label for="nickname">Username</label>
<label for="nickname">{{_('Username')}}</label>
<input type="text" class="form-control" name="nickname" id="nickname" value="{{ content.nickname if content.nickname != None }}" autocomplete="off">
</div>
{% endif %}
<div class="form-group">
<label for="email">Email address</label>
<label for="email">{{_('Email address')}}</label>
<input type="email" class="form-control" name="email" id="email" value="{{ content.email if content.email != None }}" autocomplete="off" required>
</div>
{% if g.user and g.user.role_passwd() or g.user.role_admin()%}
<div class="form-group">
<label for="password">Password</label>
<label for="password">{{_('Password')}}</label>
<input type="password" class="form-control" name="password" id="password" value="" autocomplete="off">
</div>
{% endif %}
<div class="form-group">
<label for="kindle_mail">Kindle E-Mail</label>
<label for="kindle_mail">{{_('Kindle E-Mail')}}</label>
<input type="email" class="form-control" name="kindle_mail" id="kindle_mail" value="{{ content.kindle_mail if content.kindle_mail != None }}">
</div>
<div class="form-group">
<label for="locale">{{_('Language')}}</label>
<select name="locale" id="locale" class="form-control">
{% for translation in translations %}
<option value="{{translation.language}}" {% if translation.language == content.locale %}selected{% endif %}>{{ translation.display_name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="default_language">{{_('Show books with language')}}</label>
<select name="default_language" id="default_language" class="form-control">
<option value="all" >{{ _('Show all') }}</option>
{% for language in languages %}
<option value="{{ language.lang_code }}" {% if content.default_language == language.lang_code %}selected{% endif %}>{{ language.name }}</option>
{% endfor %}
</select>
</div>
{% if g.user and g.user.role_admin() and not profile %}
<div class="form-group">
<label for="admin_role">Admin user</label>
<input type="checkbox" name="show_random" {% if content.random_books %}checked{% endif %}>
<label for="show_random">{{_('Show random books')}}</label>
</div>
<div class="form-group">
<input type="checkbox" name="show_hot" {% if content.hot_books %}checked{% endif %}>
<label for="show_hot">{{_('Show hot books')}}</label>
</div>
<div class="form-group">
<input type="checkbox" name="show_language" {% if content.language_books %}checked{% endif %}>
<label for="show_language">{{_('Show language selection')}}</label>
</div>
<div class="form-group">
<input type="checkbox" name="show_series" {% if content.series_books %}checked{% endif %}>
<label for="show_series">{{_('Show series selection')}}</label>
</div>
<div class="form-group">
<input type="checkbox" name="show_category" {% if content.category_books %}checked{% endif %}>
<label for="show_category">{{_('Show category selection')}}</label>
</div>
<div class="form-group">
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
<label for="admin_role">{{_('Admin user')}}</label>
</div>
<div class="form-group">
<label for="download_role">Allow Downloads</label>
<input type="checkbox" name="download_role" id="download_role" {% if content.role_download() %}checked{% endif %}>
<label for="download_role">{{_('Allow Downloads')}}</label>
</div>
<div class="form-group">
<label for="upload_role">Allow Uploads</label>
<input type="checkbox" name="upload_role" id="upload_role" {% if content.role_upload() %}checked{% endif %}>
<label for="upload_role">{{_('Allow Uploads')}}</label>
</div>
<div class="form-group">
<label for="edit_role">Allow Edit</label>
<input type="checkbox" name="edit_role" id="edit_role" {% if content.role_edit() %}checked{% endif %}>
<label for="edit_role">{{_('Allow Edit')}}</label>
</div>
<div class="form-group">
<label for="passwd_role">Allow Changing Password</label>
<input type="checkbox" name="passwd_role" id="passwd_role" {% if content.role_passwd() %}checked{% endif %}>
<label for="passwd_role">{{_('Allow Changing Password')}}</label>
</div>
{% endif %}
{% if g.user and g.user.role_admin() and not profile and not new_user %}
<div class="checkbox">
<label>
<input type="checkbox" name="delete"> Delete this user
<input type="checkbox" name="delete"> {{_('Delete this user')}}
</label>
</div>
{% endif %}
<button type="submit" class="btn btn-default">Submit</button>
<button type="submit" class="btn btn-default">{{_('Submit')}}</button>
</form>
{% if downloads %}
<h2>Recent Downloads</h2>
<h2>{{_('Recent Downloads')}}</h2>
{% for entry in downloads %}
<div class="col-sm-2">
<a class="pull-left" href="{{ url_for('show_book', id=entry.id) }}">
+18 -18
View File
@@ -4,15 +4,15 @@
<h2>{{title}}</h2>
<table class="table table-striped">
<tr>
<th>Nickname</th>
<th>Email</th>
<th>Kindle</th>
<th>DLS</th>
<th>Admin</th>
<th>Download</th>
<th>Upload</th>
<th>Edit</th>
<th>Passwd</th>
<th>{{_('Nickname')}}</th>
<th>{{_('Email')}}</th>
<th>{{_('Kindle')}}</th>
<th>{{_('DLS')}}</th>
<th>{{_('Admin')}}</th>
<th>{{_('Download')}}</th>
<th>{{_('Upload')}}</th>
<th>{{_('Edit')}}</th>
<th>{{_('Passwd')}}</th>
</tr>
{% for user in content %}
@@ -29,16 +29,16 @@
{% endfor %}
</table>
<div class="btn btn-default"><a href="{{url_for('new_user')}}">Add new user</a></div>
<h2>SMTP mail settings</h2>
<div class="btn btn-default"><a href="{{url_for('new_user')}}">{{_('Add new user')}}</a></div>
<h2>{{_('SMTP mail settings')}}</h2>
<table class="table table-striped">
<tr>
<th>SMTP hostname</th>
<th>SMTP port</th>
<th>SSL</th>
<th>SMTP login</th>
<th>SMTP password</th>
<th>From mail</th>
<th>{{_('SMTP hostname')}}</th>
<th>{{_('SMTP port')}}</th>
<th>{{_('SSL')}}</th>
<th>{{_('SMTP login')}}</th>
<th>{{_('SMTP password')}}</th>
<th>{{_('From mail')}}</th>
</tr>
<tr>
<td>{{email.mail_server}}</td>
@@ -50,7 +50,7 @@
</table>
<div class="btn btn-default"><a href="{{url_for('edit_mailsettings')}}">Change SMTP settings</a></div>
<div class="btn btn-default"><a href="{{url_for('edit_mailsettings')}}">{{_('Change SMTP settings')}}</a></div>
</div>
{% endblock %}
Binary file not shown.
+673
View File
@@ -0,0 +1,673 @@
# German translations for PROJECT.
# Copyright (C) 2016 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-11-09 18:52+0100\n"
"PO-Revision-Date: 2016-07-12 19:54+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
"Language-Team: de <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: cps/helper.py:76 cps/templates/detail.html:113
msgid "Send to Kindle"
msgstr "An Kindle senden"
#: cps/helper.py:77
msgid "This email has been sent via calibre web."
msgstr "Die E-Mail wurde via calibre-web versendet"
#: cps/helper.py:99 cps/helper.py:114
msgid "Could not find any formats suitable for sending by email"
msgstr "Konnte keine Formate finden welche für das versenden per E-Mail geeignet sind"
#: cps/helper.py:108
msgid "Could not convert epub to mobi"
msgstr "Konnte .epub nicht nach .mobi convertieren"
#: cps/helper.py:138
#, python-format
msgid "Failed to send mail: %s"
msgstr "E-Mail: %s konnte nicht gesendet werden"
#: cps/helper.py:158
msgid "The requested file could not be read. Maybe wrong permissions?"
msgstr "Die angeforderte Datei konnte nicht gelesen werden. Falsche Dateirechte?"
#: cps/web.py:633
msgid "Latest Books"
msgstr "Letzte Bücher"
#: cps/web.py:655
msgid "Hot Books (most downloaded)"
msgstr "Beliebte Bücher (die meisten Downloads)"
#: cps/templates/index.xml:41 cps/web.py:662
msgid "Random Books"
msgstr "Zufällige Bücher"
#: cps/web.py:673
msgid "Author list"
msgstr "Autorenliste"
#: cps/web.py:689
#, python-format
msgid "Author: %(nam)s"
msgstr "Autor: %(nam)s"
#: cps/templates/index.xml:65 cps/web.py:700
msgid "Series list"
msgstr "Liste Serien"
#: cps/web.py:708
#, python-format
msgid "Series: %(serie)s"
msgstr "Serie: %(serie)s"
#: cps/web.py:710 cps/web.py:790 cps/web.py:908 cps/web.py:1518
msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr ""
"Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht "
"zugänglich."
#: cps/web.py:736
msgid "Available languages"
msgstr "Verfügbare Sprachen"
#: cps/web.py:748
#, python-format
msgid "Language: %(name)s"
msgstr "Sprache: %(name)s"
#: cps/templates/index.xml:57 cps/web.py:759
msgid "Category list"
msgstr "Kategorieliste"
#: cps/web.py:766
#, python-format
msgid "Category: %(name)s"
msgstr "Kategorie: %(name)s"
#: cps/web.py:804
msgid "Statistics"
msgstr "Statistiken"
#: cps/web.py:892 cps/web.py:899 cps/web.py:906
msgid "Read a Book"
msgstr "Lese ein Buch"
#: cps/web.py:945 cps/web.py:1173
msgid "Please fill out all fields!"
msgstr "Bitte alle Felder ausfüllen!"
#: cps/web.py:961
msgid "An unknown error occured. Please try again later."
msgstr "Es ist ein unbekannter Fehler aufgetreten. Bitte später erneut versuchen."
#: cps/web.py:966
msgid "This username or email address is already in use."
msgstr "Der Benutzername oder die E-Mailadresse ist in bereits in Benutzung."
#: cps/web.py:969
msgid "register"
msgstr "Registieren"
#: cps/web.py:984
#, python-format
msgid "you are now logged in as: '%(nickname)s'"
msgstr "Du bist nun eingeloggt als '%(nickname)s'"
#: cps/web.py:987
msgid "Wrong Username or Password"
msgstr "Flascher Benutzername oder Passwort"
#: cps/web.py:989
msgid "login"
msgstr "Login"
#: cps/web.py:1005
msgid "Please configure the SMTP mail settings first..."
msgstr "Bitte zuerst die SMTP Mail Einstellung konfigurieren ..."
#: cps/web.py:1009
#, python-format
msgid "Book successfully send to %(kindlemail)s"
msgstr "Buch erfolgreich versandt an %(kindlemail)s"
#: cps/web.py:1012
#, python-format
msgid "There was an error sending this book: %(res)s"
msgstr "Beim Senden des Buchs trat ein Fehler auf: %(res)s"
#: cps/web.py:1014
msgid "Please configure your kindle email address first..."
msgstr "Bitte die Kindle E-Mail Adresse zuuerst konfigurieren..."
#: cps/web.py:1029
#, python-format
msgid "Book has been added to shelf: %(sname)s"
msgstr "Das Buch wurde dem Bücherregal: %(sname)s hinzugefügt"
#: cps/web.py:1048
#, python-format
msgid "Book has been removed from shelf: %(sname)s"
msgstr "Das Buch wurde aus dem Bücherregal: %(sname)s entfernt"
#: cps/web.py:1064
#, python-format
msgid "A shelf with the name '%(title)s' already exists."
msgstr "Es existiert bereits ein Bücheregal mit dem Titel '%(title)s'"
#: cps/web.py:1069
#, python-format
msgid "Shelf %(title)s created"
msgstr "Bücherregal %(title)s erzeugt"
#: cps/web.py:1071
msgid "There was an error"
msgstr "Es trat ein Fehler auf"
#: cps/web.py:1072 cps/web.py:1074
msgid "create a shelf"
msgstr "Bücherregal erzeugen"
#: cps/web.py:1090
#, python-format
msgid "successfully deleted shelf %(name)s"
msgstr "Bücherregal %(name)s erfolgreich gelöscht"
#: cps/web.py:1107
#, python-format
msgid "Shelf: '%(name)s'"
msgstr "Bücherregal: '%(name)s'"
#: cps/web.py:1144
msgid "Found an existing account for this email address."
msgstr "Es existiert ein Benutzerkonto für diese E-Mailadresse"
#: cps/web.py:1145 cps/web.py:1147
#, python-format
msgid "%(name)s's profile"
msgstr "%(name)s's Profil"
#: cps/web.py:1146
msgid "Profile updated"
msgstr "Profil aktualisiert"
#: cps/web.py:1155
msgid "User list"
msgstr "Benutzerliste"
#: cps/templates/user_list.html:32 cps/web.py:1174
msgid "Add new user"
msgstr "Neuen Benutzer hinzufügen"
#: cps/web.py:1207
#, python-format
msgid "User '%(user)s' created"
msgstr "Benutzer '%(user)s' angelegt"
#: cps/web.py:1211
msgid "Found an existing account for this email address or nickname."
msgstr ""
"Es existiert ein Benutzerkonto für diese Emailadresse oder den "
"Benutzernamen"
#: cps/web.py:1232
msgid "Mail settings updated"
msgstr "E-Mail Einstellungen aktualisiert"
#: cps/web.py:1235
msgid "Edit mail settings"
msgstr "E-Mail Einstellungen editieren"
#: cps/web.py:1257
#, python-format
msgid "User '%(nick)s' deleted"
msgstr "Benutzer '%(nick)s' gelöscht"
#: cps/web.py:1312
#, python-format
msgid "User '%(nick)s' updated"
msgstr "Benutzer '%(nick)s' aktualisiert"
#: cps/web.py:1315
msgid "An unknown error occured."
msgstr "Es ist ein unbekanter Fehler aufgetreten"
#: cps/web.py:1316
#, python-format
msgid "Edit User %(nick)s"
msgstr "Benutzer %(nick)s bearbeiten"
#: cps/web.py:1550
#, python-format
msgid "Failed to create path %s (Permission denied)."
msgstr ""
#: cps/web.py:1555
#, python-format
msgid "Failed to store file %s (Permission denied)."
msgstr ""
#: cps/web.py:1560
#, python-format
msgid "Failed to delete file %s (Permission denied)."
msgstr ""
#: cps/templates/detail.html:38
msgid "Book"
msgstr "Buch"
#: cps/templates/detail.html:38
msgid "of"
msgstr "von"
#: cps/templates/detail.html:44
msgid "language"
msgstr "Sprache"
#: cps/templates/detail.html:92
msgid "Description:"
msgstr "Beschreibung"
#: cps/templates/detail.html:103 cps/templates/user_list.html:12
msgid "Download"
msgstr "Download"
#: cps/templates/detail.html:117
msgid "Read in browser"
msgstr "Im Browser lesen"
#: cps/templates/detail.html:136
msgid "Add to shelf"
msgstr "Zu Bücherregal hinzufügen"
#: cps/templates/detail.html:176
msgid "Edit metadata"
msgstr "Metadaten bearbeiten"
#: cps/templates/edit_book.html:14 cps/templates/search_form.html:6
msgid "Book Title"
msgstr "Buchtitel"
#: cps/templates/edit_book.html:18 cps/templates/search_form.html:10
msgid "Author"
msgstr "Autor"
#: cps/templates/edit_book.html:22
msgid "Description"
msgstr "Beschreibung"
#: cps/templates/edit_book.html:26 cps/templates/search_form.html:13
msgid "Tags"
msgstr "Tags"
#: cps/templates/edit_book.html:31 cps/templates/layout.html:131
msgid "Series"
msgstr "Serien"
#: cps/templates/edit_book.html:35
msgid "Series id"
msgstr "Serien ID"
#: cps/templates/edit_book.html:39
msgid "Rating"
msgstr "Bewertung"
#: cps/templates/edit_book.html:43
msgid "Cover URL (jpg)"
msgstr "Cover URL (jpg)"
#: cps/templates/edit_book.html:48 cps/templates/user_edit.html:27
msgid "Language"
msgstr "Sprache"
#: cps/templates/edit_book.html:59
msgid "Yes"
msgstr "Ja"
#: cps/templates/edit_book.html:60
msgid "No"
msgstr "Nein"
#: cps/templates/edit_book.html:102
msgid "view book after edit"
msgstr "Buch nach Bearbeitung ansehen"
#: cps/templates/edit_book.html:105 cps/templates/email_edit.html:30
#: cps/templates/login.html:19 cps/templates/search_form.html:33
#: cps/templates/shelf_edit.html:15 cps/templates/user_edit.html:93
msgid "Submit"
msgstr "Abschicken"
#: cps/templates/email_edit.html:7 cps/templates/user_list.html:36
msgid "SMTP hostname"
msgstr "SMTP Hostname"
#: cps/templates/email_edit.html:11
msgid "SMTP port (usually 25 for plain SMTP and 587 for SSL)"
msgstr "SMTP Port (normalerweise 25 für unverschlüsseltes SMTP und 587 für SSL)"
#: cps/templates/email_edit.html:15
msgid "Server uses SSL (StartTLS)"
msgstr "Server benutzt SSL (StartTLS)"
#: cps/templates/email_edit.html:19 cps/templates/user_list.html:39
msgid "SMTP login"
msgstr "SMTP Login"
#: cps/templates/email_edit.html:23 cps/templates/user_list.html:40
msgid "SMTP password"
msgstr "SMTP Passwort"
#: cps/templates/email_edit.html:27
msgid "From e-mail"
msgstr "Absenderadresse"
#: cps/templates/feed.xml:14
msgid "Next"
msgstr "Nächste"
#: cps/templates/index.html:5
msgid "Discover (Random Books)"
msgstr "Entdecke (Zufälliges Buch)"
#: cps/templates/index.xml:8
msgid "Start"
msgstr "Start"
#: cps/templates/index.xml:12 cps/templates/layout.html:59
msgid "Search"
msgstr "Suche"
#: cps/templates/index.xml:23 cps/templates/layout.html:122
msgid "Hot Books"
msgstr "Beliebte Bücher"
#: cps/templates/index.xml:28
msgid "Popular publications from this catalog based on Rating."
msgstr "Beliebte Veröffentlichungen dieses Katalogs basierend auf Bewertungen"
#: cps/templates/index.xml:32 cps/templates/layout.html:120
msgid "New Books"
msgstr "Neue Bücher"
#: cps/templates/index.xml:37
msgid "The latest Books"
msgstr "Die neuesten Bücher"
#: cps/templates/index.xml:46
msgid "Show Random Books"
msgstr "Zeige zufällige Bücher"
#: cps/templates/index.xml:49 cps/templates/layout.html:133
msgid "Authors"
msgstr "Autoren"
#: cps/templates/index.xml:54
msgid "Books ordered by Author"
msgstr "Bücher nach Autoren sortiert"
#: cps/templates/index.xml:62
msgid "Books ordered by category"
msgstr "Bücher nach Kategorien sortiert"
#: cps/templates/index.xml:70
msgid "Books ordered by series"
msgstr ""
#: cps/templates/layout.html:46
msgid "Toggle navigation"
msgstr "Nagivation umschalten"
#: cps/templates/layout.html:61
msgid "Go!"
msgstr "Los!"
#: cps/templates/layout.html:64
msgid "Advanced Search"
msgstr "Erweiterte Suche"
#: cps/templates/layout.html:74 cps/templates/user_list.html:13
msgid "Upload"
msgstr "Hochladen"
#: cps/templates/layout.html:81 cps/templates/user_list.html:11
msgid "Admin"
msgstr "Admin"
#: cps/templates/layout.html:85
msgid "Logout"
msgstr "Logout"
#: cps/templates/layout.html:89 cps/templates/login.html:4
msgid "Login"
msgstr "Login"
#: cps/templates/layout.html:90 cps/templates/register.html:18
msgid "Register"
msgstr "Registrieren"
#: cps/templates/layout.html:119
msgid "Browse"
msgstr "Browsen"
#: cps/templates/layout.html:125
msgid "Discover"
msgstr "Entdecke"
#: cps/templates/layout.html:128
msgid "Categories"
msgstr "Kategorien"
#: cps/templates/layout.html:135
msgid "Languages"
msgstr "Sprachen"
#: cps/templates/layout.html:138
msgid "Public Shelves"
msgstr "Öffentiche Bücherregale"
#: cps/templates/layout.html:142
msgid "Your Shelves"
msgstr "Deine Bücherregale"
#: cps/templates/layout.html:147
msgid "Create a Shelf"
msgstr "Bücherregal erzeugen"
#: cps/templates/layout.html:150
msgid "About"
msgstr "Über"
#: cps/templates/login.html:7 cps/templates/login.html:8
#: cps/templates/register.html:7 cps/templates/user_edit.html:8
msgid "Username"
msgstr "Benutzername"
#: cps/templates/login.html:11 cps/templates/login.html:12
#: cps/templates/register.html:11 cps/templates/user_edit.html:18
msgid "Password"
msgstr "Passwort"
#: cps/templates/login.html:16
msgid "Remember me"
msgstr "Merken"
#: cps/templates/register.html:4
msgid "Register a new account"
msgstr "Neues Benutzerkonto erzeugen"
#: cps/templates/register.html:8
msgid "Choose a username"
msgstr "Wähle einen Benutzernamen"
#: cps/templates/register.html:12
msgid "Choose a password"
msgstr "Wähle ein Passwort"
#: cps/templates/register.html:15 cps/templates/user_edit.html:13
msgid "Email address"
msgstr "E-Mail Adresse"
#: cps/templates/register.html:16
msgid "Your email address"
msgstr "Deine E-Mail Adresse"
#: cps/templates/search.html:6
msgid "No Results for:"
msgstr "Keine Ergebnisse für:"
#: cps/templates/search.html:7
msgid "Please try a diffrent Search"
msgstr "Versuche eine andere Suche"
#: cps/templates/search.html:9
msgid "Results for:"
msgstr "Ergebnisse für:"
#: cps/templates/search_form.html:23
msgid "Exclude Tags"
msgstr "Tags ausschließen"
#: cps/templates/shelf.html:6
msgid "Delete this Shelf"
msgstr "Lösche dieses Bücherregal"
#: cps/templates/shelf_edit.html:7
msgid "Title"
msgstr "Titel"
#: cps/templates/shelf_edit.html:12
msgid "should the shelf be public?"
msgstr "Soll das Bücherregal öffentlich sein?"
#: cps/templates/stats.html:4
msgid "Books in this Library"
msgstr "Bücher in dieser Bibliothek"
#: cps/templates/stats.html:5
msgid "Authors in this Library"
msgstr "Autoren in dieser Bibliothek"
#: cps/templates/stats.html:6
msgid "Version"
msgstr "Version"
#: cps/templates/user_edit.html:23
msgid "Kindle E-Mail"
msgstr "Kindle E-Mail"
#: cps/templates/user_edit.html:35
msgid "Show books with language"
msgstr "Zeige nur Bücher mit dieser Sprache"
#: cps/templates/user_edit.html:37
msgid "Show all"
msgstr "Zeige alle"
#: cps/templates/user_edit.html:46
msgid "Show random books"
msgstr "Zeige Zufällige Bücher"
#: cps/templates/user_edit.html:50
msgid "Show hot books"
msgstr "Zeige Auswahl Beliebte Bücher"
#: cps/templates/user_edit.html:54
msgid "Show language selection"
msgstr "Zeige Sprachauswahl"
#: cps/templates/user_edit.html:58
msgid "Show series selection"
msgstr "Zeige Auswahl Serien"
#: cps/templates/user_edit.html:62
msgid "Show category selection"
msgstr "Zeige Kategorie Auswahl"
#: cps/templates/user_edit.html:67
msgid "Admin user"
msgstr "Admin Benutzer"
#: cps/templates/user_edit.html:71
msgid "Allow Downloads"
msgstr "Downloads erlauben"
#: cps/templates/user_edit.html:75
msgid "Allow Uploads"
msgstr "Uploads erlauben"
#: cps/templates/user_edit.html:79
msgid "Allow Edit"
msgstr "Bearbeiten erlauben"
#: cps/templates/user_edit.html:83
msgid "Allow Changing Password"
msgstr "Passwort ändern erlauben"
#: cps/templates/user_edit.html:89
msgid "Delete this user"
msgstr "Benutzer löschen"
#: cps/templates/user_edit.html:97
msgid "Recent Downloads"
msgstr "Letzte Downloads"
#: cps/templates/user_list.html:7
msgid "Nickname"
msgstr "Benutzername"
#: cps/templates/user_list.html:8
msgid "Email"
msgstr "E-Mail"
#: cps/templates/user_list.html:9
msgid "Kindle"
msgstr "Kindle"
#: cps/templates/user_list.html:10
msgid "DLS"
msgstr "DLS"
#: cps/templates/user_list.html:14
msgid "Edit"
msgstr "Editieren"
#: cps/templates/user_list.html:15
msgid "Passwd"
msgstr "Passwort"
#: cps/templates/user_list.html:33
msgid "SMTP mail settings"
msgstr "SMTP Mail Einstellungen"
#: cps/templates/user_list.html:37
msgid "SMTP port"
msgstr "SMTP Port"
#: cps/templates/user_list.html:38
msgid "SSL"
msgstr "SSL"
#: cps/templates/user_list.html:41
msgid "From mail"
msgstr "Absenderadresse"
#: cps/templates/user_list.html:53
msgid "Change SMTP settings"
msgstr "SMTP Einstellungen ändern"
msgid "Latin"
msgstr "Latein"
+51
View File
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
from sqlalchemy import *
from sqlalchemy import exc
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import *
import os
@@ -32,6 +33,13 @@ class User(Base):
shelf = relationship('Shelf', backref = 'user', lazy = 'dynamic')
whislist = relationship('Whislist', backref = 'user', lazy = 'dynamic')
downloads = relationship('Downloads', backref= 'user', lazy = 'dynamic')
locale = Column(String(2), default="en")
random_books = Column(Integer, default=1)
language_books = Column(Integer, default=1)
series_books = Column(Integer, default=1)
category_books = Column(Integer, default=1)
hot_books = Column(Integer, default=1)
default_language = Column(String(3), default="all")
def is_authenticated(self):
return True
@@ -70,6 +78,25 @@ class User(Base):
def get_id(self):
return unicode(self.id)
def filter_language(self):
return self.default_language
def show_random_books(self):
return self.random_books
def show_language(self):
return self.language_books
def show_hot_books(self):
return self.hot_books
def show_series(self):
return self.series_books
def show_category(self):
return self.category_books
def __repr__(self):
return '<User %r>' % (self.nickname)
@@ -147,6 +174,28 @@ class Settings(Base):
#return '<Smtp %r>' % (self.mail_server)
pass
def migrate_Database():
try:
session.query(exists().where(User.random_books)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn=engine.connect()
conn.execute("ALTER TABLE user ADD column random_books INTEGER DEFAULT 1")
conn.execute("ALTER TABLE user ADD column locale String(2) DEFAULT 'en'")
conn.execute("ALTER TABLE user ADD column default_language String(3) DEFAULT 'all'")
session.commit()
try:
session.query(exists().where(User.language_books)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE user ADD column language_books INTEGER DEFAULT 1")
conn.execute("ALTER TABLE user ADD column series_books INTEGER DEFAULT 1")
conn.execute("ALTER TABLE user ADD column category_books INTEGER DEFAULT 1")
conn.execute("ALTER TABLE user ADD column hot_books INTEGER DEFAULT 1")
session.commit()
def create_default_config():
settings = Settings()
settings.mail_server = "mail.example.com"
@@ -200,4 +249,6 @@ if not os.path.exists(dbpath):
create_admin_user()
except Exception:
pass
else:
migrate_Database()
+727 -359
View File
File diff suppressed because it is too large Load Diff