Commit cedc1839 authored by OzzieIsaacs's avatar OzzieIsaacs

Fix #590, #589.

Unified wording Calibre-Web
Replaced one table on admin page, deleted password column
Removed spaces on layout page
Removed uesless commit calls during db migration
Implementation of registering by email
parent f6ab7240
...@@ -26,6 +26,7 @@ except ImportError: ...@@ -26,6 +26,7 @@ except ImportError:
pass pass
import web import web
import server import server
import random
try: try:
import unidecode import unidecode
...@@ -76,10 +77,27 @@ def make_mobi(book_id, calibrepath, user_id, kindle_mail): ...@@ -76,10 +77,27 @@ def make_mobi(book_id, calibrepath, user_id, kindle_mail):
def send_test_mail(kindle_mail, user_name): def send_test_mail(kindle_mail, user_name):
global_WorkerThread.add_email(_(u'Calibre-web test email'),None, None, ub.get_mail_settings(), global_WorkerThread.add_email(_(u'Calibre-Web test email'),None, None, ub.get_mail_settings(),
kindle_mail, user_name, _(u"Test E-Mail")) kindle_mail, user_name, _(u"Test E-Mail"))
return 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: # Files are processed in the following order/priority:
# 1: If Mobi file is exisiting, it's directly send to kindle email, # 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 # 2: If Epub file is exisiting, it's converted and send to kindle email
...@@ -271,6 +289,11 @@ def delete_book_gdrive(book, book_format): ...@@ -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 error =_(u'Book path %s not found on Google Drive' % book.path) # file not found
return error return error
def generate_random_password():
s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%&*()?"
passlen = 8
return "".join(random.sample(s,passlen ))
################################## External interface ################################## External interface
def update_dir_stucture(book_id, calibrepath): def update_dir_stucture(book_id, calibrepath):
......
...@@ -69,7 +69,7 @@ class server: ...@@ -69,7 +69,7 @@ class server:
self.wsgiserver.close(True) self.wsgiserver.close(True)
if self.restart == 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() web.helper.global_WorkerThread.stop()
if os.name == 'nt': if os.name == 'nt':
arguments = ["\"" + sys.executable + "\""] arguments = ["\"" + sys.executable + "\""]
...@@ -79,7 +79,7 @@ class server: ...@@ -79,7 +79,7 @@ class server:
else: else:
os.execl(sys.executable, sys.executable, *sys.argv) os.execl(sys.executable, sys.executable, *sys.argv)
else: else:
web.app.logger.info("Performing shutdown of Calibre-web") web.app.logger.info("Performing shutdown of Calibre-Web")
web.helper.global_WorkerThread.stop() web.helper.global_WorkerThread.stop()
sys.exit(0) sys.exit(0)
......
This diff is collapsed.
...@@ -98,3 +98,9 @@ input.pill:not(:checked) + label .glyphicon { ...@@ -98,3 +98,9 @@ input.pill:not(:checked) + label .glyphicon {
#btn-upload-format {display: none;} #btn-upload-format {display: none;}
.panel-title > a { text-decoration: 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;}
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
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
{% block body %} {% block body %}
<div class="discover"> <div class="discover">
<h2>{{_('User list')}}</h2> <h2>{{_('User list')}}</h2>
<div id="divLoading"></div>
<table class="table table-striped" id="table_user"> <table class="table table-striped" id="table_user">
<tr> <tr>
<th>{{_('Nickname')}}</th> <th>{{_('Nickname')}}</th>
...@@ -13,7 +12,6 @@ ...@@ -13,7 +12,6 @@
<th>{{_('Download')}}</th> <th>{{_('Download')}}</th>
<th>{{_('Upload')}}</th> <th>{{_('Upload')}}</th>
<th>{{_('Edit')}}</th> <th>{{_('Edit')}}</th>
<th>{{_('Passwd')}}</th>
</tr> </tr>
{% for user in content %} {% for user in content %}
{% if not user.role_anonymous() or config.config_anonbrowse %} {% if not user.role_anonymous() or config.config_anonbrowse %}
...@@ -26,20 +24,18 @@ ...@@ -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_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_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_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> </tr>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</table> </table>
<div class="btn btn-default" id="admin_new_user"><a href="{{url_for('new_user')}}">{{_('Add new user')}}</a></div> <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"> <table class="table table-striped" id="table_email">
<tr> <tr>
<th>{{_('SMTP hostname')}}</th> <th>{{_('SMTP hostname')}}</th>
<th>{{_('SMTP port')}}</th> <th>{{_('SMTP port')}}</th>
<th>{{_('SSL')}}</th> <th>{{_('SSL')}}</th>
<th>{{_('SMTP login')}}</th> <th>{{_('SMTP login')}}</th>
<th>{{_('SMTP password')}}</th>
<th>{{_('From mail')}}</th> <th>{{_('From mail')}}</th>
</tr> </tr>
<tr> <tr>
...@@ -47,44 +43,56 @@ ...@@ -47,44 +43,56 @@
<td>{{email.mail_port}}</td> <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>{% 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>{{email.mail_login}}</td>
<td>********</td>
<td>{{email.mail_from}}</td> <td>{{email.mail_from}}</td>
</table> </table>
<div class="btn btn-default" id="admin_edit_email"><a href="{{url_for('edit_mailsettings')}}">{{_('Change SMTP settings')}}</a></div> <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> <h2>{{_('Configuration')}}</h2>
<table class="table table-striped" id="table_configuration"> <div class="col-xs-12 col-sm-6">
<tr> <div class="Row">
<th>{{_('Calibre DB dir')}}</th> <div class="col-xs-6 col-sm-6">{{_('Calibre DB dir')}}</div>
<th>{{_('Log Level')}}</th> <div class="col-xs-6 col-sm-6">{{config.config_calibre_dir}}</div>
<th>{{_('Port')}}</th> </div>
<th>{{_('Books per page')}}</th> <div class="Row">
<th>{{_('Uploading')}}</th> <div class="col-xs-6 col-sm-6">{{_('Log Level')}}</div>
<th>{{_('Public registration')}}</th> <div class="col-xs-6 col-sm-6">{{config.get_Log_Level()}}</div>
<th>{{_('Anonymous browsing')}}</th> </div>
<th>{{_('Remote Login')}}</th> <div class="Row">
</tr> <div class="col-xs-6 col-sm-6">{{_('Port')}}</div>
<tr> <div class="col-xs-6 col-sm-6">{{config.config_port}}</div>
<td>{{config.config_calibre_dir}}</td> </div>
<td>{{config.get_Log_Level()}}</td> </div>
<td>{{config.config_port}}</td> <div class="col-xs-12 col-sm-6">
<td>{{config.config_books_per_page}}</td> <div class="Row">
<td>{% if config.config_uploading %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td> <div class="col-xs-6 col-sm-7">{{_('Books per page')}}</div>
<td>{% if config.config_public_reg %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td> <div class="col-xs-6 col-sm-5">{{config.config_books_per_page}}</div>
<td>{% if config.config_anonbrowse %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td> </div>
<td>{% if config.config_remote_login %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td> <div class="Row">
</table> <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('configuration')}}">{{_('Basic Configuration')}}</a></div>
<div class="btn btn-default"><a href="{{url_for('view_configuration')}}">{{_('UI Configuration')}}</a></div> <div class="btn btn-default"><a href="{{url_for('view_configuration')}}">{{_('UI Configuration')}}</a></div>
</div>
<h2>{{_('Administration')}}</h2> <h2>{{_('Administration')}}</h2>
<div>{{_('Current commit timestamp')}}: <span>{{commit}} </span></div> <div>{{_('Current commit timestamp')}}: <span>{{commit}} </span></div>
<div class="hidden" id="update_info">{{_('Newest commit timestamp')}}: <span></span></div> <div class="hidden" id="update_info">{{_('Newest commit timestamp')}}: <span></span></div>
<p></p> <p></p>
<div class="btn btn-default" id="restart_database">{{_('Reconnect to Calibre DB')}}</div> <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="#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="#ShutdownDialog">{{_('Stop Calibre-Web')}}</div>
<div class="btn btn-default" id="check_for_update">{{_('Check for update')}}</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 class="btn btn-default hidden" id="perform_update" data-toggle="modal" data-target="#UpdateprogressDialog">{{_('Perform Update')}}</div>
</div> </div>
...@@ -95,7 +103,7 @@ ...@@ -95,7 +103,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header bg-info"></div> <div class="modal-header bg-info"></div>
<div class="modal-body text-center"> <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;"> <div id="spinner" class="spinner" style="display:none;">
<img id="img-spinner" src="/static/css/images/loading-icon.gif"/> <img id="img-spinner" src="/static/css/images/loading-icon.gif"/>
</div> </div>
...@@ -113,7 +121,7 @@ ...@@ -113,7 +121,7 @@
<div class="modal-header bg-info"> <div class="modal-header bg-info">
</div> </div>
<div class="modal-body text-center"> <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" id="shutdown" data-dismiss="modal">{{_('Ok')}}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Back')}}</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{_('Back')}}</button>
</div> </div>
......
...@@ -154,7 +154,7 @@ ...@@ -154,7 +154,7 @@
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header bg-danger text-center"> <div class="modal-header bg-danger text-center">
<span>{{_('Are really you sure?')}}</span> <span>{{_('Are you really sure?')}}</span>
</div> </div>
<div class="modal-body text-center"> <div class="modal-body text-center">
<span>{{_('Book will be deleted from Calibre database')}}</span> <span>{{_('Book will be deleted from Calibre database')}}</span>
...@@ -239,7 +239,7 @@ ...@@ -239,7 +239,7 @@
var i18nMsg = { var i18nMsg = {
'loading': {{_('Loading...')|safe|tojson}}, 'loading': {{_('Loading...')|safe|tojson}},
'search_error': {{_('Search error!')|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}}, 'author': {{_('Author')|safe|tojson}},
'publisher': {{_('Publisher')|safe|tojson}}, 'publisher': {{_('Publisher')|safe|tojson}},
'description': {{_('Description')|safe|tojson}}, 'description': {{_('Description')|safe|tojson}},
......
{% extends "layout.html" %} {% 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 %} {% block body %}
<div class="discover"> <div class="discover">
<h1>{{title}}</h1> <h1>{{title}}</h1>
...@@ -35,6 +39,52 @@ ...@@ -35,6 +39,52 @@
<button type="submit" name="test" value="test" class="btn btn-default">{{_('Save settings and send Test E-Mail')}}</button> <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> <a href="{{ url_for('admin') }}" id="back" class="btn btn-default">{{_('Back')}}</a>
</form> </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> </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 %} {% endblock %}
...@@ -124,7 +124,7 @@ ...@@ -124,7 +124,7 @@
{% if g.user.show_sorted() %} {% if g.user.show_sorted() %}
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> <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> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
...@@ -136,44 +136,44 @@ ...@@ -136,44 +136,44 @@
</li> </li>
{%endif%} {%endif%}
{% if g.user.show_hot_books() %} {% 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%} {%endif%}
{% if g.user.show_best_rated_books() %} {% 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%} {%endif%}
{% if g.user.show_read_and_unread() %} {% if g.user.show_read_and_unread() %}
{% if not g.user.is_anonymous %} {% 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%} {%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%} {%endif%}
{% if g.user.show_random_books() %} {% 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%} {%endif%}
{% if g.user.show_category() %} {% 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%} {%endif%}
{% if g.user.show_series() %} {% 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%} {%endif%}
{% if g.user.show_author() %} {% 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%} {%endif%}
{% if g.user.filter_language() == 'all' and g.user.show_language() %} {% 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%} {%endif%}
{% if g.user.is_authenticated or g.user.is_anonymous %} {% if g.user.is_authenticated or g.user.is_anonymous %}
<li class="nav-head hidden-xs">{{_('Public Shelves')}}</li> <li class="nav-head hidden-xs">{{_('Public Shelves')}}</li>
{% for shelf in g.public_shelfes %} {% 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 %} {% 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 %} {% 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 %} {% endfor %}
{% if not g.user.is_anonymous %} {% 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_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 %}
{% endif %} {% endif %}
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<LongName>{{instance}}</LongName> <LongName>{{instance}}</LongName>
<ShortName>{{instance}}</ShortName> <ShortName>{{instance}}</ShortName>
<Description>{{_('Calibre Web ebook catalog')}}</Description> <Description>{{_('Calibre-Web ebook catalog')}}</Description>
<Developer>Janeczku</Developer> <Developer>Janeczku</Developer>
<Contact>https://github.com/janeczku/calibre-web</Contact> <Contact>https://github.com/janeczku/calibre-web</Contact>
<Url type="text/html" <Url type="text/html"
......
...@@ -7,10 +7,10 @@ ...@@ -7,10 +7,10 @@
<label for="nickname">{{_('Username')}}</label> <label for="nickname">{{_('Username')}}</label>
<input type="text" class="form-control" id="nickname" name="nickname" placeholder="{{_('Choose a username')}}" required> <input type="text" class="form-control" id="nickname" name="nickname" placeholder="{{_('Choose a username')}}" required>
</div> </div>
<div class="form-group required"> <!--div class="form-group required">
<label for="password">{{_('Password')}}</label> <label for="password">{{_('Password')}}</label>
<input type="password" class="form-control" id="password" name="password" placeholder="{{_('Choose a password')}}" required> <input type="password" class="form-control" id="password" name="password" placeholder="{{_('Choose a password')}}" required>
</div> </div-->
<div class="form-group required"> <div class="form-group required">
<label for="email">{{_('Email address')}}</label> <label for="email">{{_('Email address')}}</label>
<input type="email" class="form-control" id="email" name="email" placeholder="{{_('Your email address')}}" required> <input type="email" class="form-control" id="email" name="email" placeholder="{{_('Your email address')}}" required>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
{% block body %} {% block body %}
<h3>{{_('About')}}</h3> <h3>{{_('About')}}</h3>
<p>{{instance}} powered by <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> </p>
<h3>{{_('Calibre library statistics')}}</h3> <h3>{{_('Calibre library statistics')}}</h3>
<table id="stats" class="table"> <table id="stats" class="table">
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
{% block js %} {% block js %}
<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.min.js') }}"></script>
<script> <script>
// ToDo: Move to js file
$('#table').bootstrapTable({ $('#table').bootstrapTable({
formatNoMatches: function () { formatNoMatches: function () {
return ''; return '';
......
...@@ -14,11 +14,17 @@ ...@@ -14,11 +14,17 @@
<input type="email" class="form-control" name="email" id="email" value="{{ content.email if content.email != None }}" autocomplete="off"> <input type="email" class="form-control" name="email" id="email" value="{{ content.email if content.email != None }}" autocomplete="off">
</div> </div>
{% if ( g.user and g.user.role_passwd() or g.user.role_admin() ) and not content.role_anonymous() %} {% if ( g.user and g.user.role_passwd() or g.user.role_admin() ) and not content.role_anonymous() %}
{% if g.user and g.user.role_admin() and g.allow_registration and not new_user %}
<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"> <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"> <input type="password" class="form-control" name="password" id="password" value="" autocomplete="off">
</div> </div>
{% endif %} {% endif %}
{% endif %}
<div class="form-group"> <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 }}"> <input type="email" class="form-control" name="kindle_mail" id="kindle_mail" value="{{ content.kindle_mail if content.kindle_mail != None }}">
......
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: ...@@ -149,7 +149,7 @@ class UserBase:
return '<User %r>' % self.nickname 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) # User Base (all access methods are declared there)
class User(UserBase, Base): class User(UserBase, Base):
__tablename__ = 'user' __tablename__ = 'user'
...@@ -216,7 +216,7 @@ class Shelf(Base): ...@@ -216,7 +216,7 @@ class Shelf(Base):
return '<Shelf %r>' % self.name 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): class BookShelf(Base):
__tablename__ = 'book_shelf_link' __tablename__ = 'book_shelf_link'
...@@ -260,6 +260,17 @@ class Downloads(Base): ...@@ -260,6 +260,17 @@ class Downloads(Base):
return '<Download %r' % self.book_id 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 # Baseclass for representing settings in app.db with email server settings and Calibre database settings
# (application settings) # (application settings)
class Settings(Base): class Settings(Base):
...@@ -276,7 +287,7 @@ class Settings(Base): ...@@ -276,7 +287,7 @@ class Settings(Base):
config_port = Column(Integer, default=DEFAULT_PORT) config_port = Column(Integer, default=DEFAULT_PORT)
config_certfile = Column(String) config_certfile = Column(String)
config_keyfile = 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_books_per_page = Column(Integer, default=60)
config_random_books = Column(Integer, default=4) config_random_books = Column(Integer, default=4)
config_read_column = Column(Integer, default=0) config_read_column = Column(Integer, default=0)
...@@ -516,35 +527,18 @@ class Config: ...@@ -516,35 +527,18 @@ class Config:
# everywhere to curent should work. Migration is done by checking if relevant coloums are existing, and than adding # everywhere to curent should work. Migration is done by checking if relevant coloums are existing, and than adding
# rows with SQL commands # rows with SQL commands
def migrate_Database(): 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"): if not engine.dialect.has_table(engine.connect(), "bookmark"):
Bookmark.__table__.create(bind=engine) Bookmark.__table__.create(bind=engine)
if not engine.dialect.has_table(engine.connect(), "registration"):
try: ReadBook.__table__.create(bind=engine)
session.query(exists().where(User.locale)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE user ADD column locale String(2) DEFAULT 'en'") conn.execute("insert into registration (domain) values('%.%')")
conn.execute("ALTER TABLE user ADD column default_language String(3) DEFAULT 'all'")
session.commit() session.commit()
try: # Handle table exists, but no content
session.query(exists().where(Settings.config_calibre_dir)).scalar() cnt = session.query(Registration).count()
session.commit() if not cnt:
except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_calibre_dir` String") conn.execute("insert into registration (domain) values('%.%')")
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")
session.commit() session.commit()
try: try:
session.query(exists().where(Settings.config_use_google_drive)).scalar() session.query(exists().where(Settings.config_use_google_drive)).scalar()
...@@ -553,6 +547,7 @@ def migrate_Database(): ...@@ -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_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_folder` String DEFAULT ''")
conn.execute("ALTER TABLE Settings ADD column `config_google_drive_watch_changes_response` String DEFAULT ''") conn.execute("ALTER TABLE Settings ADD column `config_google_drive_watch_changes_response` String DEFAULT ''")
session.commit()
try: try:
session.query(exists().where(Settings.config_columns_to_ignore)).scalar() session.query(exists().where(Settings.config_columns_to_ignore)).scalar()
except exc.OperationalError: except exc.OperationalError:
...@@ -561,14 +556,12 @@ def migrate_Database(): ...@@ -561,14 +556,12 @@ def migrate_Database():
session.commit() session.commit()
try: try:
session.query(exists().where(Settings.config_default_role)).scalar() session.query(exists().where(Settings.config_default_role)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_default_role` SmallInteger DEFAULT 0") conn.execute("ALTER TABLE Settings ADD column `config_default_role` SmallInteger DEFAULT 0")
session.commit() session.commit()
try: try:
session.query(exists().where(BookShelf.order)).scalar() session.query(exists().where(BookShelf.order)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1") conn.execute("ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1")
...@@ -576,7 +569,6 @@ def migrate_Database(): ...@@ -576,7 +569,6 @@ def migrate_Database():
try: try:
create = False create = False
session.query(exists().where(User.sidebar_view)).scalar() session.query(exists().where(User.sidebar_view)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE user ADD column `sidebar_view` Integer DEFAULT 1") conn.execute("ALTER TABLE user ADD column `sidebar_view` Integer DEFAULT 1")
...@@ -606,6 +598,7 @@ def migrate_Database(): ...@@ -606,6 +598,7 @@ def migrate_Database():
except exc.OperationalError: except exc.OperationalError:
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE user ADD column `theme` INTEGER DEFAULT 0") 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: if session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first() is None:
create_anonymous_user() create_anonymous_user()
try: try:
...@@ -627,21 +620,18 @@ def migrate_Database(): ...@@ -627,21 +620,18 @@ def migrate_Database():
conn.execute("ALTER TABLE Settings ADD column `config_mature_content_tags` String DEFAULT ''") conn.execute("ALTER TABLE Settings ADD column `config_mature_content_tags` String DEFAULT ''")
try: try:
session.query(exists().where(Settings.config_default_show)).scalar() session.query(exists().where(Settings.config_default_show)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_default_show` SmallInteger DEFAULT 2047") conn.execute("ALTER TABLE Settings ADD column `config_default_show` SmallInteger DEFAULT 2047")
session.commit() session.commit()
try: try:
session.query(exists().where(Settings.config_logfile)).scalar() session.query(exists().where(Settings.config_logfile)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_logfile` String DEFAULT ''") conn.execute("ALTER TABLE Settings ADD column `config_logfile` String DEFAULT ''")
session.commit() session.commit()
try: try:
session.query(exists().where(Settings.config_certfile)).scalar() session.query(exists().where(Settings.config_certfile)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_certfile` String DEFAULT ''") conn.execute("ALTER TABLE Settings ADD column `config_certfile` String DEFAULT ''")
...@@ -649,14 +639,12 @@ def migrate_Database(): ...@@ -649,14 +639,12 @@ def migrate_Database():
session.commit() session.commit()
try: try:
session.query(exists().where(Settings.config_read_column)).scalar() session.query(exists().where(Settings.config_read_column)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_read_column` INTEGER DEFAULT 0") conn.execute("ALTER TABLE Settings ADD column `config_read_column` INTEGER DEFAULT 0")
session.commit() session.commit()
try: try:
session.query(exists().where(Settings.config_ebookconverter)).scalar() session.query(exists().where(Settings.config_ebookconverter)).scalar()
session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing except exc.OperationalError: # Database is not compatible, some rows are missing
conn = engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_ebookconverter` INTEGER DEFAULT 0") conn.execute("ALTER TABLE Settings ADD column `config_ebookconverter` INTEGER DEFAULT 0")
......
This diff is collapsed.
...@@ -326,7 +326,8 @@ class WorkerThread(threading.Thread): ...@@ -326,7 +326,8 @@ class WorkerThread(threading.Thread):
addLock.release() addLock.release()
def add_email(self, subject, filepath, attachment, settings, recipient, user_name, typ): def add_email(self, subject, filepath, attachment, settings, recipient, user_name, typ,
text=_(u'This email has been sent via calibre web.')):
# if more than 20 entries in the list, clean the list # if more than 20 entries in the list, clean the list
addLock = threading.Lock() addLock = threading.Lock()
addLock.acquire() addLock.acquire()
...@@ -335,7 +336,7 @@ class WorkerThread(threading.Thread): ...@@ -335,7 +336,7 @@ class WorkerThread(threading.Thread):
# progress, runtime, and status = 0 # progress, runtime, and status = 0
self.queue.append({'subject':subject, 'attachment':attachment, 'filepath':filepath, self.queue.append({'subject':subject, 'attachment':attachment, 'filepath':filepath,
'settings':settings, 'recipent':recipient, 'starttime': 0, 'settings':settings, 'recipent':recipient, 'starttime': 0,
'status': STAT_WAITING, 'typ': TASK_EMAIL}) 'status': STAT_WAITING, 'typ': TASK_EMAIL, 'text':text})
self.UIqueue.append({'user': user_name, 'formStarttime': '', 'progress': " 0 %", 'type': typ, self.UIqueue.append({'user': user_name, 'formStarttime': '', 'progress': " 0 %", 'type': typ,
'runtime': '0 s', 'status': _('Waiting'),'id': self.id }) 'runtime': '0 s', 'status': _('Waiting'),'id': self.id })
self.id += 1 self.id += 1
...@@ -369,7 +370,7 @@ class WorkerThread(threading.Thread): ...@@ -369,7 +370,7 @@ class WorkerThread(threading.Thread):
msg['Subject'] = self.queue[self.current]['subject'] msg['Subject'] = self.queue[self.current]['subject']
msg['Message-Id'] = make_msgid('calibre-web') msg['Message-Id'] = make_msgid('calibre-web')
msg['Date'] = formatdate(localtime=True) msg['Date'] = formatdate(localtime=True)
text = _(u'This email has been sent via calibre web.') text = self.queue[self.current]['text']
msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8')) msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8'))
if obj['attachment']: if obj['attachment']:
result = get_attachment(obj['filepath'], obj['attachment']) result = get_attachment(obj['filepath'], obj['attachment'])
......
This diff is collapsed.
# About # About
Calibre Web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing [Calibre](https://calibre-ebook.com) database. Calibre-Web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing [Calibre](https://calibre-ebook.com) database.
*This software is a fork of [library](https://github.com/mutschler/calibreserver) and licensed under the GPL v3 License.* *This software is a fork of [library](https://github.com/mutschler/calibreserver) and licensed under the GPL v3 License.*
...@@ -98,7 +98,7 @@ The Drive API should now be setup and ready to use, so we need to integrate it i ...@@ -98,7 +98,7 @@ The Drive API should now be setup and ready to use, so we need to integrate it i
8. Google Drive should now be connected and be used to get images and download Epubs. The metadata.db is stored in the calibre library location 8. Google Drive should now be connected and be used to get images and download Epubs. The metadata.db is stored in the calibre library location
### Optional ### Optional
If your calibre web is using https, it is possible to add a "watch" to the drive. This will inform us if the metadata.db file is updated and allow us to update our calibre library accordingly. If your Calibre-Web is using https, it is possible to add a "watch" to the drive. This will inform us if the metadata.db file is updated and allow us to update our calibre library accordingly.
Additionally the public adress your server uses (e.g.https://example.com) has to be verified in the Google developer console. After this is done, please wait a few minutes. Additionally the public adress your server uses (e.g.https://example.com) has to be verified in the Google developer console. After this is done, please wait a few minutes.
9. Open config page 9. Open config page
...@@ -123,7 +123,7 @@ Pre-built Docker images based on Alpine Linux are available in these Docker Hub ...@@ -123,7 +123,7 @@ Pre-built Docker images based on Alpine Linux are available in these Docker Hub
Reverse proxy configuration examples for apache and nginx to use Calibre-Web: Reverse proxy configuration examples for apache and nginx to use Calibre-Web:
nginx configuration for a local server listening on port 8080, mapping calibre web to /calibre: nginx configuration for a local server listening on port 8080, mapping Calibre-Web to /calibre:
``` ```
http { http {
...@@ -148,7 +148,7 @@ http { ...@@ -148,7 +148,7 @@ http {
proxy_redirect http://$host/ https://$host:12345/; proxy_redirect http://$host/ https://$host:12345/;
``` ```
Apache 2.4 configuration for a local server listening on port 443, mapping calibre web to /calibre-web: Apache 2.4 configuration for a local server listening on port 443, mapping Calibre-Web to /calibre-web:
The following modules have to be activated: headers, proxy, rewrite. The following modules have to be activated: headers, proxy, rewrite.
``` ```
......
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