Merge branch 'master' of git.xpub.nl:/var/www/git.xpub.nl/repos/xpub-lib

master
Alice 6 years ago
commit 1b5ea42d2f

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored

@ -3,6 +3,7 @@ __pycache__/
*~ *~
app/uploads/** app/uploads/**
!app/uploads/cover !app/uploads/cover
/cover/**
app/mydatabase.db app/mydatabase.db
pyrqlite/ pyrqlite/
whoosh/ whoosh/

@ -15,7 +15,7 @@ registry.register("rqlite.pyrqlite", "sqlalchemy_rqlite.pyrqlite", "dialect")
basedir = os.path.abspath(os.path.dirname(__file__)) basedir = os.path.abspath(os.path.dirname(__file__))
UPLOAD_FOLDER = os.path.join(basedir, 'uploads') UPLOAD_FOLDER = os.path.join(basedir, 'uploads')
UPLOAD_FOLDER_COVER = os.path.join(basedir, 'uploads/cover') UPLOAD_FOLDER_COVER = os.path.join(basedir, 'cover')
#ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) #ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
load_dotenv(find_dotenv()) load_dotenv(find_dotenv())
app = Flask(__name__) app = Flask(__name__)
@ -29,7 +29,7 @@ app.config['PORT'] = 80
#app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'mydatabase.db') #app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'mydatabase.db')
db = SQLAlchemy(app) db = SQLAlchemy(app)
light = not os.path.isdir(UPLOAD_FOLDER)
DOMAIN = environ.get('DOMAIN') DOMAIN = environ.get('DOMAIN')
socketio = SocketIO(app) socketio = SocketIO(app)

@ -37,8 +37,8 @@ def get_cover(file_path, filename):
# Convert each page to a png image. # Convert each page to a png image.
for page in pages: for page in pages:
big_filename = "app/uploads/cover/"+page["filename"] + "_cover.png" big_filename = "app/cover/"+page["filename"] + "_cover.png"
small_filename = "app/uploads/cover/"+page["filename"] + "cover_small" + ".png" small_filename = "app/cover/"+page["filename"] + "cover_small" + ".png"
img = pdf_page_to_png(src_pdf, pagenum = page["pagenum"], resolution = 200) img = pdf_page_to_png(src_pdf, pagenum = page["pagenum"], resolution = 200)
img.save(filename = big_filename) img.save(filename = big_filename)

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

@ -0,0 +1,486 @@
/**
* jQuery.marquee - scrolling text like old marquee element
* @author Aamir Afridi - aamirafridi(at)gmail(dot)com / http://aamirafridi.com/jquery/jquery-marquee-plugin
*/;
(function($) {
$.fn.marquee = function(options) {
return this.each(function() {
// Extend the options if any provided
var o = $.extend({}, $.fn.marquee.defaults, options),
$this = $(this),
$marqueeWrapper, containerWidth, animationCss, verticalDir, elWidth,
loopCount = 3,
playState = 'animation-play-state',
css3AnimationIsSupported = false,
// Private methods
_prefixedEvent = function(element, type, callback) {
var pfx = ["webkit", "moz", "MS", "o", ""];
for (var p = 0; p < pfx.length; p++) {
if (!pfx[p]) type = type.toLowerCase();
element.addEventListener(pfx[p] + type, callback, false);
}
},
_objToString = function(obj) {
var tabjson = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
tabjson.push(p + ':' + obj[p]);
}
}
tabjson.push();
return '{' + tabjson.join(',') + '}';
},
_startAnimationWithDelay = function() {
$this.timer = setTimeout(animate, o.delayBeforeStart);
},
// Public methods
methods = {
pause: function() {
if (css3AnimationIsSupported && o.allowCss3Support) {
$marqueeWrapper.css(playState, 'paused');
} else {
// pause using pause plugin
if ($.fn.pause) {
$marqueeWrapper.pause();
}
}
// save the status
$this.data('runningStatus', 'paused');
// fire event
$this.trigger('paused');
},
resume: function() {
// resume using css3
if (css3AnimationIsSupported && o.allowCss3Support) {
$marqueeWrapper.css(playState, 'running');
} else {
// resume using pause plugin
if ($.fn.resume) {
$marqueeWrapper.resume();
}
}
// save the status
$this.data('runningStatus', 'resumed');
// fire event
$this.trigger('resumed');
},
toggle: function() {
methods[$this.data('runningStatus') == 'resumed' ? 'pause' : 'resume']();
},
destroy: function() {
// Clear timer
clearTimeout($this.timer);
// Unbind all events
$this.find("*").addBack().unbind();
// Just unwrap the elements that has been added using this plugin
$this.html($this.find('.js-marquee:first').html());
}
};
// Check for methods
if (typeof options === 'string') {
if ($.isFunction(methods[options])) {
// Following two IF statements to support public methods
if (!$marqueeWrapper) {
$marqueeWrapper = $this.find('.js-marquee-wrapper');
}
if ($this.data('css3AnimationIsSupported') === true) {
css3AnimationIsSupported = true;
}
methods[options]();
}
return;
}
/* Check if element has data attributes. They have top priority
For details https://twitter.com/aamirafridi/status/403848044069679104 - Can't find a better solution :/
jQuery 1.3.2 doesn't support $.data().KEY hence writting the following */
var dataAttributes = {},
attr;
$.each(o, function(key, value) {
// Check if element has this data attribute
attr = $this.attr('data-' + key);
if (typeof attr !== 'undefined') {
// Now check if value is boolean or not
switch (attr) {
case 'true':
attr = true;
break;
case 'false':
attr = false;
break;
}
o[key] = attr;
}
});
// Reintroduce speed as an option. It calculates duration as a factor of the container width
// measured in pixels per second.
if (o.speed) {
o.duration = parseInt($this.width(), 10) / o.speed * 1000;
}
// Shortcut to see if direction is upward or downward
verticalDir = o.direction == 'up' || o.direction == 'down';
// no gap if not duplicated
o.gap = o.duplicated ? parseInt(o.gap) : 0;
// wrap inner content into a div
$this.wrapInner('<div class="js-marquee"></div>');
// Make copy of the element
var $el = $this.find('.js-marquee').css({
'margin-right': o.gap,
'float': 'left'
});
if (o.duplicated) {
$el.clone(true).appendTo($this);
}
// wrap both inner elements into one div
$this.wrapInner('<div style="width:100000px" class="js-marquee-wrapper"></div>');
// Save the reference of the wrapper
$marqueeWrapper = $this.find('.js-marquee-wrapper');
// If direction is up or down, get the height of main element
if (verticalDir) {
var containerHeight = $this.height();
$marqueeWrapper.removeAttr('style');
$this.height(containerHeight);
// Change the CSS for js-marquee element
$this.find('.js-marquee').css({
'float': 'none',
'margin-bottom': o.gap,
'margin-right': 0
});
// Remove bottom margin from 2nd element if duplicated
if (o.duplicated) $this.find('.js-marquee:last').css({
'margin-bottom': 0
});
var elHeight = $this.find('.js-marquee:first').height() + o.gap;
// adjust the animation duration according to the text length
if (o.startVisible && !o.duplicated) {
// Compute the complete animation duration and save it for later reference
// formula is to: (Height of the text node + height of the main container / Height of the main container) * duration;
o._completeDuration = ((parseInt(elHeight, 10) + parseInt(containerHeight, 10)) / parseInt(containerHeight, 10)) * o.duration;
// formula is to: (Height of the text node / height of the main container) * duration
o.duration = (parseInt(elHeight, 10) / parseInt(containerHeight, 10)) * o.duration;
} else {
// formula is to: (Height of the text node + height of the main container / Height of the main container) * duration;
o.duration = ((parseInt(elHeight, 10) + parseInt(containerHeight, 10)) / parseInt(containerHeight, 10)) * o.duration;
}
} else {
// Save the width of the each element so we can use it in animation
elWidth = $this.find('.js-marquee:first').width() + o.gap;
// container width
containerWidth = $this.width();
// adjust the animation duration according to the text length
if (o.startVisible && !o.duplicated) {
// Compute the complete animation duration and save it for later reference
// formula is to: (Width of the text node + width of the main container / Width of the main container) * duration;
o._completeDuration = ((parseInt(elWidth, 10) + parseInt(containerWidth, 10)) / parseInt(containerWidth, 10)) * o.duration;
// (Width of the text node / width of the main container) * duration
o.duration = (parseInt(elWidth, 10) / parseInt(containerWidth, 10)) * o.duration;
} else {
// formula is to: (Width of the text node + width of the main container / Width of the main container) * duration;
o.duration = ((parseInt(elWidth, 10) + parseInt(containerWidth, 10)) / parseInt(containerWidth, 10)) * o.duration;
}
}
// if duplicated then reduce the duration
if (o.duplicated) {
o.duration = o.duration / 2;
}
if (o.allowCss3Support) {
var
elm = document.body || document.createElement('div'),
animationName = 'marqueeAnimation-' + Math.floor(Math.random() * 10000000),
domPrefixes = 'Webkit Moz O ms Khtml'.split(' '),
animationString = 'animation',
animationCss3Str = '',
keyframeString = '';
// Check css3 support
if (elm.style.animation !== undefined) {
keyframeString = '@keyframes ' + animationName + ' ';
css3AnimationIsSupported = true;
}
if (css3AnimationIsSupported === false) {
for (var i = 0; i < domPrefixes.length; i++) {
if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) {
var prefix = '-' + domPrefixes[i].toLowerCase() + '-';
animationString = prefix + animationString;
playState = prefix + playState;
keyframeString = '@' + prefix + 'keyframes ' + animationName + ' ';
css3AnimationIsSupported = true;
break;
}
}
}
if (css3AnimationIsSupported) {
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's infinite ' + o.css3easing;
$this.data('css3AnimationIsSupported', true);
}
}
var _rePositionVertically = function() {
$marqueeWrapper.css('transform', 'translateY(' + (o.direction == 'up' ? containerHeight + 'px' : '-' + elHeight + 'px') + ')');
},
_rePositionHorizontally = function() {
$marqueeWrapper.css('transform', 'translateX(' + (o.direction == 'left' ? containerWidth + 'px' : '-' + elWidth + 'px') + ')');
};
// if duplicated option is set to true than position the wrapper
if (o.duplicated) {
if (verticalDir) {
if (o.startVisible) {
$marqueeWrapper.css('transform', 'translateY(0)');
} else {
$marqueeWrapper.css('transform', 'translateY(' + (o.direction == 'up' ? containerHeight + 'px' : '-' + ((elHeight * 2) - o.gap) + 'px') + ')');
}
} else {
if (o.startVisible) {
$marqueeWrapper.css('transform', 'translateX(0)');
} else {
$marqueeWrapper.css('transform', 'translateX(' + (o.direction == 'left' ? containerWidth + 'px' : '-' + ((elWidth * 2) - o.gap) + 'px') + ')');
}
}
// If the text starts out visible we can skip the two initial loops
if (!o.startVisible) {
loopCount = 1;
}
} else if (o.startVisible) {
// We only have two different loops if marquee is duplicated and starts visible
loopCount = 2;
} else {
if (verticalDir) {
_rePositionVertically();
} else {
_rePositionHorizontally();
}
}
// Animate recursive method
var animate = function() {
if (o.duplicated) {
// When duplicated, the first loop will be scroll longer so double the duration
if (loopCount === 1) {
o._originalDuration = o.duration;
if (verticalDir) {
o.duration = o.direction == 'up' ? o.duration + (containerHeight / ((elHeight) / o.duration)) : o.duration * 2;
} else {
o.duration = o.direction == 'left' ? o.duration + (containerWidth / ((elWidth) / o.duration)) : o.duration * 2;
}
// Adjust the css3 animation as well
if (animationCss3Str) {
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's ' + o.css3easing;
}
loopCount++;
}
// On 2nd loop things back to normal, normal duration for the rest of animations
else if (loopCount === 2) {
o.duration = o._originalDuration;
// Adjust the css3 animation as well
if (animationCss3Str) {
animationName = animationName + '0';
keyframeString = $.trim(keyframeString) + '0 ';
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's 0s infinite ' + o.css3easing;
}
loopCount++;
}
}
if (verticalDir) {
if (o.duplicated) {
// Adjust the starting point of animation only when first loops finishes
if (loopCount > 2) {
$marqueeWrapper.css('transform', 'translateY(' + (o.direction == 'up' ? 0 : '-' + elHeight + 'px') + ')');
}
animationCss = {
'transform': 'translateY(' + (o.direction == 'up' ? '-' + elHeight + 'px' : 0) + ')'
};
} else if (o.startVisible) {
// This loop moves the marquee out of the container
if (loopCount === 2) {
// Adjust the css3 animation as well
if (animationCss3Str) {
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's ' + o.css3easing;
}
animationCss = {
'transform': 'translateY(' + (o.direction == 'up' ? '-' + elHeight + 'px' : containerHeight + 'px') + ')'
};
loopCount++;
} else if (loopCount === 3) {
// Set the duration for the animation that will run forever
o.duration = o._completeDuration;
// Adjust the css3 animation as well
if (animationCss3Str) {
animationName = animationName + '0';
keyframeString = $.trim(keyframeString) + '0 ';
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's 0s infinite ' + o.css3easing;
}
_rePositionVertically();
}
} else {
_rePositionVertically();
animationCss = {
'transform': 'translateY(' + (o.direction == 'up' ? '-' + ($marqueeWrapper.height()) + 'px' : containerHeight + 'px') + ')'
};
}
} else {
if (o.duplicated) {
// Adjust the starting point of animation only when first loops finishes
if (loopCount > 2) {
$marqueeWrapper.css('transform', 'translateX(' + (o.direction == 'left' ? 0 : '-' + elWidth + 'px') + ')');
}
animationCss = {
'transform': 'translateX(' + (o.direction == 'left' ? '-' + elWidth + 'px' : 0) + ')'
};
} else if (o.startVisible) {
// This loop moves the marquee out of the container
if (loopCount === 2) {
// Adjust the css3 animation as well
if (animationCss3Str) {
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's ' + o.css3easing;
}
animationCss = {
'transform': 'translateX(' + (o.direction == 'left' ? '-' + elWidth + 'px' : containerWidth + 'px') + ')'
};
loopCount++;
} else if (loopCount === 3) {
// Set the duration for the animation that will run forever
o.duration = o._completeDuration;
// Adjust the css3 animation as well
if (animationCss3Str) {
animationName = animationName + '0';
keyframeString = $.trim(keyframeString) + '0 ';
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's 0s infinite ' + o.css3easing;
}
_rePositionHorizontally();
}
} else {
_rePositionHorizontally();
animationCss = {
'transform': 'translateX(' + (o.direction == 'left' ? '-' + elWidth + 'px' : containerWidth + 'px') + ')'
};
}
}
// fire event
$this.trigger('beforeStarting');
// If css3 support is available than do it with css3, otherwise use jQuery as fallback
if (css3AnimationIsSupported) {
// Add css3 animation to the element
$marqueeWrapper.css(animationString, animationCss3Str);
var keyframeCss = keyframeString + ' { 100% ' + _objToString(animationCss) + '}',
$styles = $marqueeWrapper.find('style');
// Now add the keyframe animation to the marquee element
if ($styles.length !== 0) {
// Bug fixed for jQuery 1.3.x - Instead of using .last(), use following
$styles.filter(":last").html(keyframeCss);
} else {
$('head').append('<style>' + keyframeCss + '</style>');
}
// Animation iteration event
_prefixedEvent($marqueeWrapper[0], "AnimationIteration", function() {
$this.trigger('finished');
});
// Animation stopped
_prefixedEvent($marqueeWrapper[0], "AnimationEnd", function() {
animate();
$this.trigger('finished');
});
} else {
// Start animating
$marqueeWrapper.animate(animationCss, o.duration, o.easing, function() {
// fire event
$this.trigger('finished');
// animate again
if (o.pauseOnCycle) {
_startAnimationWithDelay();
} else {
animate();
}
});
}
// save the status
$this.data('runningStatus', 'resumed');
};
// bind pause and resume events
$this.bind('pause', methods.pause);
$this.bind('resume', methods.resume);
if (o.pauseOnHover) {
$this.bind('mouseenter', methods.pause);
$this.bind('mouseleave', methods.resume);
}
// If css3 animation is supported than call animate method at once
if (css3AnimationIsSupported && o.allowCss3Support) {
animate();
} else {
// Starts the recursive method
_startAnimationWithDelay();
}
});
}; // End of Plugin
// Public: plugin defaults options
$.fn.marquee.defaults = {
// If you wish to always animate using jQuery
allowCss3Support: true,
// works when allowCss3Support is set to true - for full list see http://www.w3.org/TR/2013/WD-css3-transitions-20131119/#transition-timing-function
css3easing: 'linear',
// requires jQuery easing plugin. Default is 'linear'
easing: 'linear',
// pause time before the next animation turn in milliseconds
delayBeforeStart: 1000,
// 'left', 'right', 'up' or 'down'
direction: 'left',
// true or false - should the marquee be duplicated to show an effect of continues flow
duplicated: false,
// duration in milliseconds of the marquee in milliseconds
duration: 5000,
// gap in pixels between the tickers
gap: 20,
// on cycle pause the marquee
pauseOnCycle: false,
// on hover pause the marquee - using jQuery plugin https://github.com/tobia/Pause
pauseOnHover: false,
// the marquee is visible initially positioned next to the border towards it will be moving
startVisible: false
};
})(jQuery);

@ -0,0 +1,16 @@
/**
* jQuery.marquee - scrolling text like old marquee element
* @author Aamir Afridi - aamirafridi(at)gmail(dot)com / http://aamirafridi.com/jquery/jquery-marquee-plugin
*/
(function(f){f.fn.marquee=function(x){return this.each(function(){var a=f.extend({},f.fn.marquee.defaults,x),b=f(this),c,t,e=3,y="animation-play-state",p=!1,E=function(a,b,c){for(var e=["webkit","moz","MS","o",""],d=0;d<e.length;d++)e[d]||(b=b.toLowerCase()),a.addEventListener(e[d]+b,c,!1)},F=function(a){var b=[],c;for(c in a)a.hasOwnProperty(c)&&b.push(c+":"+a[c]);b.push();return"{"+b.join(",")+"}"},l={pause:function(){p&&a.allowCss3Support?c.css(y,"paused"):f.fn.pause&&c.pause();b.data("runningStatus",
"paused");b.trigger("paused")},resume:function(){p&&a.allowCss3Support?c.css(y,"running"):f.fn.resume&&c.resume();b.data("runningStatus","resumed");b.trigger("resumed")},toggle:function(){l["resumed"==b.data("runningStatus")?"pause":"resume"]()},destroy:function(){clearTimeout(b.timer);b.find("*").addBack().unbind();b.html(b.find(".js-marquee:first").html())}};if("string"===typeof x)f.isFunction(l[x])&&(c||(c=b.find(".js-marquee-wrapper")),!0===b.data("css3AnimationIsSupported")&&(p=!0),l[x]());else{var u;
f.each(a,function(c,d){u=b.attr("data-"+c);if("undefined"!==typeof u){switch(u){case "true":u=!0;break;case "false":u=!1}a[c]=u}});a.speed&&(a.duration=parseInt(b.width(),10)/a.speed*1E3);var v="up"==a.direction||"down"==a.direction;a.gap=a.duplicated?parseInt(a.gap):0;b.wrapInner('<div class="js-marquee"></div>');var h=b.find(".js-marquee").css({"margin-right":a.gap,"float":"left"});a.duplicated&&h.clone(!0).appendTo(b);b.wrapInner('<div style="width:100000px" class="js-marquee-wrapper"></div>');
c=b.find(".js-marquee-wrapper");if(v){var k=b.height();c.removeAttr("style");b.height(k);b.find(".js-marquee").css({"float":"none","margin-bottom":a.gap,"margin-right":0});a.duplicated&&b.find(".js-marquee:last").css({"margin-bottom":0});var q=b.find(".js-marquee:first").height()+a.gap;a.startVisible&&!a.duplicated?(a._completeDuration=(parseInt(q,10)+parseInt(k,10))/parseInt(k,10)*a.duration,a.duration*=parseInt(q,10)/parseInt(k,10)):a.duration*=(parseInt(q,10)+parseInt(k,10))/parseInt(k,10)}else{var m=
b.find(".js-marquee:first").width()+a.gap;var n=b.width();a.startVisible&&!a.duplicated?(a._completeDuration=(parseInt(m,10)+parseInt(n,10))/parseInt(n,10)*a.duration,a.duration*=parseInt(m,10)/parseInt(n,10)):a.duration*=(parseInt(m,10)+parseInt(n,10))/parseInt(n,10)}a.duplicated&&(a.duration/=2);if(a.allowCss3Support){h=document.body||document.createElement("div");var g="marqueeAnimation-"+Math.floor(1E7*Math.random()),A=["Webkit","Moz","O","ms","Khtml"],B="animation",d="",r="";h.style.animation&&
(r="@keyframes "+g+" ",p=!0);if(!1===p)for(var z=0;z<A.length;z++)if(void 0!==h.style[A[z]+"AnimationName"]){h="-"+A[z].toLowerCase()+"-";B=h+B;y=h+y;r="@"+h+"keyframes "+g+" ";p=!0;break}p&&(d=g+" "+a.duration/1E3+"s "+a.delayBeforeStart/1E3+"s infinite "+a.css3easing,b.data("css3AnimationIsSupported",!0))}var C=function(){c.css("transform","translateY("+("up"==a.direction?k+"px":"-"+q+"px")+")")},D=function(){c.css("transform","translateX("+("left"==a.direction?n+"px":"-"+m+"px")+")")};a.duplicated?
(v?a.startVisible?c.css("transform","translateY(0)"):c.css("transform","translateY("+("up"==a.direction?k+"px":"-"+(2*q-a.gap)+"px")+")"):a.startVisible?c.css("transform","translateX(0)"):c.css("transform","translateX("+("left"==a.direction?n+"px":"-"+(2*m-a.gap)+"px")+")"),a.startVisible||(e=1)):a.startVisible?e=2:v?C():D();var w=function(){a.duplicated&&(1===e?(a._originalDuration=a.duration,a.duration=v?"up"==a.direction?a.duration+k/(q/a.duration):2*a.duration:"left"==a.direction?a.duration+n/
(m/a.duration):2*a.duration,d&&(d=g+" "+a.duration/1E3+"s "+a.delayBeforeStart/1E3+"s "+a.css3easing),e++):2===e&&(a.duration=a._originalDuration,d&&(g+="0",r=f.trim(r)+"0 ",d=g+" "+a.duration/1E3+"s 0s infinite "+a.css3easing),e++));v?a.duplicated?(2<e&&c.css("transform","translateY("+("up"==a.direction?0:"-"+q+"px")+")"),t={transform:"translateY("+("up"==a.direction?"-"+q+"px":0)+")"}):a.startVisible?2===e?(d&&(d=g+" "+a.duration/1E3+"s "+a.delayBeforeStart/1E3+"s "+a.css3easing),t={transform:"translateY("+
("up"==a.direction?"-"+q+"px":k+"px")+")"},e++):3===e&&(a.duration=a._completeDuration,d&&(g+="0",r=f.trim(r)+"0 ",d=g+" "+a.duration/1E3+"s 0s infinite "+a.css3easing),C()):(C(),t={transform:"translateY("+("up"==a.direction?"-"+c.height()+"px":k+"px")+")"}):a.duplicated?(2<e&&c.css("transform","translateX("+("left"==a.direction?0:"-"+m+"px")+")"),t={transform:"translateX("+("left"==a.direction?"-"+m+"px":0)+")"}):a.startVisible?2===e?(d&&(d=g+" "+a.duration/1E3+"s "+a.delayBeforeStart/1E3+"s "+a.css3easing),
t={transform:"translateX("+("left"==a.direction?"-"+m+"px":n+"px")+")"},e++):3===e&&(a.duration=a._completeDuration,d&&(g+="0",r=f.trim(r)+"0 ",d=g+" "+a.duration/1E3+"s 0s infinite "+a.css3easing),D()):(D(),t={transform:"translateX("+("left"==a.direction?"-"+m+"px":n+"px")+")"});b.trigger("beforeStarting");if(p){c.css(B,d);var h=r+" { 100% "+F(t)+"}",l=c.find("style");0!==l.length?l.filter(":last").html(h):f("head").append("<style>"+h+"</style>");E(c[0],"AnimationIteration",function(){b.trigger("finished")});
E(c[0],"AnimationEnd",function(){w();b.trigger("finished")})}else c.animate(t,a.duration,a.easing,function(){b.trigger("finished");a.pauseOnCycle?b.timer=setTimeout(w,a.delayBeforeStart):w()});b.data("runningStatus","resumed")};b.bind("pause",l.pause);b.bind("resume",l.resume);a.pauseOnHover&&(b.bind("mouseenter",l.pause),b.bind("mouseleave",l.resume));p&&a.allowCss3Support?w():b.timer=setTimeout(w,a.delayBeforeStart)}})};f.fn.marquee.defaults={allowCss3Support:!0,css3easing:"linear",easing:"linear",
delayBeforeStart:1E3,direction:"left",duplicated:!1,duration:5E3,gap:20,pauseOnCycle:!1,pauseOnHover:!1,startVisible:!1}})(jQuery);

@ -6,7 +6,7 @@
<div> Chosen book: <div> Chosen book:
<h1 class="header">{{ book.title }}</h1> <h1 class="header">{{ book.title }}</h1>
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" width="150" onerror="if (this.src != '../static/img/{{ book.cover }}') this.src = '../static/img/default_cover.gif';"> <img class="no_cover" id="{{ book.title }}" src="../cover/{{ book.cover }}" width="150" onerror="if (this.src != '../static/img/{{ book.cover }}') this.src = '../static/img/default_cover.gif';">
</div> </div>
<p>These are all the stacks that have been built so far.</p> <p>These are all the stacks that have been built so far.</p>

@ -14,7 +14,6 @@
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/js/jquery-ui-1.12.1.custom/jquery-ui.js">
{% block css %} {% endblock%} {% block css %} {% endblock%}
</head> </head>
<body> <body>
@ -43,16 +42,16 @@
{% block js %} {% endblock%} {% block js %} {% endblock%}
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.js"></script> <script src="{{ url_for("static", filename="js/jquery-3.3.1.min.js") }}"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <script src="{{ url_for("static", filename="js/jquery-ui-1.12.1.custom/jquery-ui.js") }}"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <script src="{{ url_for("static", filename="js/jquery.marquee.min.js") }}"></script>
<script type='text/javascript' src='//cdn.jsdelivr.net/jquery.marquee/1.4.0/jquery.marquee.min.js'></script>
<script type="text/javascript" src="{{ url_for("static", filename="js/jquery.tablesorter.js") }}"></script> <script type="text/javascript" src="{{ url_for("static", filename="js/jquery.tablesorter.js") }}"></script>
<script src="{{ url_for("static", filename="js/app.js") }}"></script> <script src="{{ url_for("static", filename="js/app.js") }}"></script>
<script src="{{ url_for("static", filename="js/vendor/socket.io.slim.js") }}"></script> <script src="{{ url_for("static", filename="js/vendor/socket.io.slim.js") }}"></script>
<script src="{{ url_for("static", filename="js/vendor/vue.min.js") }}"></script> <script src="{{ url_for("static", filename="js/vendor/vue.min.js") }}"></script>
<script> <script>
function convertTime(inTime){ function convertTime(inTime){
var time = inTime; var time = inTime;
time = time.match(/(\d+)\-(\d+)\-(\d+)\s*(\d+):(\d+):(\d+)/); time = time.match(/(\d+)\-(\d+)\-(\d+)\s*(\d+):(\d+):(\d+)/);
@ -121,9 +120,6 @@ socket.on('channel-' + app.channel, function(msg) {
}); });
// Add new message to HTML // Add new message to HTML
let my_messages = app.messages; let my_messages = app.messages;
my_messages.push({ my_messages.push({

@ -3,10 +3,16 @@
<li><a href="{{ url_for('home') }}">Home</a></li> <li><a href="{{ url_for('home') }}">Home</a></li>
<li><a href="{{ url_for('show_books') }}">Catalogue</a></li> <li><a href="{{ url_for('show_books') }}">Catalogue</a></li>
<li><a href="{{ url_for('show_stacks') }}">Stacks</a></li> <li><a href="{{ url_for('show_stacks') }}">Stacks</a></li>
{%if light%}
{%else%}
<li><a href="{{ url_for('add_book') }}">Add Book</a></li> <li><a href="{{ url_for('add_book') }}">Add Book</a></li>
<li><a href="{{ url_for ('add_stack') }}">Add Stack</a></li> <li><a href="{{ url_for ('add_stack') }}">Add Stack</a></li>
{%endif%}
<li><a href="{{ url_for('about') }}">About</a></li> <li><a href="{{ url_for('about') }}">About</a></li>
{%if light%}
{%else%}
<li><a href="{{ url_for('show_instances') }}">Instances</a></li> <li><a href="{{ url_for('show_instances') }}">Instances</a></li>
{%endif%}
</ul> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
</nav> </nav>

@ -15,6 +15,8 @@ Feel free to browse our catalogue, interfaced in many different ways.
<br><br><br> <br><br><br>
{%if light%}
{%else%}
<form method="GET" action="/export/csv"> <form method="GET" action="/export/csv">
<button type="submit">Export Catalogue (.CSV)</button> <button type="submit">Export Catalogue (.CSV)</button>
</form> </form>
@ -23,6 +25,7 @@ Feel free to browse our catalogue, interfaced in many different ways.
<button type = "button" onclick="document.getElementById('file_import_csv').click()">Import Catalogue (.CSV)</button> <button type = "button" onclick="document.getElementById('file_import_csv').click()">Import Catalogue (.CSV)</button>
</form> </form>
</div> </div>
{%endif%}
<div id="app" class="container"> <div id="app" class="container">
<div class="messageback1"></div> <div class="messageback1"></div>

@ -32,10 +32,13 @@
</div> </div>
{% endif %} {% endif %}
{% endwith %} {% endwith %}
{%if light%}
{%else%}
<div style="width: 220px; float: left; padding-right: 40px;"> <div style="width: 220px; float: left; padding-right: 40px;">
<img class="no_cover" width="220" src = '/static/img/default_cover.gif';> <img class="no_cover" width="220" src = '/static/img/default_cover.gif';>
</div> </div>
<br> <br>
<div> <div>
<h2>Add this book:</h2> <h2>Add this book:</h2>
@ -72,6 +75,7 @@
<br> <br>
<br> <br>
</div> </div>
{%endif%}
<div class= "Container" style= "border-top: dashed; border-width: 1px;"> <div class= "Container" style= "border-top: dashed; border-width: 1px;">
<h2> More potential books </h2> <h2> More potential books </h2>
<table class="library_table" id="table" style="width:100%"> <table class="library_table" id="table" style="width:100%">
@ -83,7 +87,10 @@
<th width="100px;">Year</th> <th width="100px;">Year</th>
<th width="100px;">Category</th> <th width="100px;">Category</th>
<th width="100px;">Stack</th> <th width="100px;">Stack</th>
{%if light%}
{%else%}
<th width="100px;">Add to a stack</th> <th width="100px;">Add to a stack</th>
{%endif%}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -91,7 +98,7 @@
<tr> <tr>
<td style= "padding: 5px;"> <td style= "padding: 5px;">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="70" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"> <img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="70" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';">
<!-- <object class="no_cover" data="../static/img/default_cover.png" type="image/png" width="65"> <!-- <object class="no_cover" data="../static/img/default_cover.png" type="image/png" width="65">
<p hidden="True"></p> <p hidden="True"></p>
@ -118,10 +125,13 @@
</td> </td>
{%if light%}
{%else%}
<td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}"> <td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}">
===> ===>
</a></td> </a></td>
{%endif%}
</tr> </tr>
{% endfor %} {% endfor %}

@ -9,12 +9,12 @@
{{ render_field(form.search) }} </div> {{ render_field(form.search) }} </div>
<button type="submit" class="button">browse</button> <button type="submit" class="button">browse</button>
<p><br> <p><br>
{{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p> {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p>
</form> </form>
</div> </div>
<br> <br>
<h3 style="line-height:0px;"">Results: "{{ query }}" included in {{ count }} out of {{ whole }} items </h3> <h3 style="line-height:0px;">Results: "{{ query }}" included in {{ count }} out of {{ whole }} items </h3>
<div style="height: 20px; background-color: yellow; line-height:4px;"> <div style="height: 20px; background-color: yellow; line-height:4px;">
<div style="background-color: black; width: {{ percentage }}%; height: 100%;"></div> <div style="background-color: black; width: {{ percentage }}%; height: 100%;"></div>
</div> </div>
@ -39,13 +39,15 @@
<th width="100px;">Year</th> <th width="100px;">Year</th>
<th width="100px;">Category</th> <th width="100px;">Category</th>
<th width="100px;">Stack</th> <th width="100px;">Stack</th>
{%if light%}
{%else%}
<th width="100px;">Add to stack</th> <th width="100px;">Add to stack</th>
{%endif%}
</tr> </tr>
{% for book in books %} {% for book in books %}
<tr> <tr>
<td style= "padding: 5px;"> <td style= "padding: 5px;">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="70" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></td> <img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="70" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></td>
<td><a href="{{url_for('show_book_by_id', id=book.id)}}">{{ book.title }}</a></td> <td><a href="{{url_for('show_book_by_id', id=book.id)}}">{{ book.title }}</a></td>
<td> {% for author in book.authors %} <td> {% for author in book.authors %}
@ -60,9 +62,12 @@
<li><a href="{{url_for('show_stack_by_id', id=stack.id)}}">{{ stack.stack_name }}</a> </li> <li><a href="{{url_for('show_stack_by_id', id=stack.id)}}">{{ stack.stack_name }}</a> </li>
{% endfor %} {% endfor %}
</td> </td>
{%if light%}
{%else%}
<td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}"> <td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}">
==> ==>
</a></td> </a></td>
{%endif%}
{% endfor %} {% endfor %}
</table> </table>
@ -81,12 +86,14 @@
<th width="100px;">Year</th> <th width="100px;">Year</th>
<th width="100px;">Category</th> <th width="100px;">Category</th>
<th width="100px;">Stack</th> <th width="100px;">Stack</th>
{%if light%}
{%else%}
<th width="100px;">Add to stack</th> <th width="100px;">Add to stack</th>
{%endif%}
</tr> </tr>
{% for book in books_all %} {% for book in books_all %}
<tr> <tr>
<td style= "padding: 5px;"><img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="80" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></td> <td style= "padding: 5px;"><img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="80" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></td>
<td><a href="{{url_for('show_book_by_id', id=book.id)}}">{{ book.title }}</a></td> <td><a href="{{url_for('show_book_by_id', id=book.id)}}">{{ book.title }}</a></td>
<td> {% for author in book.authors %} <td> {% for author in book.authors %}
@ -102,10 +109,12 @@
{% endfor %} {% endfor %}
</td> </td>
{%if light%}
{%else%}
<td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}"> <td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}">
==> ==>
</a></td> </a></td>
{%endif%}
{% endfor %} {% endfor %}
</table> </table>
<p> <p>

@ -9,12 +9,12 @@
{{ render_field(form.search) }} </div> {{ render_field(form.search) }} </div>
<button type="submit" class="button">browse</button> <button type="submit" class="button">browse</button>
<p><br> <p><br>
{{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p> {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p>
</form> </form>
</div> </div>
<br> <br>
<h3 style="line-height:0px;"">Results: "{{ query }}" included in {{ count }} out of {{ whole }} items </h3> <h3 style="line-height:0px;">Results: "{{ query }}" included in {{ count }} out of {{ whole }} items </h3>
<div style="height: 20px; background-color: yellow; line-height:4px;"> <div style="height: 20px; background-color: yellow; line-height:4px;">
<div style="background-color: black; width: {{ percentage }}%; height: 100%;"></div> <div style="background-color: black; width: {{ percentage }}%; height: 100%;"></div>
</div> </div>
@ -37,7 +37,7 @@
<div class="gridbox"> <div class="gridbox">
<a href="/books/{{ book.id }}"> <a href="/books/{{ book.id }}">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a> <img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a>
<p> <p>
<tbody> <tbody>
<th> <th>
@ -58,10 +58,10 @@
<div class="grid"> <div class="grid">
{% for book in books_all|sort(attribute='title', reverse = False) %} {% for book in books_all|sort(attribute='title', reverse = False) %}
<div class="gridbox"> <div class="gridbox">
<a href="books/{{ book.id }}"> <a href="books/{{ book.id }}">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a> <img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a>
<p> <p>
<tbody> <tbody>
<th> <th>

@ -74,7 +74,7 @@
<div class = "drag" id = "{{ book.id }}" style="position: absolute;width:70px;height:auto; top:{{ book.scapeY }}px; left:{{ book.scapeX }}px;"> <div class = "drag" id = "{{ book.id }}" style="position: absolute;width:70px;height:auto; top:{{ book.scapeY }}px; left:{{ book.scapeX }}px;">
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" style="width:100%;height:auto;" onerror="if (this.src != '../static/img/default_cover.gif') this.src = '../static/img/default_cover.gif';"> <img class="no_cover" id="{{ book.title }}" src="../cover/{{ book.cover }}" style="width:100%;height:auto;" onerror="if (this.src != '../static/img/default_cover.gif') this.src = '../static/img/default_cover.gif';">
<p class="booktitle" style="font-size:7px;"><a href="books/{{ book.id }}">{{ book.title }}</a></p> <p class="booktitle" style="font-size:7px;"><a href="books/{{ book.id }}">{{ book.title }}</a></p>
{% set got = {} %} {% set got = {} %}
@ -104,7 +104,7 @@
</div> </div>
</div> </div>
<script src="https://code.jquery.com/jquery-2.1.4.js"></script> <script src="{{ url_for("static", filename="js/jquery-3.3.1.min.js") }}"></script>
<script src="{{ url_for("static", filename="js/app.js") }}"></script> <script src="{{ url_for("static", filename="js/app.js") }}"></script>
<script> <script>
@ -150,8 +150,8 @@
} }
</script> </script>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <script src="{{ url_for("static", filename="js/jquery-ui-1.12.1.custom/jquery-ui.js") }}"></script>
<script> <script>
$( function() { $( function() {
var currentZoom = $('#scape_container').css('zoom'); var currentZoom = $('#scape_container').css('zoom');

@ -5,7 +5,7 @@
<h1 class="header">{{ book.title }}</h1> <h1 class="header">{{ book.title }}</h1>
<div style="float:right; padding-right: 140px;"> <div style="float:right; padding-right: 140px;">
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" width="280px" onerror="if (this.src != '../uploads/cover/{{ book.cover }}') this.src = '../static/img/default_cover.gif';"></div> <img class="no_cover" id="{{ book.title }}" src="../cover/{{ book.cover }}" width="280px" onerror="if (this.src != '../cover/{{ book.cover }}') this.src = '../static/img/default_cover.gif';"></div>
<table class="library_table" id="table" style="width:50%; padding-bottom: 80px;"> <table class="library_table" id="table" style="width:50%; padding-bottom: 80px;">
<thead> <thead>
@ -81,6 +81,8 @@
</tbody> </tbody>
</table> </table>
{%if light%}
{%else%}
<br> <br>
<a href="{{url_for('add_to_stack', id=book.id)}}">Add book to Stack ===></a> <br><br> <a href="{{url_for('add_to_stack', id=book.id)}}">Add book to Stack ===></a> <br><br>
<br> <br>
@ -98,10 +100,10 @@
{% else %} {% else %}
{% endif %} {% endif %}
<button style= "font-size: 10pt;"> <a href="{{ url_for('edit_book_by_id', id=book.id )}}">edit</a></button> <a href="{{ url_for('edit_book_by_id', id=book.id )}}"><button style= "font-size: 10pt;"> edit</button></a>
<button style= "font-size: 10pt;"> <a href="{{ url_for('remove_book_by_id', id=book.id)}}">delete</a></button> <a href="{{ url_for('remove_book_by_id', id=book.id)}}"><button style= "font-size: 10pt;"> delete</button></a>
<br><br> <br><br>
{%endif%}
<hr> <hr>
{% if previousbook %} {% if previousbook %}
<a href="{{ url_for('show_book_by_id', id=previousbook.id )}}" style="font-size: 9pt;"> < see the previous book added to XPPL: &nbsp;<i>{{ previousbook.title |truncate(40,True,'...') }} </i></a> {% endif %} <a href="{{ url_for('show_book_by_id', id=previousbook.id )}}" style="font-size: 9pt;"> < see the previous book added to XPPL: &nbsp;<i>{{ previousbook.title |truncate(40,True,'...') }} </i></a> {% endif %}

@ -36,7 +36,10 @@
<th width="100px;">Year</th> <th width="100px;">Year</th>
<th width="100px;">Category</th> <th width="100px;">Category</th>
<th width="100px;">Stack</th> <th width="100px;">Stack</th>
{%if light%}
{%else%}
<th width="100px;">Add to a stack</th> <th width="100px;">Add to a stack</th>
{%endif%}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -44,7 +47,7 @@
<tr> <tr>
<td style= "padding: 5px;"> <td style= "padding: 5px;">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="70" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"> <img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="70" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';">
<!-- <object class="no_cover" data="../static/img/default_cover.png" type="image/png" width="65"> <!-- <object class="no_cover" data="../static/img/default_cover.png" type="image/png" width="65">
<p hidden="True"></p> <p hidden="True"></p>
@ -75,10 +78,13 @@
</td> </td>
{%if light%}
{%else%}
<td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}"> <td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}">
===> ===>
</a></td> </a></td>{%endif%}
</tr> </tr>
{% endfor %} {% endfor %}

@ -9,7 +9,7 @@
{{ render_field(form.search) }} </div> {{ render_field(form.search) }} </div>
<button type="submit" class="button">browse</button> <button type="submit" class="button">browse</button>
<p><br> <p><br>
{{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p> {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p>
</form> </form>
</div> </div>
@ -34,7 +34,7 @@
<div class="gridbox"> <div class="gridbox">
<a href="books/{{ book.id }}"> <a href="books/{{ book.id }}">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a> <img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a>
<p> <p>
<tbody> <tbody>
<th> <th>

@ -26,7 +26,7 @@
{% endif %} {% endif %}
<li> <a href="{{url_for('show_book_by_id', id=book.id)}}">{{book.title}}</a> </li> <li> <a href="{{url_for('show_book_by_id', id=book.id)}}">{{book.title}}</a> </li>
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" width="150" onerror="if (this.src != '../uploads/cover/{{ book.cover }}') this.src = '../static/img/default_cover.gif';"> <img class="no_cover" id="{{ book.title }}" src="../cover/{{ book.cover }}" width="150" onerror="if (this.src != '../cover/{{ book.cover }}') this.src = '../static/img/default_cover.gif';">
<div class='widget'> <div class='widget'>
<iframe src="../uploads/{{ book.file }}" width="50%" ></iframe> <iframe src="../uploads/{{ book.file }}" width="50%" ></iframe>
</div> </div>
@ -36,9 +36,12 @@
<br> <br>
<br> <br>
{%if light%}
{%else%}
<p><a href="{{url_for('show_books')}}">Add some more books</a></p> <p><a href="{{url_for('show_books')}}">Add some more books</a></p>
<p><a href="{{ url_for('edit_stack_by_id', id=stack.id )}}">Edit title and/or description</a> </p> <p><a href="{{ url_for('edit_stack_by_id', id=stack.id )}}">Edit title and/or description</a> </p>
<p><a href="{{ url_for('remove_stack_by_id', id=stack.id )}}">Remove stack</a> </p> <p><a href="{{ url_for('remove_stack_by_id', id=stack.id )}}">Remove stack</a> </p>
{%endif%}
<p><a href="{{url_for('show_stacks')}}">Go back to the other stacks</p> <p><a href="{{url_for('show_stacks')}}">Go back to the other stacks</p>
</div> </div>

@ -5,9 +5,10 @@ Werkzeug Documentation: http://werkzeug.pocoo.org/documentation/
This file creates your application. This file creates your application.
""" """
from app import app, db, socketio, DOMAIN from app import app, db, socketio, DOMAIN, light
from flask import Flask, Response, session, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort from flask import Flask, Response, session, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort
import json import json
from functools import wraps
from sqlalchemy.sql.expression import func, select from sqlalchemy.sql.expression import func, select
from sqlalchemy.sql import except_ from sqlalchemy.sql import except_
from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm
@ -47,26 +48,34 @@ def allowed_file(filename):
# Routing for your application. # Routing for your application.
### ###
def check_light(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if not light:
return func(*args, **kwargs)
else:
flash("Your account has expired. Update your billing info.")
return redirect(url_for('home'))
return decorated_function
@app.route('/', methods= ['POST','GET']) @app.route('/', methods= ['POST','GET'])
def home(): def home():
print("/////////////")
print(light)
chat_form = ChatForm() chat_form = ChatForm()
chat_messages = db.session.query(Chat).all() chat_messages = db.session.query(Chat).all()
username = 'librarian' username = 'librarian'
# if request.method == 'POST':
# if chat_form.validate_on_submit():
# message = chat_form.message.data
# msg = Chat(message)
# db.session.add(msg)
# db.session.commit()
#client = request.remote_addr
server = request.host server = request.host
if request.environ.get('HTTP_X_FORWARDED_FOR') is None: if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
client =request.environ['REMOTE_ADDR'] client =request.environ['REMOTE_ADDR']
else: else:
client = request.environ['HTTP_X_FORWARDED_FOR'] client = request.environ['HTTP_X_FORWARDED_FOR']
return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username=username, client=client, server=server) return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username=username, client=client, server=server, light=light)
@app.route('/hello/<name>') @app.route('/hello/<name>')
def hello(name): def hello(name):
@ -75,9 +84,10 @@ def hello(name):
@app.route('/about/') @app.route('/about/')
def about(): def about():
"""Render the website's about page.""" """Render the website's about page."""
return render_template('about.html', name="Mary Jane") return render_template('about.html', light=light)
@app.route('/uploads/<filename>') @app.route('/uploads/<filename>')
@check_light
def uploaded_file(filename): def uploaded_file(filename):
book = Book.query.filter_by(file=filename).first() book = Book.query.filter_by(file=filename).first()
i = Instance(request.host, "download") i = Instance(request.host, "download")
@ -89,7 +99,7 @@ def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], return send_from_directory(app.config['UPLOAD_FOLDER'],
filename) filename)
@app.route('/uploads/cover/<filename>') @app.route('/cover/<filename>')
def uploaded_file_cover(filename): def uploaded_file_cover(filename):
return send_from_directory(app.config['UPLOAD_FOLDER_COVER'], return send_from_directory(app.config['UPLOAD_FOLDER_COVER'],
filename) filename)
@ -130,7 +140,7 @@ def scape():
@app.route('/books_grid') @app.route('/books_grid')
def show_books_grid(): def show_books_grid():
books = db.session.query(Book).all() # or you could have used User.query.all() books = db.session.query(Book).all() # or you could have used User.query.all()
return render_template('show_books_grid.html', books=books) return render_template('show_books_grid.html', books=books, light=light)
@app.route('/books/<int:id>') @app.route('/books/<int:id>')
def show_book_by_id(id): def show_book_by_id(id):
@ -154,12 +164,13 @@ def show_book_by_id(id):
db.session.add(user_info) db.session.add(user_info)
db.session.commit() db.session.commit()
if not book: if not book:
return render_template('red_link.html', id=id) return render_template('red_link.html', id=id, light=light)
else: else:
return render_template('show_book_detail.html', book=book, previousbook = previousbook, nextbook = nextbook, all_instances=all_instances) return render_template('show_book_detail.html', book=book, previousbook = previousbook, nextbook = nextbook, all_instances=all_instances, light=light)
@app.route('/books/<int:id>/delete', methods=['POST', 'GET']) @app.route('/books/<int:id>/delete', methods=['POST', 'GET'])
@check_light
def remove_book_by_id(id): def remove_book_by_id(id):
book_to_edit = Book.query.filter_by(id=id).first() book_to_edit = Book.query.filter_by(id=id).first()
title = book_to_edit.title title = book_to_edit.title
@ -170,6 +181,7 @@ def remove_book_by_id(id):
return redirect(url_for('show_books')) return redirect(url_for('show_books'))
@app.route('/books/<int:id>/edit', methods=['POST', 'GET']) @app.route('/books/<int:id>/edit', methods=['POST', 'GET'])
@check_light
def edit_book_by_id(id): def edit_book_by_id(id):
book_to_edit = Book.query.filter_by(id=id).first() book_to_edit = Book.query.filter_by(id=id).first()
user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published, message= book_to_edit.message, sameness=book_to_edit.sameness, gender=book_to_edit.gender, diversity=book_to_edit.diversity, who=book_to_edit.who) user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published, message= book_to_edit.message, sameness=book_to_edit.sameness, gender=book_to_edit.gender, diversity=book_to_edit.diversity, who=book_to_edit.who)
@ -238,10 +250,11 @@ def edit_book_by_id(id):
flash("%s updated" % (title)) flash("%s updated" % (title))
return redirect(url_for('show_book_by_id', id=id)) return redirect(url_for('show_book_by_id', id=id))
return render_template('edit_book_detail.html', book=book_to_edit, form=user_form) return render_template('edit_book_detail.html', book=book_to_edit, form=user_form, light=light)
@app.route('/add-book', methods=['POST', 'GET']) @app.route('/add-book', methods=['POST', 'GET'])
@check_light
def add_book(): def add_book():
upload_form = UploadForm() upload_form = UploadForm()
allbooks = db.session.query(Book).all() allbooks = db.session.query(Book).all()
@ -338,7 +351,7 @@ def add_book():
return redirect(url_for('show_books')) return redirect(url_for('show_books'))
flash_errors(upload_form) flash_errors(upload_form)
return render_template('add_book.html', form=upload_form, books_all=books_all, authors_all=authors_all, categories=categories, stacks_all=stacks_all, books_potential=books_potential, earliest=earliest, latest=latest) return render_template('add_book.html', form=upload_form, books_all=books_all, authors_all=authors_all, categories=categories, stacks_all=stacks_all, books_potential=books_potential, earliest=earliest, latest=latest, light=light)
# Flash errors from the form if validation fails # Flash errors from the form if validation fails
@ -358,10 +371,11 @@ def show_author_by_id(id):
if not author: if not author:
abort (404) abort (404)
else: else:
return render_template('show_author_detail.html', author=author) return render_template('show_author_detail.html', author=author, light=light)
@app.route('/authors/<int:id>/edit', methods=['POST', 'GET']) @app.route('/authors/<int:id>/edit', methods=['POST', 'GET'])
@check_light
def edit_author_by_id(id): def edit_author_by_id(id):
return "Ask the programmer." return "Ask the programmer."
@ -370,9 +384,10 @@ def edit_author_by_id(id):
@app.route('/stacks') @app.route('/stacks')
def show_stacks(): def show_stacks():
stacks = db.session.query(Stack).all() stacks = db.session.query(Stack).all()
return render_template('show_stacks.html', stacks=stacks) return render_template('show_stacks.html', stacks=stacks, light=light)
@app.route('/stacks/add_stack', methods=['POST', 'GET']) @app.route('/stacks/add_stack', methods=['POST', 'GET'])
@check_light
def add_stack(): def add_stack():
form = StackForm() form = StackForm()
stacks = db.session.query(Stack).all() stacks = db.session.query(Stack).all()
@ -388,7 +403,7 @@ def add_stack():
stacks = db.session.query(Stack).all() stacks = db.session.query(Stack).all()
return redirect(url_for('show_stacks')) return redirect(url_for('show_stacks'))
flash("%s stack created" % (stack_name)) flash("%s stack created" % (stack_name))
return render_template('add_stack.html', stacks=stacks, form=form) return render_template('add_stack.html', stacks=stacks, form=form, light=light)
@app.route('/stacks/tab/<int:id>', methods=['POST', 'GET']) @app.route('/stacks/tab/<int:id>', methods=['POST', 'GET'])
def show_stack_in_tab(id): def show_stack_in_tab(id):
@ -400,20 +415,22 @@ def show_stack_by_id(id, is_tab=False):
stack = Stack.query.get(id) stack = Stack.query.get(id)
if not stack: if not stack:
return render_template('add_stack.html', stacks=stacks, form=form) return render_template('add_stack.html', stacks=stacks, form=form, light=light)
else: else:
if is_tab == False: if is_tab == False:
return render_template('show_stack_detail.html', stack=stack) return render_template('show_stack_detail.html', stack=stack, light=light)
else: else:
return render_template('show_stack_detail_tab.html', stack=stack) return render_template('show_stack_detail_tab.html', stack=stack, light=light)
@app.route('/stacks/<int:id>/delete', methods=['POST', 'GET']) @app.route('/stacks/<int:id>/delete', methods=['POST', 'GET'])
@check_light
def remove_stack_by_id(id): def remove_stack_by_id(id):
Stack.query.filter_by(id=id).delete() Stack.query.filter_by(id=id).delete()
db.session.commit() db.session.commit()
return redirect(url_for('show_stacks')) return redirect(url_for('show_stacks'))
@app.route('/stacks/<int:id>/edit', methods=['POST', 'GET']) @app.route('/stacks/<int:id>/edit', methods=['POST', 'GET'])
@check_light
def edit_stack_by_id(id): def edit_stack_by_id(id):
stack = Stack.query.filter_by(id=id).first() stack = Stack.query.filter_by(id=id).first()
form = EditStackForm(edit_stack_name = stack.stack_name, edit_stack_description = stack.stack_description) form = EditStackForm(edit_stack_name = stack.stack_name, edit_stack_description = stack.stack_description)
@ -426,9 +443,10 @@ def edit_stack_by_id(id):
stack.stack_description = stack_description stack.stack_description = stack_description
db.session.commit() db.session.commit()
return redirect(url_for('show_stack_by_id', id=id)) return redirect(url_for('show_stack_by_id', id=id))
return render_template('edit_stack_detail.html', stack=stack, form=form) return render_template('edit_stack_detail.html', stack=stack, form=form, light=light)
@app.route('/instances', methods=['POST', 'GET']) @app.route('/instances', methods=['POST', 'GET'])
@check_light
def show_instances(): def show_instances():
all_instances = db.session.query(Instance).all() all_instances = db.session.query(Instance).all()
instances = [] instances = []
@ -457,17 +475,18 @@ def show_instances():
print(oldname) print(oldname)
print(name) print(name)
db.session.commit() db.session.commit()
return render_template('show_instances.html', instances=instances) return render_template('show_instances.html', instances=instances, light=light)
@app.route('/stacks/<int:stackid>/remove/<int:bookid>', methods=['POST', 'GET']) @app.route('/stacks/<int:stackid>/remove/<int:bookid>', methods=['POST', 'GET'])
@check_light
def remove_from_stack(bookid, stackid): def remove_from_stack(bookid, stackid):
book = Book.query.get(bookid) book = Book.query.get(bookid)
stack = Stack.query.get(stackid) stack = Stack.query.get(stackid)
if book not in stack.books: if book not in stack.books:
return render_template('show_book_detail.html', book=book) return render_template('show_book_detail.html', book=book, light=light)
stack.books.remove(book) stack.books.remove(book)
db.session.commit() db.session.commit()
return render_template('show_book_detail.html', book=book) return render_template('show_book_detail.html', book=book, light=light)
## search ## search
view = ['1'] view = ['1']
@ -482,12 +501,12 @@ def show_books():
if search.grid.data: if search.grid.data:
viewby = '2' viewby = '2'
view.append('2') view.append('2')
return render_template ('show_books_grid.html', books=books, form=search) return render_template ('show_books_grid.html', books=books, form=search, light=light)
if search.listview.data: if search.listview.data:
viewby = '1' viewby = '1'
view.append('1') view.append('1')
return render_template ('show_books.html', books=books, form=search) return render_template ('show_books.html', books=books, form=search, light=light)
if request.method == 'POST': if request.method == 'POST':
newmsg = 'searched for: ' + search.search.data newmsg = 'searched for: ' + search.search.data
@ -504,7 +523,7 @@ def show_books():
db.session.rollback() db.session.rollback()
return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby))) return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby)))
return render_template('show_books.html', books=books, form=search) return render_template('show_books.html', books=books, form=search, light=light)
@app.route('/search/<searchtype>/<viewby>/<query>', methods=['POST', 'GET']) @app.route('/search/<searchtype>/<viewby>/<query>', methods=['POST', 'GET'])
def search_results(searchtype, query, viewby): def search_results(searchtype, query, viewby):
@ -539,7 +558,7 @@ def search_results(searchtype, query, viewby):
if results.count() == 0: if results.count() == 0:
books = Book.query.filter(Book.file.like('potential.pdf')) books = Book.query.filter(Book.file.like('potential.pdf'))
upload_form = UploadForm(title= query, author='') upload_form = UploadForm(title= query, author='')
return render_template('red_link.html', form=upload_form, title=query, books=books) return render_template('red_link.html', form=upload_form, title=query, books=books, light=light)
count = results.count() count = results.count()
whole = Book.query.count() whole = Book.query.count()
@ -549,11 +568,11 @@ def search_results(searchtype, query, viewby):
if search.listview.data: if search.listview.data:
view.append('1') view.append('1')
return render_template('results.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) return render_template('results.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage, light=light)
if search.grid.data: if search.grid.data:
view.append('2') view.append('2')
return render_template('results_grid.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) return render_template('results_grid.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage, light=light)
if request.method == 'POST': if request.method == 'POST':
newmsg = 'searched for: ' + search.search.data newmsg = 'searched for: ' + search.search.data
@ -578,10 +597,10 @@ def search_results(searchtype, query, viewby):
return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby))) return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby)))
if viewby == '2': if viewby == '2':
return render_template('results_grid.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) return render_template('results_grid.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage, light=light)
else: else:
return render_template('results.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) return render_template('results.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage, light=light)
# ## Search - autocomplete # ## Search - autocomplete
@ -611,22 +630,24 @@ def search_results(searchtype, query, viewby):
## STACKS! ## STACKS!
@app.route('/add_to_stack/<int:id>', methods=['GET', 'POST']) @app.route('/add_to_stack/<int:id>', methods=['GET', 'POST'])
@check_light
def add_to_stack(id): def add_to_stack(id):
stacks = db.session.query(Stack).all() stacks = db.session.query(Stack).all()
add_form = AddtoStackForm(request.form) add_form = AddtoStackForm(request.form)
add_form.select_stack.choices = [(stack.id, stack.stack_name) for stack in stacks] add_form.select_stack.choices = [(stack.id, stack.stack_name) for stack in stacks]
if request.method == 'GET': if request.method == 'GET':
book = Book.query.get(id) book = Book.query.get(id)
return render_template('add_to_stacks.html', id=id, stacks=stacks, book=book, add_form=add_form) return render_template('add_to_stacks.html', id=id, stacks=stacks, book=book, add_form=add_form, light=light)
else: else:
stack = Stack.query.get(int(add_form.select_stack.data)) stack = Stack.query.get(int(add_form.select_stack.data))
book = Book.query.get(id) book = Book.query.get(id)
stack.books.append(book) stack.books.append(book)
db.session.commit() db.session.commit()
return render_template('show_stack_detail.html', stack=stack) return render_template('show_stack_detail.html', stack=stack, light=light)
@app.route('/export/csv', methods=['GET']) @app.route('/export/csv', methods=['GET'])
@check_light
def export_csv(): def export_csv():
output = io.StringIO() output = io.StringIO()
#fieldnames = ['title', 'authors', 'file', 'fileformat', 'category', 'year_published', 'description' ] #fieldnames = ['title', 'authors', 'file', 'fileformat', 'category', 'year_published', 'description' ]
@ -662,6 +683,7 @@ def export_csv():
import codecs import codecs
@app.route('/import/csv', methods= ['POST','GET']) @app.route('/import/csv', methods= ['POST','GET'])
@check_light
def import_csv(): def import_csv():
if request.method == 'POST': if request.method == 'POST':
if 'file' not in request.files: if 'file' not in request.files:
@ -718,10 +740,11 @@ def import_csv():
db.session.add(a) db.session.add(a)
book.authors.append(a) book.authors.append(a)
db.session.commit() db.session.commit()
return render_template('import_csv.html', numberadded=numberadded) return render_template('import_csv.html', numberadded=numberadded, light=light)
@app.route('/empty_catalogue487352698237465', methods= ['POST','GET']) @app.route('/emptycataloguexpubxpubfuck', methods= ['POST','GET'])
@check_light
def empty_catalogue(): def empty_catalogue():
meta = db.metadata meta = db.metadata
for table in reversed(meta.sorted_tables): for table in reversed(meta.sorted_tables):
@ -765,12 +788,6 @@ def get_chat():
# The functions below should be applicable to all Flask apps. # The functions below should be applicable to all Flask apps.
### ###
@app.route('/<file_name>.txt')
def send_text_file(file_name):
"""Send your static text file."""
file_dot_text = file_name + '.txt'
return app.send_static_file(file_dot_text)
@app.after_request @app.after_request
def add_header(response): def add_header(response):
@ -779,14 +796,14 @@ def add_header(response):
and also to cache the rendered page for 10 minutes. and also to cache the rendered page for 10 minutes.
""" """
response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1' response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1'
response.headers['Cache-Control'] = 'public, max-age=600' response.headers['Cache-Control'] = 'no-cache'
return response return response
@app.errorhandler(404) @app.errorhandler(404)
def page_not_found(error): def page_not_found(error):
"""Custom 404 page.""" """Custom 404 page."""
return render_template('404.html'), 404 return render_template('404.html', light=light), 404
### SOCKET for the chat ### SOCKET for the chat

Loading…
Cancel
Save