Fix StringIO import to cStringIO Added Blender Support from piernov Added Badge Button on ListParticipantmaster
@@ -147,6 +147,9 @@ def main(global_config, **settings): | |||||
## Users | ## Users | ||||
config.add_route('pict_user', '/user_picture') | config.add_route('pict_user', '/user_picture') | ||||
config.add_route('show_user', '/user/{user_slug:([\w-]+)?}') | config.add_route('show_user', '/user/{user_slug:([\w-]+)?}') | ||||
config.add_route('badge_user', '/user/{user_slug:([\w-]+)?}/badge') | |||||
config.add_route('badge_user1', '/user/{user_slug:([\w-]+)?}/badge1') | |||||
config.add_route('badge_user2', '/user/{user_slug:([\w-]+)?}/badge2') | |||||
# HTML Routes - Logged | # HTML Routes - Logged | ||||
#config.add_route('profil', 'MesJM2L') | #config.add_route('profil', 'MesJM2L') | ||||
@@ -0,0 +1,561 @@ | |||||
# -*- coding: utf8 -*- | |||||
from pyramid.httpexceptions import HTTPNotFound | |||||
from pyramid.response import Response | |||||
import cStringIO as StringIO | |||||
from pyramid.view import view_config | |||||
from .models import User | |||||
from reportlab.pdfgen import canvas | |||||
from reportlab.pdfbase import pdfmetrics | |||||
from reportlab.pdfbase.ttfonts import TTFont | |||||
from reportlab.lib.units import mm | |||||
import qrcode | |||||
# Create PDF container | |||||
WIDTH = 85 * mm | |||||
HEIGHT = 60 * mm | |||||
ICONSIZE = 8 * mm | |||||
def Ribbon35(DispUser, canvas): | |||||
canvas.saveState() | |||||
canvas.rotate(35) | |||||
offset_u=0 | |||||
if DispUser.Staff: | |||||
# Staff | |||||
canvas.setFillColorRGB(1,.2,.2) | |||||
canvas.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
canvas.setFillColorRGB(1,1,1) | |||||
canvas.setFont('Liberation', 20) | |||||
canvas.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+5, "STAFF") | |||||
elif DispUser.is_Intervenant: | |||||
# Intervenant | |||||
canvas.setFillColorRGB(.3,.3,1) | |||||
canvas.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
canvas.setFillColorRGB(1,1,1) | |||||
canvas.setFont('Liberation', 15) | |||||
canvas.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+10, "Intervenant") | |||||
else: | |||||
# Visiteur | |||||
canvas.setFillColorRGB(.8,.8,.8) | |||||
canvas.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
canvas.setFillColorRGB(0,0,0) | |||||
canvas.setFont('Liberation', 12) | |||||
canvas.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+7, "Visiteur") | |||||
canvas.restoreState() | |||||
return canvas | |||||
def Ribbon45(DispUser, canvas): | |||||
canvas.saveState() | |||||
canvas.rotate(45) | |||||
offset_u=0 | |||||
if DispUser.Staff: | |||||
# Staff | |||||
canvas.setFillColorRGB(1,.2,.2) | |||||
canvas.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
canvas.setFillColorRGB(1,1,1) | |||||
canvas.setFont('Liberation', 20) | |||||
canvas.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+5, "STAFF") | |||||
elif DispUser.is_Intervenant: | |||||
# Intervenant | |||||
canvas.setFillColorRGB(.3,.3,1) | |||||
canvas.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
canvas.setFillColorRGB(1,1,1) | |||||
canvas.setFont('Liberation', 15) | |||||
canvas.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+10, "Intervenant") | |||||
else: | |||||
# Visiteur | |||||
canvas.setFillColorRGB(.8,.8,.8) | |||||
canvas.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
canvas.setFillColorRGB(0,0,0) | |||||
canvas.setFont('Liberation', 12) | |||||
canvas.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+7, "Visiteur") | |||||
canvas.restoreState() | |||||
return canvas | |||||
def Ribbon90(DispUser, canvas): | |||||
canvas.saveState() | |||||
canvas.rotate(90) | |||||
offset_u=0 | |||||
if DispUser.Staff: | |||||
# Staff | |||||
canvas.setFillColorRGB(1,.2,.2) | |||||
canvas.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
canvas.setFillColorRGB(1,1,1) | |||||
canvas.setFont('Liberation', 20) | |||||
canvas.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+5, "STAFF") | |||||
elif DispUser.is_Intervenant: | |||||
# Intervenant | |||||
canvas.setFillColorRGB(.3,.3,1) | |||||
canvas.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
canvas.setFillColorRGB(1,1,1) | |||||
canvas.setFont('Liberation', 15) | |||||
canvas.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+10, "Intervenant") | |||||
else: | |||||
# Visiteur | |||||
canvas.setFillColorRGB(.8,.8,.8) | |||||
canvas.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
canvas.setFillColorRGB(0,0,0) | |||||
canvas.setFont('Liberation', 12) | |||||
canvas.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+7, "Visiteur") | |||||
canvas.restoreState() | |||||
return canvas | |||||
def JM2L_Logo(canvas, Position="Up"): | |||||
# Import font | |||||
ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||||
pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo)) | |||||
if Position=="Up": | |||||
logoobject = canvas.beginText() | |||||
logoobject.setFont('Logo', 52) | |||||
logoobject.setFillColorRGB(.83,0,.33) | |||||
logoobject.setTextOrigin(55, HEIGHT-50) | |||||
logoobject.textLines("JM2L") | |||||
canvas.drawText(logoobject) | |||||
yearobject = canvas.beginText() | |||||
yearobject.setFont("Helvetica-Bold", 15) | |||||
yearobject.setFillColorRGB(1,1,1) | |||||
yearobject.setTextOrigin(67, HEIGHT-20) | |||||
yearobject.setWordSpace(21) | |||||
yearobject.textLines("2 0 1 5") | |||||
canvas.drawText(yearobject) | |||||
elif Position=="Down": | |||||
logoobject = canvas.beginText() | |||||
logoobject.setFont('Logo', 32) | |||||
logoobject.setFillColorRGB(.83,0,.33) | |||||
logoobject.setTextOrigin(2, 17) | |||||
logoobject.textLines("JM2L") | |||||
canvas.drawText(logoobject) | |||||
yearobject = canvas.beginText() | |||||
yearobject.setFont("Helvetica-Bold", 10) | |||||
yearobject.setFillColorRGB(1,1,1) | |||||
#yearobject.setLineWidth(.1) | |||||
#yearobject.setStrokeColorRGB(.5,.5,.5) | |||||
yearobject.setTextRenderMode(0) | |||||
#yearobject.setStrokeOverprint(.2) | |||||
yearobject.setTextOrigin(9 , 35) | |||||
yearobject.setWordSpace(13) | |||||
yearobject.textLines("2 0 1 5") | |||||
canvas.drawText(yearobject) | |||||
def Tiers_Logo(canvas, DispUser, LWidth=4, StartPos=None): | |||||
Border = 1 | |||||
if StartPos is None: | |||||
StartPos = ( WIDTH -70, 0 ) | |||||
StartX, StartY = StartPos | |||||
num = 0 | |||||
canvas.setStrokeColorRGB(0.5,0.5,0.5) | |||||
Logos = filter(lambda x:x.ThumbLinks, DispUser.tiers) | |||||
for tiers in Logos: | |||||
FileName = tiers.ThumbLinks.pop().split("/")[-1] | |||||
ImagePath = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, FileName) | |||||
# Should We compute a better positionning for logos ? | |||||
#PosX = StartX + (( (2.0*num+1) / 2*len(Logos) )*(ICONSIZE+Border) - ((ICONSIZE+Border)/2)) | |||||
PosX = StartX + (num % LWidth) * (ICONSIZE+Border) | |||||
if len(Logos)<=LWidth: | |||||
# Middle of line | |||||
PosY = StartY + 0.5 * (ICONSIZE+Border) | |||||
else: | |||||
# in two or more lines | |||||
PosY = StartY + (num / LWidth) * (ICONSIZE+Border) | |||||
canvas.setLineWidth(.1) | |||||
canvas.drawImage(ImagePath, | |||||
PosX, PosY, ICONSIZE, ICONSIZE,\ | |||||
preserveAspectRatio=True, | |||||
anchor='c', | |||||
mask=[0,3,0,3,0,3] | |||||
) | |||||
canvas.roundRect(PosX, PosY, ICONSIZE, ICONSIZE, radius=2, stroke=True) | |||||
num+=1 | |||||
def QRCode(DispUser): | |||||
qr = qrcode.QRCode( | |||||
version=1, | |||||
error_correction=qrcode.constants.ERROR_CORRECT_L, | |||||
box_size=10, | |||||
border=2, | |||||
) | |||||
# Data of QR code | |||||
qr.add_data('http://jm2l.linux-azur.org/user/%s' % DispUser.slug) | |||||
qr.make(fit=True) | |||||
return qr.make_image() | |||||
@view_config(route_name='badge_user') | |||||
def badge_user(request): | |||||
user_slug = request.matchdict.get('user_slug', None) | |||||
if user_slug is None or len(user_slug)==0: | |||||
raise HTTPNotFound(u"Cet utilisateur n'a pas été reconnu") | |||||
# Query database | |||||
DispUser = User.by_slug(user_slug) | |||||
if DispUser is None: | |||||
raise HTTPNotFound() | |||||
# Ok let's generate a PDF Badge | |||||
# Register LiberationMono font | |||||
ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||||
pdfmetrics.registerFont(TTFont("Liberation", ttfFile)) | |||||
pdf = StringIO.StringIO() | |||||
c = canvas.Canvas( pdf, pagesize=(WIDTH, HEIGHT) ) | |||||
c.translate(mm, mm) | |||||
# Feed some metadata | |||||
c.setCreator("linux-azur.org") | |||||
c.setTitle("Badge") | |||||
c.saveState() | |||||
# Logo on Top | |||||
JM2L_Logo(c, "Down") | |||||
if DispUser.Staff: | |||||
# Staff | |||||
c.setFillColorRGB(.83,0,.33) | |||||
c.rect(-3, HEIGHT-30, WIDTH, HEIGHT, fill=1) | |||||
c.setFillColorRGB(1,1,1) | |||||
c.setFont('Liberation', 30) | |||||
c.drawCentredString(WIDTH/2, HEIGHT-26, "STAFF") | |||||
elif DispUser.is_Intervenant: | |||||
# Intervenant | |||||
c.setFillColorRGB(.3,.3,1) | |||||
c.rect(-3, HEIGHT-30, WIDTH, HEIGHT, fill=1) | |||||
c.setFillColorRGB(1,1,1) | |||||
c.setFont('Liberation', 30) | |||||
c.drawCentredString(WIDTH/2, HEIGHT-26, "Intervenant") | |||||
else: | |||||
# Visiteur | |||||
c.setFillColorRGB(.8,.8,.8) | |||||
c.rect(-3, HEIGHT-30, WIDTH, HEIGHT, fill=1) | |||||
c.setFillColorRGB(1,1,1) | |||||
c.setFont('Liberation', 30) | |||||
c.drawCentredString(WIDTH/2, HEIGHT-26, "Visiteur") | |||||
c.restoreState() | |||||
c.setFont('Liberation', 18) | |||||
c.setStrokeColorRGB(0,0,0) | |||||
c.setFillColorRGB(0,0,0) | |||||
# Feed Name and SurName | |||||
if len(DispUser.prenom) + len(DispUser.nom)>18: | |||||
if DispUser.pseudo: | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 + 4 * mm , "%s" % DispUser.prenom ) | |||||
c.setFont('Courier', 17) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 - 2 * mm , "%s" % DispUser.nom ) | |||||
else: | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 + 2 * mm , "%s" % DispUser.prenom ) | |||||
c.setFont('Courier', 17) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 - 6 * mm , "%s" % DispUser.nom ) | |||||
else: | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 + 0 * mm , "%s %s" % (DispUser.prenom, DispUser.nom) ) | |||||
if DispUser.pseudo: | |||||
c.setFont("Helvetica-Oblique", 14) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 - 8 * mm , "%s" % DispUser.pseudo ) | |||||
# Put QR code to user profile | |||||
c.drawInlineImage(QRCode(DispUser), \ | |||||
WIDTH - 20 * mm - 7, 0, \ | |||||
20 * mm, 20 * mm, \ | |||||
preserveAspectRatio=True, \ | |||||
anchor='s') | |||||
Tiers_Logo(c, DispUser, 4, ( 30 * mm, 2 )) | |||||
c.showPage() | |||||
c.save() | |||||
pdf.seek(0) | |||||
return Response(app_iter=pdf, content_type = 'application/pdf' ) | |||||
@view_config(route_name='badge_user1') | |||||
def badge_user1(request): | |||||
user_slug = request.matchdict.get('user_slug', None) | |||||
if user_slug is None or len(user_slug)==0: | |||||
raise HTTPNotFound(u"Cet utilisateur n'a pas été reconnu") | |||||
# Query database | |||||
DispUser = User.by_slug(user_slug) | |||||
if DispUser is None: | |||||
raise HTTPNotFound() | |||||
# Ok let's generate a PDF Badge | |||||
ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||||
ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||||
pdfmetrics.registerFont(TTFont("Liberation", ttfFile)) | |||||
pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo)) | |||||
pdf = StringIO.StringIO() | |||||
c = canvas.Canvas( pdf, pagesize=(WIDTH, HEIGHT) ) | |||||
c.translate(mm, mm) | |||||
# Feed some metadata | |||||
c.setCreator("linux-azur.org") | |||||
c.setTitle("Badge") | |||||
c.saveState() | |||||
logoobject = c.beginText() | |||||
logoobject.setFont('Logo', 52) | |||||
logoobject.setFillColorRGB(.83,0,.33) | |||||
logoobject.setTextOrigin(55, HEIGHT-50) | |||||
logoobject.textLines("JM2L") | |||||
c.drawText(logoobject) | |||||
yearobject = c.beginText() | |||||
yearobject.setFont("Helvetica-Bold", 15) | |||||
yearobject.setFillColorRGB(1,1,1) | |||||
yearobject.setTextOrigin(67, HEIGHT-20) | |||||
yearobject.setWordSpace(21) | |||||
yearobject.textLines("2 0 1 5") | |||||
c.drawText(yearobject) | |||||
num = 0 | |||||
for tiers in DispUser.tiers: | |||||
if tiers.ThumbLinks: | |||||
FileName = tiers.ThumbLinks.pop().split("/")[-1] | |||||
num+=1 | |||||
ImagePath = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, FileName) | |||||
if num<6: | |||||
c.drawImage(ImagePath, | |||||
WIDTH -5 - num * (ICONSIZE+5), 5, ICONSIZE, ICONSIZE,\ | |||||
preserveAspectRatio=True, | |||||
anchor='c', | |||||
mask=[0,3,0,3,0,3]) | |||||
else: | |||||
c.drawImage(ImagePath, | |||||
WIDTH -5 - (num-5) * (ICONSIZE+5), 5 + ICONSIZE, ICONSIZE, ICONSIZE,\ | |||||
preserveAspectRatio=True, | |||||
anchor='c', | |||||
mask=[0,3,0,3,0,3]) | |||||
if 1: | |||||
Ribbon90(DispUser, c) | |||||
if 0: | |||||
c.rotate(90) | |||||
offset_u=111 | |||||
if DispUser.Staff: | |||||
# Staff | |||||
c.setFillColorRGB(1,.2,.2) | |||||
c.rect(-5, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
c.setFillColorRGB(1,1,1) | |||||
c.setFont('Liberation', 30) | |||||
c.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+5, "STAFF") | |||||
elif DispUser.is_Intervenant: | |||||
# Intervenant | |||||
c.setFillColorRGB(.3,.3,1) | |||||
c.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
c.setFillColorRGB(1,1,1) | |||||
c.setFont('Liberation', 15) | |||||
c.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+10, "Intervenant") | |||||
else: | |||||
# Visiteur | |||||
c.setFillColorRGB(.8,.8,.8) | |||||
c.rect(-5, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
c.setFillColorRGB(0,0,0) | |||||
c.setFont('Liberation', 19) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2-offset_u+7, "Visiteur") | |||||
c.restoreState() | |||||
c.setFont('Courier', 18) | |||||
c.setStrokeColorRGB(0,0,0) | |||||
c.setFillColorRGB(0,0,0) | |||||
# Feed Name and SurName | |||||
if len(DispUser.prenom) + len(DispUser.nom)>18: | |||||
if DispUser.pseudo: | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 + 4 * mm , "%s" % DispUser.prenom ) | |||||
c.setFont('Courier', 17) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 - 2 * mm , "%s" % DispUser.nom ) | |||||
else: | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 + 2 * mm , "%s" % DispUser.prenom ) | |||||
c.setFont('Courier', 17) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 - 6 * mm , "%s" % DispUser.nom ) | |||||
else: | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 + 0 * mm , "%s %s" % (DispUser.prenom, DispUser.nom) ) | |||||
#c.drawCentredString(WIDTH/2, HEIGHT - 22 * mm, ) | |||||
if DispUser.pseudo: | |||||
#c.setFont('Liberation', 14) | |||||
c.setFont("Helvetica-Oblique", 14) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 - 8 * mm , "\"%s\"" % DispUser.pseudo ) | |||||
#c.restoreState() | |||||
# Put QR code to user profile | |||||
c.drawInlineImage(QRCode(DispUser), \ | |||||
WIDTH - 20 * mm - 7, 0, \ | |||||
20 * mm, 20 * mm, \ | |||||
preserveAspectRatio=True, \ | |||||
anchor='s') | |||||
c.showPage() | |||||
c.save() | |||||
pdf.seek(0) | |||||
return Response(app_iter=pdf, content_type = 'application/pdf' ) | |||||
@view_config(route_name='badge_user2') | |||||
def badge_user2(request): | |||||
user_slug = request.matchdict.get('user_slug', None) | |||||
if user_slug is None or len(user_slug)==0: | |||||
raise HTTPNotFound(u"Cet utilisateur n'a pas été reconnu") | |||||
# Query database | |||||
DispUser = User.by_slug(user_slug) | |||||
if DispUser is None: | |||||
raise HTTPNotFound() | |||||
# Ok let's generate a PDF Badge | |||||
ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||||
ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||||
pdfmetrics.registerFont(TTFont("Liberation", ttfFile)) | |||||
pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo)) | |||||
pdf = StringIO.StringIO() | |||||
qr = qrcode.QRCode( | |||||
version=1, | |||||
error_correction=qrcode.constants.ERROR_CORRECT_L, | |||||
box_size=10, | |||||
border=2, | |||||
) | |||||
# Data of QR code | |||||
qr.add_data('http://jm2l.linux-azur.org/user/%s' % DispUser.slug) | |||||
qr.make(fit=True) | |||||
img = qr.make_image() | |||||
# Create PDF container | |||||
WIDTH = 85 * mm | |||||
HEIGHT = 60 * mm | |||||
ICONSIZE = 10 * mm | |||||
c = canvas.Canvas( pdf, pagesize=(WIDTH, HEIGHT) ) | |||||
c.translate(mm, mm) | |||||
# Feed some metadata | |||||
c.setCreator("linux-azur.org") | |||||
c.setTitle("Badge") | |||||
c.saveState() | |||||
logoobject = c.beginText() | |||||
logoobject.setFont('Logo', 52) | |||||
logoobject.setFillColorRGB(.83,0,.33) | |||||
logoobject.setTextOrigin(55, HEIGHT-50) | |||||
logoobject.textLines("JM2L") | |||||
c.drawText(logoobject) | |||||
yearobject = c.beginText() | |||||
yearobject.setFont("Helvetica-Bold", 15) | |||||
yearobject.setFillColorRGB(1,1,1) | |||||
yearobject.setTextOrigin(67, HEIGHT-20) | |||||
yearobject.setWordSpace(21) | |||||
yearobject.textLines("2 0 1 5") | |||||
c.drawText(yearobject) | |||||
num = 0 | |||||
for tiers in DispUser.tiers: | |||||
if tiers.ThumbLinks: | |||||
FileName = tiers.ThumbLinks.pop().split("/")[-1] | |||||
num+=1 | |||||
ImagePath = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, FileName) | |||||
if num<6: | |||||
c.drawImage(ImagePath, | |||||
WIDTH -5 - num * (ICONSIZE+5), 5, ICONSIZE, ICONSIZE,\ | |||||
preserveAspectRatio=True, | |||||
anchor='c', | |||||
mask=[0,3,0,3,0,3]) | |||||
else: | |||||
c.drawImage(ImagePath, | |||||
WIDTH -5 - (num-5) * (ICONSIZE+5), 5 + ICONSIZE, ICONSIZE, ICONSIZE,\ | |||||
preserveAspectRatio=True, | |||||
anchor='c', | |||||
mask=[0,3,0,3,0,3]) | |||||
if 1: | |||||
c.rotate(35) | |||||
offset_u=0 | |||||
if DispUser.Staff: | |||||
# Staff | |||||
c.setFillColorRGB(1,.2,.2) | |||||
c.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
c.setFillColorRGB(1,1,1) | |||||
c.setFont('Liberation', 20) | |||||
c.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+5, "STAFF") | |||||
elif DispUser.is_Intervenant: | |||||
# Intervenant | |||||
c.setFillColorRGB(.3,.3,1) | |||||
c.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
c.setFillColorRGB(1,1,1) | |||||
c.setFont('Liberation', 15) | |||||
c.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+10, "Intervenant") | |||||
else: | |||||
# Visiteur | |||||
c.setFillColorRGB(.8,.8,.8) | |||||
c.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
c.setFillColorRGB(0,0,0) | |||||
c.setFont('Liberation', 12) | |||||
c.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+7, "Visiteur") | |||||
if 0: | |||||
c.rotate(90) | |||||
offset_u=111 | |||||
if DispUser.Staff: | |||||
# Staff | |||||
c.setFillColorRGB(1,.2,.2) | |||||
c.rect(-5, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
c.setFillColorRGB(1,1,1) | |||||
c.setFont('Liberation', 30) | |||||
c.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+5, "STAFF") | |||||
elif DispUser.is_Intervenant: | |||||
# Intervenant | |||||
c.setFillColorRGB(.3,.3,1) | |||||
c.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
c.setFillColorRGB(1,1,1) | |||||
c.setFont('Liberation', 15) | |||||
c.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+10, "Intervenant") | |||||
else: | |||||
# Visiteur | |||||
c.setFillColorRGB(.8,.8,.8) | |||||
c.rect(-5, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1) | |||||
c.setFillColorRGB(0,0,0) | |||||
c.setFont('Liberation', 19) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2-offset_u+7, "Visiteur") | |||||
c.restoreState() | |||||
c.setFont('Courier', 18) | |||||
c.setStrokeColorRGB(0,0,0) | |||||
c.setFillColorRGB(0,0,0) | |||||
# Feed Name and SurName | |||||
if len(DispUser.prenom) + len(DispUser.nom)>18: | |||||
if DispUser.pseudo: | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 + 4 * mm , "%s" % DispUser.prenom ) | |||||
c.setFont('Courier', 17) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 - 2 * mm , "%s" % DispUser.nom ) | |||||
else: | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 + 2 * mm , "%s" % DispUser.prenom ) | |||||
c.setFont('Courier', 17) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 - 6 * mm , "%s" % DispUser.nom ) | |||||
else: | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 + 0 * mm , "%s %s" % (DispUser.prenom, DispUser.nom) ) | |||||
#c.drawCentredString(WIDTH/2, HEIGHT - 22 * mm, ) | |||||
if DispUser.pseudo: | |||||
#c.setFont('Liberation', 14) | |||||
c.setFont("Helvetica-Oblique", 14) | |||||
c.drawCentredString(WIDTH/2, HEIGHT/2 - 8 * mm , "\"%s\"" % DispUser.pseudo ) | |||||
#c.restoreState() | |||||
# Put QR code to user profile | |||||
c.drawInlineImage(img, WIDTH - 20 * mm - 7, 0, 20 * mm, 20 * mm, preserveAspectRatio=True, anchor='s') | |||||
c.showPage() | |||||
c.save() | |||||
pdf.seek(0) | |||||
return Response(app_iter=pdf, content_type = 'application/pdf' ) |
@@ -0,0 +1,198 @@ | |||||
#!/usr/bin/env python | |||||
# ##### BEGIN GPL LICENSE BLOCK ##### | |||||
# | |||||
# This program is free software; you can redistribute it and/or | |||||
# modify it under the terms of the GNU General Public License | |||||
# as published by the Free Software Foundation; either version 2 | |||||
# of the License, or (at your option) any later version. | |||||
# | |||||
# This program is distributed in the hope that it will be useful, | |||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
# GNU General Public License for more details. | |||||
# | |||||
# You should have received a copy of the GNU General Public License | |||||
# along with this program; if not, write to the Free Software Foundation, | |||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
# | |||||
# ##### END GPL LICENSE BLOCK ##### | |||||
# <pep8 compliant> | |||||
""" | |||||
Thumbnailer runs with python 2.7 and 3.x. | |||||
To run automatically with a file manager such as Nautilus, save this file | |||||
in a directory that is listed in PATH environment variable, and create | |||||
blender.thumbnailer file in ${HOME}/.local/share/thumbnailers/ directory | |||||
with the following contents: | |||||
[Thumbnailer Entry] | |||||
TryExec=blender-thumbnailer.py | |||||
Exec=blender-thumbnailer.py %u %o | |||||
MimeType=application/x-blender; | |||||
""" | |||||
import struct | |||||
def open_wrapper_get(): | |||||
""" wrap OS spesific read functionality here, fallback to 'open()' | |||||
""" | |||||
class GFileWrapper: | |||||
__slots__ = ("mode", "g_file") | |||||
def __init__(self, url, mode='r'): | |||||
self.mode = mode # used in gzip module | |||||
self.g_file = Gio.File.parse_name(url).read(None) | |||||
def read(self, size): | |||||
return self.g_file.read_bytes(size, None).get_data() | |||||
def seek(self, offset, whence=0): | |||||
self.g_file.seek(offset, [1, 0, 2][whence], None) | |||||
return self.g_file.tell() | |||||
def tell(self): | |||||
return self.g_file.tell() | |||||
def close(self): | |||||
self.g_file.close(None) | |||||
def open_local_url(url, mode='r'): | |||||
o = urlparse(url) | |||||
if o.scheme == '': | |||||
path = o.path | |||||
elif o.scheme == 'file': | |||||
path = unquote(o.path) | |||||
else: | |||||
raise(IOError('URL scheme "%s" needs gi.repository.Gio module' % o.scheme)) | |||||
return open(path, mode) | |||||
try: | |||||
from gi.repository import Gio | |||||
return GFileWrapper | |||||
except ImportError: | |||||
try: | |||||
# Python 3 | |||||
from urllib.parse import urlparse, unquote | |||||
except ImportError: | |||||
# Python 2 | |||||
from urlparse import urlparse | |||||
from urllib import unquote | |||||
return open_local_url | |||||
def blend_extract_thumb(path): | |||||
import os | |||||
open_wrapper = open_wrapper_get() | |||||
# def MAKE_ID(tag): ord(tag[0])<<24 | ord(tag[1])<<16 | ord(tag[2])<<8 | ord(tag[3]) | |||||
REND = 1145980242 # MAKE_ID(b'REND') | |||||
TEST = 1414743380 # MAKE_ID(b'TEST') | |||||
blendfile = open_wrapper(path, 'rb') | |||||
head = blendfile.read(12) | |||||
if head[0:2] == b'\x1f\x8b': # gzip magic | |||||
import gzip | |||||
blendfile.close() | |||||
blendfile = gzip.GzipFile('', 'rb', 0, open_wrapper(path, 'rb')) | |||||
head = blendfile.read(12) | |||||
if not head.startswith(b'BLENDER'): | |||||
blendfile.close() | |||||
return None, 0, 0 | |||||
is_64_bit = (head[7] == b'-'[0]) | |||||
# true for PPC, false for X86 | |||||
is_big_endian = (head[8] == b'V'[0]) | |||||
# blender pre 2.5 had no thumbs | |||||
if head[9:11] <= b'24': | |||||
blendfile.close() | |||||
return None, 0, 0 | |||||
sizeof_bhead = 24 if is_64_bit else 20 | |||||
int_endian_pair = '>ii' if is_big_endian else '<ii' | |||||
while True: | |||||
bhead = blendfile.read(sizeof_bhead) | |||||
if len(bhead) < sizeof_bhead: | |||||
return None, 0, 0 | |||||
code, length = struct.unpack(int_endian_pair, bhead[0:8]) # 8 == sizeof(int) * 2 | |||||
if code == REND: | |||||
blendfile.seek(length, os.SEEK_CUR) | |||||
else: | |||||
break | |||||
if code != TEST: | |||||
blendfile.close() | |||||
return None, 0, 0 | |||||
try: | |||||
x, y = struct.unpack(int_endian_pair, blendfile.read(8)) # 8 == sizeof(int) * 2 | |||||
except struct.error: | |||||
blendfile.close() | |||||
return None, 0, 0 | |||||
length -= 8 # sizeof(int) * 2 | |||||
if length != x * y * 4: | |||||
blendfile.close() | |||||
return None, 0, 0 | |||||
image_buffer = blendfile.read(length) | |||||
if len(image_buffer) != length: | |||||
blendfile.close() | |||||
return None, 0, 0 | |||||
blendfile.close() | |||||
return image_buffer, x, y | |||||
def write_png(buf, width, height): | |||||
import zlib | |||||
# reverse the vertical line order and add null bytes at the start | |||||
width_byte_4 = width * 4 | |||||
raw_data = b"".join(b'\x00' + buf[span:span + width_byte_4] for span in range((height - 1) * width * 4, -1, - width_byte_4)) | |||||
def png_pack(png_tag, data): | |||||
chunk_head = png_tag + data | |||||
return struct.pack("!I", len(data)) + chunk_head + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)) | |||||
return b"".join([ | |||||
b'\x89PNG\r\n\x1a\n', | |||||
png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)), | |||||
png_pack(b'IDAT', zlib.compress(raw_data, 9)), | |||||
png_pack(b'IEND', b'')]) | |||||
def main(): | |||||
import sys | |||||
if len(sys.argv) < 3: | |||||
print("Expected 2 arguments <input.blend> <output.png>") | |||||
else: | |||||
file_in = sys.argv[-2] | |||||
buf, width, height = blend_extract_thumb(file_in) | |||||
if buf: | |||||
file_out = sys.argv[-1] | |||||
f = open(file_out, "wb") | |||||
f.write(write_png(buf, width, height)) | |||||
f.close() | |||||
if __name__ == '__main__': | |||||
main() |
@@ -3,7 +3,7 @@ | |||||
import random | import random | ||||
from PIL import Image, ImageDraw, ImageFont, ImageFilter | from PIL import Image, ImageDraw, ImageFont, ImageFilter | ||||
import StringIO | |||||
import cStringIO as StringIO | |||||
import math | import math | ||||
from pyramid.view import view_config | from pyramid.view import view_config | ||||
from .words import TabMots | from .words import TabMots | ||||
@@ -169,6 +169,13 @@ class User(Base): | |||||
return u | return u | ||||
return None | return None | ||||
@property | |||||
def is_Intervenant(self, year=2015): | |||||
""" This property will return if User do an event on specified year """ | |||||
return DBSession.query(Event).join(User_Event) \ | |||||
.filter(User_Event.user_uid==self.uid) \ | |||||
.filter(Event.for_year==year).count() | |||||
@property | @property | ||||
def my_hash(self): | def my_hash(self): | ||||
m = hashlib.sha1() | m = hashlib.sha1() | ||||
@@ -162,7 +162,10 @@ now = datetime.datetime.now() | |||||
<a href="javascript:alert('${u.nom}, ${u.prenom}\n${u.phone}');"> | <a href="javascript:alert('${u.nom}, ${u.prenom}\n${u.phone}');"> | ||||
<i class="icon-headphones"></i> | <i class="icon-headphones"></i> | ||||
</a> | </a> | ||||
% endif | |||||
% endif | |||||
<a href="/user/${u.slug}/badge"> | |||||
<i class="icon-qrcode"></i> | |||||
</a> | |||||
</span> | </span> | ||||
</td> | </td> | ||||
<td style="text-align:center;"> | <td style="text-align:center;"> | ||||
@@ -2,8 +2,7 @@ | |||||
from pyramid.view import view_config, view_defaults | from pyramid.view import view_config, view_defaults | ||||
from pyramid.response import Response | from pyramid.response import Response | ||||
from pyramid.exceptions import NotFound | from pyramid.exceptions import NotFound | ||||
from pyramid.httpexceptions import HTTPNotFound | |||||
from pyramid.request import Request | |||||
from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest | |||||
from PIL import Image | from PIL import Image | ||||
import re, os, shutil | import re, os, shutil | ||||
from os import path | from os import path | ||||
@@ -13,6 +12,7 @@ import subprocess | |||||
import cStringIO as StringIO | import cStringIO as StringIO | ||||
# Database access imports | # Database access imports | ||||
from .models import User, Place, Tiers, Event, SallePhy | from .models import User, Place, Tiers, Event, SallePhy | ||||
from .blenderthumbnailer import blend_extract_thumb, write_png | |||||
CurrentYear = 2015 | CurrentYear = 2015 | ||||
MIN_FILE_SIZE = 1 # bytes | MIN_FILE_SIZE = 1 # bytes | ||||
@@ -27,7 +27,8 @@ ACCEPTED_MIMES = ['application/pdf', | |||||
'application/vnd.oasis.opendocument.presentation-template', | 'application/vnd.oasis.opendocument.presentation-template', | ||||
'application/vnd.oasis.opendocument.spreadsheet', | 'application/vnd.oasis.opendocument.spreadsheet', | ||||
'application/vnd.oasis.opendocument.spreadsheet-template', | 'application/vnd.oasis.opendocument.spreadsheet-template', | ||||
'image/svg+xml' | |||||
'image/svg+xml', | |||||
'application/x-blender' | |||||
] | ] | ||||
@@ -136,6 +137,38 @@ class MediaPath(): | |||||
if mime=='application/pdf': | if mime=='application/pdf': | ||||
return "/img/PDF.png" | return "/img/PDF.png" | ||||
def check_blend_file(self, fileobj): | |||||
head = fileobj.read(12) | |||||
fileobj.seek(0) | |||||
if head[:2] == b'\x1f\x8b': # gzip magic | |||||
import zlib | |||||
head = zlib.decompress(fileobj.read(), 31)[:12] | |||||
fileobj.seek(0) | |||||
if head.startswith(b'BLENDER'): | |||||
return True | |||||
def get_mimetype_from_file(self, fileobj): | |||||
mimetype = magic.from_buffer(fileobj.read(1024), mime=True) | |||||
fileobj.seek(0) | |||||
# Check if the binary file is a blender file | |||||
if ( mimetype == "application/octet-stream" or mimetype == "application/x-gzip" ) and self.check_blend_file(fileobj): | |||||
return "application/x-blender", True | |||||
else: | |||||
return mimetype, False | |||||
def get_mimetype(self, name): | |||||
""" This function return the mime-type based on .type file """ | |||||
try: | |||||
with open(self.mediapath(name) + '.type', 'r', 16) as f: | |||||
mime = f.read() | |||||
return mime | |||||
except IOError: | |||||
return None | |||||
@view_defaults(route_name='media_upload') | @view_defaults(route_name='media_upload') | ||||
class MediaUpload(MediaPath): | class MediaUpload(MediaPath): | ||||
@@ -152,19 +185,31 @@ class MediaUpload(MediaPath): | |||||
return self.get_mediapath(self.media_table, self.linked_id, name) | return self.get_mediapath(self.media_table, self.linked_id, name) | ||||
def validate(self, result, filecontent): | def validate(self, result, filecontent): | ||||
# let's say we don't trust the uploader | |||||
RealMime = magic.from_buffer( filecontent.read(1024), mime=True) | |||||
""" Do some basic check to the uploaded file before to accept it """ | |||||
# Try to determine mime type from content uploaded | |||||
found_mime = magic.from_buffer(filecontent.read(1024), mime=True) | |||||
filecontent.seek(0) | filecontent.seek(0) | ||||
if RealMime!=result['type']: | |||||
result['error'] = 'l\'extension du fichier ne correspond pas à son contenu - ' | |||||
result['error'] += "( %s vs %s )" % (RealMime, result['type']) | |||||
# Do a special statement for specific detected mime type | |||||
if found_mime in ["application/octet-stream", "application/x-gzip"]: | |||||
# Lets see if it's a bender file | |||||
if self.check_blend_file(filecontent): | |||||
found_mime = "application/x-blender" | |||||
# MonKey Patch of content type | |||||
result['type'] = found_mime | |||||
# Reject mime type that don't match | |||||
if found_mime!=result['type']: | |||||
result['error'] = 'L\'extension du fichier ne correspond pas à son contenu - ' | |||||
result['error'] += "( %s vs %s )" % (found_mime, result['type']) | |||||
return False | return False | ||||
# Accept images and mime types listed | # Accept images and mime types listed | ||||
if not RealMime in ACCEPTED_MIMES: | |||||
if not (IMAGE_TYPES.match(RealMime)): | |||||
if not found_mime in ACCEPTED_MIMES: | |||||
if not (IMAGE_TYPES.match(found_mime)): | |||||
result['error'] = 'Ce type fichier n\'est malheureusement pas supporté. ' | result['error'] = 'Ce type fichier n\'est malheureusement pas supporté. ' | ||||
result['error'] += 'Les fichiers acceptées sont les images et pdf.' | |||||
return False | return False | ||||
if result['size'] < MIN_FILE_SIZE: | if result['size'] < MIN_FILE_SIZE: | ||||
result['error'] = 'le fichier est trop petit' | result['error'] = 'le fichier est trop petit' | ||||
elif result['size'] > MAX_FILE_SIZE: | elif result['size'] > MAX_FILE_SIZE: | ||||
@@ -173,12 +218,13 @@ class MediaUpload(MediaPath): | |||||
# file['error'] = u'les type de fichiers acceptés sont png, jpg et gif' | # file['error'] = u'les type de fichiers acceptés sont png, jpg et gif' | ||||
else: | else: | ||||
return True | return True | ||||
return False | return False | ||||
def get_file_size(self, file): | |||||
file.seek(0, 2) # Seek to the end of the file | |||||
size = file.tell() # Get the position of EOF | |||||
file.seek(0) # Reset the file position to the beginning | |||||
def get_file_size(self, fileobj): | |||||
fileobj.seek(0, 2) # Seek to the end of the file | |||||
size = fileobj.tell() # Get the position of EOF | |||||
fileobj.seek(0) # Reset the file position to the beginning | |||||
return size | return size | ||||
def thumbnailurl(self,name): | def thumbnailurl(self,name): | ||||
@@ -254,9 +300,6 @@ class MediaUpload(MediaPath): | |||||
def docthumbnail(self, filename): | def docthumbnail(self, filename): | ||||
TargetFileName = self.thumbnailpath(filename) | TargetFileName = self.thumbnailpath(filename) | ||||
# unoconv need a libre office server to be up | |||||
Command = ["unoconv", "-f", "pdf", "-e", "PageRange=1", "--output=%s" % TargetFileName, \ | |||||
"%s[0]" % self.mediapath(filename) ] | |||||
# let's take the thumbnail generated inside the document | # let's take the thumbnail generated inside the document | ||||
Command = ["unzip", "-p", self.mediapath(filename), "Thumbnails/thumbnail.png"] | Command = ["unzip", "-p", self.mediapath(filename), "Thumbnails/thumbnail.png"] | ||||
ThumbBytes = subprocess.check_output(Command) | ThumbBytes = subprocess.check_output(Command) | ||||
@@ -284,6 +327,40 @@ class MediaUpload(MediaPath): | |||||
timage.convert('RGB').save( TargetFileName+".jpg", 'JPEG') | timage.convert('RGB').save( TargetFileName+".jpg", 'JPEG') | ||||
return self.thumbnailurl( os.path.basename(TargetFileName+".jpg") ) | return self.thumbnailurl( os.path.basename(TargetFileName+".jpg") ) | ||||
def blendthumbnail(self, filename): | |||||
blendfile = self.mediapath(filename) | |||||
# Extract Thumb | |||||
if 0: | |||||
head = fileobj.read(12) | |||||
fileobj.seek(0) | |||||
if head[:2] == b'\x1f\x8b': # gzip magic | |||||
import zlib | |||||
head = zlib.decompress(fileobj.read(), 31)[:12] | |||||
fileobj.seek(0) | |||||
buf, width, height = blend_extract_thumb(blendfile) | |||||
if buf: | |||||
png = write_png(buf, width, height) | |||||
TargetFileName = self.thumbnailpath(filename) | |||||
image = Image.open(StringIO.StringIO(png)) | |||||
blender_indicator = Image.open( "jm2l/static/img/Blender_Thumb_Stamp.png" ) | |||||
image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS) | |||||
timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0)) | |||||
# Add thumbnail | |||||
timage.paste( | |||||
image, | |||||
((THUMBNAIL_SIZE - image.size[0]) / 2, (THUMBNAIL_SIZE - image.size[1]) / 2)) | |||||
# Stamp with Blender file type | |||||
timage.paste( | |||||
blender_indicator, | |||||
(timage.size[0]-30, timage.size[1]-30), | |||||
blender_indicator, | |||||
) | |||||
timage.save( TargetFileName+".png") | |||||
return self.thumbnailurl( os.path.basename(TargetFileName+".png") ) | |||||
return self.ExtMimeIcon('application/x-blender') | |||||
def fileinfo(self,name): | def fileinfo(self,name): | ||||
filename = self.mediapath(name) | filename = self.mediapath(name) | ||||
f, ext = os.path.splitext(name) | f, ext = os.path.splitext(name) | ||||
@@ -295,13 +372,17 @@ class MediaUpload(MediaPath): | |||||
name=name, | name=name, | ||||
media_table=self.media_table, | media_table=self.media_table, | ||||
uid=self.linked_id) | uid=self.linked_id) | ||||
mime = mimetypes.types_map.get(ext) | |||||
if (IMAGE_TYPES.match(mime)): | |||||
mime = self.get_mimetype(name) | |||||
if IMAGE_TYPES.match(mime): | |||||
info['thumbnailUrl'] = self.thumbnailurl(name) | info['thumbnailUrl'] = self.thumbnailurl(name) | ||||
elif mime in ACCEPTED_MIMES: | elif mime in ACCEPTED_MIMES: | ||||
thumb = self.thumbnailpath("%s%s" % (f, ext)) | thumb = self.thumbnailpath("%s%s" % (f, ext)) | ||||
if os.path.exists( thumb +'.jpg' ): | |||||
info['thumbnailUrl'] = self.thumbnailurl(name)+'.jpg' | |||||
thumbext = ".jpg" | |||||
if mime == "application/x-blender": | |||||
thumbext = ".png" | |||||
if os.path.exists( thumb + thumbext ): | |||||
info['thumbnailUrl'] = self.thumbnailurl(name)+thumbext | |||||
else: | else: | ||||
info['thumbnailUrl'] = self.ExtMimeIcon(mime) | info['thumbnailUrl'] = self.ExtMimeIcon(mime) | ||||
else: | else: | ||||
@@ -343,7 +424,6 @@ class MediaUpload(MediaPath): | |||||
@view_config(request_method='DELETE', xhr=True, accept="application/json", renderer='json') | @view_config(request_method='DELETE', xhr=True, accept="application/json", renderer='json') | ||||
def delete(self): | def delete(self): | ||||
import json | |||||
filename = self.request.matchdict.get('name') | filename = self.request.matchdict.get('name') | ||||
try: | try: | ||||
os.remove(self.mediapath(filename) + '.type') | os.remove(self.mediapath(filename) + '.type') | ||||
@@ -381,11 +461,17 @@ class MediaUpload(MediaPath): | |||||
result['name'] = os.path.basename(fieldStorage.filename) | result['name'] = os.path.basename(fieldStorage.filename) | ||||
result['type'] = fieldStorage.type | result['type'] = fieldStorage.type | ||||
result['size'] = self.get_file_size(fieldStorage.file) | result['size'] = self.get_file_size(fieldStorage.file) | ||||
if self.validate(result, fieldStorage.file): | if self.validate(result, fieldStorage.file): | ||||
with open( self.mediapath(result['name'] + '.type'), 'w') as f: | |||||
# Keep mime-type in .type file | |||||
with open( self.mediapath( result['name'] ) + '.type', 'w') as f: | |||||
f.write(result['type']) | f.write(result['type']) | ||||
with open( self.mediapath(result['name']), 'wb') as f: | |||||
# Store uploaded file | |||||
fieldStorage.file.seek(0) | |||||
with open( self.mediapath(result['name'] ), 'wb') as f: | |||||
shutil.copyfileobj( fieldStorage.file , f) | shutil.copyfileobj( fieldStorage.file , f) | ||||
if re.match(IMAGE_TYPES, result['type']): | if re.match(IMAGE_TYPES, result['type']): | ||||
result['thumbnailUrl'] = self.createthumbnail(result['name']) | result['thumbnailUrl'] = self.createthumbnail(result['name']) | ||||
elif result['type']=='application/pdf': | elif result['type']=='application/pdf': | ||||
@@ -394,8 +480,11 @@ class MediaUpload(MediaPath): | |||||
result['thumbnailUrl'] = self.svgthumbnail(result['name']) | result['thumbnailUrl'] = self.svgthumbnail(result['name']) | ||||
elif result['type'].startswith('application/vnd'): | elif result['type'].startswith('application/vnd'): | ||||
result['thumbnailUrl'] = self.docthumbnail(result['name']) | result['thumbnailUrl'] = self.docthumbnail(result['name']) | ||||
elif result['type']=='application/x-blender': | |||||
result['thumbnailUrl'] = self.blendthumbnail(result['name']) | |||||
else: | else: | ||||
result['thumbnailUrl'] = self.ExtMimeIcon(result['type']) | result['thumbnailUrl'] = self.ExtMimeIcon(result['type']) | ||||
result['deleteType'] = DELETEMETHOD | result['deleteType'] = DELETEMETHOD | ||||
result['deleteUrl'] = self.request.route_url('media_upload', | result['deleteUrl'] = self.request.route_url('media_upload', | ||||
sep='', | sep='', | ||||
@@ -425,12 +514,8 @@ class MediaView(MediaPath): | |||||
@view_config(request_method='GET', http_cache = (EXPIRATION_TIME, {'public':True})) | @view_config(request_method='GET', http_cache = (EXPIRATION_TIME, {'public':True})) | ||||
def get(self): | def get(self): | ||||
name = self.request.matchdict.get('name') | name = self.request.matchdict.get('name') | ||||
try: | |||||
with open( self.mediapath( os.path.basename(name) ) + '.type', 'r', 16) as f: | |||||
test = f.read() | |||||
self.request.response.content_type = test | |||||
except IOError: | |||||
pass | |||||
self.request.response.content_type = self.get_mimetype(name) | |||||
try: | try: | ||||
self.request.response.body_file = open( self.mediapath(name), 'rb', 10000) | self.request.response.body_file = open( self.mediapath(name), 'rb', 10000) | ||||
except IOError: | except IOError: | ||||
@@ -30,7 +30,9 @@ requires = [ | |||||
'pyramid_exclog', | 'pyramid_exclog', | ||||
'repoze.sendmail==4.1', | 'repoze.sendmail==4.1', | ||||
'pyramid_mailer', | 'pyramid_mailer', | ||||
'apscheduler' | |||||
'apscheduler', | |||||
'qrcode', | |||||
'reportlab' | |||||
] | ] | ||||
setup(name='JM2L', | setup(name='JM2L', | ||||