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', | ||||