Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
D
douban-api-proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
captainwong
douban-api-proxy
Commits
1c681ee3
Commit
1c681ee3
authored
Jun 11, 2020
by
Ozzieisaacs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improvements for books table editor
parent
94b5ec91
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
217 additions
and
59 deletions
+217
-59
db.py
cps/db.py
+4
-1
editbooks.py
cps/editbooks.py
+21
-8
style.css
cps/static/css/style.css
+2
-2
table.js
cps/static/js/table.js
+154
-5
book_table.html
cps/templates/book_table.html
+27
-42
web.py
cps/web.py
+9
-1
No files found.
cps/db.py
View file @
1c681ee3
...
...
@@ -240,7 +240,10 @@ class Languages(Base):
self
.
lang_code
=
lang_code
def
get
(
self
):
return
self
.
lang_code
if
self
.
language_name
:
return
self
.
language_name
else
:
return
self
.
lang_code
def
__repr__
(
self
):
return
u"<Languages('{0}')>"
.
format
(
self
.
lang_code
)
...
...
cps/editbooks.py
View file @
1c681ee3
...
...
@@ -27,6 +27,7 @@ import json
from
shutil
import
copyfile
from
uuid
import
uuid4
from
babel
import
Locale
as
LC
from
flask
import
Blueprint
,
request
,
flash
,
redirect
,
url_for
,
abort
,
Markup
,
Response
from
flask_babel
import
gettext
as
_
from
flask_login
import
current_user
,
login_required
...
...
@@ -892,7 +893,7 @@ def convert_bookformat(book_id):
@
login_required_if_no_ano
def
edit_list_book
(
param
):
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()))
book
=
calibre_db
.
get_book
(
vals
[
'pk'
])
if
param
==
'series_index'
:
...
...
@@ -902,16 +903,17 @@ def edit_list_book(param):
elif
param
==
'series'
:
edit_book_series
(
vals
[
'value'
],
book
)
elif
param
==
'publishers'
:
edit_book_publisher
(
vals
[
'value'
],
book
)
# ToDo: edit books
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'
:
edit_book_languages
(
vals
[
'value'
],
book
)
book
.
title
=
vals
[
'value'
]
elif
param
==
'sort'
:
edit_book_languages
(
vals
[
'value'
],
book
)
elif
param
==
'author_sort'
:
edit_book_languages
(
vals
[
'value'
],
book
)
book
.
sort
=
vals
[
'value'
]
# ToDo: edit books
elif
param
==
'authors'
:
edit_book_languages
(
vals
[
'value'
],
book
)
...
...
@@ -919,7 +921,18 @@ def edit_list_book(param):
calibre_db
.
session
.
commit
()
return
""
@
editbook
.
route
(
"/ajax/sort_value"
)
@
login_required
def
get_sorted_entry
():
pass
@
editbook
.
route
(
"/ajax/deletebooks"
)
@
login_required
_if_no_ano
@
login_required
def
delete_list_book
():
pass
@
editbook
.
route
(
"/ajax/mergebooks"
,
methods
=
[
'POST'
])
@
login_required
def
merge_list_book
():
vals
=
request
.
get_json
()
return
""
cps/static/css/style.css
View file @
1c681ee3
...
...
@@ -51,9 +51,9 @@ body h2 {
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
;
}
...
...
cps/static/js/table.js
View file @
1c681ee3
...
...
@@ -15,16 +15,150 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* exported TableActions, RestrictionActions, EbookActions*/
/* exported TableActions, RestrictionActions, EbookActions, responseHandler */
var
selections
=
[];
$
(
function
()
{
$
(
"#books-table"
).
bootstrapTable
({
$
(
"#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
);
}
});
$
(
"#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
)
{
event
.
preventDefault
();
$
(
"#domain_add_allow"
).
ajaxForm
();
...
...
@@ -105,7 +239,7 @@ $(function() {
var
deleteId
=
$
(
this
).
data
(
"deleteid"
);
$
.
ajax
({
method
:
"get"
,
url
:
window
.
location
.
pathname
+
"/../../delete
"
/+
deleteId
,
url
:
window
.
location
.
pathname
+
"/../../delete
/"
+
deleteId
,
});
});
...
...
@@ -238,6 +372,7 @@ function TableActions (value, row) {
].
join
(
""
);
}
/* Function for deleting domain restrictions */
function
RestrictionActions
(
value
,
row
)
{
return
[
...
...
@@ -250,8 +385,22 @@ function RestrictionActions (value, row) {
/* Function for deleting books */
function
EbookActions
(
value
,
row
)
{
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>"
,
"</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
;
}
function
validato
(
value
)
{
if
(
$
.
trim
(
value
)
==
''
)
return
'This field is required'
;
}
/*function validateVal(value) { if($.trim(value) == '') return 'This field is required'; }*/
cps/templates/book_table.html
View file @
1c681ee3
{% extends "layout.html" %}
{% macro text_table_row(parameter, edit_text, show_text) -%}
<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>
{% 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-title=
"{{ edit_text}}"
data-edit=
"true"
{%
if
validate
%}
data-edit-validate=
"true"
{%
endif
%}
{%
endif
%}
>
{{ show_text }}
</th>
{%- endmacro %}
{% block header %}
...
...
@@ -9,13 +17,14 @@
{% endblock %}
{% block body %}
<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-pagination="true"
data-pagination-detail-h-align=" hidden"
data-pagination-h-align="left"
id="books-table"
data-url=
"{{url_for('web.list_books')}}"
data-id-field="id"
data-editable-mode="inline"
data-show-columns="true"
...
...
@@ -26,24 +35,27 @@
data-checkbox-header="false"
data-maintain-meta-data="true"
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>
<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')) }}
{{ text_table_row('sort', _('Enter Titlesort'),_('Sort')) }}
{{ text_table_row('author_sort', _('Enter Authorsort'),_('Authors Sort')) }}
{{ text_table_row('authors', _('Enter Authors'),_('Authors')) }}
{{ text_table_row('tags', _('Enter Tags'),_('Tags')) }}
{{ text_table_row('series', _('Enter Series'),_('Series')) }}
{{ text_table_row('title', _('Enter Title'),_('Title')
, true
) }}
{{ text_table_row('sort', _('Enter Titlesort'),_('Sort')
, false
) }}
{{ text_table_row('author_sort', _('Enter Authorsort'),_('Authors Sort')
, false
) }}
{{ text_table_row('authors', _('Enter Authors'),_('Authors')
, false
) }}
{{ text_table_row('tags', _('Enter Tags'),_('Tags')
, false
) }}
{{ 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>
{{ text_table_row('languages', _('Enter Languages'),_('Languages')) }}
<th
data-field=
"pubdate"
id=
"pubdate"
data-sortable=
"true"
>
Publishing Date
</th>
{{ text_table_row('publishers', _('Enter Publishers'),_('Publishers')) }}
{{ text_table_row('languages', _('Enter Languages'),_('Languages')
, false
) }}
<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')
, false
) }}
{% if g.user.role_edit() %}
<th
data-align=
"right"
data-formatter=
"EbookActions"
data-switchable=
"false"
></th>
{% 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/table.js') }}"
></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>
{% endblock %}
cps/web.py
View file @
1c681ee3
...
...
@@ -30,8 +30,8 @@ import traceback
import
binascii
import
re
from
babel
import
Locale
as
LC
from
babel.dates
import
format_date
from
babel
import
Locale
as
LC
from
babel.core
import
UnknownLocaleError
from
flask
import
Blueprint
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
:
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)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment