And more agenda fixes.

- Legacy-Id: 19682
This commit is contained in:
Lars Eggert 2021-11-18 15:18:48 +00:00
parent 5772701781
commit e195a00d1b
10 changed files with 271 additions and 570 deletions

View file

@ -1530,6 +1530,7 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
is_current_meeting = (num is None) or (num == get_current_ietf_meeting_num())
rendered_page = render(request, "meeting/"+base+ext, {
"personalize": False,
"schedule": schedule,
"filtered_assignments": filtered_assignments,
"updated": updated,
@ -1701,8 +1702,9 @@ def agenda_personalize(request, num):
return render(
request,
"meeting/agenda_personalize.html",
"meeting/agenda.html",
{
'personalize': True,
'schedule': meeting.schedule,
'updated': meeting.updated(),
'filtered_assignments': filtered_assignments,

View file

@ -3,7 +3,7 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
// closure to create private scope
(function () {
'use strict'
'use strict';
/* n.b., const refers to the opts object itself, not its contents.
* Use camelCase for easy translation into element.dataset keys,
@ -15,10 +15,10 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
};
/* Remove from list, if present */
function remove_list_item (list, item) {
function remove_list_item(list, item) {
var item_index = list.indexOf(item);
if (item_index !== -1) {
list.splice(item_index, 1)
list.splice(item_index, 1);
}
}
@ -26,32 +26,34 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
*
* Returns true if added to the list, otherwise false.
*/
function toggle_list_item (list, item) {
function toggle_list_item(list, item) {
var item_index = list.indexOf(item);
if (item_index === -1) {
list.push(item)
list.push(item);
return true;
} else {
list.splice(item_index, 1)
list.splice(item_index, 1);
return false;
}
}
function parse_query_params (qs) {
var params = {}
qs = decodeURI(qs).replace(/^\?/, '').toLowerCase()
function parse_query_params(qs) {
var params = {};
qs = decodeURI(qs)
.replace(/^\?/, '')
.toLowerCase();
if (qs) {
var param_strs = qs.split('&')
var param_strs = qs.split('&');
for (var ii = 0; ii < param_strs.length; ii++) {
var toks = param_strs[ii].split('=', 2)
params[toks[0]] = toks[1] || true
var toks = param_strs[ii].split('=', 2);
params[toks[0]] = toks[1] || true;
}
}
return params
return params;
}
/* filt = 'show' or 'hide' */
function get_filter_from_qparams (qparams, filt) {
function get_filter_from_qparams(qparams, filt) {
if (!qparams[filt] || (qparams[filt] === true)) {
return [];
}
@ -64,25 +66,28 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
return result;
}
function get_filter_params (qparams) {
function get_filter_params(qparams) {
var enabled = opts.alwaysShow || qparams.show || qparams.hide;
return {
enabled: enabled,
show: get_filter_from_qparams(qparams, 'show'),
hide: get_filter_from_qparams(qparams, 'hide')
}
};
}
function get_keywords(elt) {
var keywords = $(elt).attr('data-filter-keywords');
var keywords = $(elt)
.attr('data-filter-keywords');
if (keywords) {
return keywords.toLowerCase().split(',');
return keywords.toLowerCase()
.split(',');
}
return [];
}
function get_item(elt) {
return $(elt).attr('data-filter-item');
return $(elt)
.attr('data-filter-item');
}
// utility method - is there a match between two lists of keywords?
@ -96,23 +101,25 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
}
// Find the items corresponding to a keyword
function get_items_with_keyword (keyword) {
function get_items_with_keyword(keyword) {
var items = [];
$('.view button.pickview').filter(function(index, elt) {
$('.view button.pickview')
.filter(function (index, elt) {
return keyword_match(get_keywords(elt), [keyword]);
}).each(function (index, elt) {
})
.each(function (index, elt) {
items.push(get_item($(elt)));
});
return items;
}
function filtering_is_enabled (filter_params) {
function filtering_is_enabled(filter_params) {
return filter_params.enabled;
}
// Update the filter / customization UI to match the current filter parameters
function update_filter_ui (filter_params) {
function update_filter_ui(filter_params) {
var buttons = $('.pickview');
if (!filtering_is_enabled(filter_params)) {
@ -121,12 +128,12 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
return;
}
update_href_querystrings(filter_params_as_querystring(filter_params))
update_href_querystrings(filter_params_as_querystring(filter_params));
// show the customizer - it will stay visible even if filtering is disabled
const customizer = $('#customize');
if (customizer.hasClass('collapse')) {
customizer.collapse('show')
customizer.collapse('show');
}
// Update button state to match visibility
@ -149,11 +156,11 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
* Calling the individual update_* functions outside of this method will likely cause
* various parts of the page to get out of sync.
*/
function update_view () {
var filter_params = get_filter_params(parse_query_params(window.location.search))
update_filter_ui(filter_params)
function update_view() {
var filter_params = get_filter_params(parse_query_params(window.location.search));
update_filter_ui(filter_params);
if (opts.updateCallback) {
opts.updateCallback(filter_params)
opts.updateCallback(filter_params);
}
}
@ -162,19 +169,19 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
* Updates the URL to match filter_params, then updates the history / display to match
* (if supported) or loads the new URL.
*/
function update_filters (filter_params) {
function update_filters(filter_params) {
var new_url = replace_querystring(
window.location.href,
filter_params_as_querystring(filter_params)
)
update_href_querystrings(filter_params_as_querystring(filter_params))
);
update_href_querystrings(filter_params_as_querystring(filter_params));
if (window.history && window.history.replaceState) {
// Keep current origin, replace search string, no page reload
history.replaceState({}, document.title, new_url)
update_view()
history.replaceState({}, document.title, new_url);
update_view();
} else {
// No window.history.replaceState support, page reload required
window.location = new_url
window.location = new_url;
}
}
@ -184,33 +191,34 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
function update_href_querystrings(querystring) {
Array.from(
document.getElementsByClassName('agenda-link filterable')
).forEach(
(elt) => elt.href = replace_querystring(elt.href, querystring)
)
.forEach(
(elt) => elt.href = replace_querystring(elt.href, querystring)
);
}
function filter_params_as_querystring(filter_params) {
var qparams = []
var qparams = [];
if (filter_params.show.length > 0) {
qparams.push('show=' + filter_params.show.join())
qparams.push('show=' + filter_params.show.join());
}
if (filter_params.hide.length > 0) {
qparams.push('hide=' + filter_params.hide.join())
qparams.push('hide=' + filter_params.hide.join());
}
if (qparams.length > 0) {
return '?' + qparams.join('&')
return '?' + qparams.join('&');
}
return ''
return '';
}
function replace_querystring(url, new_querystring) {
return url.replace(/(\?.*)?(#.*)?$/, new_querystring + window.location.hash)
return url.replace(/(\?.*)?(#.*)?$/, new_querystring + window.location.hash);
}
/* Helper for pick group/type button handlers - toggles the appropriate parameter entry
* elt - the jquery element that was clicked
*/
function handle_pick_button (elt) {
function handle_pick_button(elt) {
var fp = get_filter_params(parse_query_params(window.location.search));
var item = get_item(elt);
@ -232,7 +240,7 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
* an area will enable all items in the row as one would expect. */
if (just_showed_item) {
var children = get_items_with_keyword(item);
$.each(children, function(index, child) {
$.each(children, function (index, child) {
remove_list_item(fp.show, child);
remove_list_item(fp.hide, child);
});
@ -251,33 +259,24 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
}
function register_handlers() {
$('.pickview').on("click", function () {
$('.pickview')
.on("click", function () {
console.log("pickview");
if (is_disabled($(this))) { return; }
var fp = handle_pick_button($(this));
update_filters(fp);
});
}
/**
* Read options from the template
*/
function read_template_options() {
const opts_elt = document.getElementById('agenda-filter-options');
opts.keys().forEach((opt) => {
if (opt in opts_elt.dataset) {
opts[opt] = opts_elt.dataset[opt];
}
});
}
/* Entry point to filtering code when page loads
*
* This must be called if you are using the HTML template to provide a customization
* button UI. Do not call if you only want to use the parameter parsing routines.
*/
function enable () {
function enable() {
// ready handler fires immediately if document is already "ready"
$(document).ready(function () {
$(document)
.ready(function () {
register_handlers();
update_view();
})
@ -285,7 +284,7 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
// utility method - filter a jquery set to those matching a keyword
function rows_matching_filter_keyword(rows, kw) {
return rows.filter(function(index, element) {
return rows.filter(function (index, element) {
var row_kws = get_keywords(element);
return keyword_match(row_kws, [kw.toLowerCase()]);
});
@ -305,6 +304,6 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
keyword_match: keyword_match,
parse_query_params: parse_query_params,
rows_matching_filter_keyword: rows_matching_filter_keyword,
set_update_callback: function (cb) {opts.updateCallback = cb}
set_update_callback: function (cb) { opts.updateCallback = cb }
};
})();

View file

@ -0,0 +1,41 @@
// Copyright The IETF Trust 2021, All Rights Reserved
/**
* Agenda personalization JS methods
*
* Requires agenda_timezone.js and timezone.js be included.
*/
'use strict';
/**
* Update the checkbox state to match the filter parameters
*/
function updateAgendaCheckboxes(filter_params) {
var selection_inputs = document.getElementsByName('selected-sessions');
selection_inputs.forEach((inp) => {
const item_keywords = inp.dataset.filterKeywords.toLowerCase()
.split(',');
if (
agenda_filter.keyword_match(item_keywords, filter_params.show) &&
!agenda_filter.keyword_match(item_keywords, filter_params.hide)
) {
inp.checked = true;
} else {
inp.checked = false;
}
});
}
window.handleFilterParamUpdate = function (filter_params) {
updateAgendaCheckboxes(filter_params);
};
window.handleTableClick = function (event) {
if (event.target.name === 'selected-sessions') {
// hide the tooltip after clicking on a checkbox
const jqElt = jQuery(event.target);
if (jqElt.tooltip) {
jqElt.tooltip('hide');
}
}
};

View file

@ -13,12 +13,12 @@ var local_timezone = moment.tz.guess();
// get_current_tz_cb must be overwritten using set_current_tz_cb
function get_current_tz_cb() {
throw new Error('Tried to get current timezone before callback registered. Use set_current_tz_cb().')
throw new Error('Tried to get current timezone before callback registered. Use set_current_tz_cb().');
};
// Initialize moments
window.initialize_moments = function () {
var times = $('div.time')
var times = $('div.time');
$.each(times, function (i, item) {
item.start_ts = moment.unix(this.getAttribute("data-start-time"))
.utc();
@ -33,7 +33,7 @@ window.initialize_moments = function () {
item.format = +this.getAttribute("format");
}
});
var times = $('[data-slot-start-ts]')
times = $('[data-slot-start-ts]');
$.each(times, function (i, item) {
item.slot_start_ts = moment.unix(this.getAttribute("data-slot-start-ts"))
.utc();
@ -205,7 +205,7 @@ window.update_times = function (newtz) {
});
update_tooltips_all();
update_clock();
}
};
// Highlight ongoing based on the current time
window.highlight_ongoing = function () {
@ -213,7 +213,7 @@ window.highlight_ongoing = function () {
.remove("#now");
$('.table-warning')
.removeClass("table-warning");
var agenda_rows = $('[data-slot-start-ts]')
var agenda_rows = $('[data-slot-start-ts]');
agenda_rows = agenda_rows.filter(function () {
return moment()
.isBetween(this.slot_start_ts, this.slot_end_ts);
@ -245,13 +245,13 @@ window.update_tooltips_all = function () {
$(this)
.html(format_tooltip_table(this.start_ts, this.end_ts));
});
}
};
// Update clock
window.update_clock = function () {
$('#current-time')
.html(format_time(moment(), get_current_tz_cb(), 0));
}
};
$.urlParam = function (name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)')
@ -261,7 +261,7 @@ $.urlParam = function (name) {
} else {
return results[1] || 0;
}
}
};
window.init_timers = function () {
var fast_timer = 60000 / (speedup > 600 ? 600 : speedup);
@ -271,9 +271,9 @@ window.init_timers = function () {
setInterval(function () { highlight_ongoing(); }, fast_timer);
setInterval(function () { update_tooltips(); }, fast_timer);
setInterval(function () { update_tooltips_all(); }, 3600000 / speedup);
}
};
// set method used to find current time zone
window.set_current_tz_cb = function (fn) {
get_current_tz_cb = fn;
}
};

View file

@ -17,18 +17,19 @@ window.ietf_timezone; // public interface
var current_timezone;
// Select timezone to use. Arg is name of a timezone or 'local' to guess local tz.
function use_timezone (newtz) {
function use_timezone(newtz) {
// Guess local timezone if necessary
if (newtz.toLowerCase() === 'local') {
newtz = moment.tz.guess()
newtz = moment.tz.guess();
}
if (current_timezone !== newtz) {
current_timezone = newtz
current_timezone = newtz;
// Update values of tz-select inputs but do not trigger change event
$('select.tz-select').val(newtz)
$('select.tz-select')
.val(newtz);
if (timezone_change_callback) {
timezone_change_callback(newtz)
timezone_change_callback(newtz);
}
}
}
@ -38,37 +39,40 @@ window.ietf_timezone; // public interface
* This will set the timezone to the value of 'current'. Set up the tz_change callback
* before initializing.
*/
function timezone_init (current) {
var tz_names = moment.tz.names()
var select = $('select.tz-select')
function timezone_init(current) {
var tz_names = moment.tz.names();
var select = $('select.tz-select');
select.empty()
select.empty();
$.each(tz_names, function (i, item) {
if (current === item) {
select.append($('<option/>', {
selected: 'selected', html: item, value: item
}))
selected: 'selected',
html: item,
value: item
}));
} else {
select.append($('<option/>', {
html: item, value: item
}))
html: item,
value: item
}));
}
})
select.on("change", function () { use_timezone(this.value)});
});
select.on("change", function () { use_timezone(this.value); });
/* When navigating back/forward, the browser may change the select input's
* value after the window load event. It does not fire the change event on
* the input when it does this. The pageshow event occurs after such an update,
* so trigger the change event ourselves to be sure the UI stays consistent
* with the timezone select input. */
window.addEventListener('pageshow', function(){select.trigger("change"); })
window.addEventListener('pageshow', function () { select.trigger("change"); });
use_timezone(current);
}
// Expose public interface
ietf_timezone = {
get_current_tz: function() {return current_timezone},
get_current_tz: function () { return current_timezone },
initialize: timezone_init,
set_tz_change_callback: function(cb) {timezone_change_callback=cb},
set_tz_change_callback: function (cb) { timezone_change_callback = cb; },
use: use_timezone
}
};
})();

View file

@ -4,13 +4,16 @@
{% load static %}
{% load ietf_filters %}
{% load textfilters %}
{% load htmlfilters agenda_custom_tags%}
{% load htmlfilters agenda_custom_tags %}
{% block title %}
IETF {{ schedule.meeting.number }} Meeting Agenda
{% if "-utc" in request.path %}
(UTC)
{% endif %}
{% if personalize %}
Personalization
{% endif %}
{% endblock %}
{% block morecss %}
@ -24,9 +27,10 @@
<div class="row">
<div class="col-md-10">
{% if "-utc" in request.path %}
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated selected="agenda-utc" title_extra="(UTC)" %}
{% elif personalize %}
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated selected="select-sessions" title_extra="" %}
{% else %}
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated selected="agenda" title_extra="" %}
{% endif %}
@ -34,9 +38,16 @@
{# cache this part -- it takes 3-6 seconds to generate #}
{% load cache %}
{% cache cache_time ietf_meeting_agenda_utc schedule.meeting.number request.path %}
<div class="row">
<div class="col-5">
<h2>Agenda</h2>
<h2>
{% if personalize %}
Session Selection
{% else %}
Agenda
{% endif %}
</h2>
</div>
<div class="col float-end tz-display">
<div class="input-group input-group-sm">
@ -68,18 +79,13 @@
{% endif %}
<p>
{% include "meeting/agenda_filter.html" with filter_categories=filter_categories customize_button_text="Customize the agenda view..." %}
{% include "meeting/agenda_filter.html" with filter_categories=filter_categories customize_button_text="Personalize the agenda view..." always_show=personalize%}
</p>
<p>
<div class="d-inline mb-3">Download agenda as .ics:</div>
{% include "meeting/agenda_personalize_buttonlist.html" with meeting=schedule.meeting only %}
<div class="d-inline">
<a id="ical-link" class="visually-hidden btn btn-sm btn-primary agenda-link filterable" href="{% url "ietf.meeting.views.agenda_ical" num=schedule.meeting.number %}">Customized schedule above</a>
</div>
<div class="input-group input-group-sm mb-3 d-inline">
<button class="btn btn-outline-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">By area</button><ul class="dropdown-menu">
<div class="input-group input-group-sm mb-3">
<button class="btn btn-outline-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">Download area agenda</button><ul class="dropdown-menu">
{% for fc in filter_categories %}
{% if not forloop.last %} {# skip the last group, it's the office hours/misc #}
{% for p in fc|dictsort:"label" %}
@ -87,9 +93,8 @@
{% endfor %}
{% endif %}
{% endfor %}
</ul><a class="btn btn-outline-primary" href="{% url "ietf.meeting.views.agenda_ical" num=schedule.meeting.number %}?show={{ non_area_keywords|join:',' }}">Non-area events</a>
</ul><a class="btn btn-outline-primary" href="{% url "ietf.meeting.views.agenda_ical" num=schedule.meeting.number %}?show={{ non_area_keywords|join:',' }}">Download non-area events</a>
</div>
</p>
<div id="weekview" class="visually-hidden mt-3">
<h2>
@ -103,14 +108,22 @@
<iframe class="w-100 overflow-hidden border border-dark" scrolling="no"></iframe>
</div>
<h2>Detailed Agenda</h2>
<h2 class="mt-3">
{% if personalize %}
Personalize
{% endif %}
Detailed Agenda
</h2>
{% if personalize %}
<p>Check boxes below to select individual sessions.</p>
{% endif %}
<table class="table table-sm">
<table id="agenda-table" class="table table-sm">
{% for item in filtered_assignments %}
{% ifchanged item.timeslot.time|date:"Y-m-d" %}
<tr class="table-primary">
<th colspan="5">
<th colspan="6">
{# The anchor here needs to be in a div, not in the th, in order for the anchor-target margin hack to work #}
<div class="anchor-target" id="slot-{{item.timeslot.time|slugify}}"></div>
<div class="h6 mt-2">{{ item.timeslot.time|date:"l, F j, Y" }}</div>
@ -122,7 +135,20 @@
<tr id="row-{{ item.slug }}" data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-slot-start-ts="{{item.start_timestamp}}"
data-slot-end-ts="{{item.end_timestamp}}">
<td class="text-nowrap text-right">
<td class="text-center">
{% if item.session_keyword %}
<input
type="checkbox"
class="pickview form-check-input"
title="Select session"
name="selected-sessions"
value="{{ item.session_keyword }}"
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-filter-item="{{ item.session_keyword }}">
{% endif %}
</td>
<td class="text-nowrap text-end">
{% include "meeting/timeslot_start_end.html" %}
</td>
<td colspan="3">
@ -171,7 +197,10 @@
<tr class="table-secondary session-label-row"
data-slot-start-ts="{{item.start_timestamp}}"
data-slot-end-ts="{{item.end_timestamp}}">
<th class="text-nowrap text-right">
<td class="text-center">
</td>
<th class="text-nowrap text-end">
{% include "meeting/timeslot_start_end.html" %}
</th>
<th colspan="4">
@ -188,8 +217,22 @@
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-slot-start-ts="{{item.start_timestamp}}"
data-slot-end-ts="{{item.end_timestamp}}">
<td class="text-center">
{% if item.session_keyword %}
<input
type="checkbox"
class="pickview form-check-input"
title="Select session"
name="selected-sessions"
value="{{ item.session_keyword }}"
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-filter-item="{{ item.session_keyword }}">
{% endif %}
</td>
{% if item.slot_type.slug == 'plenary' %}
<th class="text-nowrap text-right">
<th class="text-nowrap text-end">
{% include "meeting/timeslot_start_end.html" %}
</th>
<td colspan="3">
@ -270,6 +313,8 @@
{% endif %}
{% endfor %}
</table>
{% include "meeting/agenda_personalize_buttonlist.html" with meeting=schedule.meeting only %}
</div>
<div class="col-md-2 d-print-none" id="affix">
@ -341,8 +386,7 @@
}
function update_ical_links(filter_params) {
var ical_link = $("#ical-link");
ical_link.toggleClass("visually-hidden", !agenda_filter.filtering_is_enabled(filter_params));
$(".ical-link").toggleClass("visually-hidden", !agenda_filter.filtering_is_enabled(filter_params));
}
function update_weekview(filter_params) {
@ -389,6 +433,10 @@
<script src="{% static 'ietf/js/timezone.js' %}"></script>
<script src="{% static 'ietf/js/agenda_materials.js' %}"></script>
<script src="{% static 'ietf/js/agenda_timezone.js' %}"></script>
{% if personalize %}
<script src="{% static 'ietf/js/agenda_personalize.js' %}"></script>
{% endif %}
<script>
{% if settings.DEBUG and settings.DEBUG_AGENDA %}
speedup = +$.urlParam('speedup');
@ -441,7 +489,18 @@
init_timers();
// Finally, set up the agenda filter UI. This does not depend on the timezone.
{% if personalize %}
agenda_filter.set_update_callback(function (e) {
handleFilterParamUpdate(e);
update_view(e);
});
document.getElementById('agenda-table')
.addEventListener('click', handleTableClick);
{% else %}
agenda_filter.set_update_callback(update_view);
{% endif %}
agenda_filter.enable();
}
);

View file

@ -1,408 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2021, All Rights Reserved #}
{% load origin %}
{% load static %}
{% load ietf_filters %}
{% load textfilters %}
{% load htmlfilters %}
{% block title %}
IETF {{ schedule.meeting.number }} meeting agenda personalization
{% endblock %}
{% block morecss %}
tr:not(:first-child) th.gap {
height: 3em !important;
background-color: inherit !important;
border: none !important;
}
tr:first-child th.gap {
height: 0 !important;
background-color: inherit !important;
border: none !important;
}
div.tz-display {
margin-bottom: 0.5em;
margin-top: 1em;
text-align: right;
}
.tz-display a {
cursor: pointer;
}
.tz-display label {
font-weight: normal;
}
.tz-display select {
min-width: 15em;
}
#affix .nav li.tz-display {
padding: 4px 20px;
}
#affix .nav li.tz-display a {
display: inline;
padding: 0;
}
{% endblock %}
{% block bodyAttrs %}data-bs-spy="scroll" data-bs-target="#affix"{% endblock %}
{% block content %}
{% origin %}
<div class="row">
<div class="col-md-12">
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated selected="select-sessions" title_extra="" %}
</div>
</div>
<div class="row">
<div class="col-md-10">
{# cache this part -- it takes 3-6 seconds to generate #}
{% load cache %}
{% cache cache_time ietf_meeting_agenda_personalize schedule.meeting.number request.path %}
<div class="row">
<div class="col-xs-6"><h1>Session Selection</h1></div>
<div class="col-xs-6">
<div class="tz-display">
<div><small>
<label for="timezone-select">Time zone:</label>
<a id="meeting-timezone" onclick="ietf_timezone.use('{{ timezone }}')">Meeting</a> |
<a id="local-timezone" onclick="ietf_timezone.use('local')">Local</a> |
<a id="utc-timezone" onclick="ietf_timezone.use('UTC')">UTC</a>
</small></div>
<select id="timezone-select" class="tz-select">
{# Avoid blank while loading. JavaScript replaces the option list after init. #}
<option selected>{{ timezone }}</option>
</select>
</div>
</div>
</div>
{% if is_current_meeting %}
<p class="alert alert-info">
<b>Note:</b> IETF agendas are subject to change, up to and during a meeting.
</p>
{% endif %}
{% include "meeting/agenda_filter.html" with filter_categories=filter_categories always_show=True %}
{% include "meeting/agenda_personalize_buttonlist.html" with meeting=schedule.meeting only %}
<h2>
Individual Sessions
</h2>
<p>
Check boxes below to select individual sessions.
</p>
<table id="agenda-table" class="table table-sm table-striped">
{% for item in filtered_assignments %}
{% ifchanged item.timeslot.time|date:"Y-m-d" %}
<tr>
<th class="gap" colspan="7"></th>
</tr>
<tr class="table-warning">
<th colspan="7">
{# The anchor here needs to be in a div, not in the th, in order for the anchor-target margin hack to work #}
<div class="anchor-target" id="slot-{{ item.timeslot.time|slugify }}"></div>
{{ item.timeslot.time|date:"l, F j, Y" }}
</th>
</tr>
{% endifchanged %}
{% if item.timeslot.type_id == 'regular' %}
{% ifchanged %}
<tr class="table-info session-label-row"
data-slot-start-ts="{{ item.start_timestamp }}"
data-slot-end-ts="{{ item.end_timestamp }}">
<td class="leftmarker"></td>
<th class="text-nowrap text-right">
<span class="d-none d-sm-block">
{% include "meeting/timeslot_start_end.html" %}
</span>
</th>
<th colspan="4">
<span class="d-none d-md-block d-lg-block d-xl-block d-xxl-block">
{% include "meeting/timeslot_start_end.html" %}
</span>
{{ item.timeslot.time|date:"l" }}
{{ item.timeslot.name|capfirst_allcaps }}
</th>
<td class="rightmarker"></td>
</tr>
{% endifchanged %}
{% endif %}
{% if item.timeslot.type.slug == 'break' or item.timeslot.type.slug == 'reg' or item.timeslot.type.slug == 'other' %}
<tr id="row-{{ item.slug }}"
data-slot-start-ts="{{ item.start_timestamp }}"
data-slot-end-ts="{{ item.end_timestamp }}">
<td class="leftmarker">
{% if item.session_keyword %}
<input
type="checkbox"
class="pickview"
title="Select session"
name="selected-sessions"
value="{{ item.session_keyword }}"
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-filter-item="{{ item.session_keyword }}">
{% endif %}
</td>
<td class="text-nowrap text-right">
<span class="d-none d-sm-block">
{% include "meeting/timeslot_start_end.html" %}
</span>
</td>
<td colspan="3">
<span class="d-none d-md-block d-lg-block d-xl-block d-xxl-block">
{% include "meeting/timeslot_start_end.html" %}
</span>
{% if item.timeslot.show_location and item.timeslot.get_html_location %}
{% if schedule.meeting.number|add:"0" < 96 %}
{% comment %}<a href="https://tools.ietf.org/agenda/{{ schedule.meeting.number }}/venue/?room={{ item.timeslot.get_html_location|xslugify }}">{% endcomment $}
{{ item.timeslot.get_html_location }}
{% comment %}</a>{% endcomment %}
{% elif item.timeslot.location.floorplan %}
<a
href="{% url 'ietf.meeting.views.floor_plan' num=schedule.meeting.number %}?room={{ item.timeslot.get_html_location|xslugify }}">{{ item.timeslot.get_html_location }}</a>
{% else %}
{{ item.timeslot.get_html_location }}
{% endif %}
{% with item.timeslot.location.floorplan as floor %}
{% if item.timeslot.location.floorplan %}
<span class="d-none d-sm-block">
<a
href="{% url 'ietf.meeting.views.floor_plan' num=schedule.meeting.number %}#{{ floor.name|xslugify }}"
class="float-end" title="{{ floor.name }}"><span
class="badge bg-blank label-wide">{{ floor.short }}</span></a>
</span>
{% endif %}
{% endwith %}
{% endif %}
</td>
<td>
{% if item.session.agenda %}
<a href="{{ item.session.agenda.get_href }}">
{{ item.timeslot.name }}
</a>
{% else %}
{{ item.timeslot.name }}
{% endif %}
{% if item.session.current_status == 'canceled' %}
<span class="badge bg-danger float-end">CANCELLED</span>
{% endif %}
</td>
<td class="rightmarker"></td>
</tr>
{% endif %}
{% if item.timeslot.type_id == 'regular' or item.timeslot.type.slug == 'plenary' %}
{% if item.session.historic_group %}
<tr id="row-{{ item.slug }}"
{% if item.timeslot.type.slug == 'plenary' %}class="{{ item.timeslot.type.slug }}danger"{% endif %}
data-slot-start-ts="{{ item.start_timestamp }}"
data-slot-end-ts="{{ item.end_timestamp }}">
<td class="leftmarker">
{% if item.session_keyword %}
<input
type="checkbox"
class="pickview"
title="Select session"
name="selected-sessions"
value="{{ item.session_keyword }}"
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-filter-item="{{ item.session_keyword }}">
{% endif %}
</td>
{% if item.timeslot.type.slug == 'plenary' %}
<th class="text-nowrap text-right">
<span class="d-none d-sm-block">
{% include "meeting/timeslot_start_end.html" %}
</span>
</th>
<td colspan="3">
<span class="d-none d-md-block d-lg-block d-xl-block d-xxl-block">
{% include "meeting/timeslot_start_end.html" %}
</span>
{% if item.timeslot.show_location and item.timeslot.get_html_location %}
{% if schedule.meeting.number|add:"0" < 96 %}
{% comment %}<a href="https://tools.ietf.org/agenda/{{ schedule.meeting.number }}/venue/?room={{ item.timeslot.get_html_location|xslugify }}">{% endcomment %}
{{ item.timeslot.get_html_location }}
{% comment %}</a>{% endcomment %}
{% elif item.timeslot.location.floorplan %}
<a
href="{% url 'ietf.meeting.views.floor_plan' num=schedule.meeting.number %}?room={{ item.timeslot.get_html_location|xslugify }}">{{ item.timeslot.get_html_location }}</a>
{% else %}
{{ item.timeslot.get_html_location }}
{% endif %}
{% endif %}
</td>
{% else %}
<td>
{% with item.timeslot.location.floorplan as floor %}
{% if item.timeslot.location.floorplan %}
<span class="d-none d-sm-block">
<a
href="{% url 'ietf.meeting.views.floor_plan' num=schedule.meeting.number %}#{{ floor.name|xslugify }}"
class="float-end" title="{{ floor.name }}"><span
class="badge bg-blank">{{ floor.short }}</span></a>
</span>
{% endif %}
{% endwith %}
</td>
<td>
{% if item.timeslot.show_location and item.timeslot.get_html_location %}
{% if schedule.meeting.number|add:"0" < 96 %}
{% comment %}<a href="https://tools.ietf.org/agenda/{{ schedule.meeting.number }}/venue/?room={{ item.timeslot.get_html_location|xslugify }}">{% endcomment %}
{{ item.timeslot.get_html_location }}
{% comment %}</a>{% endcomment %}
{% elif item.timeslot.location.floorplan %}
<a
href="{% url 'ietf.meeting.views.floor_plan' num=schedule.meeting.number %}?room={{ item.timeslot.get_html_location|xslugify }}">{{ item.timeslot.get_html_location }}</a>
{% else %}
{{ item.timeslot.get_html_location }}
{% endif %}
{% endif %}
</td>
<td><span class="d-none d-sm-block">{{ item.session.historic_group.historic_parent.acronym }}</span></td>
<td>
{% if item.session.historic_group %}
<a
href="{% url 'ietf.group.views.group_about' acronym=item.session.historic_group.acronym %}">{{ item.session.historic_group.acronym }}</a>
{% else %}
{{ item.session.historic_group.acronym }}
{% endif %}
</td>
{% endif %}
<td>
{% if item.session.agenda %}
<a href="{{ item.session.agenda.get_href }}">
{% endif %}
{% if item.timeslot.type.slug == 'plenary' %}
{{ item.timeslot.name }}
{% else %}
{{ item.session.historic_group.name }}
{% endif %}
{% if item.session.agenda %}
</a>
{% endif %}
{% if item.session.current_status == 'canceled' %}
<span class="badge bg-danger float-end">CANCELLED</span>
{% endif %}
{% if item.session.historic_group.state_id == "bof" %}
<span class="badge bg-success float-end">BOF</span>
{% endif %}
{% if item.session.current_status == 'resched' %}
<span class="badge bg-danger float-end">
RESCHEDULED
{% if item.session.rescheduled_to %}
TO
<span class="timetooltip reschedtimetooltip"><span class="time"
data-start-time="{{ item.session.rescheduled_to.utc_start_time|date:"U" }}"
data-end-time="{{ item.session.rescheduled_to.utc_end_time|date:"U" }}"
{% if item.timeslot.time|date:"l" != item.session.rescheduled_to.time|date:"l" %}
weekday="1"{% endif %}>
{% if "-utc" in request.path %}
{{ item.session.rescheduled_to.utc_start_time|date:"l G:i"|upper }}-
{{ item.session.rescheduled_to.utc_end_time|date:"G:i" }}
{% else %}
{{ item.session.rescheduled_to.time|date:"l G:i"|upper }}-
{{ item.session.rescheduled_to.end_time|date:"G:i" }}
{% endif %}
</span></span>
{% endif %}
</span>
{% endif %}
{% if item.session.agenda_note|first_url|conference_url %}
<br>
<a href={{ item.session.agenda_note|first_url }}>{{ item.session.agenda_note|slice:":23" }}</a>
{% elif item.session.agenda_note %}
<br><span class="text-danger">{{ item.session.agenda_note }}</span>
{% endif %}
</td>
<td class="rightmarker"></td>
</tr>
{% endif %}
{% endif %}
{% endfor %}
</table>
{% include "meeting/agenda_personalize_buttonlist.html" with meeting=schedule.meeting only %}
</div>
<div class="col-md-2 d-print-none" id="affix">
<ul class="nav nav-pills nav-stacked small" data-bs-spy="affix">
<li><a href="#now">Now</a></li>
{% for item in filtered_assignments %}
{% ifchanged item.timeslot.time|date:"Y-m-d" %}
<li><a href="#slot-{{ item.timeslot.time|slugify }}">{{ item.timeslot.time|date:"l, F j, Y" }}</a></li>
{% endifchanged %}
{% endfor %}
<li>
<hr/>
</li>
<li class="tz-display">Showing <span class="current-tz">{{ timezone }}</span> time</li>
<li class="tz-display"><span> {# span avoids applying nav link styling to these shortcuts #}
<a onclick="ietf_timezone.use('{{ timezone }}')">Meeting time</a> |
<a onclick="ietf_timezone.use('local')">Local time</a> |
<a onclick="ietf_timezone.use('UTC')">UTC</a></span>
</li>
{% if settings.DEBUG and settings.DEBUG_AGENDA %}
<li>
<hr/>
</li>
<li><span id="current-time"></span></li>
{% endif %}
</ul>
</div>
</div>
{% endcache %}
{# make the timezone available to JS #}
<span id="initial-data" hidden data-timezone="{{ timezone }}"></span>
{% endblock %}
{% block js %}
<script src="{% static 'ietf/js/moment.js' %}"></script>
<script src="{% static 'ietf/js/moment-timezone-with-data-10-year-range.js' %}"></script>
<script src="{% static 'ietf/js/timezone.js' %}"></script>
<script src="{% static 'ietf/js/agenda_timezone.js' %}"></script>
<script src="{% static 'ietf/js/agenda_filter.js' %}"></script>
<script src="{% static 'ietf/js/agenda/agenda_personalize.js' %}"></script>
<script>
{% if settings.DEBUG and settings.DEBUG_AGENDA %}
speedup = +$.urlParam('speedup')
if (speedup < 1) {
speedup = 1
}
start_time = moment().utc()
if ($.urlParam('date')) {
offset_time = moment.tz(decodeURIComponent($.urlParam('date')), 'UTC')
} else {
offset_time = start_time
}
if (speedup > 1 || offset_time != start_time) {
moment.now = function () {
return (+new Date() - start_time) * speedup + offset_time
}
}
{% else %}
speedup = 1
{% endif %}
/* pull this from the agenda_personalize js module to make available to agenda_timezone */
meeting_timezone = agenda_personalize.meeting_timezone;
</script>
{% endblock %}

View file

@ -4,17 +4,17 @@ Buttons for the agenda_personalize.html template
Required parameter: meeting - meeting being displayed
{% endcomment %}
{% load agenda_custom_tags %}
<div class="buttonlist">
<a class="btn btn-primary agenda-link filterable"
href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">
View customized agenda
</a>
<a class="btn btn-primary agenda-link filterable"
href="{% url 'ietf.meeting.views.agenda_ical' num=meeting.number %}">
Download as .ics
</a>
<a class="btn btn-primary agenda-link filterable"
<div class="mb-3">
<a class="btn btn-sm btn-outline-primary visually-hidden ical-link agenda-link filterable"
href="{% webcal_url 'ietf.meeting.views.agenda_ical' num=meeting.number %}">
Subscribe with webcal
Subscribe to personal agenda
</a>
<a class="visually-hidden btn btn-sm btn-outline-primary ical-link agenda-link filterable" href="{% url "ietf.meeting.views.agenda_ical" num=meeting.number %}">Download .ics of personal agenda</a>
<a class="btn btn-sm btn-outline-primary visually-hidden ical-link agenda-link filterable"
href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">
View personal agenda
</a>
</div>

View file

@ -13,6 +13,9 @@
<h1>
IETF {{ meeting.number }} Meeting Agenda {{ title_extra }}
{% if personalize %}
Personalization
{% endif %}
</h1>
<h4>
{{ meeting.city|default:"Location TBD" }}, {{ meeting.date|date:"F j" }} -
@ -42,6 +45,12 @@
UTC Agenda
</a>
</li>
<li class="nav-item">
<a class="nav-link agenda-link filterable {% if selected == "select-sessions" %}active{% endif %}" href="{% url 'ietf.meeting.views.agenda_personalize' num=meeting.number %}"
>
Personalize Agenda
</a>
</li>
{% if user|has_role:"Secretariat,Area Director,IAB" %}
{% if schedule != meeting.schedule %}
<li class="nav-item">
@ -72,11 +81,5 @@
Plaintext
</a>
</li>
<li class="nav-item">
<a class="nav-link agenda-link filterable {% if selected == "select-sessions" %}active{% endif %}" href="{% url 'ietf.meeting.views.agenda_personalize' num=meeting.number %}"
>
Select Sessions
</a>
</li>
</ul>
</p>

View file

@ -26,6 +26,7 @@
"ietf/static/js/agenda_filter.js",
"ietf/static/js/agenda_materials.js",
"ietf/static/js/agenda_timezone.js",
"ietf/static/js/agenda_personalize.js",
"ietf/static/js/timezone.js",
"ietf/static/js/room_params.js",
"ietf/static/js/week-view.js",