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.

 

 

Home video

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.

 

Home video

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
18:00 - 20:00
Welcome Reception
18:00 - 20:00
Collect your badge at the registration area
07:00 - 09:00
Keynote day 1
09:00 - 10:45
Coffee break in the expo area
10:45 - 11:15
Sessions
11:15 - 13:00
Lunch in the expo area
13:00 - 14:00
Sessions
14:00 - 18:00
Coffee break in the expo area
15:45 - 16:15
Evening reception in the expo area
18:00 - 20:00
Keynote day 2
09:00 - 10:45
Coffee break in expo area
10:45 - 11:15
Sessions
11:15 - 13:00
Lunch in the expo area
13:00 - 14:00
Sessions
14:00 - 18:00
Coffee break in the expo area
15:45 - 16:15
Expo
18:00 - 19:00
Community Party
19:00 - 00:00
Keynote day 3
09:00 - 10:00
Coffee break in expo area
10:00 - 10:30
Sessions
10:30 - 14:30
Coffee break with snacks
12:15 - 12:45
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_e86ff5a5469443c3a01a34108fd7857d.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 } 186 187 <div class="h-100 @parentContainerCss @(theme) @sessionDetailCss schedule-container item_@Model.Item.SystemName.ToLower()"> 188 @if (!string.IsNullOrEmpty(eventCode)) 189 { 190 int dayCounter = 1; 191 int dayCounterTab = 1; 192 string sessionizeAll = "https://sessionize.com/api/v2/" + eventCode + "/view/All"; 193 string responseSessionizeAll = GetJson(sessionizeAll).ToString(); 194 //responseSessionizeAll = makeStringReadable(responseSessionizeAll); 195 Root responseResult = Newtonsoft.Json.JsonConvert.DeserializeObject<Root>(responseSessionizeAll); 196 197 var sessionizeSessions = responseResult.sessions; 198 var sessionizeCategories = responseResult.categories; 199 //filter categories 200 sessionizeCategories = sessionizeCategories.Any() ? sessionizeCategories.Where(x => (x.id != 34553) && (x.id != 34539)).ToList() :sessionizeCategories; 201 var sessionizeSpeakers = responseResult.speakers; 202 var sessionizeRooms = responseResult.rooms; 203 204 // Filter out sessions with "Hidden" session type 205 var hiddenCategory = sessionizeCategories.FirstOrDefault(x => x.title?.Equals("Session type", StringComparison.OrdinalIgnoreCase) == true); 206 if (hiddenCategory != null) 207 { 208 var hiddenItem = hiddenCategory.items.FirstOrDefault(x => x.name?.Equals("Hidden", StringComparison.OrdinalIgnoreCase) == true); 209 if (hiddenItem != null) 210 { 211 sessionizeSessions = sessionizeSessions.Where(session => !session.categoryItems.Contains(hiddenItem.id)).ToList(); 212 } 213 } 214 215 var sessionizeDays = sessionizeSessions.Where(x => x.startsAt != null).Select(x => x.startsAt?.Date).Distinct(); 216 217 List<Timeslot> sessionizeTimeslots = new List<Timeslot>(); 218 foreach(var session in sessionizeSessions) 219 { 220 Timeslot slot = new Timeslot(); 221 slot.Code = GetTimeslot(session); 222 slot.DateAndStart = session.startsAt; 223 slot.SlotDate = session.startsAt?.Date; 224 slot.SlotEnd = session.endsAt?.ToString(timeFormat); // take into consideration US & EU time 225 slot.SlotStart = session.startsAt?.ToString(timeFormat); // take into consideration US & EU time 226 slot.Status = session.isServiceSession ? "SERVICE" : "SESSION"; 227 sessionizeTimeslots.Add(slot); 228 } 229 230 List<Attribute> sessionizeAttributes = new List<Attribute>(); 231 var filteredAttributes = new string []{"Product Category"}; 232 List<string> filteredAttributesId = new List<string>{"35602","186478","186480","186481"}; 233 List<string> filteredAttributesNames = new List<string>{"No","Yes"}; 234 foreach(var category in sessionizeCategories) 235 { 236 foreach(var i in category.items) 237 { 238 if(!filteredAttributesNames.Contains(i.name)) 239 { 240 Attribute attribute = new Attribute(); 241 attribute.categoryTitle = category.title; 242 attribute.categoryId = category.id; 243 attribute.categorySort = category.sort; 244 attribute.attributeId = i.id; 245 attribute.attributeName = i.name; 246 attribute.attributeSort = i.sort; 247 sessionizeAttributes.Add(attribute); 248 } 249 } 250 } 251 252 string sessionDetailLink = Dynamicweb.Context.Current.Request.RawUrl; 253 //var filtersList = new string []{"CONTENT OWNER","PRODUCT","LEVEL","SESSION TYPE","TARGET AUDIENCE","LEARNING PATH"}; 254 var filtersList = new string []{"SESSION TYPE","TARGET AUDIENCE","PRODUCT"}; 255 var sessionTimeslots = sessionizeTimeslots.GroupBy(x => x.Code) 256 .Select(grp => grp.First()).OrderBy(x => x.SlotDate) 257 .ToList(); 258 259 string[] defaultDayNames = {"Pre-day","Day 1","Day 2","Day 3"}; 260 string[] dayNames = Model.Item.GetRawValueString("DayNames") 261 .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) 262 .Select(item => item.Trim()) 263 .ToArray(); 264 dayNames = dayNames == null || dayNames.Length == 0 ? defaultDayNames : dayNames; 265 266 267 int tabHeadingCount = 1; 268 int tabContentCount = 1; 269 int accordionCount = 1; 270 271 string categoriesQueryParameter = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["categories"]) ? 272 Dynamicweb.Context.Current.Request.QueryString["categories"] : string.Empty; 273 string dayQueryParameter = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["day"]) ? 274 DateTime.ParseExact(Dynamicweb.Context.Current.Request.QueryString["day"], dateTimeFormat, CultureInfo.InvariantCulture).ToString(dateTimeFormat) : string.Empty; 275 276 277 278 List<Session> sessions = new List<Session>(); 279 for (int i = 0; i < sessionizeSessions.Count; i++) 280 { 281 if (!string.IsNullOrEmpty(categoriesQueryParameter)) 282 { 283 var categories = categoriesQueryParameter?.Split(',')?.Select(Int32.Parse)?.ToList(); 284 var sessionDay = sessionizeSessions[i].startsAt.Value.ToString(dateTimeFormat); // take into consideration US & EU time 285 286 if (sessionDay == dayQueryParameter) 287 { 288 @* Filter current selected day's sessions based on applied category filters *@ 289 if (sessionizeSessions[i].categoryItems.Any(cat => categories.Contains(cat))) 290 { 291 sessions.Add(sessionizeSessions[i]); 292 } 293 else 294 { 295 sessions.Remove(sessionizeSessions[i]); 296 } 297 } 298 else 299 { 300 @* Keep sessions for other days *@ 301 sessions.Add(sessionizeSessions[i]); 302 } 303 } 304 else 305 { 306 @* Keep all sessions if no category filter is applied *@ 307 sessions.Add(sessionizeSessions[i]); 308 } 309 } 310 311 @* Filter current selected day sessions based on categories *@ 312 if (!string.IsNullOrEmpty(categoriesQueryParameter)) 313 { 314 @* Filter only current selected date sessions *@ 315 if (!string.IsNullOrEmpty(dayQueryParameter)) 316 { 317 // sessionizeSessions = sessionizeSessions.Where(session => session.startsAt.ToString("dd-MM-yyyy") == DateTime.Parse(dayQueryParameter)).ToList(); 318 } 319 var categories = categoriesQueryParameter?.Split(',')?.Select(Int32.Parse)?.ToList(); 320 sessionizeSessions = sessionizeSessions.Where(session => categories.Any(cat => session.categoryItems.Contains(cat))).ToList(); 321 } 322 323 @* Session details page logic *@ 324 if (!string.IsNullOrEmpty(sessionQueryParameter)) 325 { 326 var session = sessionizeSessions.Where(session => session.id == sessionQueryParameter).FirstOrDefault(); 327 if (session == null) 328 { 329 // Log, redirect, show error, etc. 330 return; 331 } 332 333 int? sessionRoomId = session != null ? session.roomId : 0; 334 var room = sessionizeRooms.Where(room => room.id == sessionRoomId).FirstOrDefault(); 335 336 var speakers = sessionizeSpeakers.Where(speaker => session.speakers.Contains(speaker.id)).ToList(); 337 var sessionDay = session.startsAt?.ToString(dateTimeFormat); // take into consideration US & EU time 338 var startTime = session.startsAt?.ToString(timeFormat); // take into consideration US & EU time 339 var endTime = session.endsAt?.ToString(timeFormat); // take into consideration US & EU time 340 var sessionTitle = makeStringReadable(!string.IsNullOrEmpty(session.title) ? session.title : "TBD"); 341 var sessionDescription = session.description != null ? makeStringReadable(session.description) : ""; 342 var roomName = room.name != null ? makeStringReadable(room.name) : ""; 343 var currentPageUrl = Dynamicweb.Context.Current.Request.RawUrl.Split('?')[0]; 344 var clearFiltersUrl = currentPageUrl + "?" + "day=" + sessionDay + "&session=" + session.id; 345 var seeScheduleUrl = currentPageUrl + "?" + "day=" + sessionDay; 346 347 string requestTypeParam = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["RequestType"]) ? 348 "&RequestType=" + Dynamicweb.Context.Current.Request.QueryString["RequestType"] : ""; 349 string layoutTemplate = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"]) ? 350 "&LayoutTemplate=" + Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"] : ""; 351 352 seeScheduleUrl = seeScheduleUrl + requestTypeParam; 353 seeScheduleUrl = seeScheduleUrl + layoutTemplate; 354 355 bool renderPresentationContainer = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["RequestType"]) && Dynamicweb.Context.Current.Request.QueryString["RequestType"] == "External"; 356 357 string recordingUrl = session.recordingUrl; 358 string liveUrl = session.liveUrl; 359 360 <div class="grid w-100"> 361 <div class="g-col-lg-6 g-col-12 mb-3"> 362 <p class="mb-3 fw-bold">@sessionDay</p> 363 <h1 class="mb-3">@sessionTitle</h1> 364 365 <div class="d-flex align-items-center gap-lg-4 gap-2 flex-wrap"> 366 <span> 367 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/clock.svg") 368 @startTime - @endTime 369 </span> 370 <span> 371 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/map-pin.svg") 372 @roomName 373 </span> 374 <span> 375 376 <a class="btn btn-secondary" href="@seeScheduleUrl">@RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/calendar.svg") @Translate("Back to event schedule")</a> 377 </span> 378 </div> 379 380 @if (renderPresentationContainer) 381 { 382 string sessionsUrl = "https://sessionize.com/api/v2/"+ eventCode + "/view/Sessions"; 383 string response = GetJson(sessionsUrl).ToString(); 384 385 JArray data = JArray.Parse(response); 386 JObject[] currentSession = data[0]["sessions"].Where(o => (string)o["id"] == session.id).Select(o => (JObject)o).ToArray(); 387 JArray questionsAndAnswers = currentSession[0]["questionAnswers"] != null ? 388 (JArray)currentSession[0]["questionAnswers"] : null; 389 390 <div class="presentation-container mt-3"> 391 @if (questionsAndAnswers != null) 392 { 393 foreach (var element in questionsAndAnswers) 394 { 395 bool renderDownloadLink = element["answer"] != null && !string.IsNullOrEmpty(element["answer"].ToString()) && IsValidUrl(element["answer"].ToString()); 396 if (renderDownloadLink) 397 { 398 <a class="btn btn-primary" href="@element["answer"]">@Translate("Download presentation")</a> 399 } 400 } 401 } 402 </div> 403 } 404 </div> 405 <div class="g-col-lg-6 g-col-12 mb-3 pt-5"> 406 407 @if(liveUrl != "" && liveUrl != null) 408 { 409 <div><p>@Translate("MeetMicrosoft_Text")</p></div> 410 <div><p><a class="btn btn-primary" href="@liveUrl">@Translate("Booth Schedule")</a></p></div> 411 412 } 413 @if (sessionDescription != null) 414 { 415 <div> 416 <p>@sessionDescription</p> 417 </div> 418 } 419 </div> 420 421 @if (speakers.Any()) 422 { 423 int categoryParameter = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["Category"]) ? 424 Convert.ToInt32(Dynamicweb.Context.Current.Request.QueryString["Category"]) : 0; 425 var groupedAttributes = sessionizeAttributes.GroupBy( 426 attr => attr.categoryTitle, 427 (key, g) => new { Title = key, Items = g.ToList() }); 428 speakers = categoryParameter != 0 ? speakers.Where(speaker => speaker.categoryItems.Contains(categoryParameter)).ToList() : speakers; 429 string headerTitle = Translate("Speakers") + ":"; 430 431 <div class="g-col-12"> 432 <div class="w-100"> 433 <h4 class="mb-4">@headerTitle</h4> 434 </div> 435 436 @* Speakers wall *@ 437 <div class="grid"> 438 @for (int i = 0; i < speakers.Count(); i++) 439 { 440 var columnSize = "grid g-col-12"; 441 442 string viewSpeakerInfoTranslate = Translate("View") + " " + speakers[i].fullName + " " + Translate("info"); 443 string speakerName = speakers[i].fullName != null ? speakers[i].fullName.ToString() : ""; 444 string speakerBio = speakers[i].bio != null ? speakers[i].bio.ToString() : ""; 445 string speakerTagLine = speakers[i].tagLine != null ? speakers[i].tagLine.ToString() : ""; 446 string speakerProfilePicture = speakers[i].profilePicture != null ? speakers[i].profilePicture.ToString() : ""; 447 string speakerCategories = speakers[i].categoryItems != null ? String.Join(",", speakers[i].categoryItems) : ""; 448 var speakerSession = sessions.Where(session => speakers[i].sessions.Contains(session.id)).FirstOrDefault(); 449 var speakerSessions = sessions.Where(session => speakers[i].sessions.Contains(session.id)).ToList(); 450 var sessionsTranslate = speakerSessions.Count() > 1 ? Translate("Sessions") : Translate("Session"); 451 452 <div class="speaker @theme @columnSize gap-0 position-relative rounded-7" data-categories="@speakerCategories"> 453 <div class="h-100 g-col-lg-7 g-col-12 order-last order-md-first max-h-45-em"> 454 <div class="h-100 py-4 max-h-30-em"> 455 <div> 456 <a href="#" class="btn btn-link p-0 text-decoration-none" title="@viewSpeakerInfoTranslate" onclick="event.preventDefault(); new bootstrap.Modal(document.getElementById('SpeakerModal_@i')).show()"> 457 <h4 class="text-start">@makeStringReadable(speakerName)</h4> 458 </a> 459 <p>@speakerTagLine</p> 460 </div> 461 <div class="d-flex flex-column gap-3 justify-content-between max-h-15-em overflow-auto"> 462 <p class="m-0 pb-4">@makeStringReadable(speakerBio)</p> 463 </div> 464 @if (speakers[i].links.Any()) 465 { 466 <div class="d-flex gap-3 align-items-end position-absolute top-100 mt-negative-3-em"> 467 @foreach (var link in speakers[i].links) 468 { 469 string icon = "/Files/Templates/Designs/Swift/Assets/icons/"; 470 471 if (link.url.Contains("twitter")) 472 { 473 icon += "twitter.svg"; 474 } 475 else if (link.url.Contains("linkedin")) 476 { 477 icon += "linkedin.svg"; 478 } 479 else 480 { 481 icon += "link.svg"; 482 } 483 484 <a href="@link.url" title="@link.title" target="_blank"> 485 @RenderIcon(icon) 486 </a> 487 } 488 </div> 489 } 490 </div> 491 </div> 492 <div class="h-100 ratio g-col-lg-5 g-col-12 rounded-7" style="--bs-aspect-ratio: 100%;"> 493 <a class="btn btn-link p-4" title="@viewSpeakerInfoTranslate" onclick="new bootstrap.Modal(document.getElementById('SpeakerModal_@i')).show()"> 494 <img src="@speakerProfilePicture" class="w-100 h-100 rounded-7"></img> 495 </a> 496 </div> 497 <div class="h-100 w-100 position-absolute cursor-pointer d-none"> 498 <a class="btn btn-link stretched-link" title="@viewSpeakerInfoTranslate" onclick="new bootstrap.Modal(document.getElementById('SpeakerModal_@i')).show()"></a> 499 </div> 500 </div> 501 502 <div class="modal fade modal-xl" id="SpeakerModal_@i"> 503 <div class="modal-dialog"> 504 <div class="modal-content @theme rounded-7"> 505 <div class="modal-body p-0" id="SpeakerModalBodyContainer_@i"> 506 <div class="close-container position-absolute p-3 end-0 m-3 bg-white rounded-7 z-index-9"> 507 <button type="button" class="btn-close bg-dark z-index-9" data-bs-dismiss="modal" aria-label="Close"></button> 508 </div> 509 <div class="grid @theme gap-0 rounded-7"> 510 <div class="h-100 g-col-lg-7 g-col-12 order-last order-md-first"> 511 <div class="h-100 p-4 overflow-auto max-h-30-em"> 512 <div> 513 <h4>@makeStringReadable(speakerName)</h4> 514 <p>@speakerTagLine</p> 515 </div> 516 <div class="d-flex flex-column gap-3 justify-content-between"> 517 <p class="m-0">@makeStringReadable(speakerBio)</p> 518 </div> 519 <div class="mt-4 d-grid"> 520 <p class="m-0 fw-bold">@sessionsTranslate</p> 521 @foreach (var _item in speakerSessions) 522 { 523 var sessionLink = currentPageUrl.IndexOf("?") > 0 ? currentPageUrl + "&session=" + _item.id : currentPageUrl + "?session=" + _item.id; 524 <a class="btn btn-link text-start p-0 mb-2" title="@_item.title" href="@sessionLink"> 525 @_item.title 526 </a> 527 } 528 </div> 529 @if (speakers[i].links.Any()) 530 { 531 <div class="d-flex gap-3 align-items-end"> 532 @foreach (var link in speakers[i].links) 533 { 534 string icon = "/Files/Templates/Designs/Swift/Assets/icons/"; 535 536 if (link.url.Contains("twitter")) 537 { 538 icon += "twitter.svg"; 539 } 540 else if (link.url.Contains("linkedin")) 541 { 542 icon += "linkedin.svg"; 543 } 544 else 545 { 546 icon += "link.svg"; 547 } 548 549 <a href="@link.url" title="@link.title" target="_blank"> 550 @RenderIcon(icon) 551 </a> 552 } 553 </div> 554 } 555 </div> 556 </div> 557 <div class="h-100 ratio g-col-lg-5 g-col-12" style="--bs-aspect-ratio: 100%"> 558 <img src="@speakerProfilePicture" class="rounded-7"></img> 559 </div> 560 </div> 561 </div> 562 </div> 563 </div> 564 </div> 565 566 } 567 </div> 568 </div> 569 } 570 </div> 571 } 572 else 573 { 574 if(sessionizeDays.Count() > 1) 575 { 576 <nav class="tabs rounded-7"> 577 <ul class="nav nav-tabs days-tabs bg-light-grey rounded-7-top" role="tablist"> 578 @foreach (var day in sessionizeDays) 579 { 580 string dayDate = day?.ToString(dateTimeFormat); // take into consideration US & EU time 581 string dayName = dayNames[tabHeadingCount - 1]; 582 bool showCounterWithTabName = dayName == "Training" ? true : false; 583 string tabAnchorRaw = showCounterWithTabName ? dayName + tabHeadingCount : dayName; 584 string tabAnchor = "tabname" + tabAnchorRaw + tabHeadingCount; 585 bool hasDayParam = !string.IsNullOrEmpty(dayQueryParameter); 586 string activeClass = hasDayParam && dayQueryParameter == dayDate ? 587 "active" : tabHeadingCount == 1 && !hasDayParam ? "active" : ""; 588 589 string firstTabCss = tabHeadingCount == 1 ? "rounded-7-top-left" : ""; 590 591 <li class="flex-mobile-auto @(firstTabCss)" role="presentation"> 592 <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"> 593 <span class="fw-bold">@dayName</span> <span>@dayDate</span> 594 </a> 595 </li> 596 tabHeadingCount += 1; 597 } 598 </ul> 599 </nav> 600 } 601 <div class="tab-content days-tabs-container p-lg-4 p-2 rounded-7-bottom"> 602 @foreach (var day in sessionizeDays) 603 { 604 string dayClass = tabContentCount == 1 ? "active" : ""; 605 string timeslotsDay = day?.ToString(dateTimeFormat); // take into consideration US & EU time 606 string dayName = dayNames[tabContentCount - 1]; 607 bool showCounterWithTabName = dayName == "Training" ? true : false; 608 string tabAnchorRaw = showCounterWithTabName ? dayName + tabContentCount : dayName; 609 string tabAnchor = "tabname" + tabAnchorRaw + tabContentCount; 610 string dayAnchor = showCounterWithTabName ? dayName + tabContentCount : dayName; 611 string dayID = "day" + tabContentCount; 612 613 var daySessionsList = sessionizeSessions.Where(x => x.startsAt?.Date == day); 614 var dayAttributes = new List<int>(); 615 foreach(var daySession in daySessionsList) 616 { 617 dayAttributes.AddRange(daySession.categoryItems); 618 } 619 620 var dayAttributesFiltered = sessionizeAttributes.Where(item => dayAttributes.Any(filter => filter.Equals(item.attributeId))); 621 //remove attribute categories based on filtersList 622 dayAttributesFiltered = dayAttributesFiltered.Where(item => filtersList.Contains(item.categoryTitle.ToUpper())).ToList(); 623 var dayAttributesCategories = dayAttributesFiltered.Select(item => item.categoryTitle).Distinct(); 624 625 bool hasDayParam = !string.IsNullOrEmpty(dayQueryParameter); 626 bool matchingDate = hasDayParam && (dayQueryParameter == timeslotsDay); 627 var activeTabClass = matchingDate ? 628 "active" : tabContentCount == 1 && !hasDayParam ? "active" : ""; 629 630 var activeTabContainerClass = matchingDate ? 631 "show" : tabContentCount == 1 && !hasDayParam ? "show" : ""; 632 633 <div id="@tabAnchor" role="tabpanel" class="tab-pane @activeTabClass"> 634 635 @*<!--Filters--> 636 @if (dayAttributesFiltered.Any()) 637 { 638 <div class="grid"> 639 <div class="g-col-lg-4 g-col-12"> 640 <label class="fw-bold" for="filter-sessions-1">@Translate("Filter Sessions")</label> 641 <select id="filter-sessions-@tabContentCount" name="filter-sessions" class="form-control select2-multiple filter-session-ref filter-session-trigger" multiple="multiple" data-date="@timeslotsDay"> 642 @foreach (var category in dayAttributesFiltered) 643 { 644 //Take into consideration the dayParam 645 var categories = !string.IsNullOrEmpty(categoriesQueryParameter) ? categoriesQueryParameter?.Split(',')?.Select(Int32.Parse)?.ToList() : null; 646 var selected = !string.IsNullOrEmpty(categoriesQueryParameter) && categories.Contains(category.attributeId) ? "selected" : ""; 647 <option @selected value='@category.attributeId'>@category.attributeName @category.categoryTitle</option> 648 } 649 </select> 650 @if (!string.IsNullOrEmpty(categoriesQueryParameter)) 651 { 652 var clearFiltersUrl = Dynamicweb.Context.Current.Request.RawUrl.Split('?')[0] + "?" + "day=" + timeslotsDay; 653 //include RequestType & LayoutTemplate params if present (embedded page scenario) 654 string requestTypeParam = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["RequestType"]) ? 655 "&RequestType=" + Dynamicweb.Context.Current.Request.QueryString["RequestType"] : ""; 656 string layoutTemplate = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"]) ? 657 "&LayoutTemplate=" + Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"] : ""; 658 659 clearFiltersUrl = clearFiltersUrl + requestTypeParam; 660 clearFiltersUrl = clearFiltersUrl + layoutTemplate; 661 <a class="btn btn-link" href="@clearFiltersUrl" title="Clear filters"> 662 @Translate("Clear filters") 663 </a> 664 } 665 </div> 666 </div> 667 } 668 669 <div class="hidden"> 670 <div class="filterData"> 671 @foreach (var filter in filtersList) 672 { 673 string filterName = filter; 674 } 675 </div> 676 </div> 677 678 <!--FiltersEnd-->*@ 679 680 <!--Filters--> 681 @if (dayAttributesFiltered.Any()) 682 { 683 <div class="grid"> 684 @foreach(var filterCategory in dayAttributesCategories) 685 { 686 string filterCategoryName = filterCategory; 687 string filterLabel = Translate("Filter " + filterCategoryName); 688 filterCategoryName = filterCategoryName.Replace(" ","-").ToLower(); 689 var dayAttributesList = dayAttributesFiltered.OrderBy(item=>item.attributeName).Where(item=>item.categoryTitle == filterCategory).ToList(); 690 691 <div class="g-col-lg-4 g-col-12"> 692 <label class="fw-bold" for="filter-@filterCategoryName-@tabContentCount">@filterLabel</label> 693 <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"> 694 @foreach (var category in dayAttributesList) 695 { 696 @* Take into consideration the dayParam *@ 697 var categories = !string.IsNullOrEmpty(categoriesQueryParameter) ? categoriesQueryParameter?.Split(',')?.Select(Int32.Parse)?.ToList() : null; 698 var selected = !string.IsNullOrEmpty(categoriesQueryParameter) && categories.Contains(category.attributeId) ? "selected" : ""; 699 <option @selected value='@category.attributeId'>@category.attributeName @category.categoryTitle</option> 700 } 701 </select> 702 @if (!string.IsNullOrEmpty(categoriesQueryParameter)) 703 { 704 var clearFiltersUrl = Dynamicweb.Context.Current.Request.RawUrl.Split('?')[0] + "?" + "day=" + timeslotsDay; 705 @* include RequestType & LayoutTemplate params if present (embedded page scenario) *@ 706 string requestTypeParam = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["RequestType"]) ? 707 "&RequestType=" + Dynamicweb.Context.Current.Request.QueryString["RequestType"] : ""; 708 string layoutTemplate = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"]) ? 709 "&LayoutTemplate=" + Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"] : ""; 710 711 clearFiltersUrl = clearFiltersUrl + requestTypeParam; 712 clearFiltersUrl = clearFiltersUrl + layoutTemplate; 713 <a class="btn btn-link clear-filters" href="@clearFiltersUrl" title="Clear filters" data-target-dropdown="filter-@filterCategoryName-@tabContentCount"> 714 @Translate("Clear filters") 715 </a> 716 } 717 </div> 718 719 } 720 </div> 721 } 722 723 <div class="hidden"> 724 <div class="filterData"> 725 @foreach (var filter in filtersList) 726 { 727 string filterName = filter; 728 } 729 </div> 730 </div> 731 732 <!--FiltersEnd--> 733 734 <!-- Search field --> 735 <div class="mt-3"> 736 <label class="fw-bold" for="schedule-search-@tabContentCount">@Translate("Search sessions")</label> 737 <input type="text" id="schedule-search-@tabContentCount" class="form-control schedule-search-input bg-white text-dark border" placeholder="@Translate("Search by title, speaker, tags...")" autocomplete="off" style="color: #212529 !important; --swift-foreground-color: #6c757d;" /> 738 </div> 739 740 741 <!-- Second level content start --> 742 <div class="tab-content2 tab-content-schedule"> 743 <!-- All sessions start --> 744 <div id='@(dayID + "_allsessions")' class="tab-pane fade in @activeTabContainerClass"> 745 <div class="panel-group mt-4" id="@(dayID + "_allsessions_timeline")"> 746 <p class="not-found mb-0 d-none">@Translate("No items found.")</p> 747 @foreach (var timeslot in sessionTimeslots.Where(x => x.SlotDate == day).OrderBy(x => x.DateAndStart)) 748 { 749 string timeslotCode = timeslot.Code; 750 var start = timeslot.SlotStart; // take into consideration US & EU time 751 var end = timeslot.SlotEnd; // take into consideration US & EU time 752 var currentSessions = sessions.Where(x => GetTimeslot(x) == timeslotCode && x.startsAt?.Date == day); // filtered sessions list by day & categories [based on sessionizeSessions] 753 754 if (currentSessions.Any()) 755 { 756 string breakSessionTheme = currentSessions.Any(session => session.isServiceSession) ? "theme ice-blue" : ""; 757 string isBreakSession = currentSessions.Any(session => session.isServiceSession) ? "is-break" : ""; 758 <!-- Lecture start --> 759 <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=""> 760 <div class="g-col-12 time-wrapper position-absolute py-3 px-4 ms-4 rounded-7-bottom"> 761 <strong class="time" data-debug="">@start - @end</strong> 762 </div> 763 @foreach (var session in currentSessions) 764 { 765 766 var sessionRoom = sessionizeRooms.Where(x => x.id == session.roomId).FirstOrDefault(); 767 string sessionRoomName = sessionRoom != null ? makeStringReadable(sessionRoom.name) : "N/A"; 768 var isBreak = session.isServiceSession ? "full" : ""; 769 var test = session.isServiceSession ? "tested" : ""; // Check data structure here 770 var publishingStatus = "FULL"; 771 var approvalStatus = "approved"; 772 var categoriesList = session.categoryItems; 773 var _categories = String.Join(",", categoriesList.ToArray()); 774 string categories = ""; 775 776 var attributes = sessionizeAttributes.Where(item => categoriesList.Any(category => category == item.attributeId)); 777 // Build session detail link using the session's own day so the link remains 778 // correct even when the page was loaded for a different day (e.g. after 779 // applying a filter on Day 1 and then switching to Day 2 client-side). 780 var sessionDayForLink = session.startsAt?.ToString(dateTimeFormat); 781 string sessionDetailBase = sessionDetailLink.Split('?')[0]; 782 string categoriesParamForLink = !string.IsNullOrEmpty(categoriesQueryParameter) ? "&categories=" + categoriesQueryParameter : ""; 783 string requestTypeForLink = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["RequestType"]) ? 784 "&RequestType=" + Dynamicweb.Context.Current.Request.QueryString["RequestType"] : ""; 785 string layoutTemplateForLink = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"]) ? 786 "&LayoutTemplate=" + Dynamicweb.Context.Current.Request.QueryString["LayoutTemplate"] : ""; 787 string sessionDetailLinkAdjusted = sessionDetailBase + "?day=" + sessionDayForLink + categoriesParamForLink + requestTypeForLink + layoutTemplateForLink + "&session=" + session.id; 788 string sessionTitle = makeStringReadable(!string.IsNullOrEmpty(session.title) ? session.title : "TBD"); 789 string columnCss = currentSessions.Count() == 1 ? "g-col-12" : "g-col-lg-6 g-col-12"; 790 var sessionSpeakerNames = sessionizeSpeakers.Where(sp => session.speakers.Contains(sp.id)).Select(sp => sp.fullName); 791 var sessionTagNames = attributes.Select(a => a.attributeName); 792 string searchData = string.Join(" ", new [] { sessionTitle, string.Join(" ", sessionSpeakerNames), string.Join(" ", sessionTagNames), sessionRoomName, session.description ?? "" }).ToLower(); 793 <div class="d-grid gap-3 @columnCss title border-bottom my-3 pb-3 @test" data-search="@searchData"> 794 <div class="session-name" data-publishing="@publishingStatus" data-categories="@_categories" data-approval="@approvalStatus" data-code="@session.id"> 795 @if (publishingStatus == "FULL" && isBreak == "") 796 { 797 <a class="text-decoration-none fw-bold" href='@sessionDetailLinkAdjusted' data-id="@session.id"><h5 class="session-header">@sessionTitle</h5></a> 798 } 799 else 800 { 801 <h5 class="session-header fw-bold">@sessionTitle</h5> 802 } 803 </div> 804 805 <div class="mb-2"> 806 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/map-pin.svg") 807 <span class="speaker-name">@sessionRoomName</span> 808 </div> 809 810 @if (attributes.Any()) 811 { 812 <div> 813 <p class="fw-bold mb-2 tags-text">@Translate("Tags")</p> 814 <div class="d-flex flex-wrap gap-2"> 815 @foreach (var attr in attributes) 816 { 817 <a class="session-tags text-decoration-underline fs-7" href="#" data-target="filter-sessions-@tabContentCount" id="@attr.attributeId">@attr.attributeName</a> 818 } 819 </div> 820 </div> 821 } 822 823 </div> 824 } 825 </div> 826 <!--Lecture end--> 827 } 828 } 829 </div> 830 </div> 831 </div> 832 </div> 833 834 tabContentCount += 1; 835 } 836 </div> 837 } 838 } 839 840 </div> 841 842 843 @helper RenderIcon(string icon) 844 { 845 if (System.IO.Path.GetExtension(icon).ToLower() == ".svg") 846 { 847 if (!icon.ToLower().Contains("none") && icon != string.Empty) 848 { 849 <span class="icon-auto"> 850 @ReadFile(icon) 851 </span> 852 } 853 } 854 } 855 <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" /> 856 <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> 857 <script> 858 document.addEventListener('DOMContentLoaded', function () { 859 // Schedule search functionality 860 document.querySelectorAll('.schedule-search-input').forEach(function(input) { 861 input.addEventListener('input', function() { 862 var query = this.value.toLowerCase().trim(); 863 var tabPane = this.closest('.tab-pane'); 864 var scheduleItems = tabPane ? tabPane.querySelectorAll('.schedule-item') : []; 865 var notFound = tabPane ? tabPane.querySelector('.not-found') : null; 866 var visibleCount = 0; 867 868 scheduleItems.forEach(function(item) { 869 var sessionCards = item.querySelectorAll('.title[data-search]'); 870 var hasMatch = false; 871 872 if (query === '') { 873 sessionCards.forEach(function(card) { card.classList.remove('d-none'); }); 874 item.classList.remove('d-none'); 875 visibleCount++; 876 } else { 877 sessionCards.forEach(function(card) { 878 var searchData = card.getAttribute('data-search') || ''; 879 if (searchData.indexOf(query) !== -1) { 880 card.classList.remove('d-none'); 881 hasMatch = true; 882 } else { 883 card.classList.add('d-none'); 884 } 885 }); 886 887 if (hasMatch) { 888 item.classList.remove('d-none'); 889 visibleCount++; 890 } else { 891 item.classList.add('d-none'); 892 } 893 } 894 }); 895 896 if (notFound) { 897 if (visibleCount === 0 && query !== '') { 898 notFound.classList.remove('d-none'); 899 } else { 900 notFound.classList.add('d-none'); 901 } 902 } 903 }); 904 }); 905 }); 906 </script>