Error executing template "Designs/Swift/eCom/ProductCatalog/ProductViewDetail.cshtml"
System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) ---> System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at Dynamicweb.Data.DatabaseConnectionProvider.CreateConnection(Boolean open)
   at Dynamicweb.Data.Database.CreateConnection()
   at Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout)
   at Dynamicweb.Ecommerce.Products.DetailRepository.GetInheritedDetailsBulk(List`1 productIds, String detailType, Boolean onlyDefault)
   at Dynamicweb.Ecommerce.Products.DetailService.GetPrimaryDetailsBulk(IEnumerable`1 productKeys, String detailType)
   at System.Lazy`1.CreateValue()
   at System.Lazy`1.LazyInitValue()
   at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.GetDefaultImage(MediaViewModelSettings settings, String productId, String languageId, String variantId, Lazy`1 productImages, Lazy`1 details)
   at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.GetDefaultImage(MediaViewModelSettings settings, Product product, Lazy`1 productImages, Lazy`1 details)
   at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.<>c__DisplayClass3_2.<BulkCreateView>b__62()
   at System.Lazy`1.CreateValue()
   at System.Lazy`1.LazyInitValue()
   at CompiledRazorTemplates.Dynamic.RazorEngine_8efe56ad3681414ea4d0189a0a36e114.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\eCom\ProductCatalog\ProductViewDetail.cshtml:line 9
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()
ClientConnectionId:00000000-0000-0000-0000-000000000000
Error Number:2,State:0,Class:20

1 @inherits ViewModelTemplate<ProductViewModel> 2 @using Dynamicweb.Rendering 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 @using Dynamicweb.Core 5 6 @{ 7 string metaDescription = string.IsNullOrEmpty(Model.MetaDescription) ? Model.Name : Model.MetaDescription; 8 9 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Model.DefaultImage.Value}\">"); 10 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{Model.Name}\">"); 11 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{metaDescription}\">"); 12 13 Pageview.Meta.AddTag("twitter:image", Model.DefaultImage.Value); 14 Pageview.Meta.AddTag("twitter:image:alt", Model.Name); 15 Pageview.Meta.AddTag("twitter:description", metaDescription); 16 } 17 18 @{ 19 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 20 { 21 Dynamicweb.Context.Current.Items["ProductDetails"] = Model; 22 } 23 else 24 { 25 Dynamicweb.Context.Current.Items.Add("ProductDetails", Model); 26 } 27 28 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 29 if (isLazyLoadingForProductInfoEnabled) 30 { 31 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 32 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 33 bool hasVariantId = !string.IsNullOrEmpty(Model.VariantId); 34 string variantIdParam = hasVariantId ? $"/{Model.VariantId}" : ""; 35 string priceFilledProperties = $"Price,PriceFormatted{(showPricesWithVat == "false" && !neverShowVat ? ",PriceWithVat,PriceWithVatFormatted" : "")}"; 36 string productInfoFeed = $@"/dwapi/ecommerce/products/{Model.Id}{variantIdParam} 37 ?UserId={Converter.ToString(Pageview.User?.ID)} 38 &LanguageId={Pageview.Area.EcomLanguageId}&CurrencyCode={Pageview.Area.EcomCurrencyId}&CountryCode={Pageview.Area.EcomCountryCode}&ShopId={Pageview.Area.EcomShopId} 39 &FilledProperties=Id,Price,PriceBeforeDiscount,StockLevel,VariantInfo,NeverOutOfstock,Prices 40 &PriceSettings.ShowPricesWithVat={Pageview.Area.EcomPricesWithVat} 41 &PriceSettings.FilledProperties={priceFilledProperties} 42 &getproductinfo=true"; 43 Dynamicweb.Context.Current.Items["ProductInfoFeed"] = productInfoFeed; 44 45 <script type="module"> 46 swift.LiveProductInfo.init(); 47 </script> 48 } 49 } 50 51 <script> 52 gtag("event", "view_item", { 53 currency: "@Model.Price.CurrencyCode", 54 value: @Model.Price.ToStringInvariant(), 55 items: [ 56 { 57 item_id: "@Model.Number", 58 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 59 currency: "@Model.Price.CurrencyCode", 60 price: @Model.Price.ToStringInvariant(), 61 discount: @Model.Discount.ToStringInvariant() 62 } 63 ] 64 }); 65 </script> 66 67 <script> 68 window.addEventListener('load', function (event) { 69 swift.Video.init(); 70 }); 71 </script> 72

An error occurred while attaching module (Dynamicweb.Frontend.Content)

System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) ---> System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at Dynamicweb.Data.DatabaseConnectionProvider.CreateConnection(Boolean open)
   at Dynamicweb.Data.Database.CreateConnection()
   at Dynamicweb.Data.Database.CreateDataReader(String sql, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout, Dictionary`2 sqlParams)
   at Dynamicweb.Forms.FormRepository.GetFormById(Int32 id)
   at Dynamicweb.Forms.Form.GetFormById(Int32 id)
   at Dynamicweb.Forms.Frontend.RenderForm()
   at Dynamicweb.Forms.Frontend.GetContent()
   at Dynamicweb.Frontend.Content.GetModuleOutput(Paragraph paragraph, PageView pageview)
ClientConnectionId:00000000-0000-0000-0000-000000000000
Error Number:2,State:0,Class:20

Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGalleryCustom.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_2e17544f1d124e409c1a6ed257b6f616.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGalleryCustom.cshtml:line 85
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 6 @functions { 7 public ProductViewModel product { get; set; } = new ProductViewModel(); 8 public string galleryLayout { get; set; } 9 public string[] supportedImageFormats { get; set; } 10 public string[] supportedVideoFormats { get; set; } 11 public string[] supportedDocumentFormats { get; set; } 12 public string[] allSupportedFormats { get; set; } 13 14 public class RatioSettings { 15 public string Ratio { get; set; } 16 public string CssClass { get; set; } 17 public string CssVariable { get; set; } 18 public string Fill { get; set; } 19 } 20 21 public RatioSettings GetRatioSettings(string size = "desktop") { 22 var ratioSettings = new RatioSettings(); 23 24 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 25 ratio = ratio != "0" ? ratio : ""; 26 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 27 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 28 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 29 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 30 31 ratioSettings.Ratio = ratio; 32 ratioSettings.CssClass = cssClass; 33 ratioSettings.CssVariable = cssVariable; 34 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 35 36 return ratioSettings; 37 } 38 39 public string GetColumnClass(int total, int assetNumber) { 40 string colClass = total > 1 ? "g-col-lg-6" : "g-col-12"; 41 colClass = galleryLayout == "full-first" && assetNumber == 0 ? "g-col-12" : colClass; 42 colClass = galleryLayout == "full-last" && assetNumber == (total - 1) ? "g-col-12" : colClass; 43 colClass = galleryLayout == "advanced-grid" && assetNumber > 1 ? "g-col-4" : colClass; 44 45 colClass = galleryLayout == "advanced-grid" && total == 1 ? "g-col-12" : colClass; 46 colClass = galleryLayout == "advanced-grid" && total == 3 && assetNumber == 2 ? "g-col-12" : colClass; 47 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 2 ? "g-col-6" : colClass; 48 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 3 ? "g-col-6" : colClass; 49 colClass = galleryLayout == "advanced-grid" && total == 6 && assetNumber == 5 ? "g-col-12" : colClass; 50 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 5 ? "g-col-6" : colClass; 51 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 6 ? "g-col-6" : colClass; 52 colClass = galleryLayout == "advanced-grid" && total == 9 && assetNumber == 8 ? "g-col-12" : colClass; 53 54 return colClass; 55 } 56 57 public string GetArrowsColor() 58 { 59 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 60 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 61 return arrowsColor; 62 } 63 } 64 65 @if (product is object) 66 { 67 @* Get the product data *@ 68 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 69 { 70 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 71 } 72 73 @* Supported formats *@ 74 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 75 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 76 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 77 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 78 79 @* Collect the assets *@ 80 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 81 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 82 83 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 84 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 85 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 86 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 87 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 88 assetsList = assetsList.Union(assetsImages); 89 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 90 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 91 92 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 93 94 int totalAssets = 0; 95 foreach (MediaViewModel asset in assetsList) { 96 var assetValue = asset.Value; 97 foreach (string format in allSupportedFormats) { 98 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 99 totalAssets++; 100 } 101 } 102 } 103 104 if (totalAssets == 0) 105 { 106 if (defaultImageFallback) { 107 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 108 totalAssets = 1; 109 } else { 110 assetsList = new List<MediaViewModel>(){ }; 111 totalAssets = 0; 112 } 113 } 114 115 @* Layout settings *@ 116 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 117 spacing = spacing == "none" ? "gap-0" : spacing; 118 spacing = spacing == "small" ? "gap-3" : spacing; 119 spacing = spacing == "large" ? "gap-4" : spacing; 120 121 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 122 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 123 124 var badgeParms = new Dictionary<string, object>(); 125 badgeParms.Add("size", "h5"); 126 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 127 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 128 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 129 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 130 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 131 132 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 133 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 134 DateTime createdDate = product.Created.Value; 135 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 136 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 137 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 138 139 140 @* Get assets from selected categories or get all assets *@ 141 if (totalAssets != 0 && assetsList.Count() != 0) 142 { 143 int desktopAssetNumber = 0; 144 int mobileAssetNumber = 0; 145 int mobileAssetThumbnailNumber = 0; 146 int modalAssetNumber = 0; 147 int numberOfImages = int.Parse(Model.Item.GetRawValueString("NumberOfImagesShown")); 148 int imagesShownCounter = 0; 149 150 @* Desktop: Show the gallery on large screens *@ 151 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 152 <div class="grid @spacing"> 153 @foreach (MediaViewModel asset in assetsList) 154 { 155 if (imagesShownCounter == numberOfImages) 156 { 157 break; 158 } 159 var assetName = asset.Value; 160 foreach (string format in allSupportedFormats) 161 { 162 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 163 { 164 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 165 @{@RenderAsset(asset, desktopAssetNumber, totalAssets)} 166 </div> 167 desktopAssetNumber++; 168 } 169 } 170 imagesShownCounter++; 171 } 172 </div> 173 174 @if (showBadges) 175 { 176 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 177 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 178 </div> 179 } 180 </div> 181 182 @* Mobile: Show the thumbs on small screens *@ 183 imagesShownCounter = 0; 184 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 185 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 186 <div class="carousel-inner h-100"> 187 @foreach (MediaViewModel asset in assetsList) 188 { 189 if (imagesShownCounter == numberOfImages) 190 { 191 break; 192 } 193 var assetValue = asset.Value; 194 foreach (string format in allSupportedFormats) 195 { 196 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 197 { 198 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 199 200 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 201 @{@RenderAsset(asset, mobileAssetNumber, totalAssets, "mobile")} 202 </div> 203 mobileAssetNumber++; 204 } 205 } 206 imagesShownCounter++; 207 } 208 </div> 209 </div> 210 211 @if (totalAssets > 1) 212 { 213 imagesShownCounter = 0; 214 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 215 @foreach (MediaViewModel asset in assetsList) 216 { 217 if (imagesShownCounter == numberOfImages) 218 { 219 break; 220 } 221 var assetValue = asset.Value; 222 foreach (string format in allSupportedFormats) 223 { 224 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 225 { 226 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); 227 imagePath = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/default.jpg" : imagePath; 228 string imagePathThumb = imagePath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) < 0 ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=160&format=webp" : imagePath; 229 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 230 231 string videoId = assetValue.Substring(assetValue.LastIndexOf('/') + 1); 232 string vimeoJsClass = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 233 234 string productName = product.Name; 235 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 236 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 237 238 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 239 @foreach (string videoFormat in supportedVideoFormats) 240 { 241 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 242 { 243 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 244 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 245 </div> 246 } 247 } 248 @if (imagePathThumb.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) 249 { 250 251 <img src="@(imagePathThumb)" class="p-1 @vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;" alt="@productName" @assetTitle> 252 253 } 254 else 255 { 256 string videoType = Path.GetExtension(asset.Value).ToLower(); 257 string videoPathEncoded = System.Uri.EscapeDataString(assetValue); 258 259 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 260 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 261 </video> 262 } 263 </div> 264 265 mobileAssetThumbnailNumber++; 266 } 267 } 268 imagesShownCounter++; 269 } 270 </div> 271 } 272 273 @if (showBadges) 274 { 275 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 276 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 277 </div> 278 } 279 </div> 280 281 @* Modal with slides *@ 282 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 283 <div class="modal-dialog modal-dialog-centered modal-xl"> 284 <div class="modal-content"> 285 <div class="modal-header visually-hidden"> 286 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 287 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 288 </div> 289 <div class="modal-body p-2 p-lg-3 h-100"> 290 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 291 <div class="carousel-inner h-100 @theme"> 292 @{imagesShownCounter = 0;} 293 @foreach (MediaViewModel asset in assetsList) { 294 if (imagesShownCounter == numberOfImages) 295 { 296 break; 297 } 298 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 299 foreach (string format in allSupportedFormats) { 300 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 301 string imagePath = assetValue; 302 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 303 304 var parms = new Dictionary<string, object>(); 305 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 306 parms.Add("columns", Model.GridRowColumnCount); 307 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 308 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 309 310 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 311 @foreach (string imageFormat in supportedImageFormats) { 312 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 313 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 314 } 315 } 316 317 @foreach (string videoFormat in supportedVideoFormats) { 318 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 319 {@RenderVideoPlayer(asset, "modal")} 320 } 321 } 322 </div> 323 324 modalAssetNumber++; 325 } 326 } 327 imagesShownCounter++; 328 } 329 <button class="carousel-control-prev" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 330 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 331 <span class="visually-hidden">@Translate("Previous")</span> 332 </button> 333 <button class="carousel-control-next" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 334 <span class="carousel-control-next-icon" aria-hidden="true"></span> 335 <span class="visually-hidden">@Translate("Next")</span> 336 </button> 337 </div> 338 </div> 339 </div> 340 </div> 341 </div> 342 </div> 343 } else if (Pageview.IsVisualEditorMode) { 344 RatioSettings ratioSettings = GetRatioSettings("desktop"); 345 346 <div class="h-100 @theme"> 347 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 348 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 349 </div> 350 </div> 351 } 352 } 353 354 @helper RenderAsset(MediaViewModel asset, int assetNumber, int totalAssets, string size = "desktop") { 355 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 356 string assetValue = asset.Value; 357 358 <div class="h-100 @(theme)"> 359 @foreach (string format in supportedImageFormats) { //Images 360 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 361 {@RenderImage(asset, assetNumber, totalAssets, size)} 362 } 363 } 364 @foreach (string format in supportedVideoFormats) { //Videos 365 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 366 if (Model.Item.GetString("OpenVideoInModal") == "true") { 367 {@RenderVideoScreendump(asset, assetNumber, size)} 368 } else { 369 {@RenderVideoPlayer(asset, size)} 370 } 371 } 372 } 373 @foreach (string format in supportedDocumentFormats) { //Documents 374 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 375 {@RenderDocument(asset, assetNumber, size)} 376 } 377 } 378 </div> 379 } 380 381 @helper RenderImage(MediaViewModel asset, int number, int totalAssets, string size = "desktop") { 382 string productName = product.Name; 383 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 384 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 385 386 RatioSettings ratioSettings = GetRatioSettings(size); 387 388 var parms = new Dictionary<string, object>(); 389 parms.Add("alt", productName); 390 parms.Add("itemprop", "image"); 391 if (totalAssets > 1) { 392 parms.Add("columns", 2); 393 } else { 394 parms.Add("columns", 1); 395 } 396 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 397 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 398 399 if (!string.IsNullOrEmpty(asset.DisplayName)) { 400 parms.Add("title", asset.DisplayName); 401 } 402 403 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 404 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 405 } else { 406 parms.Add("cssClass", "mw-100 mh-100"); 407 } 408 409 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 410 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@number"> 411 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 412 </div> 413 </a> 414 } 415 416 @helper RenderVideoScreendump(MediaViewModel asset, int number, string size = "desktop") { 417 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 418 419 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 420 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 421 videoScreendumpPath = videoScreendumpPath.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || videoScreendumpPath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) != 0 ? "https://img.youtube.com/vi/" + videoId + "/maxresdefault.jpg" : videoScreendumpPath; 422 423 string vimeoJsClass = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 424 videoScreendumpPath = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : videoScreendumpPath; 425 426 string productName = product.Name; 427 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 428 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 429 430 RatioSettings ratioSettings = GetRatioSettings(size); 431 432 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 433 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@number"> 434 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 435 @if (videoScreendumpPath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) 436 { 437 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;" onload="CheckIfVideoThumbnailExist(this)"> 438 } 439 else 440 { 441 string videoType = Path.GetExtension(asset.Value).ToLower(); 442 string videoPathEncoded = System.Uri.EscapeDataString(asset.Value); 443 444 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 445 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 446 </video> 447 } 448 </div> 449 </div> 450 451 <script> 452 function CheckIfVideoThumbnailExist(image) { 453 if (image.width == 120) { 454 const lowQualityImage = "https://img.youtube.com/vi/@(videoId)/hqdefault.jpg" 455 image.src = lowQualityImage; 456 } 457 } 458 </script> 459 } 460 461 @helper RenderVideoPlayer(MediaViewModel asset, string size = "desktop") { 462 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 463 string assetValue = asset.Value; 464 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 465 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 466 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 467 type = assetValue.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf(".webm", StringComparison.OrdinalIgnoreCase) >= 0 ? "selfhosted" : type; 468 469 string openInModal = Model.Item.GetString("OpenVideoInModal"); 470 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 471 472 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 473 <span class="visually-hidden" itemprop="name">@assetName</span> 474 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 475 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 476 @if (type != "selfhosted") { 477 <div 478 id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size" 479 class="plyr__video-embed" 480 data-plyr-provider="@(type)" 481 data-plyr-embed-id="@videoId" 482 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 483 </div> 484 485 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 486 487 <script type="module"> 488 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size', { 489 type: 'video', 490 youtube: { 491 noCookie: true, 492 showinfo: 0 493 }, 494 fullscreen: { 495 enabled: true, 496 iosNative: true, 497 } 498 }); 499 500 @if (autoPlay && openInModal == "false") { 501 <text> 502 player.config.autoplay = true; 503 player.config.muted = true; 504 player.config.volume = 0; 505 player.media.loop = true; 506 507 player.on('ready', function() { 508 if (player.config.autoplay === true) { 509 player.media.play(); 510 } 511 }); 512 </text> 513 } 514 515 @if (openInModal == "true") { 516 <text> 517 var productDetailsGalleryModal = document.querySelector('#modal_@Model.ID') 518 productDetailsGalleryModal.addEventListener('hidden.bs.modal', function (event) { 519 player.media.pause(); 520 }) 521 </text> 522 } 523 </script> 524 } else { 525 string autoPlayAttributes = (autoPlay && openInModal == "false") ? "loop autoplay muted playsinline" : ""; 526 string videoType = Path.GetExtension(assetValue).ToLower(); 527 string videoPathEncoded = System.Uri.EscapeDataString(assetValue); 528 529 <video preload="auto" @autoPlayAttributes class="h-100 w-100" style="object-fit: cover;" controls> 530 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 531 </video> 532 } 533 </div> 534 } 535 536 @helper RenderDocument(MediaViewModel asset, int number, string size = "desktop") { 537 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 538 539 string productName = product.Name; 540 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 541 string imageLinkPath = imagePath; 542 543 RatioSettings ratioSettings = GetRatioSettings(size); 544 545 var parms = new Dictionary<string, object>(); 546 parms.Add("alt", productName); 547 parms.Add("itemprop", "image"); 548 parms.Add("fullwidth", true); 549 parms.Add("columns", Model.GridRowColumnCount); 550 if (!string.IsNullOrEmpty(asset.DisplayName)) { 551 parms.Add("title", asset.DisplayName); 552 } 553 554 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 555 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 556 } else { 557 parms.Add("cssClass", "mw-100 mh-100"); 558 } 559 560 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 561 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 562 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 563 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { 564 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 565 } else { 566 567 } 568 </div> 569 </a> 570 } 571 572
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGalleryCustom.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_2e17544f1d124e409c1a6ed257b6f616.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGalleryCustom.cshtml:line 85
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 6 @functions { 7 public ProductViewModel product { get; set; } = new ProductViewModel(); 8 public string galleryLayout { get; set; } 9 public string[] supportedImageFormats { get; set; } 10 public string[] supportedVideoFormats { get; set; } 11 public string[] supportedDocumentFormats { get; set; } 12 public string[] allSupportedFormats { get; set; } 13 14 public class RatioSettings { 15 public string Ratio { get; set; } 16 public string CssClass { get; set; } 17 public string CssVariable { get; set; } 18 public string Fill { get; set; } 19 } 20 21 public RatioSettings GetRatioSettings(string size = "desktop") { 22 var ratioSettings = new RatioSettings(); 23 24 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 25 ratio = ratio != "0" ? ratio : ""; 26 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 27 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 28 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 29 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 30 31 ratioSettings.Ratio = ratio; 32 ratioSettings.CssClass = cssClass; 33 ratioSettings.CssVariable = cssVariable; 34 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 35 36 return ratioSettings; 37 } 38 39 public string GetColumnClass(int total, int assetNumber) { 40 string colClass = total > 1 ? "g-col-lg-6" : "g-col-12"; 41 colClass = galleryLayout == "full-first" && assetNumber == 0 ? "g-col-12" : colClass; 42 colClass = galleryLayout == "full-last" && assetNumber == (total - 1) ? "g-col-12" : colClass; 43 colClass = galleryLayout == "advanced-grid" && assetNumber > 1 ? "g-col-4" : colClass; 44 45 colClass = galleryLayout == "advanced-grid" && total == 1 ? "g-col-12" : colClass; 46 colClass = galleryLayout == "advanced-grid" && total == 3 && assetNumber == 2 ? "g-col-12" : colClass; 47 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 2 ? "g-col-6" : colClass; 48 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 3 ? "g-col-6" : colClass; 49 colClass = galleryLayout == "advanced-grid" && total == 6 && assetNumber == 5 ? "g-col-12" : colClass; 50 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 5 ? "g-col-6" : colClass; 51 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 6 ? "g-col-6" : colClass; 52 colClass = galleryLayout == "advanced-grid" && total == 9 && assetNumber == 8 ? "g-col-12" : colClass; 53 54 return colClass; 55 } 56 57 public string GetArrowsColor() 58 { 59 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 60 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 61 return arrowsColor; 62 } 63 } 64 65 @if (product is object) 66 { 67 @* Get the product data *@ 68 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 69 { 70 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 71 } 72 73 @* Supported formats *@ 74 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 75 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 76 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 77 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 78 79 @* Collect the assets *@ 80 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 81 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 82 83 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 84 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 85 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 86 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 87 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 88 assetsList = assetsList.Union(assetsImages); 89 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 90 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 91 92 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 93 94 int totalAssets = 0; 95 foreach (MediaViewModel asset in assetsList) { 96 var assetValue = asset.Value; 97 foreach (string format in allSupportedFormats) { 98 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 99 totalAssets++; 100 } 101 } 102 } 103 104 if (totalAssets == 0) 105 { 106 if (defaultImageFallback) { 107 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 108 totalAssets = 1; 109 } else { 110 assetsList = new List<MediaViewModel>(){ }; 111 totalAssets = 0; 112 } 113 } 114 115 @* Layout settings *@ 116 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 117 spacing = spacing == "none" ? "gap-0" : spacing; 118 spacing = spacing == "small" ? "gap-3" : spacing; 119 spacing = spacing == "large" ? "gap-4" : spacing; 120 121 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 122 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 123 124 var badgeParms = new Dictionary<string, object>(); 125 badgeParms.Add("size", "h5"); 126 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 127 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 128 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 129 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 130 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 131 132 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 133 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 134 DateTime createdDate = product.Created.Value; 135 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 136 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 137 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 138 139 140 @* Get assets from selected categories or get all assets *@ 141 if (totalAssets != 0 && assetsList.Count() != 0) 142 { 143 int desktopAssetNumber = 0; 144 int mobileAssetNumber = 0; 145 int mobileAssetThumbnailNumber = 0; 146 int modalAssetNumber = 0; 147 int numberOfImages = int.Parse(Model.Item.GetRawValueString("NumberOfImagesShown")); 148 int imagesShownCounter = 0; 149 150 @* Desktop: Show the gallery on large screens *@ 151 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 152 <div class="grid @spacing"> 153 @foreach (MediaViewModel asset in assetsList) 154 { 155 if (imagesShownCounter == numberOfImages) 156 { 157 break; 158 } 159 var assetName = asset.Value; 160 foreach (string format in allSupportedFormats) 161 { 162 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 163 { 164 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 165 @{@RenderAsset(asset, desktopAssetNumber, totalAssets)} 166 </div> 167 desktopAssetNumber++; 168 } 169 } 170 imagesShownCounter++; 171 } 172 </div> 173 174 @if (showBadges) 175 { 176 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 177 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 178 </div> 179 } 180 </div> 181 182 @* Mobile: Show the thumbs on small screens *@ 183 imagesShownCounter = 0; 184 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 185 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 186 <div class="carousel-inner h-100"> 187 @foreach (MediaViewModel asset in assetsList) 188 { 189 if (imagesShownCounter == numberOfImages) 190 { 191 break; 192 } 193 var assetValue = asset.Value; 194 foreach (string format in allSupportedFormats) 195 { 196 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 197 { 198 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 199 200 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 201 @{@RenderAsset(asset, mobileAssetNumber, totalAssets, "mobile")} 202 </div> 203 mobileAssetNumber++; 204 } 205 } 206 imagesShownCounter++; 207 } 208 </div> 209 </div> 210 211 @if (totalAssets > 1) 212 { 213 imagesShownCounter = 0; 214 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 215 @foreach (MediaViewModel asset in assetsList) 216 { 217 if (imagesShownCounter == numberOfImages) 218 { 219 break; 220 } 221 var assetValue = asset.Value; 222 foreach (string format in allSupportedFormats) 223 { 224 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 225 { 226 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); 227 imagePath = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/default.jpg" : imagePath; 228 string imagePathThumb = imagePath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) < 0 ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=160&format=webp" : imagePath; 229 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 230 231 string videoId = assetValue.Substring(assetValue.LastIndexOf('/') + 1); 232 string vimeoJsClass = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 233 234 string productName = product.Name; 235 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 236 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 237 238 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 239 @foreach (string videoFormat in supportedVideoFormats) 240 { 241 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 242 { 243 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 244 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 245 </div> 246 } 247 } 248 @if (imagePathThumb.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) 249 { 250 251 <img src="@(imagePathThumb)" class="p-1 @vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;" alt="@productName" @assetTitle> 252 253 } 254 else 255 { 256 string videoType = Path.GetExtension(asset.Value).ToLower(); 257 string videoPathEncoded = System.Uri.EscapeDataString(assetValue); 258 259 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 260 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 261 </video> 262 } 263 </div> 264 265 mobileAssetThumbnailNumber++; 266 } 267 } 268 imagesShownCounter++; 269 } 270 </div> 271 } 272 273 @if (showBadges) 274 { 275 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 276 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 277 </div> 278 } 279 </div> 280 281 @* Modal with slides *@ 282 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 283 <div class="modal-dialog modal-dialog-centered modal-xl"> 284 <div class="modal-content"> 285 <div class="modal-header visually-hidden"> 286 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 287 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 288 </div> 289 <div class="modal-body p-2 p-lg-3 h-100"> 290 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 291 <div class="carousel-inner h-100 @theme"> 292 @{imagesShownCounter = 0;} 293 @foreach (MediaViewModel asset in assetsList) { 294 if (imagesShownCounter == numberOfImages) 295 { 296 break; 297 } 298 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 299 foreach (string format in allSupportedFormats) { 300 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 301 string imagePath = assetValue; 302 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 303 304 var parms = new Dictionary<string, object>(); 305 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 306 parms.Add("columns", Model.GridRowColumnCount); 307 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 308 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 309 310 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 311 @foreach (string imageFormat in supportedImageFormats) { 312 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 313 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 314 } 315 } 316 317 @foreach (string videoFormat in supportedVideoFormats) { 318 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 319 {@RenderVideoPlayer(asset, "modal")} 320 } 321 } 322 </div> 323 324 modalAssetNumber++; 325 } 326 } 327 imagesShownCounter++; 328 } 329 <button class="carousel-control-prev" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 330 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 331 <span class="visually-hidden">@Translate("Previous")</span> 332 </button> 333 <button class="carousel-control-next" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 334 <span class="carousel-control-next-icon" aria-hidden="true"></span> 335 <span class="visually-hidden">@Translate("Next")</span> 336 </button> 337 </div> 338 </div> 339 </div> 340 </div> 341 </div> 342 </div> 343 } else if (Pageview.IsVisualEditorMode) { 344 RatioSettings ratioSettings = GetRatioSettings("desktop"); 345 346 <div class="h-100 @theme"> 347 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 348 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 349 </div> 350 </div> 351 } 352 } 353 354 @helper RenderAsset(MediaViewModel asset, int assetNumber, int totalAssets, string size = "desktop") { 355 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 356 string assetValue = asset.Value; 357 358 <div class="h-100 @(theme)"> 359 @foreach (string format in supportedImageFormats) { //Images 360 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 361 {@RenderImage(asset, assetNumber, totalAssets, size)} 362 } 363 } 364 @foreach (string format in supportedVideoFormats) { //Videos 365 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 366 if (Model.Item.GetString("OpenVideoInModal") == "true") { 367 {@RenderVideoScreendump(asset, assetNumber, size)} 368 } else { 369 {@RenderVideoPlayer(asset, size)} 370 } 371 } 372 } 373 @foreach (string format in supportedDocumentFormats) { //Documents 374 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 375 {@RenderDocument(asset, assetNumber, size)} 376 } 377 } 378 </div> 379 } 380 381 @helper RenderImage(MediaViewModel asset, int number, int totalAssets, string size = "desktop") { 382 string productName = product.Name; 383 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 384 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 385 386 RatioSettings ratioSettings = GetRatioSettings(size); 387 388 var parms = new Dictionary<string, object>(); 389 parms.Add("alt", productName); 390 parms.Add("itemprop", "image"); 391 if (totalAssets > 1) { 392 parms.Add("columns", 2); 393 } else { 394 parms.Add("columns", 1); 395 } 396 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 397 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 398 399 if (!string.IsNullOrEmpty(asset.DisplayName)) { 400 parms.Add("title", asset.DisplayName); 401 } 402 403 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 404 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 405 } else { 406 parms.Add("cssClass", "mw-100 mh-100"); 407 } 408 409 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 410 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@number"> 411 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 412 </div> 413 </a> 414 } 415 416 @helper RenderVideoScreendump(MediaViewModel asset, int number, string size = "desktop") { 417 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 418 419 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 420 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 421 videoScreendumpPath = videoScreendumpPath.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || videoScreendumpPath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) != 0 ? "https://img.youtube.com/vi/" + videoId + "/maxresdefault.jpg" : videoScreendumpPath; 422 423 string vimeoJsClass = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 424 videoScreendumpPath = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : videoScreendumpPath; 425 426 string productName = product.Name; 427 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 428 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 429 430 RatioSettings ratioSettings = GetRatioSettings(size); 431 432 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 433 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@number"> 434 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 435 @if (videoScreendumpPath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) 436 { 437 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;" onload="CheckIfVideoThumbnailExist(this)"> 438 } 439 else 440 { 441 string videoType = Path.GetExtension(asset.Value).ToLower(); 442 string videoPathEncoded = System.Uri.EscapeDataString(asset.Value); 443 444 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 445 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 446 </video> 447 } 448 </div> 449 </div> 450 451 <script> 452 function CheckIfVideoThumbnailExist(image) { 453 if (image.width == 120) { 454 const lowQualityImage = "https://img.youtube.com/vi/@(videoId)/hqdefault.jpg" 455 image.src = lowQualityImage; 456 } 457 } 458 </script> 459 } 460 461 @helper RenderVideoPlayer(MediaViewModel asset, string size = "desktop") { 462 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 463 string assetValue = asset.Value; 464 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 465 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 466 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 467 type = assetValue.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf(".webm", StringComparison.OrdinalIgnoreCase) >= 0 ? "selfhosted" : type; 468 469 string openInModal = Model.Item.GetString("OpenVideoInModal"); 470 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 471 472 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 473 <span class="visually-hidden" itemprop="name">@assetName</span> 474 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 475 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 476 @if (type != "selfhosted") { 477 <div 478 id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size" 479 class="plyr__video-embed" 480 data-plyr-provider="@(type)" 481 data-plyr-embed-id="@videoId" 482 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 483 </div> 484 485 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 486 487 <script type="module"> 488 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size', { 489 type: 'video', 490 youtube: { 491 noCookie: true, 492 showinfo: 0 493 }, 494 fullscreen: { 495 enabled: true, 496 iosNative: true, 497 } 498 }); 499 500 @if (autoPlay && openInModal == "false") { 501 <text> 502 player.config.autoplay = true; 503 player.config.muted = true; 504 player.config.volume = 0; 505 player.media.loop = true; 506 507 player.on('ready', function() { 508 if (player.config.autoplay === true) { 509 player.media.play(); 510 } 511 }); 512 </text> 513 } 514 515 @if (openInModal == "true") { 516 <text> 517 var productDetailsGalleryModal = document.querySelector('#modal_@Model.ID') 518 productDetailsGalleryModal.addEventListener('hidden.bs.modal', function (event) { 519 player.media.pause(); 520 }) 521 </text> 522 } 523 </script> 524 } else { 525 string autoPlayAttributes = (autoPlay && openInModal == "false") ? "loop autoplay muted playsinline" : ""; 526 string videoType = Path.GetExtension(assetValue).ToLower(); 527 string videoPathEncoded = System.Uri.EscapeDataString(assetValue); 528 529 <video preload="auto" @autoPlayAttributes class="h-100 w-100" style="object-fit: cover;" controls> 530 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 531 </video> 532 } 533 </div> 534 } 535 536 @helper RenderDocument(MediaViewModel asset, int number, string size = "desktop") { 537 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 538 539 string productName = product.Name; 540 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 541 string imageLinkPath = imagePath; 542 543 RatioSettings ratioSettings = GetRatioSettings(size); 544 545 var parms = new Dictionary<string, object>(); 546 parms.Add("alt", productName); 547 parms.Add("itemprop", "image"); 548 parms.Add("fullwidth", true); 549 parms.Add("columns", Model.GridRowColumnCount); 550 if (!string.IsNullOrEmpty(asset.DisplayName)) { 551 parms.Add("title", asset.DisplayName); 552 } 553 554 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 555 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 556 } else { 557 parms.Add("cssClass", "mw-100 mh-100"); 558 } 559 560 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 561 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 562 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 563 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { 564 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 565 } else { 566 567 } 568 </div> 569 </a> 570 } 571 572
Error executing template "Designs/Swift/Paragraph/Swift_ProductBadges.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_b3e9b1e8ec8a4ffcac3cd503de2733ff.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductBadges.cshtml:line 72
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Frontend 3 @using Dynamicweb.Content 4 @using Dynamicweb.Ecommerce.ProductCatalog 5 6 @{ 7 ProductViewModel product = null; 8 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 9 { 10 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 11 } 12 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 13 { 14 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 15 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 16 17 if (productList?.Products is object) 18 { 19 product = productList.Products[0]; 20 } 21 } 22 } 23 24 @if (product is object) { 25 var badgeParms = new Dictionary<string, object>(); 26 badgeParms.Add("size", "h7"); 27 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 28 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 29 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 30 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 31 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 32 33 string badgeSize = Model.Item.GetRawValueString("BadgeSize", "fs-2"); 34 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 35 horizontalAlign = horizontalAlign == "center" ? "text-center" : horizontalAlign; 36 horizontalAlign = horizontalAlign == "end" ? "text-end" : horizontalAlign; 37 38 Dictionary<string, ParagraphInfoViewModel> badgeConfigurations; 39 List<string> campaignBadgesValues = Model.Item.GetRawValueString("CampaignBadges") != null ? Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList() : new List<string>(); 40 41 if (Dynamicweb.Context.Current.Items.Contains("badgeConfigurations")) 42 { 43 badgeConfigurations = (Dictionary<string, ParagraphInfoViewModel>)Dynamicweb.Context.Current.Items["badgeConfigurations"]; 44 } 45 else 46 { 47 var badgesPage = Pageview.AreaSettings.GetLink("EcommerceBadgesPage") != null ? Pageview.AreaSettings.GetLink("EcommerceBadgesPage").PageId : 0; 48 var allBadges = badgesPage != 0 ? Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(badgesPage) : null; 49 50 badgeConfigurations = new Dictionary<string, ParagraphInfoViewModel>(); 51 foreach (Paragraph badge in allBadges) 52 { 53 var paragraphviewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreateParagraphInfoViewModel(badge); 54 string cssClassName = paragraphviewModel.Item.GetString("CssClassName").Trim().ToLower(); 55 if (!badgeConfigurations.ContainsKey(cssClassName)) 56 { 57 badgeConfigurations.Add(cssClassName, paragraphviewModel); 58 } 59 } 60 Dynamicweb.Context.Current.Items.Add("badgeConfigurations", badgeConfigurations); 61 } 62 63 int badgesCount = 0; 64 if (badgeConfigurations.Any()) 65 { 66 foreach (string campaign in campaignBadgesValues) 67 { 68 if (!string.IsNullOrEmpty(campaign)) 69 { 70 71 FieldValueViewModel availableCampaignsObject; 72 product.ProductFields.TryGetValue("Campaign", out availableCampaignsObject); 73 74 if (availableCampaignsObject != null) 75 { 76 string campaignType = string.Empty; 77 78 if (badgeConfigurations.ContainsKey(campaign)) 79 { 80 ParagraphInfoViewModel paragraphviewModel; 81 if (badgeConfigurations.TryGetValue(campaign, out paragraphviewModel)) 82 { 83 campaignType = paragraphviewModel.Item.GetRawValueString("CampaignType"); 84 } 85 } 86 87 List<FieldOptionValueViewModel> availableCampaigns = (List<FieldOptionValueViewModel>)availableCampaignsObject.Value; 88 89 foreach (FieldOptionValueViewModel availableOption in availableCampaigns) 90 { 91 if (campaignType == availableOption.Value) 92 { 93 badgesCount++; 94 break; 95 } 96 } 97 } 98 } 99 } 100 } 101 102 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 103 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 104 DateTime createdDate = product.Created.Value; 105 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 106 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 107 showBadges = (!string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) && badgesCount != 0) ? true : showBadges; 108 109 if (showBadges) 110 { 111 <div class="@badgeSize @horizontalAlign item_@Model.Item.SystemName.ToLower()"> 112 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 113 </div> 114 } 115 else if (Pageview.IsVisualEditorMode) 116 { 117 <span class="badge bg-success text-light rounded-0">@Translate("Badge example")</span> 118 } 119 } 120 else if (Pageview.IsVisualEditorMode) 121 { 122 <span class="badge bg-success text-light rounded-0">@Translate("Badge example")</span> 123 } 124 125

Error executing template "Designs/Swift/Paragraph/Swift_ProductSpecification.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_6187b3f3c5bd4a7d9c2a0fa23baafca9.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification.cshtml:line 29
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 4 @{ 5 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 11 { 12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 14 15 if (productList?.Products is object) 16 { 17 product = productList.Products[0]; 18 } 19 } 20 } 21 22 @if (product is object) 23 { 24 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetRawValueString("DisplayGroups").Split(',').ToList(); 25 List<CategoryFieldViewModel> displayGroups = new List<CategoryFieldViewModel>(); 26 27 foreach (var selection in selectedDisplayGroupIds) 28 { 29 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 30 { 31 if (selection == group.Id) 32 { 33 int fieldsWithNoValueOrZero = 0; 34 35 foreach (var field in group.Fields) 36 { 37 if (string.IsNullOrEmpty(field.Value.Value.ToString())) 38 { 39 fieldsWithNoValueOrZero++; 40 } 41 } 42 43 if (fieldsWithNoValueOrZero != group.Fields.Count) 44 { 45 displayGroups.Add(group); 46 } 47 } 48 } 49 } 50 51 bool showProductFields = Model.Item.GetBoolean("ProductFields"); 52 53 bool hideTitle = Model.Item.GetBoolean("HideTitle"); 54 55 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 56 57 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4"); 58 59 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 60 contentPadding = contentPadding == "none" ? string.Empty : contentPadding; 61 contentPadding = contentPadding == "small" ? " p-2 p-md-3" : contentPadding; 62 contentPadding = contentPadding == "large" ? " p-4 p-md-5" : contentPadding; 63 64 string layout = Model.Item.GetRawValueString("Layout", "list"); 65 string size = Model.Item.GetRawValueString("Size", "full"); 66 string gaps = size == "full" ? " gap-4" : " gap-2"; 67 68 69 if (Pageview.IsVisualEditorMode && displayGroups.Count() == 0) 70 { 71 product.ProductFields.Clear(); 72 product.ProductFields.Add(Translate("Width"), new FieldValueViewModel { Name = Translate("Width"), Value = "99cm" }); 73 product.ProductFields.Add(Translate("Height"), new FieldValueViewModel { Name = Translate("Height"), Value = "195cm" }); 74 showProductFields = true; 75 } 76 77 if (layout == "commas") 78 { 79 gaps = size == "full" ? " gap-4" : " gap-2"; 80 81 } 82 83 <div class="h-100@(theme)@(contentPadding) item_@Model.Item.SystemName.ToLower()"> 84 <div class="grid@(gaps)"> 85 @if ((product.ProductFields != null && Model.Item.GetBoolean("ProductFields")) || (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) || (displayGroups.Count != 0)) 86 { 87 if (!hideTitle) 88 { 89 <h2 class="g-col-12 @titleFontSize">@Model.Item.GetString("Title")</h2> 90 } 91 } 92 93 @if (displayGroups.Count != 0) 94 { 95 if (layout != "accordion") 96 { 97 foreach (var group in displayGroups) 98 { 99 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 100 101 if (!hideHeader) 102 { 103 <h4 class="g-col-12 h4 mb-0">@group.Name</h4> 104 } 105 106 { @RenderFieldsFromList(group.Fields, layout) } 107 108 } 109 } 110 else 111 { 112 <div class="g-col-12"> 113 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 114 @foreach (var group in displayGroups) 115 { 116 <div class="accordion-item"> 117 <h2 class="accordion-header" id="SpecificationHeading_@group.Id"> 118 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Id"> 119 @group.Name 120 </button> 121 </h2> 122 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID"> 123 <div class="accordion-body"> 124 @{ @RenderFieldsFromList(group.Fields, "list") } 125 </div> 126 </div> 127 </div> 128 } 129 </div> 130 </div> 131 } 132 } 133 134 @if (product.ProductFields != null && showProductFields) 135 { 136 if (product.ProductFields.Count > 0) 137 { 138 if (layout != "accordion") 139 { 140 {@RenderFieldsFromList(product.ProductFields, layout) } 141 } 142 else 143 { 144 <div class="g-col-12"> 145 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 146 <div class="accordion-item"> 147 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID"> 148 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Model.ID" aria-expanded="false" aria-controls="SpecificationItem_@Model.ID"> 149 @Translate("Specifications") 150 </button> 151 </h2> 152 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID"> 153 <div class="accordion-body"> 154 @{ @RenderFieldsFromList(product.ProductFields, "List") } 155 </div> 156 </div> 157 </div> 158 </div> 159 </div> 160 } 161 } 162 } 163 164 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) 165 { 166 if (product.ProductCategories.Count > 0) 167 { 168 if (layout != "accordion") 169 { 170 foreach (var group in product.ProductCategories) 171 { 172 CategoryFieldViewModel category = group.Value; 173 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 174 175 if (!hideHeader) 176 { 177 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4> 178 } 179 180 { @RenderFieldsFromList(category.Fields, layout) } 181 } 182 } 183 else 184 { 185 <div class="g-col-12"> 186 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 187 @foreach (var group in product.ProductCategories) 188 { 189 CategoryFieldViewModel category = group.Value; 190 191 <div class="accordion-item"> 192 <h2 class="accordion-header" id="SpecificationHeading_@group.Value.Id"> 193 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Value.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Value.Id"> 194 @group.Value.Name 195 </button> 196 </h2> 197 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID"> 198 <div class="accordion-body"> 199 @{ @RenderFieldsFromList(category.Fields, "list") } 200 </div> 201 </div> 202 </div> 203 } 204 </div> 205 </div> 206 } 207 } 208 } 209 </div> 210 </div> 211 } 212 else if (Pageview.IsVisualEditorMode) 213 { 214 <div class="alert alert-warning m-0">@Translate("No products available")</div> 215 } 216 217 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 218 { 219 string size = Model.Item.GetRawValueString("Size", "full"); 220 string gaps = size != "full" ? " gap-1" : string.Empty; 221 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 222 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 223 224 if (layout == "columns") 225 { 226 <div class="g-col-12"> 227 <div class="grid@(gaps)"> 228 @foreach (var field in fields) 229 { 230 {@RenderField(field.Value, layout)} 231 } 232 </div> 233 </div> 234 } 235 if (layout == "list") 236 { 237 <div class="g-col-12"> 238 <dl class="grid@(gaps)"> 239 @foreach (var field in fields) 240 { 241 {@RenderField(field.Value, layout)} 242 } 243 </dl> 244 </div> 245 } 246 if (layout == "table") 247 { 248 string tableSize = size == "full" ? "" : " table-sm"; 249 <div class="g-col-12 table-responsive"> 250 <table class="table @(tableSize)"> 251 @foreach (var field in fields) 252 { 253 {@RenderField(field.Value, layout)} 254 } 255 </table> 256 </div> 257 } 258 if (layout == "bullets") 259 { 260 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 261 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 262 <div class="g-col-12"> 263 <ul class="@listSize" @listStyle> 264 @foreach (var field in fields) 265 { 266 {@RenderField(field.Value, layout)} 267 } 268 </ul> 269 </div> 270 } 271 if (layout == "commas") 272 { 273 List<string> featuresList = new List<string>(); 274 275 foreach (var field in fields) 276 { 277 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 278 279 if (field.Value?.Value != null) 280 { 281 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 282 { 283 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 284 285 //Hack to support field type providers with a single value 286 if (values.FirstOrDefault() != null) 287 { 288 firstListItemValue = values.FirstOrDefault().Value; 289 } 290 } 291 } 292 293 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 294 { 295 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 296 { 297 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 298 { 299 List<string> options = new List<string>(); 300 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 301 { 302 if (!string.IsNullOrWhiteSpace(option.Value)) 303 { 304 if ((option.Value.ToString().Contains("#") || option.Value.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 305 { 306 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 307 if (option.Value.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 308 { 309 colorSpan = $"<img src=\"/Admin/Public/GetImage.ashx?width=16&height=16&image={option.Value}\">"; 310 } 311 options.Add(colorSpan); 312 } 313 else if (!string.IsNullOrEmpty(option.Value)) 314 { 315 options.Add(option.Name); 316 } 317 } 318 } 319 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 320 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 321 { 322 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 323 } 324 325 if (!string.IsNullOrEmpty(optionsString)) 326 { 327 if (!hideFieldLabels) 328 { 329 featuresList.Add(field.Value.Name + ": " + optionsString); 330 } 331 else 332 { 333 featuresList.Add(optionsString); 334 } 335 } 336 } 337 else 338 { 339 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 340 { 341 if ((field.Value.Value.ToString().Contains("#") || field.Value.Value.ToString().StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 342 { 343 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 344 if(field.Value.Value.ToString().StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 345 { 346 colorSpan = $"<img src=\"/Admin/Public/GetImage.ashx?width=16&height=16&image={field.Value.Value}\">"; 347 } 348 349 if (!hideFieldLabels) 350 { 351 featuresList.Add(field.Value.Name + ": " + colorSpan); 352 } 353 else 354 { 355 featuresList.Add(colorSpan); 356 } 357 } 358 else 359 { 360 if (!hideFieldLabels) 361 { 362 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 363 } 364 else 365 { 366 featuresList.Add(field.Value.Value.ToString()); 367 } 368 } 369 } 370 } 371 } 372 } 373 } 374 375 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 376 377 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 378 } 379 } 380 381 @helper RenderField(FieldValueViewModel field, string layout) 382 { 383 string size = Model.Item.GetRawValueString("Size", "full"); 384 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 385 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 386 bool noValues = false; 387 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 388 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 389 390 if (!string.IsNullOrEmpty(fieldValue)) 391 { 392 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 393 { 394 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 395 noValues = values.Count > 0 ? false : true; 396 397 //Hack to support field type providers with a single value 398 if (values.FirstOrDefault() != null) 399 { 400 firstListItemValue = values.FirstOrDefault().Value; 401 } 402 } 403 } 404 405 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 406 { 407 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 408 { 409 if (layout == "columns") 410 { 411 <div class="grid g-col-6 g-col-lg-4 gap-1"> 412 @if (!hideFieldLabels) 413 { 414 <dt class="g-col-12 g-col-lg-4">@field.Name</dt> 415 } 416 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 417 @{ @RenderFieldValue(field) } 418 </dd> 419 </div> 420 } 421 if (layout == "list") 422 { 423 if (!hideFieldLabels) 424 { 425 <dt class="g-col-4">@field.Name</dt> 426 } 427 <dd class="g-col-8 mb-0 text-break"> 428 @{ @RenderFieldValue(field) } 429 </dd> 430 } 431 if (layout == "table") 432 { 433 <tr> 434 @if (!hideFieldLabels) 435 { 436 <td style="width: 35%;" scope="row">@field.Name</td> 437 } 438 <td class="text-break"> 439 @{ @RenderFieldValue(field) } 440 </td> 441 </tr> 442 } 443 if (layout == "bullets") 444 { 445 <li> 446 @if (!hideFieldLabels) 447 { 448 <strong>@field.Name</strong> 449 } 450 <span> 451 @{ @RenderFieldValue(field) } 452 </span> 453 </li> 454 } 455 } 456 } 457 } 458 459 @helper RenderFieldValue(FieldValueViewModel field) 460 { 461 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 462 463 bool isLink = field?.Type == "Link"; 464 bool isColor = false; 465 bool isBrandName = field?.SystemName == "Brand_name"; 466 467 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 468 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 469 470 471 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 472 { 473 int valueCount = 0; 474 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 475 int totalValues = values.Count; 476 477 foreach (FieldOptionValueViewModel option in values) 478 { 479 if (!string.IsNullOrEmpty(option.Value)) 480 { 481 if (option.Value.StartsWith("#", StringComparison.OrdinalIgnoreCase) || option.Value.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 482 { 483 isColor = true; 484 } 485 } 486 487 if (!isColor) 488 { 489 @option.Name 490 } 491 else 492 { 493 if (option.Value.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 494 { 495 <img style="vertical-align: baseline;" src="/Admin/Public/GetImage.ashx?width=16&height=16&image=@option.Value" title="@option.Name"> 496 } 497 else 498 { 499 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 500 } 501 } 502 503 if (valueCount != totalValues && valueCount < (totalValues - 1)) 504 { 505 if (isColor) 506 { 507 <text> </text> 508 } 509 else 510 { 511 <text>, </text> 512 } 513 } 514 valueCount++; 515 } 516 } 517 else 518 { 519 if (!string.IsNullOrEmpty(fieldValue)) 520 { 521 if (fieldValue.StartsWith("#", StringComparison.OrdinalIgnoreCase) || fieldValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 522 { 523 isColor = true; 524 } 525 } 526 527 if (!isColor) 528 { 529 if (isLink) 530 { 531 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 532 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 533 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 534 535 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 536 } 537 else if (isBrandName) 538 { 539 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 540 <span itemprop="name">@fieldValue</span> 541 </span> 542 } 543 else 544 { 545 @fieldValue 546 } 547 548 } 549 else 550 { 551 if (fieldValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 552 { 553 <img src="/Admin/Public/GetImage.ashx?width=16&height=16&image=@fieldValue"> 554 } 555 else 556 { 557 <span class="colorbox-sm" style="background-color: @fieldValue"></span> 558 } 559 } 560 } 561 } 562
Error executing template "Designs/Swift/Paragraph/Swift_ProductSpecification.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_6187b3f3c5bd4a7d9c2a0fa23baafca9.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification.cshtml:line 29
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 4 @{ 5 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 11 { 12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 14 15 if (productList?.Products is object) 16 { 17 product = productList.Products[0]; 18 } 19 } 20 } 21 22 @if (product is object) 23 { 24 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetRawValueString("DisplayGroups").Split(',').ToList(); 25 List<CategoryFieldViewModel> displayGroups = new List<CategoryFieldViewModel>(); 26 27 foreach (var selection in selectedDisplayGroupIds) 28 { 29 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 30 { 31 if (selection == group.Id) 32 { 33 int fieldsWithNoValueOrZero = 0; 34 35 foreach (var field in group.Fields) 36 { 37 if (string.IsNullOrEmpty(field.Value.Value.ToString())) 38 { 39 fieldsWithNoValueOrZero++; 40 } 41 } 42 43 if (fieldsWithNoValueOrZero != group.Fields.Count) 44 { 45 displayGroups.Add(group); 46 } 47 } 48 } 49 } 50 51 bool showProductFields = Model.Item.GetBoolean("ProductFields"); 52 53 bool hideTitle = Model.Item.GetBoolean("HideTitle"); 54 55 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 56 57 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4"); 58 59 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 60 contentPadding = contentPadding == "none" ? string.Empty : contentPadding; 61 contentPadding = contentPadding == "small" ? " p-2 p-md-3" : contentPadding; 62 contentPadding = contentPadding == "large" ? " p-4 p-md-5" : contentPadding; 63 64 string layout = Model.Item.GetRawValueString("Layout", "list"); 65 string size = Model.Item.GetRawValueString("Size", "full"); 66 string gaps = size == "full" ? " gap-4" : " gap-2"; 67 68 69 if (Pageview.IsVisualEditorMode && displayGroups.Count() == 0) 70 { 71 product.ProductFields.Clear(); 72 product.ProductFields.Add(Translate("Width"), new FieldValueViewModel { Name = Translate("Width"), Value = "99cm" }); 73 product.ProductFields.Add(Translate("Height"), new FieldValueViewModel { Name = Translate("Height"), Value = "195cm" }); 74 showProductFields = true; 75 } 76 77 if (layout == "commas") 78 { 79 gaps = size == "full" ? " gap-4" : " gap-2"; 80 81 } 82 83 <div class="h-100@(theme)@(contentPadding) item_@Model.Item.SystemName.ToLower()"> 84 <div class="grid@(gaps)"> 85 @if ((product.ProductFields != null && Model.Item.GetBoolean("ProductFields")) || (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) || (displayGroups.Count != 0)) 86 { 87 if (!hideTitle) 88 { 89 <h2 class="g-col-12 @titleFontSize">@Model.Item.GetString("Title")</h2> 90 } 91 } 92 93 @if (displayGroups.Count != 0) 94 { 95 if (layout != "accordion") 96 { 97 foreach (var group in displayGroups) 98 { 99 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 100 101 if (!hideHeader) 102 { 103 <h4 class="g-col-12 h4 mb-0">@group.Name</h4> 104 } 105 106 { @RenderFieldsFromList(group.Fields, layout) } 107 108 } 109 } 110 else 111 { 112 <div class="g-col-12"> 113 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 114 @foreach (var group in displayGroups) 115 { 116 <div class="accordion-item"> 117 <h2 class="accordion-header" id="SpecificationHeading_@group.Id"> 118 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Id"> 119 @group.Name 120 </button> 121 </h2> 122 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID"> 123 <div class="accordion-body"> 124 @{ @RenderFieldsFromList(group.Fields, "list") } 125 </div> 126 </div> 127 </div> 128 } 129 </div> 130 </div> 131 } 132 } 133 134 @if (product.ProductFields != null && showProductFields) 135 { 136 if (product.ProductFields.Count > 0) 137 { 138 if (layout != "accordion") 139 { 140 {@RenderFieldsFromList(product.ProductFields, layout) } 141 } 142 else 143 { 144 <div class="g-col-12"> 145 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 146 <div class="accordion-item"> 147 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID"> 148 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Model.ID" aria-expanded="false" aria-controls="SpecificationItem_@Model.ID"> 149 @Translate("Specifications") 150 </button> 151 </h2> 152 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID"> 153 <div class="accordion-body"> 154 @{ @RenderFieldsFromList(product.ProductFields, "List") } 155 </div> 156 </div> 157 </div> 158 </div> 159 </div> 160 } 161 } 162 } 163 164 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) 165 { 166 if (product.ProductCategories.Count > 0) 167 { 168 if (layout != "accordion") 169 { 170 foreach (var group in product.ProductCategories) 171 { 172 CategoryFieldViewModel category = group.Value; 173 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 174 175 if (!hideHeader) 176 { 177 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4> 178 } 179 180 { @RenderFieldsFromList(category.Fields, layout) } 181 } 182 } 183 else 184 { 185 <div class="g-col-12"> 186 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 187 @foreach (var group in product.ProductCategories) 188 { 189 CategoryFieldViewModel category = group.Value; 190 191 <div class="accordion-item"> 192 <h2 class="accordion-header" id="SpecificationHeading_@group.Value.Id"> 193 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Value.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Value.Id"> 194 @group.Value.Name 195 </button> 196 </h2> 197 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID"> 198 <div class="accordion-body"> 199 @{ @RenderFieldsFromList(category.Fields, "list") } 200 </div> 201 </div> 202 </div> 203 } 204 </div> 205 </div> 206 } 207 } 208 } 209 </div> 210 </div> 211 } 212 else if (Pageview.IsVisualEditorMode) 213 { 214 <div class="alert alert-warning m-0">@Translate("No products available")</div> 215 } 216 217 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 218 { 219 string size = Model.Item.GetRawValueString("Size", "full"); 220 string gaps = size != "full" ? " gap-1" : string.Empty; 221 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 222 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 223 224 if (layout == "columns") 225 { 226 <div class="g-col-12"> 227 <div class="grid@(gaps)"> 228 @foreach (var field in fields) 229 { 230 {@RenderField(field.Value, layout)} 231 } 232 </div> 233 </div> 234 } 235 if (layout == "list") 236 { 237 <div class="g-col-12"> 238 <dl class="grid@(gaps)"> 239 @foreach (var field in fields) 240 { 241 {@RenderField(field.Value, layout)} 242 } 243 </dl> 244 </div> 245 } 246 if (layout == "table") 247 { 248 string tableSize = size == "full" ? "" : " table-sm"; 249 <div class="g-col-12 table-responsive"> 250 <table class="table @(tableSize)"> 251 @foreach (var field in fields) 252 { 253 {@RenderField(field.Value, layout)} 254 } 255 </table> 256 </div> 257 } 258 if (layout == "bullets") 259 { 260 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 261 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 262 <div class="g-col-12"> 263 <ul class="@listSize" @listStyle> 264 @foreach (var field in fields) 265 { 266 {@RenderField(field.Value, layout)} 267 } 268 </ul> 269 </div> 270 } 271 if (layout == "commas") 272 { 273 List<string> featuresList = new List<string>(); 274 275 foreach (var field in fields) 276 { 277 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 278 279 if (field.Value?.Value != null) 280 { 281 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 282 { 283 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 284 285 //Hack to support field type providers with a single value 286 if (values.FirstOrDefault() != null) 287 { 288 firstListItemValue = values.FirstOrDefault().Value; 289 } 290 } 291 } 292 293 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 294 { 295 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 296 { 297 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 298 { 299 List<string> options = new List<string>(); 300 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 301 { 302 if (!string.IsNullOrWhiteSpace(option.Value)) 303 { 304 if ((option.Value.ToString().Contains("#") || option.Value.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 305 { 306 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 307 if (option.Value.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 308 { 309 colorSpan = $"<img src=\"/Admin/Public/GetImage.ashx?width=16&height=16&image={option.Value}\">"; 310 } 311 options.Add(colorSpan); 312 } 313 else if (!string.IsNullOrEmpty(option.Value)) 314 { 315 options.Add(option.Name); 316 } 317 } 318 } 319 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 320 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 321 { 322 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 323 } 324 325 if (!string.IsNullOrEmpty(optionsString)) 326 { 327 if (!hideFieldLabels) 328 { 329 featuresList.Add(field.Value.Name + ": " + optionsString); 330 } 331 else 332 { 333 featuresList.Add(optionsString); 334 } 335 } 336 } 337 else 338 { 339 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 340 { 341 if ((field.Value.Value.ToString().Contains("#") || field.Value.Value.ToString().StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 342 { 343 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 344 if(field.Value.Value.ToString().StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 345 { 346 colorSpan = $"<img src=\"/Admin/Public/GetImage.ashx?width=16&height=16&image={field.Value.Value}\">"; 347 } 348 349 if (!hideFieldLabels) 350 { 351 featuresList.Add(field.Value.Name + ": " + colorSpan); 352 } 353 else 354 { 355 featuresList.Add(colorSpan); 356 } 357 } 358 else 359 { 360 if (!hideFieldLabels) 361 { 362 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 363 } 364 else 365 { 366 featuresList.Add(field.Value.Value.ToString()); 367 } 368 } 369 } 370 } 371 } 372 } 373 } 374 375 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 376 377 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 378 } 379 } 380 381 @helper RenderField(FieldValueViewModel field, string layout) 382 { 383 string size = Model.Item.GetRawValueString("Size", "full"); 384 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 385 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 386 bool noValues = false; 387 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 388 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 389 390 if (!string.IsNullOrEmpty(fieldValue)) 391 { 392 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 393 { 394 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 395 noValues = values.Count > 0 ? false : true; 396 397 //Hack to support field type providers with a single value 398 if (values.FirstOrDefault() != null) 399 { 400 firstListItemValue = values.FirstOrDefault().Value; 401 } 402 } 403 } 404 405 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 406 { 407 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 408 { 409 if (layout == "columns") 410 { 411 <div class="grid g-col-6 g-col-lg-4 gap-1"> 412 @if (!hideFieldLabels) 413 { 414 <dt class="g-col-12 g-col-lg-4">@field.Name</dt> 415 } 416 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 417 @{ @RenderFieldValue(field) } 418 </dd> 419 </div> 420 } 421 if (layout == "list") 422 { 423 if (!hideFieldLabels) 424 { 425 <dt class="g-col-4">@field.Name</dt> 426 } 427 <dd class="g-col-8 mb-0 text-break"> 428 @{ @RenderFieldValue(field) } 429 </dd> 430 } 431 if (layout == "table") 432 { 433 <tr> 434 @if (!hideFieldLabels) 435 { 436 <td style="width: 35%;" scope="row">@field.Name</td> 437 } 438 <td class="text-break"> 439 @{ @RenderFieldValue(field) } 440 </td> 441 </tr> 442 } 443 if (layout == "bullets") 444 { 445 <li> 446 @if (!hideFieldLabels) 447 { 448 <strong>@field.Name</strong> 449 } 450 <span> 451 @{ @RenderFieldValue(field) } 452 </span> 453 </li> 454 } 455 } 456 } 457 } 458 459 @helper RenderFieldValue(FieldValueViewModel field) 460 { 461 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 462 463 bool isLink = field?.Type == "Link"; 464 bool isColor = false; 465 bool isBrandName = field?.SystemName == "Brand_name"; 466 467 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 468 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 469 470 471 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 472 { 473 int valueCount = 0; 474 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 475 int totalValues = values.Count; 476 477 foreach (FieldOptionValueViewModel option in values) 478 { 479 if (!string.IsNullOrEmpty(option.Value)) 480 { 481 if (option.Value.StartsWith("#", StringComparison.OrdinalIgnoreCase) || option.Value.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 482 { 483 isColor = true; 484 } 485 } 486 487 if (!isColor) 488 { 489 @option.Name 490 } 491 else 492 { 493 if (option.Value.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 494 { 495 <img style="vertical-align: baseline;" src="/Admin/Public/GetImage.ashx?width=16&height=16&image=@option.Value" title="@option.Name"> 496 } 497 else 498 { 499 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 500 } 501 } 502 503 if (valueCount != totalValues && valueCount < (totalValues - 1)) 504 { 505 if (isColor) 506 { 507 <text> </text> 508 } 509 else 510 { 511 <text>, </text> 512 } 513 } 514 valueCount++; 515 } 516 } 517 else 518 { 519 if (!string.IsNullOrEmpty(fieldValue)) 520 { 521 if (fieldValue.StartsWith("#", StringComparison.OrdinalIgnoreCase) || fieldValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 522 { 523 isColor = true; 524 } 525 } 526 527 if (!isColor) 528 { 529 if (isLink) 530 { 531 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 532 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 533 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 534 535 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 536 } 537 else if (isBrandName) 538 { 539 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 540 <span itemprop="name">@fieldValue</span> 541 </span> 542 } 543 else 544 { 545 @fieldValue 546 } 547 548 } 549 else 550 { 551 if (fieldValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 552 { 553 <img src="/Admin/Public/GetImage.ashx?width=16&height=16&image=@fieldValue"> 554 } 555 else 556 { 557 <span class="colorbox-sm" style="background-color: @fieldValue"></span> 558 } 559 } 560 } 561 } 562

Product description

Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGalleryForIcons_Custom.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_d14ecaa12e6d4501b2504937193cd0b3.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGalleryForIcons_Custom.cshtml:line 124
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 @using System.Text.RegularExpressions; 6 7 @functions { 8 public ProductViewModel product { get; set; } = new ProductViewModel(); 9 public string galleryLayout { get; set; } 10 public string[] supportedImageFormats { get; set; } 11 public string[] supportedVideoFormats { get; set; } 12 public string[] supportedDocumentFormats { get; set; } 13 public string[] allSupportedFormats { get; set; } 14 15 public class RatioSettings 16 { 17 public string Ratio { get; set; } 18 public string CssClass { get; set; } 19 public string CssVariable { get; set; } 20 public string Fill { get; set; } 21 } 22 23 public RatioSettings GetRatioSettings(string size = "desktop") 24 { 25 var ratioSettings = new RatioSettings(); 26 27 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 28 ratio = ratio != "0" ? ratio : ""; 29 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 30 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 31 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 32 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 33 34 ratioSettings.Ratio = ratio; 35 ratioSettings.CssClass = cssClass; 36 ratioSettings.CssVariable = cssVariable; 37 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 38 39 return ratioSettings; 40 } 41 42 public string GetColumnClass(int total, int assetNumber) 43 { 44 string colClass = Model.Item.GetRawValueString("ItemsPerRow", "g-col-2"); 45 46 return colClass; 47 } 48 49 public string GetArrowsColor() 50 { 51 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 52 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 53 return arrowsColor; 54 } 55 56 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 57 { 58 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 59 string type = GetVideoType(asset.Value); 60 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 61 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 62 63 var videoParams = new Dictionary<string, object>(); 64 videoParams.Add("AssetName", asset.Name); 65 videoParams.Add("AssetVideoType", type); 66 videoParams.Add("AssetDisplayName", asset.DisplayName); 67 videoParams.Add("OpenVideoInModal", openInModal); 68 videoParams.Add("VideoAutoPlay", autoPlay); 69 videoParams.Add("Size", size); 70 videoParams.Add("Id", Model.ID); 71 return videoParams; 72 } 73 74 public string GetVideoType(string assetValue) 75 { 76 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 77 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 78 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 79 return type; 80 } 81 82 public string GetYoutubeScreenDump(string assetValue, string quality) 83 { 84 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); 85 Match match = regex.Match(assetValue); 86 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 87 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 88 return youtubeThumbnail; 89 } 90 } 91 92 @{ 93 @* Get the product data *@ 94 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 95 { 96 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 97 } 98 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 99 { 100 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 101 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 102 103 if (productList?.Products is object) 104 { 105 product = productList.Products[0]; 106 } 107 } 108 } 109 110 @if (product is object) 111 { 112 @* Supported formats *@ 113 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 114 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 115 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 116 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 117 118 @* Collect the assets *@ 119 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 120 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 121 122 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 123 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 124 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 125 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 126 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; 127 assetsList = assetsList.Union(assetsImages); 128 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 129 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 130 131 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 132 133 int totalAssets = 0; 134 foreach (MediaViewModel asset in assetsList) 135 { 136 var assetValue = asset.Value; 137 foreach (string format in allSupportedFormats) 138 { 139 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 140 { 141 totalAssets++; 142 } 143 } 144 } 145 146 if (totalAssets == 0) 147 { 148 if (defaultImageFallback) 149 { 150 assetsList = new List<MediaViewModel>() { product.DefaultImage }; 151 totalAssets = 1; 152 } 153 else 154 { 155 assetsList = new List<MediaViewModel>() { }; 156 totalAssets = 0; 157 } 158 } 159 160 @* Layout settings *@ 161 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 162 spacing = spacing == "none" ? "gap-0" : spacing; 163 spacing = spacing == "small" ? "gap-3" : spacing; 164 spacing = spacing == "large" ? "gap-4" : spacing; 165 166 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 167 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 168 169 var badgeParms = new Dictionary<string, object>(); 170 badgeParms.Add("size", "h5"); 171 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 172 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 173 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 174 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 175 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 176 177 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 178 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 179 DateTime createdDate = product.Created.Value; 180 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 181 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 182 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 183 184 185 @* Get assets from selected categories or get all assets *@ 186 if (totalAssets != 0 && assetsList.Count() != 0) 187 { 188 int desktopAssetNumber = 0; 189 int mobileAssetNumber = 0; 190 int mobileAssetThumbnailNumber = 0; 191 int modalAssetNumber = 0; 192 193 @* Desktop: Show the gallery on large screens *@ 194 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 195 <div class="grid @spacing"> 196 @foreach (MediaViewModel asset in assetsList) 197 { 198 var assetName = asset.Value; 199 foreach (string format in allSupportedFormats) 200 { 201 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 202 { 203 string size = "desktop"; 204 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 205 string assetValue = asset.Value; 206 207 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 208 <div class="h-100 @(imageTheme)"> 209 @foreach (string imageFormat in supportedImageFormats) 210 { //Images 211 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 212 { 213 string productName = product.Name; 214 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 215 string imageLinkPath = !string.IsNullOrEmpty(imagePath) ? $"/Admin/Public/GetImage.ashx?image={Uri.EscapeDataString(imagePath)}&width=1920&format=webp" : string.Empty; ; 216 217 RatioSettings ratioSettings = GetRatioSettings(size); 218 219 var parms = new Dictionary<string, object>(); 220 parms.Add("alt", productName); 221 parms.Add("itemprop", "image"); 222 if (totalAssets > 1) 223 { 224 parms.Add("columns", 2); 225 } 226 else 227 { 228 parms.Add("columns", 1); 229 } 230 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 231 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 232 233 if (!string.IsNullOrEmpty(asset.DisplayName)) 234 { 235 parms.Add("title", asset.DisplayName); 236 } 237 238 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 239 { 240 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 241 } 242 else 243 { 244 parms.Add("cssClass", "mw-100 mh-100"); 245 } 246 247 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 248 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 249 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 250 </div> 251 </a> 252 } 253 } 254 @foreach (string videoFormat in supportedVideoFormats) 255 { //Videos 256 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 257 { 258 if (Model.Item.GetString("OpenVideoInModal") == "true") 259 { 260 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 261 262 string type = GetVideoType(asset.Value); 263 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 264 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 265 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 266 267 string productName = product.Name; 268 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 269 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 270 271 RatioSettings ratioSettings = GetRatioSettings(size); 272 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 273 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 274 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 275 @if (type != "selfhosted") 276 { 277 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 278 } 279 else 280 { 281 string videoType = Path.GetExtension(asset.Value).ToLower(); 282 283 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 284 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 285 </video> 286 } 287 </div> 288 </div> 289 290 } 291 else 292 { 293 var videoParams = GetVideoParams(asset, size); 294 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 295 } 296 } 297 } 298 @foreach (string documentFormat in supportedDocumentFormats) 299 { //Documents 300 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 301 { 302 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 303 304 string productName = product.Name; 305 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 306 string imageLinkPath = Uri.EscapeDataString(imagePath); 307 308 RatioSettings ratioSettings = GetRatioSettings(size); 309 310 var parms = new Dictionary<string, object>(); 311 parms.Add("alt", productName); 312 parms.Add("itemprop", "image"); 313 parms.Add("fullwidth", true); 314 parms.Add("columns", Model.GridRowColumnCount); 315 if (!string.IsNullOrEmpty(asset.DisplayName)) 316 { 317 parms.Add("title", asset.DisplayName); 318 } 319 320 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 321 { 322 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 323 } 324 else 325 { 326 parms.Add("cssClass", "mw-100 mh-100"); 327 } 328 329 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 330 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 331 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 332 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 333 { 334 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 335 } 336 else 337 { 338 339 } 340 </div> 341 </a> 342 } 343 } 344 </div> 345 </div> 346 desktopAssetNumber++; 347 } 348 } 349 } 350 </div> 351 352 @if (showBadges) 353 { 354 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 355 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 356 </div> 357 } 358 </div> 359 360 @* Mobile: Show the thumbs on small screens *@ 361 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 362 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 363 <div class="carousel-inner h-100"> 364 @foreach (MediaViewModel asset in assetsList) 365 { 366 var assetValue = asset.Value; 367 foreach (string format in allSupportedFormats) 368 { 369 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 370 { 371 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 372 string size = "mobile"; 373 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 374 375 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 376 <div class="h-100 @(imageTheme)"> 377 @foreach (string imageFormat in supportedImageFormats) 378 { //Images 379 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 380 { 381 string productName = product.Name; 382 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 383 string imageLinkPath = !string.IsNullOrEmpty(imagePath) ? $"/Admin/Public/GetImage.ashx?image={Uri.EscapeDataString(imagePath)}&width=1920&format=webp" : string.Empty; 384 385 RatioSettings ratioSettings = GetRatioSettings(size); 386 387 var parms = new Dictionary<string, object>(); 388 parms.Add("alt", productName); 389 parms.Add("itemprop", "image"); 390 if (totalAssets > 1) 391 { 392 parms.Add("columns", 2); 393 } 394 else 395 { 396 parms.Add("columns", 1); 397 } 398 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 399 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 400 401 if (!string.IsNullOrEmpty(asset.DisplayName)) 402 { 403 parms.Add("title", asset.DisplayName); 404 } 405 406 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 407 { 408 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 409 } 410 else 411 { 412 parms.Add("cssClass", "mw-100 mh-100"); 413 } 414 415 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 416 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@mobileAssetNumber"> 417 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 418 </div> 419 </a> 420 } 421 } 422 @foreach (string videoFormat in supportedVideoFormats) 423 { //Videos 424 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 425 { 426 if (Model.Item.GetString("OpenVideoInModal") == "true") 427 { 428 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 429 430 string type = GetVideoType(asset.Value); 431 432 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 433 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 434 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 435 436 string productName = product.Name; 437 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 438 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 439 440 RatioSettings ratioSettings = GetRatioSettings(size); 441 442 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 443 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 444 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 445 @if (type != "selfhosted") 446 { 447 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;"> 448 } 449 else 450 { 451 string videoType = Path.GetExtension(asset.Value).ToLower(); 452 453 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 454 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 455 </video> 456 } 457 </div> 458 </div> 459 } 460 else 461 { 462 Dictionary<string, object> videoParams = GetVideoParams(asset, size); 463 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 464 } 465 } 466 } 467 </div> 468 </div> 469 mobileAssetNumber++; 470 } 471 } 472 } 473 </div> 474 </div> 475 476 @if (totalAssets > 1) 477 { 478 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 479 @foreach (MediaViewModel asset in assetsList) 480 { 481 var assetValue = asset.Value; 482 foreach (string format in allSupportedFormats) 483 { 484 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 485 { 486 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 487 string type = GetVideoType(asset.Value); 488 489 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 490 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 491 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 492 493 string productName = product.Name; 494 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 495 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 496 497 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 498 @foreach (string imageFormat in supportedImageFormats) 499 { //Images 500 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 501 { 502 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 503 <img src="@imagePath" class="p-1 mw-100 mh-100" style="object-fit: cover;" alt="@productName" @assetTitle> 504 } 505 } 506 @foreach (string videoFormat in supportedVideoFormats) 507 { //Videos 508 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 509 { 510 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 511 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 512 </div> 513 if (type != "selfhosted") 514 { 515 516 <img src="@(videoScreendumpPath)" class="p-1 @videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" alt="@productName" @assetTitle> 517 518 } 519 else 520 { 521 string videoType = Path.GetExtension(asset.Value).ToLower(); 522 523 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 524 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 525 </video> 526 } 527 } 528 } 529 @foreach (string documentFormat in supportedDocumentFormats) 530 { //Documents 531 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 532 { 533 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 534 535 <a href="@Uri.EscapeDataString(assetValue)" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 536 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 537 { 538 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 539 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 540 </div> 541 <img src="@imagePath" alt="@productName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 542 } 543 else 544 { 545 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 546 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 547 </div> 548 } 549 </a> 550 } 551 } 552 </div> 553 554 mobileAssetThumbnailNumber++; 555 } 556 } 557 } 558 </div> 559 } 560 561 @if (showBadges) 562 { 563 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 564 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 565 </div> 566 } 567 </div> 568 569 @* Modal with slides *@ 570 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 571 <div class="modal-dialog modal-dialog-centered modal-xl"> 572 <div class="modal-content"> 573 <div class="modal-header visually-hidden"> 574 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 575 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 576 </div> 577 <div class="modal-body p-2 p-lg-3 h-100"> 578 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 579 <div class="carousel-inner h-100 @theme"> 580 @foreach (MediaViewModel asset in assetsList) 581 { 582 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 583 foreach (string format in allSupportedFormats) 584 { 585 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 586 { 587 string imagePath = assetValue; 588 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 589 590 var parms = new Dictionary<string, object>(); 591 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 592 parms.Add("columns", Model.GridRowColumnCount); 593 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 594 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 595 596 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 597 @foreach (string imageFormat in supportedImageFormats) 598 { 599 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 600 { 601 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 602 } 603 } 604 605 @foreach (string videoFormat in supportedVideoFormats) 606 { 607 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 608 { 609 610 Dictionary<string, object> videoParams = GetVideoParams(asset, "modal"); 611 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 612 613 } 614 } 615 </div> 616 617 modalAssetNumber++; 618 } 619 } 620 } 621 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 622 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 623 <span class="visually-hidden">@Translate("Previous")</span> 624 </button> 625 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 626 <span class="carousel-control-next-icon" aria-hidden="true"></span> 627 <span class="visually-hidden">@Translate("Next")</span> 628 </button> 629 </div> 630 </div> 631 </div> 632 </div> 633 </div> 634 </div> 635 } 636 else if (Pageview.IsVisualEditorMode) 637 { 638 RatioSettings ratioSettings = GetRatioSettings("desktop"); 639 640 <div class="h-100 @theme"> 641 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 642 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 643 </div> 644 </div> 645 } 646 } 647
Error executing template "Designs/Swift/Paragraph/Swift_RelatedProductsList.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_ef44e4022b81465dbea85c8018006e57.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_RelatedProductsList.cshtml:line 61
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 5 @{ 6 ProductViewModel product = new ProductViewModel(); 7 8 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 9 { 10 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 11 } 12 13 string servicePageId = Model.Item.GetLink("ServicePage") != null ? Model.Item.GetLink("ServicePage").PageId.ToString() : ""; 14 servicePageId = servicePageId == "" && GetPageIdByNavigationTag("RelatedProductsListService") != 0 ? GetPageIdByNavigationTag("RelatedProductsListService").ToString() : servicePageId; 15 string pageSize = Model.Item.GetInt32("PageSize") != 0 ? Model.Item.GetString("PageSize") : "10"; 16 17 IEnumerable<string> selectedDisplayGroups = Model.Item.GetList("Fields")?.GetRawValue().OfType<string>().ToList(); 18 string selectedDisplayGroupsString = selectedDisplayGroups.Count() > 0 ? (string.Join(",", selectedDisplayGroups.Select(x => x.ToString()).ToArray())) : ""; 19 20 string title = !string.IsNullOrEmpty(Model.Item.GetString("Title")) ? Model.Item.GetString("Title") : ""; 21 string hideTitle = Model.Item.GetBoolean("HideTitle").ToString(); 22 string hideImage = Model.Item.GetBoolean("HideImage").ToString(); 23 string hideProductNumber = Model.Item.GetBoolean("HideProductNumber").ToString(); 24 string hideProductName = Model.Item.GetBoolean("HideProductName").ToString(); 25 string hideStock = (Model.Item.GetBoolean("HideStock") || Pageview.AreaSettings.GetBoolean("ErpDownHideStock") && !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"])).ToString(); 26 string quantitySelector = Model.Item.GetBoolean("QuantitySelector").ToString(); 27 28 string campaignValuesString = string.Join(",", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 29 30 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 31 string modalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ModalTheme")) ? " theme " + Model.Item.GetRawValueString("ModalTheme").Replace(" ", "").Trim().ToLower() : ""; 32 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-6"); 33 34 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 35 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding; 36 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding; 37 38 //Source type 39 string sourceType = Model.Item.GetRawValueString("SourceType", "variants"); 40 IList<string> relateFromProductIds = new List<string>{}; 41 42 //--- RELATED PRODUCTS --- 43 if (sourceType == "related-products" && product?.RelatedGroups != null) { 44 foreach (var group in product.RelatedGroups) 45 { 46 foreach (var relatedProduct in group.Products) 47 { 48 if (string.IsNullOrEmpty(relatedProduct.VariantId)) 49 { 50 relateFromProductIds.Add($"{relatedProduct.ProductId}"); 51 } 52 else 53 { 54 relateFromProductIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 55 } 56 } 57 } 58 } 59 60 //Create group id collection and products id collection strings 61 string groupId = product.PrimaryOrDefaultGroup.Id; 62 string productIds = sourceType == "related-products" ? string.Join(",", relateFromProductIds) : product.Id; 63 64 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 65 } 66 67 @if (!Pageview.IsVisualEditorMode) 68 { 69 <div id="RelatedProductsListContainer_@Pageview.CurrentParagraph.ID" class="item_@Model.Item.SystemName.ToLower()"> 70 @* Initial form for first load (We pass all the settings to the actual template) *@ 71 <form action="/Default.aspx?ID=@servicePageId" id="RelatedProductsListForm_@Pageview.CurrentParagraph.ID" data-response-target-element="RelatedProductsListContainer_@Pageview.CurrentParagraph.ID"> 72 <input type="hidden" name="SortOrder" value="DESC"> 73 <input type="hidden" name="ID" value="@servicePageId"> 74 <input type="hidden" name="SourceType" value="@sourceType"> 75 <input type="hidden" name="ParagraphID" value="@Pageview.CurrentParagraph.ID"> 76 <input type="hidden" name="PageSize" value="@pageSize" id="RelatedProductsListPageSize_@Pageview.CurrentParagraph.ID"> 77 <input type="hidden" name="PageSizeSetting" value="@pageSize"> 78 <input type="hidden" name="Title" value="@title"> 79 <input type="hidden" name="HideTitle" value="@hideTitle"> 80 <input type="hidden" name="SelectedDisplayGroups" value="@selectedDisplayGroupsString"> 81 <input type="hidden" name="HideImage" value="@hideImage"> 82 <input type="hidden" name="HideProductNumber" value="@hideProductNumber"> 83 <input type="hidden" name="HideProductName" value="@hideProductName"> 84 <input type="hidden" name="HideStock" value="@hideStock"> 85 <input type="hidden" name="QuantitySelector" value="@quantitySelector"> 86 <input type="hidden" name="Theme" value="@theme"> 87 <input type="hidden" name="ModalTheme" value="@modalTheme"> 88 <input type="hidden" name="TitleFontSize" value="@titleFontSize"> 89 <input type="hidden" name="ContentPadding" value="@contentPadding"> 90 @if (isLazyLoadingForProductInfoEnabled) 91 { 92 <input type="hidden" name="getproductinfo" value="true"> 93 } 94 95 @*--- VARIANTS ---*@ 96 @if (sourceType == "variants") 97 { 98 <input type="hidden" name="MainProductID" value="@productIds"> 99 } 100 101 @*--- MOST SOLD ---*@ 102 @if (sourceType == "most-sold") 103 { 104 <input type="hidden" name="SortBy" value="OrderCount"> 105 <input type="hidden" name="GroupId" value="@groupId"> 106 <input type="hidden" name="isVariant" value="false"> 107 } 108 109 @*--- TRENDING ---*@ 110 @if (sourceType == "trending") 111 { 112 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 113 <input type="hidden" name="GroupId" value="@groupId"> 114 <input type="hidden" name="isVariant" value="false"> 115 } 116 117 @*--- LATEST ---*@ 118 @if (sourceType == "latest") 119 { 120 <input type="hidden" name="SortBy" value="Created"> 121 <input type="hidden" name="GroupId" value="@groupId"> 122 <input type="hidden" name="isVariant" value="false"> 123 } 124 125 @*--- FREQUENTLY BOUGHT ---*@ 126 @if (sourceType == "frequently") 127 { 128 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 129 <input type="hidden" name="isVariant" value="false"> 130 } 131 132 @*--- RELATED PRODUCTS ---*@ 133 @if (sourceType == "related-products") 134 { 135 <input type="hidden" name="ProductvariantId" value="@productIds"> 136 } 137 138 <input type="hidden" name="SaleBadgeType" value="@Model.Item.GetRawValue("SaleBadgeType")"> 139 <input type="hidden" name="SaleBadgeCssClassName" value="@Model.Item.GetRawValue("SaleBadgeDesign")"> 140 <input type="hidden" name="NewBadgeCssClassName" value="@Model.Item.GetRawValue("NewBadgeDesign")"> 141 <input type="hidden" name="NewPublicationDays" value="@Model.Item.GetInt32("NewPublicationDays")"> 142 143 @if (campaignValuesString != string.Empty) 144 { 145 <input type="hidden" name="CampaignBadgesValues" value="@campaignValuesString"> 146 } 147 </form> 148 </div> 149 150 <script type="module"> 151 swift.PageUpdater.Update(document.querySelector("#RelatedProductsListForm_@Pageview.CurrentParagraph.ID")); 152 </script> 153 154 <script> 155 var UpdateRelatedProductsList = function(event, type, id) { 156 var targetElement = "RelatedProductsListContainer_" + id; 157 158 if (type == "UpdateFacets") { 159 targetElement = "RelatedProductsListFacets_" + id; 160 } 161 162 document.querySelector('#RelatedProductsListRequestType_' + id).value = type; 163 document.querySelector('#RelatedProductsListForm_' + id).setAttribute("data-response-target-element", targetElement); 164 165 swift.PageUpdater.Update(document.querySelector("#RelatedProductsListForm_" + id)); 166 }; 167 </script> 168 } else { 169 <div class="alert alert-info" role="alert"> 170 <span>@Translate("Related products list")</span> 171 </div> 172 } 173
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_32cfea3b31284371b3e4e56004dce934.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 110
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using System.Text.RegularExpressions; 4 @using System.IO 5 6 @functions { 7 public ProductViewModel product { get; set; } = new ProductViewModel(); 8 public string[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 15 public string Ratio { get; set; } 16 public string CssClass { get; set; } 17 public string CssVariable { get; set; } 18 public string Fill { get; set; } 19 } 20 21 public RatioSettings GetRatioSettings() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 38 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 39 { 40 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 41 string type = GetVideoType(asset.Value); 42 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 43 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 44 45 var videoParams = new Dictionary<string, object>(); 46 videoParams.Add("AssetName", asset.Name); 47 videoParams.Add("AssetVideoType", type); 48 videoParams.Add("AssetDisplayName", asset.DisplayName); 49 videoParams.Add("OpenVideoInModal", openInModal); 50 videoParams.Add("VideoAutoPlay", autoPlay); 51 videoParams.Add("Size", size); 52 videoParams.Add("Id", Model.ID); 53 return videoParams; 54 55 } 56 57 public string GetVideoType(string assetValue) 58 { 59 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 60 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 61 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 62 return type; 63 } 64 65 public string GetYoutubeScreenDump(string assetValue) 66 { 67 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); 68 Match match = regex.Match(assetValue); 69 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 70 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/mqdefault.jpg"; 71 return youtubeThumbnail; 72 } 73 74 } 75 76 @{ 77 @* Get the product data *@ 78 ProductViewModel product = null; 79 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 80 { 81 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 82 } 83 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 84 { 85 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 86 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 87 88 if (productList?.Products is object) 89 { 90 product = productList.Products[0]; 91 } 92 } 93 } 94 95 @if (product is object) 96 { 97 @* Supported formats *@ 98 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 99 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 100 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", ".pptx", ".igs", ".ipt", ".sat", ".stp", ".dwg", ".dxf", ".dwf" }; 101 102 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 103 104 @* Collect the assets *@ 105 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 106 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 107 108 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 109 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 110 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 111 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 112 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; 113 114 assetsList = assetsList.Union(assetsImages); 115 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 116 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 117 118 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 119 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 120 121 int totalAssets = 0; 122 if (showOnlyPrimaryImage == false) { 123 foreach (MediaViewModel asset in assetsList) { 124 var assetValue = asset.Value; 125 foreach (string format in allSupportedFormats) { 126 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 127 totalAssets++; 128 } 129 } 130 } 131 } 132 133 if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 134 { 135 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 136 totalAssets = 1; 137 } 138 139 int videoNumber = 0; 140 141 @* Layout settings *@ 142 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 143 spacing = spacing == "none" ? "p-0" : spacing; 144 spacing = spacing == "small" ? "p-3" : spacing; 145 spacing = spacing == "large" ? "p-5" : spacing; 146 147 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 148 149 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 150 151 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 152 int modalVideoNumber = 0; 153 154 @* Get assets from selected categories or get all assets *@ 155 156 if (totalAssets != 0 && assetsList.Any()) 157 { 158 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 159 @if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 160 { 161 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 162 163 <h3 class="@titleFontSize mb-3"> 164 @Model.Item.GetString("Title") 165 </h3> 166 } 167 168 <div class="table-responsive"> 169 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 170 <thead> 171 <tr> 172 @if (!hideThumbnails) 173 { 174 <th style="width:60px">&nbsp;</th> 175 } 176 <th>@Translate("Name")</th> 177 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 178 <th class="text-end" style="width:100px">@Translate("File type")</th> 179 </tr> 180 </thead> 181 <tbody class="border-top-0"> 182 @foreach (MediaViewModel asset in assetsList) 183 { 184 var assetValue = asset.Value; 185 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 186 187 bool isVideo = false; 188 foreach (string format in supportedVideoFormats) 189 { //Videos 190 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 191 { 192 isVideo = true; 193 } 194 } 195 196 if (!isVideo) 197 { 198 if (assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) 199 { 200 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 201 long fileSize = 0; 202 203 if (File.Exists(filePath)) 204 { 205 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 206 207 foreach (string format in allSupportedFormats) 208 { 209 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 210 { 211 <tr> 212 @if (!hideThumbnails) 213 { 214 //From @RenderAsset 215 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 216 217 <td class="@(imageTheme) px-0"> 218 @foreach (string imageFormat in supportedImageFormats) 219 { //Images 220 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 221 { 222 string productName = product.Name; 223 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 224 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 225 string imageLinkPath = imagePath; 226 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 227 imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; 228 229 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 230 231 RatioSettings ratioSettings = GetRatioSettings(); 232 233 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 234 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 235 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 236 </div> 237 </a> 238 } 239 } 240 @foreach (string videoFormat in supportedVideoFormats) 241 { //Videos 242 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 243 { 244 string productName = product.Name; 245 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 246 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 247 248 RatioSettings ratioSettings = GetRatioSettings(); 249 250 string type = GetVideoType(asset.Value); 251 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; 252 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 253 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 254 255 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 256 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 257 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 258 @if (type != "selfhosted") 259 { 260 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;"> 261 } 262 else 263 { 264 string videoType = Path.GetExtension(asset.Value).ToLower(); 265 266 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 267 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 268 </video> 269 } 270 </div> 271 </div> 272 } 273 } 274 @foreach (string documentFormat in supportedDocumentFormats) 275 { //Documents 276 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 277 { 278 string productName = product.Name; 279 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 280 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 281 string imageLinkPath = imagePath; 282 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 283 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 284 285 RatioSettings ratioSettings = GetRatioSettings(); 286 287 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 288 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 289 { 290 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 291 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 292 </div> 293 } 294 else 295 { 296 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 297 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 298 </div> 299 } 300 </a> 301 } 302 } 303 </td> 304 305 } 306 <td> 307 <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> 308 @assetName 309 </a> 310 </td> 311 <td class="text-end d-none d-lg-table-cell"> 312 <a href="@assetValue" class="text-decoration-none" download="@assetName" title="@assetName"> 313 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 314 </a> 315 </td> 316 <td class="text-end">@format</td> 317 </tr> 318 } 319 } 320 } 321 } 322 else 323 { 324 <tr> 325 @if (!hideThumbnails) 326 { 327 //From @RenderAsset 328 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 329 330 <td class="@(imageTheme) px-0"> 331 @foreach (string format in supportedImageFormats) 332 { //Images 333 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 334 { 335 string productName = product.Name; 336 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 337 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 338 string imageLinkPath = imagePath; 339 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 340 imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; 341 342 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 343 344 RatioSettings ratioSettings = GetRatioSettings(); 345 346 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 347 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 348 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 349 </div> 350 </a> 351 } 352 } 353 @foreach (string format in supportedVideoFormats) 354 { //Videos 355 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 356 { 357 string type = GetVideoType(asset.Value); 358 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; 359 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 360 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 361 362 363 string productName = product.Name; 364 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 365 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 366 367 RatioSettings ratioSettings = GetRatioSettings(); 368 369 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 370 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 371 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 372 @if (type != "selfhosted") 373 { 374 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;"> 375 } 376 else 377 { 378 string videoType = Path.GetExtension(asset.Value).ToLower(); 379 380 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 381 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 382 </video> 383 } 384 </div> 385 </div> 386 } 387 } 388 @foreach (string format in supportedDocumentFormats) 389 { //Documents 390 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 391 { 392 string productName = product.Name; 393 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 394 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 395 string imageLinkPath = imagePath; 396 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 397 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 398 399 RatioSettings ratioSettings = GetRatioSettings(); 400 401 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 402 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 403 { 404 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 405 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 406 </div> 407 } 408 else 409 { 410 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 411 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 412 </div> 413 } 414 </a> 415 } 416 } 417 </td> 418 } 419 <td> 420 <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> 421 @assetName 422 </a> 423 </td> 424 <td>&nbsp;</td> 425 <td>&nbsp;</td> 426 </tr> 427 } 428 } 429 else 430 { 431 string videoType = asset.Value.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || asset.Value.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "Youtube" : ""; 432 videoType = asset.Value.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "Vimeo" : videoType; 433 434 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 435 @if (!hideThumbnails) 436 { 437 //From @RenderAsset 438 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 439 440 441 <td class="@(imageTheme) px-0"> 442 @foreach (string format in supportedImageFormats) 443 { //Images 444 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 445 { 446 string productName = product.Name; 447 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 448 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 449 string imageLinkPath = imagePath; 450 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 451 imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; 452 453 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 454 455 RatioSettings ratioSettings = GetRatioSettings(); 456 457 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 458 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 459 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 460 </div> 461 </a> 462 } 463 } 464 @foreach (string format in supportedVideoFormats) 465 { //Videos 466 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 467 { 468 469 string type = GetVideoType(asset.Value); 470 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; 471 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 472 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 473 474 string productName = product.Name; 475 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 476 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 477 478 RatioSettings ratioSettings = GetRatioSettings(); 479 480 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 481 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 482 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 483 @if (type != "selfhosted") 484 { 485 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;"> 486 } 487 else 488 { 489 string fileExtension = Path.GetExtension(asset.Value).ToLower(); 490 491 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 492 <source src="@(videoScreendumpPath)#t=0.001" type="video/@fileExtension.Replace(".", "")"> 493 </video> 494 } 495 </div> 496 </div> 497 } 498 } 499 @foreach (string documentFormat in supportedDocumentFormats) 500 { //Documents 501 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 502 { 503 string productName = product.Name; 504 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 505 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 506 string imageLinkPath = imagePath; 507 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 508 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 509 510 RatioSettings ratioSettings = GetRatioSettings(); 511 512 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 513 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 514 { 515 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 516 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 517 </div> 518 } 519 else 520 { 521 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 522 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 523 </div> 524 } 525 </a> 526 } 527 } 528 </td> 529 } 530 <td> 531 @assetName 532 </td> 533 <td class="d-none d-lg-table-cell">&nbsp;</td> 534 <td align="right">@videoType</td> 535 </tr> 536 537 videoNumber++; 538 } 539 } 540 </tbody> 541 </table> 542 </div> 543 544 @foreach (MediaViewModel asset in assetsList) 545 { 546 var assetName = asset.Value.ToLower(); 547 548 foreach (string videoFormat in supportedVideoFormats) 549 { //Videos 550 if (assetName.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 551 { 552 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 553 <div class="modal-dialog modal-dialog-centered modal-xl"> 554 <div class="modal-content"> 555 <div class="modal-header visually-hidden"> 556 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 557 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 558 </div> 559 <div class="modal-body p-2 p-lg-3 h-100"> 560 @{ 561 var videoParams = GetVideoParams(asset, "modal"); 562 @RenderPartial("Components/VideoPlayer.cshtml", new Dynamicweb.Frontend.FileViewModel { Path = asset.Value }, videoParams) 563 } 564 </div> 565 </div> 566 </div> 567 </div> 568 569 modalVideoNumber++; 570 } 571 } 572 } 573 </div> 574 } 575 else if (Pageview.IsVisualEditorMode) 576 { 577 <div class="h-100 @theme"> 578 <div class="alert alert-dark m-0"> 579 @Translate("No assets are available") 580 </div> 581 </div> 582 } 583 } 584
Error executing template "Designs/Swift/Paragraph/Swift_RelatedProducts.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_9e890a2f70da40818198e9f7a675cf2c.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_RelatedProducts.cshtml:line 164
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Core 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 14 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 15 16 if (productList?.Products is object) 17 { 18 product = productList.Products[0]; 19 } 20 } 21 22 string title = Model?.Item?.GetRawValueString("Title", Translate("Products")); 23 string campaignValues = string.Join(",", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 24 25 //Styling 26 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 27 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "fs-5"); 28 string buttonStyle = Model.Item.GetRawValueString("ButtonStyle", ""); 29 buttonStyle = buttonStyle == "primary" ? " btn-primary" : buttonStyle; 30 buttonStyle = buttonStyle == "secondary" ? " btn-secondary" : buttonStyle; 31 buttonStyle = buttonStyle == "link" ? " btn-link" : buttonStyle; 32 string maxWidth = Model.Item.GetRawValueString("TextReadability", ""); 33 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 34 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 35 36 string generalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GeneralTheme")) ? " theme " + Model.Item.GetRawValueString("GeneralTheme").Replace(" ", "").Trim().ToLower() : ""; 37 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 38 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 39 40 //Link generation 41 string pageId = !string.IsNullOrEmpty(Model.Item.GetRawValueString("ProductSliderServicePage")) ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 42 if (string.IsNullOrEmpty(pageId)) 43 { 44 pageId = GetPageIdByNavigationTag("ProductSliderService").ToString(); 45 } 46 47 string url = "/Default.aspx?ID=" + pageId; 48 if (!url.Contains("LayoutTemplate", StringComparison.OrdinalIgnoreCase)) 49 { 50 url += url.Contains("?") ? "&LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml" : "?LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml"; 51 } 52 if (Pageview.IsVisualEditorMode) 53 { 54 url += "&VisualEdit=True"; 55 } 56 57 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 58 if (isLazyLoadingForProductInfoEnabled) 59 { 60 url += "&getproductinfo=true"; 61 } 62 63 //Source type 64 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 65 IList<string> relateFromGroupIds = new List<string> { }; 66 IList<string> relateFromProductVariantIds = new List<string> { }; 67 IList<string> relateFromProductIds = new List<string> { }; 68 bool hasVariants = false; 69 70 //--- VARIANTS --- 71 if (sourceType == "variants" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateToVariants") is ProductListViewModel productsToRelateToVariants) 72 { 73 foreach (var productSelection in productsToRelateToVariants.Products) 74 { 75 relateFromProductIds.Add(productSelection.Id); 76 } 77 } 78 79 //--- MOST SOLD --- 80 if (sourceType == "most-sold" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToMostSold") is IList<ProductGroupViewModel> groupsToRelateToMostSold) 81 { 82 foreach (var fromGroup in groupsToRelateToMostSold) 83 { 84 relateFromGroupIds.Add(fromGroup.Id); 85 } 86 } 87 88 //--- TRENDING --- 89 if (sourceType == "trending" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToTrending") is IList<ProductGroupViewModel> groupsToRelateToTrending) 90 { 91 foreach (var fromGroup in groupsToRelateToTrending) 92 { 93 relateFromGroupIds.Add(fromGroup.Id); 94 } 95 } 96 97 //--- LATEST --- 98 if (sourceType == "latest" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToLatest") is IList<ProductGroupViewModel> groupsToRelateToLatest) 99 { 100 foreach (var fromGroup in groupsToRelateToLatest) 101 { 102 relateFromGroupIds.Add(fromGroup.Id); 103 } 104 } 105 106 //--- FREQUENTLY BOUGHT --- 107 if (sourceType == "frequently" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo") is ProductListViewModel productsToRelateTo) 108 { 109 foreach (var fromProduct in productsToRelateTo.Products) 110 { 111 relateFromProductIds.Add(fromProduct.Id); 112 } 113 } 114 115 //--- SELECTED PRODUCTS --- 116 if ((sourceType == "selected" || sourceType == "frequently") && Model.Item.GetValue<ProductListViewModel>("Products") is ProductListViewModel products) 117 { 118 hasVariants = products.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 119 foreach (var productSelection in products.Products) 120 { 121 if (hasVariants) 122 { 123 if (!string.IsNullOrEmpty(productSelection.VariantId)) 124 { 125 relateFromProductVariantIds.Add($"{productSelection.Id} {productSelection.VariantId}"); 126 } 127 else 128 { 129 relateFromProductVariantIds.Add($"{productSelection.Id}"); 130 } 131 } 132 relateFromProductIds.Add($"{productSelection.Id}"); 133 } 134 } 135 136 //--- RELATED PRODUCTS --- 137 if (sourceType == "related-products" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo2") is ProductListViewModel selectedRelationProduct) 138 { 139 if (selectedRelationProduct.Products.Any()) 140 { 141 product = selectedRelationProduct.Products.FirstOrDefault(); 142 } 143 144 if (product?.RelatedGroups != null) 145 { 146 foreach (var group in product.RelatedGroups) 147 { 148 foreach (var relatedProduct in group.Products) 149 { 150 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 151 { 152 relateFromProductVariantIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 153 } 154 else 155 { 156 relateFromProductVariantIds.Add($"{relatedProduct.ProductId}"); 157 } 158 } 159 } 160 } 161 } 162 163 //Create group id collection and products id collection strings 164 string groupIds = product is object ? product.PrimaryOrDefaultGroup.Id : string.Join(",", relateFromGroupIds); 165 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 166 string productIds = product is object && relateFromProductIds.Count == 0 ? product.Id : string.Join(",", relateFromProductIds); 167 168 //Set the parameters to the url 169 string linkParameters = ""; 170 linkParameters += sourceType != "related-products" && sourceType != "frequently" && sourceType != "selected" ? "&GroupId=" + groupIds : ""; 171 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType != "most-sold" && sourceType != "trending" && sourceType != "latest" && sourceType != "frequently" && sourceType != "related-products" ? "&MainProductId=" + productIds : ""; 172 linkParameters += !string.IsNullOrEmpty(productVariantIds) && sourceType == "related-products" ? "&ProductVariantId=" + productVariantIds : ""; 173 linkParameters += sourceType == "variants" ? "&IsVariant=true" : ""; 174 linkParameters += sourceType == "latest" ? "&SortBy=Created" : ""; 175 linkParameters += sourceType == "most-sold" ? "&SortBy=OrderCount" : ""; 176 linkParameters += sourceType == "trending" ? "&SortBy=OrderCountGrowth" : ""; 177 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType == "frequently" ? $"&BoughtWithProductIds=[{productIds}]" : ""; 178 var productListPageId = GetPageIdByNavigationTag("Shop"); 179 string link = "/Default.aspx?ID=" + productListPageId + linkParameters; 180 181 // Slider settings (documentation: swiffyslider.com/configuration) 182 string navigationStyle = $"{Model.Item.GetRawValueString("NavigationStyle", "slider-nav-round")}"; 183 string navigationPlacement = $"{Model.Item.GetRawValueString("NavigationPlacement", "slider-nav-on-slides")}"; 184 string indicatorStyle = $"{Model.Item.GetRawValueString("IndicatorStyle", "slider-indicators-hidden")}"; 185 string revealSlides = Model.Item.GetRawValueString("RevealSlides", "no-reveal") == "reveal" ? "slider-item-reveal" : string.Empty; 186 string navigationAlwaysVisible = (Model.Item.GetBoolean("NavigationAlwaysVisible")) ? "slider-nav-visible" : string.Empty; 187 string navigationVisibleOnTouch = (Model.Item.GetBoolean("NavigationVisibleOnTouch")) ? "slider-nav-touch" : string.Empty; 188 string navigationShowScrollbar = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "slider-nav-scrollbar" : string.Empty; 189 string navigationSmall = (Model.Item.GetBoolean("NavigationSmall")) ? "slider-nav-sm" : string.Empty; 190 string navigationInvertColors = (Model.Item.GetBoolean("NavigationInvertColors")) ? "slider-nav-dark" : string.Empty; 191 string navigationSlideEntirePage = (Model.Item.GetBoolean("NavigationSlideEntirePage")) ? "slider-nav-page" : string.Empty; 192 string navigationNoLoop = (Model.Item.GetBoolean("NavigationNoLoop")) ? "slider-nav-noloop" : string.Empty; 193 string indicatorsOutsideSlider = (Model.Item.GetBoolean("IndicatorsOutsideSlider") && indicatorStyle != string.Empty) ? "slider-indicators-outside" : string.Empty; 194 string indicatorsHighlightActive = (Model.Item.GetBoolean("IndicatorsHighlightActive")) ? "slider-indicators-highlight" : string.Empty; 195 string indicatorsInvertColors = (Model.Item.GetBoolean("IndicatorsInvertedColors")) ? "slider-indicators-dark" : string.Empty; 196 string indicatorsVisibleOnSmallDevices = (Model.Item.GetBoolean("IndicatorsVisibleOnSmallDevices")) ? "slider-indicators-sm" : string.Empty; 197 198 bool productsFound = true; 199 if (string.IsNullOrEmpty(groupIds) && string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(productVariantIds)) 200 { 201 if (Pageview.IsVisualEditorMode) 202 { 203 productIds = product.Id; 204 sourceType = "selected"; 205 } 206 else 207 { 208 productsFound = false; 209 } 210 } 211 } 212 213 @*Container element for the request*@ 214 @if (productsFound) 215 { 216 <form method="post" action="@url" id="RelatedProductsForm_@Model.ID" data-response-target-element="RelatedProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> 217 <input type="hidden" name="ModelID" value="@Model.ID"> 218 <input type="hidden" name="SourceType" value="@sourceType"> 219 220 @*--- SLIDER SETTINGS ---*@ 221 <input type="hidden" name="NavigationStyle" value="@navigationStyle"> 222 <input type="hidden" name="NavigationPlacement" value="@navigationPlacement"> 223 <input type="hidden" name="IndicatorStyle" value="@indicatorStyle"> 224 <input type="hidden" name="RevealSlides" value="@revealSlides"> 225 <input type="hidden" name="NavigationAlwaysVisible" value="@(navigationAlwaysVisible)"> 226 <input type="hidden" name="NavigationVisibleOnTouch" value="@(navigationVisibleOnTouch)"> 227 <input type="hidden" name="NavigationShowScrollbar" value="@(navigationShowScrollbar)"> 228 <input type="hidden" name="NavigationSmall" value="@(navigationSmall)"> 229 <input type="hidden" name="NavigationInvertColors" value="@(navigationInvertColors)"> 230 <input type="hidden" name="NavigationNoLoop" value="@(navigationNoLoop)"> 231 <input type="hidden" name="NavigationSlideEntirePage" value="@(navigationSlideEntirePage)"> 232 <input type="hidden" name="IndicatorsOutsideSlider" value="@(indicatorsOutsideSlider)"> 233 <input type="hidden" name="IndicatorsHighlightActive" value="@(indicatorsHighlightActive)"> 234 <input type="hidden" name="IndicatorsInvertColors" value="@(indicatorsInvertColors)"> 235 <input type="hidden" name="IndicatorsVisibleOnSmallDevices" value="@(indicatorsVisibleOnSmallDevices)"> 236 237 @*--- VARIANTS ---*@ 238 @if (sourceType == "variants") 239 { 240 <input type="hidden" name="isVariant" value="true"> 241 <input type="hidden" name="MainProductID" id="MainProductID_@Model.ID" value="@productIds"> 242 } 243 244 @*--- MOST SOLD ---*@ 245 @if (sourceType == "most-sold") 246 { 247 <input type="hidden" name="SortBy" value="OrderCount"> 248 if (groupIds != "") 249 { 250 <input type="hidden" name="GroupId" value="@groupIds"> 251 } 252 } 253 254 @*--- TRENDING ---*@ 255 @if (sourceType == "trending") 256 { 257 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 258 if (groupIds != "") 259 { 260 <input type="hidden" name="GroupId" value="@groupIds"> 261 } 262 } 263 264 @*--- FREQUENTLY BOUGHT ---*@ 265 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 266 { 267 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 268 } 269 @if (sourceType != "frequently" && hasVariants) 270 { 271 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 272 } 273 274 @*--- LATEST ---*@ 275 @if (sourceType == "latest") 276 { 277 <input type="hidden" name="SortBy" value="Created"> 278 <input type="hidden" name="GroupId" value="@groupIds"> 279 } 280 281 @*--- SELECTED PRODUCTS ---*@ 282 @if (sourceType == "selected" && !string.IsNullOrEmpty(productIds) && !hasVariants) 283 { 284 <input type="hidden" name="MainProductId" id="MainProductID_@Model.ID" value="@productIds"> 285 } 286 @if (sourceType == "selected" && hasVariants) 287 { 288 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 289 } 290 291 @*--- RELATED PRODUCTS ---*@ 292 @if (sourceType == "related-products") 293 { 294 <input type="hidden" name="ProductVariantId" id="MainProductID_@Model.ID" value="@productVariantIds"> 295 } 296 297 @* General parameters *@ 298 <input type="hidden" name="Link" value="@link"> 299 <input type="hidden" name="HideTitle" value="@Model.Item.GetString("HideTitle")"> 300 301 @if (Model.Item.GetInt32("ProductsCount") != 0) 302 { 303 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 304 } 305 <input type="hidden" name="HeadingTitle" id="RelatedProductsTitle_@Model.ID" value="@title"> 306 @if (!string.IsNullOrEmpty(Model.Item.GetString("Subtitle"))) 307 { 308 <input type="hidden" name="Subtitle" value="@Model.Item.GetString("Subtitle")"> 309 } 310 @if (!string.IsNullOrEmpty(Model.Item.GetString("LinkText"))) 311 { 312 <input type="hidden" name="LinkText" value="@Model.Item.GetString("LinkText")"> 313 } 314 @if (!string.IsNullOrEmpty(Model.Item.GetString("ImageAspectRatio"))) 315 { 316 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 317 ratio = ratio != "0" ? ratio : ""; 318 <input type="hidden" name="ImageAspectRatio" value="@ratio"> 319 } 320 @if (!string.IsNullOrEmpty(Model.Item.GetString("Layout"))) 321 { 322 <input type="hidden" name="Layout" value="@Model.Item.GetRawValueString("Layout")"> 323 } 324 @if (titleFontSize != "") 325 { 326 <input type="hidden" name="TitleFontSize" value="@titleFontSize"> 327 } 328 @if (subtitleFontSize != "") 329 { 330 <input type="hidden" name="SubtitleFontSize" value="@subtitleFontSize"> 331 } 332 @if (buttonStyle != "") 333 { 334 <input type="hidden" name="ButtonStyle" value="@buttonStyle"> 335 } 336 @if (generalTheme != "") 337 { 338 <input type="hidden" name="GeneralTheme" value="@generalTheme"> 339 } 340 @if (theme != "") 341 { 342 <input type="hidden" name="Theme" value="@theme"> 343 } 344 @if (imageTheme != "") 345 { 346 <input type="hidden" name="ImageTheme" value="@imageTheme"> 347 } 348 @if (!string.IsNullOrEmpty(Model.Item.GetString("ContentPadding"))) 349 { 350 string contentPadding = Model.Item.GetRawValueString("ContentPadding"); 351 <input type="hidden" name="ContentPadding" value="@contentPadding"> 352 } 353 <input type="hidden" name="TextReadability" value="@maxWidth"> 354 <input type="hidden" name="ParentColumnSize" id="ParentColumnSize_@Model.ID" value="12"> 355 356 <input type="hidden" name="SaleBadgeType" value="@Model.Item.GetRawValue("SaleBadgeType")"> 357 <input type="hidden" name="SaleBadgeCssClassName" value="@Model.Item.GetRawValue("SaleBadgeDesign")"> 358 <input type="hidden" name="NewBadgeCssClassName" value="@Model.Item.GetRawValue("NewBadgeDesign")"> 359 <input type="hidden" name="NewPublicationDays" value="@Model.Item.GetInt32("NewPublicationDays")"> 360 361 @if (campaignValues != string.Empty) 362 { 363 <input type="hidden" name="CampaignBadgesValues" value="@campaignValues"> 364 } 365 </form> 366 367 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 368 <script> 369 window.addEventListener("load", () => { 370 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 371 }); 372 </script> 373 374 if (Pageview.IsVisualEditorMode) 375 { 376 <div class="alert alert-info" role="alert"> 377 <span>@Translate("Product slider: Edit this column to configure")</span> 378 </div> 379 } 380 381 if (sourceType != "related-products") 382 { 383 <div class="w-100 h-100"> 384 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 385 <div id="RelatedProducts_@Model.ID" class="h-100 swift_product_slider_container"></div> 386 </div> 387 } 388 else if (product?.RelatedGroups != null) 389 { 390 @* Create multiple slider containers, if type is Product relation *@ 391 <div class="grid w-100 h-100@(generalTheme)" style="grid-row-gap: 4rem"> 392 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 393 @foreach (var group in product.RelatedGroups) 394 { 395 <div id="RelatedProducts_@(Model.ID)_@group.Id" class="g-col-12 h-100 swift_product_slider_container"></div> 396 } 397 </div> 398 } 399 400 @* Initialize *@ 401 if (sourceType != "related-products") 402 { 403 <script type="module"> 404 if (document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]")) { 405 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]").getAttribute("data-col-size"); 406 } 407 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")).then(function () { 408 setTimeout(function() { 409 const isVisualEditor = @(Converter.ToString(Pageview.IsVisualEditorMode).ToLowerInvariant()); 410 const productSliderContainer = document.querySelector(".swift_product_slider_container"); 411 412 if (productSliderContainer && productSliderContainer.innerHTML !== "") { 413 productSliderContainer.classList.remove("d-none"); 414 } 415 else if (!isVisualEditor) { 416 productSliderContainer.closest("[class*=column]").classList.add("d-none"); 417 } 418 }, 150); 419 }); 420 </script> 421 } 422 else if (product?.RelatedGroups != null) 423 { 424 @* Create multiple sliders, if type is Product relation *@ 425 foreach (var group in product.RelatedGroups) 426 { 427 IList<string> fromProductIds = new List<string> { }; 428 429 foreach (var relatedProduct in group.Products) 430 { 431 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 432 { 433 fromProductIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 434 } 435 else 436 { 437 fromProductIds.Add($"{relatedProduct.ProductId}"); 438 } 439 } 440 <script type="module"> 441 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@(Model.ID)_@group.Id").closest("[data-col-size]").getAttribute("data-col-size"); 442 document.querySelector("#MainProductID_@Model.ID").value = "@string.Join(",", fromProductIds)"; 443 document.querySelector("#RelatedProductsTitle_@Model.ID").value = "@group.Name"; 444 document.querySelector("#RelatedProductsForm_@Model.ID").setAttribute("data-response-target-element", "RelatedProducts_@(Model.ID)_@group.Id"); 445 446 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")); 447 </script> 448 } 449 } 450 } 451

Got questions to this product?

If you have any questions about the product, pricing, or purchasing options, don't hesitate to reach out to us.

An error occurred while attaching module (Dynamicweb.Frontend.Content)

System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) ---> System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at Dynamicweb.Data.DatabaseConnectionProvider.CreateConnection(Boolean open)
   at Dynamicweb.Data.Database.CreateConnection()
   at Dynamicweb.Data.Database.CreateDataReader(String sql, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout, Dictionary`2 sqlParams)
   at Dynamicweb.Forms.FormRepository.GetFormById(Int32 id)
   at Dynamicweb.Forms.Form.GetFormById(Int32 id)
   at Dynamicweb.Forms.Frontend.RenderForm()
   at Dynamicweb.Forms.Frontend.GetContent()
   at Dynamicweb.Frontend.Content.GetModuleOutput(Paragraph paragraph, PageView pageview)
ClientConnectionId:00000000-0000-0000-0000-000000000000
Error Number:2,State:0,Class:20

Error executing template "/Designs/Swift/Paragraph/Swift_RelatedProducts.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_9e890a2f70da40818198e9f7a675cf2c.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_RelatedProducts.cshtml:line 164
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Core 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 14 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 15 16 if (productList?.Products is object) 17 { 18 product = productList.Products[0]; 19 } 20 } 21 22 string title = Model?.Item?.GetRawValueString("Title", Translate("Products")); 23 string campaignValues = string.Join(",", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 24 25 //Styling 26 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 27 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "fs-5"); 28 string buttonStyle = Model.Item.GetRawValueString("ButtonStyle", ""); 29 buttonStyle = buttonStyle == "primary" ? " btn-primary" : buttonStyle; 30 buttonStyle = buttonStyle == "secondary" ? " btn-secondary" : buttonStyle; 31 buttonStyle = buttonStyle == "link" ? " btn-link" : buttonStyle; 32 string maxWidth = Model.Item.GetRawValueString("TextReadability", ""); 33 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 34 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 35 36 string generalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GeneralTheme")) ? " theme " + Model.Item.GetRawValueString("GeneralTheme").Replace(" ", "").Trim().ToLower() : ""; 37 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 38 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 39 40 //Link generation 41 string pageId = !string.IsNullOrEmpty(Model.Item.GetRawValueString("ProductSliderServicePage")) ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 42 if (string.IsNullOrEmpty(pageId)) 43 { 44 pageId = GetPageIdByNavigationTag("ProductSliderService").ToString(); 45 } 46 47 string url = "/Default.aspx?ID=" + pageId; 48 if (!url.Contains("LayoutTemplate", StringComparison.OrdinalIgnoreCase)) 49 { 50 url += url.Contains("?") ? "&LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml" : "?LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml"; 51 } 52 if (Pageview.IsVisualEditorMode) 53 { 54 url += "&VisualEdit=True"; 55 } 56 57 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 58 if (isLazyLoadingForProductInfoEnabled) 59 { 60 url += "&getproductinfo=true"; 61 } 62 63 //Source type 64 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 65 IList<string> relateFromGroupIds = new List<string> { }; 66 IList<string> relateFromProductVariantIds = new List<string> { }; 67 IList<string> relateFromProductIds = new List<string> { }; 68 bool hasVariants = false; 69 70 //--- VARIANTS --- 71 if (sourceType == "variants" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateToVariants") is ProductListViewModel productsToRelateToVariants) 72 { 73 foreach (var productSelection in productsToRelateToVariants.Products) 74 { 75 relateFromProductIds.Add(productSelection.Id); 76 } 77 } 78 79 //--- MOST SOLD --- 80 if (sourceType == "most-sold" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToMostSold") is IList<ProductGroupViewModel> groupsToRelateToMostSold) 81 { 82 foreach (var fromGroup in groupsToRelateToMostSold) 83 { 84 relateFromGroupIds.Add(fromGroup.Id); 85 } 86 } 87 88 //--- TRENDING --- 89 if (sourceType == "trending" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToTrending") is IList<ProductGroupViewModel> groupsToRelateToTrending) 90 { 91 foreach (var fromGroup in groupsToRelateToTrending) 92 { 93 relateFromGroupIds.Add(fromGroup.Id); 94 } 95 } 96 97 //--- LATEST --- 98 if (sourceType == "latest" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToLatest") is IList<ProductGroupViewModel> groupsToRelateToLatest) 99 { 100 foreach (var fromGroup in groupsToRelateToLatest) 101 { 102 relateFromGroupIds.Add(fromGroup.Id); 103 } 104 } 105 106 //--- FREQUENTLY BOUGHT --- 107 if (sourceType == "frequently" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo") is ProductListViewModel productsToRelateTo) 108 { 109 foreach (var fromProduct in productsToRelateTo.Products) 110 { 111 relateFromProductIds.Add(fromProduct.Id); 112 } 113 } 114 115 //--- SELECTED PRODUCTS --- 116 if ((sourceType == "selected" || sourceType == "frequently") && Model.Item.GetValue<ProductListViewModel>("Products") is ProductListViewModel products) 117 { 118 hasVariants = products.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 119 foreach (var productSelection in products.Products) 120 { 121 if (hasVariants) 122 { 123 if (!string.IsNullOrEmpty(productSelection.VariantId)) 124 { 125 relateFromProductVariantIds.Add($"{productSelection.Id} {productSelection.VariantId}"); 126 } 127 else 128 { 129 relateFromProductVariantIds.Add($"{productSelection.Id}"); 130 } 131 } 132 relateFromProductIds.Add($"{productSelection.Id}"); 133 } 134 } 135 136 //--- RELATED PRODUCTS --- 137 if (sourceType == "related-products" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo2") is ProductListViewModel selectedRelationProduct) 138 { 139 if (selectedRelationProduct.Products.Any()) 140 { 141 product = selectedRelationProduct.Products.FirstOrDefault(); 142 } 143 144 if (product?.RelatedGroups != null) 145 { 146 foreach (var group in product.RelatedGroups) 147 { 148 foreach (var relatedProduct in group.Products) 149 { 150 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 151 { 152 relateFromProductVariantIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 153 } 154 else 155 { 156 relateFromProductVariantIds.Add($"{relatedProduct.ProductId}"); 157 } 158 } 159 } 160 } 161 } 162 163 //Create group id collection and products id collection strings 164 string groupIds = product is object ? product.PrimaryOrDefaultGroup.Id : string.Join(",", relateFromGroupIds); 165 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 166 string productIds = product is object && relateFromProductIds.Count == 0 ? product.Id : string.Join(",", relateFromProductIds); 167 168 //Set the parameters to the url 169 string linkParameters = ""; 170 linkParameters += sourceType != "related-products" && sourceType != "frequently" && sourceType != "selected" ? "&GroupId=" + groupIds : ""; 171 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType != "most-sold" && sourceType != "trending" && sourceType != "latest" && sourceType != "frequently" && sourceType != "related-products" ? "&MainProductId=" + productIds : ""; 172 linkParameters += !string.IsNullOrEmpty(productVariantIds) && sourceType == "related-products" ? "&ProductVariantId=" + productVariantIds : ""; 173 linkParameters += sourceType == "variants" ? "&IsVariant=true" : ""; 174 linkParameters += sourceType == "latest" ? "&SortBy=Created" : ""; 175 linkParameters += sourceType == "most-sold" ? "&SortBy=OrderCount" : ""; 176 linkParameters += sourceType == "trending" ? "&SortBy=OrderCountGrowth" : ""; 177 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType == "frequently" ? $"&BoughtWithProductIds=[{productIds}]" : ""; 178 var productListPageId = GetPageIdByNavigationTag("Shop"); 179 string link = "/Default.aspx?ID=" + productListPageId + linkParameters; 180 181 // Slider settings (documentation: swiffyslider.com/configuration) 182 string navigationStyle = $"{Model.Item.GetRawValueString("NavigationStyle", "slider-nav-round")}"; 183 string navigationPlacement = $"{Model.Item.GetRawValueString("NavigationPlacement", "slider-nav-on-slides")}"; 184 string indicatorStyle = $"{Model.Item.GetRawValueString("IndicatorStyle", "slider-indicators-hidden")}"; 185 string revealSlides = Model.Item.GetRawValueString("RevealSlides", "no-reveal") == "reveal" ? "slider-item-reveal" : string.Empty; 186 string navigationAlwaysVisible = (Model.Item.GetBoolean("NavigationAlwaysVisible")) ? "slider-nav-visible" : string.Empty; 187 string navigationVisibleOnTouch = (Model.Item.GetBoolean("NavigationVisibleOnTouch")) ? "slider-nav-touch" : string.Empty; 188 string navigationShowScrollbar = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "slider-nav-scrollbar" : string.Empty; 189 string navigationSmall = (Model.Item.GetBoolean("NavigationSmall")) ? "slider-nav-sm" : string.Empty; 190 string navigationInvertColors = (Model.Item.GetBoolean("NavigationInvertColors")) ? "slider-nav-dark" : string.Empty; 191 string navigationSlideEntirePage = (Model.Item.GetBoolean("NavigationSlideEntirePage")) ? "slider-nav-page" : string.Empty; 192 string navigationNoLoop = (Model.Item.GetBoolean("NavigationNoLoop")) ? "slider-nav-noloop" : string.Empty; 193 string indicatorsOutsideSlider = (Model.Item.GetBoolean("IndicatorsOutsideSlider") && indicatorStyle != string.Empty) ? "slider-indicators-outside" : string.Empty; 194 string indicatorsHighlightActive = (Model.Item.GetBoolean("IndicatorsHighlightActive")) ? "slider-indicators-highlight" : string.Empty; 195 string indicatorsInvertColors = (Model.Item.GetBoolean("IndicatorsInvertedColors")) ? "slider-indicators-dark" : string.Empty; 196 string indicatorsVisibleOnSmallDevices = (Model.Item.GetBoolean("IndicatorsVisibleOnSmallDevices")) ? "slider-indicators-sm" : string.Empty; 197 198 bool productsFound = true; 199 if (string.IsNullOrEmpty(groupIds) && string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(productVariantIds)) 200 { 201 if (Pageview.IsVisualEditorMode) 202 { 203 productIds = product.Id; 204 sourceType = "selected"; 205 } 206 else 207 { 208 productsFound = false; 209 } 210 } 211 } 212 213 @*Container element for the request*@ 214 @if (productsFound) 215 { 216 <form method="post" action="@url" id="RelatedProductsForm_@Model.ID" data-response-target-element="RelatedProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> 217 <input type="hidden" name="ModelID" value="@Model.ID"> 218 <input type="hidden" name="SourceType" value="@sourceType"> 219 220 @*--- SLIDER SETTINGS ---*@ 221 <input type="hidden" name="NavigationStyle" value="@navigationStyle"> 222 <input type="hidden" name="NavigationPlacement" value="@navigationPlacement"> 223 <input type="hidden" name="IndicatorStyle" value="@indicatorStyle"> 224 <input type="hidden" name="RevealSlides" value="@revealSlides"> 225 <input type="hidden" name="NavigationAlwaysVisible" value="@(navigationAlwaysVisible)"> 226 <input type="hidden" name="NavigationVisibleOnTouch" value="@(navigationVisibleOnTouch)"> 227 <input type="hidden" name="NavigationShowScrollbar" value="@(navigationShowScrollbar)"> 228 <input type="hidden" name="NavigationSmall" value="@(navigationSmall)"> 229 <input type="hidden" name="NavigationInvertColors" value="@(navigationInvertColors)"> 230 <input type="hidden" name="NavigationNoLoop" value="@(navigationNoLoop)"> 231 <input type="hidden" name="NavigationSlideEntirePage" value="@(navigationSlideEntirePage)"> 232 <input type="hidden" name="IndicatorsOutsideSlider" value="@(indicatorsOutsideSlider)"> 233 <input type="hidden" name="IndicatorsHighlightActive" value="@(indicatorsHighlightActive)"> 234 <input type="hidden" name="IndicatorsInvertColors" value="@(indicatorsInvertColors)"> 235 <input type="hidden" name="IndicatorsVisibleOnSmallDevices" value="@(indicatorsVisibleOnSmallDevices)"> 236 237 @*--- VARIANTS ---*@ 238 @if (sourceType == "variants") 239 { 240 <input type="hidden" name="isVariant" value="true"> 241 <input type="hidden" name="MainProductID" id="MainProductID_@Model.ID" value="@productIds"> 242 } 243 244 @*--- MOST SOLD ---*@ 245 @if (sourceType == "most-sold") 246 { 247 <input type="hidden" name="SortBy" value="OrderCount"> 248 if (groupIds != "") 249 { 250 <input type="hidden" name="GroupId" value="@groupIds"> 251 } 252 } 253 254 @*--- TRENDING ---*@ 255 @if (sourceType == "trending") 256 { 257 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 258 if (groupIds != "") 259 { 260 <input type="hidden" name="GroupId" value="@groupIds"> 261 } 262 } 263 264 @*--- FREQUENTLY BOUGHT ---*@ 265 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 266 { 267 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 268 } 269 @if (sourceType != "frequently" && hasVariants) 270 { 271 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 272 } 273 274 @*--- LATEST ---*@ 275 @if (sourceType == "latest") 276 { 277 <input type="hidden" name="SortBy" value="Created"> 278 <input type="hidden" name="GroupId" value="@groupIds"> 279 } 280 281 @*--- SELECTED PRODUCTS ---*@ 282 @if (sourceType == "selected" && !string.IsNullOrEmpty(productIds) && !hasVariants) 283 { 284 <input type="hidden" name="MainProductId" id="MainProductID_@Model.ID" value="@productIds"> 285 } 286 @if (sourceType == "selected" && hasVariants) 287 { 288 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 289 } 290 291 @*--- RELATED PRODUCTS ---*@ 292 @if (sourceType == "related-products") 293 { 294 <input type="hidden" name="ProductVariantId" id="MainProductID_@Model.ID" value="@productVariantIds"> 295 } 296 297 @* General parameters *@ 298 <input type="hidden" name="Link" value="@link"> 299 <input type="hidden" name="HideTitle" value="@Model.Item.GetString("HideTitle")"> 300 301 @if (Model.Item.GetInt32("ProductsCount") != 0) 302 { 303 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 304 } 305 <input type="hidden" name="HeadingTitle" id="RelatedProductsTitle_@Model.ID" value="@title"> 306 @if (!string.IsNullOrEmpty(Model.Item.GetString("Subtitle"))) 307 { 308 <input type="hidden" name="Subtitle" value="@Model.Item.GetString("Subtitle")"> 309 } 310 @if (!string.IsNullOrEmpty(Model.Item.GetString("LinkText"))) 311 { 312 <input type="hidden" name="LinkText" value="@Model.Item.GetString("LinkText")"> 313 } 314 @if (!string.IsNullOrEmpty(Model.Item.GetString("ImageAspectRatio"))) 315 { 316 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 317 ratio = ratio != "0" ? ratio : ""; 318 <input type="hidden" name="ImageAspectRatio" value="@ratio"> 319 } 320 @if (!string.IsNullOrEmpty(Model.Item.GetString("Layout"))) 321 { 322 <input type="hidden" name="Layout" value="@Model.Item.GetRawValueString("Layout")"> 323 } 324 @if (titleFontSize != "") 325 { 326 <input type="hidden" name="TitleFontSize" value="@titleFontSize"> 327 } 328 @if (subtitleFontSize != "") 329 { 330 <input type="hidden" name="SubtitleFontSize" value="@subtitleFontSize"> 331 } 332 @if (buttonStyle != "") 333 { 334 <input type="hidden" name="ButtonStyle" value="@buttonStyle"> 335 } 336 @if (generalTheme != "") 337 { 338 <input type="hidden" name="GeneralTheme" value="@generalTheme"> 339 } 340 @if (theme != "") 341 { 342 <input type="hidden" name="Theme" value="@theme"> 343 } 344 @if (imageTheme != "") 345 { 346 <input type="hidden" name="ImageTheme" value="@imageTheme"> 347 } 348 @if (!string.IsNullOrEmpty(Model.Item.GetString("ContentPadding"))) 349 { 350 string contentPadding = Model.Item.GetRawValueString("ContentPadding"); 351 <input type="hidden" name="ContentPadding" value="@contentPadding"> 352 } 353 <input type="hidden" name="TextReadability" value="@maxWidth"> 354 <input type="hidden" name="ParentColumnSize" id="ParentColumnSize_@Model.ID" value="12"> 355 356 <input type="hidden" name="SaleBadgeType" value="@Model.Item.GetRawValue("SaleBadgeType")"> 357 <input type="hidden" name="SaleBadgeCssClassName" value="@Model.Item.GetRawValue("SaleBadgeDesign")"> 358 <input type="hidden" name="NewBadgeCssClassName" value="@Model.Item.GetRawValue("NewBadgeDesign")"> 359 <input type="hidden" name="NewPublicationDays" value="@Model.Item.GetInt32("NewPublicationDays")"> 360 361 @if (campaignValues != string.Empty) 362 { 363 <input type="hidden" name="CampaignBadgesValues" value="@campaignValues"> 364 } 365 </form> 366 367 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 368 <script> 369 window.addEventListener("load", () => { 370 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 371 }); 372 </script> 373 374 if (Pageview.IsVisualEditorMode) 375 { 376 <div class="alert alert-info" role="alert"> 377 <span>@Translate("Product slider: Edit this column to configure")</span> 378 </div> 379 } 380 381 if (sourceType != "related-products") 382 { 383 <div class="w-100 h-100"> 384 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 385 <div id="RelatedProducts_@Model.ID" class="h-100 swift_product_slider_container"></div> 386 </div> 387 } 388 else if (product?.RelatedGroups != null) 389 { 390 @* Create multiple slider containers, if type is Product relation *@ 391 <div class="grid w-100 h-100@(generalTheme)" style="grid-row-gap: 4rem"> 392 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 393 @foreach (var group in product.RelatedGroups) 394 { 395 <div id="RelatedProducts_@(Model.ID)_@group.Id" class="g-col-12 h-100 swift_product_slider_container"></div> 396 } 397 </div> 398 } 399 400 @* Initialize *@ 401 if (sourceType != "related-products") 402 { 403 <script type="module"> 404 if (document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]")) { 405 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]").getAttribute("data-col-size"); 406 } 407 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")).then(function () { 408 setTimeout(function() { 409 const isVisualEditor = @(Converter.ToString(Pageview.IsVisualEditorMode).ToLowerInvariant()); 410 const productSliderContainer = document.querySelector(".swift_product_slider_container"); 411 412 if (productSliderContainer && productSliderContainer.innerHTML !== "") { 413 productSliderContainer.classList.remove("d-none"); 414 } 415 else if (!isVisualEditor) { 416 productSliderContainer.closest("[class*=column]").classList.add("d-none"); 417 } 418 }, 150); 419 }); 420 </script> 421 } 422 else if (product?.RelatedGroups != null) 423 { 424 @* Create multiple sliders, if type is Product relation *@ 425 foreach (var group in product.RelatedGroups) 426 { 427 IList<string> fromProductIds = new List<string> { }; 428 429 foreach (var relatedProduct in group.Products) 430 { 431 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 432 { 433 fromProductIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 434 } 435 else 436 { 437 fromProductIds.Add($"{relatedProduct.ProductId}"); 438 } 439 } 440 <script type="module"> 441 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@(Model.ID)_@group.Id").closest("[data-col-size]").getAttribute("data-col-size"); 442 document.querySelector("#MainProductID_@Model.ID").value = "@string.Join(",", fromProductIds)"; 443 document.querySelector("#RelatedProductsTitle_@Model.ID").value = "@group.Name"; 444 document.querySelector("#RelatedProductsForm_@Model.ID").setAttribute("data-response-target-element", "RelatedProducts_@(Model.ID)_@group.Id"); 445 446 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")); 447 </script> 448 } 449 } 450 } 451
Error executing template "Designs/Swift/Paragraph/Swift_RelatedProducts.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_9e890a2f70da40818198e9f7a675cf2c.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_RelatedProducts.cshtml:line 164
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Core 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 14 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 15 16 if (productList?.Products is object) 17 { 18 product = productList.Products[0]; 19 } 20 } 21 22 string title = Model?.Item?.GetRawValueString("Title", Translate("Products")); 23 string campaignValues = string.Join(",", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 24 25 //Styling 26 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 27 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "fs-5"); 28 string buttonStyle = Model.Item.GetRawValueString("ButtonStyle", ""); 29 buttonStyle = buttonStyle == "primary" ? " btn-primary" : buttonStyle; 30 buttonStyle = buttonStyle == "secondary" ? " btn-secondary" : buttonStyle; 31 buttonStyle = buttonStyle == "link" ? " btn-link" : buttonStyle; 32 string maxWidth = Model.Item.GetRawValueString("TextReadability", ""); 33 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 34 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 35 36 string generalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GeneralTheme")) ? " theme " + Model.Item.GetRawValueString("GeneralTheme").Replace(" ", "").Trim().ToLower() : ""; 37 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 38 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 39 40 //Link generation 41 string pageId = !string.IsNullOrEmpty(Model.Item.GetRawValueString("ProductSliderServicePage")) ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 42 if (string.IsNullOrEmpty(pageId)) 43 { 44 pageId = GetPageIdByNavigationTag("ProductSliderService").ToString(); 45 } 46 47 string url = "/Default.aspx?ID=" + pageId; 48 if (!url.Contains("LayoutTemplate", StringComparison.OrdinalIgnoreCase)) 49 { 50 url += url.Contains("?") ? "&LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml" : "?LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml"; 51 } 52 if (Pageview.IsVisualEditorMode) 53 { 54 url += "&VisualEdit=True"; 55 } 56 57 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 58 if (isLazyLoadingForProductInfoEnabled) 59 { 60 url += "&getproductinfo=true"; 61 } 62 63 //Source type 64 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 65 IList<string> relateFromGroupIds = new List<string> { }; 66 IList<string> relateFromProductVariantIds = new List<string> { }; 67 IList<string> relateFromProductIds = new List<string> { }; 68 bool hasVariants = false; 69 70 //--- VARIANTS --- 71 if (sourceType == "variants" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateToVariants") is ProductListViewModel productsToRelateToVariants) 72 { 73 foreach (var productSelection in productsToRelateToVariants.Products) 74 { 75 relateFromProductIds.Add(productSelection.Id); 76 } 77 } 78 79 //--- MOST SOLD --- 80 if (sourceType == "most-sold" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToMostSold") is IList<ProductGroupViewModel> groupsToRelateToMostSold) 81 { 82 foreach (var fromGroup in groupsToRelateToMostSold) 83 { 84 relateFromGroupIds.Add(fromGroup.Id); 85 } 86 } 87 88 //--- TRENDING --- 89 if (sourceType == "trending" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToTrending") is IList<ProductGroupViewModel> groupsToRelateToTrending) 90 { 91 foreach (var fromGroup in groupsToRelateToTrending) 92 { 93 relateFromGroupIds.Add(fromGroup.Id); 94 } 95 } 96 97 //--- LATEST --- 98 if (sourceType == "latest" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToLatest") is IList<ProductGroupViewModel> groupsToRelateToLatest) 99 { 100 foreach (var fromGroup in groupsToRelateToLatest) 101 { 102 relateFromGroupIds.Add(fromGroup.Id); 103 } 104 } 105 106 //--- FREQUENTLY BOUGHT --- 107 if (sourceType == "frequently" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo") is ProductListViewModel productsToRelateTo) 108 { 109 foreach (var fromProduct in productsToRelateTo.Products) 110 { 111 relateFromProductIds.Add(fromProduct.Id); 112 } 113 } 114 115 //--- SELECTED PRODUCTS --- 116 if ((sourceType == "selected" || sourceType == "frequently") && Model.Item.GetValue<ProductListViewModel>("Products") is ProductListViewModel products) 117 { 118 hasVariants = products.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 119 foreach (var productSelection in products.Products) 120 { 121 if (hasVariants) 122 { 123 if (!string.IsNullOrEmpty(productSelection.VariantId)) 124 { 125 relateFromProductVariantIds.Add($"{productSelection.Id} {productSelection.VariantId}"); 126 } 127 else 128 { 129 relateFromProductVariantIds.Add($"{productSelection.Id}"); 130 } 131 } 132 relateFromProductIds.Add($"{productSelection.Id}"); 133 } 134 } 135 136 //--- RELATED PRODUCTS --- 137 if (sourceType == "related-products" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo2") is ProductListViewModel selectedRelationProduct) 138 { 139 if (selectedRelationProduct.Products.Any()) 140 { 141 product = selectedRelationProduct.Products.FirstOrDefault(); 142 } 143 144 if (product?.RelatedGroups != null) 145 { 146 foreach (var group in product.RelatedGroups) 147 { 148 foreach (var relatedProduct in group.Products) 149 { 150 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 151 { 152 relateFromProductVariantIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 153 } 154 else 155 { 156 relateFromProductVariantIds.Add($"{relatedProduct.ProductId}"); 157 } 158 } 159 } 160 } 161 } 162 163 //Create group id collection and products id collection strings 164 string groupIds = product is object ? product.PrimaryOrDefaultGroup.Id : string.Join(",", relateFromGroupIds); 165 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 166 string productIds = product is object && relateFromProductIds.Count == 0 ? product.Id : string.Join(",", relateFromProductIds); 167 168 //Set the parameters to the url 169 string linkParameters = ""; 170 linkParameters += sourceType != "related-products" && sourceType != "frequently" && sourceType != "selected" ? "&GroupId=" + groupIds : ""; 171 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType != "most-sold" && sourceType != "trending" && sourceType != "latest" && sourceType != "frequently" && sourceType != "related-products" ? "&MainProductId=" + productIds : ""; 172 linkParameters += !string.IsNullOrEmpty(productVariantIds) && sourceType == "related-products" ? "&ProductVariantId=" + productVariantIds : ""; 173 linkParameters += sourceType == "variants" ? "&IsVariant=true" : ""; 174 linkParameters += sourceType == "latest" ? "&SortBy=Created" : ""; 175 linkParameters += sourceType == "most-sold" ? "&SortBy=OrderCount" : ""; 176 linkParameters += sourceType == "trending" ? "&SortBy=OrderCountGrowth" : ""; 177 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType == "frequently" ? $"&BoughtWithProductIds=[{productIds}]" : ""; 178 var productListPageId = GetPageIdByNavigationTag("Shop"); 179 string link = "/Default.aspx?ID=" + productListPageId + linkParameters; 180 181 // Slider settings (documentation: swiffyslider.com/configuration) 182 string navigationStyle = $"{Model.Item.GetRawValueString("NavigationStyle", "slider-nav-round")}"; 183 string navigationPlacement = $"{Model.Item.GetRawValueString("NavigationPlacement", "slider-nav-on-slides")}"; 184 string indicatorStyle = $"{Model.Item.GetRawValueString("IndicatorStyle", "slider-indicators-hidden")}"; 185 string revealSlides = Model.Item.GetRawValueString("RevealSlides", "no-reveal") == "reveal" ? "slider-item-reveal" : string.Empty; 186 string navigationAlwaysVisible = (Model.Item.GetBoolean("NavigationAlwaysVisible")) ? "slider-nav-visible" : string.Empty; 187 string navigationVisibleOnTouch = (Model.Item.GetBoolean("NavigationVisibleOnTouch")) ? "slider-nav-touch" : string.Empty; 188 string navigationShowScrollbar = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "slider-nav-scrollbar" : string.Empty; 189 string navigationSmall = (Model.Item.GetBoolean("NavigationSmall")) ? "slider-nav-sm" : string.Empty; 190 string navigationInvertColors = (Model.Item.GetBoolean("NavigationInvertColors")) ? "slider-nav-dark" : string.Empty; 191 string navigationSlideEntirePage = (Model.Item.GetBoolean("NavigationSlideEntirePage")) ? "slider-nav-page" : string.Empty; 192 string navigationNoLoop = (Model.Item.GetBoolean("NavigationNoLoop")) ? "slider-nav-noloop" : string.Empty; 193 string indicatorsOutsideSlider = (Model.Item.GetBoolean("IndicatorsOutsideSlider") && indicatorStyle != string.Empty) ? "slider-indicators-outside" : string.Empty; 194 string indicatorsHighlightActive = (Model.Item.GetBoolean("IndicatorsHighlightActive")) ? "slider-indicators-highlight" : string.Empty; 195 string indicatorsInvertColors = (Model.Item.GetBoolean("IndicatorsInvertedColors")) ? "slider-indicators-dark" : string.Empty; 196 string indicatorsVisibleOnSmallDevices = (Model.Item.GetBoolean("IndicatorsVisibleOnSmallDevices")) ? "slider-indicators-sm" : string.Empty; 197 198 bool productsFound = true; 199 if (string.IsNullOrEmpty(groupIds) && string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(productVariantIds)) 200 { 201 if (Pageview.IsVisualEditorMode) 202 { 203 productIds = product.Id; 204 sourceType = "selected"; 205 } 206 else 207 { 208 productsFound = false; 209 } 210 } 211 } 212 213 @*Container element for the request*@ 214 @if (productsFound) 215 { 216 <form method="post" action="@url" id="RelatedProductsForm_@Model.ID" data-response-target-element="RelatedProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> 217 <input type="hidden" name="ModelID" value="@Model.ID"> 218 <input type="hidden" name="SourceType" value="@sourceType"> 219 220 @*--- SLIDER SETTINGS ---*@ 221 <input type="hidden" name="NavigationStyle" value="@navigationStyle"> 222 <input type="hidden" name="NavigationPlacement" value="@navigationPlacement"> 223 <input type="hidden" name="IndicatorStyle" value="@indicatorStyle"> 224 <input type="hidden" name="RevealSlides" value="@revealSlides"> 225 <input type="hidden" name="NavigationAlwaysVisible" value="@(navigationAlwaysVisible)"> 226 <input type="hidden" name="NavigationVisibleOnTouch" value="@(navigationVisibleOnTouch)"> 227 <input type="hidden" name="NavigationShowScrollbar" value="@(navigationShowScrollbar)"> 228 <input type="hidden" name="NavigationSmall" value="@(navigationSmall)"> 229 <input type="hidden" name="NavigationInvertColors" value="@(navigationInvertColors)"> 230 <input type="hidden" name="NavigationNoLoop" value="@(navigationNoLoop)"> 231 <input type="hidden" name="NavigationSlideEntirePage" value="@(navigationSlideEntirePage)"> 232 <input type="hidden" name="IndicatorsOutsideSlider" value="@(indicatorsOutsideSlider)"> 233 <input type="hidden" name="IndicatorsHighlightActive" value="@(indicatorsHighlightActive)"> 234 <input type="hidden" name="IndicatorsInvertColors" value="@(indicatorsInvertColors)"> 235 <input type="hidden" name="IndicatorsVisibleOnSmallDevices" value="@(indicatorsVisibleOnSmallDevices)"> 236 237 @*--- VARIANTS ---*@ 238 @if (sourceType == "variants") 239 { 240 <input type="hidden" name="isVariant" value="true"> 241 <input type="hidden" name="MainProductID" id="MainProductID_@Model.ID" value="@productIds"> 242 } 243 244 @*--- MOST SOLD ---*@ 245 @if (sourceType == "most-sold") 246 { 247 <input type="hidden" name="SortBy" value="OrderCount"> 248 if (groupIds != "") 249 { 250 <input type="hidden" name="GroupId" value="@groupIds"> 251 } 252 } 253 254 @*--- TRENDING ---*@ 255 @if (sourceType == "trending") 256 { 257 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 258 if (groupIds != "") 259 { 260 <input type="hidden" name="GroupId" value="@groupIds"> 261 } 262 } 263 264 @*--- FREQUENTLY BOUGHT ---*@ 265 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 266 { 267 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 268 } 269 @if (sourceType != "frequently" && hasVariants) 270 { 271 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 272 } 273 274 @*--- LATEST ---*@ 275 @if (sourceType == "latest") 276 { 277 <input type="hidden" name="SortBy" value="Created"> 278 <input type="hidden" name="GroupId" value="@groupIds"> 279 } 280 281 @*--- SELECTED PRODUCTS ---*@ 282 @if (sourceType == "selected" && !string.IsNullOrEmpty(productIds) && !hasVariants) 283 { 284 <input type="hidden" name="MainProductId" id="MainProductID_@Model.ID" value="@productIds"> 285 } 286 @if (sourceType == "selected" && hasVariants) 287 { 288 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 289 } 290 291 @*--- RELATED PRODUCTS ---*@ 292 @if (sourceType == "related-products") 293 { 294 <input type="hidden" name="ProductVariantId" id="MainProductID_@Model.ID" value="@productVariantIds"> 295 } 296 297 @* General parameters *@ 298 <input type="hidden" name="Link" value="@link"> 299 <input type="hidden" name="HideTitle" value="@Model.Item.GetString("HideTitle")"> 300 301 @if (Model.Item.GetInt32("ProductsCount") != 0) 302 { 303 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 304 } 305 <input type="hidden" name="HeadingTitle" id="RelatedProductsTitle_@Model.ID" value="@title"> 306 @if (!string.IsNullOrEmpty(Model.Item.GetString("Subtitle"))) 307 { 308 <input type="hidden" name="Subtitle" value="@Model.Item.GetString("Subtitle")"> 309 } 310 @if (!string.IsNullOrEmpty(Model.Item.GetString("LinkText"))) 311 { 312 <input type="hidden" name="LinkText" value="@Model.Item.GetString("LinkText")"> 313 } 314 @if (!string.IsNullOrEmpty(Model.Item.GetString("ImageAspectRatio"))) 315 { 316 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 317 ratio = ratio != "0" ? ratio : ""; 318 <input type="hidden" name="ImageAspectRatio" value="@ratio"> 319 } 320 @if (!string.IsNullOrEmpty(Model.Item.GetString("Layout"))) 321 { 322 <input type="hidden" name="Layout" value="@Model.Item.GetRawValueString("Layout")"> 323 } 324 @if (titleFontSize != "") 325 { 326 <input type="hidden" name="TitleFontSize" value="@titleFontSize"> 327 } 328 @if (subtitleFontSize != "") 329 { 330 <input type="hidden" name="SubtitleFontSize" value="@subtitleFontSize"> 331 } 332 @if (buttonStyle != "") 333 { 334 <input type="hidden" name="ButtonStyle" value="@buttonStyle"> 335 } 336 @if (generalTheme != "") 337 { 338 <input type="hidden" name="GeneralTheme" value="@generalTheme"> 339 } 340 @if (theme != "") 341 { 342 <input type="hidden" name="Theme" value="@theme"> 343 } 344 @if (imageTheme != "") 345 { 346 <input type="hidden" name="ImageTheme" value="@imageTheme"> 347 } 348 @if (!string.IsNullOrEmpty(Model.Item.GetString("ContentPadding"))) 349 { 350 string contentPadding = Model.Item.GetRawValueString("ContentPadding"); 351 <input type="hidden" name="ContentPadding" value="@contentPadding"> 352 } 353 <input type="hidden" name="TextReadability" value="@maxWidth"> 354 <input type="hidden" name="ParentColumnSize" id="ParentColumnSize_@Model.ID" value="12"> 355 356 <input type="hidden" name="SaleBadgeType" value="@Model.Item.GetRawValue("SaleBadgeType")"> 357 <input type="hidden" name="SaleBadgeCssClassName" value="@Model.Item.GetRawValue("SaleBadgeDesign")"> 358 <input type="hidden" name="NewBadgeCssClassName" value="@Model.Item.GetRawValue("NewBadgeDesign")"> 359 <input type="hidden" name="NewPublicationDays" value="@Model.Item.GetInt32("NewPublicationDays")"> 360 361 @if (campaignValues != string.Empty) 362 { 363 <input type="hidden" name="CampaignBadgesValues" value="@campaignValues"> 364 } 365 </form> 366 367 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 368 <script> 369 window.addEventListener("load", () => { 370 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 371 }); 372 </script> 373 374 if (Pageview.IsVisualEditorMode) 375 { 376 <div class="alert alert-info" role="alert"> 377 <span>@Translate("Product slider: Edit this column to configure")</span> 378 </div> 379 } 380 381 if (sourceType != "related-products") 382 { 383 <div class="w-100 h-100"> 384 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 385 <div id="RelatedProducts_@Model.ID" class="h-100 swift_product_slider_container"></div> 386 </div> 387 } 388 else if (product?.RelatedGroups != null) 389 { 390 @* Create multiple slider containers, if type is Product relation *@ 391 <div class="grid w-100 h-100@(generalTheme)" style="grid-row-gap: 4rem"> 392 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 393 @foreach (var group in product.RelatedGroups) 394 { 395 <div id="RelatedProducts_@(Model.ID)_@group.Id" class="g-col-12 h-100 swift_product_slider_container"></div> 396 } 397 </div> 398 } 399 400 @* Initialize *@ 401 if (sourceType != "related-products") 402 { 403 <script type="module"> 404 if (document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]")) { 405 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]").getAttribute("data-col-size"); 406 } 407 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")).then(function () { 408 setTimeout(function() { 409 const isVisualEditor = @(Converter.ToString(Pageview.IsVisualEditorMode).ToLowerInvariant()); 410 const productSliderContainer = document.querySelector(".swift_product_slider_container"); 411 412 if (productSliderContainer && productSliderContainer.innerHTML !== "") { 413 productSliderContainer.classList.remove("d-none"); 414 } 415 else if (!isVisualEditor) { 416 productSliderContainer.closest("[class*=column]").classList.add("d-none"); 417 } 418 }, 150); 419 }); 420 </script> 421 } 422 else if (product?.RelatedGroups != null) 423 { 424 @* Create multiple sliders, if type is Product relation *@ 425 foreach (var group in product.RelatedGroups) 426 { 427 IList<string> fromProductIds = new List<string> { }; 428 429 foreach (var relatedProduct in group.Products) 430 { 431 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 432 { 433 fromProductIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 434 } 435 else 436 { 437 fromProductIds.Add($"{relatedProduct.ProductId}"); 438 } 439 } 440 <script type="module"> 441 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@(Model.ID)_@group.Id").closest("[data-col-size]").getAttribute("data-col-size"); 442 document.querySelector("#MainProductID_@Model.ID").value = "@string.Join(",", fromProductIds)"; 443 document.querySelector("#RelatedProductsTitle_@Model.ID").value = "@group.Name"; 444 document.querySelector("#RelatedProductsForm_@Model.ID").setAttribute("data-response-target-element", "RelatedProducts_@(Model.ID)_@group.Id"); 445 446 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")); 447 </script> 448 } 449 } 450 } 451
Error executing template "Designs/Swift/Paragraph/Swift_RelatedProducts.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_9e890a2f70da40818198e9f7a675cf2c.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_RelatedProducts.cshtml:line 164
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Core 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 14 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 15 16 if (productList?.Products is object) 17 { 18 product = productList.Products[0]; 19 } 20 } 21 22 string title = Model?.Item?.GetRawValueString("Title", Translate("Products")); 23 string campaignValues = string.Join(",", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 24 25 //Styling 26 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 27 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "fs-5"); 28 string buttonStyle = Model.Item.GetRawValueString("ButtonStyle", ""); 29 buttonStyle = buttonStyle == "primary" ? " btn-primary" : buttonStyle; 30 buttonStyle = buttonStyle == "secondary" ? " btn-secondary" : buttonStyle; 31 buttonStyle = buttonStyle == "link" ? " btn-link" : buttonStyle; 32 string maxWidth = Model.Item.GetRawValueString("TextReadability", ""); 33 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 34 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 35 36 string generalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GeneralTheme")) ? " theme " + Model.Item.GetRawValueString("GeneralTheme").Replace(" ", "").Trim().ToLower() : ""; 37 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 38 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 39 40 //Link generation 41 string pageId = !string.IsNullOrEmpty(Model.Item.GetRawValueString("ProductSliderServicePage")) ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 42 if (string.IsNullOrEmpty(pageId)) 43 { 44 pageId = GetPageIdByNavigationTag("ProductSliderService").ToString(); 45 } 46 47 string url = "/Default.aspx?ID=" + pageId; 48 if (!url.Contains("LayoutTemplate", StringComparison.OrdinalIgnoreCase)) 49 { 50 url += url.Contains("?") ? "&LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml" : "?LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml"; 51 } 52 if (Pageview.IsVisualEditorMode) 53 { 54 url += "&VisualEdit=True"; 55 } 56 57 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 58 if (isLazyLoadingForProductInfoEnabled) 59 { 60 url += "&getproductinfo=true"; 61 } 62 63 //Source type 64 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 65 IList<string> relateFromGroupIds = new List<string> { }; 66 IList<string> relateFromProductVariantIds = new List<string> { }; 67 IList<string> relateFromProductIds = new List<string> { }; 68 bool hasVariants = false; 69 70 //--- VARIANTS --- 71 if (sourceType == "variants" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateToVariants") is ProductListViewModel productsToRelateToVariants) 72 { 73 foreach (var productSelection in productsToRelateToVariants.Products) 74 { 75 relateFromProductIds.Add(productSelection.Id); 76 } 77 } 78 79 //--- MOST SOLD --- 80 if (sourceType == "most-sold" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToMostSold") is IList<ProductGroupViewModel> groupsToRelateToMostSold) 81 { 82 foreach (var fromGroup in groupsToRelateToMostSold) 83 { 84 relateFromGroupIds.Add(fromGroup.Id); 85 } 86 } 87 88 //--- TRENDING --- 89 if (sourceType == "trending" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToTrending") is IList<ProductGroupViewModel> groupsToRelateToTrending) 90 { 91 foreach (var fromGroup in groupsToRelateToTrending) 92 { 93 relateFromGroupIds.Add(fromGroup.Id); 94 } 95 } 96 97 //--- LATEST --- 98 if (sourceType == "latest" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToLatest") is IList<ProductGroupViewModel> groupsToRelateToLatest) 99 { 100 foreach (var fromGroup in groupsToRelateToLatest) 101 { 102 relateFromGroupIds.Add(fromGroup.Id); 103 } 104 } 105 106 //--- FREQUENTLY BOUGHT --- 107 if (sourceType == "frequently" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo") is ProductListViewModel productsToRelateTo) 108 { 109 foreach (var fromProduct in productsToRelateTo.Products) 110 { 111 relateFromProductIds.Add(fromProduct.Id); 112 } 113 } 114 115 //--- SELECTED PRODUCTS --- 116 if ((sourceType == "selected" || sourceType == "frequently") && Model.Item.GetValue<ProductListViewModel>("Products") is ProductListViewModel products) 117 { 118 hasVariants = products.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 119 foreach (var productSelection in products.Products) 120 { 121 if (hasVariants) 122 { 123 if (!string.IsNullOrEmpty(productSelection.VariantId)) 124 { 125 relateFromProductVariantIds.Add($"{productSelection.Id} {productSelection.VariantId}"); 126 } 127 else 128 { 129 relateFromProductVariantIds.Add($"{productSelection.Id}"); 130 } 131 } 132 relateFromProductIds.Add($"{productSelection.Id}"); 133 } 134 } 135 136 //--- RELATED PRODUCTS --- 137 if (sourceType == "related-products" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo2") is ProductListViewModel selectedRelationProduct) 138 { 139 if (selectedRelationProduct.Products.Any()) 140 { 141 product = selectedRelationProduct.Products.FirstOrDefault(); 142 } 143 144 if (product?.RelatedGroups != null) 145 { 146 foreach (var group in product.RelatedGroups) 147 { 148 foreach (var relatedProduct in group.Products) 149 { 150 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 151 { 152 relateFromProductVariantIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 153 } 154 else 155 { 156 relateFromProductVariantIds.Add($"{relatedProduct.ProductId}"); 157 } 158 } 159 } 160 } 161 } 162 163 //Create group id collection and products id collection strings 164 string groupIds = product is object ? product.PrimaryOrDefaultGroup.Id : string.Join(",", relateFromGroupIds); 165 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 166 string productIds = product is object && relateFromProductIds.Count == 0 ? product.Id : string.Join(",", relateFromProductIds); 167 168 //Set the parameters to the url 169 string linkParameters = ""; 170 linkParameters += sourceType != "related-products" && sourceType != "frequently" && sourceType != "selected" ? "&GroupId=" + groupIds : ""; 171 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType != "most-sold" && sourceType != "trending" && sourceType != "latest" && sourceType != "frequently" && sourceType != "related-products" ? "&MainProductId=" + productIds : ""; 172 linkParameters += !string.IsNullOrEmpty(productVariantIds) && sourceType == "related-products" ? "&ProductVariantId=" + productVariantIds : ""; 173 linkParameters += sourceType == "variants" ? "&IsVariant=true" : ""; 174 linkParameters += sourceType == "latest" ? "&SortBy=Created" : ""; 175 linkParameters += sourceType == "most-sold" ? "&SortBy=OrderCount" : ""; 176 linkParameters += sourceType == "trending" ? "&SortBy=OrderCountGrowth" : ""; 177 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType == "frequently" ? $"&BoughtWithProductIds=[{productIds}]" : ""; 178 var productListPageId = GetPageIdByNavigationTag("Shop"); 179 string link = "/Default.aspx?ID=" + productListPageId + linkParameters; 180 181 // Slider settings (documentation: swiffyslider.com/configuration) 182 string navigationStyle = $"{Model.Item.GetRawValueString("NavigationStyle", "slider-nav-round")}"; 183 string navigationPlacement = $"{Model.Item.GetRawValueString("NavigationPlacement", "slider-nav-on-slides")}"; 184 string indicatorStyle = $"{Model.Item.GetRawValueString("IndicatorStyle", "slider-indicators-hidden")}"; 185 string revealSlides = Model.Item.GetRawValueString("RevealSlides", "no-reveal") == "reveal" ? "slider-item-reveal" : string.Empty; 186 string navigationAlwaysVisible = (Model.Item.GetBoolean("NavigationAlwaysVisible")) ? "slider-nav-visible" : string.Empty; 187 string navigationVisibleOnTouch = (Model.Item.GetBoolean("NavigationVisibleOnTouch")) ? "slider-nav-touch" : string.Empty; 188 string navigationShowScrollbar = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "slider-nav-scrollbar" : string.Empty; 189 string navigationSmall = (Model.Item.GetBoolean("NavigationSmall")) ? "slider-nav-sm" : string.Empty; 190 string navigationInvertColors = (Model.Item.GetBoolean("NavigationInvertColors")) ? "slider-nav-dark" : string.Empty; 191 string navigationSlideEntirePage = (Model.Item.GetBoolean("NavigationSlideEntirePage")) ? "slider-nav-page" : string.Empty; 192 string navigationNoLoop = (Model.Item.GetBoolean("NavigationNoLoop")) ? "slider-nav-noloop" : string.Empty; 193 string indicatorsOutsideSlider = (Model.Item.GetBoolean("IndicatorsOutsideSlider") && indicatorStyle != string.Empty) ? "slider-indicators-outside" : string.Empty; 194 string indicatorsHighlightActive = (Model.Item.GetBoolean("IndicatorsHighlightActive")) ? "slider-indicators-highlight" : string.Empty; 195 string indicatorsInvertColors = (Model.Item.GetBoolean("IndicatorsInvertedColors")) ? "slider-indicators-dark" : string.Empty; 196 string indicatorsVisibleOnSmallDevices = (Model.Item.GetBoolean("IndicatorsVisibleOnSmallDevices")) ? "slider-indicators-sm" : string.Empty; 197 198 bool productsFound = true; 199 if (string.IsNullOrEmpty(groupIds) && string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(productVariantIds)) 200 { 201 if (Pageview.IsVisualEditorMode) 202 { 203 productIds = product.Id; 204 sourceType = "selected"; 205 } 206 else 207 { 208 productsFound = false; 209 } 210 } 211 } 212 213 @*Container element for the request*@ 214 @if (productsFound) 215 { 216 <form method="post" action="@url" id="RelatedProductsForm_@Model.ID" data-response-target-element="RelatedProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> 217 <input type="hidden" name="ModelID" value="@Model.ID"> 218 <input type="hidden" name="SourceType" value="@sourceType"> 219 220 @*--- SLIDER SETTINGS ---*@ 221 <input type="hidden" name="NavigationStyle" value="@navigationStyle"> 222 <input type="hidden" name="NavigationPlacement" value="@navigationPlacement"> 223 <input type="hidden" name="IndicatorStyle" value="@indicatorStyle"> 224 <input type="hidden" name="RevealSlides" value="@revealSlides"> 225 <input type="hidden" name="NavigationAlwaysVisible" value="@(navigationAlwaysVisible)"> 226 <input type="hidden" name="NavigationVisibleOnTouch" value="@(navigationVisibleOnTouch)"> 227 <input type="hidden" name="NavigationShowScrollbar" value="@(navigationShowScrollbar)"> 228 <input type="hidden" name="NavigationSmall" value="@(navigationSmall)"> 229 <input type="hidden" name="NavigationInvertColors" value="@(navigationInvertColors)"> 230 <input type="hidden" name="NavigationNoLoop" value="@(navigationNoLoop)"> 231 <input type="hidden" name="NavigationSlideEntirePage" value="@(navigationSlideEntirePage)"> 232 <input type="hidden" name="IndicatorsOutsideSlider" value="@(indicatorsOutsideSlider)"> 233 <input type="hidden" name="IndicatorsHighlightActive" value="@(indicatorsHighlightActive)"> 234 <input type="hidden" name="IndicatorsInvertColors" value="@(indicatorsInvertColors)"> 235 <input type="hidden" name="IndicatorsVisibleOnSmallDevices" value="@(indicatorsVisibleOnSmallDevices)"> 236 237 @*--- VARIANTS ---*@ 238 @if (sourceType == "variants") 239 { 240 <input type="hidden" name="isVariant" value="true"> 241 <input type="hidden" name="MainProductID" id="MainProductID_@Model.ID" value="@productIds"> 242 } 243 244 @*--- MOST SOLD ---*@ 245 @if (sourceType == "most-sold") 246 { 247 <input type="hidden" name="SortBy" value="OrderCount"> 248 if (groupIds != "") 249 { 250 <input type="hidden" name="GroupId" value="@groupIds"> 251 } 252 } 253 254 @*--- TRENDING ---*@ 255 @if (sourceType == "trending") 256 { 257 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 258 if (groupIds != "") 259 { 260 <input type="hidden" name="GroupId" value="@groupIds"> 261 } 262 } 263 264 @*--- FREQUENTLY BOUGHT ---*@ 265 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 266 { 267 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 268 } 269 @if (sourceType != "frequently" && hasVariants) 270 { 271 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 272 } 273 274 @*--- LATEST ---*@ 275 @if (sourceType == "latest") 276 { 277 <input type="hidden" name="SortBy" value="Created"> 278 <input type="hidden" name="GroupId" value="@groupIds"> 279 } 280 281 @*--- SELECTED PRODUCTS ---*@ 282 @if (sourceType == "selected" && !string.IsNullOrEmpty(productIds) && !hasVariants) 283 { 284 <input type="hidden" name="MainProductId" id="MainProductID_@Model.ID" value="@productIds"> 285 } 286 @if (sourceType == "selected" && hasVariants) 287 { 288 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 289 } 290 291 @*--- RELATED PRODUCTS ---*@ 292 @if (sourceType == "related-products") 293 { 294 <input type="hidden" name="ProductVariantId" id="MainProductID_@Model.ID" value="@productVariantIds"> 295 } 296 297 @* General parameters *@ 298 <input type="hidden" name="Link" value="@link"> 299 <input type="hidden" name="HideTitle" value="@Model.Item.GetString("HideTitle")"> 300 301 @if (Model.Item.GetInt32("ProductsCount") != 0) 302 { 303 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 304 } 305 <input type="hidden" name="HeadingTitle" id="RelatedProductsTitle_@Model.ID" value="@title"> 306 @if (!string.IsNullOrEmpty(Model.Item.GetString("Subtitle"))) 307 { 308 <input type="hidden" name="Subtitle" value="@Model.Item.GetString("Subtitle")"> 309 } 310 @if (!string.IsNullOrEmpty(Model.Item.GetString("LinkText"))) 311 { 312 <input type="hidden" name="LinkText" value="@Model.Item.GetString("LinkText")"> 313 } 314 @if (!string.IsNullOrEmpty(Model.Item.GetString("ImageAspectRatio"))) 315 { 316 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 317 ratio = ratio != "0" ? ratio : ""; 318 <input type="hidden" name="ImageAspectRatio" value="@ratio"> 319 } 320 @if (!string.IsNullOrEmpty(Model.Item.GetString("Layout"))) 321 { 322 <input type="hidden" name="Layout" value="@Model.Item.GetRawValueString("Layout")"> 323 } 324 @if (titleFontSize != "") 325 { 326 <input type="hidden" name="TitleFontSize" value="@titleFontSize"> 327 } 328 @if (subtitleFontSize != "") 329 { 330 <input type="hidden" name="SubtitleFontSize" value="@subtitleFontSize"> 331 } 332 @if (buttonStyle != "") 333 { 334 <input type="hidden" name="ButtonStyle" value="@buttonStyle"> 335 } 336 @if (generalTheme != "") 337 { 338 <input type="hidden" name="GeneralTheme" value="@generalTheme"> 339 } 340 @if (theme != "") 341 { 342 <input type="hidden" name="Theme" value="@theme"> 343 } 344 @if (imageTheme != "") 345 { 346 <input type="hidden" name="ImageTheme" value="@imageTheme"> 347 } 348 @if (!string.IsNullOrEmpty(Model.Item.GetString("ContentPadding"))) 349 { 350 string contentPadding = Model.Item.GetRawValueString("ContentPadding"); 351 <input type="hidden" name="ContentPadding" value="@contentPadding"> 352 } 353 <input type="hidden" name="TextReadability" value="@maxWidth"> 354 <input type="hidden" name="ParentColumnSize" id="ParentColumnSize_@Model.ID" value="12"> 355 356 <input type="hidden" name="SaleBadgeType" value="@Model.Item.GetRawValue("SaleBadgeType")"> 357 <input type="hidden" name="SaleBadgeCssClassName" value="@Model.Item.GetRawValue("SaleBadgeDesign")"> 358 <input type="hidden" name="NewBadgeCssClassName" value="@Model.Item.GetRawValue("NewBadgeDesign")"> 359 <input type="hidden" name="NewPublicationDays" value="@Model.Item.GetInt32("NewPublicationDays")"> 360 361 @if (campaignValues != string.Empty) 362 { 363 <input type="hidden" name="CampaignBadgesValues" value="@campaignValues"> 364 } 365 </form> 366 367 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 368 <script> 369 window.addEventListener("load", () => { 370 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 371 }); 372 </script> 373 374 if (Pageview.IsVisualEditorMode) 375 { 376 <div class="alert alert-info" role="alert"> 377 <span>@Translate("Product slider: Edit this column to configure")</span> 378 </div> 379 } 380 381 if (sourceType != "related-products") 382 { 383 <div class="w-100 h-100"> 384 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 385 <div id="RelatedProducts_@Model.ID" class="h-100 swift_product_slider_container"></div> 386 </div> 387 } 388 else if (product?.RelatedGroups != null) 389 { 390 @* Create multiple slider containers, if type is Product relation *@ 391 <div class="grid w-100 h-100@(generalTheme)" style="grid-row-gap: 4rem"> 392 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 393 @foreach (var group in product.RelatedGroups) 394 { 395 <div id="RelatedProducts_@(Model.ID)_@group.Id" class="g-col-12 h-100 swift_product_slider_container"></div> 396 } 397 </div> 398 } 399 400 @* Initialize *@ 401 if (sourceType != "related-products") 402 { 403 <script type="module"> 404 if (document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]")) { 405 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]").getAttribute("data-col-size"); 406 } 407 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")).then(function () { 408 setTimeout(function() { 409 const isVisualEditor = @(Converter.ToString(Pageview.IsVisualEditorMode).ToLowerInvariant()); 410 const productSliderContainer = document.querySelector(".swift_product_slider_container"); 411 412 if (productSliderContainer && productSliderContainer.innerHTML !== "") { 413 productSliderContainer.classList.remove("d-none"); 414 } 415 else if (!isVisualEditor) { 416 productSliderContainer.closest("[class*=column]").classList.add("d-none"); 417 } 418 }, 150); 419 }); 420 </script> 421 } 422 else if (product?.RelatedGroups != null) 423 { 424 @* Create multiple sliders, if type is Product relation *@ 425 foreach (var group in product.RelatedGroups) 426 { 427 IList<string> fromProductIds = new List<string> { }; 428 429 foreach (var relatedProduct in group.Products) 430 { 431 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 432 { 433 fromProductIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 434 } 435 else 436 { 437 fromProductIds.Add($"{relatedProduct.ProductId}"); 438 } 439 } 440 <script type="module"> 441 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@(Model.ID)_@group.Id").closest("[data-col-size]").getAttribute("data-col-size"); 442 document.querySelector("#MainProductID_@Model.ID").value = "@string.Join(",", fromProductIds)"; 443 document.querySelector("#RelatedProductsTitle_@Model.ID").value = "@group.Name"; 444 document.querySelector("#RelatedProductsForm_@Model.ID").setAttribute("data-response-target-element", "RelatedProducts_@(Model.ID)_@group.Id"); 445 446 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")); 447 </script> 448 } 449 } 450 } 451
By clicking 'Accept All' you consent that we may collect information about you for various purposes, including: Statistics and Marketing