Skip to content

Commit

Permalink
Merge pull request #1 from apollo1220/feature/custom-pages
Browse files Browse the repository at this point in the history
Feature/custom pages
  • Loading branch information
apollo1220 committed Mar 21, 2024
2 parents bf014e5 + 84182e7 commit f20b0f3
Show file tree
Hide file tree
Showing 29 changed files with 674 additions and 289 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ gdrive_credentials
client_secrets.json
gmail.json
/.key

pages/
5 changes: 4 additions & 1 deletion cps/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1705,7 +1705,7 @@ def _db_configuration_update_helper():
return _db_configuration_result('{}'.format(ex), gdrive_error)

if db_change or not db_valid or not config.db_configured \
or config.config_calibre_dir != to_save["config_calibre_dir"]:
or config.config_calibre_dir != to_save["config_calibre_dir"]:
if not os.path.exists(metadata_db) or not to_save['config_calibre_dir']:
return _db_configuration_result(_('DB Location is not Valid, Please Enter Correct Path'), gdrive_error)
else:
Expand All @@ -1728,6 +1728,9 @@ def _db_configuration_update_helper():
calibre_db.update_config(config)
if not os.access(os.path.join(config.config_calibre_dir, "metadata.db"), os.W_OK):
flash(_("DB is not Writeable"), category="warning")
_config_string(to_save, "config_calibre_split_dir")
config.config_calibre_split = to_save.get('config_calibre_split', 0) == "on"
calibre_db.update_config(config)
config.save()
return _db_configuration_result(None, gdrive_error)

Expand Down
5 changes: 5 additions & 0 deletions cps/config_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ class _Settings(_Base):

config_calibre_dir = Column(String)
config_calibre_uuid = Column(String)
config_calibre_split = Column(Boolean, default=False)
config_calibre_split_dir = Column(String)
config_port = Column(Integer, default=constants.DEFAULT_PORT)
config_external_port = Column(Integer, default=constants.DEFAULT_PORT)
config_certfile = Column(String)
Expand Down Expand Up @@ -389,6 +391,9 @@ def invalidate(self, error=None):
self.db_configured = False
self.save()

def get_book_path(self):
return self.config_calibre_split_dir if self.config_calibre_split_dir else self.config_calibre_dir

def store_calibre_uuid(self, calibre_db, Library_table):
try:
calibre_uuid = calibre_db.session.query(Library_table).one_or_none()
Expand Down
22 changes: 11 additions & 11 deletions cps/editbooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def edit_book(book_id):
edited_books_id = book.id
modify_date = True
title_author_error = helper.update_dir_structure(edited_books_id,
config.config_calibre_dir,
config.get_book_path(),
input_authors[0],
renamed_author=renamed)
if title_author_error:
Expand Down Expand Up @@ -280,7 +280,7 @@ def upload():
meta.extension.lower())
else:
error = helper.update_dir_structure(book_id,
config.config_calibre_dir,
config.get_book_path(),
input_authors[0],
meta.file_path,
title_dir + meta.extension.lower(),
Expand Down Expand Up @@ -330,7 +330,7 @@ def convert_bookformat(book_id):
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))

log.info('converting: book id: %s from: %s to: %s', book_id, book_format_from, book_format_to)
rtn = helper.convert_book_format(book_id, config.config_calibre_dir, book_format_from.upper(),
rtn = helper.convert_book_format(book_id, config.get_book_path(), book_format_from.upper(),
book_format_to.upper(), current_user.name)

if rtn is None:
Expand Down Expand Up @@ -400,7 +400,7 @@ def edit_list_book(param):
elif param == 'title':
sort_param = book.sort
if handle_title_on_edit(book, vals.get('value', "")):
rename_error = helper.update_dir_structure(book.id, config.config_calibre_dir)
rename_error = helper.update_dir_structure(book.id, config.get_book_path())
if not rename_error:
ret = Response(json.dumps({'success': True, 'newValue': book.title}),
mimetype='application/json')
Expand All @@ -418,7 +418,7 @@ def edit_list_book(param):
mimetype='application/json')
elif param == 'authors':
input_authors, __, renamed = handle_author_on_edit(book, vals['value'], vals.get('checkA', None) == "true")
rename_error = helper.update_dir_structure(book.id, config.config_calibre_dir, input_authors[0],
rename_error = helper.update_dir_structure(book.id, config.get_book_path(), input_authors[0],
renamed_author=renamed)
if not rename_error:
ret = Response(json.dumps({
Expand Down Expand Up @@ -522,10 +522,10 @@ def merge_list_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,
filepath_new = os.path.normpath(os.path.join(config.get_book_path(),
to_book.path,
to_name + "." + element.format.lower()))
filepath_old = os.path.normpath(os.path.join(config.config_calibre_dir,
filepath_old = os.path.normpath(os.path.join(config.get_book_path(),
from_book.path,
element.name + "." + element.format.lower()))
copyfile(filepath_old, filepath_new)
Expand Down Expand Up @@ -565,7 +565,7 @@ def table_xchange_author_title():

if edited_books_id:
# toDo: Handle error
edit_error = helper.update_dir_structure(edited_books_id, config.config_calibre_dir, input_authors[0],
edit_error = helper.update_dir_structure(edited_books_id, config.get_book_path(), input_authors[0],
renamed_author=renamed)
if modify_date:
book.last_modified = datetime.utcnow()
Expand Down Expand Up @@ -762,7 +762,7 @@ def move_coverfile(meta, db_book):
cover_file = meta.cover
else:
cover_file = os.path.join(constants.STATIC_DIR, 'generic_cover.jpg')
new_cover_path = os.path.join(config.config_calibre_dir, db_book.path)
new_cover_path = os.path.join(config.get_book_path(), db_book.path)
try:
os.makedirs(new_cover_path, exist_ok=True)
copyfile(cover_file, os.path.join(new_cover_path, "cover.jpg"))
Expand Down Expand Up @@ -848,7 +848,7 @@ def delete_book_from_table(book_id, book_format, json_response):
book = calibre_db.get_book(book_id)
if book:
try:
result, error = helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper())
result, error = helper.delete_book(book, config.get_book_path(), book_format=book_format.upper())
if not result:
if json_response:
return json.dumps([{"location": url_for("edit-book.show_edit_book", book_id=book_id),
Expand Down Expand Up @@ -1184,7 +1184,7 @@ def upload_single_file(file_request, book, book_id):
return False

file_name = book.path.rsplit('/', 1)[-1]
filepath = os.path.normpath(os.path.join(config.config_calibre_dir, book.path))
filepath = os.path.normpath(os.path.join(config.get_book_path(), book.path))
saved_filename = os.path.join(filepath, file_name + '.' + file_ext)

# check if file path exists, otherwise create it, copy file to calibre path and delete temp file
Expand Down
108 changes: 108 additions & 0 deletions cps/editpage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import os
import flask
from flask import Blueprint, Flask, abort, request
from functools import wraps
from pathlib import Path
from flask_login import current_user, login_required
from werkzeug.exceptions import NotFound

from .render_template import render_title_template
from . import logger, config, ub
from .constants import CONFIG_DIR as _CONFIG_DIR

log = logger.create()

editpage = Blueprint('editpage', __name__)

def edit_required(f):
@wraps(f)
def inner(*args, **kwargs):
if current_user.role_edit() or current_user.role_admin():
return f(*args, **kwargs)
abort(403)

return inner

def _get_checkbox(dictionary, field, default):
new_value = dictionary.get(field, default)
convertor = lambda y: y == "on"
new_value = convertor(new_value)

return new_value

@editpage.route("/admin/page/<string:file>", methods=["GET", "POST"])
@login_required
@edit_required
def edit_page(file):
doc = ""
title = ""
name = ""
icon = "file"
is_enabled = True
order = 0
position = "0"

page = ub.session.query(ub.Page).filter(ub.Page.id == file).first()

try:
title = page.title
name = page.name
icon = page.icon
is_enabled = page.is_enabled
order = page.order
position = page.position
except AttributeError:
if file != "new":
abort(404)

if request.method == "POST":
to_save = request.form.to_dict()
title = to_save.get("title", "").strip()
name = to_save.get("name", "").strip()
icon = to_save.get("icon", "").strip()
position = to_save.get("position", "").strip()
order = int(to_save.get("order", 0))
content = to_save.get("content", "").strip()
is_enabled = _get_checkbox(to_save, "is_enabled", True)

if page:
page.title = title
page.name = name
page.icon = icon
page.is_enabled = is_enabled
page.order = order
page.position = position
ub.session_commit("Page edited {}".format(file))
else:
new_page = ub.Page(title=title, name=name, icon=icon, is_enabled=is_enabled, order=order, position=position)
ub.session.add(new_page)
ub.session_commit("Page added {}".format(file))

if (file == "new"):
file = str(new_page.id)
dir_config_path = os.path.join(_CONFIG_DIR, 'pages')
file_name = Path(name + '.md')
file_path = dir_config_path / file_name
os.makedirs(dir_config_path, exist_ok=True)

try:
with open(file_path, 'w') as f:
f.write(content)
f.close()
except Exception as ex:
log.error(ex)

if file != "new":
try:
dir_config_path = Path(_CONFIG_DIR) / 'pages'
file_path = dir_config_path / f"{name}.md"

with open(file_path, 'r') as f:
doc = f.read()
except NotFound:
log.error("'%s' was accessed but file doesn't exists." % file)

else:
doc = "## New file\n\nInformation"

return render_title_template("edit_page.html", title=title, name=name, icon=icon, is_enabled=is_enabled, order=order, position=position, content=doc, file=file)
3 changes: 2 additions & 1 deletion cps/epub.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def get_epub_layout(book, book_data):
'n': 'urn:oasis:names:tc:opendocument:xmlns:container',
'pkg': 'http://www.idpf.org/2007/opf',
}
file_path = os.path.normpath(os.path.join(config.config_calibre_dir, book.path, book_data.name + "." + book_data.format.lower()))
file_path = os.path.normpath(os.path.join(config.get_book_path(),
book.path, book_data.name + "." + book_data.format.lower()))

try:
epubZip = zipfile.ZipFile(file_path)
Expand Down
6 changes: 3 additions & 3 deletions cps/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ def get_book_cover_internal(book, resolution=None):

# Send the book cover from the Calibre directory
else:
cover_file_path = os.path.join(config.config_calibre_dir, book.path)
cover_file_path = os.path.join(config.get_book_path(), book.path)
if os.path.isfile(os.path.join(cover_file_path, "cover.jpg")):
return send_from_directory(cover_file_path, "cover.jpg")
else:
Expand Down Expand Up @@ -934,7 +934,7 @@ def save_cover(img, book_path):
else:
return False, message
else:
return save_cover_from_filestorage(os.path.join(config.config_calibre_dir, book_path), "cover.jpg", img)
return save_cover_from_filestorage(os.path.join(config.get_book_path(), book_path), "cover.jpg", img)


def do_download_file(book, book_format, client, data, headers):
Expand All @@ -947,7 +947,7 @@ def do_download_file(book, book_format, client, data, headers):
else:
abort(404)
else:
filename = os.path.join(config.config_calibre_dir, book.path)
filename = os.path.join(config.get_book_path(), book.path)
if not os.path.isfile(os.path.join(filename, data.name + "." + book_format)):
# ToDo: improve error handling
log.error('File not found: %s', os.path.join(filename, data.name + "." + book_format))
Expand Down
2 changes: 1 addition & 1 deletion cps/kobo.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def HandleSyncRequest():
for book in books:
formats = [data.format for data in book.Books.data]
if 'KEPUB' not in formats and config.config_kepubifypath and 'EPUB' in formats:
helper.convert_book_format(book.Books.id, config.config_calibre_dir, 'EPUB', 'KEPUB', current_user.name)
helper.convert_book_format(book.Books.id, config.get_book_path(), 'EPUB', 'KEPUB', current_user.name)

kobo_reading_state = get_or_create_reading_state(book.Books.id)
entitlement = {
Expand Down
28 changes: 28 additions & 0 deletions cps/listpages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import flask
import json
from flask import Blueprint, jsonify, make_response,abort
from flask_login import current_user, login_required
from functools import wraps
from flask_babel import gettext as _

from .render_template import render_title_template
from . import ub, db

listpages = Blueprint('listpages', __name__)

def edit_required(f):
@wraps(f)
def inner(*args, **kwargs):
if current_user.role_edit() or current_user.role_admin():
return f(*args, **kwargs)
abort(403)

return inner

@listpages.route("/admin/pages/", methods=["GET"])
@login_required
@edit_required
def show_list():
pages = ub.session.query(ub.Page).order_by(ub.Page.position).order_by(ub.Page.order).all()

return render_title_template('list_pages.html', title=_("Pages List"), page="book_table", pages=pages)
6 changes: 6 additions & 0 deletions cps/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ def main():
from .gdrive import gdrive
from .editbooks import editbook
from .about import about
from .page import page
from .listpages import listpages
from .editpage import editpage
from .search import search
from .search_metadata import meta
from .shelf import shelf
Expand Down Expand Up @@ -65,6 +68,9 @@ def main():
limiter.limit("3/minute",key_func=request_username)(opds)
app.register_blueprint(jinjia)
app.register_blueprint(about)
app.register_blueprint(page)
app.register_blueprint(listpages)
app.register_blueprint(editpage)
app.register_blueprint(shelf)
app.register_blueprint(admi)
app.register_blueprint(remotelogin)
Expand Down
2 changes: 1 addition & 1 deletion cps/opds.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ def render_element_index(database_column, linked_table, folder):
entries = entries.join(linked_table).join(db.Books)
entries = entries.filter(calibre_db.common_filters()).group_by(func.upper(func.substr(database_column, 1, 1))).all()
elements = []
if off == 0:
if off == 0 and entries:
elements.append({'id': "00", 'name': _("All")})
shift = 1
for entry in entries[
Expand Down
38 changes: 38 additions & 0 deletions cps/page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import os
import flask
import markdown
from flask import abort
from pathlib import Path
from flask_babel import gettext as _
from werkzeug.exceptions import NotFound

from . import logger, config, ub
from .render_template import render_title_template
from .constants import CONFIG_DIR as _CONFIG_DIR

page = flask.Blueprint('page', __name__)

log = logger.create()

@page.route('/page/<string:file>', methods=['GET'])
def get_page(file):
page = ub.session.query(ub.Page)\
.filter(ub.Page.name == file)\
.filter(ub.Page.is_enabled)\
.first()

if not page:
log.error(f"'{file}' was accessed but is not enabled or it's not in database.")
abort(404)

try:
dir_config_path = Path(_CONFIG_DIR) / 'pages'
file_path = dir_config_path / f"{file}.md"
with open(file_path, 'r') as f:
temp_md = f.read()
body = markdown.markdown(temp_md)

return render_title_template('page.html', body=body, title=page.title, page=page.name)
except NotFound:
log.error("'%s' was accessed but file doesn't exists." % file)
abort(404)
Loading

0 comments on commit f20b0f3

Please sign in to comment.