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