1
0
mirror of https://github.com/janeczku/calibre-web synced 2024-11-24 18:47:23 +00:00

Improvements for books table editor

This commit is contained in:
Ozzieisaacs 2020-06-11 21:19:09 +02:00
parent 94b5ec91cc
commit 1c681ee378
6 changed files with 219 additions and 61 deletions

View File

@ -240,6 +240,9 @@ class Languages(Base):
self.lang_code = lang_code self.lang_code = lang_code
def get(self): def get(self):
if self.language_name:
return self.language_name
else:
return self.lang_code return self.lang_code
def __repr__(self): def __repr__(self):

View File

@ -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
@ -892,7 +893,7 @@ def convert_bookformat(book_id):
@login_required_if_no_ano @login_required_if_no_ano
def edit_list_book(param): def edit_list_book(param):
vals = request.form.to_dict() vals = request.form.to_dict()
#calibre_db.update_title_sort(config) # calibre_db.update_title_sort(config)
#calibre_db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4())) #calibre_db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
book = calibre_db.get_book(vals['pk']) book = calibre_db.get_book(vals['pk'])
if param =='series_index': if param =='series_index':
@ -902,16 +903,17 @@ def edit_list_book(param):
elif param =='series': elif param =='series':
edit_book_series(vals['value'], book) edit_book_series(vals['value'], book)
elif param =='publishers': elif param =='publishers':
edit_book_publisher(vals['value'], book) vals['publisher'] = vals['value']
# ToDo: edit books edit_book_publisher(vals, book)
elif param =='languages': elif param =='languages':
edit_book_languages(vals['value'], book) edit_book_languages(vals['value'], book)
elif param =='title':
edit_book_languages(vals['value'], book)
elif param =='sort':
edit_book_languages(vals['value'], book)
elif param =='author_sort': elif param =='author_sort':
edit_book_languages(vals['value'], book) book.author_sort = vals['value']
elif param =='title':
book.title = vals['value']
elif param =='sort':
book.sort = vals['value']
# ToDo: edit books
elif param =='authors': elif param =='authors':
edit_book_languages(vals['value'], book) edit_book_languages(vals['value'], book)
@ -919,7 +921,18 @@ def edit_list_book(param):
calibre_db.session.commit() calibre_db.session.commit()
return "" return ""
@editbook.route("/ajax/sort_value")
@login_required
def get_sorted_entry():
pass
@editbook.route("/ajax/deletebooks") @editbook.route("/ajax/deletebooks")
@login_required_if_no_ano @login_required
def delete_list_book(): def delete_list_book():
pass pass
@editbook.route("/ajax/mergebooks", methods=['POST'])
@login_required
def merge_list_book():
vals = request.get_json()
return ""

View File

@ -51,9 +51,9 @@ body h2 {
color:#444; color:#444;
} }
a, .danger, .editable-empty, .editable-empty:hover { color: #45b29d; } a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d; }
.danger:hover { color: #23527c; } .book-remove:hover { color: #23527c; }
.btn-default a { color: #444; } .btn-default a { color: #444; }

View File

@ -15,16 +15,150 @@
* 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, EbookActions*/ /* exported TableActions, RestrictionActions, EbookActions, responseHandler */
var selections = [];
$(function() { $(function() {
$("#books-table").bootstrapTable({ $("#books-table").on("check.bs.table check-all.bs.table uncheck.bs.table uncheck-all.bs.table",
formatNoMatches: function () { function (e, rowsAfter, rowsBefore) {
return ""; 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);
} }
}); });
$("#merge_books").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() {
// ToDo:
}
});
});
/*$("#books-table").bootstrapTable({
});
test = $("#books-table").bootstrapTable('getOptions');
var column = test.columns[0]
column.forEach(function(item){
if ('editableValidate' in item) {
item.editable = {
mode: 'inline',
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
}
}
});
$("#books-table").bootstrapTable('refreshOptions', {columns: [column]});*/
var column = [];
$('#table1 > thead > tr > th').each(function(){
if ($(this).attr('data-edit')) {
if ($(this).attr('data-edit-validate')) {
column.push({
editable: {
mode: 'inline',
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
}
});
} else {
column.push({
editable: {
mode: 'inline',
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
}
});
}
} else {
column.push({});
}
});
$("#table1").bootstrapTable({
sidePagination: "server",
pagination: true,
paginationDetailHAlign: " hidden",
paginationHAlign: "left",
/*url: window.location.pathname + "/../../ajax/listbooks",*/
idField: "id",
search: true,
showColumns: true,
searchAlign: "left",
showSearchButton : false,
searchOnEnterKey: true,
checkboxHeader: false,
maintainMetaData: true,
responseHandler: responseHandler,
columns: column,
/*editable-mode="inline"
editable-emptytext="<span class='glyphicon glyphicon-plus'></span>">*/
/*columns: [
{},
{}, {
editable: {
mode: 'inline',
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
},
}, {
editable: {
mode: 'inline',
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
},
}, {
editable: {
mode: 'inline',
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
}
}, {
editable: {
mode: 'inline',
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
},
}, {
editable: {
mode: 'inline',
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
},
}, {
editable: {
mode: 'inline',
emptytext: "<span class='glyphicon glyphicon-plus'></span>",
validate: function (value) { if($.trim(value) == '') return 'This field is required'; },
},
}],*/
formatNoMatches: function () {
return "";
},
});
/*var $table = $('#books-table');
$('#books-table').bootstrapTable('getOptions')*/
/*$('#books-table').bootstrapTable('editable')*/
/*{ field: 'aimValue', title: 'Aim<br>Value', class: 'danger',editable: { mode: 'inline', validate: function (v) { return validateData(this, v); }, }, },*/
$("#domain_allow_submit").click(function(event) { $("#domain_allow_submit").click(function(event) {
event.preventDefault(); event.preventDefault();
$("#domain_add_allow").ajaxForm(); $("#domain_add_allow").ajaxForm();
@ -105,7 +239,7 @@ $(function() {
var deleteId = $(this).data("deleteid"); var deleteId = $(this).data("deleteid");
$.ajax({ $.ajax({
method:"get", method:"get",
url: window.location.pathname + "/../../delete"/+deleteId, url: window.location.pathname + "/../../delete/" + deleteId,
}); });
}); });
@ -238,6 +372,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 [
@ -250,8 +385,22 @@ function RestrictionActions (value, row) {
/* Function for deleting books */ /* Function for deleting books */
function EbookActions (value, row) { function EbookActions (value, row) {
return [ return [
"<div class=\"danger remove\" data-toggle=\"modal\" data-target=\"#deleteModal\" data-delete-id=\"" + row.id + "\" title=\"Remove\">", "<div class=\"book-remove\" data-toggle=\"modal\" data-target=\"#deleteModal\" data-delete-id=\"" + row.id + "\" title=\"Remove\">",
"<i class=\"glyphicon glyphicon-trash\"></i>", "<i class=\"glyphicon glyphicon-trash\"></i>",
"</div>" "</div>"
].join(""); ].join("");
} }
/* Function for keeping checked rows */
function responseHandler(res) {
$.each(res.rows, function (i, row) {
row.state = $.inArray(row.id, selections) !== -1;
});
return res;
}
function validato(value) {
if($.trim(value) == '') return 'This field is required';
}
/*function validateVal(value) { if($.trim(value) == '') return 'This field is required'; }*/

View File

@ -1,6 +1,14 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% macro text_table_row(parameter, edit_text, show_text) -%} {% macro text_table_row(parameter, edit_text, show_text, validate) -%}
<th data-field="{{ parameter }}" id="{{ parameter }}" data-sortable="true" {% if g.user.role_edit() %}data-editable-type="text" data-editable-url="{{ url_for('editbook.edit_list_book', param=parameter)}}" data-editable="true" data-editable-title="{{ edit_text}}"{% endif %}>{{ show_text }}</th> <th data-field="{{ parameter }}" id="{{ parameter }}" data-sortable="true"
{% 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="true" {% endif %}
{% endif %}
>{{ show_text }}</th>
{%- endmacro %} {%- endmacro %}
{% block header %} {% block header %}
@ -9,13 +17,14 @@
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<h2 class="{{page}}">{{_(title)}}</h2> <h2 class="{{page}}">{{_(title)}}</h2>
<table class="table table-no-bordered table-striped" <!--table id="table1"></table-->
<!--a href="{{url_for('editbook.merge_list_book')}}" class="btn btn-default disabled" id="merge_book" role="button" aria-disabled="true">{{_('Merge selected books')}}
data-side-pagination="server" data-side-pagination="server"
data-pagination="true" data-pagination="true"
data-pagination-detail-h-align=" hidden" data-pagination-detail-h-align=" hidden"
data-pagination-h-align="left" data-pagination-h-align="left"
id="books-table" id="books-table"
data-url="{{url_for('web.list_books')}}"
data-id-field="id" data-id-field="id"
data-editable-mode="inline" data-editable-mode="inline"
data-show-columns="true" data-show-columns="true"
@ -26,24 +35,27 @@
data-checkbox-header="false" data-checkbox-header="false"
data-maintain-meta-data="true" data-maintain-meta-data="true"
data-response-handler="responseHandler" data-response-handler="responseHandler"
data-editable-emptytext="<span class='glyphicon glyphicon-plus'></span>"> data-editable-emptytext="<span class='glyphicon glyphicon-plus'></span>"
</a-->
<div class="btn btn-default disabled" id="merge_books" aria-disabled="true">{{_('Merge selected books')}}</div>
<table id="table1" class="table table-no-bordered table-striped"
data-url="{{url_for('web.list_books')}}">
<thead> <thead>
<tr> <tr>
{% if g.user.role_edit() %} {% if g.user.role_edit() %}
<th data-field="state" data-checkbox="true" data-sortable="true"></th> <th data-field="state" data-checkbox="true" data-sortable="true"></th>
{% endif %} {% endif %}
<th data-field="id" id="id" data-visible="false" data-switchable="false"></th> <th data-field="id" id="id" data-visible="false" data-switchable="false"></th>
{{ text_table_row('title', _('Enter Title'),_('Title')) }} {{ text_table_row('title', _('Enter Title'),_('Title'), true) }}
{{ text_table_row('sort', _('Enter Titlesort'),_('Sort')) }} {{ text_table_row('sort', _('Enter Titlesort'),_('Sort'), false) }}
{{ text_table_row('author_sort', _('Enter Authorsort'),_('Authors Sort')) }} {{ text_table_row('author_sort', _('Enter Authorsort'),_('Authors Sort'), false) }}
{{ text_table_row('authors', _('Enter Authors'),_('Authors')) }} {{ text_table_row('authors', _('Enter Authors'),_('Authors'), false) }}
{{ text_table_row('tags', _('Enter Tags'),_('Tags')) }} {{ text_table_row('tags', _('Enter Tags'),_('Tags'), false) }}
{{ text_table_row('series', _('Enter Series'),_('Series')) }} {{ text_table_row('series', _('Enter Series'),_('Series'), false) }}
<th data-field="series_index" id="series_index" 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-editable="true" data-editable-title="{{_('Enter title')}}"{% endif %}>{{_('Series Index')}}</th> <th data-field="series_index" id="series_index" 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-editable="true" data-editable-title="{{_('Enter title')}}"{% endif %}>{{_('Series Index')}}</th>
{{ text_table_row('languages', _('Enter Languages'),_('Languages')) }} {{ text_table_row('languages', _('Enter Languages'),_('Languages'), false) }}
<th data-field="pubdate" id="pubdate" data-sortable="true">Publishing Date</th> <th data-field="pubdate" data-type="date" data-viewformat="dd.mm.yyyy" id="pubdate" data-sortable="true">Publishing Date</th>
{{ text_table_row('publishers', _('Enter Publishers'),_('Publishers')) }} {{ text_table_row('publishers', _('Enter Publishers'),_('Publishers'), false) }}
{% if g.user.role_edit() %} {% if g.user.role_edit() %}
<th data-align="right" data-formatter="EbookActions" data-switchable="false"></th> <th data-align="right" data-formatter="EbookActions" data-switchable="false"></th>
{% endif %} {% endif %}
@ -60,32 +72,5 @@
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-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 src="{{ url_for('static', filename='js/table.js') }}"></script>
<script> <script>
var $table = $('#books-table')
var selections = []
function responseHandler(res) {
$.each(res.rows, function (i, row) {
row.state = $.inArray(row.id, selections) !== -1
})
return res
}
$(function() {
$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)
})
})
</script> </script>
{% endblock %} {% endblock %}

View File

@ -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 from flask import Blueprint
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
@ -849,6 +849,14 @@ def list_books():
else: else:
entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order) entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order)
filtered_count = total_count 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} table_entries = {'totalNotFiltered': total_count, 'total': filtered_count, "rows": entries}
js_list = json.dumps(table_entries, cls=db.AlchemyEncoder) js_list = json.dumps(table_entries, cls=db.AlchemyEncoder)
#js_list = json.dumps(entries, cls=db.AlchemyEncoder) #js_list = json.dumps(entries, cls=db.AlchemyEncoder)