@ -0,0 +1,50 @@ |
||||
"""Added the 'image' tables. |
||||
|
||||
Revision ID: 23e928dda837 |
||||
Revises: 1ee62841eb90 |
||||
Create Date: 2019-12-17 07:15:55.472022 |
||||
|
||||
""" |
||||
from alembic import op |
||||
import sqlalchemy as sa |
||||
|
||||
|
||||
# revision identifiers, used by Alembic. |
||||
revision = '23e928dda837' |
||||
down_revision = '1ee62841eb90' |
||||
branch_labels = None |
||||
depends_on = None |
||||
|
||||
|
||||
def upgrade(): |
||||
# ### commands auto generated by Alembic - please adjust! ### |
||||
op.create_table('publisher_image', |
||||
sa.Column('publ_id', sa.Integer(), nullable=False), |
||||
sa.Column('image_filename', sa.String(length=500), nullable=False), |
||||
sa.Column('image_data', sa.LargeBinary(), nullable=False), |
||||
sa.ForeignKeyConstraint(['publ_id'], ['publisher.publ_id'], ondelete='CASCADE'), |
||||
sa.PrimaryKeyConstraint('publ_id') |
||||
) |
||||
op.create_table('publication_image', |
||||
sa.Column('pub_id', sa.Integer(), nullable=False), |
||||
sa.Column('image_filename', sa.String(length=500), nullable=False), |
||||
sa.Column('image_data', sa.LargeBinary(), nullable=False), |
||||
sa.ForeignKeyConstraint(['pub_id'], ['publication.pub_id'], ondelete='CASCADE'), |
||||
sa.PrimaryKeyConstraint('pub_id') |
||||
) |
||||
op.create_table('article_image', |
||||
sa.Column('article_id', sa.Integer(), nullable=False), |
||||
sa.Column('image_filename', sa.String(length=500), nullable=False), |
||||
sa.Column('image_data', sa.LargeBinary(), nullable=False), |
||||
sa.ForeignKeyConstraint(['article_id'], ['article.article_id'], ondelete='CASCADE'), |
||||
sa.PrimaryKeyConstraint('article_id') |
||||
) |
||||
# ### end Alembic commands ### |
||||
|
||||
|
||||
def downgrade(): |
||||
# ### commands auto generated by Alembic - please adjust! ### |
||||
op.drop_table('article_image') |
||||
op.drop_table('publication_image') |
||||
op.drop_table('publisher_image') |
||||
# ### end Alembic commands ### |
@ -0,0 +1,25 @@ |
||||
""" Handle image requests. """ |
||||
|
||||
import io |
||||
|
||||
from flask import send_file, abort |
||||
|
||||
from asl_articles import app |
||||
#from asl_articles.models import PublisherImage, PublicationImage, ArticleImage |
||||
import asl_articles.models |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
@app.route( "/images/<image_type>/<image_id>" ) |
||||
def get_image( image_type, image_id ): |
||||
"""Return an image.""" |
||||
model = getattr( asl_articles.models, image_type.capitalize()+"Image" ) |
||||
if not model: |
||||
abort( 404 ) |
||||
img = model.query.get( image_id ) |
||||
if not img: |
||||
abort( 404 ) |
||||
return send_file( |
||||
io.BytesIO( img.image_data ), |
||||
attachment_filename = img.image_filename # nb: so that Flask can set the MIME type |
||||
) |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,76 @@ |
||||
import { MAX_IMAGE_UPLOAD_SIZE } from "./constants.js" ; |
||||
import { bytesDisplayString } from "./utils.js" ; |
||||
import { gAppRef } from "./index.js" ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
export class FileUploader { |
||||
|
||||
// Because Selenium can't control a browser's native "open file" dialog, we need a different mechanism
|
||||
// to test features that require uploading a file. The test suite stores the data it wants to upload
|
||||
// as a base64-encoded string in a hidden textarea, and we load it from there.
|
||||
|
||||
getFile( evt, maxSize, onLoad ) { |
||||
|
||||
function onLoadWrapper( fname, data ) { |
||||
// check that the uploaded file is not too big
|
||||
if ( maxSize && data.length > maxSize ) { |
||||
gAppRef.showErrorMsg( "The file must be no more than " + bytesDisplayString(maxSize) + " in size." ) ; |
||||
return ; |
||||
} |
||||
// notify the caller about the uploaded data
|
||||
onLoad( fname, data ) ; |
||||
} |
||||
|
||||
// check if we're being run by the test suite
|
||||
if ( gAppRef.isFakeUploads() ) { |
||||
// yup - load the file data sent to us by the test suite
|
||||
let data = gAppRef.getStoredMsg( "upload" ) ; |
||||
let pos = data.indexOf( "|" ) ; |
||||
let fname = data.substr( 0, pos ) ; |
||||
data = data.substr( pos+1 ) ; |
||||
onLoadWrapper( fname, data ) ; |
||||
// let the test suite know we've received the data
|
||||
gAppRef.setStoredMsg( "upload", "" ) ; |
||||
return ; |
||||
} else { |
||||
// nope - read the file data normally
|
||||
let fname = evt.target.files[0].name ; |
||||
let fileReader = new FileReader() ; |
||||
fileReader.onload = () => { onLoadWrapper( fname, fileReader.result ) } ; |
||||
fileReader.readAsDataURL( evt.target.files[0] ) ; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
export class ImageFileUploader { |
||||
|
||||
getFile( evt, imageRef, removeImageRef, onLoad ) { |
||||
|
||||
let fileUploader = new FileUploader() ; |
||||
let maxSize = MAX_IMAGE_UPLOAD_SIZE ; |
||||
if ( gAppRef.isTestMode() && gAppRef.args.max_image_upload_size ) |
||||
maxSize = gAppRef.args.max_image_upload_size ; |
||||
fileUploader.getFile( evt, maxSize, (fname,data) => { |
||||
// fix-up the image data received
|
||||
let prefix ; |
||||
if ( gAppRef.isFakeUploads() ) |
||||
prefix = "data:image/unknown;base64," ; |
||||
else { |
||||
let pos = data.indexOf( ";base64," ) ; |
||||
prefix = data.substr( 0, pos+8 ) ; |
||||
data = data.substring( pos+8 ) ; |
||||
} |
||||
// update the UI
|
||||
imageRef.src = prefix + data ; |
||||
removeImageRef.style.display = "inline" ; |
||||
// notify the caller
|
||||
onLoad( fname, data ) ; |
||||
} ) ; |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,2 @@ |
||||
.row.image img.image { height: 2em ; cursor: pointer ; } |
||||
.row.image img.remove-image { height: 1em ; margin-left: 0.25em ; cursor: pointer ; } |
@ -0,0 +1 @@ |
||||
export let MAX_IMAGE_UPLOAD_SIZE = ( 100 * 1024 ) ; |