diff --git a/cps/epub.py b/cps/epub.py index e6945d75..f9d46362 100644 --- a/cps/epub.py +++ b/cps/epub.py @@ -7,13 +7,14 @@ import os import uploader -def extractCover(zip, coverFile, tmp_file_name): +def extractCover(zip, coverFile, coverpath, tmp_file_name): if coverFile is None: return None else: - cf = zip.read("OPS/" + coverFile) + zipCoverPath = os.path.join(coverpath , coverFile).replace('\\','/') + cf = zip.read(zipCoverPath) prefix = os.path.splitext(tmp_file_name)[0] - tmp_cover_name = prefix + "." + coverFile + tmp_cover_name = prefix + '.' + os.path.basename(zipCoverPath) image = open(tmp_cover_name, 'wb') image.write(cf) image.close() @@ -32,10 +33,11 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension): txt = zip.read('META-INF/container.xml') tree = etree.fromstring(txt) cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path', namespaces=ns)[0] - cf = zip.read(cfname) tree = etree.fromstring(cf) + coverpath=os.path.dirname(cfname) + p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0] epub_metadata = {} @@ -46,11 +48,16 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension): else: epub_metadata[s] = "Unknown" - coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover']/@href", namespaces=ns) + coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover-image']/@href", namespaces=ns) if len(coversection) > 0: - coverfile = extractCover(zip, coversection[0], tmp_file_path) + coverfile = extractCover(zip, coversection[0], coverpath, tmp_file_path) else: - coverfile = None + coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover']/@href", namespaces=ns) + if len(coversection) > 0: + coverfile = extractCover(zip, coversection[0], coverpath, tmp_file_path) + else: + coverfile = None + if epub_metadata['title'] is None: title = original_file_name else: diff --git a/cps/helper.py b/cps/helper.py index 5e9c9210..54fa1946 100755 --- a/cps/helper.py +++ b/cps/helper.py @@ -19,6 +19,8 @@ from email.MIMEBase import MIMEBase from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText from email.generator import Generator +from email.utils import formatdate +from email.utils import make_msgid from flask_babel import gettext as _ import subprocess import threading @@ -165,6 +167,8 @@ def send_mail(book_id, kindle_mail, calibrepath): # create MIME message msg = MIMEMultipart() msg['Subject'] = _(u'Send to Kindle') + msg['Message-Id'] = make_msgid('calibre-web') + msg['Date'] = formatdate(localtime=True) text = _(u'This email has been sent via calibre web.') msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8')) diff --git a/cps/static/js/main.js b/cps/static/js/main.js index 74e1a1d8..8b0b099f 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -36,7 +36,7 @@ $(function() { success: function(data) { $('#spinner').show(); displaytext=data.text; - window.setTimeout(restartTimer, 3000);} + setTimeout(restartTimer, 3000);} }); }); $("#shutdown").click(function() { @@ -50,7 +50,7 @@ $(function() { }); $("#check_for_update").click(function() { var button_text = $("#check_for_update").html(); - $("#check_for_update").html('Checking...'); + $("#check_for_update").html('...'); $.ajax({ dataType: 'json', url: window.location.pathname+"/../../get_update_status", @@ -110,7 +110,8 @@ function updateTimer() { $('#UpdateprogressDialog #updateFinished').removeClass('hidden'); $("#check_for_update").removeClass('hidden'); $("#perform_update").addClass('hidden'); - } + }, + timeout:2000 }); } diff --git a/cps/templates/detail.html b/cps/templates/detail.html index 03c7258b..9920046d 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -144,10 +144,10 @@ {% endif %} - {% if g.user.is_authenticated %} - {% if g.user.kindle_mail %} + {% if g.user.kindle_mail and g.user.is_authenticated %} {{_('Send to Kindle')}} {% endif %} + {% if (g.user.role_download() and g.user.is_anonymous()) or g.user.is_authenticated %}
{% endblock %} diff --git a/cps/templates/list.html b/cps/templates/list.html index d424d495..0a63b139 100644 --- a/cps/templates/list.html +++ b/cps/templates/list.html @@ -2,11 +2,17 @@ {% block body %}

{{title}}

+
{% for entry in entries %} + {% if loop.index0 == (loop.length/2)|int and loop.length > 20 %} +
+
+ {% endif %}
{{entry.count}}
{% endfor %}
+
{% endblock %} diff --git a/cps/templates/stats.html b/cps/templates/stats.html index 22826003..4b08afda 100644 --- a/cps/templates/stats.html +++ b/cps/templates/stats.html @@ -1,7 +1,27 @@ {% extends "layout.html" %} {% block body %} +

{{_('Calibre library statistics')}}

+ + + + + + + + + + + + + + + + + + + +
{{bookcounter}}{{_('Books in this Library')}}
{{authorcounter}}{{_('Authors in this Library')}}
{{categorycounter}}{{_('Categories in this Library')}}
{{seriecounter}}{{_('Series in this Library')}}

{{_('Linked libraries')}}

- @@ -24,30 +44,51 @@ - + - -
PyPDF2{{versions['PyPdfVersion']}}v{{versions['PyPdfVersion']}}
- -

{{_('Calibre library statistics')}}

- - - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + +
{{bookcounter}}{{_('Books in this Library')}}Babelv{{versions['babel']}}
{{authorcounter}}{{_('Authors in this Library')}}SqlAlchemyv{{versions['sqlalchemy']}}
{{categorycounter}}{{_('Categories in this Library')}}Flaskv{{versions['flask']}}
{{seriecounter}}{{_('Series in this Library')}}Flask Loginv{{versions['flasklogin']}}
Flask Principalv{{versions['flask_principal']}}
Tornado web serverv{{versions['tornado']}}
ISO639 Languagesv{{versions['iso639']}}
Requestsv{{versions['requests']}}
SQlitev{{versions['sqlite']}}
Pysqlitev{{versions['pysqlite']}}
+ {% endblock %} + + diff --git a/cps/templates/user_edit.html b/cps/templates/user_edit.html index 7cb4155d..2e006ad0 100644 --- a/cps/templates/user_edit.html +++ b/cps/templates/user_edit.html @@ -40,6 +40,8 @@ {% endfor %} +
+
@@ -76,7 +78,8 @@
- +
+
{% if g.user and g.user.role_admin() and not profile %} {% if not content.role_anonymous() %}
@@ -110,13 +113,17 @@
{% endif %} +
+
{% if not profile %} {{_('Back')}} +
{% endif %} {% if downloads %} +

{{_('Recent Downloads')}}

{% for entry in downloads %}
@@ -125,6 +132,7 @@
{% endfor %} +
{% endif %} {% endblock %} diff --git a/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.mo b/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.mo index d3a83c0d..5cb1b1c9 100644 Binary files a/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.mo and b/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.mo differ diff --git a/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po b/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po index 5356f3b2..c68904f6 100644 --- a/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po +++ b/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po @@ -61,31 +61,31 @@ msgstr "游客" #: cps/web.py:734 msgid "Requesting update package" -msgstr "" +msgstr "正在请求更新包" #: cps/web.py:735 msgid "Downloading update package" -msgstr "" +msgstr "正在下载更新包" #: cps/web.py:736 msgid "Unzipping update package" -msgstr "" +msgstr "正在解压更新包" #: cps/web.py:737 msgid "Files are replaced" -msgstr "" +msgstr "文件已替换" #: cps/web.py:738 msgid "Database connections are closed" -msgstr "" +msgstr "数据库连接已关闭" #: cps/web.py:739 msgid "Server is stopped" -msgstr "" +msgstr "服务器已停止" #: cps/web.py:740 msgid "Update finished, please press okay and reload page" -msgstr "" +msgstr "更新完成,请按确定并刷新页面" #: cps/web.py:810 msgid "Latest Books" @@ -97,7 +97,7 @@ msgstr "热门书籍(最多下载)" #: cps/web.py:845 msgid "Best rated books" -msgstr "" +msgstr "最高评分书籍" #: cps/templates/index.xml:36 cps/web.py:854 msgid "Random Books" @@ -108,9 +108,9 @@ msgid "Author list" msgstr "作者列表" #: cps/web.py:878 -#, python-format +#, python-forma msgid "Author: %(name)s" -msgstr "" +msgstr "作者: %(name)s" #: cps/web.py:880 cps/web.py:908 cps/web.py:1007 cps/web.py:1235 #: cps/web.py:2115 @@ -150,7 +150,7 @@ msgstr "统计" #: cps/web.py:1061 msgid "Server restarted, please reload page" -msgstr "" +msgstr "服务器已重启,请刷新页面" #: cps/web.py:1063 msgid "Performing shutdown of server, please close window" @@ -158,7 +158,7 @@ msgstr "正在关闭服务器,请关闭窗口" #: cps/web.py:1073 msgid "Update done" -msgstr "" +msgstr "更新完成" #: cps/web.py:1147 cps/web.py:1160 msgid "search" @@ -475,11 +475,11 @@ msgstr "管理" #: cps/templates/admin.html:80 msgid "Current commit timestamp" -msgstr "" +msgstr "当前提交时间戳" #: cps/templates/admin.html:81 msgid "Newest commit timestamp" -msgstr "" +msgstr "最新提交时间戳" #: cps/templates/admin.html:83 msgid "Restart Calibre-web" @@ -491,11 +491,11 @@ msgstr "停止 Calibre-web" #: cps/templates/admin.html:85 msgid "Check for update" -msgstr "" +msgstr "检查更新" #: cps/templates/admin.html:86 msgid "Perform Update" -msgstr "" +msgstr "执行更新" #: cps/templates/admin.html:96 msgid "Do you really want to restart Calibre-web?" @@ -519,7 +519,7 @@ msgstr "您确定要关闭 Calibre-web 吗?" #: cps/templates/admin.html:127 msgid "Updating, please do not reload page" -msgstr "" +msgstr "正在更新,请不要刷新页面" #: cps/templates/book_edit.html:16 cps/templates/search_form.html:6 msgid "Book Title" @@ -610,7 +610,7 @@ msgstr "启用注册" #: cps/templates/config_edit.html:52 msgid "Default Settings for new users" -msgstr "" +msgstr "新用户默认设置" #: cps/templates/config_edit.html:55 cps/templates/user_edit.html:80 msgid "Admin user" @@ -651,7 +651,7 @@ msgstr "语言" #: cps/templates/detail.html:74 msgid "Publishing date" -msgstr "" +msgstr "出版日期" #: cps/templates/detail.html:106 msgid "Description:" @@ -723,11 +723,11 @@ msgstr "热门书籍" #: cps/templates/index.xml:19 msgid "Popular publications from this catalog based on Downloads." -msgstr "" +msgstr "基于下载数的热门书籍" #: cps/templates/index.xml:22 cps/templates/layout.html:129 msgid "Best rated Books" -msgstr "" +msgstr "最高评分书籍" #: cps/templates/index.xml:26 msgid "Popular publications from this catalog based on Rating." @@ -933,11 +933,11 @@ msgstr "个作者在此书库" #: cps/templates/stats.html:45 msgid "Categories in this Library" -msgstr "" +msgstr "个分类在此书库" #: cps/templates/stats.html:49 msgid "Series in this Library" -msgstr "" +msgstr "个丛书在此书库" #: cps/templates/user_edit.html:23 msgid "Kindle E-Mail" @@ -961,7 +961,7 @@ msgstr "显示热门书籍" #: cps/templates/user_edit.html:53 msgid "Show best rated books" -msgstr "" +msgstr "显示最高评分书籍" #: cps/templates/user_edit.html:57 msgid "Show language selection" diff --git a/cps/ub.py b/cps/ub.py index d5063866..aa064282 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -7,7 +7,6 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import * from flask_login import AnonymousUserMixin import os -import traceback import logging from werkzeug.security import generate_password_hash from flask_babel import gettext as _ diff --git a/cps/web.py b/cps/web.py index e8b2af88..6b7a329d 100755 --- a/cps/web.py +++ b/cps/web.py @@ -8,6 +8,7 @@ from logging.handlers import RotatingFileHandler import textwrap from flask import Flask, render_template, session, request, Response, redirect, url_for, send_from_directory, \ make_response, g, flash, abort, send_file +from flask import __version__ as flaskVersion import ub from ub import config import helper @@ -16,9 +17,12 @@ import errno from sqlalchemy.sql.expression import func from sqlalchemy.sql.expression import false from sqlalchemy.exc import IntegrityError +from sqlalchemy import __version__ as sqlalchemyVersion from math import ceil from flask_login import LoginManager, login_user, logout_user, login_required, current_user +from flask_login import __version__ as flask_loginVersion from flask_principal import Principal, Identity, AnonymousIdentity, identity_changed +from flask_login import __version__ as flask_principalVersion from flask_babel import Babel from flask_babel import gettext as _ import requests @@ -26,6 +30,7 @@ import zipfile from werkzeug.security import generate_password_hash, check_password_hash from babel import Locale as LC from babel import negotiate_locale +from babel import __version__ as babelVersion from babel.dates import format_date from functools import wraps import base64 @@ -34,13 +39,13 @@ import json import urllib import datetime from iso639 import languages as isoLanguages +from iso639 import __version__ as iso639Version from uuid import uuid4 import os.path import sys import subprocess import re import db -import thread from shutil import move, copyfile from tornado.ioloop import IOLoop import shutil @@ -50,6 +55,8 @@ import io import hashlib import threading +from tornado import version as tornadoVersion + import time current_milli_time = lambda: int(round(time.time() * 1000)) @@ -66,6 +73,8 @@ from cgi import escape gdrive_watch_callback_token='target=calibreweb-watch_files' global_task = None +ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx', 'fb2']) + def md5(fname): hash_md5 = hashlib.md5() with open(fname, "rb") as f: @@ -1162,6 +1171,17 @@ def stats(): if re.search('Amazon kindlegen\(', lines): versions['KindlegenVersion'] = lines versions['PythonVersion'] = sys.version + versions['babel'] = babelVersion + versions['sqlalchemy'] = sqlalchemyVersion + versions['flask'] = flaskVersion + versions['flasklogin'] = flask_loginVersion + versions['flask_principal'] = flask_principalVersion + versions['tornado'] = tornadoVersion + versions['iso639'] = iso639Version + versions['requests'] = requests.__version__ + versions['pysqlite'] = db.engine.dialect.dbapi.version + versions['sqlite'] = db.engine.dialect.dbapi.sqlite_version + return render_title_template('stats.html', bookcounter=counter, authorcounter=authors, versions=versions, categorycounter=categorys, seriecounter=series, title=_(u"Statistics")) @@ -1457,7 +1477,7 @@ def unread_books(page): return render_read_books(page, False) @app.route("/read//") -@login_required +@login_required_if_no_ano def read_book(book_id, format): book = db.session.query(db.Books).filter(db.Books.id == book_id).first() if book: @@ -2105,6 +2125,8 @@ def edit_mailsettings(): category="success") else: flash(_(u"There was an error sending the Test E-Mail: %(res)s", res=result), category="error") + else: + flash(_(u"E-Mail settings updated"), category="success") return render_title_template("email_edit.html", content=content, title=_(u"Edit mail settings")) @@ -2448,6 +2470,18 @@ def upload(): db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4())) if request.method == 'POST' and 'btn-upload' in request.files: file = request.files['btn-upload'] + if '.' in file.filename: + file_ext = file.filename.rsplit('.', 1)[-1].lower() + if file_ext not in ALLOWED_EXTENSIONS: + flash( + _('File extension "%s" is not allowed to be uploaded to this server' % + file_ext), + category="error" + ) + return redirect(url_for('index')) + else: + flash(_('File to be uploaded must have an extension'), category="error") + return redirect(url_for('index')) meta = uploader.upload(file) title = meta.title