mirror of
https://github.com/janeczku/calibre-web
synced 2024-12-01 05:49:58 +00:00
Merge branch 'Develop' into tasks
# Conflicts: # cps/db.py
This commit is contained in:
commit
6322919bc7
@ -81,10 +81,10 @@ SIDEBAR_PUBLISHER = 1 << 12
|
|||||||
SIDEBAR_RATING = 1 << 13
|
SIDEBAR_RATING = 1 << 13
|
||||||
SIDEBAR_FORMAT = 1 << 14
|
SIDEBAR_FORMAT = 1 << 14
|
||||||
SIDEBAR_ARCHIVED = 1 << 15
|
SIDEBAR_ARCHIVED = 1 << 15
|
||||||
# SIDEBAR_LIST = 1 << 16
|
SIDEBAR_LIST = 1 << 16
|
||||||
|
|
||||||
ADMIN_USER_ROLES = sum(r for r in ALL_ROLES.values()) & ~ROLE_ANONYMOUS
|
ADMIN_USER_ROLES = sum(r for r in ALL_ROLES.values()) & ~ROLE_ANONYMOUS
|
||||||
ADMIN_USER_SIDEBAR = (SIDEBAR_ARCHIVED << 1) - 1
|
ADMIN_USER_SIDEBAR = (SIDEBAR_LIST << 1) - 1
|
||||||
|
|
||||||
UPDATE_STABLE = 0 << 0
|
UPDATE_STABLE = 0 << 0
|
||||||
AUTO_UPDATE_STABLE = 1 << 0
|
AUTO_UPDATE_STABLE = 1 << 0
|
||||||
|
99
cps/db.py
99
cps/db.py
@ -29,7 +29,8 @@ from sqlalchemy import create_engine
|
|||||||
from sqlalchemy import Table, Column, ForeignKey, CheckConstraint
|
from sqlalchemy import Table, Column, ForeignKey, CheckConstraint
|
||||||
from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float
|
from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float
|
||||||
from sqlalchemy.orm import relationship, sessionmaker, scoped_session
|
from sqlalchemy.orm import relationship, sessionmaker, scoped_session
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.orm.collections import InstrumentedList
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
|
||||||
from sqlalchemy.pool import StaticPool
|
from sqlalchemy.pool import StaticPool
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy.sql.expression import and_, true, false, text, func, or_
|
from sqlalchemy.sql.expression import and_, true, false, text, func, or_
|
||||||
@ -97,6 +98,9 @@ class Identifiers(Base):
|
|||||||
self.type = id_type
|
self.type = id_type
|
||||||
self.book = book
|
self.book = book
|
||||||
|
|
||||||
|
#def get(self):
|
||||||
|
# return {self.type: self.val}
|
||||||
|
|
||||||
def formatType(self):
|
def formatType(self):
|
||||||
if self.type == "amazon":
|
if self.type == "amazon":
|
||||||
return u"Amazon"
|
return u"Amazon"
|
||||||
@ -149,6 +153,9 @@ class Comments(Base):
|
|||||||
self.text = text
|
self.text = text
|
||||||
self.book = book
|
self.book = book
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.text
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Comments({0})>".format(self.text)
|
return u"<Comments({0})>".format(self.text)
|
||||||
|
|
||||||
@ -162,6 +169,9 @@ class Tags(Base):
|
|||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Tags('{0})>".format(self.name)
|
return u"<Tags('{0})>".format(self.name)
|
||||||
|
|
||||||
@ -179,6 +189,9 @@ class Authors(Base):
|
|||||||
self.sort = sort
|
self.sort = sort
|
||||||
self.link = link
|
self.link = link
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Authors('{0},{1}{2}')>".format(self.name, self.sort, self.link)
|
return u"<Authors('{0},{1}{2}')>".format(self.name, self.sort, self.link)
|
||||||
|
|
||||||
@ -194,6 +207,9 @@ class Series(Base):
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.sort = sort
|
self.sort = sort
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Series('{0},{1}')>".format(self.name, self.sort)
|
return u"<Series('{0},{1}')>".format(self.name, self.sort)
|
||||||
|
|
||||||
@ -207,6 +223,9 @@ class Ratings(Base):
|
|||||||
def __init__(self, rating):
|
def __init__(self, rating):
|
||||||
self.rating = rating
|
self.rating = rating
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.rating
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Ratings('{0}')>".format(self.rating)
|
return u"<Ratings('{0}')>".format(self.rating)
|
||||||
|
|
||||||
@ -220,6 +239,12 @@ class Languages(Base):
|
|||||||
def __init__(self, lang_code):
|
def __init__(self, lang_code):
|
||||||
self.lang_code = lang_code
|
self.lang_code = lang_code
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
if self.language_name:
|
||||||
|
return self.language_name
|
||||||
|
else:
|
||||||
|
return self.lang_code
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Languages('{0}')>".format(self.lang_code)
|
return u"<Languages('{0}')>".format(self.lang_code)
|
||||||
|
|
||||||
@ -235,6 +260,9 @@ class Publishers(Base):
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.sort = sort
|
self.sort = sort
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Publishers('{0},{1}')>".format(self.name, self.sort)
|
return u"<Publishers('{0},{1}')>".format(self.name, self.sort)
|
||||||
|
|
||||||
@ -255,6 +283,10 @@ class Data(Base):
|
|||||||
self.uncompressed_size = uncompressed_size
|
self.uncompressed_size = uncompressed_size
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
# ToDo: Check
|
||||||
|
def get(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Data('{0},{1}{2}{3}')>".format(self.book, self.format, self.uncompressed_size, self.name)
|
return u"<Data('{0},{1}{2}{3}')>".format(self.book, self.format, self.uncompressed_size, self.name)
|
||||||
|
|
||||||
@ -262,14 +294,14 @@ class Data(Base):
|
|||||||
class Books(Base):
|
class Books(Base):
|
||||||
__tablename__ = 'books'
|
__tablename__ = 'books'
|
||||||
|
|
||||||
DEFAULT_PUBDATE = "0101-01-01 00:00:00+00:00"
|
DEFAULT_PUBDATE = datetime(101, 1, 1, 0, 0, 0, 0) # ("0101-01-01 00:00:00+00:00")
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
title = Column(String(collation='NOCASE'), nullable=False, default='Unknown')
|
title = Column(String(collation='NOCASE'), nullable=False, default='Unknown')
|
||||||
sort = Column(String(collation='NOCASE'))
|
sort = Column(String(collation='NOCASE'))
|
||||||
author_sort = Column(String(collation='NOCASE'))
|
author_sort = Column(String(collation='NOCASE'))
|
||||||
timestamp = Column(TIMESTAMP, default=datetime.utcnow)
|
timestamp = Column(TIMESTAMP, default=datetime.utcnow)
|
||||||
pubdate = Column(String) # , default=datetime.utcnow)
|
pubdate = Column(TIMESTAMP, default=DEFAULT_PUBDATE)
|
||||||
series_index = Column(String, nullable=False, default="1.0")
|
series_index = Column(String, nullable=False, default="1.0")
|
||||||
last_modified = Column(TIMESTAMP, default=datetime.utcnow)
|
last_modified = Column(TIMESTAMP, default=datetime.utcnow)
|
||||||
path = Column(String, default="", nullable=False)
|
path = Column(String, default="", nullable=False)
|
||||||
@ -301,6 +333,9 @@ class Books(Base):
|
|||||||
self.path = path
|
self.path = path
|
||||||
self.has_cover = has_cover
|
self.has_cover = has_cover
|
||||||
|
|
||||||
|
#def as_dict(self):
|
||||||
|
# return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Books('{0},{1}{2}{3}{4}{5}{6}{7}{8}')>".format(self.title, self.sort, self.author_sort,
|
return u"<Books('{0},{1}{2}{3}{4}{5}{6}{7}{8}')>".format(self.title, self.sort, self.author_sort,
|
||||||
self.timestamp, self.pubdate, self.series_index,
|
self.timestamp, self.pubdate, self.series_index,
|
||||||
@ -329,6 +364,39 @@ class Custom_Columns(Base):
|
|||||||
display_dict['enum_values'] = [x.decode('unicode_escape') for x in display_dict['enum_values']]
|
display_dict['enum_values'] = [x.decode('unicode_escape') for x in display_dict['enum_values']]
|
||||||
return display_dict
|
return display_dict
|
||||||
|
|
||||||
|
class AlchemyEncoder(json.JSONEncoder):
|
||||||
|
|
||||||
|
def default(self, obj):
|
||||||
|
if isinstance(obj.__class__, DeclarativeMeta):
|
||||||
|
# an SQLAlchemy class
|
||||||
|
fields = {}
|
||||||
|
for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
|
||||||
|
if field == 'books':
|
||||||
|
continue
|
||||||
|
data = obj.__getattribute__(field)
|
||||||
|
try:
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = data.replace("'","\'")
|
||||||
|
elif isinstance(data, InstrumentedList):
|
||||||
|
el =list()
|
||||||
|
for ele in data:
|
||||||
|
if ele.get:
|
||||||
|
el.append(ele.get())
|
||||||
|
else:
|
||||||
|
el.append(json.dumps(ele, cls=AlchemyEncoder))
|
||||||
|
data =",".join(el)
|
||||||
|
if data == '[]':
|
||||||
|
data = ""
|
||||||
|
else:
|
||||||
|
json.dumps(data)
|
||||||
|
fields[field] = data
|
||||||
|
except:
|
||||||
|
fields[field] = ""
|
||||||
|
# a json-encodable dict
|
||||||
|
return fields
|
||||||
|
|
||||||
|
return json.JSONEncoder.default(self, obj)
|
||||||
|
|
||||||
|
|
||||||
class CalibreDB():
|
class CalibreDB():
|
||||||
|
|
||||||
@ -494,10 +562,11 @@ class CalibreDB():
|
|||||||
pos_content_cc_filter, ~neg_content_cc_filter, archived_filter)
|
pos_content_cc_filter, ~neg_content_cc_filter, archived_filter)
|
||||||
|
|
||||||
# Fill indexpage with all requested data from database
|
# Fill indexpage with all requested data from database
|
||||||
def fill_indexpage(self, page, database, db_filter, order, *join):
|
def fill_indexpage(self, page, pagesize, database, db_filter, order, *join):
|
||||||
return self.fill_indexpage_with_archived_books(page, database, db_filter, order, False, *join)
|
return self.fill_indexpage_with_archived_books(page, pagesize, database, db_filter, order, False, *join)
|
||||||
|
|
||||||
def fill_indexpage_with_archived_books(self, page, database, db_filter, order, allow_show_archived, *join):
|
def fill_indexpage_with_archived_books(self, page, pagesize, database, db_filter, order, allow_show_archived, *join):
|
||||||
|
pagesize = pagesize or self.config.config_books_per_page
|
||||||
if current_user.show_detail_random():
|
if current_user.show_detail_random():
|
||||||
randm = self.session.query(Books) \
|
randm = self.session.query(Books) \
|
||||||
.filter(self.common_filters(allow_show_archived)) \
|
.filter(self.common_filters(allow_show_archived)) \
|
||||||
@ -505,14 +574,14 @@ class CalibreDB():
|
|||||||
.limit(self.config.config_random_books)
|
.limit(self.config.config_random_books)
|
||||||
else:
|
else:
|
||||||
randm = false()
|
randm = false()
|
||||||
off = int(int(self.config.config_books_per_page) * (page - 1))
|
off = int(int(pagesize) * (page - 1))
|
||||||
query = self.session.query(database) \
|
query = self.session.query(database) \
|
||||||
.join(*join, isouter=True) \
|
.join(*join, isouter=True) \
|
||||||
.filter(db_filter) \
|
.filter(db_filter) \
|
||||||
.filter(self.common_filters(allow_show_archived))
|
.filter(self.common_filters(allow_show_archived))
|
||||||
pagination = Pagination(page, self.config.config_books_per_page,
|
pagination = Pagination(page, pagesize,
|
||||||
len(query.all()))
|
len(query.all()))
|
||||||
entries = query.order_by(*order).offset(off).limit(self.config.config_books_per_page).all()
|
entries = query.order_by(*order).offset(off).limit(pagesize).all()
|
||||||
for book in entries:
|
for book in entries:
|
||||||
book = self.order_authors(book)
|
book = self.order_authors(book)
|
||||||
return entries, randm, pagination
|
return entries, randm, pagination
|
||||||
@ -552,20 +621,26 @@ class CalibreDB():
|
|||||||
.filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first()
|
.filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first()
|
||||||
|
|
||||||
# read search results from calibre-database and return it (function is used for feed and simple search
|
# read search results from calibre-database and return it (function is used for feed and simple search
|
||||||
def get_search_results(self, term):
|
def get_search_results(self, term, offset=None, order=None, limit=None):
|
||||||
|
order = order or [Books.sort]
|
||||||
|
if offset != None and limit != None:
|
||||||
|
offset = int(offset)
|
||||||
|
limit = offset + int(limit)
|
||||||
term.strip().lower()
|
term.strip().lower()
|
||||||
self.session.connection().connection.connection.create_function("lower", 1, lcase)
|
self.session.connection().connection.connection.create_function("lower", 1, lcase)
|
||||||
q = list()
|
q = list()
|
||||||
authorterms = re.split("[, ]+", term)
|
authorterms = re.split("[, ]+", term)
|
||||||
for authorterm in authorterms:
|
for authorterm in authorterms:
|
||||||
q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%")))
|
q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%")))
|
||||||
return self.session.query(Books).filter(self.common_filters(True)).filter(
|
result = self.session.query(Books).filter(self.common_filters(True)).filter(
|
||||||
or_(Books.tags.any(func.lower(Tags.name).ilike("%" + term + "%")),
|
or_(Books.tags.any(func.lower(Tags.name).ilike("%" + term + "%")),
|
||||||
Books.series.any(func.lower(Series.name).ilike("%" + term + "%")),
|
Books.series.any(func.lower(Series.name).ilike("%" + term + "%")),
|
||||||
Books.authors.any(and_(*q)),
|
Books.authors.any(and_(*q)),
|
||||||
Books.publishers.any(func.lower(Publishers.name).ilike("%" + term + "%")),
|
Books.publishers.any(func.lower(Publishers.name).ilike("%" + term + "%")),
|
||||||
func.lower(Books.title).ilike("%" + term + "%")
|
func.lower(Books.title).ilike("%" + term + "%")
|
||||||
)).order_by(Books.sort).all()
|
)).order_by(*order).all()
|
||||||
|
result_count = len(result)
|
||||||
|
return result[offset:limit], result_count
|
||||||
|
|
||||||
# Creates for all stored languages a translated speaking name in the array for the UI
|
# Creates for all stored languages a translated speaking name in the array for the UI
|
||||||
def speaking_language(self, languages=None):
|
def speaking_language(self, languages=None):
|
||||||
|
165
cps/editbooks.py
165
cps/editbooks.py
@ -27,6 +27,7 @@ import json
|
|||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from babel import Locale as LC
|
||||||
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
|
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
from flask_login import current_user, login_required
|
from flask_login import current_user, login_required
|
||||||
@ -171,21 +172,42 @@ def modify_identifiers(input_identifiers, db_identifiers, db_session):
|
|||||||
changed = True
|
changed = True
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
|
@editbook.route("/ajax/delete/<int:book_id>")
|
||||||
@editbook.route("/delete/<int:book_id>/", defaults={'book_format': ""})
|
|
||||||
@editbook.route("/delete/<int:book_id>/<string:book_format>/")
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_book(book_id, book_format):
|
def delete_book_from_details(book_id):
|
||||||
|
return Response(delete_book(book_id,"", True), mimetype='application/json')
|
||||||
|
|
||||||
|
|
||||||
|
@editbook.route("/delete/<int:book_id>", defaults={'book_format': ""})
|
||||||
|
@editbook.route("/delete/<int:book_id>/<string:book_format>")
|
||||||
|
@login_required
|
||||||
|
def delete_book_ajax(book_id, book_format):
|
||||||
|
return delete_book(book_id,book_format, False)
|
||||||
|
|
||||||
|
def delete_book(book_id, book_format, jsonResponse):
|
||||||
|
warning = {}
|
||||||
if current_user.role_delete_books():
|
if current_user.role_delete_books():
|
||||||
book = calibre_db.get_book(book_id)
|
book = calibre_db.get_book(book_id)
|
||||||
if book:
|
if book:
|
||||||
try:
|
try:
|
||||||
result, error = helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper())
|
result, error = helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper())
|
||||||
if not result:
|
if not result:
|
||||||
flash(error, category="error")
|
if jsonResponse:
|
||||||
return redirect(url_for('editbook.edit_book', book_id=book_id))
|
return json.dumps({"location": url_for("editbook.edit_book"),
|
||||||
|
"type": "alert",
|
||||||
|
"format": "",
|
||||||
|
"error": error}),
|
||||||
|
else:
|
||||||
|
flash(error, category="error")
|
||||||
|
return redirect(url_for('editbook.edit_book', book_id=book_id))
|
||||||
if error:
|
if error:
|
||||||
flash(error, category="warning")
|
if jsonResponse:
|
||||||
|
warning = {"location": url_for("editbook.edit_book"),
|
||||||
|
"type": "warning",
|
||||||
|
"format": "",
|
||||||
|
"error": error}
|
||||||
|
else:
|
||||||
|
flash(error, category="warning")
|
||||||
if not book_format:
|
if not book_format:
|
||||||
# delete book from Shelfs, Downloads, Read list
|
# delete book from Shelfs, Downloads, Read list
|
||||||
ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete()
|
ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete()
|
||||||
@ -241,11 +263,23 @@ def delete_book(book_id, book_format):
|
|||||||
# book not found
|
# book not found
|
||||||
log.error('Book with id "%s" could not be deleted: not found', book_id)
|
log.error('Book with id "%s" could not be deleted: not found', book_id)
|
||||||
if book_format:
|
if book_format:
|
||||||
flash(_('Book Format Successfully Deleted'), category="success")
|
if jsonResponse:
|
||||||
return redirect(url_for('editbook.edit_book', book_id=book_id))
|
return json.dumps([warning, {"location": url_for("editbook.edit_book", book_id=book_id),
|
||||||
|
"type": "success",
|
||||||
|
"format": book_format,
|
||||||
|
"message": _('Book Format Successfully Deleted')}])
|
||||||
|
else:
|
||||||
|
flash(_('Book Format Successfully Deleted'), category="success")
|
||||||
|
return redirect(url_for('editbook.edit_book', book_id=book_id))
|
||||||
else:
|
else:
|
||||||
flash(_('Book Successfully Deleted'), category="success")
|
if jsonResponse:
|
||||||
return redirect(url_for('web.index'))
|
return json.dumps([warning, {"location": url_for('web.index'),
|
||||||
|
"type": "success",
|
||||||
|
"format": book_format,
|
||||||
|
"message": _('Book Successfully Deleted')}])
|
||||||
|
else:
|
||||||
|
flash(_('Book Successfully Deleted'), category="success")
|
||||||
|
return redirect(url_for('web.index'))
|
||||||
|
|
||||||
|
|
||||||
def render_edit_book(book_id):
|
def render_edit_book(book_id):
|
||||||
@ -896,3 +930,112 @@ def convert_bookformat(book_id):
|
|||||||
else:
|
else:
|
||||||
flash(_(u"There was an error converting this book: %(res)s", res=rtn), category="error")
|
flash(_(u"There was an error converting this book: %(res)s", res=rtn), category="error")
|
||||||
return redirect(url_for('editbook.edit_book', book_id=book_id))
|
return redirect(url_for('editbook.edit_book', book_id=book_id))
|
||||||
|
|
||||||
|
@editbook.route("/ajax/editbooks/<param>", methods=['POST'])
|
||||||
|
@login_required_if_no_ano
|
||||||
|
def edit_list_book(param):
|
||||||
|
vals = request.form.to_dict()
|
||||||
|
# calibre_db.update_title_sort(config)
|
||||||
|
#calibre_db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
|
||||||
|
book = calibre_db.get_book(vals['pk'])
|
||||||
|
if param =='series_index':
|
||||||
|
edit_book_series_index(vals['value'], book)
|
||||||
|
elif param =='tags':
|
||||||
|
edit_book_tags(vals['value'], book)
|
||||||
|
elif param =='series':
|
||||||
|
edit_book_series(vals['value'], book)
|
||||||
|
elif param =='publishers':
|
||||||
|
vals['publisher'] = vals['value']
|
||||||
|
edit_book_publisher(vals, book)
|
||||||
|
elif param =='languages':
|
||||||
|
edit_book_languages(vals['value'], book)
|
||||||
|
elif param =='author_sort':
|
||||||
|
book.author_sort = vals['value']
|
||||||
|
elif param =='title':
|
||||||
|
book.title = vals['value']
|
||||||
|
helper.update_dir_stucture(book.id, config.config_calibre_dir)
|
||||||
|
elif param =='sort':
|
||||||
|
book.sort = vals['value']
|
||||||
|
# ToDo: edit books
|
||||||
|
elif param =='authors':
|
||||||
|
input_authors = vals['value'].split('&')
|
||||||
|
input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors))
|
||||||
|
modify_database_object(input_authors, book.authors, db.Authors, calibre_db.session, 'author')
|
||||||
|
sort_authors_list = list()
|
||||||
|
for inp in input_authors:
|
||||||
|
stored_author = calibre_db.session.query(db.Authors).filter(db.Authors.name == inp).first()
|
||||||
|
if not stored_author:
|
||||||
|
stored_author = helper.get_sorted_author(inp)
|
||||||
|
else:
|
||||||
|
stored_author = stored_author.sort
|
||||||
|
sort_authors_list.append(helper.get_sorted_author(stored_author))
|
||||||
|
sort_authors = ' & '.join(sort_authors_list)
|
||||||
|
if book.author_sort != sort_authors:
|
||||||
|
book.author_sort = sort_authors
|
||||||
|
helper.update_dir_stucture(book.id, config.config_calibre_dir, input_authors[0])
|
||||||
|
book.last_modified = datetime.utcnow()
|
||||||
|
calibre_db.session.commit()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@editbook.route("/ajax/sort_value/<field>/<int:bookid>")
|
||||||
|
@login_required
|
||||||
|
def get_sorted_entry(field, bookid):
|
||||||
|
if field == 'title' or field == 'authors':
|
||||||
|
book = calibre_db.get_filtered_book(bookid)
|
||||||
|
if book:
|
||||||
|
if field == 'title':
|
||||||
|
return json.dumps({'sort': book.sort})
|
||||||
|
elif field == 'authors':
|
||||||
|
return json.dumps({'author_sort': book.author_sort})
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
@editbook.route("/ajax/simulatemerge", methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def simulate_merge_list_book():
|
||||||
|
vals = request.get_json().get('Merge_books')
|
||||||
|
if vals:
|
||||||
|
to_book = calibre_db.get_book(vals[0]).title
|
||||||
|
vals.pop(0)
|
||||||
|
if to_book:
|
||||||
|
for book_id in vals:
|
||||||
|
from_book = []
|
||||||
|
from_book.append(calibre_db.get_book(book_id).title)
|
||||||
|
return json.dumps({'to': to_book, 'from': from_book})
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
@editbook.route("/ajax/mergebooks", methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def merge_list_book():
|
||||||
|
vals = request.get_json().get('Merge_books')
|
||||||
|
to_file = list()
|
||||||
|
if vals:
|
||||||
|
# load all formats from target book
|
||||||
|
to_book = calibre_db.get_book(vals[0])
|
||||||
|
vals.pop(0)
|
||||||
|
if to_book:
|
||||||
|
for file in to_book.data:
|
||||||
|
to_file.append(file.format)
|
||||||
|
to_name = helper.get_valid_filename(to_book.title) + ' - ' + \
|
||||||
|
helper.get_valid_filename(to_book.authors[0].name)
|
||||||
|
for book_id in vals:
|
||||||
|
from_book = calibre_db.get_book(book_id)
|
||||||
|
if from_book:
|
||||||
|
for element in from_book.data:
|
||||||
|
if element.format not in to_file:
|
||||||
|
# create new data entry with: book_id, book_format, uncompressed_size, name
|
||||||
|
filepath_new = os.path.normpath(os.path.join(config.config_calibre_dir,
|
||||||
|
to_book.path,
|
||||||
|
to_name + "." + element.format.lower()))
|
||||||
|
filepath_old = os.path.normpath(os.path.join(config.config_calibre_dir,
|
||||||
|
from_book.path,
|
||||||
|
element.name + "." + element.format.lower()))
|
||||||
|
copyfile(filepath_old, filepath_new)
|
||||||
|
to_book.data.append(db.Data(to_book.id,
|
||||||
|
element.format,
|
||||||
|
element.uncompressed_size,
|
||||||
|
to_name))
|
||||||
|
delete_book(from_book.id,"", True) # json_resp =
|
||||||
|
return json.dumps({'success': True})
|
||||||
|
return ""
|
||||||
|
@ -76,22 +76,18 @@ def mimetype_filter(val):
|
|||||||
@jinjia.app_template_filter('formatdate')
|
@jinjia.app_template_filter('formatdate')
|
||||||
def formatdate_filter(val):
|
def formatdate_filter(val):
|
||||||
try:
|
try:
|
||||||
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val)
|
return format_date(val, format='medium', locale=get_locale())
|
||||||
formatdate = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S")
|
|
||||||
return format_date(formatdate, format='medium', locale=get_locale())
|
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
log.error('Babel error: %s, Current user locale: %s, Current User: %s', e,
|
log.error('Babel error: %s, Current user locale: %s, Current User: %s', e,
|
||||||
current_user.locale,
|
current_user.locale,
|
||||||
current_user.nickname
|
current_user.nickname
|
||||||
)
|
)
|
||||||
return formatdate
|
return val
|
||||||
|
|
||||||
|
|
||||||
@jinjia.app_template_filter('formatdateinput')
|
@jinjia.app_template_filter('formatdateinput')
|
||||||
def format_date_input(val):
|
def format_date_input(val):
|
||||||
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val)
|
input_date = val.isoformat().split('T', 1)[0] # Hack to support dates <1900
|
||||||
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
|
return '' if input_date == "0101-01-01" else input_date
|
||||||
|
|
||||||
|
|
||||||
|
20
cps/opds.py
20
cps/opds.py
@ -100,7 +100,7 @@ def feed_normal_search():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_new():
|
def feed_new():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
|
||||||
db.Books, True, [db.Books.timestamp.desc()])
|
db.Books, True, [db.Books.timestamp.desc()])
|
||||||
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
|
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ def feed_discover():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_best_rated():
|
def feed_best_rated():
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
|
||||||
db.Books, db.Books.ratings.any(db.Ratings.rating > 9),
|
db.Books, db.Books.ratings.any(db.Ratings.rating > 9),
|
||||||
[db.Books.timestamp.desc()])
|
[db.Books.timestamp.desc()])
|
||||||
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
|
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
|
||||||
@ -164,7 +164,7 @@ def feed_authorindex():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_author(book_id):
|
def feed_author(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.authors.any(db.Authors.id == book_id),
|
db.Books.authors.any(db.Authors.id == book_id),
|
||||||
[db.Books.timestamp.desc()])
|
[db.Books.timestamp.desc()])
|
||||||
@ -190,7 +190,7 @@ def feed_publisherindex():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_publisher(book_id):
|
def feed_publisher(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.publishers.any(db.Publishers.id == book_id),
|
db.Books.publishers.any(db.Publishers.id == book_id),
|
||||||
[db.Books.timestamp.desc()])
|
[db.Books.timestamp.desc()])
|
||||||
@ -218,7 +218,7 @@ def feed_categoryindex():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_category(book_id):
|
def feed_category(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.tags.any(db.Tags.id == book_id),
|
db.Books.tags.any(db.Tags.id == book_id),
|
||||||
[db.Books.timestamp.desc()])
|
[db.Books.timestamp.desc()])
|
||||||
@ -245,7 +245,7 @@ def feed_seriesindex():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_series(book_id):
|
def feed_series(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.series.any(db.Series.id == book_id),
|
db.Books.series.any(db.Series.id == book_id),
|
||||||
[db.Books.series_index])
|
[db.Books.series_index])
|
||||||
@ -276,7 +276,7 @@ def feed_ratingindex():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_ratings(book_id):
|
def feed_ratings(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.ratings.any(db.Ratings.id == book_id),
|
db.Books.ratings.any(db.Ratings.id == book_id),
|
||||||
[db.Books.timestamp.desc()])
|
[db.Books.timestamp.desc()])
|
||||||
@ -304,7 +304,7 @@ def feed_formatindex():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_format(book_id):
|
def feed_format(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.data.any(db.Data.format == book_id.upper()),
|
db.Books.data.any(db.Data.format == book_id.upper()),
|
||||||
[db.Books.timestamp.desc()])
|
[db.Books.timestamp.desc()])
|
||||||
@ -338,7 +338,7 @@ def feed_languagesindex():
|
|||||||
@requires_basic_auth_if_no_ano
|
@requires_basic_auth_if_no_ano
|
||||||
def feed_languages(book_id):
|
def feed_languages(book_id):
|
||||||
off = request.args.get("offset") or 0
|
off = request.args.get("offset") or 0
|
||||||
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.languages.any(db.Languages.id == book_id),
|
db.Books.languages.any(db.Languages.id == book_id),
|
||||||
[db.Books.timestamp.desc()])
|
[db.Books.timestamp.desc()])
|
||||||
@ -408,7 +408,7 @@ def get_metadata_calibre_companion(uuid, library):
|
|||||||
|
|
||||||
def feed_search(term):
|
def feed_search(term):
|
||||||
if 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
|
entriescount = len(entries) if len(entries) > 0 else 1
|
||||||
pagination = Pagination(1, entriescount, entriescount)
|
pagination = Pagination(1, entriescount, entriescount)
|
||||||
return render_xml_template('feed.xml', searchterm=term, entries=entries, pagination=pagination)
|
return render_xml_template('feed.xml', searchterm=term, entries=entries, pagination=pagination)
|
||||||
|
@ -5,7 +5,7 @@ body.serieslist.grid-view div.container-fluid>div>div.col-sm-10:before{
|
|||||||
.cover .badge{
|
.cover .badge{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
left: 0;
|
||||||
background-color: #cc7b19;
|
background-color: #cc7b19;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
|
11
cps/static/css/libs/bootstrap-table.min.css
vendored
11
cps/static/css/libs/bootstrap-table.min.css
vendored
File diff suppressed because one or more lines are too long
@ -51,7 +51,22 @@ body h2 {
|
|||||||
color:#444;
|
color:#444;
|
||||||
}
|
}
|
||||||
|
|
||||||
a { color: #45b29d; }
|
a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d; }
|
||||||
|
|
||||||
|
.book-remove:hover { color: #23527c; }
|
||||||
|
|
||||||
|
.btn-default a { color: #444; }
|
||||||
|
|
||||||
|
.btn-default a:hover {
|
||||||
|
color: #45b29d;
|
||||||
|
text-decoration: None;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default:hover {
|
||||||
|
color: #45b29d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editable-click, a.editable-click, a.editable-click:hover { border-bottom: None; }
|
||||||
|
|
||||||
.navigation .nav-head {
|
.navigation .nav-head {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@ -63,6 +78,7 @@ a { color: #45b29d; }
|
|||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation li a {
|
.navigation li a {
|
||||||
color: #444;
|
color: #444;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -411,6 +411,19 @@ bitjs.archive = bitjs.archive || {};
|
|||||||
return "unrar.js";
|
return "unrar.js";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unrarrer5
|
||||||
|
* @extends {bitjs.archive.Unarchiver}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
bitjs.archive.Unrarrer5 = function(arrayBuffer, optPathToBitJS) {
|
||||||
|
bitjs.base(this, arrayBuffer, optPathToBitJS);
|
||||||
|
};
|
||||||
|
bitjs.inherits(bitjs.archive.Unrarrer5, bitjs.archive.Unarchiver);
|
||||||
|
bitjs.archive.Unrarrer5.prototype.getScriptFileName = function() {
|
||||||
|
return "unrar5.js";
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Untarrer
|
* Untarrer
|
||||||
* @extends {bitjs.archive.Unarchiver}
|
* @extends {bitjs.archive.Unarchiver}
|
||||||
|
@ -14,10 +14,10 @@
|
|||||||
/* global VM_FIXEDGLOBALSIZE, VM_GLOBALMEMSIZE, MAXWINMASK, VM_GLOBALMEMADDR, MAXWINSIZE */
|
/* global VM_FIXEDGLOBALSIZE, VM_GLOBALMEMSIZE, MAXWINMASK, VM_GLOBALMEMADDR, MAXWINSIZE */
|
||||||
|
|
||||||
// This file expects to be invoked as a Worker (see onmessage below).
|
// This file expects to be invoked as a Worker (see onmessage below).
|
||||||
importScripts("../io/bitstream.js");
|
/*importScripts("../io/bitstream.js");
|
||||||
importScripts("../io/bytebuffer.js");
|
importScripts("../io/bytebuffer.js");
|
||||||
importScripts("archive.js");
|
importScripts("archive.js");
|
||||||
importScripts("rarvm.js");
|
importScripts("rarvm.js");*/
|
||||||
|
|
||||||
// Progress variables.
|
// Progress variables.
|
||||||
var currentFilename = "";
|
var currentFilename = "";
|
||||||
@ -29,19 +29,21 @@ var totalFilesInArchive = 0;
|
|||||||
|
|
||||||
// Helper functions.
|
// Helper functions.
|
||||||
var info = function(str) {
|
var info = function(str) {
|
||||||
postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
|
console.log(str);
|
||||||
|
// postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
|
||||||
};
|
};
|
||||||
var err = function(str) {
|
var err = function(str) {
|
||||||
postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
|
console.log(str);
|
||||||
|
// postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
|
||||||
};
|
};
|
||||||
var postProgress = function() {
|
var postProgress = function() {
|
||||||
postMessage(new bitjs.archive.UnarchiveProgressEvent(
|
/*postMessage(new bitjs.archive.UnarchiveProgressEvent(
|
||||||
currentFilename,
|
currentFilename,
|
||||||
currentFileNumber,
|
currentFileNumber,
|
||||||
currentBytesUnarchivedInFile,
|
currentBytesUnarchivedInFile,
|
||||||
currentBytesUnarchived,
|
currentBytesUnarchived,
|
||||||
totalUncompressedBytesInArchive,
|
totalUncompressedBytesInArchive,
|
||||||
totalFilesInArchive));
|
totalFilesInArchive));*/
|
||||||
};
|
};
|
||||||
|
|
||||||
// shows a byte value as its hex representation
|
// shows a byte value as its hex representation
|
||||||
@ -1298,7 +1300,7 @@ var unrar = function(arrayBuffer) {
|
|||||||
totalUncompressedBytesInArchive = 0;
|
totalUncompressedBytesInArchive = 0;
|
||||||
totalFilesInArchive = 0;
|
totalFilesInArchive = 0;
|
||||||
|
|
||||||
postMessage(new bitjs.archive.UnarchiveStartEvent());
|
//postMessage(new bitjs.archive.UnarchiveStartEvent());
|
||||||
var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */);
|
var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */);
|
||||||
|
|
||||||
var header = new RarVolumeHeader(bstream);
|
var header = new RarVolumeHeader(bstream);
|
||||||
@ -1348,7 +1350,7 @@ var unrar = function(arrayBuffer) {
|
|||||||
localfile.unrar();
|
localfile.unrar();
|
||||||
|
|
||||||
if (localfile.isValid) {
|
if (localfile.isValid) {
|
||||||
postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
|
// postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
|
||||||
postProgress();
|
postProgress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1358,7 +1360,7 @@ var unrar = function(arrayBuffer) {
|
|||||||
} else {
|
} else {
|
||||||
err("Invalid RAR file");
|
err("Invalid RAR file");
|
||||||
}
|
}
|
||||||
postMessage(new bitjs.archive.UnarchiveFinishEvent());
|
// postMessage(new bitjs.archive.UnarchiveFinishEvent());
|
||||||
};
|
};
|
||||||
|
|
||||||
// event.data.file has the ArrayBuffer.
|
// event.data.file has the ArrayBuffer.
|
||||||
|
1371
cps/static/js/archive/unrar5.js
Normal file
1371
cps/static/js/archive/unrar5.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -19,6 +19,17 @@ var direction = 0; // Descending order
|
|||||||
var sort = 0; // Show sorted entries
|
var sort = 0; // Show sorted entries
|
||||||
|
|
||||||
$("#sort_name").click(function() {
|
$("#sort_name").click(function() {
|
||||||
|
var class_name = $("h1").attr('Class') + "_sort_name";
|
||||||
|
var obj = {};
|
||||||
|
obj[class_name] = sort;
|
||||||
|
$.ajax({
|
||||||
|
method:"post",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../../ajax/view",
|
||||||
|
data: JSON.stringify({obj}),
|
||||||
|
});
|
||||||
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
var index = 0;
|
var index = 0;
|
||||||
var store;
|
var store;
|
||||||
@ -40,9 +51,7 @@ $("#sort_name").click(function() {
|
|||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
/*listItems.sort(function(a,b){
|
|
||||||
return $(a).children()[1].innerText.localeCompare($(b).children()[1].innerText)
|
|
||||||
});*/
|
|
||||||
// Find count of middle element
|
// Find count of middle element
|
||||||
if (count > 20) {
|
if (count > 20) {
|
||||||
var middle = parseInt(count / 2, 10) + (count % 2);
|
var middle = parseInt(count / 2, 10) + (count % 2);
|
||||||
|
@ -162,10 +162,15 @@ function initProgressClick() {
|
|||||||
function loadFromArrayBuffer(ab) {
|
function loadFromArrayBuffer(ab) {
|
||||||
var start = (new Date).getTime();
|
var start = (new Date).getTime();
|
||||||
var h = new Uint8Array(ab, 0, 10);
|
var h = new Uint8Array(ab, 0, 10);
|
||||||
|
unrar5(ab);
|
||||||
var pathToBitJS = "../../static/js/archive/";
|
var pathToBitJS = "../../static/js/archive/";
|
||||||
var lastCompletion = 0;
|
var lastCompletion = 0;
|
||||||
if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { //Rar!
|
/*if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { //Rar!
|
||||||
unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS);
|
if (h[7] === 0x01) {
|
||||||
|
unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS);
|
||||||
|
} else {
|
||||||
|
unarchiver = new bitjs.archive.Unrarrer5(ab, pathToBitJS);
|
||||||
|
}
|
||||||
} else if (h[0] === 80 && h[1] === 75) { //PK (Zip)
|
} else if (h[0] === 80 && h[1] === 75) { //PK (Zip)
|
||||||
unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS);
|
unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS);
|
||||||
} else if (h[0] === 255 && h[1] === 216) { // JPEG
|
} else if (h[0] === 255 && h[1] === 216) { // JPEG
|
||||||
@ -229,7 +234,7 @@ function loadFromArrayBuffer(ab) {
|
|||||||
unarchiver.start();
|
unarchiver.start();
|
||||||
} else {
|
} else {
|
||||||
alert("Some error");
|
alert("Some error");
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollTocToActive() {
|
function scrollTocToActive() {
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-fi-FI.min.js
vendored
Normal file
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-fi-FI.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-fr-CH.min.js
vendored
Normal file
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-fr-CH.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-fr-LU.min.js
vendored
Normal file
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-fr-LU.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-nl-BE.min.js
vendored
Normal file
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-nl-BE.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-sr-Cyrl-RS.min.js
vendored
Normal file
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-sr-Cyrl-RS.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-sr-Latn-RS.min.js
vendored
Normal file
10
cps/static/js/libs/bootstrap-table/locale/bootstrap-table-sr-Latn-RS.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -58,6 +58,60 @@ $(document).on("change", "select[data-controlall]", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#delete_confirm").click(function() {
|
||||||
|
//get data-id attribute of the clicked element
|
||||||
|
var pathname = document.getElementsByTagName("script"), src = pathname[pathname.length - 1].src;
|
||||||
|
var path = src.substring(0, src.lastIndexOf("/"));
|
||||||
|
var deleteId = $(this).data("delete-id");
|
||||||
|
var bookFormat = $(this).data("delete-format");
|
||||||
|
if (bookFormat) {
|
||||||
|
window.location.href = path + "/../../delete/" + deleteId + "/" + bookFormat;
|
||||||
|
} else {
|
||||||
|
if ($(this).data("delete-format")) {
|
||||||
|
path = path + "/../../ajax/delete/" + deleteId;
|
||||||
|
$.ajax({
|
||||||
|
method:"get",
|
||||||
|
url: path,
|
||||||
|
timeout: 900,
|
||||||
|
success:function(data) {
|
||||||
|
data.forEach(function(item) {
|
||||||
|
if (!jQuery.isEmptyObject(item)) {
|
||||||
|
if (item.format != "") {
|
||||||
|
$("button[data-delete-format='"+item.format+"']").addClass('hidden');
|
||||||
|
}
|
||||||
|
$( ".navbar" ).after( '<div class="row-fluid text-center" style="margin-top: -20px;">' +
|
||||||
|
'<div id="flash_'+item.type+'" class="alert alert-'+item.type+'">'+item.message+'</div>' +
|
||||||
|
'</div>');
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.location.href = path + "/../../delete/" + deleteId;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//triggered when modal is about to be shown
|
||||||
|
$("#deleteModal").on("show.bs.modal", function(e) {
|
||||||
|
//get data-id attribute of the clicked element and store in button
|
||||||
|
var bookId = $(e.relatedTarget).data("delete-id");
|
||||||
|
var bookfomat = $(e.relatedTarget).data("delete-format");
|
||||||
|
if (bookfomat) {
|
||||||
|
$("#book_format").removeClass('hidden');
|
||||||
|
$("#book_complete").addClass('hidden');
|
||||||
|
} else {
|
||||||
|
$("#book_complete").removeClass('hidden');
|
||||||
|
$("#book_format").addClass('hidden');
|
||||||
|
}
|
||||||
|
$(e.currentTarget).find("#delete_confirm").data("delete-id", bookId);
|
||||||
|
$(e.currentTarget).find("#delete_confirm").data("delete-format", bookfomat);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
var updateTimerID;
|
var updateTimerID;
|
||||||
@ -324,16 +378,19 @@ $(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(".update-view").click(function(e) {
|
$(".update-view").click(function(e) {
|
||||||
var target = $(this).data("target");
|
|
||||||
var view = $(this).data("view");
|
var view = $(this).data("view");
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
var data = {};
|
$.ajax({
|
||||||
data[target] = view;
|
method:"post",
|
||||||
console.debug("Updating view data: ", data);
|
contentType: "application/json; charset=utf-8",
|
||||||
$.post( "/ajax/view", data).done(function( ) {
|
dataType: "json",
|
||||||
location.reload();
|
url: window.location.pathname + "/../../ajax/view",
|
||||||
|
data: JSON.stringify({"series_view":view}),
|
||||||
|
success: function success() {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
/* This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
||||||
* Copyright (C) 2018 OzzieIsaacs
|
* Copyright (C) 2020 OzzieIsaacs
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -15,10 +15,157 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* exported TableActions, RestrictionActions*/
|
/* exported TableActions, RestrictionActions, EbookActions, responseHandler */
|
||||||
|
|
||||||
|
var selections = [];
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
|
$("#books-table").on("check.bs.table check-all.bs.table uncheck.bs.table uncheck-all.bs.table",
|
||||||
|
function (e, rowsAfter, rowsBefore) {
|
||||||
|
var rows = rowsAfter;
|
||||||
|
|
||||||
|
if (e.type === "uncheck-all") {
|
||||||
|
rows = rowsBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ids = $.map(!$.isArray(rows) ? [rows] : rows, function (row) {
|
||||||
|
return row.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
var func = $.inArray(e.type, ["check", "check-all"]) > -1 ? "union" : "difference";
|
||||||
|
selections = window._[func](selections, ids);
|
||||||
|
if (selections.length >= 2) {
|
||||||
|
$("#merge_books").removeClass("disabled");
|
||||||
|
$("#merge_books").attr("aria-disabled", false);
|
||||||
|
} else {
|
||||||
|
$("#merge_books").addClass("disabled");
|
||||||
|
$("#merge_books").attr("aria-disabled", true);
|
||||||
|
}
|
||||||
|
if (selections.length < 1) {
|
||||||
|
$("#delete_selection").addClass("disabled");
|
||||||
|
$("#delete_selection").attr("aria-disabled", true);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$("#delete_selection").removeClass("disabled");
|
||||||
|
$("#delete_selection").attr("aria-disabled", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#delete_selection").click(function() {
|
||||||
|
$("#books-table").bootstrapTable('uncheckAll');
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#merge_confirm").click(function() {
|
||||||
|
$.ajax({
|
||||||
|
method:"post",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../../ajax/mergebooks",
|
||||||
|
data: JSON.stringify({"Merge_books":selections}),
|
||||||
|
success: function success() {
|
||||||
|
$('#books-table').bootstrapTable('refresh');
|
||||||
|
$("#books-table").bootstrapTable('uncheckAll');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#merge_books").click(function() {
|
||||||
|
$.ajax({
|
||||||
|
method:"post",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../../ajax/simulatemerge",
|
||||||
|
data: JSON.stringify({"Merge_books":selections}),
|
||||||
|
success: function success(book_titles) {
|
||||||
|
$.each(book_titles.from, function(i, item) {
|
||||||
|
$("<span>- " + item + "</span>").appendTo("#merge_from");
|
||||||
|
});
|
||||||
|
$('#merge_to').text("- " + book_titles.to);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var column = [];
|
||||||
|
$("#books-table > thead > tr > th").each(function() {
|
||||||
|
var element = {};
|
||||||
|
if ($(this).attr("data-edit")) {
|
||||||
|
element = {
|
||||||
|
editable: {
|
||||||
|
mode: "inline",
|
||||||
|
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var validateText = $(this).attr("data-edit-validate");
|
||||||
|
if (validateText) {
|
||||||
|
element.editable.validate = function (value) {
|
||||||
|
if ($.trim(value) === "") return validateText;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
column.push(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#books-table").bootstrapTable({
|
||||||
|
sidePagination: "server",
|
||||||
|
pagination: true,
|
||||||
|
paginationDetailHAlign: " hidden",
|
||||||
|
paginationHAlign: "left",
|
||||||
|
idField: "id",
|
||||||
|
uniqueId: "id",
|
||||||
|
search: true,
|
||||||
|
showColumns: true,
|
||||||
|
searchAlign: "left",
|
||||||
|
showSearchButton : false,
|
||||||
|
searchOnEnterKey: true,
|
||||||
|
checkboxHeader: false,
|
||||||
|
maintainMetaData: true,
|
||||||
|
responseHandler: responseHandler,
|
||||||
|
columns: column,
|
||||||
|
formatNoMatches: function () {
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
onEditableSave: function (field, row, oldvalue, $el) {
|
||||||
|
if (field === 'title' || field === 'authors') {
|
||||||
|
$.ajax({
|
||||||
|
method:"get",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../../ajax/sort_value/" + field + '/' + row.id,
|
||||||
|
success: function success(data) {
|
||||||
|
var key = Object.keys(data)[0]
|
||||||
|
$("#books-table").bootstrapTable('updateCellByUniqueId', {
|
||||||
|
id: row.id,
|
||||||
|
field: key,
|
||||||
|
value: data[key]
|
||||||
|
});
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onColumnSwitch: function (field, checked) {
|
||||||
|
var visible = $("#books-table").bootstrapTable('getVisibleColumns');
|
||||||
|
var hidden = $("#books-table").bootstrapTable('getHiddenColumns');
|
||||||
|
var visibility =[]
|
||||||
|
var st = ""
|
||||||
|
visible.forEach(function(item) {
|
||||||
|
st += "\""+ item.field + "\":\"" +"true"+ "\","
|
||||||
|
});
|
||||||
|
hidden.forEach(function(item) {
|
||||||
|
st += "\""+ item.field + "\":\"" +"false"+ "\","
|
||||||
|
});
|
||||||
|
st = st.slice(0, -1);
|
||||||
|
$.ajax({
|
||||||
|
method:"post",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
dataType: "json",
|
||||||
|
url: window.location.pathname + "/../../ajax/table_settings",
|
||||||
|
data: "{" + st + "}",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
$("#domain_allow_submit").click(function(event) {
|
$("#domain_allow_submit").click(function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$("#domain_add_allow").ajaxForm();
|
$("#domain_add_allow").ajaxForm();
|
||||||
@ -33,6 +180,7 @@ $(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#domain-allow-table").bootstrapTable({
|
$("#domain-allow-table").bootstrapTable({
|
||||||
formatNoMatches: function () {
|
formatNoMatches: function () {
|
||||||
return "";
|
return "";
|
||||||
@ -205,6 +353,7 @@ function TableActions (value, row) {
|
|||||||
].join("");
|
].join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Function for deleting domain restrictions */
|
/* Function for deleting domain restrictions */
|
||||||
function RestrictionActions (value, row) {
|
function RestrictionActions (value, row) {
|
||||||
return [
|
return [
|
||||||
@ -213,3 +362,20 @@ function RestrictionActions (value, row) {
|
|||||||
"</div>"
|
"</div>"
|
||||||
].join("");
|
].join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Function for deleting books */
|
||||||
|
function EbookActions (value, row) {
|
||||||
|
return [
|
||||||
|
"<div class=\"book-remove\" data-toggle=\"modal\" data-target=\"#deleteModal\" data-ajax=\"1\" data-delete-id=\"" + row.id + "\" title=\"Remove\">",
|
||||||
|
"<i class=\"glyphicon glyphicon-trash\"></i>",
|
||||||
|
"</div>"
|
||||||
|
].join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function for keeping checked rows */
|
||||||
|
function responseHandler(res) {
|
||||||
|
$.each(res.rows, function (i, row) {
|
||||||
|
row.state = $.inArray(row.id, selections) !== -1;
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@ -161,8 +161,8 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="hidden" id="update_error"> <span>{{update_error}}</span></div>
|
<div class="hidden" id="update_error"> <span>{{update_error}}</span></div>
|
||||||
<div class="btn btn-default" id="check_for_update">{{_('Check for Update')}}</div>
|
<div class="btn btn-primary" id="check_for_update">{{_('Check for Update')}}</div>
|
||||||
<div class="btn btn-default hidden" id="perform_update" data-toggle="modal" data-target="#StatusDialog">{{_('Perform Update')}}</div>
|
<div class="btn btn-primary hidden" id="perform_update" data-toggle="modal" data-target="#StatusDialog">{{_('Perform Update')}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,14 +23,14 @@
|
|||||||
<h3>{{_("In Library")}}</h3>
|
<h3>{{_("In Library")}}</h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="filterheader hidden-xs hidden-sm">
|
<div class="filterheader hidden-xs hidden-sm">
|
||||||
<a id="new" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort='new')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
<a id="new" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='new')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
||||||
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort='old')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='old')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
||||||
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
|
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
|
||||||
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
|
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
|
||||||
<a id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort='pubnew')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
<a id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubnew')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
||||||
<a id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
<a id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
||||||
<!--div class="btn-group character" role="group">
|
<!--div class="btn-group character" role="group">
|
||||||
<a id="no_shelf" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort='pubold')}}"><span class="glyphicon glyphicon-list"></span><b>?</b></a>
|
<a id="no_shelf" class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-list"></span><b>?</b></a>
|
||||||
<div id="all" class="btn btn-primary">{{_('All')}}</div>
|
<div id="all" class="btn btn-primary">{{_('All')}}</div>
|
||||||
</div-->
|
</div-->
|
||||||
</div>
|
</div>
|
||||||
@ -53,7 +53,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span class="author-hidden-divider">&</span>
|
<span class="author-hidden-divider">&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% if loop.last %}
|
{% if loop.last %}
|
||||||
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -61,7 +61,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span>&</span>
|
<span>&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for format in entry.data %}
|
{% for format in entry.data %}
|
||||||
|
@ -7,14 +7,12 @@
|
|||||||
</div>
|
</div>
|
||||||
{% if g.user.role_delete_books() %}
|
{% if g.user.role_delete_books() %}
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<button type="button" class="btn btn-danger" id="delete" data-toggle="modal" data-target="#deleteModal">{{_("Delete Book")}}</button>
|
<button type="button" class="btn btn-danger" id="delete" data-toggle="modal" data-delete-id="{{ book.id }}" data-target="#deleteModal">{{_("Delete Book")}}</button>
|
||||||
</div>
|
</div>
|
||||||
{% if book.data|length > 1 %}
|
{% if book.data|length > 1 %}
|
||||||
<div class="text-center more-stuff"><h4>{{_('Delete formats:')}}</h4>
|
<div class="text-center more-stuff"><h4>{{_('Delete formats:')}}</h4>
|
||||||
{% for file in book.data %}
|
{% for file in book.data %}
|
||||||
<div class="form-group">
|
<button type="button" class="btn btn-danger" id="delete_format" data-toggle="modal" data-delete-id="{{ book.id }}" data-delete-format="{{ file.format }}" data-target="#deleteModal">{{_('Delete')}} - {{file.format}}</button>
|
||||||
<a href="{{ url_for('editbook.delete_book', book_id=book.id, book_format=file.format) }}" class="btn btn-danger" type="button">{{_('Delete')}} - {{file.format}}</a>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -193,34 +191,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block modal %}
|
{% block modal %}
|
||||||
{% if g.user.role_delete_books() %}
|
{{ delete_book(book.id) }}
|
||||||
<div class="modal fade" id="deleteModal" role="dialog" aria-labelledby="metaDeleteLabel">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header bg-danger text-center">
|
|
||||||
<span>{{_('Are you really sure?')}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body text-center">
|
|
||||||
<p>
|
|
||||||
<span>{{_('This book will be permanently erased from database')}}</span>
|
|
||||||
<span>{{_('and hard disk')}}</span>
|
|
||||||
</p>
|
|
||||||
{% if config.config_kobo_sync %}
|
|
||||||
<p>
|
|
||||||
<span>{{_('Important Kobo Note: deleted books will remain on any paired Kobo device.')}}</span>
|
|
||||||
<span>{{_('Books must first be archived and the device synced before a book can safely be deleted.')}}</span>
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a href="{{ url_for('editbook.delete_book', book_id=book.id) }}" id="delete_confirm" class="btn btn-danger">{{_('Delete')}}</a>
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="modal fade" id="metaModal" tabindex="-1" role="dialog" aria-labelledby="metaModalLabel">
|
<div class="modal fade" id="metaModal" tabindex="-1" role="dialog" aria-labelledby="metaModalLabel">
|
||||||
<div class="modal-dialog modal-lg" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
@ -1,59 +1,99 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
{% macro text_table_row(parameter, edit_text, show_text, validate) -%}
|
||||||
|
<th data-field="{{ parameter }}" id="{{ parameter }}" data-sortable="true"
|
||||||
|
data-visible = "{{visiblility.get(parameter)}}"
|
||||||
|
{% if g.user.role_edit() %}
|
||||||
|
data-editable-type="text"
|
||||||
|
data-editable-url="{{ url_for('editbook.edit_list_book', param=parameter)}}"
|
||||||
|
data-editable-title="{{ edit_text }}"
|
||||||
|
data-edit="true"
|
||||||
|
{% if validate %}data-edit-validate="{{ _('This Field is Required') }}" {% endif %}
|
||||||
|
{% endif %}
|
||||||
|
>{{ show_text }}</th>
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% 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 %}
|
||||||
<h1 class="{{page}}">{{_(title)}}</h1>
|
<h2 class="{{page}}">{{_(title)}}</h2>
|
||||||
|
<div class="col-xs-12 col-sm-6">
|
||||||
<div class="filterheader hidden-xs hidden-sm">
|
<div class="row">
|
||||||
{% if entries.__len__() %}
|
<div class="btn btn-default disabled" id="merge_books" data-toggle="modal" data-target="#mergeModal" aria-disabled="true">{{_('Merge selected books')}}</div>
|
||||||
{% if data == 'author' %}
|
<div class="btn btn-default disabled" id="delete_selection" aria-disabled="true">{{_('Remove Selections')}}</div>
|
||||||
<button id="sort_name" class="btn btn-primary"><b>B,A <-> A B</b></button>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
<button id="desc" class="btn btn-primary"><span class="glyphicon glyphicon-sort-by-alphabet"></span></button>
|
|
||||||
<button id="asc" class="btn btn-primary"><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></button>
|
|
||||||
{% if charlist|length %}
|
|
||||||
<button id="all" class="btn btn-primary">{{_('All')}}</button>
|
|
||||||
{% endif %}
|
|
||||||
<div class="btn-group character" role="group">
|
|
||||||
{% for char in charlist%}
|
|
||||||
<button class="btn btn-primary char">{{char.char}}</button>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if title == "Series" %}
|
|
||||||
<button class="update-view btn btn-primary" href="#" data-target="series_view" data-view="grid">Grid</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div id="list" class="col-xs-12 col-sm-6">
|
|
||||||
{% for entry in entries %}
|
|
||||||
{% if loop.index0 == (loop.length/2+loop.length%2)|int and loop.length > 20 %}
|
|
||||||
</div>
|
</div>
|
||||||
<div id="second" class="col-xs-12 col-sm-6">
|
|
||||||
{% endif %}
|
|
||||||
<div class="row" {% if entry[0].sort %}data-name="{{entry[0].name}}"{% endif %} data-id="{% if entry[0].sort %}{{entry[0].sort}}{% else %}{% if entry.name %}{{entry.name}}{% else %}{{entry[0].name}}{% endif %}{% endif %}">
|
|
||||||
<div class="col-xs-2 col-sm-2 col-md-1" align="left"><span class="badge">{{entry.count}}</span></div>
|
|
||||||
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{% if entry.format %}{{url_for('web.books_list', data=data, sort='new', book_id=entry.format )}}{% else %}{{url_for('web.books_list', data=data, sort='new', book_id=entry[0].id )}}{% endif %}">
|
|
||||||
{% if entry.name %}
|
|
||||||
<div class="rating">
|
|
||||||
{% for number in range(entry.name) %}
|
|
||||||
<span class="glyphicon glyphicon-star good"></span>
|
|
||||||
{% if loop.last and loop.index < 5 %}
|
|
||||||
{% for numer in range(5 - loop.index) %}
|
|
||||||
<span class="glyphicon glyphicon-star"></span>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
{% if entry.format %}
|
|
||||||
{{entry.format}}
|
|
||||||
{% else %}
|
|
||||||
{{entry[0].name}}{% endif %}{% endif %}</a></div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
<div class="col-xs-12 col-sm-6">
|
||||||
|
<div class="row">
|
||||||
|
<input type="checkbox" id="autoupdate_titlesort" name="autoupdate_titlesort" checked>
|
||||||
|
<label for="autoupdate_titlesort">{{_('Update Title Sort automatically')}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<input type="checkbox" id="autoupdate_autorsort" name="autoupdate_autorsort" checked>
|
||||||
|
<label for="autoupdate_autorsort">{{_('Update Author Sort automatically')}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table id="books-table" class="table table-no-bordered table-striped"
|
||||||
|
data-url="{{url_for('web.list_books')}}">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% if g.user.role_edit() %}
|
||||||
|
<th data-field="state" data-checkbox="true" data-sortable="true"></th>
|
||||||
|
{% endif %}
|
||||||
|
<th data-field="id" id="id" data-visible="false" data-switchable="false"></th>
|
||||||
|
{{ text_table_row('title', _('Enter Title'),_('Title'), true) }}
|
||||||
|
{{ text_table_row('sort', _('Enter Title Sort'),_('Title Sort'), false) }}
|
||||||
|
{{ text_table_row('author_sort', _('Enter Author Sort'),_('Author Sort'), false) }}
|
||||||
|
{{ text_table_row('authors', _('Enter Authors'),_('Authors'), true) }}
|
||||||
|
{{ text_table_row('tags', _('Enter Categories'),_('Categories'), false) }}
|
||||||
|
{{ text_table_row('series', _('Enter Series'),_('Series'), false) }}
|
||||||
|
<th data-field="series_index" id="series_index" data-visible="{{visiblility.get('series_index')}}" data-edit-validate="{{ _('This Field is Required') }}" data-sortable="true" {% if g.user.role_edit() %} data-editable-type="number" data-editable-placeholder="1" data-editable-step="0.01" data-editable-min="0" data-editable-url="{{ url_for('editbook.edit_list_book', param='series_index')}}" data-edit="true" data-editable-title="{{_('Enter title')}}"{% endif %}>{{_('Series Index')}}</th>
|
||||||
|
{{ text_table_row('languages', _('Enter Languages'),_('Languages'), false) }}
|
||||||
|
<!--th data-field="pubdate" data-type="date" data-visible="{{visiblility.get('pubdate')}}" data-viewformat="dd.mm.yyyy" id="pubdate" data-sortable="true">{{_('Publishing Date')}}</th-->
|
||||||
|
{{ text_table_row('publishers', _('Enter Publishers'),_('Publishers'), false) }}
|
||||||
|
{% if g.user.role_edit() %}
|
||||||
|
<th data-align="right" data-formatter="EbookActions" data-switchable="false">{{_('Delete')}}</th>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
||||||
|
{% block modal %}
|
||||||
|
{{ delete_book(0) }}
|
||||||
|
{% if g.user.role_edit() %}
|
||||||
|
<div class="modal fade" id="mergeModal" role="dialog" aria-labelledby="metaMergeLabel">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-danger text-center">
|
||||||
|
<span>{{_('Are you really sure?')}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p></p>
|
||||||
|
<div class="text-left">{{_('Books with Title will be merged from:')}}</div>
|
||||||
|
<p></p>
|
||||||
|
<div class=text-left" id="merge_from"></div>
|
||||||
|
<p></p>
|
||||||
|
<div class="text-left">{{_('Into Book with Title:')}}</div>
|
||||||
|
<p></p>
|
||||||
|
<div class=text-left" id="merge_to"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<input type="button" class="btn btn-danger" value="{{_('Merge')}}" name="merge_confirm" id="merge_confirm" data-dismiss="modal">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script src="{{ url_for('static', filename='js/filter_list.js') }}"></script>
|
<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>
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
<h2 id="title">{{entry.title|shortentitle(40)}}</h2>
|
<h2 id="title">{{entry.title|shortentitle(40)}}</h2>
|
||||||
<p class="author">
|
<p class="author">
|
||||||
{% for author in entry.authors %}
|
{% for author in entry.authors %}
|
||||||
<a href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id ) }}">{{author.name.replace('|',',')}}</a>
|
<a href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id ) }}">{{author.name.replace('|',',')}}</a>
|
||||||
{% if not loop.last %}
|
{% if not loop.last %}
|
||||||
&
|
&
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -114,7 +114,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if entry.series|length > 0 %}
|
{% if entry.series|length > 0 %}
|
||||||
<p>{{_('Book')}} {{entry.series_index}} {{_('of')}} <a href="{{url_for('web.books_list', data='series',sort='abc', book_id=entry.series[0].id)}}">{{entry.series[0].name}}</a></p>
|
<p>{{_('Book')}} {{entry.series_index}} {{_('of')}} <a href="{{url_for('web.books_list', data='series', sort_param='abc', book_id=entry.series[0].id)}}">{{entry.series[0].name}}</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if entry.languages.__len__() > 0 %}
|
{% if entry.languages.__len__() > 0 %}
|
||||||
@ -143,7 +143,7 @@
|
|||||||
<span class="glyphicon glyphicon-tags"></span>
|
<span class="glyphicon glyphicon-tags"></span>
|
||||||
|
|
||||||
{% for tag in entry.tags %}
|
{% for tag in entry.tags %}
|
||||||
<a href="{{ url_for('web.books_list', data='category', sort='new', book_id=tag.id) }}" class="btn btn-xs btn-info" role="button">{{tag.name}}</a>
|
<a href="{{ url_for('web.books_list', data='category', sort_param='new', book_id=tag.id) }}" class="btn btn-xs btn-info" role="button">{{tag.name}}</a>
|
||||||
{%endfor%}
|
{%endfor%}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -154,13 +154,13 @@
|
|||||||
<div class="publishers">
|
<div class="publishers">
|
||||||
<p>
|
<p>
|
||||||
<span>{{_('Publisher')}}:
|
<span>{{_('Publisher')}}:
|
||||||
<a href="{{url_for('web.books_list', data='publisher', sort='new', book_id=entry.publishers[0].id ) }}">{{entry.publishers[0].name}}</a>
|
<a href="{{url_for('web.books_list', data='publisher', sort_param='new', book_id=entry.publishers[0].id ) }}">{{entry.publishers[0].name}}</a>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if entry.pubdate[:10] != '0101-01-01' %}
|
{% if (entry.pubdate|string)[:10] != '0101-01-01' %}
|
||||||
<div class="publishing-date">
|
<div class="publishing-date">
|
||||||
<p>{{_('Published')}}: {{entry.pubdate|formatdate}} </p>
|
<p>{{_('Published')}}: {{entry.pubdate|formatdate}} </p>
|
||||||
</div>
|
</div>
|
||||||
@ -281,7 +281,7 @@
|
|||||||
{% if g.user.role_edit() %}
|
{% if g.user.role_edit() %}
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group" role="group" aria-label="Edit/Delete book">
|
<div class="btn-group" role="group" aria-label="Edit/Delete book">
|
||||||
<a href="{{ url_for('editbook.edit_book', book_id=entry.id) }}" class="btn btn-sm btn-warning" id="edit_book" role="button"><span class="glyphicon glyphicon-edit"></span> {{_('Edit Metadata')}}</a>
|
<a href="{{ url_for('editbook.edit_book', book_id=entry.id) }}" class="btn btn-sm btn-primary" id="edit_book" role="button"><span class="glyphicon glyphicon-edit"></span> {{_('Edit Metadata')}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span class="author-hidden-divider">&</span>
|
<span class="author-hidden-divider">&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% if loop.last %}
|
{% if loop.last %}
|
||||||
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -30,7 +30,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span>&</span>
|
<span>&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
|
@ -27,13 +27,13 @@
|
|||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
<div class="col-sm-3 col-lg-2 col-xs-6 book sortable" {% if entry[0].sort %}data-name="{{entry[0].series[0].name}}"{% endif %} data-id="{% if entry[0].series[0].name %}{{entry[0].series[0].name}}{% endif %}">
|
<div class="col-sm-3 col-lg-2 col-xs-6 book sortable" {% if entry[0].sort %}data-name="{{entry[0].series[0].name}}"{% endif %} data-id="{% if entry[0].series[0].name %}{{entry[0].series[0].name}}{% endif %}">
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
<a href="{{url_for('web.books_list', data=data, sort='new', book_id=entry[0].series[0].id )}}">
|
<a href="{{url_for('web.books_list', data=data, sort_param='new', book_id=entry[0].series[0].id )}}">
|
||||||
<img src="{{ url_for('web.get_cover', book_id=entry[0].id) }}" alt="{{ entry[0].name }}"/>
|
<img src="{{ url_for('web.get_cover', book_id=entry[0].id) }}" alt="{{ entry[0].name }}"/>
|
||||||
<span class="badge">{{entry.count}}</span>
|
<span class="badge">{{entry.count}}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<a href="{{url_for('web.books_list', data=data, sort='new', book_id=entry[0].series[0].id )}}">
|
<a href="{{url_for('web.books_list', data=data, sort_param='new', book_id=entry[0].series[0].id )}}">
|
||||||
<p class="title">{{entry[0].series[0].name|shortentitle}}</p>
|
<p class="title">{{entry[0].series[0].name|shortentitle}}</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span class="author-hidden-divider">&</span>
|
<span class="author-hidden-divider">&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% if loop.last %}
|
{% if loop.last %}
|
||||||
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -29,7 +29,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span>&</span>
|
<span>&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
@ -54,14 +54,14 @@
|
|||||||
<div class="discover load-more">
|
<div class="discover load-more">
|
||||||
<h2 class="{{title}}">{{_(title)}}</h2>
|
<h2 class="{{title}}">{{_(title)}}</h2>
|
||||||
<div class="filterheader hidden-xs hidden-sm">
|
<div class="filterheader hidden-xs hidden-sm">
|
||||||
<a data-toggle="tooltip" id="new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort='new')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
<a data-toggle="tooltip" id="new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='new')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
||||||
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort='old')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='old')}}"><span class="glyphicon glyphicon-book"></span> <span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
||||||
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
|
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
|
||||||
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
|
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
|
||||||
<a id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort='pubnew')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
<a id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubnew')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
||||||
<a id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
<a id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
||||||
<!--div class="btn-group character">
|
<!--div class="btn-group character">
|
||||||
<a id="no_shelf" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort='pubold')}}"><span class="glyphicon glyphicon-list"></span> <b>{{_('Group by series')}}</b></a>
|
<a id="no_shelf" class="btn btn-primary" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubold')}}"><span class="glyphicon glyphicon-list"></span> <b>{{_('Group by series')}}</b></a>
|
||||||
</div-->
|
</div-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -84,7 +84,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span class="author-hidden-divider">&</span>
|
<span class="author-hidden-divider">&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', book_id=author.id, sort='new') }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', book_id=author.id, sort_param='new') }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% if loop.last %}
|
{% if loop.last %}
|
||||||
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -92,7 +92,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span>&</span>
|
<span>&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name" href="{{url_for('web.books_list', data='author', book_id=author.id, sort='new') }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name" href="{{url_for('web.books_list', data='author', book_id=author.id, sort_param='new') }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for format in entry.data %}
|
{% for format in entry.data %}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-2 col-sm-2 col-md-1" align="left"><span class="badge">{{lang_counter[loop.index0].bookcount}}</span></div>
|
<div class="col-xs-2 col-sm-2 col-md-1" align="left"><span class="badge">{{lang_counter[loop.index0].bookcount}}</span></div>
|
||||||
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{{url_for('web.books_list', book_id=lang.lang_code, data=data, sort='new')}}">{{lang.name}}</a></div>
|
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{{url_for('web.books_list', book_id=lang.lang_code, data=data, sort_param='new')}}">{{lang.name}}</a></div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% from 'modal_restriction.html' import restrict_modal %}
|
{% from 'modal_dialogs.html' import restrict_modal, delete_book %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ g.user.locale }}">
|
<html lang="{{ g.user.locale }}">
|
||||||
<head>
|
<head>
|
||||||
@ -128,7 +128,7 @@
|
|||||||
<li class="nav-head hidden-xs">{{_('Browse')}}</li>
|
<li class="nav-head hidden-xs">{{_('Browse')}}</li>
|
||||||
{% for element in sidebar %}
|
{% for element in sidebar %}
|
||||||
{% if g.user.check_visibility(element['visibility']) and element['public'] %}
|
{% if g.user.check_visibility(element['visibility']) and element['public'] %}
|
||||||
<li id="nav_{{element['id']}}" {% if page == element['page'] %}class="active"{% endif %}><a href="{{url_for(element['link'], data=element['page'], sort='new')}}"><span class="glyphicon {{element['glyph']}}"></span>{{_(element['text'])}}</a></li>
|
<li id="nav_{{element['id']}}" {% if page == element['page'] %}class="active"{% endif %}><a href="{{url_for(element['link'], data=element['page'], sort_param='stored')}}"><span class="glyphicon {{element['glyph']}}"></span>{{_(element['text'])}}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if g.user.is_authenticated or g.allow_anonymous %}
|
{% if g.user.is_authenticated or g.allow_anonymous %}
|
||||||
@ -136,10 +136,6 @@
|
|||||||
{% for shelf in g.shelves_access %}
|
{% for shelf in g.shelves_access %}
|
||||||
<li><a href="{{url_for('shelf.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list shelf"></span>{{shelf.name|shortentitle(40)}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}</a></li>
|
<li><a href="{{url_for('shelf.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list shelf"></span>{{shelf.name|shortentitle(40)}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<!--li class="nav-head hidden-xs your-shelves">{{_('Your Shelves')}}</li>
|
|
||||||
{% for shelf in g.user.shelf %}
|
|
||||||
<li><a href="{{url_for('shelf.show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list private_shelf"></span>{{shelf.name|shortentitle(40)}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}</a></li>
|
|
||||||
{% endfor %}-->
|
|
||||||
{% if not g.user.is_anonymous %}
|
{% if not g.user.is_anonymous %}
|
||||||
<li id="nav_createshelf" class="create-shelf"><a href="{{url_for('shelf.create_shelf')}}">{{_('Create a Shelf')}}</a></li>
|
<li id="nav_createshelf" class="create-shelf"><a href="{{url_for('shelf.create_shelf')}}">{{_('Create a Shelf')}}</a></li>
|
||||||
<li id="nav_about" {% if page == 'stat' %}class="active"{% endif %}><a href="{{url_for('about.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('about.stats')}}"><span class="glyphicon glyphicon-info-sign"></span> {{_('About')}}</a></li>
|
||||||
@ -155,29 +151,29 @@
|
|||||||
{% 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 %}
|
{% if pagination.has_prev %}
|
||||||
<a class="previous" href="{{ (pagination.page - 1)|url_for_other_page
|
<li class="page-item page-next"><a class="page-link" aria-label="next page" href="{{ (pagination.page - 1)|url_for_other_page
|
||||||
}}">« {{_('Previous')}}</a>
|
}}">« {{_('Previous')}}</a></li>
|
||||||
{% endif %}
|
{% 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 %}
|
||||||
<a href="{{ (page)|url_for_other_page }}">{{ page }}</a>
|
<li class="page-item"><a class="page-link" aria-label="to page {{ page }}" href="{{ (page)|url_for_other_page }}">{{ page }}</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<strong>{{ page }}</strong>
|
<li class="page-item active"><a class="page-link" aria-label="to page {{ page }}" href="{{ (page)|url_for_other_page }}">{{ page }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="ellipsis">…</span>
|
<li class="page-item page-last-separator disabled"><a class="page-link" aria-label="">…</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if pagination.has_next %}
|
{% if pagination.has_next %}
|
||||||
<a class="next" href="{{ (pagination.page + 1)|url_for_other_page
|
<li class="page-item page-next"><a class="page-link" aria-label="next page" href="{{ (pagination.page + 1)|url_for_other_page
|
||||||
}}">{{_('Next')}} »</a>
|
}}">{{_('Next')}} »</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="modal fade" id="bookDetailsModal" tabindex="-1" role="dialog" aria-labelledby="bookDetailsModalLabel">
|
<div class="modal fade" id="bookDetailsModal" tabindex="-1" role="dialog" aria-labelledby="bookDetailsModalLabel">
|
||||||
<div class="modal-dialog modal-lg" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@ -196,7 +192,6 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
<!-- 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>
|
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
|
||||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
<!-- 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/bootstrap.min.js') }}"></script>
|
||||||
@ -227,9 +222,11 @@
|
|||||||
});
|
});
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
var inp = $('#query').first()
|
var inp = $('#query').first()
|
||||||
var val = inp.val()
|
if (inp.length) {
|
||||||
if (val !== "undefined") {
|
var val = inp.val()
|
||||||
|
if (val.length) {
|
||||||
inp.val('').blur().focus().val(val)
|
inp.val('').blur().focus().val(val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="row" {% if entry[0].sort %}data-name="{{entry[0].name}}"{% endif %} data-id="{% if entry[0].sort %}{{entry[0].sort}}{% else %}{% if entry.name %}{{entry.name}}{% else %}{{entry[0].name}}{% endif %}{% endif %}">
|
<div class="row" {% if entry[0].sort %}data-name="{{entry[0].name}}"{% endif %} data-id="{% if entry[0].sort %}{{entry[0].sort}}{% else %}{% if entry.name %}{{entry.name}}{% else %}{{entry[0].name}}{% endif %}{% endif %}">
|
||||||
<div class="col-xs-2 col-sm-2 col-md-1" align="left"><span class="badge">{{entry.count}}</span></div>
|
<div class="col-xs-2 col-sm-2 col-md-1" align="left"><span class="badge">{{entry.count}}</span></div>
|
||||||
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{% if entry.format %}{{url_for('web.books_list', data=data, sort='new', book_id=entry.format )}}{% else %}{{url_for('web.books_list', data=data, sort='new', book_id=entry[0].id )}}{% endif %}">
|
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{% if entry.format %}{{url_for('web.books_list', data=data, sort_param='new', book_id=entry.format )}}{% else %}{{url_for('web.books_list', data=data, sort_param='new', book_id=entry[0].id )}}{% endif %}">
|
||||||
{% if entry.name %}
|
{% if entry.name %}
|
||||||
<div class="rating">
|
<div class="rating">
|
||||||
{% for number in range(entry.name) %}
|
{% for number in range(entry.name) %}
|
||||||
|
@ -37,3 +37,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
{% macro delete_book(bookid) %}
|
||||||
|
{% if g.user.role_delete_books() %}
|
||||||
|
<div class="modal fade" id="deleteModal" role="dialog" aria-labelledby="metaDeleteLabel">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-danger text-center">
|
||||||
|
<span>{{_('Are you really sure?')}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body text-center">
|
||||||
|
<p>
|
||||||
|
<span class="hidden" id="book_format">{{_('This book format will be permanently erased from database')}}</span>
|
||||||
|
<span class="hidden" id="book_complete">{{_('This book will be permanently erased from database')}}</span>
|
||||||
|
<span>{{_('and hard disk')}}</span>
|
||||||
|
</p>
|
||||||
|
{% if config.config_kobo_sync %}
|
||||||
|
<p>
|
||||||
|
<span>{{_('Important Kobo Note: deleted books will remain on any paired Kobo device.')}}</span>
|
||||||
|
<span>{{_('Books must first be archived and the device synced before a book can safely be deleted.')}}</span>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<input type="button" class="btn btn-danger" value="{{_('Delete')}}" name="delete_confirm" id="delete_confirm" data-dismiss="modal">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
@ -14,8 +14,13 @@
|
|||||||
|
|
||||||
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/io/bytestream.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/io/bytebuffer.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/io/bitstream.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/archive/archive.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/archive/rarvm.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/archive/unrar5.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/kthoom.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/kthoom.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/archive/archive.js') }}"></script>
|
|
||||||
<script>
|
<script>
|
||||||
var updateArrows = function() {
|
var updateArrows = function() {
|
||||||
if ($('input[name="direction"]:checked').val() === "0") {
|
if ($('input[name="direction"]:checked').val() === "0") {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<h2>{{_('No Results Found')}} {{adv_searchterm}}</h2>
|
<h2>{{_('No Results Found')}} {{adv_searchterm}}</h2>
|
||||||
<p>{{_('Search Term:')}} {{adv_searchterm}}</p>
|
<p>{{_('Search Term:')}} {{adv_searchterm}}</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h2>{{entries|length}} {{_('Results for:')}} {{adv_searchterm}}</h2>
|
<h2>{{result_count}} {{_('Results for:')}} {{adv_searchterm}}</h2>
|
||||||
{% if g.user.is_authenticated %}
|
{% if g.user.is_authenticated %}
|
||||||
{% if g.user.shelf.all() or g.shelves_access %}
|
{% if g.user.shelf.all() or g.shelves_access %}
|
||||||
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
<div id="shelf-actions" class="btn-toolbar" role="toolbar">
|
||||||
@ -25,18 +25,14 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<!--div class="filterheader hidden-xs hidden-sm"--><!-- ToDo: Implement filter for search results -->
|
<div class="filterheader hidden-xs hidden-sm"><!-- ToDo: Implement filter for search results -->
|
||||||
<!--a id="new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='new')}}"><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
<a id="new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='new', query=query)}}"><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
||||||
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='old')}}"><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='old', query=query)}}"><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
||||||
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
|
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='abc', query=query)}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
|
||||||
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
|
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='zyx', query=query)}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
|
||||||
<a id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='pubnew')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
<a id="pub_new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='pubnew', query=query)}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order"></span></a>
|
||||||
<a id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='pubold')}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
<a id="pub_old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='pubold', query=query)}}"><span class="glyphicon glyphicon-calendar"></span><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group character" role="group">
|
|
||||||
<a id="no_shelf" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='pubold')}}"><span class="glyphicon glyphicon-list"></span><b>?</b></a>
|
|
||||||
<div id="all" class="btn btn-primary">{{_('All')}}</div>
|
|
||||||
</div-->
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -59,7 +55,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span class="author-hidden-divider">&</span>
|
<span class="author-hidden-divider">&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% if loop.last %}
|
{% if loop.last %}
|
||||||
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -67,7 +63,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span>&</span>
|
<span>&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for format in entry.data %}
|
{% for format in entry.data %}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="col-md-10 col-lg-6">
|
<div class="col-md-10 col-lg-6">
|
||||||
<form role="form" id="search" action="{{ url_for('web.advanced_search') }}" method="GET">
|
<form role="form" id="search" action="{{ url_for('web.advanced_search_form') }}" method="POST">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="book_title">{{_('Book Title')}}</label>
|
<label for="book_title">{{_('Book Title')}}</label>
|
||||||
<input type="text" class="form-control" name="book_title" id="book_title" value="">
|
<input type="text" class="form-control" name="book_title" id="book_title" value="">
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span class="author-hidden-divider">&</span>
|
<span class="author-hidden-divider">&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name author-hidden" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% if loop.last %}
|
{% if loop.last %}
|
||||||
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
<a href="#" class="author-expand" data-authors-max="{{g.config_authors_max}}" data-collapse-caption="({{_('reduce')}})">(...)</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -39,7 +39,7 @@
|
|||||||
{% if not loop.first %}
|
{% if not loop.first %}
|
||||||
<span>&</span>
|
<span>&</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="author-name" href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
<a class="author-name" href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')|shortentitle(30)}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
<p class="title">{{entry.title|shortentitle}}</p>
|
<p class="title">{{entry.title|shortentitle}}</p>
|
||||||
<p class="author">
|
<p class="author">
|
||||||
{% for author in entry.authors %}
|
{% for author in entry.authors %}
|
||||||
<a href="{{url_for('web.books_list', data='author', sort='new', book_id=author.id) }}">{{author.name.replace('|',',')}}</a>
|
<a href="{{url_for('web.books_list', data='author', sort_param='new', book_id=author.id) }}">{{author.name.replace('|',',')}}</a>
|
||||||
{% if not loop.last %}
|
{% if not loop.last %}
|
||||||
&
|
&
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
28
cps/ub.py
28
cps/ub.py
@ -23,6 +23,7 @@ import sys
|
|||||||
import datetime
|
import datetime
|
||||||
import itertools
|
import itertools
|
||||||
import uuid
|
import uuid
|
||||||
|
import json
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
|
||||||
from flask import g
|
from flask import g
|
||||||
@ -41,7 +42,7 @@ except ImportError:
|
|||||||
oauth_support = False
|
oauth_support = False
|
||||||
from sqlalchemy import create_engine, exc, exists, event
|
from sqlalchemy import create_engine, exc, exists, event
|
||||||
from sqlalchemy import Column, ForeignKey
|
from sqlalchemy import Column, ForeignKey
|
||||||
from sqlalchemy import String, Integer, SmallInteger, Boolean, DateTime, Float
|
from sqlalchemy import String, Integer, SmallInteger, Boolean, DateTime, Float, JSON
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import backref, relationship, sessionmaker, Session
|
from sqlalchemy.orm import backref, relationship, sessionmaker, Session
|
||||||
from werkzeug.security import generate_password_hash
|
from werkzeug.security import generate_password_hash
|
||||||
@ -109,10 +110,10 @@ def get_sidebar_config(kwargs=None):
|
|||||||
{"glyph": "glyphicon-trash", "text": _('Archived Books'), "link": 'web.books_list', "id": "archived",
|
{"glyph": "glyphicon-trash", "text": _('Archived Books'), "link": 'web.books_list', "id": "archived",
|
||||||
"visibility": constants.SIDEBAR_ARCHIVED, 'public': (not g.user.is_anonymous), "page": "archived",
|
"visibility": constants.SIDEBAR_ARCHIVED, 'public': (not g.user.is_anonymous), "page": "archived",
|
||||||
"show_text": _('Show archived books'), "config_show": content})
|
"show_text": _('Show archived books'), "config_show": content})
|
||||||
'''sidebar.append(
|
sidebar.append(
|
||||||
{"glyph": "glyphicon-th-list", "text": _('Books List'), "link": 'web.books_list', "id": "list",
|
{"glyph": "glyphicon-th-list", "text": _('Books List'), "link": 'web.books_table', "id": "list",
|
||||||
"visibility": constants.SIDEBAR_LIST, 'public': (not g.user.is_anonymous), "page": "list",
|
"visibility": constants.SIDEBAR_LIST, 'public': (not g.user.is_anonymous), "page": "list",
|
||||||
"show_text": _('Show Books List'), "config_show": content})'''
|
"show_text": _('Show Books List'), "config_show": content})
|
||||||
|
|
||||||
return sidebar
|
return sidebar
|
||||||
|
|
||||||
@ -218,7 +219,9 @@ class User(UserBase, Base):
|
|||||||
denied_column_value = Column(String, default="")
|
denied_column_value = Column(String, default="")
|
||||||
allowed_column_value = Column(String, default="")
|
allowed_column_value = Column(String, default="")
|
||||||
remote_auth_token = relationship('RemoteAuthToken', backref='user', lazy='dynamic')
|
remote_auth_token = relationship('RemoteAuthToken', backref='user', lazy='dynamic')
|
||||||
series_view = Column(String(10), default="list")
|
#series_view = Column(String(10), default="list")
|
||||||
|
view_settings = Column(JSON, default={})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if oauth_support:
|
if oauth_support:
|
||||||
@ -259,7 +262,8 @@ class Anonymous(AnonymousUserMixin, UserBase):
|
|||||||
self.allowed_tags = data.allowed_tags
|
self.allowed_tags = data.allowed_tags
|
||||||
self.denied_column_value = data.denied_column_value
|
self.denied_column_value = data.denied_column_value
|
||||||
self.allowed_column_value = data.allowed_column_value
|
self.allowed_column_value = data.allowed_column_value
|
||||||
self.series_view = data.series_view
|
self.view_settings = data.view_settings
|
||||||
|
|
||||||
|
|
||||||
def role_admin(self):
|
def role_admin(self):
|
||||||
return False
|
return False
|
||||||
@ -567,10 +571,11 @@ def migrate_Database(session):
|
|||||||
conn.execute("ALTER TABLE user ADD column `allowed_column_value` String DEFAULT ''")
|
conn.execute("ALTER TABLE user ADD column `allowed_column_value` String DEFAULT ''")
|
||||||
session.commit()
|
session.commit()
|
||||||
try:
|
try:
|
||||||
session.query(exists().where(User.series_view)).scalar()
|
session.query(exists().where(User.view_settings)).scalar()
|
||||||
except exc.OperationalError:
|
except exc.OperationalError:
|
||||||
with engine.connect() as conn:
|
with engine.connect() as conn:
|
||||||
conn.execute("ALTER TABLE user ADD column `series_view` VARCHAR(10) DEFAULT 'list'")
|
conn.execute("ALTER TABLE user ADD column `view_settings` VARCHAR(10) DEFAULT '{}'")
|
||||||
|
session.commit()
|
||||||
|
|
||||||
if session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS).first() \
|
if session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS).first() \
|
||||||
is None:
|
is None:
|
||||||
@ -591,14 +596,15 @@ def migrate_Database(session):
|
|||||||
"locale VARCHAR(2),"
|
"locale VARCHAR(2),"
|
||||||
"sidebar_view INTEGER,"
|
"sidebar_view INTEGER,"
|
||||||
"default_language VARCHAR(3),"
|
"default_language VARCHAR(3),"
|
||||||
"series_view VARCHAR(10),"
|
# "series_view VARCHAR(10),"
|
||||||
|
"view_settings VARCHAR,"
|
||||||
"UNIQUE (nickname),"
|
"UNIQUE (nickname),"
|
||||||
"UNIQUE (email))")
|
"UNIQUE (email))")
|
||||||
conn.execute("INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale,"
|
conn.execute("INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale,"
|
||||||
"sidebar_view, default_language, series_view) "
|
"sidebar_view, default_language, view_settings) "
|
||||||
"SELECT id, nickname, email, role, password, kindle_mail, locale,"
|
"SELECT id, nickname, email, role, password, kindle_mail, locale,"
|
||||||
"sidebar_view, default_language FROM user")
|
"sidebar_view, default_language FROM user")
|
||||||
# delete old user table and rename new user_id table to user:
|
# delete old user table and rename new user_id table to user:
|
||||||
conn.execute("DROP TABLE user")
|
conn.execute("DROP TABLE user")
|
||||||
conn.execute("ALTER TABLE user_id RENAME TO user")
|
conn.execute("ALTER TABLE user_id RENAME TO user")
|
||||||
session.commit()
|
session.commit()
|
||||||
|
463
cps/web.py
463
cps/web.py
@ -30,8 +30,8 @@ import traceback
|
|||||||
import binascii
|
import binascii
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from babel import Locale as LC
|
|
||||||
from babel.dates import format_date
|
from babel.dates import format_date
|
||||||
|
from babel import Locale as LC
|
||||||
from babel.core import UnknownLocaleError
|
from babel.core import UnknownLocaleError
|
||||||
from flask import Blueprint, jsonify
|
from flask import Blueprint, jsonify
|
||||||
from flask import render_template, request, redirect, send_from_directory, make_response, g, flash, abort, url_for
|
from flask import render_template, request, redirect, send_from_directory, make_response, g, flash, abort, url_for
|
||||||
@ -39,6 +39,7 @@ from flask_babel import gettext as _
|
|||||||
from flask_login import login_user, logout_user, login_required, current_user, confirm_login
|
from flask_login import login_user, logout_user, login_required, current_user, confirm_login
|
||||||
from sqlalchemy.exc import IntegrityError, InvalidRequestError, OperationalError
|
from sqlalchemy.exc import IntegrityError, InvalidRequestError, OperationalError
|
||||||
from sqlalchemy.sql.expression import text, func, true, false, not_, and_, or_
|
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, InternalServerError
|
||||||
from sqlalchemy.sql.functions import coalesce
|
from sqlalchemy.sql.functions import coalesce
|
||||||
|
|
||||||
@ -287,14 +288,6 @@ def edit_required(f):
|
|||||||
# ################################### Helper functions ################################################################
|
# ################################### Helper functions ################################################################
|
||||||
|
|
||||||
|
|
||||||
# Returns the template for rendering and includes the instance name
|
|
||||||
def render_title_template(*args, **kwargs):
|
|
||||||
sidebar = ub.get_sidebar_config(kwargs)
|
|
||||||
return render_template(instance=config.config_calibre_web_title, sidebar=sidebar,
|
|
||||||
accept=constants.EXTENSIONS_UPLOAD,
|
|
||||||
*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@web.before_app_request
|
@web.before_app_request
|
||||||
def before_request():
|
def before_request():
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
@ -472,20 +465,30 @@ def toggle_archived(book_id):
|
|||||||
@web.route("/ajax/view", methods=["POST"])
|
@web.route("/ajax/view", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def update_view():
|
def update_view():
|
||||||
to_save = request.form.to_dict()
|
to_save = request.get_json()
|
||||||
allowed_view = ['grid', 'list']
|
allowed_view = ['grid', 'list']
|
||||||
if "series_view" in to_save and to_save["series_view"] in allowed_view:
|
if "series_view" in to_save and to_save["series_view"] in allowed_view:
|
||||||
current_user.series_view = to_save["series_view"]
|
try:
|
||||||
|
#visibility = json.loads(current_user.view_settings)
|
||||||
|
current_user.view_settings['series_view'] = to_save["series_view"]
|
||||||
|
# current_user.view_settings = json.dumps(visibility)
|
||||||
|
try:
|
||||||
|
flag_modified(current_user, "view_settings")
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
ub.session.commit()
|
||||||
|
except InvalidRequestError:
|
||||||
|
log.error("Invalid request received: %r ", request, )
|
||||||
|
return "Invalid request", 400
|
||||||
|
except Exception:
|
||||||
|
log.error("Could not save series_view_settings: %r %r", request, to_save)
|
||||||
|
return "Invalid request", 400
|
||||||
|
elif "authorslist" in to_save:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
log.error("Invalid request received: %r %r", request, to_save)
|
log.error("Invalid request received: %r %r", request, to_save)
|
||||||
return "Invalid request", 400
|
return "Invalid request", 400
|
||||||
|
return "1", 200
|
||||||
try:
|
|
||||||
ub.session.commit()
|
|
||||||
except InvalidRequestError:
|
|
||||||
log.error("Invalid request received: %r ", request, )
|
|
||||||
return "Invalid request", 400
|
|
||||||
return "", 200
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -609,25 +612,29 @@ def get_matching_tags():
|
|||||||
return json_dumps
|
return json_dumps
|
||||||
|
|
||||||
|
|
||||||
# ################################### View Books list ##################################################################
|
# Returns the template for rendering and includes the instance name
|
||||||
|
def render_title_template(*args, **kwargs):
|
||||||
|
sidebar = ub.get_sidebar_config(kwargs)
|
||||||
|
return render_template(instance=config.config_calibre_web_title, sidebar=sidebar,
|
||||||
|
accept=constants.EXTENSIONS_UPLOAD,
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@web.route("/", defaults={'page': 1})
|
def render_books_list(data, sort, book_id, page):
|
||||||
@web.route('/page/<int:page>')
|
|
||||||
@login_required_if_no_ano
|
|
||||||
def index(page):
|
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page, db.Books, True, [db.Books.timestamp.desc()])
|
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
|
||||||
title=_(u"Recently Added Books"), page="root")
|
|
||||||
|
|
||||||
|
|
||||||
@web.route('/<data>/<sort>', defaults={'page': 1, 'book_id': "1"})
|
|
||||||
@web.route('/<data>/<sort>/', defaults={'page': 1, 'book_id': "1"})
|
|
||||||
@web.route('/<data>/<sort>/<book_id>', defaults={'page': 1})
|
|
||||||
@web.route('/<data>/<sort>/<book_id>/<int:page>')
|
|
||||||
@login_required_if_no_ano
|
|
||||||
def books_list(data, sort, book_id, page):
|
|
||||||
order = [db.Books.timestamp.desc()]
|
order = [db.Books.timestamp.desc()]
|
||||||
|
if sort == 'stored':
|
||||||
|
view = current_user.view_settings.get(data)
|
||||||
|
sort = view
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
current_user.view_settings[data] = sort
|
||||||
|
try:
|
||||||
|
flag_modified(current_user, "view_settings")
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
ub.session.commit()
|
||||||
|
except InvalidRequestError:
|
||||||
|
log.error("Invalid request received: %r ", request, )
|
||||||
if sort == 'pubnew':
|
if sort == 'pubnew':
|
||||||
order = [db.Books.pubdate.desc()]
|
order = [db.Books.pubdate.desc()]
|
||||||
if sort == 'pubold':
|
if sort == 'pubold':
|
||||||
@ -643,7 +650,7 @@ def books_list(data, sort, book_id, page):
|
|||||||
|
|
||||||
if data == "rated":
|
if data == "rated":
|
||||||
if current_user.check_visibility(constants.SIDEBAR_BEST_RATED):
|
if current_user.check_visibility(constants.SIDEBAR_BEST_RATED):
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page,
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.ratings.any(db.Ratings.rating > 9),
|
db.Books.ratings.any(db.Ratings.rating > 9),
|
||||||
order)
|
order)
|
||||||
@ -653,7 +660,7 @@ def books_list(data, sort, book_id, page):
|
|||||||
abort(404)
|
abort(404)
|
||||||
elif data == "discover":
|
elif data == "discover":
|
||||||
if current_user.check_visibility(constants.SIDEBAR_RANDOM):
|
if current_user.check_visibility(constants.SIDEBAR_RANDOM):
|
||||||
entries, __, pagination = calibre_db.fill_indexpage(page, db.Books, True, [func.randomblob(2)])
|
entries, __, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, [func.randomblob(2)])
|
||||||
pagination = Pagination(1, config.config_books_per_page, config.config_books_per_page)
|
pagination = Pagination(1, config.config_books_per_page, config.config_books_per_page)
|
||||||
return render_title_template('discover.html', entries=entries, pagination=pagination, id=book_id,
|
return render_title_template('discover.html', entries=entries, pagination=pagination, id=book_id,
|
||||||
title=_(u"Discover (Random Books)"), page="discover")
|
title=_(u"Discover (Random Books)"), page="discover")
|
||||||
@ -681,10 +688,18 @@ def books_list(data, sort, book_id, page):
|
|||||||
return render_language_books(page, book_id, order)
|
return render_language_books(page, book_id, order)
|
||||||
elif data == "archived":
|
elif data == "archived":
|
||||||
return render_archived_books(page, order)
|
return render_archived_books(page, order)
|
||||||
|
elif data == "search":
|
||||||
|
term = (request.args.get('query') or '')
|
||||||
|
offset = int(int(config.config_books_per_page) * (page - 1))
|
||||||
|
if '&' not in term:
|
||||||
|
return render_search_results(term, offset, order, config.config_books_per_page)
|
||||||
|
else:
|
||||||
|
return render_adv_search_results(term, offset, order, config.config_books_per_page)
|
||||||
else:
|
else:
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page, db.Books, True, order)
|
website = data or "newest"
|
||||||
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||||
title=_(u"Books"), page="newest")
|
title=_(u"Books"), page=website)
|
||||||
|
|
||||||
|
|
||||||
def render_hot_books(page):
|
def render_hot_books(page):
|
||||||
@ -717,7 +732,7 @@ def render_hot_books(page):
|
|||||||
|
|
||||||
|
|
||||||
def render_author_books(page, author_id, order):
|
def render_author_books(page, author_id, order):
|
||||||
entries, __, pagination = calibre_db.fill_indexpage(page,
|
entries, __, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.authors.any(db.Authors.id == author_id),
|
db.Books.authors.any(db.Authors.id == author_id),
|
||||||
[order[0], db.Series.name, db.Books.series_index],
|
[order[0], db.Series.name, db.Books.series_index],
|
||||||
@ -745,7 +760,7 @@ def render_author_books(page, author_id, order):
|
|||||||
def render_publisher_books(page, book_id, order):
|
def render_publisher_books(page, book_id, order):
|
||||||
publisher = calibre_db.session.query(db.Publishers).filter(db.Publishers.id == book_id).first()
|
publisher = calibre_db.session.query(db.Publishers).filter(db.Publishers.id == book_id).first()
|
||||||
if publisher:
|
if publisher:
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page,
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.publishers.any(db.Publishers.id == book_id),
|
db.Books.publishers.any(db.Publishers.id == book_id),
|
||||||
[db.Series.name, order[0], db.Books.series_index],
|
[db.Series.name, order[0], db.Books.series_index],
|
||||||
@ -760,7 +775,7 @@ def render_publisher_books(page, book_id, order):
|
|||||||
def render_series_books(page, book_id, order):
|
def render_series_books(page, book_id, order):
|
||||||
name = calibre_db.session.query(db.Series).filter(db.Series.id == book_id).first()
|
name = calibre_db.session.query(db.Series).filter(db.Series.id == book_id).first()
|
||||||
if name:
|
if name:
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page,
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.series.any(db.Series.id == book_id),
|
db.Books.series.any(db.Series.id == book_id),
|
||||||
[db.Books.series_index, order[0]])
|
[db.Books.series_index, order[0]])
|
||||||
@ -772,7 +787,7 @@ def render_series_books(page, book_id, order):
|
|||||||
|
|
||||||
def render_ratings_books(page, book_id, order):
|
def render_ratings_books(page, book_id, order):
|
||||||
name = calibre_db.session.query(db.Ratings).filter(db.Ratings.id == book_id).first()
|
name = calibre_db.session.query(db.Ratings).filter(db.Ratings.id == book_id).first()
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page,
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.ratings.any(db.Ratings.id == book_id),
|
db.Books.ratings.any(db.Ratings.id == book_id),
|
||||||
[db.Books.timestamp.desc(), order[0]])
|
[db.Books.timestamp.desc(), order[0]])
|
||||||
@ -786,7 +801,7 @@ def render_ratings_books(page, book_id, order):
|
|||||||
def render_formats_books(page, book_id, order):
|
def render_formats_books(page, book_id, order):
|
||||||
name = calibre_db.session.query(db.Data).filter(db.Data.format == book_id.upper()).first()
|
name = calibre_db.session.query(db.Data).filter(db.Data.format == book_id.upper()).first()
|
||||||
if name:
|
if name:
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page,
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.data.any(db.Data.format == book_id.upper()),
|
db.Books.data.any(db.Data.format == book_id.upper()),
|
||||||
[db.Books.timestamp.desc(), order[0]])
|
[db.Books.timestamp.desc(), order[0]])
|
||||||
@ -799,7 +814,7 @@ def render_formats_books(page, book_id, order):
|
|||||||
def render_category_books(page, book_id, order):
|
def render_category_books(page, book_id, order):
|
||||||
name = calibre_db.session.query(db.Tags).filter(db.Tags.id == book_id).first()
|
name = calibre_db.session.query(db.Tags).filter(db.Tags.id == book_id).first()
|
||||||
if name:
|
if name:
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page,
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.tags.any(db.Tags.id == book_id),
|
db.Books.tags.any(db.Tags.id == book_id),
|
||||||
[order[0], db.Series.name, db.Books.series_index],
|
[order[0], db.Series.name, db.Books.series_index],
|
||||||
@ -819,19 +834,201 @@ def render_language_books(page, name, order):
|
|||||||
lang_name = _(isoLanguages.get(part3=name).name)
|
lang_name = _(isoLanguages.get(part3=name).name)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
abort(404)
|
abort(404)
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page,
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.languages.any(db.Languages.lang_code == name),
|
db.Books.languages.any(db.Languages.lang_code == name),
|
||||||
[db.Books.timestamp.desc(), order[0]])
|
[db.Books.timestamp.desc(), order[0]])
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name,
|
||||||
title=_(u"Language: %(name)s", name=lang_name), page="language")
|
title=_(u"Language: %(name)s", name=lang_name), page="language")
|
||||||
|
|
||||||
|
def render_read_books(page, are_read, as_xml=False, order=None, *args, **kwargs):
|
||||||
|
order = order or []
|
||||||
|
if not config.config_read_column:
|
||||||
|
if are_read:
|
||||||
|
db_filter = and_(ub.ReadBook.user_id == int(current_user.id),
|
||||||
|
ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED)
|
||||||
|
else:
|
||||||
|
db_filter = coalesce(ub.ReadBook.read_status, 0) != ub.ReadBook.STATUS_FINISHED
|
||||||
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
|
db.Books,
|
||||||
|
db_filter,
|
||||||
|
order,
|
||||||
|
ub.ReadBook, db.Books.id==ub.ReadBook.book_id)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
if are_read:
|
||||||
|
db_filter = db.cc_classes[config.config_read_column].value == True
|
||||||
|
else:
|
||||||
|
db_filter = coalesce(db.cc_classes[config.config_read_column].value, False) != True
|
||||||
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
|
||||||
|
db.Books,
|
||||||
|
db_filter,
|
||||||
|
order,
|
||||||
|
db.cc_classes[config.config_read_column])
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column)
|
||||||
|
if not as_xml:
|
||||||
|
flash(_("Custom Column No.%(column)d is not existing in calibre database",
|
||||||
|
column=config.config_read_column),
|
||||||
|
category="error")
|
||||||
|
return redirect(url_for("web.index"))
|
||||||
|
# ToDo: Handle error Case for opds
|
||||||
|
if as_xml:
|
||||||
|
return entries, pagination
|
||||||
|
else:
|
||||||
|
if are_read:
|
||||||
|
name = _(u'Read Books') + ' (' + str(pagination.total_count) + ')'
|
||||||
|
pagename = "read"
|
||||||
|
else:
|
||||||
|
name = _(u'Unread Books') + ' (' + str(pagination.total_count) + ')'
|
||||||
|
pagename = "unread"
|
||||||
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||||
|
title=name, page=pagename)
|
||||||
|
|
||||||
'''@web.route("/table")
|
|
||||||
|
def render_archived_books(page, order):
|
||||||
|
order = order or []
|
||||||
|
archived_books = (
|
||||||
|
ub.session.query(ub.ArchivedBook)
|
||||||
|
.filter(ub.ArchivedBook.user_id == int(current_user.id))
|
||||||
|
.filter(ub.ArchivedBook.is_archived == True)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
archived_book_ids = [archived_book.book_id for archived_book in archived_books]
|
||||||
|
|
||||||
|
archived_filter = db.Books.id.in_(archived_book_ids)
|
||||||
|
|
||||||
|
entries, random, pagination = calibre_db.fill_indexpage_with_archived_books(page, 0,
|
||||||
|
db.Books,
|
||||||
|
archived_filter,
|
||||||
|
order,
|
||||||
|
allow_show_archived=True)
|
||||||
|
|
||||||
|
name = _(u'Archived Books') + ' (' + str(len(archived_book_ids)) + ')'
|
||||||
|
pagename = "archived"
|
||||||
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||||
|
title=name, page=pagename)
|
||||||
|
|
||||||
|
|
||||||
|
def render_prepare_search_form(cc):
|
||||||
|
# prepare data for search-form
|
||||||
|
tags = calibre_db.session.query(db.Tags)\
|
||||||
|
.join(db.books_tags_link)\
|
||||||
|
.join(db.Books)\
|
||||||
|
.filter(calibre_db.common_filters()) \
|
||||||
|
.group_by(text('books_tags_link.tag'))\
|
||||||
|
.order_by(db.Tags.name).all()
|
||||||
|
series = calibre_db.session.query(db.Series)\
|
||||||
|
.join(db.books_series_link)\
|
||||||
|
.join(db.Books)\
|
||||||
|
.filter(calibre_db.common_filters()) \
|
||||||
|
.group_by(text('books_series_link.series'))\
|
||||||
|
.order_by(db.Series.name)\
|
||||||
|
.filter(calibre_db.common_filters()).all()
|
||||||
|
extensions = calibre_db.session.query(db.Data)\
|
||||||
|
.join(db.Books)\
|
||||||
|
.filter(calibre_db.common_filters()) \
|
||||||
|
.group_by(db.Data.format)\
|
||||||
|
.order_by(db.Data.format).all()
|
||||||
|
if current_user.filter_language() == u"all":
|
||||||
|
languages = calibre_db.speaking_language()
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
|
def render_search_results(term, offset=None, order=None, limit=None):
|
||||||
|
entries, result_count = calibre_db.get_search_results(term, offset, order, limit)
|
||||||
|
ids = list()
|
||||||
|
for element in entries:
|
||||||
|
ids.append(element.id)
|
||||||
|
searched_ids[current_user.id] = ids
|
||||||
|
return render_title_template('search.html',
|
||||||
|
searchterm=term,
|
||||||
|
query=term,
|
||||||
|
adv_searchterm=term,
|
||||||
|
entries=entries,
|
||||||
|
result_count=result_count,
|
||||||
|
title=_(u"Search"),
|
||||||
|
page="search")
|
||||||
|
|
||||||
|
# ################################### View Books list ##################################################################
|
||||||
|
|
||||||
|
|
||||||
|
@web.route("/", defaults={'page': 1})
|
||||||
|
@web.route('/page/<int:page>')
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
|
def index(page):
|
||||||
|
sort_param = (request.args.get('sort') or 'stored').lower()
|
||||||
|
return render_books_list("newest", sort_param, 1, page)
|
||||||
|
|
||||||
|
|
||||||
|
@web.route('/<data>/<sort_param>', defaults={'page': 1, 'book_id': "1"})
|
||||||
|
@web.route('/<data>/<sort_param>/', defaults={'page': 1, 'book_id': "1"})
|
||||||
|
@web.route('/<data>/<sort_param>/<book_id>', defaults={'page': 1})
|
||||||
|
@web.route('/<data>/<sort_param>/<book_id>/<int:page>')
|
||||||
|
@login_required_if_no_ano
|
||||||
|
def books_list(data, sort_param, book_id, page):
|
||||||
|
return render_books_list(data, sort_param, book_id, page)
|
||||||
|
|
||||||
|
|
||||||
|
@web.route("/table")
|
||||||
|
@login_required
|
||||||
def books_table():
|
def books_table():
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name,
|
visibility = current_user.view_settings.get('table', {})
|
||||||
title=_(u"Language: %(name)s", name=lang_name), page="language")'''
|
return render_title_template('book_table.html', title=_(u"Books list"), page="book_table",
|
||||||
|
visiblility=visibility)
|
||||||
|
|
||||||
|
@web.route("/ajax/listbooks")
|
||||||
|
@login_required
|
||||||
|
def list_books():
|
||||||
|
off = request.args.get("offset") or 0
|
||||||
|
limit = request.args.get("limit") or config.config_books_per_page
|
||||||
|
sort = request.args.get("sort")
|
||||||
|
if request.args.get("order") == 'asc':
|
||||||
|
order = [db.Books.timestamp.asc()]
|
||||||
|
else:
|
||||||
|
order = [db.Books.timestamp.desc()]
|
||||||
|
search = request.args.get("search")
|
||||||
|
total_count = calibre_db.session.query(db.Books).count()
|
||||||
|
if search:
|
||||||
|
entries, filtered_count = calibre_db.get_search_results(search, off, order, limit)
|
||||||
|
else:
|
||||||
|
entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order)
|
||||||
|
filtered_count = total_count
|
||||||
|
for entry in entries:
|
||||||
|
for index in range(0, len(entry.languages)):
|
||||||
|
try:
|
||||||
|
entry.languages[index].language_name = LC.parse(entry.languages[index].lang_code)\
|
||||||
|
.get_language_name(get_locale())
|
||||||
|
except UnknownLocaleError:
|
||||||
|
entry.languages[index].language_name = _(
|
||||||
|
isoLanguages.get(part3=entry.languages[index].lang_code).name)
|
||||||
|
table_entries = {'totalNotFiltered': total_count, 'total': filtered_count, "rows": entries}
|
||||||
|
js_list = json.dumps(table_entries, cls=db.AlchemyEncoder)
|
||||||
|
#js_list = json.dumps(entries, cls=db.AlchemyEncoder)
|
||||||
|
|
||||||
|
response = make_response(js_list)
|
||||||
|
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
||||||
|
return response
|
||||||
|
|
||||||
|
@web.route("/ajax/table_settings", methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def update_table_settings():
|
||||||
|
# vals = request.get_json()
|
||||||
|
# ToDo: Save table settings
|
||||||
|
current_user.view_settings['table'] = json.loads(request.data)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
flag_modified(current_user, "view_settings")
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
ub.session.commit()
|
||||||
|
except InvalidRequestError:
|
||||||
|
log.error("Invalid request received: %r ", request, )
|
||||||
|
return "Invalid request", 400
|
||||||
|
return ""
|
||||||
|
|
||||||
@web.route("/author")
|
@web.route("/author")
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
@ -871,7 +1068,7 @@ def publisher_list():
|
|||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
def series_list():
|
def series_list():
|
||||||
if current_user.check_visibility(constants.SIDEBAR_SERIES):
|
if current_user.check_visibility(constants.SIDEBAR_SERIES):
|
||||||
if current_user.series_view == 'list':
|
if current_user.view_settings.get('series_view') == 'list':
|
||||||
entries = calibre_db.session.query(db.Series, func.count('books_series_link.book').label('count')) \
|
entries = calibre_db.session.query(db.Series, func.count('books_series_link.book').label('count')) \
|
||||||
.join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \
|
.join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \
|
||||||
.group_by(text('books_series_link.series')).order_by(db.Series.sort).all()
|
.group_by(text('books_series_link.series')).order_by(db.Series.sort).all()
|
||||||
@ -988,55 +1185,43 @@ def reconnect():
|
|||||||
|
|
||||||
# ################################### Search functions ################################################################
|
# ################################### Search functions ################################################################
|
||||||
|
|
||||||
|
|
||||||
@web.route("/search", methods=["GET"])
|
@web.route("/search", methods=["GET"])
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
def search():
|
def search():
|
||||||
term = request.args.get("query")
|
term = request.args.get("query")
|
||||||
if term:
|
if term:
|
||||||
entries = calibre_db.get_search_results(term)
|
return render_search_results(term)
|
||||||
ids = list()
|
|
||||||
for element in entries:
|
|
||||||
ids.append(element.id)
|
|
||||||
searched_ids[current_user.id] = ids
|
|
||||||
return render_title_template('search.html',
|
|
||||||
searchterm=term,
|
|
||||||
adv_searchterm=term,
|
|
||||||
entries=entries,
|
|
||||||
title=_(u"Search"),
|
|
||||||
page="search")
|
|
||||||
else:
|
else:
|
||||||
return render_title_template('search.html',
|
return render_title_template('search.html',
|
||||||
searchterm="",
|
searchterm="",
|
||||||
|
result_count=0,
|
||||||
title=_(u"Search"),
|
title=_(u"Search"),
|
||||||
page="search")
|
page="search")
|
||||||
|
|
||||||
|
@web.route("/advanced_search", methods=['POST'])
|
||||||
@web.route("/advanced_search", methods=['GET'])
|
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
def advanced_search():
|
def advanced_search():
|
||||||
# Build custom columns names
|
|
||||||
cc = get_cc_columns(filter_config_custom_read=True)
|
cc = get_cc_columns(filter_config_custom_read=True)
|
||||||
calibre_db.session.connection().connection.connection.create_function("lower", 1, db.lcase)
|
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)).order_by(db.Books.sort)
|
q = calibre_db.session.query(db.Books).filter(calibre_db.common_filters(True)).order_by(db.Books.sort)
|
||||||
|
|
||||||
include_tag_inputs = request.args.getlist('include_tag')
|
include_tag_inputs = request.form.getlist('include_tag')
|
||||||
exclude_tag_inputs = request.args.getlist('exclude_tag')
|
exclude_tag_inputs = request.form.getlist('exclude_tag')
|
||||||
include_series_inputs = request.args.getlist('include_serie')
|
include_series_inputs = request.form.getlist('include_serie')
|
||||||
exclude_series_inputs = request.args.getlist('exclude_serie')
|
exclude_series_inputs = request.form.getlist('exclude_serie')
|
||||||
include_languages_inputs = request.args.getlist('include_language')
|
include_languages_inputs = request.form.getlist('include_language')
|
||||||
exclude_languages_inputs = request.args.getlist('exclude_language')
|
exclude_languages_inputs = request.form.getlist('exclude_language')
|
||||||
include_extension_inputs = request.args.getlist('include_extension')
|
include_extension_inputs = request.form.getlist('include_extension')
|
||||||
exclude_extension_inputs = request.args.getlist('exclude_extension')
|
exclude_extension_inputs = request.form.getlist('exclude_extension')
|
||||||
|
|
||||||
author_name = request.args.get("author_name")
|
author_name = request.form.get("author_name")
|
||||||
book_title = request.args.get("book_title")
|
book_title = request.form.get("book_title")
|
||||||
publisher = request.args.get("publisher")
|
publisher = request.form.get("publisher")
|
||||||
pub_start = request.args.get("Publishstart")
|
pub_start = request.form.get("Publishstart")
|
||||||
pub_end = request.args.get("Publishend")
|
pub_end = request.form.get("Publishend")
|
||||||
rating_low = request.args.get("ratinghigh")
|
rating_low = request.form.get("ratinghigh")
|
||||||
rating_high = request.args.get("ratinglow")
|
rating_high = request.form.get("ratinglow")
|
||||||
description = request.args.get("comment")
|
description = request.form.get("comment")
|
||||||
if author_name:
|
if author_name:
|
||||||
author_name = author_name.strip().lower().replace(',', '|')
|
author_name = author_name.strip().lower().replace(',', '|')
|
||||||
if book_title:
|
if book_title:
|
||||||
@ -1047,8 +1232,8 @@ def advanced_search():
|
|||||||
searchterm = []
|
searchterm = []
|
||||||
cc_present = False
|
cc_present = False
|
||||||
for c in cc:
|
for c in cc:
|
||||||
if request.args.get('custom_column_' + str(c.id)):
|
if request.form.get('custom_column_' + str(c.id)):
|
||||||
searchterm.extend([(u"%s: %s" % (c.name, request.args.get('custom_column_' + str(c.id))))])
|
searchterm.extend([(u"%s: %s" % (c.name, request.form.get('custom_column_' + str(c.id))))])
|
||||||
cc_present = True
|
cc_present = True
|
||||||
|
|
||||||
if include_tag_inputs or exclude_tag_inputs or include_series_inputs or exclude_series_inputs or \
|
if include_tag_inputs or exclude_tag_inputs or include_series_inputs or exclude_series_inputs or \
|
||||||
@ -1087,8 +1272,8 @@ def advanced_search():
|
|||||||
searchterm.extend(ext for ext in exclude_extension_inputs)
|
searchterm.extend(ext for ext in exclude_extension_inputs)
|
||||||
# handle custom columns
|
# handle custom columns
|
||||||
for c in cc:
|
for c in cc:
|
||||||
if request.args.get('custom_column_' + str(c.id)):
|
if request.form.get('custom_column_' + str(c.id)):
|
||||||
searchterm.extend([(u"%s: %s" % (c.name, request.args.get('custom_column_' + str(c.id))))])
|
searchterm.extend([(u"%s: %s" % (c.name, request.form.get('custom_column_' + str(c.id))))])
|
||||||
searchterm = " + ".join(filter(None, searchterm))
|
searchterm = " + ".join(filter(None, searchterm))
|
||||||
q = q.filter()
|
q = q.filter()
|
||||||
if author_name:
|
if author_name:
|
||||||
@ -1131,7 +1316,7 @@ def advanced_search():
|
|||||||
|
|
||||||
# search custom culumns
|
# search custom culumns
|
||||||
for c in cc:
|
for c in cc:
|
||||||
custom_query = request.args.get('custom_column_' + str(c.id))
|
custom_query = request.form.get('custom_column_' + str(c.id))
|
||||||
if custom_query != '' and custom_query is not None:
|
if custom_query != '' and custom_query is not None:
|
||||||
if c.datatype == 'bool':
|
if c.datatype == 'bool':
|
||||||
q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any(
|
q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any(
|
||||||
@ -1150,103 +1335,22 @@ def advanced_search():
|
|||||||
for element in q:
|
for element in q:
|
||||||
ids.append(element.id)
|
ids.append(element.id)
|
||||||
searched_ids[current_user.id] = ids
|
searched_ids[current_user.id] = ids
|
||||||
return render_title_template('search.html', adv_searchterm=searchterm,
|
return render_title_template('search.html',
|
||||||
entries=q, title=_(u"search"), page="search")
|
adv_searchterm=searchterm,
|
||||||
# prepare data for search-form
|
query=request.form,
|
||||||
tags = calibre_db.session.query(db.Tags)\
|
entries=q,
|
||||||
.join(db.books_tags_link)\
|
result_count=len(q),
|
||||||
.join(db.Books)\
|
title=_(u"search"), page="search")
|
||||||
.filter(calibre_db.common_filters()) \
|
|
||||||
.group_by(text('books_tags_link.tag'))\
|
|
||||||
.order_by(db.Tags.name).all()
|
|
||||||
series = calibre_db.session.query(db.Series)\
|
|
||||||
.join(db.books_series_link)\
|
|
||||||
.join(db.Books)\
|
|
||||||
.filter(calibre_db.common_filters()) \
|
|
||||||
.group_by(text('books_series_link.series'))\
|
|
||||||
.order_by(db.Series.name)\
|
|
||||||
.filter(calibre_db.common_filters()).all()
|
|
||||||
extensions = calibre_db.session.query(db.Data)\
|
|
||||||
.join(db.Books)\
|
|
||||||
.filter(calibre_db.common_filters()) \
|
|
||||||
.group_by(db.Data.format)\
|
|
||||||
.order_by(db.Data.format).all()
|
|
||||||
if current_user.filter_language() == u"all":
|
|
||||||
languages = calibre_db.speaking_language()
|
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
def render_read_books(page, are_read, as_xml=False, order=None, *args, **kwargs):
|
@web.route("/advanced_search", methods=['GET'])
|
||||||
order = order or []
|
@login_required_if_no_ano
|
||||||
if not config.config_read_column:
|
def advanced_search_form():
|
||||||
if are_read:
|
# Build custom columns names
|
||||||
db_filter = and_(ub.ReadBook.user_id == int(current_user.id),
|
cc = get_cc_columns(filter_config_custom_read=True)
|
||||||
ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED)
|
return render_prepare_search_form(cc)
|
||||||
else:
|
|
||||||
db_filter = coalesce(ub.ReadBook.read_status, 0) != ub.ReadBook.STATUS_FINISHED
|
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page,
|
|
||||||
db.Books,
|
|
||||||
db_filter,
|
|
||||||
order,
|
|
||||||
ub.ReadBook, db.Books.id==ub.ReadBook.book_id)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
if are_read:
|
|
||||||
db_filter = db.cc_classes[config.config_read_column].value == True
|
|
||||||
else:
|
|
||||||
db_filter = coalesce(db.cc_classes[config.config_read_column].value, False) != True
|
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page,
|
|
||||||
db.Books,
|
|
||||||
db_filter,
|
|
||||||
order,
|
|
||||||
db.cc_classes[config.config_read_column])
|
|
||||||
except (KeyError, AttributeError):
|
|
||||||
log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column)
|
|
||||||
if not as_xml:
|
|
||||||
flash(_("Custom Column No.%(column)d is not existing in calibre database",
|
|
||||||
column=config.config_read_column),
|
|
||||||
category="error")
|
|
||||||
return redirect(url_for("web.index"))
|
|
||||||
# ToDo: Handle error Case for opds
|
|
||||||
if as_xml:
|
|
||||||
return entries, pagination
|
|
||||||
else:
|
|
||||||
if are_read:
|
|
||||||
name = _(u'Read Books') + ' (' + str(pagination.total_count) + ')'
|
|
||||||
pagename = "read"
|
|
||||||
else:
|
|
||||||
name = _(u'Unread Books') + ' (' + str(pagination.total_count) + ')'
|
|
||||||
pagename = "unread"
|
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
|
||||||
title=name, page=pagename)
|
|
||||||
|
|
||||||
|
|
||||||
def render_archived_books(page, order):
|
|
||||||
order = order or []
|
|
||||||
archived_books = (
|
|
||||||
ub.session.query(ub.ArchivedBook)
|
|
||||||
.filter(ub.ArchivedBook.user_id == int(current_user.id))
|
|
||||||
.filter(ub.ArchivedBook.is_archived == True)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
archived_book_ids = [archived_book.book_id for archived_book in archived_books]
|
|
||||||
|
|
||||||
archived_filter = db.Books.id.in_(archived_book_ids)
|
|
||||||
|
|
||||||
entries, random, pagination = calibre_db.fill_indexpage_with_archived_books(page,
|
|
||||||
db.Books,
|
|
||||||
archived_filter,
|
|
||||||
order,
|
|
||||||
allow_show_archived=True)
|
|
||||||
|
|
||||||
name = _(u'Archived Books') + ' (' + str(len(archived_book_ids)) + ')'
|
|
||||||
pagename = "archived"
|
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
|
||||||
title=name, page=pagename)
|
|
||||||
|
|
||||||
# ################################### Download/Send ##################################################################
|
# ################################### Download/Send ##################################################################
|
||||||
|
|
||||||
|
|
||||||
@ -1551,10 +1655,11 @@ def profile():
|
|||||||
languages = calibre_db.speaking_language()
|
languages = calibre_db.speaking_language()
|
||||||
translations = babel.list_translations() + [LC('en')]
|
translations = babel.list_translations() + [LC('en')]
|
||||||
kobo_support = feature_support['kobo'] and config.config_kobo_sync
|
kobo_support = feature_support['kobo'] and config.config_kobo_sync
|
||||||
if feature_support['oauth']:
|
if feature_support['oauth'] and config.config_login_type == 2:
|
||||||
oauth_status = get_oauth_status()
|
oauth_status = get_oauth_status()
|
||||||
else:
|
else:
|
||||||
oauth_status = None
|
oauth_status = None
|
||||||
|
oauth_check = {}
|
||||||
|
|
||||||
for book in current_user.downloads:
|
for book in current_user.downloads:
|
||||||
downloadBook = calibre_db.get_book(book.book_id)
|
downloadBook = calibre_db.get_book(book.book_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user