Commit 709fa88c authored by OzzieIsaacs's avatar OzzieIsaacs

Navbar reduced to icons on smaller screens

Feedback updater improved (#81)
parent 3cadde65
......@@ -3,7 +3,6 @@
import os
import sys
import time
base_path = os.path.dirname(os.path.abspath(__file__))
# Insert local directories into path
......@@ -22,7 +21,7 @@ if __name__ == '__main__':
http_server.listen(web.ub.config.config_port)
IOLoop.instance().start()
if web.global_task == 0:
if web.helper.global_task == 0:
print("Performing restart of Calibre-web")
os.execl(sys.executable, sys.executable, *sys.argv)
else:
......
......@@ -6,7 +6,7 @@ import ub
from flask import current_app as app
import logging
import smtplib
import tempfile
from tempfile import gettempdir
import socket
import sys
import os
......@@ -21,13 +21,22 @@ from email.MIMEText import MIMEText
from email.generator import Generator
from flask_babel import gettext as _
import subprocess
import threading
import shutil
import requests
import zipfile
from tornado.ioloop import IOLoop
try:
import unidecode
use_unidecode=True
except:
use_unidecode=False
# Global variables
global_task = None
updater_thread = None
def update_download(book_id, user_id):
check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id ==
book_id).first()
......@@ -267,14 +276,46 @@ def update_dir_stucture(book_id, calibrepath):
book.path = new_authordir + '/' + book.path.split('/')[1]
db.session.commit()
class Updater(threading.Thread):
def file_to_list(file):
def __init__(self):
threading.Thread.__init__(self)
self.status=0
def run(self):
global global_task
self.status=1
r = requests.get('https://api.github.com/repos/janeczku/calibre-web/zipball/master', stream=True)
fname = re.findall("filename=(.+)", r.headers['content-disposition'])[0]
self.status=2
z = zipfile.ZipFile(StringIO(r.content))
self.status=3
tmp_dir = gettempdir()
z.extractall(tmp_dir)
self.status=4
self.update_source(os.path.join(tmp_dir,os.path.splitext(fname)[0]),ub.config.get_main_dir)
self.status=5
global_task = 0
db.session.close()
db.engine.dispose()
ub.session.close()
ub.engine.dispose()
self.status=6
# stop tornado server
server = IOLoop.instance()
server.add_callback(server.stop)
self.status=7
def get_update_status(self):
return self.status
def file_to_list(self, file):
return [x.strip() for x in open(file, 'r') if not x.startswith('#EXT')]
def one_minus_two(one, two):
def one_minus_two(self, one, two):
return [x for x in one if x not in set(two)]
def reduce_dirs(delete_files, new_list):
def reduce_dirs(self, delete_files, new_list):
new_delete = []
for file in delete_files:
parts = file.split(os.sep)
......@@ -294,57 +335,57 @@ def reduce_dirs(delete_files, new_list):
break
return list(set(new_delete))
def reduce_files(remove_items, exclude_items):
def reduce_files(self, remove_items, exclude_items):
rf = []
for item in remove_items:
if not item in exclude_items:
rf.append(item)
return rf
def moveallfiles(root_src_dir, root_dst_dir):
def moveallfiles(self, root_src_dir, root_dst_dir):
change_permissions = True
if sys.platform == "win32" or sys.platform == "darwin":
change_permissions=False
change_permissions = False
else:
app.logger.debug('Update on OS-System : '+sys.platform )
#print('OS-System: '+sys.platform )
new_permissions=os.stat(root_dst_dir)
#print new_permissions
app.logger.debug('Update on OS-System : ' + sys.platform)
# print('OS-System: '+sys.platform )
new_permissions = os.stat(root_dst_dir)
# print new_permissions
for src_dir, dirs, files in os.walk(root_src_dir):
dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
if not os.path.exists(dst_dir):
os.makedirs(dst_dir)
#print('Create-Dir: '+dst_dir)
# print('Create-Dir: '+dst_dir)
if change_permissions:
#print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
os.chown(dst_dir,new_permissions.st_uid,new_permissions.st_gid)
# print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
os.chown(dst_dir, new_permissions.st_uid, new_permissions.st_gid)
for file_ in files:
src_file = os.path.join(src_dir, file_)
dst_file = os.path.join(dst_dir, file_)
if os.path.exists(dst_file):
if change_permissions:
permission=os.stat(dst_file)
#print('Remove file before copy: '+dst_file)
permission = os.stat(dst_file)
# print('Remove file before copy: '+dst_file)
os.remove(dst_file)
else:
if change_permissions:
permission=new_permissions
permission = new_permissions
shutil.move(src_file, dst_dir)
#print('Move File '+src_file+' to '+dst_dir)
# print('Move File '+src_file+' to '+dst_dir)
if change_permissions:
try:
os.chown(dst_file, permission.st_uid, permission.st_uid)
#print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
# print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
except:
e = sys.exc_info()
#print('Fail '+str(dst_file)+' error: '+str(e))
# print('Fail '+str(dst_file)+' error: '+str(e))
return
def update_source(source,destination):
def update_source(self, source, destination):
# destination files
old_list=list()
exclude = (['vendor' + os.sep + 'kindlegen.exe','vendor' + os.sep + 'kindlegen','/app.db','vendor','/update.py'])
old_list = list()
exclude = (
['vendor' + os.sep + 'kindlegen.exe', 'vendor' + os.sep + 'kindlegen', '/app.db', 'vendor', '/update.py'])
for root, dirs, files in os.walk(destination, topdown=True):
for name in files:
old_list.append(os.path.join(root, name).replace(destination, ''))
......@@ -358,26 +399,23 @@ def update_source(source,destination):
for name in dirs:
new_list.append(os.path.join(root, name).replace(source, ''))
delete_files = one_minus_two(old_list, new_list)
#print('raw delete list', delete_files)
delete_files = self.one_minus_two(old_list, new_list)
rf= reduce_files(delete_files, exclude)
#print('reduced delete list', rf)
rf = self.reduce_files(delete_files, exclude)
remove_items = reduce_dirs(rf, new_list)
#print('delete files', remove_items)
remove_items = self.reduce_dirs(rf, new_list)
moveallfiles(source, destination)
self.moveallfiles(source, destination)
for item in remove_items:
item_path = os.path.join(destination, item[1:])
if os.path.isdir(item_path):
print("Delete dir "+ item_path)
app.logger.debug("Delete dir " + item_path)
shutil.rmtree(item_path)
else:
try:
print("Delete file "+ item_path)
app.logger.debug("Delete file " + item_path)
os.remove(item_path)
except:
print("Could not remove:"+item_path)
app.logger.debug("Could not remove:" + item_path)
shutil.rmtree(source, ignore_errors=True)
......@@ -47,3 +47,7 @@ span.glyphicon.glyphicon-tags {padding-right: 5px;color: #999;vertical-align: te
.btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary{ background-color: #1C5484; }
.btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #89B9E2; }
.btn-toolbar>.btn+.btn, .btn-toolbar>.btn-group+.btn, .btn-toolbar>.btn+.btn-group, .btn-toolbar>.btn-group+.btn-group { margin-left:0px; }
.spinner {margin:0 41%;}
.spinner2 {margin:0 41%;}
var displaytext;
var updateTimerID;
var updateText;
$(function() {
$('.discover .row').isotope({
......@@ -31,7 +34,9 @@ $(function() {
url: window.location.pathname+"/../../shutdown",
data: {"parameter":0},
success: function(data) {
return alert(data.text);}
$('#spinner').show();
displaytext=data.text;
window.setTimeout(restartTimer, 3000);}
});
});
$("#shutdown").click(function() {
......@@ -50,17 +55,66 @@ $(function() {
dataType: 'json',
url: window.location.pathname+"/../../get_update_status",
success: function(data) {
$("#check_for_update").html(button_text);
if (data.status == true) {
$("#check_for_update").addClass('hidden');
$("#perform_update").removeClass('hidden');
}else{
$("#check_for_update").html(button_text);
};}
$("#update_info").removeClass('hidden');
$("#update_info").find('span').html(data.commit);
}
}
});
});
$("#perform_update").click(function() {
$('#spinner2').show();
$.ajax({
type: "POST",
dataType: 'json',
data: { start: "True"},
url: window.location.pathname+"/../../get_updater_status",
success: function(data) {
updateText=data.text
$("#UpdateprogressDialog #Updatecontent").html(updateText[data.status]);
console.log(data.status);
updateTimerID=setInterval(updateTimer, 2000);}
});
});
});
function restartTimer() {
$('#spinner').hide();
$('#RestartDialog').modal('hide');
}
function updateTimer() {
$.ajax({
dataType: 'json',
url: window.location.pathname+"/../../get_updater_status",
success: function(data) {
console.log(data.status);
$("#UpdateprogressDialog #Updatecontent").html(updateText[data.status]);
if (data.status >6){
clearInterval(updateTimerID);
$('#spinner2').hide();
$('#UpdateprogressDialog #updateFinished').removeClass('hidden');
$("#check_for_update").removeClass('hidden');
$("#perform_update").addClass('hidden');
}
},
error: function() {
console.log('Done');
clearInterval(updateTimerID);
$('#spinner2').hide();
$("#UpdateprogressDialog #Updatecontent").html(updateText[7]);
$('#UpdateprogressDialog #updateFinished').removeClass('hidden');
$("#check_for_update").removeClass('hidden');
$("#perform_update").addClass('hidden');
}
});
}
$(window).resize(function(event) {
$('.discover .row').isotope('reLayout');
});
......@@ -2,6 +2,7 @@
{% block body %}
<div class="discover">
<h2>{{_('User list')}}</h2>
<div id="divLoading"></div>
<table class="table table-striped" id="table_user">
<tr>
<th>{{_('Nickname')}}</th>
......@@ -76,11 +77,13 @@
<div class="btn btn-default"><a href="{{url_for('configuration')}}">{{_('Configuration')}}</a></div>
<h2>{{_('Administration')}}</h2>
{% if not development %}
<p>{{_('Current commit timestamp')}}: {{commit}} </p>
<div class="btn btn-default" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-web')}}</a></div>
<div class="btn btn-default" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-web')}}</a></div>
<div class="btn btn-default" id="check_for_update">{{_('Check for update')}}</a></div>
<a href="{{url_for('update')}}" class="btn btn-default hidden" id="perform_update">{{_('Perform Update')}}</a>
<div>{{_('Current commit timestamp')}}: <span>{{commit}} </span></div>
<div class="hidden" id="update_info">{{_('Newest commit timestamp')}}: <span></span></div>
<p></p>
<div class="btn btn-default" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-web')}}</div>
<div class="btn btn-default" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-web')}}</div>
<div class="btn btn-default" id="check_for_update">{{_('Check for update')}}</div>
<div class="btn btn-default hidden" id="perform_update" data-toggle="modal" data-target="#UpdateprogressDialog">{{_('Perform Update')}}</div>
{% endif %}
</div>
<!-- Modal -->
......@@ -88,15 +91,17 @@
<div class="modal-dialog modal-sm">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header bg-info">
</div>
<div class="modal-header bg-info"></div>
<div class="modal-body text-center">
<p>{{_('Do you really want to restart Calibre-web?')}}</p>
<button type="button" class="btn btn-default" id="restart" data-dismiss="modal">{{_('Ok')}}</button>
<div id="spinner" class="spinner" style="display:none;">
<img id="img-spinner" src="/static/css/images/loading-icon.gif"/>
</div>
<p></p>
<button type="button" class="btn btn-default" id="restart" >{{_('Ok')}}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Back')}}</button>
</div>
</div>
</div>
</div>
<div id="ShutdownDialog" class="modal fade" role="dialog">
......@@ -114,5 +119,23 @@
</div>
</div>
<div id="UpdateprogressDialog" class="modal fade" role="dialog">
<div class="modal-dialog modal-sm">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header bg-info text-center">
<span>{{_('Updating, please do not reload page')}}</span>
</div>
<div class="modal-body text-center">
<div id="spinner2" class="spinner2" style="display:none;">
<img id="img-spinner" src="/static/css/images/loading-icon.gif"/>
</div>
<p></p>
<div id="Updatecontent"></div>
<p></p>
<button type="button" class="btn btn-default hidden" id="updateFinished" data-dismiss="modal">{{_('Ok')}}</button>
</div>
</div>
</div>
</div>
{% endblock %}
......@@ -65,7 +65,7 @@
<div class="navbar-collapse collapse">
{% if g.user.is_authenticated or g.user.is_anonymous() %}
<ul class="nav navbar-nav ">
<li><a href="{{url_for('advanced_search')}}"><span class="glyphicon glyphicon-search"></span> {{_('Advanced Search')}}</a></li>
<li><a href="{{url_for('advanced_search')}}"><span class="glyphicon glyphicon-search"></span><span class="hidden-sm"> {{_('Advanced Search')}}</span></a></li>
</ul>
{% endif %}
<ul class="nav navbar-nav navbar-right" id="main-nav">
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -3,7 +3,6 @@
import mimetypes
import logging
from logging.handlers import RotatingFileHandler
from tempfile import gettempdir
import textwrap
from flask import Flask, render_template, session, request, Response, redirect, url_for, send_from_directory, \
make_response, g, flash, abort
......@@ -39,9 +38,10 @@ import sys
import subprocess
import re
import db
import thread
from shutil import move, copyfile
from tornado.ioloop import IOLoop
import StringIO
try:
from wand.image import Image
......@@ -51,9 +51,6 @@ except ImportError, e:
use_generic_pdf_cover = True
from cgi import escape
# Global variables
global_task = None
# Proxy Helper class
class ReverseProxied(object):
......@@ -668,7 +665,7 @@ def get_opds_download_link(book_id, format):
file_name = book.title
if len(book.authors) > 0:
file_name = book.authors[0].name + '-' + file_name
file_name = helper.get_valid_filename(file_name)
# file_name = helper.get_valid_filename(file_name)
response = make_response(send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + format))
response.headers["Content-Disposition"] = "attachment; filename=\"%s.%s\"" % (data.name, format)
return response
......@@ -716,10 +713,39 @@ def get_update_status():
commit = requests.get('https://api.github.com/repos/janeczku/calibre-web/git/refs/heads/master').json()
if "object" in commit and commit['object']['sha'] != commit_id:
status['status'] = True
commitdate = requests.get('https://api.github.com/repos/janeczku/calibre-web/git/commits/'+commit['object']['sha']).json()
if "committer" in commitdate:
status['commit'] = commitdate['committer']['date']
else:
status['commit'] = u'Unknown'
else:
status['status'] = False
return json.dumps(status)
@app.route("/get_updater_status", methods=['GET','POST'])
@login_required
@admin_required
def get_updater_status():
status = {}
if request.method == "POST":
commit = request.form.to_dict()
if "start" in commit and commit['start'] == 'True':
text={
"1": _(u'Requesting update package'),
"2": _(u'Downloading update package'),
"3": _(u'Unzipping update package'),
"4": _(u'Files are replaced'),
"5": _(u'Database connections are closed'),
"6": _(u'Server is stopped'),
"7": _(u'Update finished, please press okay and reload page')
}
status['text']=text
helper.updater_thread = helper.Updater()
helper.updater_thread.start()
status['status']=helper.updater_thread.get_update_status()
elif request.method == "GET":
status['status']=helper.updater_thread.get_update_status()
return json.dumps(status)
@app.route("/get_languages_json", methods=['GET', 'POST'])
......@@ -1032,7 +1058,7 @@ def shutdown():
server.add_callback(server.stop)
showtext = {}
if task == 0:
showtext['text'] = _(u'Performing Restart, please reload page')
showtext['text'] = _(u'Server restarted, please reload page')
else:
showtext['text'] = _(u'Performing shutdown of server, please close window')
return json.dumps(showtext)
......@@ -1043,23 +1069,10 @@ def shutdown():
@login_required
@admin_required
def update():
global global_task
r = requests.get('https://api.github.com/repos/janeczku/calibre-web/zipball/master', stream=True)
fname = re.findall("filename=(.+)", r.headers['content-disposition'])[0]
z = zipfile.ZipFile(StringIO.StringIO(r.content))
tmp_dir = gettempdir()
z.extractall(tmp_dir)
helper.update_source(os.path.join(tmp_dir,os.path.splitext(fname)[0]),config.get_main_dir)
global_task = 0
db.session.close()
db.engine.dispose()
ub.session.close()
ub.engine.dispose()
# stop tornado server
server = IOLoop.instance()
server.add_callback(server.stop)
helper.updater_thread = helper.Updater()
flash(_(u"Update done"), category="info")
return logout()
return ""
@app.route("/search", methods=["GET"])
@login_required_if_no_ano
......@@ -1605,7 +1618,6 @@ def configuration_helper(origin):
if request.method == "POST":
to_save = request.form.to_dict()
content = ub.session.query(ub.Settings).first()
# ToDo: check lib vaild, and change without restart
if "config_calibre_dir" in to_save:
if content.config_calibre_dir != to_save["config_calibre_dir"]:
content.config_calibre_dir = to_save["config_calibre_dir"]
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment