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 @@
-