diff --git a/pinaxcon/settings.py b/pinaxcon/settings.py index 33a3344b8911cc692e2f8414d1ce53529ba1503b..a0de22313dc5388a094c9a089caa2a138d2ac10a 100644 --- a/pinaxcon/settings.py +++ b/pinaxcon/settings.py @@ -453,6 +453,8 @@ GAPC_STORAGE = { SETTINGS_EXPORT = [ 'DEBUG', 'ANALYTICS_KEY', + 'TIME_ZONE', + 'LCA_START', ] if DEV_MODE and DEV_MODE == "LAPTOP": @@ -514,11 +516,11 @@ class PenguinDinnerCat(Category): return t -_TZINFO = pytz.timezone(TIME_ZONE) -LCA_START = datetime(2021, 1, 23, tzinfo=_TZINFO) -LCA_END = datetime(2021, 1, 25, tzinfo=_TZINFO) -LCA_MINICONF_END = datetime(2021, 1, 23, 23, 59, tzinfo=_TZINFO) -EARLY_BIRD_DEADLINE = datetime(2020, 12, 1, tzinfo=_TZINFO) +LCA_TZINFO = pytz.timezone(TIME_ZONE) +LCA_START = LCA_TZINFO.localize(datetime(2021, 1, 23)) +LCA_END = LCA_TZINFO.localize(datetime(2021, 1, 25)) +LCA_MINICONF_END = LCA_TZINFO.localize(datetime(2021, 1, 23, 23, 59)) +EARLY_BIRD_DEADLINE = LCA_TZINFO.localize(datetime(2020, 12, 1)) PENGUIN_DINNER_TICKET_DATE = date(2021, 1, 23) SPEAKER_DINNER_TICKET_DATE = date(2021, 1, 25) PDNS_TICKET_DATE = date(2021, 1, 24) diff --git a/pinaxcon/templates/site_base.html b/pinaxcon/templates/site_base.html index d39ab252bd437aba0c5b2802d9e6058b67d8ec27..803bd81b5f29f798e0b2e32de83470723d19e841 100644 --- a/pinaxcon/templates/site_base.html +++ b/pinaxcon/templates/site_base.html @@ -40,6 +40,10 @@ {% block extra_head_base %} {% block extra_head %}{% endblock %} {% endblock %} + + {% block template_overrides %}{% endblock %} @@ -85,7 +89,7 @@ {% endblock %} {% block scripts %} - + diff --git a/pinaxcon/templates/symposion/schedule/_grid.html b/pinaxcon/templates/symposion/schedule/_grid.html index c3d80deebd9b966aca73fe557b92cd6af7c56363..d3c109163592b6ee417bb6aa83534546d86c63e2 100644 --- a/pinaxcon/templates/symposion/schedule/_grid.html +++ b/pinaxcon/templates/symposion/schedule/_grid.html @@ -1,7 +1,7 @@ {% load lca2018_tags %} {% load waffle_tags %} - +
@@ -26,9 +26,9 @@ {% for row in timetable %} - + {% for slot in row.slots %} -
Room

{{ row.time|date:"h:iA" }}

{{ row.time|date:"h:iA" }}

+ {% with slot.kind.label.lower as label %} {% if label == "talk" or label == "tutorial" %} {% if slot.content.unpublish and not request.user.is_staff %} diff --git a/pinaxcon/templates/symposion/schedule/_schedule_nav_link.html b/pinaxcon/templates/symposion/schedule/_schedule_nav_link.html index 8f900305f97bb1323adc3ca8bce34a2e349132bc..5c1931503c4a672423a1d3303050c39be490be31 100644 --- a/pinaxcon/templates/symposion/schedule/_schedule_nav_link.html +++ b/pinaxcon/templates/symposion/schedule/_schedule_nav_link.html @@ -1,10 +1,11 @@ + aria-selected="{% if active %}true{% else %}false{% endif %}" + data-date="{{ date }}"> {{ label }} diff --git a/pinaxcon/templates/symposion/schedule/base.html b/pinaxcon/templates/symposion/schedule/base.html new file mode 100644 index 0000000000000000000000000000000000000000..afae9acf1f370f9032ef6c498d8cb801497133cf --- /dev/null +++ b/pinaxcon/templates/symposion/schedule/base.html @@ -0,0 +1,8 @@ +{% extends "site_base.html" %} + +{% load static %} + +{% block extra_script %} + + +{% endblock %} diff --git a/pinaxcon/templates/symposion/schedule/presentation_detail.html b/pinaxcon/templates/symposion/schedule/presentation_detail.html index faccffb07534d99c9fbf50aa727de6cdababd644..f8bd2d4fcd5a9812658e7e934e6efb719bd9cc89 100644 --- a/pinaxcon/templates/symposion/schedule/presentation_detail.html +++ b/pinaxcon/templates/symposion/schedule/presentation_detail.html @@ -1,4 +1,4 @@ -{% extends "site_base.html" %} +{% extends "symposion/schedule/base.html" %} {% load lca2018_tags %} {% load lca2019_tags %} @@ -10,7 +10,7 @@ {% block page_title %}{{ presentation.title }}{% endblock %} {% block page_lead %} {% if presentation.slot %} -{{ presentation.slot.rooms.0 }} | {{ presentation.slot.day.date|date:"D d M" }} | {{ presentation.slot.start }}–{{ presentation.slot.end }} +{{ presentation.slot.rooms.0 }} | {{ presentation.slot.day.date|date:"D d M" }} {{ presentation.slot.start }}–{{ presentation.slot.end }} {% else %} Not currently scheduled. {% endif %} diff --git a/pinaxcon/templates/symposion/schedule/schedule_conference.html b/pinaxcon/templates/symposion/schedule/schedule_conference.html index 1e0fda4181924ccf94819e9927a4c6810df5e52e..09413074f63e86b6a7e4d38640e1459fd7db8097 100644 --- a/pinaxcon/templates/symposion/schedule/schedule_conference.html +++ b/pinaxcon/templates/symposion/schedule/schedule_conference.html @@ -1,4 +1,4 @@ -{% extends "site_base.html" %} +{% extends "symposion/schedule/base.html" %} {% load i18n %} {% load cache %} @@ -27,7 +27,7 @@ {% for section in sections %} {% for timetable in section.days %} {% endfor %} {% endfor %} @@ -46,6 +46,7 @@ {{ timetable.day.date|date:"l" }}, {{ timetable.day.date }} +

Conference times are in {{ settings.LCA_START|date:'T' }} (UTC{{ settings.LCA_START|date:'O' }}).

{% include "symposion/schedule/_grid.html" %}
@@ -62,34 +63,6 @@ {% block scripts_extra %} -{% endblock %} diff --git a/pinaxcon/templates/symposion/schedule/schedule_list.html b/pinaxcon/templates/symposion/schedule/schedule_list.html index 0faa6ae2c2eee3d9834c8b18b4ba3798fec9853f..b02a949d7d2580da2729d6d3afff62e2e932f25d 100644 --- a/pinaxcon/templates/symposion/schedule/schedule_list.html +++ b/pinaxcon/templates/symposion/schedule/schedule_list.html @@ -1,4 +1,4 @@ -{% extends "site_base.html" %} +{% extends "symposion/schedule/base.html" %} {% load i18n %} {% load cache %} diff --git a/pinaxcon/templates/symposion/schedule/session_detail.html b/pinaxcon/templates/symposion/schedule/session_detail.html index 070d130038396e392b9f05ff41f032bc6a2cfd73..17917b27ac95e569d4bf7730d030c48d3a351a6c 100644 --- a/pinaxcon/templates/symposion/schedule/session_detail.html +++ b/pinaxcon/templates/symposion/schedule/session_detail.html @@ -1,4 +1,4 @@ -{% extends "site_base.html" %} +{% extends "symposion/schedule/base.html" %} {% load lca2018_tags %} {% load sitetree %} diff --git a/pinaxcon/templates/symposion/schedule/session_list.html b/pinaxcon/templates/symposion/schedule/session_list.html index 9cb78c2c4a1586c7fe53558c307ccbcba15bbfc6..8d59d0068e423eae709b22645c5caadbf637808e 100644 --- a/pinaxcon/templates/symposion/schedule/session_list.html +++ b/pinaxcon/templates/symposion/schedule/session_list.html @@ -1,4 +1,4 @@ -{% extends "site_base.html" %} +{% extends "symposion/schedule/base.html" %} {% load lca2018_tags %} {% load sitetree %} diff --git a/static/src/js/luxon.min.js b/static/src/js/luxon.min.js new file mode 100644 index 0000000000000000000000000000000000000000..59df19e5abd17ba970990df538b99d4d0bf42257 --- /dev/null +++ b/static/src/js/luxon.min.js @@ -0,0 +1 @@ +var luxon=function(e){"use strict";function r(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[t++]}};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var n=function(e){function t(){return e.apply(this,arguments)||this}return a(t,e),t}(t(Error)),l=function(t){function e(e){return t.call(this,"Invalid DateTime: "+e.toMessage())||this}return a(e,t),e}(n),d=function(t){function e(e){return t.call(this,"Invalid Interval: "+e.toMessage())||this}return a(e,t),e}(n),f=function(t){function e(e){return t.call(this,"Invalid Duration: "+e.toMessage())||this}return a(e,t),e}(n),L=function(e){function t(){return e.apply(this,arguments)||this}return a(t,e),t}(n),h=function(t){function e(e){return t.call(this,"Invalid unit "+e)||this}return a(e,t),e}(n),m=function(e){function t(){return e.apply(this,arguments)||this}return a(t,e),t}(n),y=function(e){function t(){return e.call(this,"Zone is an abstract class")||this}return a(t,e),t}(n),v="numeric",g="short",p="long",w={year:v,month:v,day:v},k={year:v,month:g,day:v},b={year:v,month:g,day:v,weekday:g},O={year:v,month:p,day:v},S={year:v,month:p,day:v,weekday:p},T={hour:v,minute:v},M={hour:v,minute:v,second:v},N={hour:v,minute:v,second:v,timeZoneName:g},D={hour:v,minute:v,second:v,timeZoneName:p},E={hour:v,minute:v,hour12:!1},x={hour:v,minute:v,second:v,hour12:!1},C={hour:v,minute:v,second:v,hour12:!1,timeZoneName:g},F={hour:v,minute:v,second:v,hour12:!1,timeZoneName:p},Z={year:v,month:v,day:v,hour:v,minute:v},j={year:v,month:v,day:v,hour:v,minute:v,second:v},A={year:v,month:g,day:v,hour:v,minute:v},z={year:v,month:g,day:v,hour:v,minute:v,second:v},_={year:v,month:g,day:v,weekday:g,hour:v,minute:v},q={year:v,month:p,day:v,hour:v,minute:v,timeZoneName:g},H={year:v,month:p,day:v,hour:v,minute:v,second:v,timeZoneName:g},U={year:v,month:p,day:v,weekday:p,hour:v,minute:v,timeZoneName:p},R={year:v,month:p,day:v,weekday:p,hour:v,minute:v,second:v,timeZoneName:p};function W(e){return void 0===e}function P(e){return"number"==typeof e}function J(e){return"number"==typeof e&&e%1==0}function I(){try{return"undefined"!=typeof Intl&&Intl.DateTimeFormat}catch(e){return!1}}function Y(){return!W(Intl.DateTimeFormat.prototype.formatToParts)}function G(){try{return"undefined"!=typeof Intl&&!!Intl.RelativeTimeFormat}catch(e){return!1}}function $(e,r,i){if(0!==e.length)return e.reduce(function(e,t){var n=[r(t),t];return e&&i(e[0],n[0])===e[0]?e:n},null)[1]}function B(n,e){return e.reduce(function(e,t){return e[t]=n[t],e},{})}function Q(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function K(e,t,n){return J(e)&&t<=e&&e<=n}function X(e,t){return void 0===t&&(t=2),e.toString().lengthtn.indexOf(c)&&an(this.matrix,u,h,a,c)}else P(u[c])&&(o[c]=u[c])}for(var m in o)0!==o[m]&&(a[r]+=m===r?o[m]:o[m]/this.matrix[r][m]);return rn(this,{values:a},!0).normalize()},e.negate=function(){if(!this.isValid)return this;for(var e={},t=0,n=Object.keys(this.values);te},e.isBefore=function(e){return!!this.isValid&&this.e<=e},e.contains=function(e){return!!this.isValid&&(this.s<=e&&this.e>e)},e.set=function(e){var t=void 0===e?{}:e,n=t.start,r=t.end;return this.isValid?f.fromDateTimes(n||this.s,r||this.e):this},e.splitAt=function(){var t=this;if(!this.isValid)return[];for(var e=arguments.length,n=new Array(e),r=0;r+this.e?this.e:s;a.push(f.fromDateTimes(o,c)),o=c,u+=1}return a},e.splitBy=function(e){var t=un(e);if(!this.isValid||!t.isValid||0===t.as("milliseconds"))return[];for(var n,r,i=this.s,a=[];i+this.e?this.e:n,a.push(f.fromDateTimes(i,r)),i=r;return a},e.divideEqually=function(e){return this.isValid?this.splitBy(this.length()/e).slice(0,e):[]},e.overlaps=function(e){return this.e>e.s&&this.s=e.e)},e.equals=function(e){return!(!this.isValid||!e.isValid)&&(this.s.equals(e.s)&&this.e.equals(e.e))},e.intersection=function(e){if(!this.isValid)return this;var t=this.s>e.s?this.s:e.s,n=this.ee.e?this.e:e.e;return f.fromDateTimes(t,n)},f.merge=function(e){var t=e.sort(function(e,t){return e.s-t.s}).reduce(function(e,t){var n=e[0],r=e[1];return r?r.overlaps(t)||r.abutsStart(t)?[n,r.union(t)]:[n.concat([r]),t]:[n,t]},[[],null]),n=t[0],r=t[1];return r&&n.push(r),n},f.xor=function(e){for(var t,n,r=null,i=0,a=[],o=e.map(function(e){return[{time:e.s,type:"s"},{time:e.e,type:"e"}]}),u=V((t=Array.prototype).concat.apply(t,o).sort(function(e,t){return e.time-t.time}));!(n=u()).done;){var s=n.value;r=1===(i+="s"===s.type?1:-1)?s.time:(r&&+r!=+s.time&&a.push(f.fromDateTimes(r,s.time)),null)}return f.merge(a)},e.difference=function(){for(var t=this,e=arguments.length,n=new Array(e),r=0;rue(n)?(t=n+1,u=1):t=n,Object.assign({weekYear:t,weekNumber:u,weekday:o},me(e))}function zn(e){var t,n=e.weekYear,r=e.weekNumber,i=e.weekday,a=Fn(n,1,4),o=ie(n),u=7*r+i-a-3;u<1?u+=ie(t=n-1):othis.valueOf(),u=dn(o?this:e,o?e:this,a,i);return o?u.negate():u},e.diffNow=function(e,t){return void 0===e&&(e="milliseconds"),void 0===t&&(t={}),this.diff(I.local(),e,t)},e.until=function(e){return this.isValid?cn.fromDateTimes(this,e):this},e.hasSame=function(e,t){if(!this.isValid)return!1;if("millisecond"===t)return this.valueOf()===e.valueOf();var n=e.valueOf();return this.startOf(t)<=n&&n<=this.endOf(t)},e.equals=function(e){return this.isValid&&e.isValid&&this.valueOf()===e.valueOf()&&this.zone.equals(e.zone)&&this.loc.equals(e.loc)},e.toRelative=function(e){if(void 0===e&&(e={}),!this.isValid)return null;var t=e.base||I.fromObject({zone:this.zone}),n=e.padding?thisthis.set({month:1}).offset||this.offset>this.set({month:5}).offset)}},{key:"isInLeapYear",get:function(){return re(this.year)}},{key:"daysInMonth",get:function(){return ae(this.year,this.month)}},{key:"daysInYear",get:function(){return this.isValid?ie(this.year):NaN}},{key:"weeksInWeekYear",get:function(){return this.isValid?ue(this.weekYear):NaN}}],[{key:"DATE_SHORT",get:function(){return w}},{key:"DATE_MED",get:function(){return k}},{key:"DATE_MED_WITH_WEEKDAY",get:function(){return b}},{key:"DATE_FULL",get:function(){return O}},{key:"DATE_HUGE",get:function(){return S}},{key:"TIME_SIMPLE",get:function(){return T}},{key:"TIME_WITH_SECONDS",get:function(){return M}},{key:"TIME_WITH_SHORT_OFFSET",get:function(){return N}},{key:"TIME_WITH_LONG_OFFSET",get:function(){return D}},{key:"TIME_24_SIMPLE",get:function(){return E}},{key:"TIME_24_WITH_SECONDS",get:function(){return x}},{key:"TIME_24_WITH_SHORT_OFFSET",get:function(){return C}},{key:"TIME_24_WITH_LONG_OFFSET",get:function(){return F}},{key:"DATETIME_SHORT",get:function(){return Z}},{key:"DATETIME_SHORT_WITH_SECONDS",get:function(){return j}},{key:"DATETIME_MED",get:function(){return A}},{key:"DATETIME_MED_WITH_SECONDS",get:function(){return z}},{key:"DATETIME_MED_WITH_WEEKDAY",get:function(){return _}},{key:"DATETIME_FULL",get:function(){return q}},{key:"DATETIME_FULL_WITH_SECONDS",get:function(){return H}},{key:"DATETIME_HUGE",get:function(){return U}},{key:"DATETIME_HUGE_WITH_SECONDS",get:function(){return R}}]),I}();function lr(e){if(cr.isDateTime(e))return e;if(e&&e.valueOf&&P(e.valueOf()))return cr.fromJSDate(e);if(e&&"object"==typeof e)return cr.fromObject(e);throw new m("Unknown datetime argument: "+e+", of type "+typeof e)}return e.DateTime=cr,e.Duration=on,e.FixedOffsetZone=Re,e.IANAZone=He,e.Info=ln,e.Interval=cn,e.InvalidZone=We,e.LocalZone=je,e.Settings=Ke,e.Zone=Fe,e}({}); \ No newline at end of file diff --git a/static/src/js/schedule.js b/static/src/js/schedule.js new file mode 100644 index 0000000000000000000000000000000000000000..7eddd54bf6b37181a1c851f6c29d372b4db98150 --- /dev/null +++ b/static/src/js/schedule.js @@ -0,0 +1,129 @@ +$(function() { + /* Schedule display localisation */ + var showCurrentTab = function() { + var fragment = window.location.hash.toLowerCase().substring(1); + + var dayTabs = $('#schedule-tabs .schedule-day'); + if (dayTabs.length === 0) return; + + if (fragment) { + var fragmentId = "#schedule_day_" + fragment + "-tab"; + $(fragmentId).tab('show'); + } else { + // Show tab based on current time. + var now = luxon.DateTime.local(); + for (var i = 0; i < dayTabs.length; ++i) { + var dayTab = $(dayTabs[i]); + var tabDate = dayTab.data('date'); + + var scheduleDate = luxon.DateTime.fromISO(tabDate, { zone: CONF_TZ }); + var startOfDay = scheduleDate.startOf('day'); + var endOfDay = scheduleDate.endOf('day'); + if (now >= startOfDay && now < endOfDay) { + tabShown = true; + dayTab.tab('show'); + break; + } + } + } + } + + var updateScheduleGrid = function() { + var rowHeaders = $('.calendar-row th.time'); + for (var i = 0; i < rowHeaders.length; ++i) { + var rowHeader = $(rowHeaders[i]); + var rowTime = rowHeader.data('time'); + var scheduleDate = luxon.DateTime.fromISO(rowTime, { zone: CONF_TZ }); + var localDate = scheduleDate.toLocal(); + + // If the schedule date is already in the user's TZ, skip it. + if (scheduleDate.offset === localDate.offset) break; + + var confFormatted = scheduleDate.toLocaleString({ + weekday: scheduleDate.weekday === localDate.weekday ? undefined : 'short', + hour: 'numeric', + minute: 'numeric', + timeZoneName: 'short', + }); + var localFormatted = localDate.toLocaleString({ + weekday: scheduleDate.weekday === localDate.weekday ? undefined : 'short', + hour: 'numeric', + minute: 'numeric', + timeZoneName: 'short', + }); + var timeText = rowHeader.find('p').text(); + rowHeader.find('p').html(confFormatted + '
(' + localFormatted + ')'); + } + } + + var updatePresentationTimes = function() { + var presentationTimes = $('span.presentation-time'); + for (var i = 0; i < presentationTimes.length; ++i) { + var presentationTime = $(presentationTimes[i]); + var startTime = presentationTime.data('starttime'); + var endTime = presentationTime.data('endtime'); + var confStartTime = luxon.DateTime.fromISO(startTime, { zone: CONF_TZ }); + var confEndTime = luxon.DateTime.fromISO(endTime, { zone: CONF_TZ }); + + var localStartTime = confStartTime.toLocal(); + var localEndTime = confEndTime.toLocal(); + + // If the conf date is already in the user's TZ, skip it. + if (confStartTime.offset === localStartTime.offset) break; + + var confStartTimeFormatted = confStartTime.toLocaleString({ + weekday: 'short', + month: 'short', + day: '2-digit', + hour: 'numeric', + minute: 'numeric', + }); + var confEndTimeFormatted = confEndTime.toLocaleString({ + hour: 'numeric', + minute: 'numeric', + timeZoneName: 'short', + }); + var localStartTimeFormatted = localStartTime.toLocaleString({ + weekday: confStartTime.weekday === localStartTime.weekday ? undefined : 'short', + month: confStartTime.weekday === localStartTime.weekday ? undefined : 'short', + day: confStartTime.weekday === localStartTime.weekday ? undefined : '2-digit', + hour: 'numeric', + minute: 'numeric', + }); + var localEndTimeFormatted = localEndTime.toLocaleString({ + weekday: localStartTime.weekday === localEndTime.weekday ? undefined : 'short', + month: localStartTime.weekday === localEndTime.weekday ? undefined : 'short', + day: localStartTime.weekday === localEndTime.weekday ? undefined : '2-digit', + hour: 'numeric', + minute: 'numeric', + timeZoneName: 'short', + }); + + presentationTime.html(confStartTimeFormatted + ' - ' + confEndTimeFormatted + ' (' + localStartTimeFormatted + ' - ' + localEndTimeFormatted + ')'); + } + } + + /* Schedule Editing */ + $("a.edit-slot").click(function(e) { + $("#slotEditModal").load($(this).data("action"), function() { + $("#slotEditModal").modal("show"); + }); + e.preventDefault(); + }); + + $("form#schedule-builder :submit").click(function(e) { + var name = this.name; + if(name == 'delete') { + if (!confirm("Are you sure you want to delete the schedule?")) + { + e.preventDefault(); + return; + } + } + }); + + /* Update schedule display */ + showCurrentTab(); + updateScheduleGrid(); + updatePresentationTimes(); +});