using System.Reflection; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Nop.Core; using Nop.Core.Domain; using Nop.Plugin.Widgets.FacebookPixel.Domain; using Nop.Plugin.Widgets.FacebookPixel.Models; using Nop.Plugin.Widgets.FacebookPixel.Services; using Nop.Services.Common; using Nop.Services.Localization; using Nop.Services.Messages; using Nop.Services.Security; using Nop.Services.Stores; using Nop.Web.Areas.Admin.Factories; using Nop.Web.Areas.Admin.Infrastructure.Mapper.Extensions; using Nop.Web.Framework; using Nop.Web.Framework.Controllers; using Nop.Web.Framework.Extensions; using Nop.Web.Framework.Infrastructure; using Nop.Web.Framework.Models.Extensions; using Nop.Web.Framework.Mvc; using Nop.Web.Framework.Mvc.Filters; using Nop.Web.Framework.Mvc.ModelBinding; using Nop.Web.Framework.Validators; namespace Nop.Plugin.Widgets.FacebookPixel.Controllers; [Area(AreaNames.ADMIN)] [AuthorizeAdmin] [AutoValidateAntiforgeryToken] public class FacebookPixelController : BasePluginController { #region Fields protected readonly FacebookPixelService _facebookPixelService; protected readonly IBaseAdminModelFactory _baseAdminModelFactory; protected readonly IGenericAttributeService _genericAttributeService; protected readonly ILocalizationService _localizationService; protected readonly INotificationService _notificationService; protected readonly IPermissionService _permissionService; protected readonly IStoreContext _storeContext; protected readonly IStoreService _storeService; protected readonly IWorkContext _workContext; protected readonly StoreInformationSettings _storeInformationSettings; #endregion #region Ctor public FacebookPixelController(FacebookPixelService facebookPixelService, IBaseAdminModelFactory baseAdminModelFactory, IGenericAttributeService genericAttributeService, ILocalizationService localizationService, INotificationService notificationService, IPermissionService permissionService, IStoreContext storeContext, IStoreService storeService, IWorkContext workContext, StoreInformationSettings storeInformationSettings) { _facebookPixelService = facebookPixelService; _baseAdminModelFactory = baseAdminModelFactory; _genericAttributeService = genericAttributeService; _localizationService = localizationService; _notificationService = notificationService; _permissionService = permissionService; _storeContext = storeContext; _storeService = storeService; _workContext = workContext; _storeInformationSettings = storeInformationSettings; } #endregion #region Utilities /// /// Prepare list of available public widget zones /// /// Available widget zones public IList PreparePublicWidgetZones() { return typeof(PublicWidgetZones) .GetProperties(BindingFlags.Public | BindingFlags.Static) .Where(property => property.PropertyType == typeof(string)) .Select(property => property.GetValue(null) is string value ? new SelectListItem(value, value) : null) .Where(item => item != null) .ToList(); } #endregion #region Methods public async Task Configure() { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return AccessDeniedView(); //prepare plugin configuration model var model = new ConfigurationModel(); await _baseAdminModelFactory.PrepareStoresAsync(model.FacebookPixelSearchModel.AvailableStores); model.FacebookPixelSearchModel.HideStoresList = model.FacebookPixelSearchModel.AvailableStores.SelectionIsNotPossible(); model.FacebookPixelSearchModel.HideSearchBlock = await _genericAttributeService .GetAttributeAsync(await _workContext.GetCurrentCustomerAsync(), FacebookPixelDefaults.HideSearchBlockAttribute); model.FacebookPixelSearchModel.SetGridPageSize(); model.HideList = !(await _facebookPixelService.GetPagedConfigurationsAsync()).Any(); return View("~/Plugins/Widgets.FacebookPixel/Views/Configuration/Configure.cshtml", model); } [HttpPost] public virtual async Task List(FacebookPixelSearchModel searchModel) { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return await AccessDeniedDataTablesJson(); var configurations = await _facebookPixelService.GetPagedConfigurationsAsync(searchModel.StoreId, searchModel.Page - 1, searchModel.PageSize); var model = await new FacebookPixelListModel().PrepareToGridAsync(searchModel, configurations, () => { //fill in model values from the configuration return configurations.SelectAwait(async configuration => new FacebookPixelModel { Id = configuration.Id, PixelId = configuration.PixelId, AccessToken = configuration.AccessToken, PixelScriptEnabled = configuration.PixelScriptEnabled, ConversionsApiEnabled = configuration.ConversionsApiEnabled, StoreId = configuration.StoreId, StoreName = (await _storeService.GetStoreByIdAsync(configuration.StoreId))?.Name }); }); return Json(model); } public virtual async Task Create() { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return AccessDeniedView(); //set default values var model = new FacebookPixelModel { TrackAddToCart = true, TrackPageView = true, TrackPurchase = true, TrackViewContent = true, UseAdvancedMatching = true }; //prepare other model properties await _baseAdminModelFactory.PrepareStoresAsync(model.AvailableStores, false); model.HideStoresList = model.AvailableStores.SelectionIsNotPossible(); return View("~/Plugins/Widgets.FacebookPixel/Views/Configuration/Create.cshtml", model); } [HttpPost] public virtual async Task Create(FacebookPixelModel model) { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return AccessDeniedView(); if (ModelState.IsValid) { //save configuration var store = await _storeContext.GetCurrentStoreAsync(); model.StoreId = model.StoreId > 0 ? model.StoreId : store.Id; var configuration = model.ToEntity(); await _facebookPixelService.InsertConfigurationAsync(configuration); _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Plugins.Saved")); return RedirectToAction("Configure", "FacebookPixel"); } //if we got this far, something failed, redisplay form await _baseAdminModelFactory.PrepareStoresAsync(model.AvailableStores, false); model.HideStoresList = model.AvailableStores.SelectionIsNotPossible(); return View("~/Plugins/Widgets.FacebookPixel/Views/Configuration/Create.cshtml", model); } public virtual async Task Edit(int id) { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return AccessDeniedView(); var configuration = await _facebookPixelService.GetConfigurationByIdAsync(id); if (configuration == null) return RedirectToAction("Configure", "FacebookPixel"); //fill in model values from the configuration var model = configuration.ToModel(); //prepare other model properties await _baseAdminModelFactory.PrepareStoresAsync(model.AvailableStores, false); model.HideStoresList = model.AvailableStores.SelectionIsNotPossible(); model.HideCustomEventsSearch = !(await _facebookPixelService.GetCustomEventsAsync(configuration.Id)).Any(); model.CustomEventSearchModel.ConfigurationId = configuration.Id; model.CustomEventSearchModel.AddCustomEvent.AvailableWidgetZones = PreparePublicWidgetZones(); model.CustomEventSearchModel.SetGridPageSize(); return View("~/Plugins/Widgets.FacebookPixel/Views/Configuration/Edit.cshtml", model); } [HttpPost] public virtual async Task Edit(FacebookPixelModel model) { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return AccessDeniedView(); var configuration = await _facebookPixelService.GetConfigurationByIdAsync(model.Id); if (configuration == null) return RedirectToAction("Configure", "FacebookPixel"); if (ModelState.IsValid) { var store = await _storeContext.GetCurrentStoreAsync(); //save configuration var customEvents = configuration.CustomEvents; model.StoreId = model.StoreId > 0 ? model.StoreId : store.Id; configuration = model.ToEntity(); configuration.CustomEvents = customEvents; await _facebookPixelService.UpdateConfigurationAsync(configuration); _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Plugins.Saved")); return RedirectToAction("Configure", "FacebookPixel"); } //if we got this far, something failed, redisplay form await _baseAdminModelFactory.PrepareStoresAsync(model.AvailableStores, false); model.HideStoresList = model.AvailableStores.SelectionIsNotPossible(); model.HideCustomEventsSearch = !(await _facebookPixelService.GetCustomEventsAsync(configuration.Id)).Any(); model.CustomEventSearchModel.ConfigurationId = configuration.Id; model.CustomEventSearchModel.AddCustomEvent.AvailableWidgetZones = PreparePublicWidgetZones(); model.CustomEventSearchModel.SetGridPageSize(); return View("~/Plugins/Widgets.FacebookPixel/Views/Configuration/Edit.cshtml", model); } [HttpPost] public virtual async Task Delete(int id) { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return AccessDeniedView(); var configuration = await _facebookPixelService.GetConfigurationByIdAsync(id); if (configuration == null) return RedirectToAction("Configure", "FacebookPixel"); //delete configuration await _facebookPixelService.DeleteConfigurationAsync(configuration); _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Plugins.Saved")); return RedirectToAction("Configure", "FacebookPixel"); } [HttpPost] public virtual async Task CustomEventList(CustomEventSearchModel searchModel) { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return await AccessDeniedDataTablesJson(); var configuration = await _facebookPixelService.GetConfigurationByIdAsync(searchModel.ConfigurationId) ?? throw new ArgumentException("No configuration found with the specified id", nameof(searchModel.ConfigurationId)); var customEvents = (await _facebookPixelService.GetCustomEventsAsync(configuration.Id, searchModel.WidgetZone)).ToPagedList(searchModel); var model = new CustomEventListModel().PrepareToGrid(searchModel, customEvents, () => { //fill in model values from the configuration return customEvents.Select(customEvent => new CustomEventModel { ConfigurationId = configuration.Id, EventName = customEvent.EventName, WidgetZonesName = string.Join(", ", customEvent.WidgetZones) }); }); return Json(model); } //ValidateAttribute is used to force model validation [HttpPost] public virtual async Task CustomEventAdd(int configurationId, [Validate] CustomEventModel model) { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return await AccessDeniedDataTablesJson(); if (!ModelState.IsValid) return ErrorJson(ModelState.SerializeErrors()); //save custom events configuration await _facebookPixelService.SaveCustomEventsAsync(configurationId, model.EventName, model.WidgetZones); return Json(new { Result = true }); } [HttpPost] public virtual async Task CustomEventDelete(int configurationId, string id) { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return await AccessDeniedDataTablesJson(); //save custom events configuration var eventName = id; await _facebookPixelService.SaveCustomEventsAsync(configurationId, eventName, null); return new NullJsonResult(); } public async Task CookieSettingsWarning(bool disableForUsersNotAcceptingCookieConsent) { if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageWidgets)) return await AccessDeniedDataTablesJson(); if (!disableForUsersNotAcceptingCookieConsent || _storeInformationSettings.DisplayEuCookieLawWarning) return Json(new { Result = string.Empty }); //display a warning when "DisplayEuCookieLawWarning" setting is disabled, //but merchant want to disable Facebook Pixel for users not accepting Cookie Consent var url = Url.Action("GeneralCommon", "Setting"); var warning = string.Format(await _localizationService.GetResourceAsync("Plugins.Widgets.FacebookPixel.Configuration.CookieSettingsWarning"), url); return Json(new { Result = warning }); } #endregion }