mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-13 10:35:40 +00:00
Add the ability to extract image URL information when drag-and-dropping image URL from a browser window
- Can't do anything with it yet... - Code is almost there but leaving for now
This commit is contained in:
@ -4,6 +4,7 @@ Provides helper functions used throughout the InvenTree project
|
|||||||
|
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
|
import os.path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
@ -20,6 +21,19 @@ def TestIfImage(img):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def TestIfImageURL(url):
|
||||||
|
""" Test if an image URL (or filename) looks like a valid image format.
|
||||||
|
|
||||||
|
Simply tests the extension against a set of allowed values
|
||||||
|
"""
|
||||||
|
return os.path.splitext(os.path.basename(url))[-1].lower() in [
|
||||||
|
'.jpg', '.jpeg',
|
||||||
|
'.png', '.bmp',
|
||||||
|
'.tif', '.tiff',
|
||||||
|
'.webp',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def str2bool(text, test=True):
|
def str2bool(text, test=True):
|
||||||
""" Test if a string 'looks' like a boolean value.
|
""" Test if a string 'looks' like a boolean value.
|
||||||
|
|
||||||
|
@ -106,37 +106,29 @@
|
|||||||
|
|
||||||
var transfer = event.originalEvent.dataTransfer;
|
var transfer = event.originalEvent.dataTransfer;
|
||||||
|
|
||||||
|
var formData = new FormData();
|
||||||
|
|
||||||
if (isFileTransfer(transfer)) {
|
if (isFileTransfer(transfer)) {
|
||||||
|
formData.append('image_file', transfer.files[0]);
|
||||||
var file = transfer.files[0];
|
|
||||||
|
|
||||||
inventreeFileUpload(
|
|
||||||
"{% url 'part-image-upload' part.id %}",
|
|
||||||
file,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
success: function(data, status, xhr) {
|
|
||||||
location.reload();
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
showAlertDialog('Error uploading image', renderErrorMessage(xhr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else if (isOnlineTransfer(transfer)) {
|
} else if (isOnlineTransfer(transfer)) {
|
||||||
|
formData.append('image_url', getImageUrlFromTransfer(transfer));
|
||||||
getImageUrlFromTransfer(transfer);
|
|
||||||
/*
|
|
||||||
for (var i = 0; i < 12; i++) {
|
|
||||||
transfer.items[i].getAsString(function(text) {
|
|
||||||
console.log('item ' + i + ' - ' + text);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} else {
|
} else {
|
||||||
console.log('Unknown transfer');
|
console.log('Unknown transfer');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inventreeFormDataUpload(
|
||||||
|
"{% url 'part-image-upload' part.id %}",
|
||||||
|
formData,
|
||||||
|
{
|
||||||
|
success: function(data, status, xhr) {
|
||||||
|
location.reload();
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
showAlertDialog('Error uploading image', renderErrorMessage(xhr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#show-qr-code").click(function() {
|
$("#show-qr-code").click(function() {
|
||||||
|
@ -7,6 +7,9 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
|
from django.core.validators import URLValidator
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
@ -30,7 +33,7 @@ from .forms import EditSupplierPartForm
|
|||||||
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||||
from InvenTree.views import QRCodeView
|
from InvenTree.views import QRCodeView
|
||||||
|
|
||||||
from InvenTree.helpers import DownloadFile, str2bool, TestIfImage
|
from InvenTree.helpers import DownloadFile, str2bool, TestIfImage, TestIfImageURL
|
||||||
|
|
||||||
|
|
||||||
class PartIndex(ListView):
|
class PartIndex(ListView):
|
||||||
@ -270,7 +273,20 @@ class PartImage(AjaxUpdateView):
|
|||||||
|
|
||||||
|
|
||||||
class UploadPartImage(AjaxView):
|
class UploadPartImage(AjaxView):
|
||||||
""" View for uploading a Part image """
|
""" View for uploading a Part image via AJAX request.
|
||||||
|
e.g. via a "drag and drop" event.
|
||||||
|
|
||||||
|
There are two ways to upload a file:
|
||||||
|
|
||||||
|
1. Attach an image file as request.FILES['image_file']
|
||||||
|
- Image is validated saved
|
||||||
|
- Part object is saved
|
||||||
|
2. Attach an iamge URL as request.POST['image_url']
|
||||||
|
NOT YET IMPLEMENTED
|
||||||
|
- Image URL is valiated
|
||||||
|
- Image is downloaded and validated
|
||||||
|
- Part object is saved
|
||||||
|
"""
|
||||||
|
|
||||||
model = Part
|
model = Part
|
||||||
|
|
||||||
@ -285,18 +301,63 @@ class UploadPartImage(AjaxView):
|
|||||||
response['error'] = 'Part not found'
|
response['error'] = 'Part not found'
|
||||||
return JsonResponse(error_dict, status=404)
|
return JsonResponse(error_dict, status=404)
|
||||||
|
|
||||||
uploaded_file = request.FILES['file']
|
# Direct image upload
|
||||||
|
if 'image_file' in request.FILES:
|
||||||
|
image = request.FILES['image_file']
|
||||||
|
|
||||||
if TestIfImage(uploaded_file):
|
if TestIfImage(image):
|
||||||
part.image = uploaded_file
|
part.image = image
|
||||||
part.clean()
|
part.clean()
|
||||||
part.save()
|
part.save()
|
||||||
|
|
||||||
response['success'] = 'File was uploaded successfully'
|
response['success'] = 'File was uploaded successfully'
|
||||||
else:
|
status = 200
|
||||||
response['error'] = 'Not a valid image file'
|
else:
|
||||||
status = 400
|
response['error'] = 'Not a valid image file'
|
||||||
|
status = 400
|
||||||
|
|
||||||
|
return JsonResponse(response, status=status)
|
||||||
|
|
||||||
|
elif 'image_url' in request.POST:
|
||||||
|
image_url = request.POST['image_url']
|
||||||
|
|
||||||
|
validator = URLValidator()
|
||||||
|
|
||||||
|
try:
|
||||||
|
validator(image_url)
|
||||||
|
except ValidationError:
|
||||||
|
response['error'] = 'Invalid image URL'
|
||||||
|
response['url'] = image_url
|
||||||
|
|
||||||
|
return JsonResponse(response, status=400)
|
||||||
|
|
||||||
|
# Test the the URL at least looks like an image
|
||||||
|
if not TestIfImageURL(image_url):
|
||||||
|
response['error'] = 'Invalid image URL'
|
||||||
|
return JsonResponse(response, status=400)
|
||||||
|
|
||||||
|
response['error'] = 'Cannot download cross-site images (yet)'
|
||||||
|
response['url'] = image_url
|
||||||
|
response['apology'] = 'deepest'
|
||||||
|
|
||||||
|
return JsonResponse(response, status=400)
|
||||||
|
|
||||||
|
# TODO - Attempt to download the image file here
|
||||||
|
|
||||||
|
"""
|
||||||
|
head = requests.head(url, stream=True, headers={'User-agent': 'Mozilla/5.0'})
|
||||||
|
|
||||||
|
if head.headers['Content-Length'] < SOME_MAX_LENGTH:
|
||||||
|
|
||||||
|
image = requests.get(url, stream=True, headers={'User-agent': 'Mozilla/5.0'})
|
||||||
|
|
||||||
|
file = io.BytesIO(image.raw.read())
|
||||||
|
|
||||||
|
- Save the file?
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Default response
|
||||||
return JsonResponse(response, status=status)
|
return JsonResponse(response, status=status)
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ function inventreeGet(url, filters={}, options={}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function inventreeFileUpload(url, file, data={}, options={}) {
|
function inventreeFormDataUpload(url, data, options={}) {
|
||||||
/* Upload a file via AJAX using the FormData approach.
|
/* Upload via AJAX using the FormData approach.
|
||||||
*
|
*
|
||||||
* Note that the following AJAX parameters are required for FormData upload
|
* Note that the following AJAX parameters are required for FormData upload
|
||||||
*
|
*
|
||||||
@ -53,10 +53,6 @@ function inventreeFileUpload(url, file, data={}, options={}) {
|
|||||||
|
|
||||||
// CSRF cookie token
|
// CSRF cookie token
|
||||||
var csrftoken = getCookie('csrftoken');
|
var csrftoken = getCookie('csrftoken');
|
||||||
|
|
||||||
var data = new FormData();
|
|
||||||
|
|
||||||
data.append('file', file);
|
|
||||||
|
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
beforeSend: function(xhr, settings) {
|
beforeSend: function(xhr, settings) {
|
||||||
@ -68,14 +64,13 @@ function inventreeFileUpload(url, file, data={}, options={}) {
|
|||||||
processData: false,
|
processData: false,
|
||||||
contentType: false,
|
contentType: false,
|
||||||
success: function(data, status, xhr) {
|
success: function(data, status, xhr) {
|
||||||
console.log('Uploaded file - ' + file.name);
|
console.log('Form data upload success');
|
||||||
|
|
||||||
if (options.success) {
|
if (options.success) {
|
||||||
options.success(data, status, xhr);
|
options.success(data, status, xhr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
console.log('Error uploading file: ' + status);
|
console.log('Form data upload failure: ' + status);
|
||||||
|
|
||||||
if (options.error) {
|
if (options.error) {
|
||||||
options.error(xhr, status, error);
|
options.error(xhr, status, error);
|
||||||
|
@ -58,5 +58,9 @@ function getImageUrlFromTransfer(transfer) {
|
|||||||
/* Extract external image URL from a drag-and-dropped image
|
/* Extract external image URL from a drag-and-dropped image
|
||||||
*/
|
*/
|
||||||
|
|
||||||
console.log(transfer.getData('text/html').match(/src\s*=\s*"(.+?)"/)[1]);
|
var url = transfer.getData('text/html').match(/src\s*=\s*"(.+?)"/)[1];
|
||||||
|
|
||||||
|
console.log('Image URL: ' + url);
|
||||||
|
|
||||||
|
return url;
|
||||||
}
|
}
|
Reference in New Issue
Block a user