16-17 May 2024
Directions ASIA 2024
Directions ASIA is focusing on bringing state-of-the-art keynotes and sessions about how the SMB market can unlock their full technological potential with ERP, CRM and Cloud solutions in the form of the Microsoft Power Platform, Microsoft Dynamics 365 Business Central, and Azure.
NUEVA EDICION 8 Abril 2025
Business Central en Español 2025
BC Day ES 2025, es un evento para usuarios y profesionales de Dynamics 365 Business Central que se celebra en España. Aquí podrás aprender y compartir, tanto en la parte técnica como en la parte funcional, en Español. La participación es gratuita pero es necesario registrase ya que las plazas son limitadas.
7-9 May 2025
Directions ASIA 2025
Directions ASIA is expanding to a 3-day conference in 2025, with an even greater focus on learning and growth. Join us again in Bangkok, where you’ll enjoy exceptional hospitality and a culinary experience like no other, for the latest updates from Microsoft and the ecosystem. Connect with the entire Dynamics community, including resellers, add-on providers, Microsoft, CSPs, MVPs, developers, consultants, sales and marketing professionals and business leaders. Fuel your motivation, inspiration, and success through sharing and collaboration.
Interested in sponsoring? Click here to contact our sponsor team.
The Directions EMEA 2023 calendar
Check the program
The session schedule is live! Dive into three exciting conference days packed with 3 keynotes, 200+ sessions, 15 workshops, and 20 roundtables. To make the most of your conference experience, start personalizing your schedule in the app.
This year, we're optimizing session rooms based on your preferences from the app, so be sure to add the sessions you'd like to attend in advance. Check the app to ensure you're heading to the correct room for each session.
Collect your badge at the registration area
Welcome Reception
Collect your badge at the registration area
Keynote day 1
Coffee break in the expo area
Sessions
Lunch in the expo area
Sessions
Coffee break in the expo area
Evening reception in the expo area
Keynote day 2
Coffee break in expo area
Sessions
Lunch in the expo area
Sessions
Coffee break in the expo area
Expo
Community Party
Keynote day 3
Coffee break in expo area
Sessions
Coffee break with snacks
Error executing template "Designs/Swift/Paragraph/COMM_Schedule.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_2097ba48f6dd4daba340b22807cf4873.Execute() in C:\inetpub\wwwroot\directions2023\Files\Templates\Designs\Swift\Paragraph\COMM_Schedule.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.Frontend.Navigation 3 @using Dynamicweb.Content.Items 4 @using Dynamicweb.Frontend 5 @using System.Web.Helpers 6 @using System.Web 7 @using System.Text 8 @using System.Net 9 @using System.Linq 10 @using System 11 @using System.Globalization 12 @using Newtonsoft.Json 13 @using Newtonsoft.Json.Linq 14 15 @functions{ 16 17 public bool IsValidUrl(string url) 18 { 19 if (string.IsNullOrWhiteSpace(url)) return false; 20 return Uri.TryCreate(url, UriKind.Absolute, out Uri uriResult) && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); 21 } 22 23 static string GetJson(string address) 24 { 25 using (var client = new WebClient()) 26 { 27 client.Headers.Add("Content-Type", "application/json; charset=utf-8"); 28 try { 29 return client.DownloadString(address); 30 } catch (InvalidCastException e) 31 { 32 return ""; 33 } 34 } 35 } 36 37 string makeStringReadable(string data){ 38 string response = ""; 39 if(data != "") 40 { 41 string myStr = data; 42 myStr = myStr.Replace('’', '\'').Replace('‘', '\'').Replace('–','-'); 43 byte[] bytes = Encoding.UTF8.GetBytes(myStr); 44 response = Encoding.UTF8.GetString(bytes); 45 response = response.Replace("\r\n", "<br>"); 46 response = response.Replace("’", "'"); 47 response = response.Replace("–","-"); 48 } 49 return response; 50 } 51 52 53 54 public class Root 55 { 56 public List<Session> sessions {get; set;} 57 public List<Speaker> speakers {get; set;} 58 //public List<Question> questions {get; set;} 59 public List<Category> categories {get; set;} 60 public List<Item> rooms {get; set;} 61 } 62 63 public class Session 64 { 65 public string id {get; set;} 66 public string title {get; set;} 67 public string description {get; set;} 68 public DateTime? startsAt {get; set;} 69 public DateTime? endsAt {get; set;} 70 public bool isServiceSession {get; set;} 71 public bool isPlenumSession {get; set;} 72 public List<string> speakers {get; set;} 73 public List<int> categoryItems {get; set;} 74 public List<QuestionAnswers> questionAnswers {get; set;} 75 public int? roomId {get; set;} 76 public string room {get; set;} 77 public string liveUrl {get; set;} 78 public string recordingUrl {get; set;} 79 public string status {get; set;} 80 } 81 82 public class Speaker 83 { 84 public string id {get; set;} 85 public string firstName {get; set;} 86 public string lastName {get; set;} 87 public string bio {get; set;} 88 public string tagLine {get; set;} 89 public string profilePicture {get; set;} 90 public bool isTopSpeaker {get; set;} 91 public List<Link> links {get; set;} 92 public List<string> sessions {get; set;} 93 public string fullName {get; set;} 94 public List<int> categoryItems {get; set;} 95 public List<QuestionAnswers> questionAnswers {get; set;} 96 } 97 98 public class Category 99 { 100 public int id {get; set;} 101 public string title {get; set;} 102 public List<Item> items {get; set;} 103 public int sort {get; set;} 104 public string type {get; set;} 105 } 106 107 public class Link 108 { 109 public string title {get; set;} 110 public string url {get; set;} 111 public string linType {get; set;} 112 } 113 114 public class Item 115 { 116 public int id {get; set;} 117 public string name {get; set;} 118 public int sort {get; set;} 119 } 120 121 public class Timeslot 122 { 123 public string Code {get; set;} 124 public DateTime? DateAndStart {get; set;} 125 public DateTime? SlotDate {get; set;} 126 public DateTime SlotDuration {get; set;} 127 public string SlotEnd {get; set;} 128 public string SlotStart {get; set;} 129 public string Status {get; set;} 130 131 } 132 133 public class Attribute 134 { 135 public string categoryTitle {get; set;} 136 public int categoryId {get; set;} 137 public int categorySort {get; set;} 138 public int attributeId {get; set;} 139 public string attributeName {get; set;} 140 public int attributeSort {get; set;} 141 } 142 143 public string GetTimeslot(Session session) 144 { 145 string code = session.startsAt != null && session.endsAt != null ? session.startsAt.ToString().GetHashCode().ToString("x") + session.endsAt.ToString().GetHashCode().ToString("x") : "UNDEFINED"; 146 return code; 147 } 148 149 public class QuestionAnswers 150 { 151 public string questionId {get; set;} 152 public string answerValue {get; set;} 153 } 154 155 public class Test 156 { 157 public List<QuestionAnswers> questionAnswers { get; set; } 158 } 159 } 160 161 @{ 162 var parentPages = Dynamicweb.Content.Services.Pages.GetAncestors(Pageview.Page.ID, true); 163 Dynamicweb.Content.Page rootPage = parentPages.Any() ? parentPages.Where(x => x.ItemType == "COMM_EventSection").FirstOrDefault() : new Dynamicweb.Content.Page(); 164 var item = Dynamicweb.Content.Services.Items.GetItem(rootPage.ItemType, rootPage.ItemId); 165 var sessionizeCode = item?["SessionizeCode"].ToString() ?? ""; 166 var eventCode = !string.IsNullOrEmpty(Model.Item?.GetString("SessionizeCode")) ? Model.Item?.GetString("SessionizeCode") : sessionizeCode; 167 var title = Model.Item?.GetString("Title"); 168 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) && Model.Item.GetRawValueString("Theme") != "no-theme" ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 169 string detailTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("DetailTheme")) && Model.Item.GetRawValueString("DetailTheme") != "no-theme" ? " theme " + Model.Item.GetRawValueString("DetailTheme").Replace(" ", "").Trim().ToLower() : ""; 170 detailTheme = detailTheme != "" ? detailTheme : theme; 171 string parentContainerCss = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["session"]) ? "" : "shadow rounded-7"; 172 string sessionQueryParameter = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["session"]) ? 173 Dynamicweb.Context.Current.Request.QueryString["session"] : string.Empty; 174 string sessionDetailCss = !string.IsNullOrEmpty(sessionQueryParameter) ? "p-4 rounded-7" : string.Empty; 175 176 theme = sessionQueryParameter != "" ? detailTheme : theme; 177 178 var dateTimeFormatField = !string.IsNullOrEmpty(Model.Item?.GetString("DateFormat")) ? Model.Item?.GetString("DateFormat") : ""; 179 bool isUsDateTime = dateTimeFormatField == "us"; 180 string dateTimeFormat = isUsDateTime ? "MM-dd-yyyy" : "dd-MM-yyyy"; 181 string timeFormat = isUsDateTime ? "hh:mm tt" : "HH:mm"; 182 183 } 184 185 <div class="h-100 @parentContainerCss @(theme) @sessionDetailCss schedule-container item_@Model.Item.SystemName.ToLower()"> 186 @if (!string.IsNullOrEmpty(eventCode)) 187 { 188 int dayCounter = 1; 189 int dayCounterTab = 1; 190 string sessionizeAll = "https://sessionize.com/api/v2/" + eventCode + "/view/All"; 191 192 string responseSessionizeAll = GetJson(sessionizeAll).ToString(); 193 //responseSessionizeAll = makeStringReadable(responseSessionizeAll); 194 Root responseResult = Newtonsoft.Json.JsonConvert.DeserializeObject<Root>(responseSessionizeAll); 195 196 var sessionizeSessions = responseResult.sessions; 197 var sessionizeCategories = responseResult.categories; 198 //filter categories 199 sessionizeCategories = sessionizeCategories.Any() ? sessionizeCategories.Where(x => (x.id != 34553) && (x.id != 34539)).ToList() :sessionizeCategories; 200 var sessionizeSpeakers = responseResult.speakers; 201 var sessionizeRooms = responseResult.rooms; 202 var sessionizeDays = sessionizeSessions.Where(x => x.startsAt != null).Select(x => x.startsAt?.Date).Distinct(); 203 204 List<Timeslot> sessionizeTimeslots = new List<Timeslot>(); 205 foreach(var session in sessionizeSessions) 206 { 207 Timeslot slot = new Timeslot(); 208 slot.Code = GetTimeslot(session); 209 slot.DateAndStart = session.startsAt; 210 slot.SlotDate = session.startsAt?.Date; 211 slot.SlotEnd = session.endsAt?.ToString(timeFormat); // take into consideration US & EU time 212 slot.SlotStart = session.startsAt?.ToString(timeFormat); // take into consideration US & EU time 213 slot.Status = session.isServiceSession ? "SERVICE" : "SESSION"; 214 sessionizeTimeslots.Add(slot); 215 } 216 217 List<Attribute> sessionizeAttributes = new List<Attribute>(); 218 var filteredAttributes = new string []{"Product Category"}; 219 List<string> filteredAttributesId = new List<string>{"35602","186478","186480","186481"}; 220 List<string> filteredAttributesNames = new List<string>{"No","Yes"}; 221 foreach(var category in sessionizeCategories) 222 { 223 foreach(var i in category.items) 224 { 225 if(!filteredAttributesNames.Contains(i.name)) 226 { 227 Attribute attribute = new Attribute(); 228 attribute.categoryTitle = category.title; 229 attribute.categoryId = category.id; 230 attribute.categorySort = category.sort; 231 attribute.attributeId = i.id; 232 attribute.attributeName = i.name; 233 attribute.attributeSort = i.sort; 234 sessionizeAttributes.Add(attribute); 235 } 236 } 237 } 238 239 string sessionDetailLink = Dynamicweb.Context.Current.Request.RawUrl; 240 //var filtersList = new string []{"CONTENT OWNER","PRODUCT","LEVEL","SESSION TYPE","TARGET AUDIENCE","LEARNING PATH"}; 241 var filtersList = new string []{"SESSION TYPE","TARGET AUDIENCE","PRODUCT"}; 242 var sessionTimeslots = sessionizeTimeslots.GroupBy(x => x.Code) 243 .Select(grp => grp.First()).OrderBy(x => x.SlotDate) 244 .ToList(); 245 246 string[] dayNames = {"Pre-day","Day 1","Day 2","Day 3"}; 247 248 249 int tabHeadingCount = 1; 250 int tabContentCount = 1; 251 int accordionCount = 1; 252 253 string categoriesQueryParameter = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["categories"]) ? 254 Dynamicweb.Context.Current.Request.QueryString["categories"] : string.Empty; 255 string dayQueryParameter = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["day"]) ? 256 DateTime.ParseExact(Dynamicweb.Context.Current.Request.QueryString["day"], dateTimeFormat, CultureInfo.InvariantCulture).ToString(dateTimeFormat) : string.Empty; 257 258 259 260 List<Session> sessions = new List<Session>(); 261 for (int i = 0; i < sessionizeSessions.Count; i++) 262 { 263 if (!string.IsNullOrEmpty(categoriesQueryParameter)) 264 { 265 var categories = categoriesQueryParameter?.Split(',')?.Select(Int32.Parse)?.ToList(); 266 var sessionDay = sessionizeSessions[i].startsAt.Value.ToString(dateTimeFormat); // take into consideration US & EU time 267 268 if (sessionDay == dayQueryParameter) 269 { 270 @* Filter current selected day's sessions based on applied category filters *@ 271 if (sessionizeSessions[i].categoryItems.Any(cat => categories.Contains(cat))) 272 { 273 sessions.Add(sessionizeSessions[i]); 274 } 275 else 276 { 277 sessions.Remove(sessionizeSessions[i]); 278 } 279 } 280 else 281 { 282 @* Keep sessions for other days *@ 283 sessions.Add(sessionizeSessions[i]); 284 } 285 } 286 else 287 { 288 @* Keep all sessions if no category filter is applied *@ 289 sessions.Add(sessionizeSessions[i]); 290 } 291 } 292 293 @* Filter current selected day sessions based on categories *@ 294 if (!string.IsNullOrEmpty(categoriesQueryParameter)) 295 { 296 @* Filter only current selected date sessions *@ 297 if (!string.IsNullOrEmpty(dayQueryParameter)) 298 { 299 // sessionizeSessions = sessionizeSessions.Where(session => session.startsAt.ToString("dd-MM-yyyy") == DateTime.Parse(dayQueryParameter)).ToList(); 300 } 301 var categories = categoriesQueryParameter?.Split(',')?.Select(Int32.Parse)?.ToList(); 302 sessionizeSessions = sessionizeSessions.Where(session => categories.Any(cat => session.categoryItems.Contains(cat))).ToList(); 303 } 304 305 @* Session details page logic *@ 306 if (!string.IsNullOrEmpty(sessionQueryParameter)) 307 { 308 if(sessionizeSessions.Any() && sessionizeSpeakers.Any()) 309 { 310 var session = sessionizeSessions.Where(session => session.id == sessionQueryParameter).FirstOrDefault(); 311 var room = sessionizeRooms.Any() ? sessionizeRooms?.Where(room => room.id == session.roomId)?.FirstOrDefault() : new Item(); 312 313 var speakers = sessionizeSpeakers.Where(speaker => session.speakers.Contains(speaker.id))?.ToList(); 314 var sessionDay = session.startsAt?.ToString(dateTimeFormat); // take into consideration US & EU time 315 var startTime = session.startsAt?.ToString(timeFormat); // take into consideration US & EU time 316 var endTime = session.endsAt?.ToString(timeFormat); // take into consideration US & EU time 317 var sessionTitle = makeStringReadable(!string.IsNullOrEmpty(session.title) ? session.title : "TBD"); 318 var sessionDescription = session.description != null ? makeStringReadable(session.description) : ""; 319 var roomName = room.name != null ? makeStringReadable(room.name) : ""; 320 var currentPageUrl = Dynamicweb.Context.Current.Request.RawUrl.Split('?')[0]; 321 var clearFiltersUrl = currentPageUrl + "?" + "day=" + sessionDay + "&session=" + session.id; 322 var seeScheduleUrl = currentPageUrl + "?" + "day=" + sessionDay; 323 324 string requestTypeParam = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["RequestType"]) ? 325 "&RequestType=" + Dynamicweb.Context.Current.Request.QueryString["RequestType"] : ""; 326 string layoutTemplate = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"]) ? 327 "&LayoutTemplate=" + Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"] : ""; 328 329 seeScheduleUrl = seeScheduleUrl + requestTypeParam; 330 seeScheduleUrl = seeScheduleUrl + layoutTemplate; 331 332 bool renderPresentationContainer = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["RequestType"]) && Dynamicweb.Context.Current.Request.QueryString["RequestType"] == "External"; 333 334 string recordingUrl = session.recordingUrl; 335 string liveUrl = session.liveUrl; 336 337 <div class="grid w-100"> 338 <div class="g-col-lg-6 g-col-12 mb-3"> 339 <p class="mb-3 fw-bold">@sessionDay</p> 340 <h1 class="mb-3">@sessionTitle</h1> 341 342 <div class="d-flex align-items-center gap-lg-4 gap-2 flex-wrap"> 343 <span> 344 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/clock.svg") 345 @startTime - @endTime 346 </span> 347 <span> 348 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/map-pin.svg") 349 @roomName 350 </span> 351 <span> 352 353 <a class="btn btn-secondary" href="@seeScheduleUrl">@RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/calendar.svg") @Translate("Back to event schedule")</a> 354 </span> 355 </div> 356 357 @if (renderPresentationContainer) 358 { 359 string sessionsUrl = "https://sessionize.com/api/v2/"+ eventCode + "/view/Sessions"; 360 string response = GetJson(sessionsUrl).ToString(); 361 362 JArray data = JArray.Parse(response); 363 JObject[] currentSession = data[0]["sessions"].Where(o => (string)o["id"] == session.id).Select(o => (JObject)o).ToArray(); 364 JArray questionsAndAnswers = currentSession[0]["questionAnswers"] != null ? 365 (JArray)currentSession[0]["questionAnswers"] : null; 366 367 <div class="presentation-container mt-3"> 368 @if (questionsAndAnswers != null) 369 { 370 foreach (var element in questionsAndAnswers) 371 { 372 bool renderDownloadLink = element["answer"] != null && !string.IsNullOrEmpty(element["answer"].ToString()) && IsValidUrl(element["answer"].ToString()); 373 if (renderDownloadLink) 374 { 375 <a class="btn btn-primary" href="@element["answer"]">@Translate("Download presentation")</a> 376 } 377 } 378 } 379 </div> 380 } 381 </div> 382 <div class="g-col-lg-6 g-col-12 mb-3 pt-5"> 383 384 @if(liveUrl != "" && liveUrl != null) 385 { 386 <div><p>@Translate("MeetMicrosoft_Text")</p></div> 387 <div><p><a class="btn btn-primary" href="@liveUrl">@Translate("Booth Schedule")</a></p></div> 388 389 } 390 @if (sessionDescription != null) 391 { 392 <div> 393 <p>@sessionDescription</p> 394 </div> 395 } 396 </div> 397 398 @if (speakers.Any()) 399 { 400 int categoryParameter = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["Category"]) ? 401 Convert.ToInt32(Dynamicweb.Context.Current.Request.QueryString["Category"]) : 0; 402 var groupedAttributes = sessionizeAttributes.GroupBy( 403 attr => attr.categoryTitle, 404 (key, g) => new { Title = key, Items = g.ToList() }); 405 speakers = categoryParameter != 0 ? speakers.Where(speaker => speaker.categoryItems.Contains(categoryParameter)).ToList() : speakers; 406 string headerTitle = Translate("Speakers") + ":"; 407 408 <div class="g-col-12"> 409 <div class="w-100"> 410 <h4 class="mb-4">@headerTitle</h4> 411 </div> 412 413 @* Speakers wall *@ 414 <div class="grid"> 415 @for (int i = 0; i < speakers.Count(); i++) 416 { 417 var columnSize = "grid g-col-12"; 418 419 string viewSpeakerInfoTranslate = Translate("View") + " " + speakers[i].fullName + " " + Translate("info"); 420 string speakerName = speakers[i].fullName != null ? speakers[i].fullName.ToString() : ""; 421 string speakerBio = speakers[i].bio != null ? speakers[i].bio.ToString() : ""; 422 string speakerTagLine = speakers[i].tagLine != null ? speakers[i].tagLine.ToString() : ""; 423 string speakerProfilePicture = speakers[i].profilePicture != null ? speakers[i].profilePicture.ToString() : ""; 424 string speakerCategories = speakers[i].categoryItems != null ? String.Join(",", speakers[i].categoryItems) : ""; 425 var speakerSession = sessions.Where(session => speakers[i].sessions.Contains(session.id)).FirstOrDefault(); 426 var speakerSessions = sessions.Where(session => speakers[i].sessions.Contains(session.id)).ToList(); 427 var sessionsTranslate = speakerSessions.Count() > 1 ? Translate("Sessions") : Translate("Session"); 428 429 <div class="speaker @theme @columnSize gap-0 position-relative rounded-7" data-categories="@speakerCategories"> 430 <div class="h-100 g-col-lg-7 g-col-12 order-last order-md-first max-h-45-em"> 431 <div class="h-100 py-4 max-h-30-em"> 432 <div> 433 <a href="#" class="btn btn-link p-0 text-decoration-none" title="@viewSpeakerInfoTranslate" onclick="event.preventDefault(); new bootstrap.Modal(document.getElementById('SpeakerModal_@i')).show()"> 434 <h4 class="text-start">@makeStringReadable(speakerName)</h4> 435 </a> 436 <p>@speakerTagLine</p> 437 </div> 438 <div class="d-flex flex-column gap-3 justify-content-between max-h-15-em overflow-auto"> 439 <p class="m-0 pb-4">@makeStringReadable(speakerBio)</p> 440 </div> 441 @if (speakers[i].links.Any()) 442 { 443 <div class="d-flex gap-3 align-items-end position-absolute top-100 mt-negative-3-em"> 444 @foreach (var link in speakers[i].links) 445 { 446 string icon = "/Files/Templates/Designs/Swift/Assets/icons/"; 447 448 if (link.url.Contains("twitter")) 449 { 450 icon += "twitter.svg"; 451 } 452 else if (link.url.Contains("linkedin")) 453 { 454 icon += "linkedin.svg"; 455 } 456 else 457 { 458 icon += "link.svg"; 459 } 460 461 <a href="@link.url" title="@link.title" target="_blank"> 462 @RenderIcon(icon) 463 </a> 464 } 465 </div> 466 } 467 </div> 468 </div> 469 <div class="h-100 ratio g-col-lg-5 g-col-12 rounded-7" style="--bs-aspect-ratio: 100%;"> 470 <a class="btn btn-link p-4" title="@viewSpeakerInfoTranslate" onclick="new bootstrap.Modal(document.getElementById('SpeakerModal_@i')).show()"> 471 <img src="@speakerProfilePicture" class="w-100 h-100 rounded-7"></img> 472 </a> 473 </div> 474 <div class="h-100 w-100 position-absolute cursor-pointer d-none"> 475 <a class="btn btn-link stretched-link" title="@viewSpeakerInfoTranslate" onclick="new bootstrap.Modal(document.getElementById('SpeakerModal_@i')).show()"></a> 476 </div> 477 </div> 478 479 <div class="modal fade modal-xl" id="SpeakerModal_@i"> 480 <div class="modal-dialog"> 481 <div class="modal-content @theme rounded-7"> 482 <div class="modal-body p-0" id="SpeakerModalBodyContainer_@i"> 483 <div class="close-container position-absolute p-3 end-0 m-3 bg-white rounded-7 z-index-9"> 484 <button type="button" class="btn-close bg-dark z-index-9" data-bs-dismiss="modal" aria-label="Close"></button> 485 </div> 486 <div class="grid @theme gap-0 rounded-7"> 487 <div class="h-100 g-col-lg-7 g-col-12 order-last order-md-first"> 488 <div class="h-100 p-4 overflow-auto max-h-30-em"> 489 <div> 490 <h4>@makeStringReadable(speakerName)</h4> 491 <p>@speakerTagLine</p> 492 </div> 493 <div class="d-flex flex-column gap-3 justify-content-between"> 494 <p class="m-0">@makeStringReadable(speakerBio)</p> 495 </div> 496 <div class="mt-4 d-grid"> 497 <p class="m-0 fw-bold">@sessionsTranslate</p> 498 @foreach (var _item in speakerSessions) 499 { 500 var sessionLink = currentPageUrl.IndexOf("?") > 0 ? currentPageUrl + "&session=" + _item.id : currentPageUrl + "?session=" + _item.id; 501 <a class="btn btn-link text-start p-0 mb-2" title="@_item.title" href="@sessionLink"> 502 @_item.title 503 </a> 504 } 505 </div> 506 @if (speakers[i].links.Any()) 507 { 508 <div class="d-flex gap-3 align-items-end"> 509 @foreach (var link in speakers[i].links) 510 { 511 string icon = "/Files/Templates/Designs/Swift/Assets/icons/"; 512 513 if (link.url.Contains("twitter")) 514 { 515 icon += "twitter.svg"; 516 } 517 else if (link.url.Contains("linkedin")) 518 { 519 icon += "linkedin.svg"; 520 } 521 else 522 { 523 icon += "link.svg"; 524 } 525 526 <a href="@link.url" title="@link.title" target="_blank"> 527 @RenderIcon(icon) 528 </a> 529 } 530 </div> 531 } 532 </div> 533 </div> 534 <div class="h-100 ratio g-col-lg-5 g-col-12" style="--bs-aspect-ratio: 100%"> 535 <img src="@speakerProfilePicture" class="rounded-7"></img> 536 </div> 537 </div> 538 </div> 539 </div> 540 </div> 541 </div> 542 543 } 544 </div> 545 </div> 546 } 547 </div> 548 } 549 } 550 else 551 { 552 if(sessionizeDays.Count() > 1) 553 { 554 <nav class="tabs rounded-7"> 555 <ul class="nav nav-tabs days-tabs bg-light-grey rounded-7-top" role="tablist"> 556 @foreach (var day in sessionizeDays) 557 { 558 string dayDate = day?.ToString(dateTimeFormat); // take into consideration US & EU time 559 string dayName = dayNames[tabHeadingCount - 1]; 560 bool showCounterWithTabName = dayName == "Training" ? true : false; 561 string tabAnchorRaw = showCounterWithTabName ? dayName + tabHeadingCount : dayName; 562 string tabAnchor = "tabname" + tabAnchorRaw + tabHeadingCount; 563 bool hasDayParam = !string.IsNullOrEmpty(dayQueryParameter); 564 string activeClass = hasDayParam && dayQueryParameter == dayDate ? 565 "active" : tabHeadingCount == 1 && !hasDayParam ? "active" : ""; 566 567 string firstTabCss = tabHeadingCount == 1 ? "rounded-7-top-left" : ""; 568 569 <li class="flex-mobile-auto @(firstTabCss)" role="presentation"> 570 <a class="session-tab nav-link d-grid text-decoration-none p-3 pe-4 @(activeClass) @(firstTabCss)" href="@("#" + tabAnchor)" aria-controls="@tabAnchor" role="tab" data-toggle="tab" data-date="@dayDate"> 571 <span class="fw-bold">@dayName</span> <span>@dayDate</span> 572 </a> 573 </li> 574 tabHeadingCount += 1; 575 } 576 </ul> 577 </nav> 578 } 579 <div class="tab-content days-tabs-container p-lg-4 p-2 rounded-7-bottom"> 580 @foreach (var day in sessionizeDays) 581 { 582 string dayClass = tabContentCount == 1 ? "active" : ""; 583 string timeslotsDay = day?.ToString(dateTimeFormat); // take into consideration US & EU time 584 string dayName = dayNames[tabContentCount - 1]; 585 bool showCounterWithTabName = dayName == "Training" ? true : false; 586 string tabAnchorRaw = showCounterWithTabName ? dayName + tabContentCount : dayName; 587 string tabAnchor = "tabname" + tabAnchorRaw + tabContentCount; 588 string dayAnchor = showCounterWithTabName ? dayName + tabContentCount : dayName; 589 string dayID = "day" + tabContentCount; 590 591 var daySessionsList = sessionizeSessions.Where(x => x.startsAt?.Date == day); 592 var dayAttributes = new List<int>(); 593 foreach(var daySession in daySessionsList) 594 { 595 dayAttributes.AddRange(daySession.categoryItems); 596 } 597 598 var dayAttributesFiltered = sessionizeAttributes.Where(item => dayAttributes.Any(filter => filter.Equals(item.attributeId))); 599 //remove attribute categories based on filtersList 600 dayAttributesFiltered = dayAttributesFiltered.Where(item => filtersList.Contains(item.categoryTitle.ToUpper())).ToList(); 601 var dayAttributesCategories = dayAttributesFiltered.Select(item => item.categoryTitle).Distinct(); 602 603 bool hasDayParam = !string.IsNullOrEmpty(dayQueryParameter); 604 bool matchingDate = hasDayParam && (dayQueryParameter == timeslotsDay); 605 var activeTabClass = matchingDate ? 606 "active" : tabContentCount == 1 && !hasDayParam ? "active" : ""; 607 608 var activeTabContainerClass = matchingDate ? 609 "show" : tabContentCount == 1 && !hasDayParam ? "show" : ""; 610 611 <div id="@tabAnchor" role="tabpanel" class="tab-pane @activeTabClass"> 612 613 @*<!--Filters--> 614 @if (dayAttributesFiltered.Any()) 615 { 616 <div class="grid"> 617 <div class="g-col-lg-4 g-col-12"> 618 <label class="fw-bold" for="filter-sessions-1">@Translate("Filter Sessions")</label> 619 <select id="filter-sessions-@tabContentCount" name="filter-sessions" class="form-control select2-multiple filter-session-ref filter-session-trigger" multiple="multiple" data-date="@timeslotsDay"> 620 @foreach (var category in dayAttributesFiltered) 621 { 622 //Take into consideration the dayParam 623 var categories = !string.IsNullOrEmpty(categoriesQueryParameter) ? categoriesQueryParameter?.Split(',')?.Select(Int32.Parse)?.ToList() : null; 624 var selected = !string.IsNullOrEmpty(categoriesQueryParameter) && categories.Contains(category.attributeId) ? "selected" : ""; 625 <option @selected value='@category.attributeId'>@category.attributeName @category.categoryTitle</option> 626 } 627 </select> 628 @if (!string.IsNullOrEmpty(categoriesQueryParameter)) 629 { 630 var clearFiltersUrl = Dynamicweb.Context.Current.Request.RawUrl.Split('?')[0] + "?" + "day=" + timeslotsDay; 631 //include RequestType & LayoutTemplate params if present (embedded page scenario) 632 string requestTypeParam = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["RequestType"]) ? 633 "&RequestType=" + Dynamicweb.Context.Current.Request.QueryString["RequestType"] : ""; 634 string layoutTemplate = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"]) ? 635 "&LayoutTemplate=" + Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"] : ""; 636 637 clearFiltersUrl = clearFiltersUrl + requestTypeParam; 638 clearFiltersUrl = clearFiltersUrl + layoutTemplate; 639 <a class="btn btn-link" href="@clearFiltersUrl" title="Clear filters"> 640 @Translate("Clear filters") 641 </a> 642 } 643 </div> 644 </div> 645 } 646 647 <div class="hidden"> 648 <div class="filterData"> 649 @foreach (var filter in filtersList) 650 { 651 string filterName = filter; 652 } 653 </div> 654 </div> 655 656 <!--FiltersEnd-->*@ 657 658 <!--Filters--> 659 @if (dayAttributesFiltered.Any()) 660 { 661 <div class="grid"> 662 @foreach(var filterCategory in dayAttributesCategories) 663 { 664 string filterCategoryName = filterCategory; 665 string filterLabel = Translate("Filter " + filterCategoryName); 666 filterCategoryName = filterCategoryName.Replace(" ","-").ToLower(); 667 var dayAttributesList = dayAttributesFiltered.OrderBy(item=>item.attributeName).Where(item=>item.categoryTitle == filterCategory).ToList(); 668 669 <div class="g-col-lg-4 g-col-12"> 670 <label class="fw-bold" for="filter-@filterCategoryName-@tabContentCount">@filterLabel</label> 671 <select id="filter-@filterCategoryName-@tabContentCount" name="filter-@filterCategoryName" class="filter-dropdown form-control select2-multiple filter-@filterCategoryName-ref filter-@filterCategoryName-trigger" multiple="multiple" data-date="@timeslotsDay"> 672 @foreach (var category in dayAttributesList) 673 { 674 @* Take into consideration the dayParam *@ 675 var categories = !string.IsNullOrEmpty(categoriesQueryParameter) ? categoriesQueryParameter?.Split(',')?.Select(Int32.Parse)?.ToList() : null; 676 var selected = !string.IsNullOrEmpty(categoriesQueryParameter) && categories.Contains(category.attributeId) ? "selected" : ""; 677 <option @selected value='@category.attributeId'>@category.attributeName @category.categoryTitle</option> 678 } 679 </select> 680 @if (!string.IsNullOrEmpty(categoriesQueryParameter)) 681 { 682 var clearFiltersUrl = Dynamicweb.Context.Current.Request.RawUrl.Split('?')[0] + "?" + "day=" + timeslotsDay; 683 @* include RequestType & LayoutTemplate params if present (embedded page scenario) *@ 684 string requestTypeParam = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["RequestType"]) ? 685 "&RequestType=" + Dynamicweb.Context.Current.Request.QueryString["RequestType"] : ""; 686 string layoutTemplate = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"]) ? 687 "&LayoutTemplate=" + Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"] : ""; 688 689 clearFiltersUrl = clearFiltersUrl + requestTypeParam; 690 clearFiltersUrl = clearFiltersUrl + layoutTemplate; 691 <a class="btn btn-link clear-filters" href="@clearFiltersUrl" title="Clear filters" data-target-dropdown="filter-@filterCategoryName-@tabContentCount"> 692 @Translate("Clear filters") 693 </a> 694 } 695 </div> 696 697 } 698 </div> 699 } 700 701 <div class="hidden"> 702 <div class="filterData"> 703 @foreach (var filter in filtersList) 704 { 705 string filterName = filter; 706 } 707 </div> 708 </div> 709 710 <!--FiltersEnd--> 711 712 713 <!-- Second level content start --> 714 <div class="tab-content2 tab-content-schedule"> 715 <!-- All sessions start --> 716 <div id='@(dayID + "_allsessions")' class="tab-pane fade in @activeTabContainerClass"> 717 <div class="panel-group mt-4" id="@(dayID + "_allsessions_timeline")"> 718 <p class="not-found mb-0 d-none">@Translate("No items found.")</p> 719 @foreach (var timeslot in sessionTimeslots.Where(x => x.SlotDate == day).OrderBy(x => x.DateAndStart)) 720 { 721 string timeslotCode = timeslot.Code; 722 var start = timeslot.SlotStart; // take into consideration US & EU time 723 var end = timeslot.SlotEnd; // take into consideration US & EU time 724 var currentSessions = sessions.Where(x => GetTimeslot(x) == timeslotCode); // filtered sessions list by day & categories [based on sessionizeSessions] 725 726 if (currentSessions.Any()) 727 { 728 string breakSessionTheme = currentSessions.Any(session => session.isServiceSession) ? "theme ice-blue" : ""; 729 string isBreakSession = currentSessions.Any(session => session.isServiceSession) ? "is-break" : ""; 730 <!-- Lecture start --> 731 <div class="grid gap-lg-4 panel schedule-item border p-4 pt-6 mb-3 position-relative rounded-7 grid-auto-rows-1-fr @timeslotCode @isBreakSession" data-duration="" data-status=""> 732 <div class="g-col-12 time-wrapper position-absolute py-3 px-4 ms-4 rounded-7-bottom"> 733 <strong class="time" data-debug="">@start - @end</strong> 734 </div> 735 @foreach (var session in currentSessions) 736 { 737 738 var sessionRoom = sessionizeRooms.Where(x => x.id == session.roomId).FirstOrDefault(); 739 string sessionRoomName = sessionRoom != null ? makeStringReadable(sessionRoom.name) : "N/A"; 740 var isBreak = session.isServiceSession ? "full" : ""; 741 var test = session.isServiceSession ? "tested" : ""; // Check data structure here 742 var publishingStatus = "FULL"; 743 var approvalStatus = "approved"; 744 var categoriesList = session.categoryItems; 745 var _categories = String.Join(",", categoriesList.ToArray()); 746 string categories = ""; 747 748 var attributes = sessionizeAttributes.Where(item => categoriesList.Any(category => category == item.attributeId)); 749 string sessionDetailLinkAdjusted = sessionDetailLink.IndexOf("?") > 0 ? sessionDetailLink + "&session=" + session.id : sessionDetailLink + "?session=" + session.id; 750 string sessionTitle = makeStringReadable(!string.IsNullOrEmpty(session.title) ? session.title : "TBD"); 751 string columnCss = currentSessions.Count() == 1 ? "g-col-12" : "g-col-lg-6 g-col-12"; 752 <div class="d-grid gap-3 @columnCss title border-bottom my-3 pb-3 @test"> 753 <div class="session-name" data-publishing="@publishingStatus" data-categories="@_categories" data-approval="@approvalStatus" data-code="@session.id"> 754 @if (publishingStatus == "FULL" && isBreak == "") 755 { 756 <a class="text-decoration-none fw-bold" href='@sessionDetailLinkAdjusted' data-id="@session.id"><h5 class="session-header">@sessionTitle</h5></a> 757 } 758 else 759 { 760 <h5 class="session-header fw-bold">@sessionTitle</h5> 761 } 762 </div> 763 764 <div class="mb-2"> 765 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/map-pin.svg") 766 <span class="speaker-name">@sessionRoomName</span> 767 </div> 768 769 @if (attributes.Any()) 770 { 771 <div> 772 <p class="fw-bold mb-2 tags-text">@Translate("Tags")</p> 773 <div class="d-flex flex-wrap gap-2"> 774 @foreach (var attr in attributes) 775 { 776 <a class="session-tags text-decoration-underline fs-7" href="#" data-target="filter-sessions-@tabContentCount" id="@attr.attributeId">@attr.attributeName</a> 777 } 778 </div> 779 </div> 780 } 781 782 </div> 783 } 784 </div> 785 <!--Lecture end--> 786 } 787 } 788 </div> 789 </div> 790 </div> 791 </div> 792 793 tabContentCount += 1; 794 } 795 </div> 796 } 797 } 798 799 </div> 800 801 802 @helper RenderIcon(string icon) 803 { 804 if (System.IO.Path.GetExtension(icon).ToLower() == ".svg") 805 { 806 if (!icon.ToLower().Contains("none") && icon != string.Empty) 807 { 808 <span class="icon-auto"> 809 @ReadFile(icon) 810 </span> 811 } 812 } 813 } 814 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.css" integrity="sha512-ijXMfMV6D0xH0UfHpPnqrwbw9cjd4AbjtWbdfVd204tXEtJtvL3TTNztvqqr9AbLcCiuNTvqHL5c9v2hOjdjpA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> 815 <script src="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.js" integrity="sha512-0E8oaoA2v32h26IycsmRDShtQ8kMgD91zWVBxdIvUCjU3xBw81PV61QBsBqNQpWkp/zYJZip8Ag3ifmzz1wCKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>