Commit 1e03a2ae authored by Ozzieisaacs's avatar Ozzieisaacs

Merge branch 'master' into Develop

parents df295e92 25da6aee
......@@ -12,7 +12,7 @@ Calibre-Web is a web app providing a clean interface for browsing, reading and d
- full graphical setup
- User management with fine-grained per-user permissions
- Admin interface
- User Interface in czech, dutch, english, finnish, french, german, hungarian, italian, japanese, khmer, polish, russian, simplified chinese, spanish, swedish, turkish, ukrainian
- User Interface in czech, dutch, english, finnish, french, german, greek, hungarian, italian, japanese, khmer, polish, russian, simplified chinese, spanish, swedish, turkish, ukrainian
- OPDS feed for eBook reader apps
- Filter and search by titles, authors, tags, series and language
- Create a custom book collection (shelves)
......
......@@ -799,7 +799,7 @@ def _handle_new_user(to_save, content,languages, translations, kobo_support):
flash(_(u"Settings DB is not Writeable"), category="error")
def _handle_edit_user(to_save, content,languages, translations, kobo_support, downloads):
def _handle_edit_user(to_save, content,languages, translations, kobo_support):
if "delete" in to_save:
if ub.session.query(ub.User).filter(ub.User.role.op('&')(constants.ROLE_ADMIN) == constants.ROLE_ADMIN,
ub.User.id != content.id).count():
......@@ -858,7 +858,6 @@ def _handle_edit_user(to_save, content,languages, translations, kobo_support, do
kobo_support=kobo_support,
new_user=0,
content=content,
downloads=downloads,
registered_oauth=oauth_check,
title=_(u"Edit User %(nick)s", nick=content.nickname), page="edituser")
if "nickname" in to_save and to_save["nickname"] != content.nickname:
......@@ -872,7 +871,6 @@ def _handle_edit_user(to_save, content,languages, translations, kobo_support, do
languages=languages,
mail_configured=config.get_mail_server_configured(),
new_user=0, content=content,
downloads=downloads,
registered_oauth=oauth_check,
kobo_support=kobo_support,
title=_(u"Edit User %(nick)s", nick=content.nickname),
......@@ -961,28 +959,20 @@ def update_mailsettings():
@admin_required
def edit_user(user_id):
content = ub.session.query(ub.User).filter(ub.User.id == int(user_id)).first() # type: ub.User
if not content:
if not content or (not config.config_anonbrowse and content.nickname == "Guest"):
flash(_(u"User not found"), category="error")
return redirect(url_for('admin.admin'))
downloads = list()
languages = calibre_db.speaking_language()
translations = babel.list_translations() + [LC('en')]
kobo_support = feature_support['kobo'] and config.config_kobo_sync
for book in content.downloads:
downloadbook = calibre_db.get_book(book.book_id)
if downloadbook:
downloads.append(downloadbook)
else:
ub.delete_download(book.book_id)
if request.method == "POST":
to_save = request.form.to_dict()
_handle_edit_user(to_save, content, languages, translations, kobo_support, downloads)
_handle_edit_user(to_save, content, languages, translations, kobo_support)
return render_title_template("user_edit.html",
translations=translations,
languages=languages,
new_user=0,
content=content,
downloads=downloads,
registered_oauth=oauth_check,
mail_configured=config.get_mail_server_configured(),
kobo_support=kobo_support,
......@@ -1039,7 +1029,8 @@ def send_logfile(logtype):
@admi.route("/get_update_status", methods=['GET'])
@login_required_if_no_ano
@login_required
@admin_required
def get_update_status():
log.info(u"Update status requested")
return updater_thread.get_available_updates(request.method, locale=get_locale())
......
......@@ -19,7 +19,6 @@
from __future__ import division, print_function, unicode_literals
import os
import json
import sys
from sqlalchemy import exc, Column, String, Integer, SmallInteger, Boolean, BLOB, JSON
......
......@@ -19,7 +19,6 @@
from __future__ import division, print_function, unicode_literals
import os
import re
import sys
from flask_babel import gettext as _
from . import config, logger
......
......@@ -147,7 +147,7 @@ class Identifiers(Base):
elif format_type == "kobo":
return u"https://www.kobo.com/ebook/{0}".format(self.val)
elif format_type == "lubimyczytac":
return u" https://lubimyczytac.pl/ksiazka/{0}/ksiazka".format(self.val)
return u"https://lubimyczytac.pl/ksiazka/{0}/ksiazka".format(self.val)
elif format_type == "litres":
return u"https://www.litres.ru/{0}".format(self.val)
elif format_type == "issn":
......@@ -687,9 +687,7 @@ class CalibreDB():
limit_all = result_count
ub.store_ids(result)
return result[offset:limit_all], result_count, pagination,
return result[offset:limit_all], result_count, pagination
# Creates for all stored languages a translated speaking name in the array for the UI
def speaking_language(self, languages=None):
......
......@@ -27,7 +27,6 @@ import json
from shutil import copyfile
from uuid import uuid4
from babel import Locale as LC
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
from flask_babel import gettext as _
from flask_login import current_user, login_required
......@@ -930,6 +929,7 @@ def convert_bookformat(book_id):
@editbook.route("/ajax/editbooks/<param>", methods=['POST'])
@login_required_if_no_ano
@edit_required
def edit_list_book(param):
vals = request.form.to_dict()
# calibre_db.update_title_sort(config)
......@@ -989,6 +989,7 @@ def get_sorted_entry(field, bookid):
@editbook.route("/ajax/simulatemerge", methods=['POST'])
@login_required
@edit_required
def simulate_merge_list_book():
vals = request.get_json().get('Merge_books')
if vals:
......@@ -1004,6 +1005,7 @@ def simulate_merge_list_book():
@editbook.route("/ajax/mergebooks", methods=['POST'])
@login_required
@edit_required
def merge_list_book():
vals = request.get_json().get('Merge_books')
to_file = list()
......
......@@ -123,38 +123,36 @@ def revoke_watch_gdrive():
@gdrive.route("/gdrive/watch/callback", methods=['GET', 'POST'])
def on_received_watch_confirmation():
if request.headers.get('X-Goog-Channel-Token') != gdrive_watch_callback_token \
or request.headers.get('X-Goog-Resource-State') != 'change' \
or not request.data:
return redirect(url_for('admin.configuration'))
log.debug('%r', request.headers)
if request.headers.get('X-Goog-Channel-Token') == gdrive_watch_callback_token \
and request.headers.get('X-Goog-Resource-State') == 'change' \
and request.data:
data = request.data
def updateMetaData():
log.info('Change received from gdrive')
log.debug('%r', data)
try:
j = json.loads(data)
log.info('Getting change details')
response = gdriveutils.getChangeById(gdriveutils.Gdrive.Instance().drive, j['id'])
log.debug('%r', response)
if response:
if sys.version_info < (3, 0):
dbpath = os.path.join(config.config_calibre_dir, "metadata.db")
else:
dbpath = os.path.join(config.config_calibre_dir, "metadata.db").encode()
if not response['deleted'] and response['file']['title'] == 'metadata.db' \
and response['file']['md5Checksum'] != hashlib.md5(dbpath):
tmpDir = tempfile.gettempdir()
log.info('Database file updated')
copyfile(dbpath, os.path.join(tmpDir, "metadata.db_" + str(current_milli_time())))
log.info('Backing up existing and downloading updated metadata.db')
gdriveutils.downloadFile(None, "metadata.db", os.path.join(tmpDir, "tmp_metadata.db"))
log.info('Setting up new DB')
# prevent error on windows, as os.rename does on exisiting files
move(os.path.join(tmpDir, "tmp_metadata.db"), dbpath)
calibre_db.reconnect_db(config, ub.app_DB_path)
except Exception as e:
log.exception(e)
updateMetaData()
log.debug('%r', request.data)
log.info('Change received from gdrive')
try:
j = json.loads(request.data)
log.info('Getting change details')
response = gdriveutils.getChangeById(gdriveutils.Gdrive.Instance().drive, j['id'])
log.debug('%r', response)
if response:
if sys.version_info < (3, 0):
dbpath = os.path.join(config.config_calibre_dir, "metadata.db")
else:
dbpath = os.path.join(config.config_calibre_dir, "metadata.db").encode()
if not response['deleted'] and response['file']['title'] == 'metadata.db' \
and response['file']['md5Checksum'] != hashlib.md5(dbpath):
tmpDir = tempfile.gettempdir()
log.info('Database file updated')
copyfile(dbpath, os.path.join(tmpDir, "metadata.db_" + str(current_milli_time())))
log.info('Backing up existing and downloading updated metadata.db')
gdriveutils.downloadFile(None, "metadata.db", os.path.join(tmpDir, "tmp_metadata.db"))
log.info('Setting up new DB')
# prevent error on windows, as os.rename does on exisiting files
move(os.path.join(tmpDir, "tmp_metadata.db"), dbpath)
calibre_db.reconnect_db(config, ub.app_DB_path)
except Exception as e:
log.exception(e)
return ''
......@@ -35,7 +35,7 @@ from babel.units import format_unit
from flask import send_from_directory, make_response, redirect, abort, url_for
from flask_babel import gettext as _
from flask_login import current_user
from sqlalchemy.sql.expression import true, false, and_, text, func
from sqlalchemy.sql.expression import true, false, and_, text
from werkzeug.datastructures import Headers
from werkzeug.security import generate_password_hash
from . import calibre_db
......@@ -373,23 +373,26 @@ def update_dir_structure_file(book_id, calibrepath, first_author, orignal_filepa
new_name = get_valid_filename(localbook.title) + ' - ' + get_valid_filename(new_authordir)
try:
if orignal_filepath:
os.renames(os.path.normcase(path),
os.path.normcase(os.path.join(new_path, db_filename)))
if not os.path.isdir(new_path):
os.makedirs(new_path)
shutil.move(os.path.normcase(path), os.path.normcase(os.path.join(new_path, db_filename)))
log.debug("Moving title: %s to %s/%s", path, new_path, new_name)
# Check new path is not valid path
elif not os.path.exists(new_path):
# move original path to new path
os.renames(os.path.normcase(path), os.path.normcase(new_path))
log.debug("Moving title: %s to %s", path, new_path)
else: # path is valid copy only files to new location (merge)
log.info("Moving title: %s into existing: %s", path, new_path)
# Take all files and subfolder from old path (strange command)
for dir_name, __, file_list in os.walk(path):
for file in file_list:
os.renames(os.path.normcase(os.path.join(dir_name, file)),
os.path.normcase(os.path.join(new_path + dir_name[len(path):], file)))
# Check new path is not valid path
else:
if not os.path.exists(new_path):
# move original path to new path
log.debug("Moving title: %s to %s", path, new_path)
shutil.move(os.path.normcase(path), os.path.normcase(new_path))
else: # path is valid copy only files to new location (merge)
log.info("Moving title: %s into existing: %s", path, new_path)
# Take all files and subfolder from old path (strange command)
for dir_name, __, file_list in os.walk(path):
for file in file_list:
shutil.move(os.path.normcase(os.path.join(dir_name, file)),
os.path.normcase(os.path.join(new_path + dir_name[len(path):], file)))
# os.unlink(os.path.normcase(os.path.join(dir_name, file)))
# change location in database to new author/title path
localbook.path = os.path.join(new_authordir, new_titledir)
localbook.path = os.path.join(new_authordir, new_titledir).replace('\\','/')
except OSError as ex:
log.error("Rename title from: %s to %s: %s", path, new_path, ex)
log.debug(ex, exc_info=True)
......@@ -399,10 +402,12 @@ def update_dir_structure_file(book_id, calibrepath, first_author, orignal_filepa
# Rename all files from old names to new names
try:
for file_format in localbook.data:
os.renames(os.path.normcase(
shutil.move(os.path.normcase(
os.path.join(new_path, file_format.name + '.' + file_format.format.lower())),
os.path.normcase(os.path.join(new_path, new_name + '.' + file_format.format.lower())))
os.path.normcase(os.path.join(new_path, new_name + '.' + file_format.format.lower())))
file_format.name = new_name
if not orignal_filepath and len(os.listdir(os.path.dirname(path))) == 0:
shutil.rmtree(os.path.dirname(path))
except OSError as ex:
log.error("Rename file in path %s to %s: %s", new_path, new_name, ex)
log.debug(ex, exc_info=True)
......
This diff is collapsed.
......@@ -25,7 +25,6 @@
from __future__ import division, print_function, unicode_literals
import datetime
import mimetypes
import re
from babel.dates import format_date
from flask import Blueprint, request, url_for
......@@ -110,9 +109,21 @@ def timestamptodate(date, fmt=None):
def yesno(value, yes, no):
return yes if value else no
@jinjia.app_template_filter('formatfloat')
def formatfloat(value, decimals=1):
formatedstring = '%d' % value
if (value % 1) != 0:
formatedstring = ('%s.%d' % (formatedstring, (value % 1) * 10**decimals)).rstrip('0')
return formatedstring
@jinjia.app_template_filter('formatseriesindex')
def formatseriesindex_filter(series_index):
if series_index:
if int(series_index) - series_index == 0:
return int(series_index)
else:
return series_index
return 0
......@@ -39,7 +39,7 @@ from flask import (
redirect,
abort
)
from flask_login import current_user, login_required
from flask_login import current_user
from werkzeug.datastructures import Headers
from sqlalchemy import func
from sqlalchemy.sql.expression import and_, or_
......
......@@ -408,7 +408,7 @@ def get_metadata_calibre_companion(uuid, library):
def feed_search(term):
if term:
entries, __ = calibre_db.get_search_results(term)
entries, __, ___ = calibre_db.get_search_results(term)
entriescount = len(entries) if len(entries) > 0 else 1
pagination = Pagination(1, entriescount, entriescount)
return render_xml_template('feed.xml', searchterm=term, entries=entries, pagination=pagination)
......
......@@ -150,7 +150,7 @@ class WebServer(object):
def my_wrap_socket(*args, **kwargs):
try:
return wrap_socket(*args, **kwargs)
except (ssl.SSLError) as ex:
except (ssl.SSLError, OSError) as ex:
log.warning('Gevent SSL Error: %s', ex)
raise GreenletExit
......
......@@ -72,7 +72,7 @@ def get_author_info(author_name):
author_info = _client.find_author(author_name=author_name)
except Exception as ex:
# Skip goodreads, if site is down/inaccessible
log.warning('Goodreads website is down/inaccessible? %s', ex)
log.warning('Goodreads website is down/inaccessible? %s', ex.__str__())
return
if author_info:
......
......@@ -831,7 +831,10 @@ body:not(.read-frame) {
font-family: Open Sans Semibold, Helvetica Neue, Helvetica, Arial, sans-serif;
font-weight: 400;
overflow: hidden;
margin: 0
margin: 0;
/* scroll bar fix for firefox */
scrollbar-color: hsla(0, 0%, 100%, .2) transparent;
scrollbar-width: thin;
}
body > div.navbar.navbar-default.navbar-static-top > div > form > div {
......@@ -2174,7 +2177,7 @@ body.langlist > div.container-fluid > div > div.col-sm-10:before {
content: "\e034"
}
body.advsearch > div.container-fluid > div > div.col-sm-10:before, body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.catlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.langlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.me > div.container-fluid > div > div.col-sm-10 > div.discover:before, body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container:before {
body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.catlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.langlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.me > div.container-fluid > div > div.col-sm-10 > div.discover:before, body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container:before {
top: 60px;
font-size: 24px;
color: #eee;
......@@ -2244,10 +2247,14 @@ body.langlist > div.container-fluid > div > div.col-sm-10 > div.container:before
content: "Languages"
}
body.advsearch > div.container-fluid > div > div.col-sm-10:before {
content: "Advanced Search";
margin-left: 20%;
left: 0
body.advsearch > div.container-fluid > div > div.col-sm-10 > div.col-md-10.col-lg-6 {
padding: 15px 10px 15px 40px;
}
@media screen and (max-width: 992px) {
body.advsearch > div.container-fluid > div > div.col-sm-10 > div.col-md-10.col-lg-6 {
padding-left: 20px;
}
}
body.me > div.container-fluid > div > div.col-sm-10 > div.discover:before {
......@@ -3502,6 +3509,20 @@ body.shelf > div.container-fluid > div > div.col-sm-10 > div.discover > h2 {
padding-right: 25px !important
}
body.shelf-down > .discover > h2 {
color: inherit;
margin-bottom: 20px;
}
body.shelf-down > .discover > .row > .book.col-sm-3.col-lg-2.col-xs-6 {
max-width: 225px !important;
width: 225px !important;
}
body.shelf-down .btn-group button#btnGroupDrop1 {
height: 50px;
}
.author > .container-fluid > .row-fluid > .col-sm-10 > h2:before, .plexBack > a {
-moz-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
......
......@@ -143,6 +143,12 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d
color: #444;
}
.container-fluid .book .meta .series {
font-weight: 400;
font-size: 12px;
color: #444;
}
.container-fluid .book .meta .author {
font-size: 12px;
color: #999;
......
!function(a){a.fn.datepicker.dates.el={days:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],daysShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],daysMin:["Κυ","Δε","Τρ","Τε","Πε","Πα","Σα"],months:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],monthsShort:["Ιαν","Φεβ","Μαρ","Απρ","Μάι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ"],today:"Σήμερα",clear:"Καθαρισμός",weekStart:1,format:"d/m/yyyy"}}(jQuery);
\ No newline at end of file
!function(a){a.fn.datepicker.dates.tr={days:["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"],daysShort:["Pz","Pzt","Sal","Çrş","Prş","Cu","Cts"],daysMin:["Pz","Pzt","Sa","Çr","Pr","Cu","Ct"],months:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],monthsShort:["Oca","Şub","Mar","Nis","May","Haz","Tem","Ağu","Eyl","Eki","Kas","Ara"],today:"Bugün",clear:"Temizle",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
......@@ -8,7 +8,7 @@ from shutil import copyfile
from sqlalchemy.exc import SQLAlchemyError
from cps.services.worker import CalibreTask, STAT_FINISH_SUCCESS
from cps.services.worker import CalibreTask
from cps import calibre_db, db
from cps import logger, config
from cps.subproc_wrapper import process_open
......
......@@ -70,6 +70,14 @@
{% endif %}
{% endfor %}
</p>
{% if entry.series.__len__() > 0 %}
<p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='new', book_id=entry.series[0].id )}}">
{{entry.series[0].name}}
</a>
({{entry.series_index|formatseriesindex}})
</p>
{% endif %}
{% if entry.ratings.__len__() > 0 %}
<div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
......@@ -114,6 +122,14 @@
{% endif %}
{% endfor %}
</p>
{% if entry.series.__len__() > 0 %}
<p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='new', book_id=entry.series[0].id )}}">
{{entry.series[0].name}}
</a>
({{entry.series_index|formatseriesindex}})
</p>
{% endif %}
<div class="rating">
{% for number in range((entry.average_rating)|float|round|int(2)) %}
<span class="glyphicon glyphicon-star good"></span>
......
......@@ -34,6 +34,14 @@
{% endif %}
{% endfor %}
</p>
{% if entry.series.__len__() > 0 %}
<p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='new', book_id=entry.series[0].id )}}">
{{entry.series[0].name}}
</a>
({{entry.series_index|formatseriesindex}})
</p>
{% endif %}
{% if entry.ratings.__len__() > 0 %}
<div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
......
......@@ -13,7 +13,7 @@
<link href="{{ url_for('static', filename='css/libs/bootstrap.min.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" media="screen">
{% if g.current_theme == 1 %}
<link href="{{ url_for('static', filename='css/caliBlur.min.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='css/caliBlur.css') }}" rel="stylesheet" media="screen">
{% endif %}
</head>
<body>
......
......@@ -33,6 +33,14 @@
{% endif %}
{% endfor %}
</p>
{% if entry.series.__len__() > 0 %}
<p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='new', book_id=entry.series[0].id )}}">
{{entry.series[0].name}}
</a>
({{entry.series_index|formatseriesindex}})
</p>
{% endif %}
{% if entry.ratings.__len__() > 0 %}
<div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
......@@ -101,6 +109,14 @@
{% endif %}
{%endfor%}
</p>
{% if entry.series.__len__() > 0 %}
<p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='new', book_id=entry.series[0].id )}}">
{{entry.series[0].name}}
</a>
({{entry.series_index|formatseriesindex}})
</p>
{% endif %}
{% if entry.ratings.__len__() > 0 %}
<div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
......
......@@ -16,7 +16,7 @@
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='css/upload.css') }}" rel="stylesheet" media="screen">
{% if g.current_theme == 1 %}
<link href="{{ url_for('static', filename='css/caliBlur.min.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='css/caliBlur.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='css/caliBlur_override.css') }}" rel="stylesheet" media="screen">
{% endif %}
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
......
......@@ -72,6 +72,15 @@
{% endif %}
{% endfor %}
</p>
{% if entry.series.__len__() > 0 %}
<p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='new', book_id=entry.series[0].id )}}">
{{entry.series[0].name}}
</a>
({{entry.series_index|formatseriesindex}})
</p>
{% endif %}
{% if entry.ratings.__len__() > 0 %}
<div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
......
{% extends "layout.html" %}
{% block body %}
<h1 class="{{page}}">{{title}}</h1>
<div class="col-md-10 col-lg-6">
<form role="form" id="search" action="{{ url_for('web.advanced_search_form') }}" method="POST">
<div class="form-group">
......
......@@ -43,6 +43,14 @@
{% endif %}
{% endfor %}
</p>
{% if entry.series.__len__() > 0 %}
<p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='new', book_id=entry.series[0].id )}}">
{{entry.series[0].name}}
</a>
({{entry.series_index|formatseriesindex}})
</p>
{% endif %}
{% if entry.ratings.__len__() > 0 %}
<div class="rating">
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
......
......@@ -12,8 +12,9 @@
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
<link href="{{ url_for('static', filename='css/libs/bootstrap.min.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" media="screen">
{% if g.user.get_theme == 1 %}
<link href="{{ url_for('static', filename='css/caliBlur-style.css') }}" rel="stylesheet" media="screen">
{% if g.current_theme == 1 %}
<link href="{{ url_for('static', filename='css/caliBlur.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='css/caliBlur_override.css') }}" rel="stylesheet" media="screen">
{% endif %}
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
......@@ -24,7 +25,7 @@
{% block header %}{% endblock %}
</head>
<body class="{{ page }}">
<body class="{{ page }} shelf-down">
{% block body %}
<div class="discover">
<h2>{{title}}</h2>
......@@ -43,7 +44,14 @@
{% endif %}
{% endfor %}
</p>
{% if entry.series.__len__() > 0 %}
<p class="series">
<a href="{{url_for('web.books_list', data='series', sort_param='new', book_id=entry.series[0].id )}}">
{{entry.series[0].name}}
</a>
({{entry.series_index}})
</p>
{% endif %}
</div>
<div class="btn-group" role="group" aria-label="Download, send to Kindle, reading">
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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.
......@@ -41,7 +41,7 @@ from flask_login import login_user, logout_user, login_required, current_user, c
from sqlalchemy.exc import IntegrityError, InvalidRequestError, OperationalError
from sqlalchemy.sql.expression import text, func, true, false, not_, and_, or_
from sqlalchemy.orm.attributes import flag_modified
from werkzeug.exceptions import default_exceptions, InternalServerError
from werkzeug.exceptions import default_exceptions
from sqlalchemy.sql.functions import coalesce
from .services.worker import WorkerThread
......@@ -307,6 +307,8 @@ def before_request():
@app.route('/import_ldap_users')
@login_required
@admin_required
def import_ldap_users():
showtext = {}
try:
......@@ -624,6 +626,10 @@ def render_books_list(data, sort, book_id, page):
order = [db.Books.timestamp.desc()]
if sort == 'old':
order = [db.Books.timestamp]
if sort == 'authaz':
order = [db.Books.author_sort.asc()]
if sort == 'authza':
order = [db.Books.author_sort.desc()]
if data == "rated":
if current_user.check_visibility(constants.SIDEBAR_BEST_RATED):
......@@ -951,7 +957,7 @@ def render_prepare_search_form(cc):
else:
languages = None
return render_title_template('search_form.html', tags=tags, languages=languages, extensions=extensions,
series=series, title=_(u"search"), cc=cc, page="advsearch")
series=series, title=_(u"Advanced Search"), cc=cc, page="advsearch")
def render_search_results(term, offset=None, order=None, limit=None):
......@@ -1227,7 +1233,9 @@ def reconnect():
def search():
term = request.args.get("query")
if term:
return render_search_results(term, 0, None, config.config_books_per_page)
# flask_session['query'] = json.dumps(request.form)
return redirect(url_for('web.books_list', data="search", sort_param='stored', query=term))
# return render_search_results(term, 0, None, config.config_books_per_page)
else:
return render_title_template('search.html',
searchterm="",
......@@ -1236,11 +1244,17 @@ def search():
page="search")
@web.route("/advanced_search", methods=['POST'])
@web.route("/advsearch", methods=['POST'])
@login_required_if_no_ano
def advanced_search():
term = request.form
return render_adv_search_results(term, 0, None, config.config_books_per_page)
values = dict(request.form)
params = ['include_tag', 'exclude_tag', 'include_serie', 'exclude_serie', 'include_language',
'exclude_language', 'include_extension', 'exclude_extension']
for param in params:
values[param] = list(request.form.getlist(param))
flask_session['query'] = json.dumps(values)
return redirect(url_for('web.books_list', data="advsearch", sort_param='stored', query=""))
def render_adv_search_results(term, offset=None, order=None, limit=None):
order = order or [db.Books.sort]
......@@ -1250,14 +1264,14 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
calibre_db.session.connection().connection.connection.create_function("lower", 1, db.lcase)
q = calibre_db.session.query(db.Books).filter(calibre_db.common_filters(True))
include_tag_inputs = request.form.getlist('include_tag')
exclude_tag_inputs = request.form.getlist('exclude_tag')
include_series_inputs = request.form.getlist('include_serie')
exclude_series_inputs = request.form.getlist('exclude_serie')
include_languages_inputs = request.form.getlist('include_language')
exclude_languages_inputs = request.form.getlist('exclude_language')
include_extension_inputs = request.form.getlist('include_extension')
exclude_extension_inputs = request.form.getlist('exclude_extension')
include_tag_inputs = term.get('include_tag')
exclude_tag_inputs = term.get('exclude_tag')
include_series_inputs = term.get('include_serie')
exclude_series_inputs = term.get('exclude_serie')
include_languages_inputs = term.get('include_language')
exclude_languages_inputs = term.get('exclude_language')
include_extension_inputs = term.get('include_extension')
exclude_extension_inputs = term.get('exclude_extension')
author_name = term.get("author_name")
book_title = term.get("book_title")
......@@ -1277,15 +1291,14 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
searchterm = []
cc_present = False
for c in cc:
if request.form.get('custom_column_' + str(c.id)):
searchterm.extend([(u"%s: %s" % (c.name, request.form.get('custom_column_' + str(c.id))))])
if term.get('custom_column_' + str(c.id)):
searchterm.extend([(u"%s: %s" % (c.name, term.get('custom_column_' + str(c.id))))])
cc_present = True
if include_tag_inputs or exclude_tag_inputs or include_series_inputs or exclude_series_inputs or \
include_languages_inputs or exclude_languages_inputs or author_name or book_title or \
publisher or pub_start or pub_end or rating_low or rating_high or description or cc_present or \
include_extension_inputs or exclude_extension_inputs:
searchterm = []
include_languages_inputs or exclude_languages_inputs or author_name or book_title or \
publisher or pub_start or pub_end or rating_low or rating_high or description or cc_present or \
include_extension_inputs or exclude_extension_inputs:
searchterm.extend((author_name.replace('|', ','), book_title, publisher))
if pub_start:
try:
......@@ -1305,7 +1318,8 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
searchterm.extend(tag.name for tag in tag_names)
serie_names = calibre_db.session.query(db.Series).filter(db.Series.id.in_(include_series_inputs)).all()
searchterm.extend(serie.name for serie in serie_names)
language_names = calibre_db.session.query(db.Languages).filter(db.Languages.id.in_(include_languages_inputs)).all()
language_names = calibre_db.session.query(db.Languages).\
filter(db.Languages.id.in_(include_languages_inputs)).all()
if language_names:
language_names = calibre_db.speaking_language(language_names)
searchterm.extend(language.name for language in language_names)
......@@ -1316,9 +1330,9 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
searchterm.extend(ext for ext in include_extension_inputs)
searchterm.extend(ext for ext in exclude_extension_inputs)
# handle custom columns
for c in cc:
if request.form.get('custom_column_' + str(c.id)):
searchterm.extend([(u"%s: %s" % (c.name, request.form.get('custom_column_' + str(c.id))))])
#for c in cc:
# if term.get('custom_column_' + str(c.id)):
# searchterm.extend([(u"%s: %s" % (c.name, term.get('custom_column_' + str(c.id))))])
searchterm = " + ".join(filter(None, searchterm))
q = q.filter()
if author_name:
......@@ -1361,7 +1375,7 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
# search custom culumns
for c in cc:
custom_query = request.form.get('custom_column_' + str(c.id))
custom_query = term.get('custom_column_' + str(c.id))
if custom_query != '' and custom_query is not None:
if c.datatype == 'bool':
q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any(
......@@ -1375,28 +1389,28 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
else:
q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any(
func.lower(db.cc_classes[c.id].value).ilike("%" + custom_query + "%")))
q = q.order_by(*order).all()
flask_session['query'] = json.dumps(term)
ub.store_ids(q)
# entries, result_count, pagination = calibre_db.get_search_results(term, offset, order, limit)
result_count = len(q)
if offset != None and limit != None:
offset = int(offset)
limit_all = offset + int(limit)
pagination = Pagination((offset / (int(limit)) + 1), limit, result_count)
else:
offset = 0
limit_all = result_count
q = q.order_by(*order).all()
flask_session['query'] = json.dumps(term)
ub.store_ids(q)
# entries, result_count, pagination = calibre_db.get_search_results(term, offset, order, limit)
result_count = len(q)
if offset != None and limit != None:
offset = int(offset)
limit_all = offset + int(limit)
pagination = Pagination((offset / (int(limit)) + 1), limit, result_count)
else:
offset = 0
limit_all = result_count
return render_title_template('search.html',
adv_searchterm=searchterm,
pagination=pagination,
entries=q[offset:limit_all],
result_count=result_count,
title=_(u"search"), page="advsearch")
title=_(u"Advanced Search"), page="advsearch")
@web.route("/advanced_search", methods=['GET'])
@web.route("/advsearch", methods=['GET'])
@login_required_if_no_ano
def advanced_search_form():
# Build custom columns names
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment