wooncherk wrote:Basically you cant because all the Actions are not virtual methods. You can, at best, override those helper methods. :D
I see. So I'd have to update the source to include "virtual" in the definition but that would defeat the point of a plug-in. It'd be great to have the ability to override all the core functions with plug-ins.
This is what I have for my controller so far. My next step is to setup the configuration so that it doesn't require hard coding the Amazon Key and Secret.
The myGetDownload is pretty much the same as the original GetDownload but it calls the signDownload function to sign any S3 urls.
using System;
using System.Web.Mvc;
using Nop.Core;
using Nop.Core.Domain.Customers;
using Nop.Services.Catalog;
using Nop.Services.Media;
using Nop.Services.Orders;
using Nop.Web.Controllers;
using Amazon.S3;
using Amazon.S3.Model;
namespace Nop.Plugin.AmazonAWS.S3Download.Controllers
{
public class myDownloadController : BaseNopController
{
private readonly IDownloadService _downloadService;
private readonly IProductService _productService;
private readonly IOrderService _orderService;
private readonly IWorkContext _workContext;
private readonly CustomerSettings _customerSettings;
public myDownloadController(IDownloadService downloadService, IProductService productService,
IOrderService orderService, IWorkContext workContext, CustomerSettings customerSettings)
{
this._downloadService = downloadService;
this._productService = productService;
this._orderService = orderService;
this._workContext = workContext;
this._customerSettings = customerSettings;
}
public ActionResult myGetDownload(Guid opvId, bool agree = false)
{
var orderProductVariant = _orderService.GetOrderProductVariantByGuid(opvId);
if (orderProductVariant == null)
return RedirectToRoute("HomePage");
var order = orderProductVariant.Order;
var productVariant = orderProductVariant.ProductVariant;
if (!_downloadService.IsDownloadAllowed(orderProductVariant))
return Content("Downloads are not allowed");
if (_customerSettings.DownloadableProductsValidateUser)
{
if (_workContext.CurrentCustomer == null)
return new HttpUnauthorizedResult();
if (order.CustomerId != _workContext.CurrentCustomer.Id)
return Content("This is not your order");
}
var download = _downloadService.GetDownloadById(productVariant.DownloadId);
if (download == null)
return Content("Download is not available any more.");
if (productVariant.HasUserAgreement)
{
if (!agree)
return RedirectToRoute("DownloadUserAgreement", new { opvid = opvId });
}
if (!productVariant.UnlimitedDownloads && orderProductVariant.DownloadCount >= productVariant.MaxNumberOfDownloads)
return Content(string.Format("You have reached maximum number of downloads {0}", productVariant.MaxNumberOfDownloads));
if (download.UseDownloadUrl)
{
//increase download
orderProductVariant.DownloadCount++;
_orderService.UpdateOrder(order);
//return result
return new RedirectResult(signDownload(download.DownloadUrl));
}
else
{
if (download.DownloadBinary == null)
return Content("Download data is not available any more.");
//increase download
orderProductVariant.DownloadCount++;
_orderService.UpdateOrder(order);
//return result
string fileName = !String.IsNullOrWhiteSpace(download.Filename) ? download.Filename : productVariant.Id.ToString();
string contentType = !String.IsNullOrWhiteSpace(download.ContentType) ? download.ContentType : "application/octet-stream";
return new FileContentResult(download.DownloadBinary, contentType) { FileDownloadName = fileName + download.Extension };
}
}
private string signDownload(string url)
{
string urlDomainMatch = ".s3.amazonaws.com";
string awsS3Key;
string awsS3Secret;
System.Uri urlParts = new System.Uri(url);
awsS3Key = @"Your Amazon S3 Key";
awsS3Secret = @"Your Amazon S3 Secret";
/* is this an Amazon S3 url? */
if (urlParts.Host.ToLower().Contains(urlDomainMatch))
{
//We've got a match! This is an amazon s3 link that needs to be signed. Let's begin...
//Parse the bucket name. It's the subdomain of the host.
string urlBucket = urlParts.Host.Split('.')[0];
//Parse the path of the url.
//UrlDecoding because Amazon SDK doesn't want an encoded path when looking up the key.
//Ignore the leading slash with the substring.
string urlPath = Server.UrlDecode(urlParts.PathAndQuery.Substring(1));
//Parse the filename from the url by looking for the last forward slash and getting everything after it.
string urlFilename = urlPath.Substring(urlPath.LastIndexOf('/') + 1);
//Do we have all the settings we need?
if (!string.IsNullOrEmpty(awsS3Key) &&
!string.IsNullOrEmpty(awsS3Secret) &&
!string.IsNullOrEmpty(urlBucket) &&
!string.IsNullOrEmpty(urlFilename))
{
//make sure the file actually exists on S3
AmazonS3 client = (AmazonS3Client)Amazon.AWSClientFactory.CreateAmazonS3Client(awsS3Key, awsS3Secret);
GetObjectRequest reqS3 = new GetObjectRequest().WithBucketName(urlBucket).WithKey(urlPath);
try
{
S3Response respS3 = client.GetObject(reqS3);
//above code will fail if the file is missing or there's another error and skip the code below this.
//Let's sign the url and redirect the customer to the file on Amazon.
//Give it an expiration of 60 seconds (playing it safe) to account for a potential difference in local server and amazon server's time.
//It'll also account for any latency in the redirect time.
//We're expiring the URL so the customer will have to return to the store's download url if they want another download.
//This allows the store to regulate the number of downloads allowed.
Amazon.S3.Model.GetPreSignedUrlRequest signedUrl;
signedUrl = new Amazon.S3.Model.GetPreSignedUrlRequest();
signedUrl.WithBucketName(urlBucket);
signedUrl.WithExpires(DateTime.UtcNow.AddSeconds(60));
signedUrl.WithKey(urlPath);
//Add the content-disposition header value as attachment so it opens a download/save as window.
ResponseHeaderOverrides respOverride = new ResponseHeaderOverrides();
respOverride.ContentDisposition = string.Format("attachment;filename=\"{0}\"", urlFilename);
signedUrl.WithResponseHeaderOverrides(respOverride);
return client.GetPreSignedURL(signedUrl);
}
catch (AmazonS3Exception amazonS3Exception)
{
//Possible place to account for the "NotFound" error but, for now, we're going to throw all errors.
throw amazonS3Exception;
//if (amazonS3Exception.StatusCode.ToString() == "NotFound")
//{
// //No found logic here.
//}
//else
//{
// throw amazonS3Exception;
//}
}
}
}
//If we fell to this code, we didn't get a host match, the S3 values weren't set or there was a problem in parsing the url.
return url;
}
}
}