SignalR message serialize, deserialize, toaster, messagehandler JS library
This commit is contained in:
parent
01bd971a80
commit
53abcb5a64
|
|
@ -1,9 +1,13 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Nop.Plugin.Misc.AuctionPlugin.Areas.Admin.Models;
|
||||
using Nop.Plugin.Misc.AuctionPlugin.Domains;
|
||||
using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities;
|
||||
using Nop.Plugin.Misc.AuctionPlugin.Hubs;
|
||||
using Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages;
|
||||
using Nop.Plugin.Misc.AuctionPlugin.Services;
|
||||
using Nop.Services.Logging;
|
||||
using Nop.Web.Framework;
|
||||
|
|
@ -64,13 +68,67 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Areas.Admin.Controllers
|
|||
|
||||
if (viewModel.IsActive == true)
|
||||
{
|
||||
var announcement = new MessageBase
|
||||
{
|
||||
MessageType = "announcement",
|
||||
Data = new AnnouncementMessage
|
||||
{
|
||||
Title = viewModel.Name,
|
||||
Message = viewModel.Body,
|
||||
}
|
||||
};
|
||||
var jsonMessage = JsonConvert.SerializeObject(announcement, Formatting.Indented,
|
||||
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
|
||||
await _logger.InformationAsync($"sending announcements");
|
||||
await _announcementHubContext.Clients.All.SendAsync("send", viewModel.Body.ToString());
|
||||
await _announcementHubContext.Clients.All.SendAsync("send", jsonMessage);
|
||||
//await _announcementHubContext.Clients.All.SendAsync("send", viewModel.Body.ToString());
|
||||
}
|
||||
return RedirectToAction("AnnouncementList");
|
||||
|
||||
}
|
||||
|
||||
public async Task<IActionResult> SendBidNotificationViewModel()
|
||||
{
|
||||
var viewModel = new BidNotificationViewModel();
|
||||
viewModel.ProductName = "Picasso - Önarckép";
|
||||
viewModel.BidPrice = 200000;
|
||||
viewModel.NextStepAmount = 50000;
|
||||
return View("~/Plugins/Misc.AuctionPlugin/Areas/Admin/Views/BidNotification.cshtml", viewModel);
|
||||
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> SendBidNotificationViewModel(BidNotificationViewModel viewModel)
|
||||
{
|
||||
AuctionBid objOfAuctionBid = new AuctionBid();
|
||||
objOfAuctionBid.ProductId = 4;
|
||||
objOfAuctionBid.BidPrice = viewModel.BidPrice;
|
||||
|
||||
objOfAuctionBid.Created = DateTime.UtcNow;
|
||||
//await _announcementService.InsertAsync(objOfAuctionBid);
|
||||
|
||||
|
||||
var bid = new MessageBase
|
||||
{
|
||||
MessageType = "bidNotification",
|
||||
Data = new BidNotificationMessage
|
||||
{
|
||||
ProductName = viewModel.ProductName,
|
||||
BidPrice = viewModel.BidPrice,
|
||||
NextStepAmount = viewModel.NextStepAmount
|
||||
}
|
||||
};
|
||||
var jsonMessage = JsonConvert.SerializeObject(bid, Formatting.Indented,
|
||||
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }
|
||||
);
|
||||
|
||||
await _announcementHubContext.Clients.All.SendAsync("send", jsonMessage);
|
||||
|
||||
|
||||
return View("~/Plugins/Misc.AuctionPlugin/Areas/Admin/Views/BidNotification.cshtml", viewModel);
|
||||
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Edit(Announcement model)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.AuctionPlugin.Areas.Admin.Models
|
||||
{
|
||||
public record BidNotificationViewModel : BaseNopModel
|
||||
{
|
||||
[NopResourceDisplayName("Name")]
|
||||
public string ProductName { get; set; }
|
||||
|
||||
public int BidPrice { get; set; }
|
||||
|
||||
public int NextStepAmount { get; set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
@model BidNotificationViewModel
|
||||
@using Nop.Core.Infrastructure
|
||||
@using Nop.Web.Framework
|
||||
|
||||
@{
|
||||
var defaultGridPageSize = EngineContext.Current.Resolve<Nop.Core.Domain.Common.AdminAreaSettings>().DefaultGridPageSize;
|
||||
var gridPageSizes = EngineContext.Current.Resolve<Nop.Core.Domain.Common.AdminAreaSettings>().GridPageSizes;
|
||||
Layout = "_AdminLayout";
|
||||
//page title
|
||||
ViewBag.Title = T("Admin.Plugins.HomePageProduct").Text;
|
||||
}
|
||||
|
||||
@using (Html.BeginForm())
|
||||
{
|
||||
<div class="content-header clearfix">
|
||||
<h1 class="pull-left">
|
||||
Send BidNotification
|
||||
</h1>
|
||||
|
||||
<div class="pull-right">
|
||||
<button type="submit" class="btn bg-purple">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
Send BidNotification
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="form-horizontal">
|
||||
<div class="panel-group">
|
||||
<div class="panel panel-default panel-search">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<div class="col-md-3" style="text-align:center;">
|
||||
|
||||
@Html.LabelFor(model => model.ProductName)
|
||||
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
|
||||
@Html.EditorFor(model => model.ProductName)
|
||||
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-3" style="text-align:center;">
|
||||
|
||||
@Html.LabelFor(model => model.BidPrice)
|
||||
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
@Html.EditorFor(model => model.BidPrice)
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-3" style="text-align:center;">
|
||||
|
||||
@Html.LabelFor(model => model.NextStepAmount)
|
||||
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
|
||||
@Html.EditorFor(model => model.NextStepAmount)
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-1">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ public static class AuctionDefaults
|
|||
public static string AnnouncementListRouteName => "Plugin.Misc.AuctionPlugin.AnnouncementList";
|
||||
public static string AuctionRouteName => "Plugin.Misc.AuctionPlugin.Auction";
|
||||
public static string TestPageRouteName => "Plugin.Misc.AuctionPlugin.TestPage";
|
||||
public static string BidNotificationRouteName => "Plugin.Misc.AuctionPlugin.BidNotification";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of autosuggest component
|
||||
|
|
|
|||
|
|
@ -193,6 +193,14 @@ namespace Nop.Plugin.Misc.AuctionPlugin
|
|||
Url = "~/Admin/AuctionPluginAdmin/TestPage"
|
||||
});
|
||||
|
||||
liveAnnouncementPluginNode.ChildNodes.Add(new SiteMapNode()
|
||||
{
|
||||
Title = await _localizationService.GetResourceAsync("Misc.SendBid"),
|
||||
Visible = true,
|
||||
IconClass = "fa-dot-circle-o",
|
||||
Url = "~/Admin/Announcement/SendBidNotificationViewModel"
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
$(function () {
|
||||
// Define the message handler library
|
||||
|
||||
// SignalR connection setup
|
||||
$(function () {
|
||||
console.log("signalRJs Starts");
|
||||
|
||||
var connection = new signalR.HubConnectionBuilder()
|
||||
.withUrl('/auctionhub')
|
||||
.build();
|
||||
|
||||
connection.on('send', data => {
|
||||
showannouncement(data);
|
||||
MessageHandler.handle(data);
|
||||
});
|
||||
|
||||
function start() {
|
||||
|
|
@ -20,67 +23,97 @@
|
|||
connection.onclose(function () {
|
||||
start();
|
||||
});
|
||||
|
||||
|
||||
start();
|
||||
});
|
||||
|
||||
|
||||
|
||||
function showannouncement(announcemant) {
|
||||
console.log("announcement arrived!");
|
||||
if (announcemant) {
|
||||
toastr.options = {
|
||||
"closeButton": true,
|
||||
"debug": false,
|
||||
"newestOnTop": false,
|
||||
"progressBar": false,
|
||||
"positionClass": "toast-bottom-right",
|
||||
"preventDuplicates": false,
|
||||
"onclick": null,
|
||||
"showDuration": 300,
|
||||
"hideDuration": 10000,
|
||||
"timeOut": 100000,
|
||||
"extendedTimeOut": 20000,
|
||||
"showEasing": "swing",
|
||||
"hideEasing": "linear",
|
||||
"showMethod": "fadeIn",
|
||||
"hideMethod": "fadeOut"
|
||||
};
|
||||
|
||||
tostView = '<div class="item announcemantToast">' + announcemant + '</div>'
|
||||
toastr["info"](tostView);
|
||||
$('.toast-info').css("background-color", "#008080");
|
||||
|
||||
toastr.options.onclick = function () {
|
||||
$("html, body").animate(
|
||||
{ scrollTop: 0 },
|
||||
1000);
|
||||
}
|
||||
//$(function () {
|
||||
|
||||
$(".toast").click(function () {
|
||||
$("html, body").animate(
|
||||
{ scrollTop: 0 },
|
||||
1000);
|
||||
});
|
||||
// console.log("signalRJs Starts");
|
||||
// var connection = new signalR.HubConnectionBuilder()
|
||||
// .withUrl('/auctionhub')
|
||||
// .build();
|
||||
|
||||
$(".toast-info").click(function () {
|
||||
$("html, body").animate(
|
||||
{ scrollTop: 0 },
|
||||
1000);
|
||||
});
|
||||
// connection.on('send', data => {
|
||||
// showannouncement(data);
|
||||
// });
|
||||
|
||||
toastr.options = {
|
||||
onclick: function () {
|
||||
$("html, body").animate(
|
||||
{ scrollTop: 0 },
|
||||
1000);
|
||||
}
|
||||
}
|
||||
// function start() {
|
||||
// connection.start().catch(function (err) {
|
||||
// setTimeout(function () {
|
||||
// start();
|
||||
// }, 100000);
|
||||
// });
|
||||
// }
|
||||
|
||||
$(".announcemantToast").on("click", function () {
|
||||
$("html, body").animate(
|
||||
{ scrollTop: 0 },
|
||||
1000);
|
||||
});
|
||||
}
|
||||
}
|
||||
// connection.onclose(function () {
|
||||
// start();
|
||||
// });
|
||||
|
||||
// start();
|
||||
//});
|
||||
|
||||
|
||||
|
||||
//function showannouncement(announcemant) {
|
||||
// console.log("announcement arrived!");
|
||||
// if (announcemant) {
|
||||
// toastr.options = {
|
||||
// "closeButton": true,
|
||||
// "debug": false,
|
||||
// "newestOnTop": false,
|
||||
// "progressBar": false,
|
||||
// "positionClass": "toast-bottom-right",
|
||||
// "preventDuplicates": false,
|
||||
// "onclick": null,
|
||||
// "showDuration": 300,
|
||||
// "hideDuration": 10000,
|
||||
// "timeOut": 100000,
|
||||
// "extendedTimeOut": 20000,
|
||||
// "showEasing": "swing",
|
||||
// "hideEasing": "linear",
|
||||
// "showMethod": "fadeIn",
|
||||
// "hideMethod": "fadeOut"
|
||||
// };
|
||||
|
||||
// tostView = '<div class="item announcemantToast">' + announcemant + '</div>'
|
||||
// toastr["info"](tostView);
|
||||
// $('.toast-info').css("background-color", "#008080");
|
||||
|
||||
// toastr.options.onclick = function () {
|
||||
// $("html, body").animate(
|
||||
// { scrollTop: 0 },
|
||||
// 1000);
|
||||
// }
|
||||
|
||||
// $(".toast").click(function () {
|
||||
// $("html, body").animate(
|
||||
// { scrollTop: 0 },
|
||||
// 1000);
|
||||
// });
|
||||
|
||||
// $(".toast-info").click(function () {
|
||||
// $("html, body").animate(
|
||||
// { scrollTop: 0 },
|
||||
// 1000);
|
||||
// });
|
||||
|
||||
// toastr.options = {
|
||||
// onclick: function () {
|
||||
// $("html, body").animate(
|
||||
// { scrollTop: 0 },
|
||||
// 1000);
|
||||
// }
|
||||
// }
|
||||
|
||||
// $(".announcemantToast").on("click", function () {
|
||||
// $("html, body").animate(
|
||||
// { scrollTop: 0 },
|
||||
// 1000);
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
var MessageHandler = (function () {
|
||||
// Handlers for each message type
|
||||
let animation = "slideDown";
|
||||
const handlers = {
|
||||
announcement: function (data) {
|
||||
toastr.info(`<div class="item announcemantToast">${data.message}</div>`, data.title, {
|
||||
"closeButton": true,
|
||||
"positionClass": "toast-bottom-right",
|
||||
"newestOnTop": true,
|
||||
"progressBar": true,
|
||||
"preventDuplicates": false,
|
||||
"onclick": null,
|
||||
"showDuration": "30000",
|
||||
"hideDuration": "1000",
|
||||
"timeOut": "5000",
|
||||
"extendedTimeOut": "1000",
|
||||
"showEasing": "swing",
|
||||
"hideEasing": "linear",
|
||||
"showMethod": animation,
|
||||
"hideMethod": "fadeOut"
|
||||
});
|
||||
$('.toast-info').css("background-color", "#008080");
|
||||
},
|
||||
bidNotification: function (data) {
|
||||
//console.log("Bid product name" + data.productName);
|
||||
toastr.success(`<div class="item bidToast"><p>${data.bidPrice}</p><p>${data.productName}</p></div>`, "New bid arrived", {
|
||||
//"timeOut": 150000,
|
||||
"closeButton": true,
|
||||
"positionClass": "toast-bottom-right",
|
||||
"newestOnTop": true,
|
||||
"progressBar": true,
|
||||
"preventDuplicates": false,
|
||||
"onclick": null,
|
||||
"showDuration": "30000",
|
||||
"hideDuration": "1000",
|
||||
"timeOut": "5000",
|
||||
"extendedTimeOut": "1000",
|
||||
"showEasing": "swing",
|
||||
"hideEasing": "linear",
|
||||
"showMethod": animation,
|
||||
"hideMethod": "fadeOut"
|
||||
|
||||
});
|
||||
$('.toast-success').css("background-color", "#4caf50");
|
||||
},
|
||||
// Add more handlers as needed
|
||||
default: function (data) {
|
||||
console.warn("Unhandled message type:", data);
|
||||
}
|
||||
};
|
||||
|
||||
// Message router to route to the appropriate handler based on message type
|
||||
function messageRouter(message) {
|
||||
// Parse the JSON message
|
||||
try {
|
||||
/*console.log(message);*/
|
||||
const parsedMessage = JSON.parse(message);
|
||||
const messageType = parsedMessage.messageType;
|
||||
const messageData = parsedMessage.data;
|
||||
console.log("Message type:" + messageType);
|
||||
// Route to appropriate handler, default if no match
|
||||
(handlers[messageType] || handlers.default)(messageData);
|
||||
} catch (e) {
|
||||
console.error("Error parsing message:", e);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
handle: messageRouter
|
||||
};
|
||||
})();
|
||||
|
|
@ -41,11 +41,11 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs
|
|||
//await _signalRservice.TestHub();
|
||||
}
|
||||
|
||||
public async Task Send(string announcement)
|
||||
{
|
||||
await _logger.InformationAsync($" Hub Send method called with messgae: {announcement}");
|
||||
await Clients.All.SendAsync("send", announcement);
|
||||
}
|
||||
//public async Task Send(string announcement)
|
||||
//{
|
||||
// await _logger.InformationAsync($" Hub Send method called with messgae: {announcement}");
|
||||
// await Clients.All.SendAsync("send", announcement);
|
||||
//}
|
||||
|
||||
public async Task SendPriceToUsers(string message)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages
|
||||
{
|
||||
public class AnnouncementMessage
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages
|
||||
{
|
||||
public class BidNotificationMessage
|
||||
{
|
||||
public string ProductName { get; set; }
|
||||
public int BidPrice { get; set; }
|
||||
public int NextStepAmount { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages
|
||||
{
|
||||
public class MessageBase
|
||||
{
|
||||
public string MessageType { get; set; }
|
||||
public object Data { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -15,6 +16,7 @@ using Nop.Services.Catalog;
|
|||
using Nop.Services.Configuration;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Web.Framework;
|
||||
using Nop.Web.Framework.Mvc.Routing;
|
||||
|
||||
namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure
|
||||
{
|
||||
|
|
@ -51,7 +53,8 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure
|
|||
services.AddScoped<IAuctionService, AuctionService>();
|
||||
services.AddScoped<IProductAttributeService, ProductAttributeService>();
|
||||
services.AddScoped<UrlHelperFactory>();
|
||||
services.AddScoped<IActionContextAccessor, ActionContextAccessor>();
|
||||
services.AddScoped<IActionContextAccessor, ActionContextAccessor>();
|
||||
services.AddScoped<IRouteProvider, RouteProvider>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Infrastructure
|
|||
endpointRouteBuilder.MapControllerRoute(name: AuctionDefaults.TestPageRouteName,
|
||||
pattern: "Admin/Auction/TestPage",
|
||||
defaults: new { controller = "AuctionPluginAdmin", action = "TestPage" });
|
||||
|
||||
endpointRouteBuilder.MapControllerRoute(name: AuctionDefaults.BidNotificationRouteName,
|
||||
pattern: "Admin/Announcement/SendBidNotificationViewModel",
|
||||
defaults: new { controller = "Announcement", action = "SendBidNotificationViewModel" });
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<None Remove="Areas\Admin\Views\Auction.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\BidNotification.cshtml" />
|
||||
<None Remove="Areas\Admin\Views\TestPage.cshtml" />
|
||||
<None Remove="logo.jpg" />
|
||||
<None Remove="plugin.json" />
|
||||
|
|
@ -29,6 +30,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Areas\Admin\Views\BidNotification.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Areas\Admin\Views\TestPage.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
@ -100,6 +104,9 @@
|
|||
<None Update="Content\Css\toastr.min.css">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Js\MgMessageHandler.js">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Content\Js\LiveAnnouncement.js">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
IStoreContext _storeContext = EngineContext.Current.Resolve<IStoreContext>();
|
||||
|
||||
NopHtml.AddScriptParts(ResourceLocation.Head, "~/Plugins/Misc.AuctionPlugin/Content/Js/signalr.js");
|
||||
NopHtml.AddScriptParts(ResourceLocation.Footer, "~/Plugins/Misc.AuctionPlugin/Content/Js/MgMessageHandler.js");
|
||||
NopHtml.AddScriptParts(ResourceLocation.Footer, "~/Plugins/Misc.AuctionPlugin/Content/Js/LiveAnnouncement.js");
|
||||
NopHtml.AddCssFileParts("~/Plugins/Misc.AuctionPlugin/Content/Css/toastr.min.css");
|
||||
NopHtml.AddScriptParts(ResourceLocation.Footer, "~/Plugins/Misc.AuctionPlugin/Content/Js/toastr.js");
|
||||
|
|
|
|||
Loading…
Reference in New Issue