Error executing template "Designs/Tapas/eCom/Productlist/productlist_Default.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_20b938ed1f1b4dadbc80c9a43829cff1.<>c__DisplayClass16_0.b__0(TextWriter __razor_helper_writer) in D:\dynamicweb.net\Solutions\millarco.staging\Files\Templates\Designs\Tapas\eCom\Productlist\productlist_Default.cshtml:line 996
   at CompiledRazorTemplates.Dynamic.RazorEngine_20b938ed1f1b4dadbc80c9a43829cff1.<>c__DisplayClass16_0.b__0(TextWriter __razor_helper_writer) in D:\dynamicweb.net\Solutions\millarco.staging\Files\Templates\Designs\Tapas\eCom\Productlist\productlist_Default.cshtml:line 1028
   at CompiledRazorTemplates.Dynamic.RazorEngine_20b938ed1f1b4dadbc80c9a43829cff1.Execute() in D:\dynamicweb.net\Solutions\millarco.staging\Files\Templates\Designs\Tapas\eCom\Productlist\productlist_Default.cshtml:line 669
   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.b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits RazorTemplateBase<RazorTemplateModel<Template>> 2 @using Dynamicweb.Rendering; 3 @using Dynamicweb.Environment; 4 5 @inherits RazorTemplateBase<RazorTemplateModel<Template>> 6 @using Dynamicweb.Rendering; 7 @using System.Text.RegularExpressions; 8 9 10 11 @helper renderProduct(bool showShopFunctionsAlternativeIfNotLoggedIn, LoopItem product, bool renderAs4Cols = false) 12 { 13 14 string productID = product.GetString("Ecom:Product.ID"); 15 string productNumber = product.GetString("Ecom:Product.Number"); 16 string mainProductID = product.GetString("Ecom:Product:Field.FirstwebMainProductID"); 17 string PrimaryProductPageId = Firstweb.Custom.CustomCode.Tapas.Context.AreaPages.GetPageId("productcatalog"); 18 19 string productLink = product.GetString("Ecom:Product.Link.Clean"); 20 21 if (!String.IsNullOrEmpty(PrimaryProductPageId)) 22 { 23 productLink = "/Default.aspx?id=" + PrimaryProductPageId + "&productid=" + productID; 24 } 25 26 string languageId = product.GetString("Ecom:Product.LanguageID"); 27 string variantId = product.GetString("Ecom:Product.VariantID"); 28 29 string productName = product.GetString("Ecom:Product.Name"); 30 string productShortDescription = product.GetString("Ecom:Product.ShortDescription"); 31 32 string nettoPrice = product.GetString("Firstweb:ErpPriceInfo.NettoPriceFormatted"); 33 int bruttoPrice = product.GetInteger("Firstweb:ErpPriceInfo.BruttoAmountFormattedNoSymbol"); 34 35 string availableAmount = product.GetString("Firstweb:ErpStockInfo.StockQuantity"); 36 string stockColor = product.GetString("Firstweb:ErpStockInfo.StockColor"); 37 bool priceAndStockFound = product.GetBoolean("Firstweb:ErpPriceInfo.PriceAndStockFound"); 38 bool priceFound = product.GetBoolean("Firstweb:ErpPriceInfo.PriceFound"); 39 bool hasQuantityPrices = product.GetBoolean("Firstweb:ErpPriceInfo.HasQuantityPrices"); 40 string RRPrice = product.GetString("Firstweb:ErpPriceInfo:Prices.RRPFormatted"); 41 string OutletPrice = product.GetString("Firstweb:ErpPriceInfo.NettoPriceFormatted"); 42 43 int productPackagingQuantity = product.GetInteger("Firstweb:ErpPriceInfo:ExtraInfos.PackagingSize"); 44 int defaultQuantity = productPackagingQuantity > 0 ? productPackagingQuantity : 1; 45 46 47 bool hasVariants = (product.GetLoop("VariantCombinations").Count() > 0); 48 49 //Images 50 List<Dynamicweb.Ecommerce.Products.Detail> productImages = Firstweb.Custom.CustomCode.Frontend.Helpers.ProductImages.GetProductImages(languageId, productID, variantId); 51 Dynamicweb.Ecommerce.Products.Detail primaryProductImage = productImages.FirstOrDefault(x=>x.IsDefault); //Tag you can use instead: product.GetString("Ecom:Product.ImageDefault.Clean"); 52 //check for valid default image 53 if (primaryProductImage == null) 54 { 55 primaryProductImage = new Dynamicweb.Ecommerce.Products.Detail { Value = "" }; 56 } 57 else if (primaryProductImage.Value == null) 58 { 59 primaryProductImage.Value = ""; 60 } 61 62 //Sorted: first by imagegroup then by sortorder 63 //@foreach (var productImage in productImages) 64 //{ 65 //productImage.Value; //This is the imagepath eks. /Images/products/R12155_10_2.jpg 66 //productImage.IsDefault; //If the image is default or not. 67 //} 68 69 string primaryProductImageUrl = "/admin/public/getimage.ashx?image=" + primaryProductImage.Value + "&altFmImage_path=/Files/Images/ecom/Products/no-image.jpg&width=250&height=150&Compression=90&Crop=5"; 70 if (String.IsNullOrEmpty(primaryProductImage.Value)) 71 { 72 primaryProductImageUrl = "/admin/public/getimage.ashx?image=/Files/Images/ecom/Products/no-image.jpg&width=250&height=150&Compression=90&Crop=5"; 73 } 74 75 //Other packagings 76 var otherPackagingProducts = Firstweb.Custom.CustomCode.Frontend.Helpers.OtherPackagingProducts.GetOtherPackagingProducts(mainProductID, false).Where(i => i.Id != productID); 77 var showOtherPackagingProducts = otherPackagingProducts.Count() > 0; 78 79 80 //Customer product number 81 //string customerProductNumber = Firstweb.Custom.CustomCode.Frontend.Helpers.CustomerProductNumbers.GetCustomerProductNumber(productID); 82 83 //OrderTemplate 84 int productInFavoritLists = product.GetInteger("Firstweb:Ecom:Product:OrderTemplate.InTemplates.Count"); 85 86 string orderLineId = product.GetString("Firstweb:OrderTemplate:Line.ID"); 87 string orderTemplateId = product.GetString("Firstweb:OrderTemplate:Line.OrderTemplateID"); 88 int orderLineQuantity = product.GetInteger("Firstweb:OrderTemplate:Line.Quantity"); 89 90 bool inFavourite = productInFavoritLists > 0 ? true : false; 91 string inFavouriteBoolJS = inFavourite.ToString().ToLower(); 92 93 bool isOrderTemplate = !String.IsNullOrEmpty(orderLineId); 94 95 string productPriceCacheKey = orderLineId != "" ? orderLineId : productNumber; 96 97 98 99 100 string stock = ""; 101 102 string addBorderCss = isOrderTemplate ? "border-1" : ""; 103 104 105 if (!priceAndStockFound) 106 { 107 stock = "asyncLoad"; 108 } 109 else 110 { 111 stock = availableAmount; 112 } 113 114 string WidthClass = renderAs4Cols ? "col-md-4 col-lg-3" : "col-md-4"; 115 bool ShowShopFunctions = Firstweb.Custom.CustomCode.Tapas.Context.Current.ShopFunctionsVisibility.ShowShopFunctions(); 116 string BrandLogo = "/admin/public/getimage.ashx?image=" + product.GetString("Ecom:Product:Field.FirstwebBrandLogo.Value.FullPath") + "&height=25&crop=5"; 117 string BrandName = product.GetString("Ecom:Product:Field.FirstwebBrand.Value.Clean"); 118 string AddingToCartText = Translate("Product.AddingToCart", "Tilføjer produkt"); 119 string PickListText = Translate("Ordertemplate.SelectList", "Vælg en liste"); 120 bool IsOutletShop = Firstweb.Custom.CustomCode.Frontend.Helpers.Millarco.IsOutletShop(); 121 122 123 <!-- ko viewModel: 'ProductViewModel'--> 124 <!-- ko initValue: {observable: ProductId, value:'@productID'}--><!-- /ko--> 125 <div class="col-xs-12 col-sm-6 @WidthClass price xs-m-b-2 asyncLoad" data-productid="@productNumber" data-productkey="@productPriceCacheKey" data-test="@PrimaryProductPageId"> 126 <div class="product-list-item bg-white"> 127 <a href="@productLink"> 128 <div class="brand-image"> 129 @if (!String.IsNullOrEmpty(product.GetString("Ecom:Product:Field.FirstwebBrandLogo.Value.FullPath"))) 130 { 131 <img src="@BrandLogo" alt="@BrandName" /> 132 } 133 </div> 134 <div class="product-image"> 135 <img src="@primaryProductImageUrl" alt="@productName" /> 136 </div> 137 <div class="product-info"> 138 <p class="product-number">@Translate("Product.NumberShort", "Varenr.") @productNumber</p> 139 <p class="product-name">@productName</p> 140 </div> 141 @if (ShowShopFunctions && !IsOutletShop) 142 { 143 <p class="product-price"> 144 <span class="wave"> 145 <span class="dot"></span> 146 <span class="dot"></span> 147 <span class="dot"></span> 148 </span> 149 <span class="price-load-async"></span> 150 </p> 151 <p class="unit-price">@Translate("Product.PricePer", "Pris pr.") <span class="js-unit-size"></span> @Translate("Product.Unit", "stk.")</p> 152 } 153 else if (IsOutletShop) 154 { 155 <p class="product-price"> 156 @nettoPrice 157 </p> 158 } 159 else if (ShowShopFunctions) 160 { 161 <p class="product-price"> 162 @RRPrice 163 </p> 164 } 165 </a> 166 @if (ShowShopFunctions) 167 { 168 <div class="add-to-cart-area"> 169 @if (isOrderTemplate) 170 { 171 <!-- ko initValue: {observable: Quantity, value:@orderLineQuantity}--><!-- /ko--> 172 } 173 else 174 { 175 <!-- ko initValue: {observable: Quantity, value:@defaultQuantity}--><!-- /ko--> 176 } 177 <input class="product-quantity js-product-quantity hide" type="number" name="quantity" data-bind="value: Quantity" /> 178 <div class="btn btn-primary js-product-buy-btn hide" 179 data-bind="click: function() { $parent.addItemToCart(ProductId(), Quantity(), '', '', '@AddingToCartText') }" 180 data-productid="@productID"> 181 @Translate("Product.AddToCart", "TILFØJ TIL KURV") 182 </div> 183 <div class="not-in-stock-indicator js-not-in-stock-indicator hide"> 184 <p>@Translate("Product.NotInStock", "Ikke p&aring; lager")</p> 185 </div> 186 @if (!isOrderTemplate) 187 { 188 <div class="favorite-list-icon" data-toggle="modal" data-target="#modal-@productID" data-bind="with: OrderTemplateViewModel"> 189 <i class="fas fa-heart" data-bind=" 190 oninit: function() { IsInFavoriteList('@inFavourite'.toLowerCase()) }, 191 css : { showFavorite : ShowOrderTemplateDialog, showNewList : OrderTemplateShowNewList, active: IsInFavoriteList() == 'true' }, 192 visible: $root.User().IsLoggedIn(), 193 click: ToggleOrderTemplateDialog"> 194 </i> 195 </div> 196 <div class="modal fade" id="modal-@productID" tabindex="-1" role="dialog" data-bind="with: OrderTemplateViewModel"> 197 <!-- ko initValue: {observable: OrderTemplateRelationCount, value:'@productInFavoritLists'}--><!-- /ko--> 198 <!-- ko initValue: {observable: ShowInFavourite, value: @inFavouriteBoolJS}--><!-- /ko--> 199 <!-- ko initValue: {observable: ModalSelector, value: '#modal-@productID'}--><!-- /ko--> 200 <div class="modal-dialog" role="document"> 201 <div class="modal-content fav-list"> 202 203 <p class="favlist-header">@Translate("Ordertemplate.AddToExistingList", "Tilføj til eksisterende favoritliste")</p> 204 205 <div class="existing-lists"> 206 207 <select class="favField select-fix" 208 data-bind="options: OrderTemplateList, 209 optionsCaption: '@PickListText', 210 optionsText: function(item) { return item.Value.Name() + ' (' + item.Value.Count() + ')' }, 211 value: OrderTemplateSelectedList"></select> 212 213 <input class="product-quantity" type="number" name="quantity" data-bind="textInput: OrderTemplateQuantity" /> 214 215 <div class="btn btn-primary" 216 data-bind="click: function() { 217 OrderTemplateShowNewList() 218 ? CreateNewOrderTemplateList('@productID') 219 : AddProductToOrderTemplate('@productID' , OrderTemplateQuantity()) 220 }"> 221 @Translate("Ordertemplate.AddToList", "Tilføj") 222 </div> 223 224 </div> 225 226 <p class="or-text">@Translate("Ordertemplate.OrNewList", "eller...")</p> 227 228 <p class="favlist-header">@Translate("Ordertemplate.AddToNewList", "Tilføj til ny favoritliste")</p> 229 230 <form id="EditForm-@productID" 231 name="EditForm"> 232 <label class="xs-m-b-1" for="name">@Translate("Ordertemplate.ListName", "Favoritliste navn")</label> 233 <div class="new-list"> 234 <input type="text" 235 name="name" 236 _id="name" 237 data-bind="textInput: OrderTemplateNewListName" 238 autofocus 239 autocomplete="off" /> 240 241 <input class="product-quantity" type="number" name="quantity" data-bind="textInput: OrderTemplateNewListQuantity" /> 242 243 <button type="submit" 244 class="btn btn-primary" 245 _id="btnSave" 246 data-bind="click: function() { CreateNewOrderTemplateList('@productID', OrderTemplateNewListQuantity()) }"> 247 @Translate("Ordertemplate.SaveToNewList", "Tilføj til ny favoritliste") 248 </button> 249 </div> 250 </form> 251 252 </div> 253 </div> 254 </div> 255 } 256 else 257 { 258 <div class="favorite-list-icon" data-bind="with: OrderTemplateViewModel"> 259 <!-- ko initValue: {observable: OrderTemplateId, value:'@orderTemplateId'}--><!-- /ko--> 260 <!-- ko initValue: {observable: OrderTemplateLineId, value:'@orderLineId'}--><!-- /ko--> 261 <i class="fas fa-times" 262 data-bind=" 263 oninit: function() { IsInFavoriteList('@inFavourite'.toLowerCase()) }, 264 css: { showFavorite: ShowOrderTemplateDialog, showNewList: OrderTemplateShowNewList, active: IsInFavoriteList() == 'true' }, 265 click: DeleteOrderTemplateLine"> 266 </i> 267 </div> 268 } 269 </div> 270 } 271 @if (ShowShopFunctions && !IsOutletShop) 272 { 273 <p class="stock-indicator js-stock-indicator"> 274 <span class="stock-load-async"></span> 275 <span class="stock-name js-stock-name">@Translate("Product.StockStatus", "Lagerstatus")</span> 276 <span class="stock-name js-in-stock-again hide">@Translate("Product.StockExpectedAgaing", "P&aring; lager: ")<span class="js-stock-date"></span></span> 277 </p> 278 } 279 @if (IsOutletShop) 280 { 281 <div class="bg-secondary regular-rrp"> 282 <p class="xs-m-b-0"> 283 @Translate("Product.NormalRRP", "Normal butikspris: ") @RRPrice 284 </p> 285 </div> 286 } 287 </div> 288 289 </div> 290 <!-- /ko--> 291 } 292 293 294 295 296 297 @helper renderRelatedProduct(LoopItem repatedProduct) 298 { 299 string productID = repatedProduct.GetString("Ecom:Product.ID"); 300 string productNumber = repatedProduct.GetString("Ecom:Product.Number"); 301 string mainProductID = GetString("Ecom:Product:Field.FirstwebMainProductID"); 302 303 string productName = repatedProduct.GetString("Ecom:Product.Name"); 304 string productDescription = GetString("Ecom:Product.LongDescription"); 305 string productShortDescription = GetString("Ecom:Product.ShortDescription"); 306 307 string nettoPrice = repatedProduct.GetString("Firstweb:ErpPriceInfo.NettoPriceFormatted"); 308 int bruttoPrice = repatedProduct.GetInteger("Firstweb:ErpPriceInfo.BruttoAmountFormattedNoSymbol"); 309 310 int productPackagingQuantity = repatedProduct.GetInteger("Ecom:Product:Field.FirstwebNoPerColli"); 311 int defaultQuantity = productPackagingQuantity > 0 ? productPackagingQuantity : 1; 312 313 string languageId = repatedProduct.GetString("Ecom:Product.LanguageID"); 314 string variantId = repatedProduct.GetString("Ecom:Product.VariantID"); 315 316 int productInFavoritLists = GetInteger("Firstweb:Ecom:Product:OrderTemplate.InTemplates.Count"); 317 318 List<Dynamicweb.Ecommerce.Products.Detail> productImages = Firstweb.Custom.CustomCode.Frontend.Helpers.ProductImages.GetProductImages(languageId, productID, variantId); 319 Dynamicweb.Ecommerce.Products.Detail primaryProductImage = productImages.FirstOrDefault(x=>x.IsDefault); //Tag you can use instead: product.GetString("Ecom:Product.ImageDefault.Clean"); 320 //check for valid default image 321 if (primaryProductImage == null) 322 { 323 primaryProductImage = new Dynamicweb.Ecommerce.Products.Detail { Value = "" }; 324 } 325 else if (primaryProductImage.Value == null) 326 { 327 primaryProductImage.Value = ""; 328 } 329 330 string primaryProductImageUrl = "/admin/public/getimage.ashx?image=" + primaryProductImage.Value + "&altFmImage_path=/Files/Images/ecom/Products/no-image.jpg&width=300&height=300&Compression=90&Crop=5"; 331 332 //Sorted: first by imagegroup then by sortorder 333 //@foreach (var productImage in productImages) 334 //{ 335 //productImage.Value; //This is the imagepath eks. /Images/products/R12155_10_2.jpg 336 //productImage.IsDefault; //If the image is default or not. 337 //} 338 339 <li> 340 <article class="xs-p-2 lg-p-1 rounded-5 border-1 border-color-default"> 341 <div class="row is-flex xs-is-flex-column sm-is-flex-col md-is-flex-col"> 342 343 <div class="col-md-12 md-is-flex lg-p-r-0"> 344 <section class="col-md-3 col-lg-3 xs-p-0 md-p-l-0 md-p-r-1 lg-p-r-1 lg-p-l-0"> 345 <img class="img-responsive xs-auto-margin sm-auto-margin" src="@primaryProductImageUrl" /> 346 </section> 347 348 <section class="col-md-9 col-lg-7 xs-m-t-2 sm-m-t-2 md-m-t-0 xs-p-0 md-p-r-0 md-p-l-1 lg-p-r-0 lg-p-l-0"> 349 <header class="xs-m-b-1"> 350 <h2 data-bind="text: name" 351 class="h2 tertiary-font xs-m-t-0"> 352 @productName 353 </h2> 354 </header> 355 356 <section class="xs-m-b-1 clearfix"> 357 <span class="pull-left font-size-small"> 358 @Translate("Product.NumberShort", "Varenr."): <span>@productNumber</span> 359 </span> 360 </section> 361 362 <section class="word-break"> 363 <p class="font-size-small"> 364 @productShortDescription 365 </p> 366 </section> 367 </section> 368 </div> 369 370 <section class="col-md-9 col-md-offset-3 col-lg-5 col-lg-offset-0 sm-m-t-1 lg-m-t-0 md-p-l-2 371 lg-p-l-0 is-flex is-flex-col sm-is-flex-row sm-is-flex-wrap sm-flex-justify-space-between 372 md-is-flex-row md-is-flex-wrap md-flex-justify-space-between lg-flex-grow-2 lg-flex-justify-space-between"> 373 374 <div class="is-flex xs-m-t-1 sm-m-t-0 is-flex-col lg-m-b-auto"> 375 376 </div> 377 378 <div class="is-flex is-flex-col md-m-b-1 lg-m-b-0"> 379 <h4 class="h4 tertiary-font font-size-16px xs-m-b-1 sm-m-t-0 md-text-right lg-text-right"> 380 @nettoPrice 381 </h4> 382 383 <section class="xs-m-b-1 clearfix is-flex xs-flex-space-between lg-is-row-reverse"> 384 <div class="pull-right margin-left-1 xs-order-1 sm-order-1 md-order-1 xs-is-self-end sm-is-self-end md-is-self-end lg-m-t-0"> 385 @renderQuantityBox(defaultQuantity) 386 </div> 387 388 <div class="is-flex is-flex-col is-centered xs-margin-right-auto sm-m-r-1 md-m-r-1 font-size-small line-height-base md-text-right lg-text-right"> 389 <span>@Translate("Product.Packaging", "Forpakning"): </span> 390 <span> @productPackagingQuantity</span> 391 </div> 392 </section> 393 </div> 394 395 <section class="t-align-right clearfix sm-width-100 md-width-100"> 396 <button _data-bind="click: handleAddProductToCart" 397 class="btn btn-primary btn-xs tertiary-font lg-p-l-1 lg-p-r-1 xs-width-100 sm-width-100 md-width-100"> 398 @Translate("Product.AddToCart", "Tilføj til kurv") 399 </button> 400 </section> 401 </section> 402 403 </div> 404 </article> 405 </li> 406 407 408 409 } 410 411 @helper renderProductBuyBox(bool showShopFunctionsAlternativeIfNotLoggedIn, string productName, string productId, string price, int productPackagingQuantity, int quantity, string stockColor) 412 { 413 414 415 <article class="product-packaging border-bottom-1"> 416 <section class="row md-is-flex lg-is-flex md-is-flex-center lg-is-flex-center"> 417 <section class="packaging-section col-md-4 col-lg-5"> 418 <h4 class="h4 xs-m-t-0 xs-m-b-0 tertiary-font v-align-mid">@productName</h4> 419 </section> 420 421 422 423 @if (Firstweb.Custom.CustomCode.Tapas.Context.Current.ShopFunctionsVisibility.ShowShopFunctions()) 424 { 425 <section class="packaging-section md-p-l-0 xs-col-12 col-md-2 col-lg-2 tertiary-font"> 426 <span>@price</span> 427 428 429 </section> 430 <div class="stock md-is-self-end lg-is-self-end lg-order-0" > 431 @Translate("Product.StockStatus", "Lagerstatus"): <div class="stock-load-async @stockColor"></div> 432 433 </div> 434 <section class="packaging-section md-p-l-0 lg-p-r-8p packaging-section--alt xs-col-12 col-md-4 col-lg-3"> 435 <ul class="list-unstyled lg-m-l-auto lg-p-r-24p xs-m-b-0"> 436 <li> 437 <h5 class="h5 xs-m-b-0 xs-m-t-0 line-height-16px"> 438 <span>@Translate("Product.Packaging", "Forpakning"): </span> 439 <span> @productPackagingQuantity</span> 440 </h5> 441 </li> 442 </ul> 443 444 @renderQuantityBox(quantity) 445 446 </section> 447 448 <section class="packaging-section md-p-l-0 packaging-section--buy xs-col-12 col-md-2 col-lg-2 t-align-right"> 449 <button class="btn btn-primary btn-xs tertiary-font lg-p-l-1 lg-p-r-1 xs-width-100 sm-width-100 margin-bottom-4px" 450 _data-bind="click: handleAddProductToCart"> 451 @Translate("Product.AddToCart", "Tilføj til kurv") 452 </button> 453 </section> 454 455 } 456 else if (showShopFunctionsAlternativeIfNotLoggedIn) 457 { 458 var replaceWith = Firstweb.Custom.CustomCode.Tapas.Context.Current.ShopFunctionsVisibility.ReplaceWith(); 459 460 <section> 461 462 @if (replaceWith == "LOGIN") 463 { 464 <text>@renderLogin()</text> 465 } 466 else if (replaceWith == "RESELLER") 467 { 468 <text>@renderResellerLink()</text> 469 } 470 </section> 471 } 472 473 </section> 474 </article> 475 } 476 477 478 @helper renderInstantSearchProduct(LoopItem product) 479 { 480 string productID = product.GetString("Ecom:Product.ID"); 481 string productNumber = product.GetString("Ecom:Product.Number"); 482 string mainProductID = product.GetString("Ecom:Product:Field.FirstwebMainProductID"); 483 484 string productLink = product.GetString("Ecom:Product.Link.Clean"); 485 486 string productName = product.GetString("Ecom:Product.Name"); 487 string productShortDescription = product.GetString("Ecom:Product.ShortDescription"); 488 489 int productPackagingQuantity = product.GetInteger("Firstweb:ErpPriceInfo:ExtraInfos.PackagingSize"); 490 int defaultQuantity = productPackagingQuantity > 0 ? productPackagingQuantity : 1; 491 492 string languageId = product.GetString("Ecom:Product.LanguageID"); 493 string variantId = product.GetString("Ecom:Product.VariantID"); 494 495 bool hasVariants = (product.GetLoop("VariantCombinations").Count() > 0); 496 497 //Images 498 List<Dynamicweb.Ecommerce.Products.Detail> productImages = Firstweb.Custom.CustomCode.Frontend.Helpers.ProductImages.GetProductImages(languageId, productID, variantId); 499 Dynamicweb.Ecommerce.Products.Detail primaryProductImage = productImages.FirstOrDefault(x=>x.IsDefault); //Tag you can use instead: product.GetString("Ecom:Product.ImageDefault.Clean"); 500 501 //check for valid default image 502 if (primaryProductImage == null) 503 { 504 primaryProductImage = new Dynamicweb.Ecommerce.Products.Detail { Value = "" }; 505 } 506 else if (primaryProductImage.Value == null) 507 { 508 primaryProductImage.Value = ""; 509 } 510 511 string primaryProductImageUrl = "/admin/public/getimage.ashx?image=" + primaryProductImage.Value + "&altFmImage_path=/Files/Images/ecom/Products/no-image.jpg&width=200&height=125&Compression=90&Crop=5"; 512 if (String.IsNullOrEmpty(primaryProductImage.Value)) 513 { 514 primaryProductImageUrl = "/admin/public/getimage.ashx?image=/Files/Images/ecom/Products/no-image.jpg&width=200&height=125&Compression=90&Crop=5"; 515 } 516 //Sorted: first by imagegroup then by sortorder 517 //@foreach (var productImage in productImages) 518 //{ 519 //productImage.Value; //This is the imagepath eks. /Images/products/R12155_10_2.jpg 520 //productImage.IsDefault; //If the image is default or not. 521 //} 522 523 //Other packagings 524 //var otherPackagingProducts = Firstweb.Custom.CustomCode.Frontend.Helpers.OtherPackagingProducts.GetOtherPackagingProducts(mainProductID, false).Where(i => i.Id != productID); 525 //var showOtherPackagingProducts = otherPackagingProducts.Count() > 0; 526 527 int loopCount = product.GetInteger("Products.LoopCounter") - 1; 528 <div class="col-xs-12 col-sm-3 xs-m-b-1"> 529 530 <article class="instant-search-product bg-white productCount @loopCount" data-bind="css: {'item--selected': navSelectedItem() == @loopCount}"> 531 <div class="cursor-pointer info" 532 data-bind="click: function() { window.location.href='@productLink'}"> 533 <div class="hidden-xs product-image"> 534 <img class="img-responsive center-block" src="@primaryProductImageUrl"> 535 </div> 536 537 <div class="name"> 538 <p>@productName</p> 539 540 541 <span>@Translate("Product.NumberShort", "Varenr.") @productNumber</span> 542 </div> 543 </div> 544 545 @if (Firstweb.Custom.CustomCode.Tapas.Context.Current.ShopFunctionsVisibility.ShowShopFunctions()) 546 { 547 <div class="buy-container" data-bind="defineObservable: { quantity: 1 }"> 548 549 <input class="product-quantity" type="number" name="quantity" data-bind="value: quantity" /> 550 551 <button class="btn btn-primary btn-xs add-to-cart" 552 data-bind="click: function(evt) { $parent.onAddToCart('@productID', quantity()) }"> 553 @Translate("Product.AddToCart", "Tilføj til kurv") 554 </button> 555 </div> 556 } 557 </article> 558 559 </div> 560 561 } 562 563 564 565 @helper renderQuantityBox(int quantity) 566 { 567 <input class="product-quantity" type="number" name="quantity" value="@quantity" /> 568 } 569 570 571 @helper renderLogin() 572 { 573 <button class="btn btn-primary" data-toggle="modal" data-target=".loginModal">@Translate("Product.LoginToShop", "Log ind for at købe")</button> 574 } 575 @helper renderResellerLink() 576 { 577 <a href="@Firstweb.Custom.CustomCode.Tapas.Context.Current.ShopFunctionsVisibility.ReplaceLink()" class="btn btn-primary">@Translate("Product.FindReseller", "Find forhandler")</a> 578 } 579 580 @inherits RazorTemplateBase<RazorTemplateModel<Template>> 581 @using Dynamicweb.Rendering 582 @using Dynamicweb.Environment 583 584 @helper renderBreadcrumbs(string productId = "") 585 { 586 string currentGroupId = Dynamicweb.Context.Current.Request.GetString("GroupID"); 587 string productPageID = Firstweb.Custom.CustomCode.Tapas.Context.AreaPages.GetPageId("productcatalog"); 588 string productPageHref = "/Default.aspx?Id=" + productPageID; 589 var breadCrumbGroupList = Firstweb.Custom.CustomCode.Frontend.Helpers.EcomGroups.getBreadCrumbGroupList(currentGroupId, true); 590 591 if (!String.IsNullOrEmpty(productId)) { 592 breadCrumbGroupList = Firstweb.Custom.CustomCode.Frontend.Helpers.EcomGroups.getBreadCrumbGroupListByProduct(productId, true); 593 } 594 595 <div class="col-xs-12"> 596 <ul class="breadcrumb"> 597 <li><a href="@productPageHref" title="@Translate("Firstweb.Content.Breadcrumbs.ProductPage", "Produkter")">@Translate("Firstweb.Content.Breadcrumbs.ProductPage", "Produkter")</a></li> 598 @foreach (var g in breadCrumbGroupList) { 599 string breadCrumbLink = String.Format("/Default.aspx?Id={0}&amp;GroupId={1}", productPageID, g.Id); 600 <li> <a href='@breadCrumbLink' title="@g.Name">@g.Name </a></li> 601 } 602 </ul> 603 </div> 604 } 605 606 @{ 607 string sortOrder = GetString("Ecom:ProductList.SortOrder"); 608 bool isAjaxRequest = Dynamicweb.Context.Current.Request.GetBoolean("ajaxTapas"); 609 string searchQuery = Dynamicweb.Context.Current.Request.GetString("Search"); 610 string currentGroupId = Dynamicweb.Context.Current.Request.GetString("GroupID"); 611 var breadCrumbGroupList = Firstweb.Custom.CustomCode.Frontend.Helpers.EcomGroups.getBreadCrumbGroupList(currentGroupId, false); 612 bool IsGroupList = !String.IsNullOrEmpty(currentGroupId); 613 614 int totalPages = GetInteger("Ecom:ProductList.TotalPages"); 615 int currentPage = GetInteger("Ecom:ProductList.CurrentPage"); 616 int productCount = GetInteger("Ecom:ProductList.PageProdCnt"); 617 bool productsFound = (productCount > 0); 618 bool IsOutletShop = Firstweb.Custom.CustomCode.Frontend.Helpers.Millarco.IsOutletShop(); 619 620 int ProductGroupVideoList = GetInteger("Ecom:Group:Field.FirstwebProductGroupVideoList"); 621 } 622 623 @if (!isAjaxRequest) 624 { 625 <div class="container"> 626 <div class="row"> 627 @renderBreadcrumbs() 628 </div> 629 </div> 630 631 <div class="millarco-productlist"> 632 633 <section> 634 635 @if (IsGroupList) 636 { 637 @RenderGroupHeader() 638 } 639 else if (!String.IsNullOrWhiteSpace(searchQuery)) 640 { 641 <div class="container xs-p-t-3"> 642 @RenderSearchHeader(searchQuery) 643 </div> 644 } 645 else if (!IsOutletShop) 646 { 647 <div class="container xs-p-t-3"> 648 @RenderParagraphHeader() 649 </div> 650 } 651 else 652 { 653 @RenderOutletShopHeader() 654 } 655 656 </section> 657 658 <div class="container product-container"> 659 660 <main data-bind="viewModel: 'ProductListViewModel'"> 661 <div class="row"> 662 <div class="col-lg-3"> 663 <p class="h3">@Translate("ProductSubNavTitle", "Produkter")</p> 664 @if (productsFound) 665 { 666 if (IsGroupList) 667 { 668 <div class="product-groups"> 669 @RenderGroupLevelFilter(Firstweb.Custom.CustomCode.Frontend.Helpers.EcomGroups.GetFilterTopLevelGroup(), currentGroupId, currentGroupId) 670 </div> 671 } 672 } 673 </div> 674 <div class="col-lg-9"> 675 <div class="row"> 676 @if (productsFound) 677 { 678 <div class="col-xs-12 filter-section"> 679 <form method="get" 680 action="/Default.aspx" 681 data-bind="formSubmitOnChange: { 682 text: '@Translate("Productlist.UpdatingFilters", "Opdaterer filter")', 683 delay: '1000' }"> 684 <input type="hidden" name="id" value="@GetString("Ecom:ProductList:Page.ID")" /> 685 686 @if (!String.IsNullOrEmpty(currentGroupId)) 687 { 688 <input type="hidden" name="groupid" value="@currentGroupId" /> 689 } 690 @if (!String.IsNullOrEmpty(searchQuery)) 691 { 692 <input type="hidden" name="search" value="@searchQuery" /> 693 } 694 695 @RenderSorting(sortOrder) 696 697 @RenderFacets() 698 </form> 699 </div> 700 } 701 <div class="col-xs-12 productList" 702 data-bind="asyncPriceLoad: productListLoad, 703 loadOnBool: { observableBool: CartLoading, text: 'Tilføjer produkt' }, 704 css: 'loaded'"> 705 706 @RenderProductList(GetLoop("Products"), productsFound) 707 708 @if (ProductGroupVideoList > 0) 709 { 710 @RenderParagraphContent(ProductGroupVideoList) 711 } 712 713 @if (totalPages > 1 && currentPage == 1) 714 { 715 <div class="loadMoreProducts" 716 data-bind="loadMoreProducts: { 717 beforeLoad: function() { $root.stuffLoadingQueue({load: true}) }, 718 afterLoad: function() { $root.stuffLoadingQueue({load: false}) }, 719 text: '@Translate("Productlist.LoadingProducts", "Henter produkter")', 720 currentPage: @currentPage, 721 totalPages: @totalPages 722 }"> 723 </div> 724 } 725 726 </div> 727 </div> 728 </div> 729 </div> 730 </main> 731 </div> 732 733 </div> 734 } 735 else 736 { 737 <div class="productsContainer"> 738 @RenderProductList(GetLoop("Products"), productsFound) 739 </div> 740 } 741 742 @helper RenderProductList(IEnumerable<LoopItem> Loop, bool productsFound) 743 { 744 if (!productsFound) 745 { 746 <div class="is-flex col-xs-12 xs-m-t-3 xs-m-b-4 sm-m-b-7 font-primary"> 747 <h1>@Translate("ProductList.NoProductsFound", ":( There are no products in this category")</h1> 748 </div> 749 } 750 else 751 { 752 var productLoop = Loop.ToList(); 753 int currentPage = GetInteger("Ecom:ProductList.CurrentPage"); 754 int totalPages = GetInteger("Ecom:ProductList.TotalPages"); 755 var count = 0; 756 757 for (int i = 0; i < productLoop.Count(); i++) 758 { 759 var item = productLoop[i]; 760 int loopMod = item.GetInteger("Products.LoopMod4"); 761 bool renderStartRow = i % 3 == 0 ? true : false; 762 bool renderEndRow = i % 3 == 0 ? true : false; 763 764 if (i == 0 && totalPages > 1) 765 { 766 @:<div class="row row-ce" id="@currentPage"> 767 } 768 769 @renderProduct(true, item, true); //This is from renderProduct.cshtml 770 771 772 if (productLoop.Count() - 1 == i && totalPages > 1) 773 { 774 @:</div> 775 } 776 } 777 } 778 } 779 780 @helper RenderOutletShopHeader() 781 { 782 <div class="content-element solid-bg bg-image-and-text-element bg-secondary" style="background-image:url('/Files/Images/@Dynamicweb.Frontend.PageView.Current().CurrentParagraph.Image')"> 783 <div class="container"> 784 <div class="row"> 785 <div class="col-xs-12 col-md-8 col-lg-6"> 786 <div class="text-area"> 787 <div class="rte-content"> 788 @Dynamicweb.Frontend.PageView.Current().CurrentParagraph.Text 789 </div> 790 <div class="red-divider"></div> 791 </div> 792 </div> 793 </div> 794 </div> 795 </div> 796 } 797 798 799 @helper RenderGroupHeader() 800 { 801 int ProductGroupVideoTop = GetInteger("Ecom:Group:Field.FirstwebProductGroupVideoTop"); 802 string Description = GetString("Ecom:Group.Description"); 803 string GroupName = GetString("Ecom:Group.Name"); 804 string BgImage = GetString("Ecom:Group.LargeImage"); 805 bool IsOutletShop = Firstweb.Custom.CustomCode.Frontend.Helpers.Millarco.IsOutletShop(); 806 if (IsOutletShop && !String.IsNullOrEmpty(GetString("Ecom:Group:Field.FirstwebOutletGroupImage.Value.Clean"))) 807 { 808 BgImage = GetString("Ecom:Group:Field.FirstwebOutletGroupImage.Value.Clean"); 809 } 810 811 if (ProductGroupVideoTop > 0) 812 { 813 @RenderParagraphContent(ProductGroupVideoTop) 814 } 815 else 816 { 817 <div class="content-element solid-bg bg-image-and-text-element bg-secondary" style="background-image:url('@BgImage')"> 818 <div class="container"> 819 <div class="row"> 820 <div class="col-xs-12 col-md-8 col-lg-6"> 821 <div class="text-area"> 822 @if (!String.IsNullOrEmpty(Description)) 823 { 824 <div class="rte-content"> 825 @Description 826 </div> 827 } 828 else 829 { 830 <h1 class="xs-m-t-0">@GroupName</h1> 831 } 832 <div class="red-divider"></div> 833 </div> 834 </div> 835 </div> 836 </div> 837 </div> 838 } 839 } 840 841 @helper RenderSearchHeader(string searchQuery) 842 { 843 <h1 class="h1 xs-m-t-0 tertiary-font"> 844 @Translate("ProductList.SearchResultHeader", "S&oslash;geresultat") 845 </h1> 846 847 <h4 class="h4 lg-m-b-0"> 848 @Translate("ProductList.SearchResultText", "Resultater for din s&oslash;gning"): @searchQuery 849 </h4> 850 } 851 852 @helper RenderParagraphHeader() 853 { 854 <h1 class="h1 lg-m-t-0 tertiary-font">@Dynamicweb.Frontend.PageView.Current().CurrentParagraph.Header</h1> 855 856 if (!String.IsNullOrEmpty(Dynamicweb.Frontend.PageView.Current().CurrentParagraph.Text)) 857 { 858 <p>@Dynamicweb.Frontend.PageView.Current().CurrentParagraph.Text</p> 859 } 860 } 861 862 863 @helper RenderGroups(List<Dynamicweb.Ecommerce.Products.Group> breadCrumbGroupList, string currentGroupId) 864 { 865 if (breadCrumbGroupList.Count() > 0) 866 { 867 var ParentGroup = breadCrumbGroupList.LastOrDefault(); 868 var baseLink = "/Default.aspx?ID=" + Dynamicweb.Frontend.PageView.Current().ID + "&GroupID="; 869 if (ParentGroup != null) 870 { 871 <section class="product__sub-nav"> 872 <p class="xs-m-b-0"> 873 @ParentGroup.Name 874 </p> 875 876 <ul> 877 @foreach (var subgroup in ParentGroup.Subgroups) 878 { 879 <li> 880 @if (subgroup.Id == currentGroupId) 881 { 882 <span>@subgroup.Name</span> 883 } 884 else 885 { 886 bool DisplayGroup = subgroup.Products.Count() > 0 || subgroup.Subgroups.Where(x => x.Products.Count() > 0).Count() > 0; 887 if (DisplayGroup) 888 { 889 <a href="@baseLink@subgroup.Id">@subgroup.Name</a> 890 } 891 } 892 </li> 893 } 894 </ul> 895 </section> 896 } 897 } 898 } 899 900 @helper RenderFacets() 901 { 902 foreach (LoopItem facetGroup in GetLoop("FacetGroups")) 903 { 904 foreach (LoopItem facet in facetGroup.GetLoop("Facets").Where(x => x.GetLoop("FacetOptions").Any())) 905 { 906 string facetName = facet.GetString("Facet.Name"); 907 string translatedFacetName = Translate("ProductList.FacetName" + facet.GetString("Facet.Name"), facet.GetString("Facet.Name")); 908 bool hasOptions = facet.GetLoop("FacetOptions").Any(); 909 string hasOptionsCss = hasOptions ? "xs-p-t-1 xs-p-b-1" : ""; 910 string FacetIsOpen = facet.GetLoop("FacetOptions").Where(x => x.GetBoolean("FacetOption.Selected")).Any() ? "true" : "false"; 911 912 <section class="bg-white filter" data-bind="defineObservable: { sortDropdown: @FacetIsOpen }"> 913 @if (hasOptions) 914 { 915 <button class="filter__toggle xs-p-l-0 xs-p-r-0 clear-button is-flex is-space-between is-flex-align-center width-100" data-bind="click: function() { sortDropdown(!sortDropdown()) }"> 916 <h3 class="h3 xs-m-t-0 xs-m-b-0 tertiary-font font-size-base filterHeader" 917 data-bind="toggleClassParent: 'active'"> 918 @translatedFacetName 919 </h3> 920 <span> 921 <i class="fa fa-chevron-down"></i> 922 </span> 923 </button> 924 } 925 926 <div class="filter__options line-height-base" data-bind="css: { hidden: !sortDropdown() }"> 927 @foreach (LoopItem option in facet.GetLoop("FacetOptions")) 928 { 929 var value = option.GetValue("FacetOption.Value"); 930 string label = option.GetString("FacetOption.Label").Replace("\"", "&#x22;"); 931 if (label.ToUpper() == "TRUE" || label.ToUpper() == "FALSE") 932 { 933 label = Translate("ProductList.FacetOption:" + label, "Ja"); 934 } 935 936 937 int count = option.GetInteger("FacetOption.Count"); 938 bool selected = option.GetBoolean("FacetOption.Selected"); 939 label += " (" + count + ")"; 940 <div class="form-group"> 941 <custom-checkbox params="{ label: '@label', 942 value: '@value', 943 checked: @(selected.ToString().ToLower()), 944 name: '@facet.GetString("Facet.QueryParameter")' }" /> 945 </div> 946 } 947 </div> 948 </section> 949 } 950 } 951 } 952 953 954 @helper RenderSorting(string sortOrder) 955 { 956 <section class="bg-white filter filter__container" 957 data-bind="defineObservable: { 958 sortOrder: '@sortOrder' || 'ASC', 959 sortDropdown: false }"> 960 <button class="filter__toggle clear-button is-flex is-space-between is-flex-align-center clear-button xs-p-l-0 xs-p-r-0 width-100" data-bind="click: function() { sortDropdown(!sortDropdown()) }"> 961 <p class="h3 xs-m-t-0 xs-m-b-0 tertiary-font font-size-base" 962 data-bind="toggleClassParent: 'active'"> 963 @Translate("filtersort", "Sortering") 964 </p> 965 966 <span> 967 <i class="fa fa-chevron-down"></i> 968 </span> 969 </button> 970 971 <input type="hidden" name="SortBy" value="ProductNameSort" /> 972 973 <section class="filter__options" data-bind="css: { hidden: !sortDropdown() }"> 974 <div class="form-group"> 975 <custom-radio params="{ 976 label: '@Translate("sortnameaz", "Navn: A - &Aring;")', 977 value: 'ASC', 978 checked: sortOrder, 979 name: 'SortOrder' }" /> 980 </div> 981 982 <div class="form-group xs-m-b-0"> 983 <custom-radio params="{ 984 label: '@Translate("sortnameza", "Navn: &Aring; - A")', 985 value: 'DESC', 986 checked: sortOrder, 987 name: 'SortOrder' }" /> 988 </div> 989 </section> 990 </section> 991 } 992 993 @helper RenderGroupLevelFilter(IEnumerable<Dynamicweb.Ecommerce.Products.Group> groups, string currentGroupId, string activeGroup) 994 { 995 var ProductGroups = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(currentGroupId); 996 bool isTopLevelGroup = ProductGroups.IsTopGroup; 997 var parentGroup = ProductGroups.PrimaryParentGroupId; 998 999 if (isTopLevelGroup) 1000 { 1001 foreach (var group in groups.Where(x => Firstweb.Custom.CustomCode.Frontend.Helpers.EcomGroups.IsFilterGroup(x))) 1002 { 1003 var currentGroup = group.Id.Equals(currentGroupId); 1004 var isCurrentGroup = currentGroup ? "is-active" : ""; 1005 1006 <div class="@isCurrentGroup"> 1007 <a href="/Default.aspx?Id=450&groupid=@group.Id">@group.Name</a> 1008 </div> 1009 1010 if (currentGroup && group.HasChildGroups) 1011 { 1012 <div class="sub-group"> 1013 @foreach (var isSubGroup in group.Subgroups) 1014 { 1015 var setActive = isSubGroup.Id == activeGroup ? "is-active" : ""; 1016 1017 <div class="@setActive"> 1018 <a href="/Default.aspx?Id=450&groupid=@isSubGroup.Id">@isSubGroup.Name</a> 1019 </div> 1020 } 1021 @*@RenderGroupLevelFilter(group.Subgroups, currentGroupId, (level + 1))*@ 1022 </div> 1023 } 1024 } 1025 } 1026 else 1027 { 1028 @RenderGroupLevelFilter(groups, parentGroup, currentGroupId) 1029 } 1030 }

Denne hjemmeside bruger cookies til trafikmåling og optimering af indhold