Get plugin settings when it's not part of the constructor

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
11 年 前
I'm trying to override the DownloadController GetDownload function. To do this, I created a new route for "download/getdownload/{opvid}/{agree}" that directs the customer to my version of DownloadController.cs

The problem I'm having is reading the settings of my plug-in from within this class because I never get anything like ISettingService to pull the settings I created for this plug-in. You can see the code here https://www.nopcommerce.com/boards/t/20771/overriding-actionresult-getdownload-from-plug-in.aspx#84839

So my question is, how do I read settings from my plug-in when I don't have any services passed into the constructor which could help me do that?

Thanks!
11 年 前
Where are your settings defined?  I thought you could just have them inherit ISettings and inject them into your constructor like you are doing with CustomerSettings.  The key is the setting name in the database has to fit the format {ClassName}.{PropertyName}
11 年 前
AndyMcKenna wrote:
Where are your settings defined?  I thought you could just have them inherit ISettings and inject them into your constructor like you are doing with CustomerSettings.  The key is the setting name in the database has to fit the format {ClassName}.{PropertyName}


Thanks for your response.

I'm very new to MVC and this plugin architecture so I don't know what you mean by "just have them inherit ISettings and inject them into your constructor". Pretty much all my code for the plugin (aside from the signDownload function) was copy/paste/replace using the Verizon SMS plugin. Please help me by showing actual code because I don't know what "inject" means or how I make something "inherit ISettings".

Thanks for your help! My two code snippets are below.

I'm storing the settings in the plug-in config page using the code below. It's able to store and retrieve these values anywhere the isettings is already available so I know the values are being stored correctly.

using System;
using System.Web.Mvc;
using Nop.Core.Plugins;
using Nop.Plugin.AmazonAWS.S3Download.Models;
using Nop.Plugin.AmazonAWS.S3Download;
using Nop.Services.Configuration;
using Nop.Services.Localization;
using Nop.Web.Framework.Controllers;

namespace Nop.Plugin.AmazonAWS.S3Download.Controllers
{
    [AdminAuthorize]
    public class S3Controller : Controller
    {
        private readonly AmazonAWSSettings _amazonAWSSettings;
        private readonly ISettingService _settingService;
        private readonly IPluginFinder _pluginFinder;
        private readonly ILocalizationService _localizationService;

        public S3Controller(AmazonAWSSettings amazonAWSSettings,
            ISettingService settingService, IPluginFinder pluginFinder,
            ILocalizationService localizationService)
        {
            this._amazonAWSSettings = amazonAWSSettings;
            this._settingService = settingService;
            this._pluginFinder = pluginFinder;
            this._localizationService = localizationService;
        }

        public ActionResult Configure()
        {
            var model = new S3Model();
            model.Enabled = _amazonAWSSettings.Enabled;
            model.UrlMatch = _amazonAWSSettings.UrlMatch;
            model.AWSKey = _amazonAWSSettings.AWSKey;
            model.AWSSecret = _amazonAWSSettings.AWSSecret;
            return View("Nop.Plugin.AmazonAWS.S3Download.Views.AWS.Configure", model);
        }

        [HttpPost, ActionName("Configure")]
        [FormValueRequired("save")]
        public ActionResult ConfigurePOST(S3Model model)
        {
            if (!ModelState.IsValid)
            {
                return Configure();
            }

            //save settings
            _amazonAWSSettings.Enabled = model.Enabled;
            _amazonAWSSettings.UrlMatch = model.UrlMatch;
            _amazonAWSSettings.AWSKey = model.AWSKey;
            _amazonAWSSettings.AWSSecret = model.AWSSecret;
            _settingService.SaveSetting(_amazonAWSSettings);

            return View("Nop.Plugin.AmazonAWS.S3Download.Views.AWS.Configure", model);
        }
    }
}








The code for the constructor is below. I'm trying to pull the settings in the signDownload function.



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 = /* !!!!Plugin Setting Here!!!!! */;

            string awsS3Key;
            string awsS3Secret;

            System.Uri urlParts = new System.Uri(url);

            awsS3Key = /* !!!!Plugin Setting Here!!!!! */;
            awsS3Secret = /* !!!!Plugin Setting Here!!!!! */;

            /* 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;

        }
    }
}
11 年 前
MichaelApproved wrote:
Where are your settings defined?  I thought you could just have them inherit ISettings and inject them into your constructor like you are doing with CustomerSettings.  The key is the setting name in the database has to fit the format {ClassName}.{PropertyName}

Thanks for your response.

I'm very new to MVC and this plugin architecture so I don't know what you mean by "just have them inherit ISettings and inject them into your constructor". Pretty much all my code for the plugin (aside from the signDownload function) was copy/paste/replace using the Verizon SMS plugin. Please help me by showing actual code because I don't know what "inject" means or how I make something "inherit ISettings".

Thanks for your help! My two code snippets are below.

I'm storing the settings in the plug-in config page using the code below. It's able to store and retrieve these values anywhere the isettings is already available so I know the values are being stored correctly.

using System;
using System.Web.Mvc;
using Nop.Core.Plugins;
using Nop.Plugin.AmazonAWS.S3Download.Models;
using Nop.Plugin.AmazonAWS.S3Download;
using Nop.Services.Configuration;
using Nop.Services.Localization;
using Nop.Web.Framework.Controllers;

namespace Nop.Plugin.AmazonAWS.S3Download.Controllers
{
    [AdminAuthorize]
    public class S3Controller : Controller
    {
        private readonly AmazonAWSSettings _amazonAWSSettings;
        private readonly ISettingService _settingService;
        private readonly IPluginFinder _pluginFinder;
        private readonly ILocalizationService _localizationService;

        public S3Controller(AmazonAWSSettings amazonAWSSettings,
            ISettingService settingService, IPluginFinder pluginFinder,
            ILocalizationService localizationService)
        {
            this._amazonAWSSettings = amazonAWSSettings;
            this._settingService = settingService;
            this._pluginFinder = pluginFinder;
            this._localizationService = localizationService;
        }

        public ActionResult Configure()
        {
            var model = new S3Model();
            model.Enabled = _amazonAWSSettings.Enabled;
            model.UrlMatch = _amazonAWSSettings.UrlMatch;
            model.AWSKey = _amazonAWSSettings.AWSKey;
            model.AWSSecret = _amazonAWSSettings.AWSSecret;
            return View("Nop.Plugin.AmazonAWS.S3Download.Views.AWS.Configure", model);
        }

        [HttpPost, ActionName("Configure")]
        [FormValueRequired("save")]
        public ActionResult ConfigurePOST(S3Model model)
        {
            if (!ModelState.IsValid)
            {
                return Configure();
            }

            //save settings
            _amazonAWSSettings.Enabled = model.Enabled;
            _amazonAWSSettings.UrlMatch = model.UrlMatch;
            _amazonAWSSettings.AWSKey = model.AWSKey;
            _amazonAWSSettings.AWSSecret = model.AWSSecret;
            _settingService.SaveSetting(_amazonAWSSettings);

            return View("Nop.Plugin.AmazonAWS.S3Download.Views.AWS.Configure", model);
        }
    }
}








The code for the constructor is below. I'm trying to pull the settings in the signDownload function.



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 = /* !!!!Plugin Setting Here!!!!! */;

            string awsS3Key;
            string awsS3Secret;

            System.Uri urlParts = new System.Uri(url);

            awsS3Key = /* !!!!Plugin Setting Here!!!!! */;
            awsS3Secret = /* !!!!Plugin Setting Here!!!!! */;

            /* 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;

        }
    }
}


Hi,

Did you noticed that you've injected the settings in your S3Controller? Look at this line:

public S3Controller(AmazonAWSSettings amazonAWSSettings,
            ISettingService settingService, IPluginFinder pluginFinder,
            ILocalizationService localizationService)


Similarly, you just need to inject the settings in your myDownloadController as follow:

public myDownloadController(IDownloadService downloadService, IProductService productService,
            IOrderService orderService, IWorkContext workContext, CustomerSettings customerSettings, AmazonAWSSettings amazonAWSSettings)


Then you are good to go! :D
11 年 前
Success! Thanks.

I didn't know that those parameters were flexible like that to allow for additional arguments. For anyone coming to this thread looking for the code, here's the relevant code:

    public class myDownloadController : BaseNopController
    {
        private readonly IDownloadService _downloadService;
        private readonly IProductService _productService;
        private readonly IOrderService _orderService;
        private readonly IWorkContext _workContext;

        private readonly CustomerSettings _customerSettings;

        private readonly AmazonAWSSettings _amazonAWSSettings;

        //Similar to the original source code but also includes the AmazonAWSSettings so we could get the plug-in settings.
        public myDownloadController(IDownloadService downloadService, IProductService productService,
            IOrderService orderService, IWorkContext workContext, CustomerSettings customerSettings, AmazonAWSSettings amazonAWSSettings)
        {
            this._downloadService = downloadService;
            this._productService = productService;
            this._orderService = orderService;
            this._workContext = workContext;
            this._customerSettings = customerSettings;

            //Save the settings so we could access it using signDownload.
            this._amazonAWSSettings = amazonAWSSettings;
        }


Thanks again. I'll wrap thing up in the next few days and release the plug-in soon. This will allow people to store downloads using S3 but still have control over the flow of downloads and limit it to whatever extent they'd like to using nop admin.
11 年 前
MichaelApproved wrote:
Success! Thanks.

I didn't know that those parameters were flexible like that to allow for additional arguments. For anyone coming to this thread looking for the code, here's the relevant code:

    public class myDownloadController : BaseNopController
    {
        private readonly IDownloadService _downloadService;
        private readonly IProductService _productService;
        private readonly IOrderService _orderService;
        private readonly IWorkContext _workContext;

        private readonly CustomerSettings _customerSettings;

        private readonly AmazonAWSSettings _amazonAWSSettings;

        //Similar to the original source code but also includes the AmazonAWSSettings so we could get the plug-in settings.
        public myDownloadController(IDownloadService downloadService, IProductService productService,
            IOrderService orderService, IWorkContext workContext, CustomerSettings customerSettings, AmazonAWSSettings amazonAWSSettings)
        {
            this._downloadService = downloadService;
            this._productService = productService;
            this._orderService = orderService;
            this._workContext = workContext;
            this._customerSettings = customerSettings;

            //Save the settings so we could access it using signDownload.
            this._amazonAWSSettings = amazonAWSSettings;
        }


Thanks again. I'll wrap thing up in the next few days and release the plug-in soon. This will allow people to store downloads using S3 but still have control over the flow of downloads and limit it to whatever extent they'd like to using nop admin.


Cool thing. :D
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.