Commit 3ec000ad authored by bodybybuddha's avatar bodybybuddha

Merge branch 'master' of https://github.com/janeczku/calibre-web into ebookconvert-any

parents e160efbd a7a91ae5
......@@ -26,6 +26,7 @@ except ImportError:
pass
import web
import server
import random
try:
import unidecode
......@@ -76,10 +77,27 @@ def make_mobi(book_id, calibrepath, user_id, kindle_mail):
def send_test_mail(kindle_mail, user_name):
global_WorkerThread.add_email(_(u'Calibre-web test email'),None, None, ub.get_mail_settings(),
kindle_mail, user_name, _(u"Test E-Mail"))
global_WorkerThread.add_email(_(u'Calibre-Web test e-mail'),None, None, ub.get_mail_settings(),
kindle_mail, user_name, _(u"Test e-mail"))
return
# Send registration email or password reset email, depending on parameter resend (False means welcome email)
def send_registration_mail(e_mail, user_name, default_password, resend=False):
text = "Hello %s!\r\n" % user_name
if not resend:
text += "Your new account at Calibre-Web has been created. Thanks for joining us!\r\n"
text += "Please log in to your account using the following informations:\r\n"
text += "User name: %s\n" % user_name
text += "Password: %s\r\n" % default_password
text += "Don't forget to change your password after first login.\r\n"
text += "Sincerely\r\n\r\n"
text += "Your Calibre-Web team"
global_WorkerThread.add_email(_(u'Get Started with Calibre-Web'),None, None, ub.get_mail_settings(),
e_mail, user_name, _(u"Registration e-mail for user: %s" % user_name),text)
return
# Files are processed in the following order/priority:
# 1: If Mobi file is exisiting, it's directly send to kindle email,
# 2: If Epub file is exisiting, it's converted and send to kindle email
......@@ -99,7 +117,7 @@ def send_mail(book_id, kindle_mail, calibrepath, user_id):
formats["pdf"] = entry.name + ".pdf"
if len(formats) == 0:
return _(u"Could not find any formats suitable for sending by email")
return _(u"Could not find any formats suitable for sending by e-mail")
if 'mobi' in formats:
result = formats['mobi']
......@@ -109,7 +127,7 @@ def send_mail(book_id, kindle_mail, calibrepath, user_id):
elif 'pdf' in formats:
result = formats['pdf'] # worker.get_attachment()
else:
return _(u"Could not find any formats suitable for sending by email")
return _(u"Could not find any formats suitable for sending by e-mail")
if result:
global_WorkerThread.add_email(_(u"Send to Kindle"), book.path, result, ub.get_mail_settings(),
kindle_mail, user_id, _(u"E-Mail: %s" % book.title))
......@@ -271,6 +289,11 @@ def delete_book_gdrive(book, book_format):
error =_(u'Book path %s not found on Google Drive' % book.path) # file not found
return error
def generate_random_password():
s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%&*()?"
passlen = 8
return "".join(random.sample(s,passlen ))
################################## External interface
def update_dir_stucture(book_id, calibrepath):
......
......@@ -69,7 +69,7 @@ class server:
self.wsgiserver.close(True)
if self.restart == True:
web.app.logger.info("Performing restart of Calibre-web")
web.app.logger.info("Performing restart of Calibre-Web")
web.helper.global_WorkerThread.stop()
if os.name == 'nt':
arguments = ["\"" + sys.executable + "\""]
......@@ -79,7 +79,7 @@ class server:
else:
os.execl(sys.executable, sys.executable, *sys.argv)
else:
web.app.logger.info("Performing shutdown of Calibre-web")
web.app.logger.info("Performing shutdown of Calibre-Web")
web.helper.global_WorkerThread.stop()
sys.exit(0)
......
This diff is collapsed.
......@@ -94,7 +94,13 @@ input.pill:not(:checked) + label .glyphicon {
.upload-format-input-text {display: initial;}
#btn-upload-format {display: none;}
.upload-format-input-text {display: initial;}
#btn-upload-format {display: none;}
.upload-cover-input-text {display: initial;}
#btn-upload-cover {display: none;}
.panel-title > a { text-decoration: none;}
.editable-buttons { display:inline-block; margin-left: 7px ;}
.editable-input { display:inline-block;}
.editable-cancel { margin-bottom: 0px !important; margin-left: 7px !important;}
.editable-submit { margin-bottom: 0px !important;}
......@@ -246,3 +246,11 @@ $("#btn-upload-format").on("change", function () {
} // Remove c:\fake at beginning from localhost chrome
$("#upload-format").html(filename);
});
$("#btn-upload-cover").on("change", function () {
var filename = $(this).val();
if (filename.substring(3, 11) === "fakepath") {
filename = filename.substring(12);
} // Remove c:\fake at beginning from localhost chrome
$("#upload-cover").html(filename);
});
This diff is collapsed.
/*
* bootstrap-table - v1.12.1 - 2018-03-12
* https://github.com/wenzhixin/bootstrap-table
* Copyright (c) 2018 zhixin wen
* Licensed MIT License
*/
!function(a){"use strict";a.extend(a.fn.bootstrapTable.defaults,{editable:!0,onEditableInit:function(){return!1},onEditableSave:function(){return!1},onEditableShown:function(){return!1},onEditableHidden:function(){return!1}}),a.extend(a.fn.bootstrapTable.Constructor.EVENTS,{"editable-init.bs.table":"onEditableInit","editable-save.bs.table":"onEditableSave","editable-shown.bs.table":"onEditableShown","editable-hidden.bs.table":"onEditableHidden"});var b=a.fn.bootstrapTable.Constructor,c=b.prototype.initTable,d=b.prototype.initBody;b.prototype.initTable=function(){var b=this;c.apply(this,Array.prototype.slice.apply(arguments)),this.options.editable&&a.each(this.columns,function(c,d){if(d.editable){var e={},f=[],g="editable-",h=function(a,b){var c=a.replace(/([A-Z])/g,function(a){return"-"+a.toLowerCase()});if(c.slice(0,g.length)==g){var d=c.replace(g,"data-");e[d]=b}};a.each(b.options,h),d.formatter=d.formatter||function(a){return a},d._formatter=d._formatter?d._formatter:d.formatter,d.formatter=function(c,g,i){var j=d._formatter?d._formatter(c,g,i):c;a.each(d,h),a.each(e,function(a,b){f.push(" "+a+'="'+b+'"')});var k=!1;return d.editable.hasOwnProperty("noeditFormatter")&&(k=d.editable.noeditFormatter(c,g,i)),k===!1?['<a href="javascript:void(0)"',' data-name="'+d.field+'"',' data-pk="'+g[b.options.idField]+'"',' data-value="'+j+'"',f.join(""),"></a>"].join(""):k}}})},b.prototype.initBody=function(){var b=this;d.apply(this,Array.prototype.slice.apply(arguments)),this.options.editable&&(a.each(this.columns,function(c,d){d.editable&&(b.$body.find('a[data-name="'+d.field+'"]').editable(d.editable).off("save").on("save",function(c,e){var f=b.getData(),g=a(this).parents("tr[data-index]").data("index"),h=f[g],i=h[d.field];a(this).data("value",e.submitValue),h[d.field]=e.submitValue,b.trigger("editable-save",d.field,h,i,a(this)),b.resetFooter()}),b.$body.find('a[data-name="'+d.field+'"]').editable(d.editable).off("shown").on("shown",function(c,e){var f=b.getData(),g=a(this).parents("tr[data-index]").data("index"),h=f[g];b.trigger("editable-shown",d.field,h,a(this),e)}),b.$body.find('a[data-name="'+d.field+'"]').editable(d.editable).off("hidden").on("hidden",function(c,e){var f=b.getData(),g=a(this).parents("tr[data-index]").data("index"),h=f[g];b.trigger("editable-hidden",d.field,h,a(this),e)}))}),this.trigger("editable-init"))}}(jQuery);
\ No newline at end of file
$(function() {
$("#domain_submit").click(function(event){
event.preventDefault();
$("#domain_add").ajaxForm();
$(this).closest("form").submit();
$.ajax({
method:"get",
url: window.location.pathname + "/../../ajax/domainlist",
async: true,
timeout: 900,
success:function(data){
$('#domain-table').bootstrapTable("load", data);
}
});
});
$('#domain-table').bootstrapTable({
formatNoMatches: function () {
return '';
},
striped: false
});
$("#btndeletedomain").click(function() {
//get data-id attribute of the clicked element
var domainId = $(this).data('domainId');
$.ajax({
method:"post",
url: window.location.pathname + "/../../ajax/deletedomain",
data: {"domainid":domainId}
});
$('#DeleteDomain').modal('hide');
$.ajax({
method:"get",
url: window.location.pathname + "/../../ajax/domainlist",
async: true,
timeout: 900,
success:function(data){
$('#domain-table').bootstrapTable("load", data);
}
});
});
//triggered when modal is about to be shown
$('#DeleteDomain').on('show.bs.modal', function(e) {
//get data-id attribute of the clicked element and store in button
var domainId = $(e.relatedTarget).data('domain-id');
$(e.currentTarget).find("#btndeletedomain").data('domainId',domainId);
});
});
function TableActions (value, row, index) {
return [
'<a class="danger remove" data-toggle="modal" data-target="#DeleteDomain" data-domain-id="'+row.id+'" title="Remove">',
'<i class="glyphicon glyphicon-trash"></i>',
'</a>'
].join('');
}
......@@ -2,7 +2,6 @@
{% block body %}
<div class="discover">
<h2>{{_('User list')}}</h2>
<div id="divLoading"></div>
<table class="table table-striped" id="table_user">
<tr>
<th>{{_('Nickname')}}</th>
......@@ -13,7 +12,6 @@
<th>{{_('Download')}}</th>
<th>{{_('Upload')}}</th>
<th>{{_('Edit')}}</th>
<th>{{_('Passwd')}}</th>
</tr>
{% for user in content %}
{% if not user.role_anonymous() or config.config_anonbrowse %}
......@@ -26,20 +24,18 @@
<td>{% if user.role_download() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
<td>{% if user.role_upload() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
<td>{% if user.role_edit() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
<td>{% if user.role_passwd() %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
</tr>
{% endif %}
{% endfor %}
</table>
<div class="btn btn-default" id="admin_new_user"><a href="{{url_for('new_user')}}">{{_('Add new user')}}</a></div>
<h2>{{_('SMTP mail settings')}}</h2>
<h2>{{_('SMTP e-mail server settings')}}</h2>
<table class="table table-striped" id="table_email">
<tr>
<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>
......@@ -47,44 +43,56 @@
<td>{{email.mail_port}}</td>
<td>{% if email.mail_use_ssl %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
<td>{{email.mail_login}}</td>
<td>********</td>
<td>{{email.mail_from}}</td>
</table>
<div class="btn btn-default" id="admin_edit_email"><a href="{{url_for('edit_mailsettings')}}">{{_('Change SMTP settings')}}</a></div>
<div id="container">
<h2>{{_('Configuration')}}</h2>
<table class="table table-striped" id="table_configuration">
<tr>
<th>{{_('Calibre DB dir')}}</th>
<th>{{_('Log Level')}}</th>
<th>{{_('Port')}}</th>
<th>{{_('Books per page')}}</th>
<th>{{_('Uploading')}}</th>
<th>{{_('Public registration')}}</th>
<th>{{_('Anonymous browsing')}}</th>
<th>{{_('Remote Login')}}</th>
</tr>
<tr>
<td>{{config.config_calibre_dir}}</td>
<td>{{config.get_Log_Level()}}</td>
<td>{{config.config_port}}</td>
<td>{{config.config_books_per_page}}</td>
<td>{% if config.config_uploading %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
<td>{% if config.config_public_reg %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
<td>{% if config.config_anonbrowse %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
<td>{% if config.config_remote_login %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
</table>
<div class="col-xs-12 col-sm-6">
<div class="Row">
<div class="col-xs-6 col-sm-6">{{_('Calibre DB dir')}}</div>
<div class="col-xs-6 col-sm-6">{{config.config_calibre_dir}}</div>
</div>
<div class="Row">
<div class="col-xs-6 col-sm-6">{{_('Log Level')}}</div>
<div class="col-xs-6 col-sm-6">{{config.get_Log_Level()}}</div>
</div>
<div class="Row">
<div class="col-xs-6 col-sm-6">{{_('Port')}}</div>
<div class="col-xs-6 col-sm-6">{{config.config_port}}</div>
</div>
</div>
<div class="col-xs-12 col-sm-6">
<div class="Row">
<div class="col-xs-6 col-sm-7">{{_('Books per page')}}</div>
<div class="col-xs-6 col-sm-5">{{config.config_books_per_page}}</div>
</div>
<div class="Row">
<div class="col-xs-6 col-sm-7">{{_('Uploading')}}</div>
<div class="col-xs-6 col-sm-5">{% if config.config_uploading %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</div>
</div>
<div class="Row">
<div class="col-xs-6 col-sm-7">{{_('Public registration')}}</div>
<div class="col-xs-6 col-sm-5">{% if config.config_public_reg %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</div>
</div>
<div class="Row">
<div class="col-xs-6 col-sm-7">{{_('Remote Login')}}</div>
<div class="col-xs-6 col-sm-5">{% if config.config_remote_login %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</div>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-12">
<p></p>
<div class="btn btn-default"><a href="{{url_for('configuration')}}">{{_('Basic Configuration')}}</a></div>
<div class="btn btn-default"><a href="{{url_for('view_configuration')}}">{{_('UI Configuration')}}</a></div>
</div>
<h2>{{_('Administration')}}</h2>
<div>{{_('Current commit timestamp')}}: <span>{{commit}} </span></div>
<div class="hidden" id="update_info">{{_('Newest commit timestamp')}}: <span></span></div>
<p></p>
<div class="btn btn-default" id="restart_database">{{_('Reconnect to Calibre DB')}}</div>
<div class="btn btn-default" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-web')}}</div>
<div class="btn btn-default" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-web')}}</div>
<div class="btn btn-default" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-Web')}}</div>
<div class="btn btn-default" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-Web')}}</div>
<div class="btn btn-default" id="check_for_update">{{_('Check for update')}}</div>
<div class="btn btn-default hidden" id="perform_update" data-toggle="modal" data-target="#UpdateprogressDialog">{{_('Perform Update')}}</div>
</div>
......@@ -95,7 +103,7 @@
<div class="modal-content">
<div class="modal-header bg-info"></div>
<div class="modal-body text-center">
<p>{{_('Do you really want to restart Calibre-web?')}}</p>
<p>{{_('Do you really want to restart Calibre-Web?')}}</p>
<div id="spinner" class="spinner" style="display:none;">
<img id="img-spinner" src="/static/css/images/loading-icon.gif"/>
</div>
......@@ -113,7 +121,7 @@
<div class="modal-header bg-info">
</div>
<div class="modal-body text-center">
<p>{{_('Do you really want to stop Calibre-web?')}}</p>
<p>{{_('Do you really want to stop Calibre-Web?')}}</p>
<button type="button" class="btn btn-default" id="shutdown" data-dismiss="modal">{{_('Ok')}}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Back')}}</button>
</div>
......
......@@ -60,7 +60,11 @@
<label for="cover_url">{{_('Cover URL (jpg, cover is downloaded and stored in database, field is afterwards empty again)')}}</label>
<input type="text" class="form-control" name="cover_url" id="cover_url" value="">
</div>
<div class="form-group" aria-label="Upload cover from local drive">
<label class="btn btn-default btn-file" for="btn-upload-cover">{{ _('Upload Cover from local drive') }}</label>
<div class="upload-cover-input-text" id="upload-cover"></div>
<input id="btn-upload-cover" name="btn-upload-cover" type="file">
</div>
<div class="form-group">
<label for="pubdate">{{_('Publishing date')}}</label>
<div style="position: relative">
......@@ -70,7 +74,7 @@
</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 %}">
<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 %}
......@@ -170,7 +174,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-center">
<span>{{_('Are really you sure?')}}</span>
<span>{{_('Are you really sure?')}}</span>
</div>
<div class="modal-body text-center">
<span>{{_('Book will be deleted from Calibre database')}}</span>
......@@ -255,7 +259,7 @@
var i18nMsg = {
'loading': {{_('Loading...')|safe|tojson}},
'search_error': {{_('Search error!')|safe|tojson}},
'no_result': {{_('No Result! Please try anonther keyword.')|safe|tojson}},
'no_result': {{_('No Result(s) found! Please try aonther keyword.')|safe|tojson}},
'author': {{_('Author')|safe|tojson}},
'publisher': {{_('Publisher')|safe|tojson}},
'description': {{_('Description')|safe|tojson}},
......
{% extends "layout.html" %}
{% block header %}
<link href="{{ url_for('static', filename='css/libs/bootstrap-table.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/libs/bootstrap-editable.css') }}" rel="stylesheet">
{% endblock %}
{% block body %}
<div class="discover">
<h1>{{title}}</h1>
......@@ -35,6 +39,52 @@
<button type="submit" name="test" value="test" class="btn btn-default">{{_('Save settings and send Test E-Mail')}}</button>
<a href="{{ url_for('admin') }}" id="back" class="btn btn-default">{{_('Back')}}</a>
</form>
{% if g.allow_registration %}
<h2>{{_('Allowed domains for registering')}}</h2>
<table class="table table-no-bordered" id="domain-table" data-url="{{url_for('list_domain')}}" data-id-field="id" data-show-header="false" data-editable-mode="inline">
<thead>
<tr>
<th data-field="domain" id="domain" data-editable-type="text" data-editable-url="{{ url_for('edit_domain')}}" data-editable="true" data-editable-title="{{_('Enter domainname')}}"></th>
<th data-field="id" id="id" data-visible="false"></th>
<th data-align="right" data-formatter="TableActions"></th>
</tr>
</thead>
</table>
<form id="domain_add" action="{{ url_for('add_domain')}}" method="POST">
<div class="form-group required">
<label for="domainname">{{_('Add Domain')}}</label>
<input type="text" class="form-control" name="domainname" id="domainname" >
</div>
<button id="domain_submit" class="btn btn-default">{{_('Add')}}</button>
</form>
{% endif %}
</div>
{% endblock %}
{% block modal %}
{% if g.allow_registration %}
<div id="DeleteDomain" class="modal fade" role="dialog">
<div class="modal-dialog modal-sm">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header bg-danger">
</div>
<div class="modal-body text-center">
<p>{{_('Do you really want to delete this domain rule?')}}</p>
<button type="button" class="btn btn-danger" id="btndeletedomain" >{{_('Delete')}}</button>
<!--a href="{{ url_for('delete_domain')}}" class="btn btn-danger">{{_('Delete')}}</a-->
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Back')}}</button>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block js %}
{% if g.allow_registration %}
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-table.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-table-editable.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-editable.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/table.js') }}"></script>
{% endif %}
{% endblock %}
......@@ -124,7 +124,7 @@
{% if g.user.show_sorted() %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-sort-by-attributes"></span> {{_('Sorted Books')}}
<span class="glyphicon glyphicon-sort-by-attributes"></span>{{_('Sorted Books')}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
......@@ -136,44 +136,44 @@
</li>
{%endif%}
{% if g.user.show_hot_books() %}
<li id="nav_hot" {% if page == 'hot' %}class="active"{% endif %}><a href="{{url_for('hot_books')}}"><span class="glyphicon glyphicon-fire"></span> {{_('Hot Books')}}</a></li>
<li id="nav_hot" {% if page == 'hot' %}class="active"{% endif %}><a href="{{url_for('hot_books')}}"><span class="glyphicon glyphicon-fire"></span>{{_('Hot Books')}}</a></li>
{%endif%}
{% if g.user.show_best_rated_books() %}
<li {% if page == 'rated' %}class="active"{% endif %}><a href="{{url_for('best_rated_books')}}"><span class="glyphicon glyphicon-star"></span> {{_('Best rated Books')}}</a></li>
<li {% if page == 'rated' %}class="active"{% endif %}><a href="{{url_for('best_rated_books')}}"><span class="glyphicon glyphicon-star"></span>{{_('Best rated Books')}}</a></li>
{%endif%}
{% if g.user.show_read_and_unread() %}
{% if not g.user.is_anonymous %}
<li {% if page == 'read' %}class="active"{% endif %}><a href="{{url_for('read_books')}}"><span class="glyphicon glyphicon-eye-open"></span> {{_('Read Books')}}</a></li>
<li {% if page == 'read' %}class="active"{% endif %}><a href="{{url_for('read_books')}}"><span class="glyphicon glyphicon-eye-open"></span>{{_('Read Books')}}</a></li>
{%endif%}
<li {% if page == 'read' %}class="active"{% endif %}><a href="{{url_for('unread_books')}}"><span class="glyphicon glyphicon-eye-close"></span> {{_('Unread Books')}}</a></li>
<li {% if page == 'read' %}class="active"{% endif %}><a href="{{url_for('unread_books')}}"><span class="glyphicon glyphicon-eye-close"></span>{{_('Unread Books')}}</a></li>
{%endif%}
{% if g.user.show_random_books() %}
<li id="nav_rand" {% if page == 'discover' %}class="active"{% endif %}><a href="{{url_for('discover')}}"><span class="glyphicon glyphicon-random"></span> {{_('Discover')}}</a></li>
<li id="nav_rand" {% if page == 'discover' %}class="active"{% endif %}><a href="{{url_for('discover')}}"><span class="glyphicon glyphicon-random"></span>{{_('Discover')}}</a></li>
{%endif%}
{% if g.user.show_category() %}
<li id="nav_cat" {% if page == 'discover' %}class="category"{% endif %}><a href="{{url_for('category_list')}}"><span class="glyphicon glyphicon-inbox"></span> {{_('Categories')}}</a></li>
<li id="nav_cat" {% if page == 'discover' %}class="category"{% endif %}><a href="{{url_for('category_list')}}"><span class="glyphicon glyphicon-inbox"></span>{{_('Categories')}}</a></li>
{%endif%}
{% if g.user.show_series() %}
<li id="nav_serie" {% if page == 'series' %}class="active"{% endif %}><a href="{{url_for('series_list')}}"><span class="glyphicon glyphicon-bookmark"></span> {{_('Series')}}</a></li>
<li id="nav_serie" {% if page == 'series' %}class="active"{% endif %}><a href="{{url_for('series_list')}}"><span class="glyphicon glyphicon-bookmark"></span>{{_('Series')}}</a></li>
{%endif%}
{% if g.user.show_author() %}
<li id="nav_author" {% if page == 'author' %}class="active"{% endif %}><a href="{{url_for('author_list')}}"><span class="glyphicon glyphicon-user"></span> {{_('Authors')}}</a></li>
<li id="nav_author" {% if page == 'author' %}class="active"{% endif %}><a href="{{url_for('author_list')}}"><span class="glyphicon glyphicon-user"></span>{{_('Authors')}}</a></li>
{%endif%}
{% if g.user.filter_language() == 'all' and g.user.show_language() %}
<li id="nav_lang" {% if page == 'language' %}class="active"{% endif %}><a href="{{url_for('language_overview')}}"><span class="glyphicon glyphicon-flag"></span> {{_('Languages')}} </a></li>
<li id="nav_lang" {% if page == 'language' %}class="active"{% endif %}><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>
<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>
{% 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>
<li><a href="{{url_for('show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list"></span>{{shelf.name}}</a></li>
{% endfor %}
{% if not g.user.is_anonymous %}
<li id="nav_createshelf" class="create-shelf"><a href="{{url_for('create_shelf')}}">{{_('Create a Shelf')}}</a></li>
<li id="nav_about" {% if page == 'stat' %}class="active"{% endif %}><a href="{{url_for('stats')}}"><span class="glyphicon glyphicon-info-sign"></span> {{_('About')}}</a></li>
<li id="nav_about" {% if page == 'stat' %}class="active"{% endif %}><a href="{{url_for('stats')}}"><span class="glyphicon glyphicon-info-sign"></span>{{_('About')}}</a></li>
{% endif %}
{% endif %}
......
......@@ -2,7 +2,7 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<LongName>{{instance}}</LongName>
<ShortName>{{instance}}</ShortName>
<Description>{{_('Calibre Web ebook catalog')}}</Description>
<Description>{{_('Calibre-Web ebook catalog')}}</Description>
<Developer>Janeczku</Developer>
<Contact>https://github.com/janeczku/calibre-web</Contact>
<Url type="text/html"
......
......@@ -7,10 +7,10 @@
<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">
<!--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>
</div>
</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>
......
......@@ -2,7 +2,7 @@
{% block body %}
<h3>{{_('About')}}</h3>
<p>{{instance}} powered by
<a href="https://github.com/janeczku/calibre-web" title="Calibre-Web">Calibre Web</a>.
<a href="https://github.com/janeczku/calibre-web" title="Calibre-Web">Calibre-Web</a>.
</p>
<h3>{{_('Calibre library statistics')}}</h3>
<table id="stats" class="table">
......
......@@ -28,6 +28,7 @@
{% block js %}
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-table.min.js') }}"></script>
<script>
// ToDo: Move to js file
$('#table').bootstrapTable({
formatNoMatches: function () {
return '';
......
......@@ -14,10 +14,16 @@
<input type="email" class="form-control" name="email" id="email" value="{{ content.email if content.email != None }}" autocomplete="off">
</div>
{% if ( g.user and g.user.role_passwd() or g.user.role_admin() ) and not content.role_anonymous() %}
<div class="form-group">
<label for="password">{{_('Password')}}</label>
<input type="password" class="form-control" name="password" id="password" value="" autocomplete="off">
</div>
{% if g.user and g.user.role_admin() and g.allow_registration and not new_user and not profile %}
<div class="btn btn-default" id="resend_password"><a href="{{url_for('reset_password', user_id = content.id) }}">{{_('Reset user Password')}}</a></div>
{% else %}
<div class="form-group">
<label for="password">{{_('Password')}}</label>
<input type="password" class="form-control" name="password" id="password" value="" autocomplete="off">
</div>
{% endif %}
{% endif %}
<div class="form-group">
<label for="kindle_mail">{{_('Kindle E-Mail')}}</label>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -149,7 +149,7 @@ class UserBase:
return '<User %r>' % self.nickname
# Baseclass for Users in Calibre-web, settings which are depending on certain users are stored here. It is derived from
# Baseclass for Users in Calibre-Web, settings which are depending on certain users are stored here. It is derived from
# User Base (all access methods are declared there)
class User(UserBase, Base):
__tablename__ = 'user'
......@@ -216,7 +216,7 @@ class Shelf(Base):
return '<Shelf %r>' % self.name
# Baseclass representing Relationship between books and Shelfs in Calibre-web in app.db (N:M)
# Baseclass representing Relationship between books and Shelfs in Calibre-Web in app.db (N:M)
class BookShelf(Base):
__tablename__ = 'book_shelf_link'
......@@ -259,6 +259,17 @@ class Downloads(Base):
def __repr__(self):
return '<Download %r' % self.book_id
# Baseclass representing allowed domains for registration
class Registration(Base):
__tablename__ = 'registration'
id = Column(Integer, primary_key=True)
domain = Column(String)
def __repr__(self):
return u"<Registration('{0}')>".format(self.domain)
# Baseclass for representing settings in app.db with email server settings and Calibre database settings
# (application settings)
......@@ -276,7 +287,7 @@ class Settings(Base):
config_port = Column(Integer, default=DEFAULT_PORT)
config_certfile = Column(String)
config_keyfile = Column(String)
config_calibre_web_title = Column(String, default=u'Calibre-web')
config_calibre_web_title = Column(String, default=u'Calibre-Web')
config_books_per_page = Column(Integer, default=60)
config_random_books = Column(Integer, default=4)
config_read_column = Column(Integer, default=0)
......@@ -516,35 +527,18 @@ class Config:
# everywhere to curent should work. Migration is done by checking if relevant coloums are existing, and than adding
# rows with SQL commands
def migrate_Database():
if not engine.dialect.has_table(engine.connect(), "book_read_link"):
ReadBook.__table__.create(bind=engine)
if not engine.dialect.has_table(engine.connect(), "bookmark"):
Bookmark.__table__.create(bind=engine)
try:
session.query(exists().where(User.locale)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
if not engine.dialect.has_table(engine.connect(), "registration"):
ReadBook.__table__.create(bind=engine)
conn = engine.connect()
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(Settings.config_calibre_dir)).scalar()
conn.execute("insert into registration (domain) values('%.%')")
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
# Handle table exists, but no content
cnt = session.query(Registration).count()
if not cnt:
conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_calibre_dir` String")
conn.execute("ALTER TABLE Settings ADD column `config_port` INTEGER DEFAULT 8083")
conn.execute("ALTER TABLE Settings ADD column `config_calibre_web_title` String DEFAULT 'Calibre-web'")
conn.execute("ALTER TABLE Settings ADD column `config_books_per_page` INTEGER DEFAULT 60")
conn.execute("ALTER TABLE Settings ADD column `config_random_books` INTEGER DEFAULT 4")
conn.execute("ALTER TABLE Settings ADD column `config_title_regex` String DEFAULT "
"'^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+'")
conn.execute("ALTER TABLE Settings ADD column `config_log_level` SmallInteger DEFAULT " + str(logging.INFO))
conn.execute("ALTER TABLE Settings ADD column `config_uploading` SmallInteger DEFAULT 0")
conn.execute("ALTER TABLE Settings ADD column `config_anonbrowse` SmallInteger DEFAULT 0")
conn.execute("ALTER TABLE Settings ADD column `config_public_reg` SmallInteger DEFAULT 0")
conn.execute("insert into registration (domain) values('%.%')")
session.commit()
try:
session.query(exists().where(Settings.config_use_google_drive)).scalar()
......@@ -553,6 +547,7 @@ def migrate_Database():
conn.execute("ALTER TABLE Settings ADD column `config_use_google_drive` INTEGER DEFAULT 0")
conn.execute("ALTER TABLE Settings ADD column `config_google_drive_folder` String DEFAULT ''")
conn.execute("ALTER TABLE Settings ADD column `config_google_drive_watch_changes_response` String DEFAULT ''")
session.commit()
try:
session.query(exists().where(Settings.config_columns_to_ignore)).scalar()
except exc.OperationalError:
......@@ -561,14 +556,12 @@ def migrate_Database():
session.commit()
try:
session.query(exists().where(Settings.config_default_role)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_default_role` SmallInteger DEFAULT 0")
session.commit()
try:
session.query(exists().where(BookShelf.order)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1")
......@@ -576,7 +569,6 @@ def migrate_Database():
try:
create = False
session.query(exists().where(User.sidebar_view)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE user ADD column `sidebar_view` Integer DEFAULT 1")
......@@ -606,6 +598,7 @@ def migrate_Database():
except exc.OperationalError:
conn = engine.connect()
conn.execute("ALTER TABLE user ADD column `theme` INTEGER DEFAULT 0")
session.commit()
if session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first() is None:
create_anonymous_user()
try:
......@@ -627,21 +620,18 @@ def migrate_Database():
conn.execute("ALTER TABLE Settings ADD column `config_mature_content_tags` String DEFAULT ''")
try:
session.query(exists().where(Settings.config_default_show)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_default_show` SmallInteger DEFAULT 2047")
session.commit()
try:
session.query(exists().where(Settings.config_logfile)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_logfile` String DEFAULT ''")
session.commit()
try:
session.query(exists().where(Settings.config_certfile)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_certfile` String DEFAULT ''")
......@@ -649,14 +639,12 @@ def migrate_Database():
session.commit()
try:
session.query(exists().where(Settings.config_read_column)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_read_column` INTEGER DEFAULT 0")
session.commit()
try:
session.query(exists().where(Settings.config_ebookconverter)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_ebookconverter` INTEGER DEFAULT 0")
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment