diff --git a/apps/member/views.py b/apps/member/views.py index 25dcbf8a7cf9444dc660860df033506c3d71e05c..9b185cca5dcc06246fb7eb28b55e7b95d2cf9613 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -16,6 +16,7 @@ from django.core.exceptions import ValidationError from django_tables2.views import SingleTableView from rest_framework.authtoken.models import Token from dal import autocomplete +from PIL import Image from note.models import Alias, NoteUser from note.models.transactions import Transaction @@ -229,8 +230,16 @@ class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView): return self.form_invalid(form) def form_valid(self,form): - print(form) - self.object.note.display_image = form.cleaned_data.get("image","pic/default.png") + image_file_field = form.cleaned_data['image'] + x = form.cleaned_data['x'] + y = form.cleaned_data['y'] + w = form.cleaned_data['width'] + h = form.cleaned_data['height'] + with Image.open(image_file_field.name) as image: + image = image.crop((x, y, w+x, h+y)) + image.thumbnail((256, 256), Image.ANTIALIAS) + image.save(image_file_field.name,format=image.format) + self.object.note.display_image = image_file_field self.object.note.save() return super().form_valid(form) diff --git a/apps/note/forms.py b/apps/note/forms.py index af6c97bc68e06d976dd5e62d264f64d757c9b9b0..819ed97a45aa2654646c666c7d767f318a951a10 100644 --- a/apps/note/forms.py +++ b/apps/note/forms.py @@ -3,8 +3,11 @@ from dal import autocomplete from django import forms +from django.conf import settings from django.utils.translation import gettext_lazy as _ +import os + from crispy_forms.helper import FormHelper from crispy_forms.bootstrap import Div from crispy_forms.layout import Layout, HTML @@ -27,8 +30,12 @@ class ImageForm(forms.Form): image = forms.ImageField(required = False, label=_('select an image'), help_text=_('Maximal size: 2MB')) + x = forms.FloatField(widget=forms.HiddenInput()) + y = forms.FloatField(widget=forms.HiddenInput()) + width = forms.FloatField(widget=forms.HiddenInput()) + height = forms.FloatField(widget=forms.HiddenInput()) - + class TransactionTemplateForm(forms.ModelForm): class Meta: model = TransactionTemplate diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index fad5e6f78509479f0db2e2809209f9f9f4f5d716..63a07c033246c1d56d10d6e8d074d3763d84fb53 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -191,3 +191,7 @@ ALIAS_VALIDATOR_REGEX = r'' MEDIA_ROOT=os.path.join(BASE_DIR,"media") MEDIA_URL='/media/' + +# Profile Picture Settings +PIC_WIDTH = 200 +PIC_RATIO = 1 diff --git a/note_kfet/urls.py b/note_kfet/urls.py index 385ce991dcf2c1345d728a98d8acb21af2d12a8c..0a79e414616a672c7c9dc8aaf0f06cf0974123ab 100644 --- a/note_kfet/urls.py +++ b/note_kfet/urls.py @@ -26,3 +26,4 @@ urlpatterns = [ ] urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT) +urlpatterns += static(settings.STATIC_URL,document_root=settings.STATIC_ROOT) diff --git a/templates/member/profile_picture_update.html b/templates/member/profile_picture_update.html index a5de13dcc66407cea0ede64c0a7503d32ae53c07..f0e308347e0b695ec8c2eacbed57cd5206777e3b 100644 --- a/templates/member/profile_picture_update.html +++ b/templates/member/profile_picture_update.html @@ -2,62 +2,94 @@ {% load i18n static pretty_money django_tables2 crispy_forms_tags %} {% block profile_content %} -<div class="row"> -<div class="col-sm-4 text-center"> - <img class="img-thumbnail" alt="" src="{{user_object.note.display_image.url }}"/> - <p class=""> {% trans "current image" %} </p> -</div> -<div class="col-sm-4 justify-content-center"> - <form class=" text-center form my-2" action="" enctype="multipart/form-data" method="post"> - {% csrf_token %} - {{ form |crispy }} - <button class="btn btn-primary mx-2" type="submit"> - {% trans "Change image" %} - </button> - </form> -</div> -<div class="col-sm-4"> - <div id="image-holder"></div> -</div> +<form method="post" enctype="multipart/form-data" id="formUpload"> + {% csrf_token %} + {{ form |crispy }} +</form> +<!-- MODAL TO CROP THE IMAGE --> +<div class="modal fade" id="modalCrop"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-body"> + <img src="" id="modal-image" style="max-width: 100%;"> + </div> + <div class="modal-footer"> + <div class="btn-group pull-left" role="group"> + <button type="button" class="btn btn-default" id="js-zoom-in"> + <span class="glyphicon glyphicon-zoom-in"></span> + </button> + <button type="button" class="btn btn-default js-zoom-out"> + <span class="glyphicon glyphicon-zoom-out"></span> + </button> + </div> + <button type="button" class="btn btn-default" data-dismiss="modal">Nevermind</button> + <button type="button" class="btn btn-primary js-crop-and-upload">Crop and upload</button> + </div> + </div> + </div> </div> {% endblock %} -{% block extrajavascript%} -<script> -// https://codepedia.info/upload-image-using-jquery-ajax-asp-net-c-sharp/ - $("#id_image").on('change', function () { - - //Get count of selected files - var countFiles = $(this)[0].files.length; - - var imgPath = $(this)[0].value; - var extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase(); - var image_holder = $("#image-holder"); - image_holder.empty(); - - if (extn == "gif" || extn == "png" || extn == "jpg" || extn == "jpeg") { - if (typeof (FileReader) != "undefined") { +{% block extracss %} + <link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.6/cropper.min.css" rel="stylesheet"> +{% endblock %} - //loop for each file selected for uploaded. - for (var i = 0; i < countFiles; i++) { +{% block extrajavascript%} + <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.6/cropper.min.js"></script> + <script src="https://cdn.jsdelivr.net/npm/jquery-cropper@1.0.1/dist/jquery-cropper.min.js"></script> + <script> + $(function () { + /* SCRIPT TO OPEN THE MODAL WITH THE PREVIEW */ + $("#id_image").change(function (e) { + if (this.files && this.files[0]) { var reader = new FileReader(); reader.onload = function (e) { - $("<img />", { - "src": e.target.result, - "class": "img-thumbnail" - }).appendTo(image_holder); + $("#modal-image").attr("src", e.target.result); + $("#modalCrop").modal("show"); } - - image_holder.show(); - reader.readAsDataURL($(this)[0].files[i]); + reader.readAsDataURL(this.files[0]); } + }); + + /* SCRIPTS TO HANDLE THE CROPPER BOX */ + var $image = $("#modal-image"); + var cropBoxData; + var canvasData; + $("#modalCrop").on("shown.bs.modal", function () { + $image.cropper({ + viewMode: 1, + aspectRatio: 1/1, + minCropBoxWidth: 200, + minCropBoxHeight: 200, + ready: function () { + $image.cropper("setCanvasData", canvasData); + $image.cropper("setCropBoxData", cropBoxData); + } + }); + }).on("hidden.bs.modal", function () { + cropBoxData = $image.cropper("getCropBoxData"); + canvasData = $image.cropper("getCanvasData"); + $image.cropper("destroy"); + }); + + $(".js-zoom-in").click(function () { + $image.cropper("zoom", 0.1); + }); + + $(".js-zoom-out").click(function () { + $image.cropper("zoom", -0.1); + }); + + /* SCRIPT TO COLLECT THE DATA AND POST TO THE SERVER */ + $(".js-crop-and-upload").click(function () { + var cropData = $image.cropper("getData"); + $("#id_x").val(cropData["x"]); + $("#id_y").val(cropData["y"]); + $("#id_height").val(cropData["height"]); + $("#id_width").val(cropData["width"]); + $("#formUpload").submit(); + }); - } else { - alert("{% trans 'This browser does not support FileReader.' %}"); - } - } else { - alert("{% trans 'Please select only images' %}"); - } - }); -</script> + }); + </script> {% endblock %}