Merge branch 'janeczku-master'

This commit is contained in:
idalin 2017-08-11 09:26:36 +08:00
commit 2b27d6570f
22 changed files with 323 additions and 242 deletions

View File

@ -230,6 +230,8 @@ class Data(Base):
class Books(Base): class Books(Base):
__tablename__ = 'books' __tablename__ = 'books'
DEFAULT_PUBDATE = "0101-01-01 00:00:00+00:00"
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
title = Column(String) title = Column(String)
sort = Column(String) sort = Column(String)

View File

@ -176,7 +176,7 @@ def send_raw_email(kindle_mail, msg):
mailserver.starttls() mailserver.starttls()
if settings["mail_password"]: if settings["mail_password"]:
mailserver.login(settings["mail_login"], settings["mail_password"]) mailserver.login(str(settings["mail_login"]), str(settings["mail_password"]))
mailserver.sendmail(settings["mail_from"], kindle_mail, msg) mailserver.sendmail(settings["mail_from"], kindle_mail, msg)
mailserver.quit() mailserver.quit()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,31 @@
/** /**
* Created by SpeedProg on 05.04.2015. * Created by SpeedProg on 05.04.2015.
*/ */
/* global Bloodhound */ /* global Bloodhound, language, Modernizr, tinymce */
tinymce.init({
selector: "#description",
branding: false,
menubar: "edit view format",
language
});
if (!Modernizr.inputtypes.date) {
$("#pubdate").datepicker({
format: "yyyy-mm-dd",
language
}).on("change", function () {
// Show localized date over top of the standard YYYY-MM-DD date
let pubDate;
const results = /(\d{4})[-\/\\](\d{1,2})[-\/\\](\d{1,2})/.exec(this.value); // YYYY-MM-DD
if (results) {
pubDate = new Date(results[1], parseInt(results[2], 10)-1, results[3]) || new Date(this.value);
}
$("#fake_pubdate")
.val(pubDate.toLocaleDateString(language))
.removeClass("hidden");
}).trigger("change");
}
/* /*
Takes a prefix, query typeahead callback, Bloodhound typeahead adapter Takes a prefix, query typeahead callback, Bloodhound typeahead adapter

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.de={days:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],daysShort:["Son","Mon","Die","Mit","Don","Fre","Sam"],daysMin:["So","Mo","Di","Mi","Do","Fr","Sa"],months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthsShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],today:"Heute",monthsTitle:"Monate",clear:"Löschen",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);

View File

@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.es={days:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],daysShort:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb"],daysMin:["Do","Lu","Ma","Mi","Ju","Vi","Sa"],months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthsShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],today:"Hoy",monthsTitle:"Meses",clear:"Borrar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);

View File

@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.fr={days:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],daysShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],daysMin:["d","l","ma","me","j","v","s"],months:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthsShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],today:"Aujourd'hui",monthsTitle:"Mois",clear:"Effacer",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);

View File

@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.nl={days:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],daysShort:["zo","ma","di","wo","do","vr","za"],daysMin:["zo","ma","di","wo","do","vr","za"],months:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthsShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],today:"Vandaag",monthsTitle:"Maanden",clear:"Wissen",weekStart:1,format:"dd-mm-yyyy"}}(jQuery);

View File

@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.pl={days:["Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota"],daysShort:["Niedz.","Pon.","Wt.","Śr.","Czw.","Piąt.","Sob."],daysMin:["Ndz.","Pn.","Wt.","Śr.","Czw.","Pt.","Sob."],months:["Styczeń","Luty","Marzec","Kwiecień","Maj","Czerwiec","Lipiec","Sierpień","Wrzesień","Październik","Listopad","Grudzień"],monthsShort:["Sty.","Lut.","Mar.","Kwi.","Maj","Cze.","Lip.","Sie.","Wrz.","Paź.","Lis.","Gru."],today:"Dzisiaj",weekStart:1,clear:"Wyczyść",format:"dd.mm.yyyy"}}(jQuery);

View File

@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.ru={days:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],daysShort:["Вск","Пнд","Втр","Срд","Чтв","Птн","Суб"],daysMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Сегодня",clear:"Очистить",format:"dd.mm.yyyy",weekStart:1,monthsTitle:"Месяцы"}}(jQuery);

View File

@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates["zh-CN"]={days:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],daysShort:["周日","周一","周二","周三","周四","周五","周六"],daysMin:["日","一","二","三","四","五","六"],months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],today:"今日",clear:"清除",format:"yyyy年mm月dd日",titleFormat:"yyyy年mm月",weekStart:1}}(jQuery);

View File

@ -35,23 +35,30 @@
<input type="text" class="form-control typeahead" name="tags" id="tags" value="{% for tag in book.tags %}{{tag.name.strip()}}, {% endfor %}"> <input type="text" class="form-control typeahead" name="tags" id="tags" value="{% for tag in book.tags %}{{tag.name.strip()}}, {% endfor %}">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="series">{{_('Series')}}</label> <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 %}"> <input type="text" class="form-control typeahead" name="series" id="series" value="{% if book.series %}{{book.series[0].name}}{% endif %}">
</div> </div>
<div class="form-group"> <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}}"> <input type="text" class="form-control" name="series_index" id="series_index" value="{{book.series_index}}">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="rating">{{_('Rating')}}</label> <label for="rating">{{_('Rating')}}</label>
<input type="number" name="rating" id="rating" class="rating input-lg" data-clearable="" value="{% if book.ratings %}{{(book.ratings[0].rating / 2)|int}}{% endif %}"> <input type="number" name="rating" id="rating" class="rating input-lg" data-clearable="" value="{% if book.ratings %}{{(book.ratings[0].rating / 2)|int}}{% endif %}">
</div> </div>
<div class="form-group"> <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=""> <input type="text" class="form-control" name="cover_url" id="cover_url" value="">
</div> </div>
<div class="form-group">
<label for="pubdate">{{_('Publishing date')}}</label>
<div style="position: relative">
<input type="date" class="form-control" name="pubdate" id="pubdate" value="{% if book.pubdate %}{{book.pubdate|formatdateinput}}{% endif %}">
<input type="text" class="form-control fake-input hidden" id="fake_pubdate" value="{% if book.pubdate %}{{book.pubdate|formatdate}}{% endif %}">
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="languages">{{_('Language')}}</label> <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 %}">
@ -183,21 +190,19 @@
'description': {{_('Description')|safe|tojson}}, 'description': {{_('Description')|safe|tojson}},
'source': {{_('Source')|safe|tojson}}, 'source': {{_('Source')|safe|tojson}},
}; };
var language = '{{ g.user.locale }}';
</script> </script>
<script src="{{ url_for('static', filename='js/libs/typeahead.bundle.js') }}"></script> <script src="{{ url_for('static', filename='js/libs/typeahead.bundle.js') }}"></script>
<script src="{{ url_for('static', filename='js/edit_books.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/bootstrap-rating-input.min.js') }}"></script> <script src="{{ url_for('static', filename='js/libs/bootstrap-rating-input.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/get_meta.js') }}"></script> <script src="{{ url_for('static', filename='js/get_meta.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/tinymce/tinymce.min.js') }}"></script> <script src="{{ url_for('static', filename='js/libs/tinymce/tinymce.min.js') }}"></script>
<script type="text/javascript"> <script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/bootstrap-datepicker.min.js') }}"></script>
tinymce.init({ {% if not g.user.locale == 'en' %}
selector: '#description', <script src="{{ url_for('static', filename='js/libs/bootstrap-datepicker/locales/bootstrap-datepicker.' + g.user.locale + '.min.js') }}" charset="UTF-8"></script>
branding: false, {% endif %}
menubar: 'edit view format', <script src="{{ url_for('static', filename='js/edit_books.js') }}"></script>
language: '{{ g.user.locale }}'
});
</script>
{% endblock %} {% endblock %}
{% block header %} {% block header %}
<link href="{{ url_for('static', filename='css/libs/typeahead.css') }}" rel="stylesheet" media="screen"> <link href="{{ url_for('static', filename='css/libs/typeahead.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='css/libs/bootstrap-datepicker3.min.css') }}" rel="stylesheet" media="screen">
{% endblock %} {% endblock %}

View File

@ -5,9 +5,9 @@
<div class="col-sm-3 col-lg-3 col-xs-5"> <div class="col-sm-3 col-lg-3 col-xs-5">
<div class="cover"> <div class="cover">
{% if entry.has_cover %} {% if entry.has_cover %}
<img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" /> <img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" alt="{{ entry.title }}" />
{% else %} {% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" /> <img src="{{ url_for('static', filename='generic_cover.jpg') }}" alt="{{ entry.title }}" />
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -17,7 +17,7 @@
{% if g.user.role_download() %} {% if g.user.role_download() %}
<div class="btn-group" role="group"> <div class="btn-group" role="group">
{% if entry.data|length < 3 %} {% if entry.data|length < 3 %}
<button id="btnGroupDrop1" type="button" class="btn btn-primary"> <button type="button" class="btn btn-primary">
{{_('Download')}} : {{_('Download')}} :
</button> </button>
{% for format in entry.data %} {% for format in entry.data %}
@ -44,11 +44,11 @@
{% endif %} {% endif %}
{% if (g.user.role_download() and g.user.is_anonymous()) or g.user.is_authenticated %} {% if (g.user.role_download() and g.user.is_anonymous()) or g.user.is_authenticated %}
<div class="btn-group" role="group"> <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"> <button id="read-in-browser" 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> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop2"> <ul class="dropdown-menu" aria-labelledby="read-in-browser">
{% for format in entry.data %} {% for format in entry.data %}
{%if format.format|lower == 'epub' or format.format|lower == 'txt' or format.format|lower == 'pdf' or format.format|lower == 'cbr' or format.format|lower == 'cbt' or format.format|lower == 'cbz' %} {%if format.format|lower == 'epub' or format.format|lower == 'txt' or format.format|lower == 'pdf' or format.format|lower == 'cbr' or format.format|lower == 'cbt' or format.format|lower == 'cbz' %}
<li><a target="_blank" href="{{ url_for('read_book', book_id=entry.id, book_format=format.format|lower) }}">{{format.format}}</a></li> <li><a target="_blank" href="{{ url_for('read_book', book_id=entry.id, book_format=format.format|lower) }}">{{format.format}}</a></li>
@ -184,11 +184,11 @@
{% if g.user.shelf.all() or g.public_shelfes %} {% if g.user.shelf.all() or g.public_shelfes %}
<div id="shelf-actions" class="btn-toolbar" role="toolbar"> <div id="shelf-actions" class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group" aria-label="Add to shelves"> <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"> <button id="add-to-shelf" 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> <span class="caret"></span>
</button> </button>
<ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="btnGroupDrop2"> <ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="add-to-shelf">
{% for shelf in g.user.shelf %} {% for shelf in g.user.shelf %}
{% if not shelf.id in books_shelfs and shelf.is_public != 1 %} {% if not shelf.id in books_shelfs and shelf.is_public != 1 %}
<li> <li>

View File

@ -9,7 +9,7 @@
<div class="cover"> <div class="cover">
{% if entry.has_cover is defined %} {% if entry.has_cover is defined %}
<a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"> <a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
<img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" /> <img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" alt="{{ entry.title }}" />
</a> </a>
{% endif %} {% endif %}
</div> </div>

View File

@ -6,13 +6,13 @@
<div class="row"> <div class="row">
{% for entry in random %} {% for entry in random %}
<div id="books_rand" class="col-sm-3 col-lg-2 col-xs-6 book"> <div class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="cover"> <div class="cover">
<a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"> <a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
{% if entry.has_cover %} {% if entry.has_cover %}
<img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" /> <img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" alt="{{ entry.title }}" />
{% else %} {% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" /> <img src="{{ url_for('static', filename='generic_cover.jpg') }}" alt="{{ entry.title }}" />
{% endif %} {% endif %}
</a> </a>
</div> </div>
@ -42,13 +42,13 @@
<div class="row"> <div class="row">
{% if entries[0] %} {% if entries[0] %}
{% for entry in entries %} {% for entry in entries %}
<div id="books" class="col-sm-3 col-lg-2 col-xs-6 book"> <div class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="cover"> <div class="cover">
<a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"> <a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
{% if entry.has_cover %} {% if entry.has_cover %}
<img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" /> <img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" alt="{{ entry.title }}"/>
{% else %} {% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" /> <img src="{{ url_for('static', filename='generic_cover.jpg') }}" alt="{{ entry.title }}" />
{% endif %} {% endif %}
</a> </a>
</div> </div>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="{{ g.user.locale }}">
<head> <head>
<title>{{instance}} | {{title}}</title> <title>{{instance}} | {{title}}</title>
<meta charset="utf-8"> <meta charset="utf-8">
@ -19,28 +19,9 @@
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]--> <![endif]-->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<!--script src="https://code.jquery.com/jquery.js"></script-->
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{ url_for('static', filename='js/libs/bootstrap.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/underscore-min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/intention.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/context.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/plugins.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/jquery.form.js') }}"></script>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
{% block header %}{% endblock %} {% block header %}{% endblock %}
</head> </head>
<body> <body>
<script>
$(document).ready(function(){
$("#btn-upload").change(function() {
$("#form-upload").submit();
});
});
</script>
<!-- Static navbar --> <!-- Static navbar -->
<div class="navbar navbar-default navbar-static-top" role="navigation"> <div class="navbar navbar-default navbar-static-top" role="navigation">
<div class="container-fluid"> <div class="container-fluid">
@ -56,7 +37,8 @@
{% if g.user.is_authenticated or g.user.is_anonymous() %} {% if g.user.is_authenticated or g.user.is_anonymous() %}
<form class="navbar-form navbar-left" role="search" action="{{url_for('search')}}" method="GET"> <form class="navbar-form navbar-left" role="search" action="{{url_for('search')}}" method="GET">
<div class="form-group input-group input-group-sm"> <div class="form-group input-group input-group-sm">
<input type="text" class="form-control" name="query" placeholder="{{_('Search')}}"> <label for="query" class="sr-only">{{_('Search')}}</label>
<input type="text" class="form-control" id="query" name="query" placeholder="{{_('Search')}}">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="submit" class="btn btn-default">{{_('Go!')}}</button> <button type="submit" class="btn btn-default">{{_('Go!')}}</button>
</span> </span>
@ -181,6 +163,10 @@
{% block body %}{% endblock %} {% block body %}{% endblock %}
{% if pagination and (pagination.has_next or pagination.has_prev) %} {% if pagination and (pagination.has_next or pagination.has_prev) %}
<div class="pagination"> <div class="pagination">
{% if pagination.has_prev %}
<a class="previous" href="{{ url_for_other_page(pagination.page - 1)
}}">&laquo; {{_('Previous')}}</a>
{% endif %}
{% for page in pagination.iter_pages() %} {% for page in pagination.iter_pages() %}
{% if page %} {% if page %}
{% if page != pagination.page %} {% if page != pagination.page %}
@ -194,7 +180,7 @@
{% endfor %} {% endfor %}
{% if pagination.has_next %} {% if pagination.has_next %}
<a class="next" href="{{ url_for_other_page(pagination.page + 1) <a class="next" href="{{ url_for_other_page(pagination.page + 1)
}}">Next &raquo;</a> }}">{{_('Next')}} &raquo;</a>
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
@ -216,6 +202,26 @@
</div> </div>
</div> </div>
{% block modal %}{% endblock %} {% block modal %}{% endblock %}
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<!--script src="https://code.jquery.com/jquery.js"></script-->
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{ url_for('static', filename='js/libs/bootstrap.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/underscore-min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/intention.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/context.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/plugins.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/jquery.form.js') }}"></script>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script type="text/javascript">
$(function() {
$("#btn-upload").change(function() {
$("#form-upload").submit();
});
});
</script>
{% block js %}{% endblock %} {% block js %}{% endblock %}
</body> </body>
</html> </html>

View File

@ -16,7 +16,7 @@
<div class="cover"> <div class="cover">
{% if entry.has_cover is defined %} {% if entry.has_cover is defined %}
<a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"> <a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
<img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" /> <img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" alt="{{ entry.title }}" />
</a> </a>
{% endif %} {% endif %}
</div> </div>

View File

@ -28,7 +28,7 @@
<div class="form-group"> <div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons"> <div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for tag in tags %} {% for tag in tags %}
<label id="tag_{{tag.id}}" class="btn btn-danger tags_click"> <label id="exclude_tag_{{tag.id}}" class="btn btn-danger tags_click">
<input type="checkbox" autocomplete="off" name="exclude_tag" id="exclude_tag" value="{{tag.id}}">{{tag.name}}</input> <input type="checkbox" autocomplete="off" name="exclude_tag" id="exclude_tag" value="{{tag.id}}">{{tag.name}}</input>
</label> </label>
{% endfor %} {% endfor %}
@ -48,7 +48,7 @@
<div class="form-group"> <div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons"> <div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for serie in series %} {% for serie in series %}
<label id="serie_{{serie.id}}" class="btn btn-danger serie_click"> <label id="exclude_serie_{{serie.id}}" class="btn btn-danger serie_click">
<input type="checkbox" autocomplete="off" name="exclude_serie" id="exclude_serie" value="{{serie.id}}">{{serie.name}}</input> <input type="checkbox" autocomplete="off" name="exclude_serie" id="exclude_serie" value="{{serie.id}}">{{serie.name}}</input>
</label> </label>
{% endfor %} {% endfor %}
@ -69,7 +69,7 @@
<div class="form-group"> <div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons"> <div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for language in languages %} {% for language in languages %}
<label id="language_{{language.id}}" class="btn btn-danger language_click"> <label id="exclude_language_{{language.id}}" class="btn btn-danger language_click">
<input type="checkbox" autocomplete="off" name="exclude_language" id="exclude_language" value="{{language.id}}">{{language.name}}</input> <input type="checkbox" autocomplete="off" name="exclude_language" id="exclude_language" value="{{language.id}}">{{language.name}}</input>
</label> </label>
{% endfor %} {% endfor %}

View File

@ -415,6 +415,14 @@ def formatdate(val):
return format_date(formatdate, format='medium', locale=get_locale()) return format_date(formatdate, format='medium', locale=get_locale())
@app.template_filter('formatdateinput')
def format_date_input(val):
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val)
date_obj = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S")
input_date = date_obj.isoformat().split('T', 1)[0] # Hack to support dates <1900
return '' if input_date == "0101-01-01" else input_date
@app.template_filter('strftime') @app.template_filter('strftime')
def timestamptodate(date, fmt=None): def timestamptodate(date, fmt=None):
date = datetime.datetime.fromtimestamp( date = datetime.datetime.fromtimestamp(
@ -2689,195 +2697,208 @@ def edit_book(book_id):
lang_filter = True lang_filter = True
book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(lang_filter).first() book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(lang_filter).first()
author_names = [] author_names = []
if book:
for index in range(0, len(book.languages)): # Book not found
try: if not book:
book.languages[index].language_name = LC.parse(book.languages[index].lang_code).get_language_name( flash(_(u"Error opening eBook. File does not exist or file is not accessible"), category="error")
get_locale()) return redirect(url_for("index"))
except Exception:
book.languages[index].language_name = _(isoLanguages.get(part3=book.languages[index].lang_code).name) for index in range(0, len(book.languages)):
for author in book.authors: try:
author_names.append(author.name) book.languages[index].language_name = LC.parse(book.languages[index].lang_code).get_language_name(
if request.method == 'POST': get_locale())
edited_books_id = set() except Exception:
to_save = request.form.to_dict() book.languages[index].language_name = _(isoLanguages.get(part3=book.languages[index].lang_code).name)
if book.title != to_save["book_title"]: for author in book.authors:
book.title = to_save["book_title"] author_names.append(author.name)
edited_books_id.add(book.id)
input_authors = to_save["author_name"].split('&') # Show form
input_authors = map(lambda it: it.strip(), input_authors) if request.method != 'POST':
# we have all author names now return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc,
if input_authors == ['']: title=_(u"edit metadata"))
input_authors = [_(u'unknown')] # prevent empty Author
if book.authors: # Update book
author0_before_edit = book.authors[0].name edited_books_id = set()
to_save = request.form.to_dict()
if book.title != to_save["book_title"]:
book.title = to_save["book_title"]
edited_books_id.add(book.id)
input_authors = to_save["author_name"].split('&')
input_authors = map(lambda it: it.strip(), input_authors)
# we have all author names now
if input_authors == ['']:
input_authors = [_(u'unknown')] # prevent empty Author
if book.authors:
author0_before_edit = book.authors[0].name
else:
author0_before_edit = db.Authors(_(u'unknown'),'',0)
modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author')
if book.authors:
if author0_before_edit != book.authors[0].name:
edited_books_id.add(book.id)
book.author_sort = helper.get_sorted_author(input_authors[0])
if to_save["cover_url"] and os.path.splitext(to_save["cover_url"])[1].lower() == ".jpg":
img = requests.get(to_save["cover_url"])
if config.config_use_google_drive:
tmpDir = tempfile.gettempdir()
f = open(os.path.join(tmpDir, "uploaded_cover.jpg"), "wb")
f.write(img.content)
f.close()
gdriveutils.uploadFileToEbooksFolder(Gdrive.Instance().drive, os.path.join(book.path, 'cover.jpg'), os.path.join(tmpDir, f.name))
else:
f = open(os.path.join(config.config_calibre_dir, book.path, "cover.jpg"), "wb")
f.write(img.content)
f.close()
book.has_cover = 1
if book.series_index != to_save["series_index"]:
book.series_index = to_save["series_index"]
if len(book.comments):
book.comments[0].text = to_save["description"]
else:
book.comments.append(db.Comments(text=to_save["description"], book=book.id))
input_tags = to_save["tags"].split(',')
input_tags = map(lambda it: it.strip(), input_tags)
modify_database_object(input_tags, book.tags, db.Tags, db.session, 'tags')
input_series = [to_save["series"].strip()]
input_series = [x for x in input_series if x != '']
modify_database_object(input_series, book.series, db.Series, db.session, 'series')
input_languages = to_save["languages"].split(',')
input_languages = map(lambda it: it.strip().lower(), input_languages)
if to_save["pubdate"]:
try:
book.pubdate = datetime.datetime.strptime(to_save["pubdate"], "%Y-%m-%d")
except ValueError:
book.pubdate = db.Books.DEFAULT_PUBDATE
else:
book.pubdate = db.Books.DEFAULT_PUBDATE
# retranslate displayed text to language codes
languages = db.session.query(db.Languages).all()
input_l = []
for lang in languages:
try:
lang.name = LC.parse(lang.lang_code).get_language_name(get_locale()).lower()
except Exception:
lang.name = _(isoLanguages.get(part3=lang.lang_code).name).lower()
for inp_lang in input_languages:
if inp_lang == lang.name:
input_l.append(lang.lang_code)
modify_database_object(input_l, book.languages, db.Languages, db.session, 'languages')
if to_save["rating"].strip():
old_rating = False
if len(book.ratings) > 0:
old_rating = book.ratings[0].rating
ratingx2 = int(float(to_save["rating"]) * 2)
if ratingx2 != old_rating:
is_rating = db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first()
if is_rating:
book.ratings.append(is_rating)
else: else:
author0_before_edit = db.Authors(_(u'unknown'),'',0) new_rating = db.Ratings(rating=ratingx2)
modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author') book.ratings.append(new_rating)
if book.authors: if old_rating:
if author0_before_edit != book.authors[0].name: book.ratings.remove(book.ratings[0])
edited_books_id.add(book.id) else:
book.author_sort = helper.get_sorted_author(input_authors[0]) if len(book.ratings) > 0:
book.ratings.remove(book.ratings[0])
if to_save["cover_url"] and os.path.splitext(to_save["cover_url"])[1].lower() == ".jpg": for c in cc:
img = requests.get(to_save["cover_url"]) cc_string = "custom_column_" + str(c.id)
if config.config_use_google_drive: if not c.is_multiple:
tmpDir = tempfile.gettempdir() if len(getattr(book, cc_string)) > 0:
f = open(os.path.join(tmpDir, "uploaded_cover.jpg"), "wb") cc_db_value = getattr(book, cc_string)[0].value
f.write(img.content)
f.close()
gdriveutils.uploadFileToEbooksFolder(Gdrive.Instance().drive, os.path.join(book.path, 'cover.jpg'), os.path.join(tmpDir, f.name))
else:
f = open(os.path.join(config.config_calibre_dir, book.path, "cover.jpg"), "wb")
f.write(img.content)
f.close()
book.has_cover = 1
if book.series_index != to_save["series_index"]:
book.series_index = to_save["series_index"]
if len(book.comments):
book.comments[0].text = to_save["description"]
else: else:
book.comments.append(db.Comments(text=to_save["description"], book=book.id)) cc_db_value = None
if to_save[cc_string].strip():
input_tags = to_save["tags"].split(',') if c.datatype == 'bool':
input_tags = map(lambda it: it.strip(), input_tags) if to_save[cc_string] == 'None':
modify_database_object(input_tags, book.tags, db.Tags, db.session, 'tags') to_save[cc_string] = None
input_series = [to_save["series"].strip()]
input_series = [x for x in input_series if x != '']
modify_database_object(input_series, book.series, db.Series, db.session, 'series')
input_languages = to_save["languages"].split(',')
input_languages = map(lambda it: it.strip().lower(), input_languages)
# retranslate displayed text to language codes
languages = db.session.query(db.Languages).all()
input_l = []
for lang in languages:
try:
lang.name = LC.parse(lang.lang_code).get_language_name(get_locale()).lower()
except Exception:
lang.name = _(isoLanguages.get(part3=lang.lang_code).name).lower()
for inp_lang in input_languages:
if inp_lang == lang.name:
input_l.append(lang.lang_code)
modify_database_object(input_l, book.languages, db.Languages, db.session, 'languages')
if to_save["rating"].strip():
old_rating = False
if len(book.ratings) > 0:
old_rating = book.ratings[0].rating
ratingx2 = int(float(to_save["rating"]) * 2)
if ratingx2 != old_rating:
is_rating = db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first()
if is_rating:
book.ratings.append(is_rating)
else: else:
new_rating = db.Ratings(rating=ratingx2) to_save[cc_string] = 1 if to_save[cc_string] == 'True' else 0
book.ratings.append(new_rating) if to_save[cc_string] != cc_db_value:
if old_rating: if cc_db_value is not None:
book.ratings.remove(book.ratings[0]) if to_save[cc_string] is not None:
else: setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string])
if len(book.ratings) > 0:
book.ratings.remove(book.ratings[0])
for c in cc:
cc_string = "custom_column_" + str(c.id)
if not c.is_multiple:
if len(getattr(book, cc_string)) > 0:
cc_db_value = getattr(book, cc_string)[0].value
else:
cc_db_value = None
if to_save[cc_string].strip():
if c.datatype == 'bool':
if to_save[cc_string] == 'None':
to_save[cc_string] = None
else: else:
to_save[cc_string] = 1 if to_save[cc_string] == 'True' else 0 del_cc = getattr(book, cc_string)[0]
if to_save[cc_string] != cc_db_value: getattr(book, cc_string).remove(del_cc)
if cc_db_value is not None: db.session.delete(del_cc)
if to_save[cc_string] is not None:
setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string])
else:
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
db.session.delete(del_cc)
else:
cc_class = db.cc_classes[c.id]
new_cc = cc_class(value=to_save[cc_string], book=book_id)
db.session.add(new_cc)
elif c.datatype == 'int':
if to_save[cc_string] == 'None':
to_save[cc_string] = None
if to_save[cc_string] != cc_db_value:
if cc_db_value is not None:
if to_save[cc_string] is not None:
setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string])
else:
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
db.session.delete(del_cc)
else:
cc_class = db.cc_classes[c.id]
new_cc = cc_class(value=to_save[cc_string], book=book_id)
db.session.add(new_cc)
else: else:
if c.datatype == 'rating': cc_class = db.cc_classes[c.id]
to_save[cc_string] = str(int(float(to_save[cc_string]) * 2)) new_cc = cc_class(value=to_save[cc_string], book=book_id)
if to_save[cc_string].strip() != cc_db_value: db.session.add(new_cc)
if cc_db_value is not None: elif c.datatype == 'int':
# remove old cc_val if to_save[cc_string] == 'None':
del_cc = getattr(book, cc_string)[0] to_save[cc_string] = None
getattr(book, cc_string).remove(del_cc) if to_save[cc_string] != cc_db_value:
if len(del_cc.books) == 0: if cc_db_value is not None:
db.session.delete(del_cc) if to_save[cc_string] is not None:
cc_class = db.cc_classes[c.id] setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string])
new_cc = db.session.query(cc_class).filter( else:
cc_class.value == to_save[cc_string].strip()).first() del_cc = getattr(book, cc_string)[0]
# if no cc val is found add it getattr(book, cc_string).remove(del_cc)
if new_cc is None: db.session.delete(del_cc)
new_cc = cc_class(value=to_save[cc_string].strip()) else:
db.session.add(new_cc) cc_class = db.cc_classes[c.id]
new_cc = db.session.query(cc_class).filter( new_cc = cc_class(value=to_save[cc_string], book=book_id)
cc_class.value == to_save[cc_string].strip()).first() db.session.add(new_cc)
# add cc value to book
getattr(book, cc_string).append(new_cc) else:
else: if c.datatype == 'rating':
to_save[cc_string] = str(int(float(to_save[cc_string]) * 2))
if to_save[cc_string].strip() != cc_db_value:
if cc_db_value is not None: if cc_db_value is not None:
# remove old cc_val # remove old cc_val
del_cc = getattr(book, cc_string)[0] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0: if len(del_cc.books) == 0:
db.session.delete(del_cc) db.session.delete(del_cc)
else: cc_class = db.cc_classes[c.id]
input_tags = to_save[cc_string].split(',') new_cc = db.session.query(cc_class).filter(
input_tags = map(lambda it: it.strip(), input_tags) cc_class.value == to_save[cc_string].strip()).first()
modify_database_object(input_tags, getattr(book, cc_string),db.cc_classes[c.id], db.session, 'custom') # if no cc val is found add it
db.session.commit() if new_cc is None:
author_names = [] new_cc = cc_class(value=to_save[cc_string].strip())
for author in book.authors: db.session.add(new_cc)
author_names.append(author.name) new_cc = db.session.query(cc_class).filter(
for b in edited_books_id: cc_class.value == to_save[cc_string].strip()).first()
if config.config_use_google_drive: # add cc value to book
helper.update_dir_structure_gdrive(b) getattr(book, cc_string).append(new_cc)
else:
helper.update_dir_stucture(b, config.config_calibre_dir)
if config.config_use_google_drive:
updateGdriveCalibreFromLocal()
if "detail_view" in to_save:
return redirect(url_for('show_book', book_id=book.id))
else: else:
return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc, if cc_db_value is not None:
title=_(u"edit metadata")) # remove old cc_val
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0:
db.session.delete(del_cc)
else: else:
return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc, input_tags = to_save[cc_string].split(',')
title=_(u"edit metadata")) input_tags = map(lambda it: it.strip(), input_tags)
modify_database_object(input_tags, getattr(book, cc_string),db.cc_classes[c.id], db.session, 'custom')
db.session.commit()
author_names = []
for author in book.authors:
author_names.append(author.name)
for b in edited_books_id:
if config.config_use_google_drive:
helper.update_dir_structure_gdrive(b)
else:
helper.update_dir_stucture(b, config.config_calibre_dir)
if config.config_use_google_drive:
updateGdriveCalibreFromLocal()
if "detail_view" in to_save:
return redirect(url_for('show_book', book_id=book.id))
else: else:
flash(_(u"Error opening eBook. File does not exist or file is not accessible"), category="error") return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc,
return redirect(url_for("index")) title=_(u"edit metadata"))
@app.route("/upload", methods=["GET", "POST"]) @app.route("/upload", methods=["GET", "POST"])

View File

@ -10,3 +10,4 @@ requests>=2.11.1
SQLAlchemy>=0.8.4 SQLAlchemy>=0.8.4
tornado>=4.1 tornado>=4.1
Wand>=0.4.4 Wand>=0.4.4
unidecode>=0.04.19