diff --git a/InvenTree/company/forms.py b/InvenTree/company/forms.py
index 0ad95c3e8c..67ac402ba7 100644
--- a/InvenTree/company/forms.py
+++ b/InvenTree/company/forms.py
@@ -66,6 +66,24 @@ class CompanyImageForm(HelperForm):
]
+class CompanyImageDownloadForm(HelperForm):
+ """
+ Form for downloading an image from a URL
+ """
+
+ url = django.forms.URLField(
+ label=_('URL'),
+ help_text=_('Image URL'),
+ required=True
+ )
+
+ class Meta:
+ model = Company
+ fields = [
+ 'url',
+ ]
+
+
class EditSupplierPartForm(HelperForm):
""" Form for editing a SupplierPart object """
diff --git a/InvenTree/company/templates/company/company_base.html b/InvenTree/company/templates/company/company_base.html
index eb4ced9965..9331e5d895 100644
--- a/InvenTree/company/templates/company/company_base.html
+++ b/InvenTree/company/templates/company/company_base.html
@@ -2,14 +2,19 @@
{% load static %}
{% load i18n %}
+{% load inventree_extras %}
+
{% block page_title %}
InvenTree | {% trans "Company" %} - {{ company.name }}
{% endblock %}
+
{% block thumbnail %}
+{% settings_value "INVENTREE_DOWNLOAD_FROM_URL" as allow_download %}
+
-
+ {% if allow_download %}
+
+ {% endif %}
@@ -141,7 +149,7 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
);
{% if company.image %}
- $('#company-thumb').click(function() {
+ $('#company-image').click(function() {
showModalImage('{{ company.image.url }}');
});
{% endif %}
@@ -155,4 +163,17 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
);
});
+ {% settings_value "INVENTREE_DOWNLOAD_FROM_URL" as allow_download %}
+
+ {% if allow_download %}
+ $('#company-image-url').click(function() {
+ launchModalForm(
+ '{% url "company-image-download" company.id %}',
+ {
+ reload: true,
+ }
+ )
+ });
+ {% endif %}
+
{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/company/urls.py b/InvenTree/company/urls.py
index f5fbeede47..b5ad06019b 100644
--- a/InvenTree/company/urls.py
+++ b/InvenTree/company/urls.py
@@ -21,6 +21,7 @@ company_detail_urls = [
url(r'^notes/', views.CompanyNotes.as_view(), name='company-notes'),
url(r'^thumbnail/', views.CompanyImage.as_view(), name='company-image'),
+ url(r'^thumb-download/', views.CompanyImageDownloadFromURL.as_view(), name='company-image-download'),
# Any other URL
url(r'^.*$', views.CompanyDetail.as_view(), name='company-detail'),
diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py
index 59e9b23904..42457d6101 100644
--- a/InvenTree/company/views.py
+++ b/InvenTree/company/views.py
@@ -11,9 +11,14 @@ from django.views.generic import DetailView, ListView, UpdateView
from django.urls import reverse
from django.forms import HiddenInput
+from django.core.files.base import ContentFile
from moneyed import CURRENCIES
+from PIL import Image
+import requests
+import io
+
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
from InvenTree.helpers import str2bool
from InvenTree.views import InvenTreeRoleMixin
@@ -28,6 +33,7 @@ from .forms import EditCompanyForm
from .forms import CompanyImageForm
from .forms import EditSupplierPartForm
from .forms import EditPriceBreakForm
+from .forms import CompanyImageDownloadForm
import common.models
import common.settings
@@ -150,6 +156,84 @@ class CompanyDetail(DetailView):
return ctx
+class CompanyImageDownloadFromURL(AjaxUpdateView):
+ """
+ View for downloading an image from a provided URL
+ """
+
+ model = Company
+ ajax_template_name = 'image_download.html'
+ form_class = CompanyImageDownloadForm
+ ajax_form_title = _('Download Image')
+
+ def validate(self, company, form):
+ """
+ Validate that the image data are correct
+ """
+ # First ensure that the normal validation routines pass
+ if not form.is_valid():
+ return
+
+ # We can now extract a valid URL from the form data
+ url = form.cleaned_data.get('url', None)
+
+ # Download the file
+ response = requests.get(url, stream=True)
+
+ # Look at response header, reject if too large
+ content_length = response.headers.get('Content-Length', '0')
+
+ try:
+ content_length = int(content_length)
+ except (ValueError):
+ # If we cannot extract meaningful length, just assume it's "small enough"
+ content_length = 0
+
+ # TODO: Factor this out into a configurable setting
+ MAX_IMG_LENGTH = 10 * 1024 * 1024
+
+ if content_length > MAX_IMG_LENGTH:
+ form.add_error('url', _('Image size exceeds maximum allowable size for download'))
+ return
+
+ self.response = response
+
+ # Check for valid response code
+ if not response.status_code == 200:
+ form.add_error('url', f"{_('Invalid response')}: {response.status_code}")
+ return
+
+ response.raw.decode_content = True
+
+ try:
+ self.image = Image.open(response.raw).convert()
+ self.image.verify()
+ except:
+ form.add_error('url', _("Supplied URL is not a valid image file"))
+ return
+
+ def save(self, company, form, **kwargs):
+ """
+ Save the downloaded image to the company
+ """
+ fmt = self.image.format
+
+ if not fmt:
+ fmt = 'PNG'
+
+ buffer = io.BytesIO()
+
+ self.image.save(buffer, format=fmt)
+
+ # Construct a simplified name for the image
+ filename = f"company_{company.pk}_image.{fmt.lower()}"
+
+ company.image.save(
+ filename,
+ ContentFile(buffer.getvalue()),
+ )
+
+
class CompanyImage(AjaxUpdateView):
""" View for uploading an image for the Company """
model = Company
diff --git a/InvenTree/part/templates/part/part_thumb.html b/InvenTree/part/templates/part/part_thumb.html
index 9e1de6fc26..dd192843dc 100644
--- a/InvenTree/part/templates/part/part_thumb.html
+++ b/InvenTree/part/templates/part/part_thumb.html
@@ -7,7 +7,7 @@