Skip to content

Commit

Permalink
Improvements - Admin panel - Product - Allow uploading multiple pictures
Browse files Browse the repository at this point in the history
  • Loading branch information
KrzysztofPajak committed Aug 9, 2021
1 parent b23c732 commit e214741
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</div>
<vc:admin-widget widget-zone="product_details_pictures_bottom" additional-data="Model" />
</div>
<script>
<script>
$(document).ready(function () {
$("#productpictures-grid").kendoGrid({
dataSource: {
Expand Down Expand Up @@ -109,93 +109,37 @@
}]
});
});
</script>
</script>

<p>
<strong>@Loc["Admin.Catalog.Products.Pictures.AddNew"]</strong>
</p>
<script>
$(document).ready(function () {
$('#addProductPicture').click(function () {
var pictureId = $("#@Html.IdFor(model => model.AddPictureModel.PictureId)").val();
var overrideAltAttribute = $("#@Html.IdFor(model => model.AddPictureModel.OverrideAltAttribute)").val();
var overrideTitleAttribute = $("#@Html.IdFor(model => model.AddPictureModel.OverrideTitleAttribute)").val();
var displayOrder = $("#@Html.IdFor(model => model.AddPictureModel.DisplayOrder)").val();
if (pictureId == 0) {
alert('Upload picture first');
return;
}
$('#addProductPicture').attr('disabled', true);
var postData = {
pictureId: pictureId,
displayOrder: displayOrder,
overrideAltAttribute: overrideAltAttribute,
overrideTitleAttribute: overrideTitleAttribute,
productId: '@Model.Id'
};
addAntiForgeryToken(postData);
$.ajax({
cache: false,
type: "POST",
url: "@(Url.Action("ProductPictureAdd", "Product", new { area = Constants.AreaAdmin }))",
data: postData,
success: function(data) {
var grid = $("#productpictures-grid");
grid.data('kendoGrid').dataSource.read();
$('#addProductPicture').attr('disabled', false);
},
error: function (xhr) {
alert(xhr.responseJSON.Errors);
$('#addProductPicture').attr('disabled', false);
}
});
$('#addProductPicture').attr('disabled', false);
});
});
</script>
<div class="form-horizontal">
<div class="form-body">
<div class="form-group">
@{
ViewData["Reference"] = "Product";
ViewData["ObjectId"] = Model.Id;
ViewData["Endpoint"] = this.Url.Action("ProductPictureAdd", "Product", new { area = Constants.AreaAdmin });
ViewData["Click"] = "btnRefreshProductPictures";
}
<admin-label asp-for="AddPictureModel.PictureId" />
<div class="col-md-9 col-sm-9">
<admin-input asp-for="AddPictureModel.PictureId" />
<span asp-validation-for="AddPictureModel.PictureId"></span>
</div>
</div>
<div class="form-group">
<admin-label asp-for="AddPictureModel.OverrideAltAttribute" />
<div class="col-md-9 col-sm-9">
<admin-input asp-for="AddPictureModel.OverrideAltAttribute" />
<span asp-validation-for="AddPictureModel.OverrideAltAttribute"></span>
</div>
</div>
<div class="form-group">
<admin-label asp-for="AddPictureModel.OverrideTitleAttribute" />
<div class="col-md-9 col-sm-9">
<admin-input asp-for="AddPictureModel.OverrideTitleAttribute" />
<span asp-validation-for="AddPictureModel.OverrideTitleAttribute"></span>
</div>
</div>
<div class="form-group">
<admin-label asp-for="AddPictureModel.DisplayOrder" />
<div class="col-md-9 col-sm-9">
<admin-input asp-for="AddPictureModel.DisplayOrder" />
<span asp-validation-for="AddPictureModel.DisplayOrder"></span>
</div>
</div>
<div class="form-actions">
<div class="offset-md-3 col-md-9 col-sm-9">
<input type="button" id="addProductPicture" class="k-button" value="@Loc["Admin.Catalog.Products.Pictures.AddButton"]" />
</div>
<input type="submit" id="btnRefreshProductPictures" style="display: none" />
<script>
$(document).ready(function () {
$('#btnRefreshProductPictures').click(function () {
//refresh grid
var grid = $("#productpictures-grid").data('kendoGrid');
grid.dataSource.read();
//return false to don't reload a page
return false;
});
});
</script>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
@model string
@inject Grand.Business.Storage.Interfaces.IPictureService pictureService
@{
var random = CommonHelper.GenerateRandomInteger();
var clientId = "multipicture" + random;
var picture = await pictureService.GetPictureById(Model);

var reference = ViewData["Reference"];
var objectId = ViewData["ObjectId"];
var endpoint = ViewData["Endpoint"];
var click = ViewData["Click"];
}
<link rel="stylesheet" type="text/css" asp-src="/_content/Grand.Web.Admin/administration/fineuploader/fineuploader-4.2.2.min.css" />
<script asp-location="Footer" asp-src="/_content/Grand.Web.Admin/administration/fineuploader/jquery.fineuploader-4.2.2.min.js"></script>

<div id="@clientId"></div>

<div id="@(clientId + "image")">
</div>
<script type="text/template" id="@(clientId)-qq-template">
<div class="qq-uploader-selector qq-uploader">
<div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone>
<span>@Loc["Common.FileUploader.DropFiles"]</span>
</div>
<div class="qq-upload-button-selector qq-upload-button btn yellow" style="margin: 10px 0px 10px 0px;">
<div>@Loc["Common.FileUploader.Upload"]</div>
</div>
<span class="qq-drop-processing-selector qq-drop-processing">
<span>@Loc["Common.FileUploader.Processing"]</span>
<span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
</span>
<ul class="qq-upload-list-selector qq-upload-list">
<li>
<div class="qq-progress-bar-container-selector">
<div class="qq-progress-bar-selector qq-progress-bar"></div>
</div>
<span class="qq-upload-spinner-selector qq-upload-spinner"></span>
<span class="qq-edit-filename-icon-selector qq-edit-filename-icon"></span>
<span class="qq-upload-file-selector qq-upload-file"></span>
<input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text">
<span class="qq-upload-size-selector qq-upload-size"></span>
<a class="qq-upload-cancel-selector qq-upload-cancel" href="#">@Loc["Common.FileUploader.Cancel"]</a>
<a class="qq-upload-retry-selector qq-upload-retry" href="#">@Loc["Common.FileUploader.Retry"]</a>
<a class="qq-upload-delete-selector qq-upload-delete" href="#">@Loc["Common.FileUploader.Delete"]</a>
<span class="qq-upload-status-text-selector qq-upload-status-text"></span>
<span class="qq-upload-status-message"></span>
</li>
</ul>
</div>
</script>

<script>
$(document).ready(function() {
$("#@(clientId)").fineUploader({
request: {
endpoint: '@endpoint',
params: {
Reference: '@reference',
ObjectId: '@objectId'
}
},
template: "@(clientId)-qq-template",
multiple: true,
validation: {
itemLimit: 10,
acceptFiles: 'image/*',
}
}).on("complete", function(event, id, name, responseJSON, xhr) {
if (responseJSON.success) {
$("#@(clientId + "image")").append("<img src='" + responseJSON.data[0].Item1 + "'/>");
$("#@click").click();
}
else {
$('.qq-upload-status-message').text(responseJSON.message);
}
});
});
</script>
124 changes: 114 additions & 10 deletions src/Web/Grand.Web.Admin/Controllers/ProductController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
using Grand.Business.Common.Interfaces.Stores;
using Grand.Business.Common.Services.Security;
using Grand.Business.Customers.Interfaces;
using Grand.Business.Storage.Extensions;
using Grand.Business.Storage.Interfaces;
using Grand.Business.System.Interfaces.ExportImport;
using Grand.Domain.Catalog;
using Grand.Domain.Common;
using Grand.Domain.Media;
using Grand.Infrastructure;
using Grand.Web.Admin.Extensions;
using Grand.Web.Admin.Interfaces;
Expand All @@ -24,6 +28,7 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

Expand Down Expand Up @@ -1194,28 +1199,127 @@ public async Task<IActionResult> AssociatedProductAddPopup(ProductModel.AddAssoc
#endregion

#region Product pictures
public async Task<IActionResult> ProductPictureAdd(string pictureId, int displayOrder,
string overrideAltAttribute, string overrideTitleAttribute,
string productId)

[HttpPost]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> ProductPictureAdd(Reference reference, string objectId,
[FromServices] IPictureService pictureService,
[FromServices] MediaSettings mediaSettings)
{
if (string.IsNullOrEmpty(pictureId))
throw new ArgumentException();
if (!await _permissionService.Authorize(PermissionSystemName.Pictures))
return Json(new
{
success = false,
message = "Access denied - picture permissions",
});

var product = await _productService.GetProductById(productId);
if (reference != Reference.Product || string.IsNullOrEmpty(objectId))
return Json(new
{
success = false,
message = "Please save form before upload new pictures",
});

var form = await HttpContext.Request.ReadFormAsync();
var httpPostedFiles = form.Files.ToList();
if (!httpPostedFiles.Any())
{
return Json(new
{
success = false,
message = "No files uploaded",
});
}

var product = await _productService.GetProductById(objectId);

//a vendor should have access only to his products
if (_workContext.CurrentVendor != null && product.VendorId != _workContext.CurrentVendor.Id && !await _groupService.IsStaff(_workContext.CurrentCustomer))
return Json(new { Result = false });
return Json(new
{
success = false,
message = "Access denied - vendor permissions",
});


if (await _groupService.IsStaff(_workContext.CurrentCustomer))
if (!product.AccessToEntityByStore(_workContext.CurrentCustomer.StaffStoreId))
return Json(new { Result = false });
return Json(new
{
success = false,
message = "Access denied - staff permissions",
});

await _productViewModelService.InsertProductPicture(product, pictureId, displayOrder, overrideAltAttribute, overrideTitleAttribute);
var values = new List<(string pictureUrl, string pictureId)>();

return Json(new { Result = true });
foreach (var file in httpPostedFiles)
{
var qqFileNameParameter = "qqfilename";
var fileName = file.FileName;
if (String.IsNullOrEmpty(fileName) && form.ContainsKey(qqFileNameParameter))
fileName = form[qqFileNameParameter].ToString();

fileName = Path.GetFileName(fileName);

var contentType = file.ContentType;
var fileExtension = Path.GetExtension(fileName);
if (!string.IsNullOrEmpty(fileExtension))
fileExtension = fileExtension.ToLowerInvariant();

if (string.IsNullOrEmpty(contentType))
{
contentType = GetContentType(fileExtension);
}

if (GetAllowedFileTypes(mediaSettings).Contains(fileExtension))
{
var fileBinary = file.GetDownloadBits();
//insert picture
var picture = await pictureService.InsertPicture(fileBinary, contentType, null, reference: reference, objectId: objectId);
var pictureUrl = await pictureService.GetPictureUrl(picture);

values.Add((pictureUrl, picture.Id));
//assign picture to the product
await _productViewModelService.InsertProductPicture(product, picture, 0, "", "");
}
}

return Json(new { success = true, data = values });
}

protected virtual IList<string> GetAllowedFileTypes(MediaSettings mediaSettings)
{
if (string.IsNullOrEmpty(mediaSettings.AllowedFileTypes))
return new List<string> { ".gif", ".jpg", ".jpeg", ".png", ".bmp", ".webp" };
else
return mediaSettings.AllowedFileTypes.Split(',');
}
protected virtual string GetContentType(string fileExtension)
{
switch (fileExtension)
{
case ".bmp":
return "image/bmp";
case ".gif":
return "image/gif";
case ".jpeg":
case ".jpg":
case ".jpe":
case ".jfif":
case ".pjpeg":
case ".pjp":
return "image/jpeg";
case ".png":
return "image/png";
case ".tiff":
case ".tif":
return "image/tiff";
default:
return "";
}
}


[PermissionAuthorizeAction(PermissionActionName.Preview)]
[HttpPost]
public async Task<IActionResult> ProductPictureList(DataSourceRequest command, string productId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Grand.Domain.Catalog;
using Grand.Domain.Media;
using Grand.Web.Admin.Models.Catalog;
using System.Collections.Generic;
using System.Threading.Tasks;
Expand Down Expand Up @@ -99,7 +100,7 @@ public interface IProductViewModelService

//Pictures
Task<IList<ProductModel.ProductPictureModel>> PrepareProductPictureModel(Product product);
Task InsertProductPicture(Product product, string pictureId, int displayOrder, string overrideAltAttribute, string overrideTitleAttribute);
Task InsertProductPicture(Product product, Picture picture, int displayOrder, string overrideAltAttribute, string overrideTitleAttribute);
Task UpdateProductPicture(ProductModel.ProductPictureModel model);
Task DeleteProductPicture(ProductModel.ProductPictureModel model);

Expand Down
2 changes: 1 addition & 1 deletion src/Web/Grand.Web.Admin/Models/Catalog/ProductModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ public partial class ProductPictureModel : BaseEntityModel
{
public string ProductId { get; set; }

[UIHint("Picture")]
[UIHint("MultiPicture")]
[GrandResourceDisplayName("Admin.Catalog.Products.Pictures.Fields.Picture")]
public string PictureId { get; set; }

Expand Down
Loading

0 comments on commit e214741

Please sign in to comment.