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()) is_current_meeting = (num is None) or (num == get_current_ietf_meeting_num())
rendered_page = render(request, "meeting/"+base+ext, { rendered_page = render(request, "meeting/"+base+ext, {
"personalize": False,
"schedule": schedule, "schedule": schedule,
"filtered_assignments": filtered_assignments, "filtered_assignments": filtered_assignments,
"updated": updated, "updated": updated,
@ -1701,8 +1702,9 @@ def agenda_personalize(request, num):
return render( return render(
request, request,
"meeting/agenda_personalize.html", "meeting/agenda.html",
{ {
'personalize': True,
'schedule': meeting.schedule, 'schedule': meeting.schedule,
'updated': meeting.updated(), 'updated': meeting.updated(),
'filtered_assignments': filtered_assignments, 'filtered_assignments': filtered_assignments,
@ -4161,4 +4163,4 @@ def approve_proposed_slides(request, slidesubmission_id, num):
'session_number': session_number, 'session_number': session_number,
'existing_doc' : existing_doc, 'existing_doc' : existing_doc,
'form': form, 'form': form,
}) })

View file

@ -3,7 +3,7 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
// closure to create private scope // closure to create private scope
(function () { (function () {
'use strict' 'use strict';
/* n.b., const refers to the opts object itself, not its contents. /* n.b., const refers to the opts object itself, not its contents.
* Use camelCase for easy translation into element.dataset keys, * 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 */ /* Remove from list, if present */
function remove_list_item (list, item) { function remove_list_item(list, item) {
var item_index = list.indexOf(item); var item_index = list.indexOf(item);
if (item_index !== -1) { if (item_index !== -1) {
list.splice(item_index, 1) list.splice(item_index, 1);
} }
} }
@ -26,63 +26,68 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
* *
* Returns true if added to the list, otherwise false. * 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); var item_index = list.indexOf(item);
if (item_index === -1) { if (item_index === -1) {
list.push(item) list.push(item);
return true; return true;
} else { } else {
list.splice(item_index, 1) list.splice(item_index, 1);
return false; return false;
} }
} }
function parse_query_params (qs) { function parse_query_params(qs) {
var params = {} var params = {};
qs = decodeURI(qs).replace(/^\?/, '').toLowerCase() qs = decodeURI(qs)
.replace(/^\?/, '')
.toLowerCase();
if (qs) { if (qs) {
var param_strs = qs.split('&') var param_strs = qs.split('&');
for (var ii = 0; ii < param_strs.length; ii++) { for (var ii = 0; ii < param_strs.length; ii++) {
var toks = param_strs[ii].split('=', 2) var toks = param_strs[ii].split('=', 2);
params[toks[0]] = toks[1] || true params[toks[0]] = toks[1] || true;
} }
} }
return params return params;
} }
/* filt = 'show' or 'hide' */ /* filt = 'show' or 'hide' */
function get_filter_from_qparams (qparams, filt) { function get_filter_from_qparams(qparams, filt) {
if (!qparams[filt] || (qparams[filt] === true)) { if (!qparams[filt] || (qparams[filt] === true)) {
return []; return [];
} }
var result = []; var result = [];
var qp = qparams[filt].split(','); var qp = qparams[filt].split(',');
for (var ii = 0; ii < qp.length; ii++) { for (var ii = 0; ii < qp.length; ii++) {
result.push(qp[ii].trim()); result.push(qp[ii].trim());
} }
return result; return result;
} }
function get_filter_params (qparams) { function get_filter_params(qparams) {
var enabled = opts.alwaysShow || qparams.show || qparams.hide; var enabled = opts.alwaysShow || qparams.show || qparams.hide;
return { return {
enabled: enabled, enabled: enabled,
show: get_filter_from_qparams(qparams, 'show'), show: get_filter_from_qparams(qparams, 'show'),
hide: get_filter_from_qparams(qparams, 'hide') hide: get_filter_from_qparams(qparams, 'hide')
} };
} }
function get_keywords(elt) { function get_keywords(elt) {
var keywords = $(elt).attr('data-filter-keywords'); var keywords = $(elt)
.attr('data-filter-keywords');
if (keywords) { if (keywords) {
return keywords.toLowerCase().split(','); return keywords.toLowerCase()
.split(',');
} }
return []; return [];
} }
function get_item(elt) { 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? // utility method - is there a match between two lists of keywords?
@ -94,25 +99,27 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
} }
return false; return false;
} }
// Find the items corresponding to a keyword // Find the items corresponding to a keyword
function get_items_with_keyword (keyword) { function get_items_with_keyword(keyword) {
var items = []; var items = [];
$('.view button.pickview').filter(function(index, elt) { $('.view button.pickview')
return keyword_match(get_keywords(elt), [keyword]); .filter(function (index, elt) {
}).each(function (index, elt) { return keyword_match(get_keywords(elt), [keyword]);
items.push(get_item($(elt))); })
}); .each(function (index, elt) {
items.push(get_item($(elt)));
});
return items; return items;
} }
function filtering_is_enabled (filter_params) { function filtering_is_enabled(filter_params) {
return filter_params.enabled; return filter_params.enabled;
} }
// Update the filter / customization UI to match the current filter parameters // 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'); var buttons = $('.pickview');
if (!filtering_is_enabled(filter_params)) { if (!filtering_is_enabled(filter_params)) {
@ -121,12 +128,12 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
return; 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 // show the customizer - it will stay visible even if filtering is disabled
const customizer = $('#customize'); const customizer = $('#customize');
if (customizer.hasClass('collapse')) { if (customizer.hasClass('collapse')) {
customizer.collapse('show') customizer.collapse('show');
} }
// Update button state to match visibility // Update button state to match visibility
@ -135,7 +142,7 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
var keywords = get_keywords(elt); var keywords = get_keywords(elt);
keywords.push(get_item(elt)); // treat item as one of its keywords keywords.push(get_item(elt)); // treat item as one of its keywords
var hidden = keyword_match(filter_params.hide, keywords); var hidden = keyword_match(filter_params.hide, keywords);
var shown = keyword_match(filter_params.show, keywords); var shown = keyword_match(filter_params.show, keywords);
if (shown && !hidden) { if (shown && !hidden) {
elt.addClass('active'); elt.addClass('active');
} else { } else {
@ -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 * Calling the individual update_* functions outside of this method will likely cause
* various parts of the page to get out of sync. * various parts of the page to get out of sync.
*/ */
function update_view () { function update_view() {
var filter_params = get_filter_params(parse_query_params(window.location.search)) var filter_params = get_filter_params(parse_query_params(window.location.search));
update_filter_ui(filter_params) update_filter_ui(filter_params);
if (opts.updateCallback) { 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 * Updates the URL to match filter_params, then updates the history / display to match
* (if supported) or loads the new URL. * (if supported) or loads the new URL.
*/ */
function update_filters (filter_params) { function update_filters(filter_params) {
var new_url = replace_querystring( var new_url = replace_querystring(
window.location.href, window.location.href,
filter_params_as_querystring(filter_params) 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) { if (window.history && window.history.replaceState) {
// Keep current origin, replace search string, no page reload // Keep current origin, replace search string, no page reload
history.replaceState({}, document.title, new_url) history.replaceState({}, document.title, new_url);
update_view() update_view();
} else { } else {
// No window.history.replaceState support, page reload required // No window.history.replaceState support, page reload required
window.location = new_url window.location = new_url;
} }
} }
@ -183,34 +190,35 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
*/ */
function update_href_querystrings(querystring) { function update_href_querystrings(querystring) {
Array.from( Array.from(
document.getElementsByClassName('agenda-link filterable') 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) { function filter_params_as_querystring(filter_params) {
var qparams = [] var qparams = [];
if (filter_params.show.length > 0) { 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) { if (filter_params.hide.length > 0) {
qparams.push('hide=' + filter_params.hide.join()) qparams.push('hide=' + filter_params.hide.join());
} }
if (qparams.length > 0) { if (qparams.length > 0) {
return '?' + qparams.join('&') return '?' + qparams.join('&');
} }
return '' return '';
} }
function replace_querystring(url, new_querystring) { 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 /* Helper for pick group/type button handlers - toggles the appropriate parameter entry
* elt - the jquery element that was clicked * 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 fp = get_filter_params(parse_query_params(window.location.search));
var item = get_item(elt); var item = get_item(elt);
@ -232,17 +240,17 @@ 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. */ * an area will enable all items in the row as one would expect. */
if (just_showed_item) { if (just_showed_item) {
var children = get_items_with_keyword(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.show, child);
remove_list_item(fp.hide, child); remove_list_item(fp.hide, child);
}); });
} }
// If the show list is empty, clear the hide list because there is nothing to hide // If the show list is empty, clear the hide list because there is nothing to hide
if (fp.show.length === 0) { if (fp.show.length === 0) {
fp.hide = []; fp.hide = [];
} }
return fp; return fp;
} }
@ -251,23 +259,13 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
} }
function register_handlers() { function register_handlers() {
$('.pickview').on("click", function () { $('.pickview')
if (is_disabled($(this))) { return; } .on("click", function () {
var fp = handle_pick_button($(this)); console.log("pickview");
update_filters(fp); 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 /* Entry point to filtering code when page loads
@ -275,17 +273,18 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin
* This must be called if you are using the HTML template to provide a customization * 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. * 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" // ready handler fires immediately if document is already "ready"
$(document).ready(function () { $(document)
register_handlers(); .ready(function () {
update_view(); register_handlers();
}) update_view();
})
} }
// utility method - filter a jquery set to those matching a keyword // utility method - filter a jquery set to those matching a keyword
function rows_matching_filter_keyword(rows, kw) { 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); var row_kws = get_keywords(element);
return keyword_match(row_kws, [kw.toLowerCase()]); 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, keyword_match: keyword_match,
parse_query_params: parse_query_params, parse_query_params: parse_query_params,
rows_matching_filter_keyword: rows_matching_filter_keyword, 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 // get_current_tz_cb must be overwritten using set_current_tz_cb
function get_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 // Initialize moments
window.initialize_moments = function () { window.initialize_moments = function () {
var times = $('div.time') var times = $('div.time');
$.each(times, function (i, item) { $.each(times, function (i, item) {
item.start_ts = moment.unix(this.getAttribute("data-start-time")) item.start_ts = moment.unix(this.getAttribute("data-start-time"))
.utc(); .utc();
@ -33,7 +33,7 @@ window.initialize_moments = function () {
item.format = +this.getAttribute("format"); item.format = +this.getAttribute("format");
} }
}); });
var times = $('[data-slot-start-ts]') times = $('[data-slot-start-ts]');
$.each(times, function (i, item) { $.each(times, function (i, item) {
item.slot_start_ts = moment.unix(this.getAttribute("data-slot-start-ts")) item.slot_start_ts = moment.unix(this.getAttribute("data-slot-start-ts"))
.utc(); .utc();
@ -205,7 +205,7 @@ window.update_times = function (newtz) {
}); });
update_tooltips_all(); update_tooltips_all();
update_clock(); update_clock();
} };
// Highlight ongoing based on the current time // Highlight ongoing based on the current time
window.highlight_ongoing = function () { window.highlight_ongoing = function () {
@ -213,7 +213,7 @@ window.highlight_ongoing = function () {
.remove("#now"); .remove("#now");
$('.table-warning') $('.table-warning')
.removeClass("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 () { agenda_rows = agenda_rows.filter(function () {
return moment() return moment()
.isBetween(this.slot_start_ts, this.slot_end_ts); .isBetween(this.slot_start_ts, this.slot_end_ts);
@ -245,13 +245,13 @@ window.update_tooltips_all = function () {
$(this) $(this)
.html(format_tooltip_table(this.start_ts, this.end_ts)); .html(format_tooltip_table(this.start_ts, this.end_ts));
}); });
} };
// Update clock // Update clock
window.update_clock = function () { window.update_clock = function () {
$('#current-time') $('#current-time')
.html(format_time(moment(), get_current_tz_cb(), 0)); .html(format_time(moment(), get_current_tz_cb(), 0));
} };
$.urlParam = function (name) { $.urlParam = function (name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)') var results = new RegExp('[\?&]' + name + '=([^&#]*)')
@ -261,7 +261,7 @@ $.urlParam = function (name) {
} else { } else {
return results[1] || 0; return results[1] || 0;
} }
} };
window.init_timers = function () { window.init_timers = function () {
var fast_timer = 60000 / (speedup > 600 ? 600 : speedup); var fast_timer = 60000 / (speedup > 600 ? 600 : speedup);
@ -271,9 +271,9 @@ window.init_timers = function () {
setInterval(function () { highlight_ongoing(); }, fast_timer); setInterval(function () { highlight_ongoing(); }, fast_timer);
setInterval(function () { update_tooltips(); }, fast_timer); setInterval(function () { update_tooltips(); }, fast_timer);
setInterval(function () { update_tooltips_all(); }, 3600000 / speedup); setInterval(function () { update_tooltips_all(); }, 3600000 / speedup);
} };
// set method used to find current time zone // set method used to find current time zone
window.set_current_tz_cb = function (fn) { window.set_current_tz_cb = function (fn) {
get_current_tz_cb = fn; get_current_tz_cb = fn;
} };

View file

@ -17,18 +17,19 @@ window.ietf_timezone; // public interface
var current_timezone; var current_timezone;
// Select timezone to use. Arg is name of a timezone or 'local' to guess local tz. // 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 // Guess local timezone if necessary
if (newtz.toLowerCase() === 'local') { if (newtz.toLowerCase() === 'local') {
newtz = moment.tz.guess() newtz = moment.tz.guess();
} }
if (current_timezone !== newtz) { if (current_timezone !== newtz) {
current_timezone = newtz current_timezone = newtz;
// Update values of tz-select inputs but do not trigger change event // 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) { 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 * This will set the timezone to the value of 'current'. Set up the tz_change callback
* before initializing. * before initializing.
*/ */
function timezone_init (current) { function timezone_init(current) {
var tz_names = moment.tz.names() var tz_names = moment.tz.names();
var select = $('select.tz-select') var select = $('select.tz-select');
select.empty() select.empty();
$.each(tz_names, function (i, item) { $.each(tz_names, function (i, item) {
if (current === item) { if (current === item) {
select.append($('<option/>', { select.append($('<option/>', {
selected: 'selected', html: item, value: item selected: 'selected',
})) html: item,
value: item
}));
} else { } else {
select.append($('<option/>', { 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 /* 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 * 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, * 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 * so trigger the change event ourselves to be sure the UI stays consistent
* with the timezone select input. */ * with the timezone select input. */
window.addEventListener('pageshow', function(){select.trigger("change"); }) window.addEventListener('pageshow', function () { select.trigger("change"); });
use_timezone(current); use_timezone(current);
} }
// Expose public interface // Expose public interface
ietf_timezone = { ietf_timezone = {
get_current_tz: function() {return current_timezone}, get_current_tz: function () { return current_timezone },
initialize: timezone_init, 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 use: use_timezone
} };
})(); })();

View file

@ -4,13 +4,16 @@
{% load static %} {% load static %}
{% load ietf_filters %} {% load ietf_filters %}
{% load textfilters %} {% load textfilters %}
{% load htmlfilters agenda_custom_tags%} {% load htmlfilters agenda_custom_tags %}
{% block title %} {% block title %}
IETF {{ schedule.meeting.number }} Meeting Agenda IETF {{ schedule.meeting.number }} Meeting Agenda
{% if "-utc" in request.path %} {% if "-utc" in request.path %}
(UTC) (UTC)
{% endif %} {% endif %}
{% if personalize %}
Personalization
{% endif %}
{% endblock %} {% endblock %}
{% block morecss %} {% block morecss %}
@ -24,9 +27,10 @@
<div class="row"> <div class="row">
<div class="col-md-10"> <div class="col-md-10">
{% if "-utc" in request.path %} {% if "-utc" in request.path %}
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated selected="agenda-utc" title_extra="(UTC)" %} {% 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 %} {% else %}
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated selected="agenda" title_extra="" %} {% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated selected="agenda" title_extra="" %}
{% endif %} {% endif %}
@ -34,9 +38,16 @@
{# cache this part -- it takes 3-6 seconds to generate #} {# cache this part -- it takes 3-6 seconds to generate #}
{% load cache %} {% load cache %}
{% cache cache_time ietf_meeting_agenda_utc schedule.meeting.number request.path %} {% cache cache_time ietf_meeting_agenda_utc schedule.meeting.number request.path %}
<div class="row"> <div class="row">
<div class="col-5"> <div class="col-5">
<h2>Agenda</h2> <h2>
{% if personalize %}
Session Selection
{% else %}
Agenda
{% endif %}
</h2>
</div> </div>
<div class="col float-end tz-display"> <div class="col float-end tz-display">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
@ -68,28 +79,22 @@
{% endif %} {% endif %}
<p> <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>
<p> {% include "meeting/agenda_personalize_buttonlist.html" with meeting=schedule.meeting only %}
<div class="d-inline mb-3">Download agenda as .ics:</div>
<div class="d-inline"> <div class="input-group input-group-sm mb-3">
<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> <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">
</div> {% for fc in filter_categories %}
{% if not forloop.last %} {# skip the last group, it's the office hours/misc #}
<div class="input-group input-group-sm mb-3 d-inline"> {% for p in fc|dictsort:"label" %}
<button class="btn btn-outline-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">By area</button><ul class="dropdown-menu"> <li><a class="dropdown-item" href="{% url "ietf.meeting.views.agenda_ical" num=schedule.meeting.number %}?show={{p.keyword}}">{{p.label}}</a></li>
{% for fc in filter_categories %} {% endfor %}
{% if not forloop.last %} {# skip the last group, it's the office hours/misc #} {% endif %}
{% for p in fc|dictsort:"label" %} {% endfor %}
<li><a class="dropdown-item" href="{% url "ietf.meeting.views.agenda_ical" num=schedule.meeting.number %}?show={{p.keyword}}">{{p.label}}</a></li> </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>
{% endfor %} </div>
{% 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>
</div>
</p>
<div id="weekview" class="visually-hidden mt-3"> <div id="weekview" class="visually-hidden mt-3">
<h2> <h2>
@ -103,14 +108,22 @@
<iframe class="w-100 overflow-hidden border border-dark" scrolling="no"></iframe> <iframe class="w-100 overflow-hidden border border-dark" scrolling="no"></iframe>
</div> </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 %} {% for item in filtered_assignments %}
{% ifchanged item.timeslot.time|date:"Y-m-d" %} {% ifchanged item.timeslot.time|date:"Y-m-d" %}
<tr class="table-primary"> <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 #} {# 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="anchor-target" id="slot-{{item.timeslot.time|slugify}}"></div>
<div class="h6 mt-2">{{ item.timeslot.time|date:"l, F j, Y" }}</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:',' }}" <tr id="row-{{ item.slug }}" data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-slot-start-ts="{{item.start_timestamp}}" data-slot-start-ts="{{item.start_timestamp}}"
data-slot-end-ts="{{item.end_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" %} {% include "meeting/timeslot_start_end.html" %}
</td> </td>
<td colspan="3"> <td colspan="3">
@ -171,7 +197,10 @@
<tr class="table-secondary session-label-row" <tr class="table-secondary session-label-row"
data-slot-start-ts="{{item.start_timestamp}}" data-slot-start-ts="{{item.start_timestamp}}"
data-slot-end-ts="{{item.end_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" %} {% include "meeting/timeslot_start_end.html" %}
</th> </th>
<th colspan="4"> <th colspan="4">
@ -188,8 +217,22 @@
data-filter-keywords="{{ item.filter_keywords|join:',' }}" data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-slot-start-ts="{{item.start_timestamp}}" data-slot-start-ts="{{item.start_timestamp}}"
data-slot-end-ts="{{item.end_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' %} {% if item.slot_type.slug == 'plenary' %}
<th class="text-nowrap text-right"> <th class="text-nowrap text-end">
{% include "meeting/timeslot_start_end.html" %} {% include "meeting/timeslot_start_end.html" %}
</th> </th>
<td colspan="3"> <td colspan="3">
@ -270,6 +313,8 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</table> </table>
{% include "meeting/agenda_personalize_buttonlist.html" with meeting=schedule.meeting only %}
</div> </div>
<div class="col-md-2 d-print-none" id="affix"> <div class="col-md-2 d-print-none" id="affix">
@ -341,8 +386,7 @@
} }
function update_ical_links(filter_params) { 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) { function update_weekview(filter_params) {
@ -389,6 +433,10 @@
<script src="{% static 'ietf/js/timezone.js' %}"></script> <script src="{% static 'ietf/js/timezone.js' %}"></script>
<script src="{% static 'ietf/js/agenda_materials.js' %}"></script> <script src="{% static 'ietf/js/agenda_materials.js' %}"></script>
<script src="{% static 'ietf/js/agenda_timezone.js' %}"></script> <script src="{% static 'ietf/js/agenda_timezone.js' %}"></script>
{% if personalize %}
<script src="{% static 'ietf/js/agenda_personalize.js' %}"></script>
{% endif %}
<script> <script>
{% if settings.DEBUG and settings.DEBUG_AGENDA %} {% if settings.DEBUG and settings.DEBUG_AGENDA %}
speedup = +$.urlParam('speedup'); speedup = +$.urlParam('speedup');
@ -441,7 +489,18 @@
init_timers(); init_timers();
// Finally, set up the agenda filter UI. This does not depend on the timezone. // Finally, set up the agenda filter UI. This does not depend on the timezone.
agenda_filter.set_update_callback(update_view); {% 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(); 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 Required parameter: meeting - meeting being displayed
{% endcomment %} {% endcomment %}
{% load agenda_custom_tags %} {% load agenda_custom_tags %}
<div class="buttonlist">
<a class="btn btn-primary agenda-link filterable" <div class="mb-3">
href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}"> <a class="btn btn-sm btn-outline-primary visually-hidden ical-link agenda-link filterable"
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"
href="{% webcal_url 'ietf.meeting.views.agenda_ical' num=meeting.number %}"> href="{% webcal_url 'ietf.meeting.views.agenda_ical' num=meeting.number %}">
Subscribe with webcal Subscribe to personal agenda
</a> </a>
</div>
<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> <h1>
IETF {{ meeting.number }} Meeting Agenda {{ title_extra }} IETF {{ meeting.number }} Meeting Agenda {{ title_extra }}
{% if personalize %}
Personalization
{% endif %}
</h1> </h1>
<h4> <h4>
{{ meeting.city|default:"Location TBD" }}, {{ meeting.date|date:"F j" }} - {{ meeting.city|default:"Location TBD" }}, {{ meeting.date|date:"F j" }} -
@ -42,6 +45,12 @@
UTC Agenda UTC Agenda
</a> </a>
</li> </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 user|has_role:"Secretariat,Area Director,IAB" %}
{% if schedule != meeting.schedule %} {% if schedule != meeting.schedule %}
<li class="nav-item"> <li class="nav-item">
@ -72,11 +81,5 @@
Plaintext Plaintext
</a> </a>
</li> </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> </ul>
</p> </p>

View file

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