feat: replace old agenda with agenda-neue (#4406)

* feat: remove old agenda (django-side)

* fix: bring in latest commits

* test: remove -neue from playwright tests

* test: remove agenda selenium js tests

* test: remove agenda views tests

* fix: remove deprecated agenda views (week-view, agenda-by, floor-plan)

* test: fix failing python tests

* test: remove more deprecated tests

* chore: remove deprecated templates

* test: remove unused import

* feat: handle agenda personalize with filter + move agenda specific stuff out of root component

* fix: redirect deprecated urls to agenda / floorplan

* feat: agenda - open picker mode when from personalize path

* fix: safari doesn't support device-pixel-content-box property on ResizeObserver

* test: move floor plan test into main agenda test

Co-authored-by: Robert Sparks <rjsparks@nostrum.com>
This commit is contained in:
Nicolas Giard 2022-10-12 16:46:28 -04:00 committed by GitHub
parent 8560bec09c
commit 6f2114fb0c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 286 additions and 3042 deletions

View file

@ -1,9 +1,9 @@
<template lang="pug">
n-theme
n-message-provider
.app-error(v-if='agendaStore.criticalError')
.app-error(v-if='siteStore.criticalError')
i.bi.bi-x-octagon-fill.me-2
span {{agendaStore.criticalError}}
span {{siteStore.criticalError}}
.app-container(ref='appContainer')
router-view.meeting
</template>
@ -12,13 +12,13 @@ n-theme
import { onBeforeUnmount ,onMounted, ref } from 'vue'
import { NMessageProvider } from 'naive-ui'
import { useAgendaStore } from './agenda/store'
import { useSiteStore } from './shared/store'
import NTheme from './components/n-theme.vue'
// STORES
const agendaStore = useAgendaStore()
const siteStore = useSiteStore()
// STATE
@ -29,14 +29,14 @@ const appContainer = ref(null)
// --------------------------------------------------------------------
const resizeObserver = new ResizeObserver(entries => {
agendaStore.$patch({ viewport: Math.round(window.innerWidth) })
siteStore.$patch({ viewport: Math.round(window.innerWidth) })
// for (const entry of entries) {
// const newWidth = entry.contentBoxSize ? entry.contentBoxSize[0].inlineSize : entry.contentRect.width
// }
})
onMounted(() => {
resizeObserver.observe(appContainer.value, { box: 'device-pixel-content-box' })
resizeObserver.observe(appContainer.value)
})
onBeforeUnmount(() => {
@ -47,7 +47,6 @@ onBeforeUnmount(() => {
<style lang="scss">
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "./shared/breakpoints";
.app-error {
background-color: $red-500;
@ -57,69 +56,4 @@ onBeforeUnmount(() => {
padding: 1rem;
text-align: center;
}
.meeting {
> h1 {
font-weight: 500;
color: $gray-700;
display: flex;
justify-content: space-between;
align-items: center;
@media screen and (max-width: $bs5-break-sm) {
justify-content: center;
> span {
font-size: .95em;
}
}
strong {
font-weight: 700;
background: linear-gradient(220deg, $blue-500 20%, $purple-500 70%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
box-decoration-break: clone;
}
}
&-h1-badges {
display: flex;
justify-content: end;
align-items: center;
> span {
font-size: 13px;
font-weight: 700;
background-color: $pink-500;
box-shadow: 0 0 5px 0 rgba($pink-500, .5);
color: #FFF;
padding: 5px 8px;
border-radius: 6px;
& + span {
margin-left: 10px;
}
}
}
&-warning {
background-color: $red-500 !important;
box-shadow: 0 0 5px 0 rgba($red-500, .5) !important;
color: #FFF;
animation: warningBorderFlash 1s ease infinite;
}
> h4 {
@media screen and (max-width: $bs5-break-sm) {
text-align: center;
> span {
font-size: .8em;
text-align: center;
}
}
}
}
</style>

View file

@ -7,7 +7,6 @@
span #[strong IETF {{agendaStore.meeting.number}}] Meeting Agenda {{titleExtra}}
.meeting-h1-badges.d-none.d-sm-flex
span.meeting-warning(v-if='agendaStore.meeting.warningNote') {{agendaStore.meeting.warningNote}}
span.meeting-beta BETA
h4
span {{agendaStore.meeting.city}}, {{ meetingDate }}
h6.float-end.d-none.d-lg-inline(v-if='meetingUpdated') #[span.text-muted Updated:] {{ meetingUpdated }}
@ -54,7 +53,7 @@
@click='setTimezone(`UTC`)'
) UTC
n-select.agenda-timezone-ddn(
v-if='agendaStore.viewport > 1250'
v-if='siteStore.viewport > 1250'
v-model:value='agendaStore.timezone'
:options='timezones'
placeholder='Select Time Zone'
@ -134,7 +133,7 @@
// -----------------------------------
// -> Anchored Day Quick Access Menu
// -----------------------------------
.col-auto.d-print-none(v-if='agendaStore.viewport >= 990')
.col-auto.d-print-none(v-if='siteStore.viewport >= 990')
agenda-quick-access
agenda-mobile-bar
@ -166,6 +165,9 @@ import MeetingNavigation from './MeetingNavigation.vue'
import timezones from '../shared/timezones'
import { useAgendaStore } from './store'
import { useSiteStore } from '../shared/store'
import './agenda.scss'
// MESSAGE PROVIDER
@ -174,6 +176,7 @@ const message = useMessage()
// STORES
const agendaStore = useAgendaStore()
const siteStore = useSiteStore()
// ROUTER
@ -215,7 +218,35 @@ watch(() => agendaStore.meetingDays, () => {
})
})
watch(() => agendaStore.isLoaded, handleCurrentMeetingRedirect)
watch(() => agendaStore.isLoaded, () => {
if (route.query.show) {
// Handle legacy ?show= parameter
const keywords = route.query.show.split(',').map(k => k.trim()).filter(k => !!k)
if (keywords?.length > 0) {
const pickedIds = []
for (const ev of agendaStore.scheduleAdjusted) {
if (keywords.includes(ev.sessionKeyword)) {
pickedIds.push(ev.id)
}
}
if (pickedIds.length > 0) {
agendaStore.$patch({
pickerMode: true,
pickerModeView: true,
pickedEvents: pickedIds
})
agendaStore.persistMeetingPreferences()
}
}
}
if (route.query.pick) {
// Handle legacy /personalize path (open picker mode)
agendaStore.$patch({ pickerMode: true })
router.replace({ query: null })
}
handleCurrentMeetingRedirect()
})
// COMPUTED

View file

@ -1,5 +1,5 @@
<template lang="pug">
.agenda-mobile-bar(v-if='agendaStore.viewport < 990')
.agenda-mobile-bar(v-if='siteStore.viewport < 990')
button(@click='agendaStore.$patch({ filterShown: true })')
i.bi.bi-filter-square-fill.me-2
span Filters
@ -31,6 +31,7 @@ import {
} from 'naive-ui'
import { useAgendaStore } from './store'
import { useSiteStore } from '../shared/store';
// MESSAGE PROVIDER
@ -39,6 +40,7 @@ const message = useMessage()
// STORES
const agendaStore = useAgendaStore()
const siteStore = useSiteStore()
// Download Ics Options

View file

@ -108,6 +108,7 @@
<script setup>
import { computed, h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { DateTime } from 'luxon'
import {
NAffix,
@ -119,6 +120,7 @@ import {
} from 'naive-ui'
import { useAgendaStore } from './store'
import { useSiteStore } from '../shared/store';
// MESSAGE PROVIDER
@ -127,6 +129,12 @@ const message = useMessage()
// STORES
const agendaStore = useAgendaStore()
const siteStore = useSiteStore()
// ROUTER
const router = useRouter()
const route = useRoute()
// Download Ics Options
@ -146,7 +154,7 @@ const downloadIcsOptions = [
// COMPUTED
const shortMode = computed(() => {
return agendaStore.viewport <= 1350
return siteStore.viewport <= 1350
})
// METHODS
@ -163,6 +171,9 @@ function pickerModify () {
}
function pickerDiscard () {
agendaStore.$patch({ pickerMode: false })
if (route.query.show) {
router.push({ query: null })
}
}
function downloadIcs (key) {

View file

@ -4,7 +4,7 @@ n-drawer(v-model:show='isShown', placement='bottom', :height='state.drawerHeight
template(#header)
span Calendar View
.agenda-calendar-actions
template(v-if='agendaStore.viewport > 990')
template(v-if='siteStore.viewport > 990')
i.bi.bi-globe.me-2
small.me-2: strong Timezone:
n-button-group
@ -91,10 +91,12 @@ import bootstrap5Plugin from '@fullcalendar/bootstrap5'
import AgendaDetailsModal from './AgendaDetailsModal.vue'
import { useAgendaStore } from './store'
import { useSiteStore } from '../shared/store'
// STORES
const agendaStore = useAgendaStore()
const siteStore = useSiteStore()
// STATE

View file

@ -7,7 +7,7 @@
th.agenda-table-head-check(v-if='pickerModeActive') &nbsp;
th.agenda-table-head-time Time
th.agenda-table-head-location(colspan='2') Location
th.agenda-table-head-event(colspan='2') {{ agendaStore.viewport < 990 ? '' : 'Event' }}
th.agenda-table-head-event(colspan='2') {{ siteStore.viewport < 990 ? '' : 'Event' }}
tbody
tr.agenda-table-display-noresult(
v-if='!meetingEvents || meetingEvents.length < 1'
@ -58,13 +58,13 @@
span.badge {{item.location.short}}
span {{item.location.name}}
router-link.discreet(
:to='`/meeting/` + agendaStore.meeting.number + `/floor-plan-neue?room=` + xslugify(item.room)'
:to='`/meeting/` + agendaStore.meeting.number + `/floor-plan?room=` + xslugify(item.room)'
:aria-label='item.room'
) {{item.room}}
span(v-else) {{item.room}}
//- CELL - GROUP --------------------------
td.agenda-table-cell-group(v-if='item.type === `regular`')
span.badge(v-if='agendaStore.areaIndicatorsShown && agendaStore.viewport > 1200') {{item.groupAcronym}}
span.badge(v-if='agendaStore.areaIndicatorsShown && siteStore.viewport > 1200') {{item.groupAcronym}}
a.discreet(:href='`/group/` + item.acronym + `/about/`') {{item.acronym}}
//- CELL - NAME ---------------------------
td.agenda-table-cell-name
@ -105,7 +105,7 @@
template(v-else)
span.badge.is-cancelled(v-if='!isMobile && item.status === `canceled`') Cancelled
span.badge.is-rescheduled(v-else-if='!isMobile && item.status === `resched`') Rescheduled
.agenda-table-cell-links-buttons(v-else-if='agendaStore.viewport < 1200 && item.links && item.links.length > 0')
.agenda-table-cell-links-buttons(v-else-if='siteStore.viewport < 1200 && item.links && item.links.length > 0')
n-dropdown(
v-if='!agendaStore.colorPickerVisible'
trigger='click'
@ -201,6 +201,7 @@ import {
import AgendaDetailsModal from './AgendaDetailsModal.vue'
import { useAgendaStore } from './store'
import { useSiteStore } from '../shared/store'
// MESSAGE PROVIDER
@ -209,6 +210,7 @@ const message = useMessage()
// STORES
const agendaStore = useAgendaStore()
const siteStore = useSiteStore()
// DATA
@ -236,7 +238,7 @@ const meetingEvents = computed(() => {
return reduce(sortBy(agendaStore.scheduleAdjusted, 'adjustedStartDate'), (acc, item) => {
const isLive = current >= item.adjustedStart && current < item.adjustedEnd
const itemTimeSlot = agendaStore.viewport > 576 ?
const itemTimeSlot = siteStore.viewport > 576 ?
`${item.adjustedStart.toFormat('HH:mm')} - ${item.adjustedEnd.toFormat('HH:mm')}` :
`${item.adjustedStart.toFormat('HH:mm')} ${item.adjustedEnd.toFormat('HH:mm')}`
@ -482,7 +484,7 @@ const pickedEvents = computed({
})
const isMobile = computed(() => {
return agendaStore.viewport < 576
return siteStore.viewport < 576
})
// METHODS

View file

@ -198,6 +198,8 @@ import {
} from 'naive-ui'
import { useAgendaStore } from './store'
import { useSiteStore } from '../shared/store'
import timezones from '../shared/timezones'
// MESSAGE PROVIDER
@ -207,6 +209,7 @@ const message = useMessage()
// STORES
const agendaStore = useAgendaStore()
const siteStore = useSiteStore()
// STATE
@ -266,7 +269,7 @@ const calcOffset = computed(() => {
return agendaStore.nowDebugDiff ? JSON.stringify(agendaStore.nowDebugDiff.toObject()) : 'None'
})
const panelWidth = computed(() => {
return agendaStore.viewport > 500 ? 500 : agendaStore.viewport
return siteStore.viewport > 500 ? 500 : siteStore.viewport
})
// WATCHERS

View file

@ -59,13 +59,18 @@ import find from 'lodash/find'
import xslugify from '../shared/xslugify'
import { DateTime } from 'luxon'
import { useRoute, useRouter } from 'vue-router'
import { useAgendaStore } from './store'
import { useSiteStore } from '../shared/store'
import MeetingNavigation from './MeetingNavigation.vue'
import './agenda.scss'
// STORES
const agendaStore = useAgendaStore()
const siteStore = useSiteStore()
// ROUTER
@ -144,7 +149,7 @@ watch(() => state.currentRoom, () => {
}, 100)
})
})
watch(() => agendaStore.viewport, () => {
watch(() => siteStore.viewport, () => {
nextTick(() => {
computePlanSizeRatio()
})

View file

@ -10,7 +10,7 @@ ul.nav.nav-tabs.meeting-nav(v-if='agendaStore.isLoaded')
router-link.nav-link(
v-else
active-class='active'
:to='`/meeting/` + agendaStore.meeting.number + `/` + tab.key + `-neue`'
:to='`/meeting/` + agendaStore.meeting.number + `/` + tab.key'
)
i.bi.me-2.d-none.d-sm-inline(:class='tab.icon')
span {{tab.title}}

68
client/agenda/agenda.scss Normal file
View file

@ -0,0 +1,68 @@
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "../shared/breakpoints";
.meeting {
> h1 {
font-weight: 500;
color: $gray-700;
display: flex;
justify-content: space-between;
align-items: center;
@media screen and (max-width: $bs5-break-sm) {
justify-content: center;
> span {
font-size: .95em;
}
}
strong {
font-weight: 700;
background: linear-gradient(220deg, $blue-500 20%, $purple-500 70%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
box-decoration-break: clone;
}
}
&-h1-badges {
display: flex;
justify-content: end;
align-items: center;
> span {
font-size: 13px;
font-weight: 700;
background-color: $pink-500;
box-shadow: 0 0 5px 0 rgba($pink-500, .5);
color: #FFF;
padding: 5px 8px;
border-radius: 6px;
& + span {
margin-left: 10px;
}
}
}
&-warning {
background-color: $red-500 !important;
box-shadow: 0 0 5px 0 rgba($red-500, .5) !important;
color: #FFF;
animation: warningBorderFlash 1s ease infinite;
}
> h4 {
@media screen and (max-width: $bs5-break-sm) {
text-align: center;
> span {
font-size: .8em;
text-align: center;
}
}
}
}

View file

@ -3,6 +3,8 @@ import { DateTime } from 'luxon'
import uniqBy from 'lodash/uniqBy'
import murmur from 'murmurhash-js/murmurhash3_gc'
import { useSiteStore } from '../shared/store'
const urlRe = /http[s]?:\/\/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+/
const conferenceDomains = ['webex.com', 'zoom.us', 'jitsi.org', 'meetecho.com', 'gather.town']
@ -23,7 +25,6 @@ export const useAgendaStore = defineStore('agenda', {
{ hex: '#20c997', tag: 'Attended' }
],
colorAssignments: {},
criticalError: null,
currentTab: 'agenda',
dayIntersectId: '',
defaultCalendarView: 'week',
@ -35,7 +36,6 @@ export const useAgendaStore = defineStore('agenda', {
infoNoteShown: true,
isCurrentMeeting: false,
isLoaded: false,
isMobile: /Mobi/i.test(navigator.userAgent),
listDayCollapse: false,
meeting: {},
nowDebugDiff: null,
@ -50,7 +50,6 @@ export const useAgendaStore = defineStore('agenda', {
settingsShown: false,
timezone: DateTime.local().zoneName,
useHedgeDoc: false,
viewport: Math.round(window.innerWidth),
visibleDays: []
}),
getters: {
@ -119,10 +118,11 @@ export const useAgendaStore = defineStore('agenda', {
})
},
meetingDays () {
const siteStore = useSiteStore()
return uniqBy(this.scheduleAdjusted, 'adjustedStartDate').sort().map(s => ({
slug: s.id.toString(),
ts: s.adjustedStartDate,
label: this.viewport < 1350 ? DateTime.fromISO(s.adjustedStartDate).toFormat('ccc LLL d') : DateTime.fromISO(s.adjustedStartDate).toLocaleString(DateTime.DATE_HUGE)
label: siteStore.viewport < 1350 ? DateTime.fromISO(s.adjustedStartDate).toFormat('ccc LLL d') : DateTime.fromISO(s.adjustedStartDate).toLocaleString(DateTime.DATE_HUGE)
}))
},
isMeetingLive (state) {
@ -168,7 +168,10 @@ export const useAgendaStore = defineStore('agenda', {
this.isLoaded = true
} catch (err) {
console.error(err)
this.criticalError = `Failed to load this meeting: ${err.message}`
const siteStore = useSiteStore()
siteStore.$patch({
criticalError: `Failed to load this meeting: ${err.message}`
})
}
this.hideLoadingScreen()

View file

@ -3,9 +3,12 @@ import { createRouter, createWebHistory } from 'vue-router'
export default createRouter({
history: createWebHistory(),
routes: [
// ---------------------------------------------------------
// MEETING
// ---------------------------------------------------------
{
name: 'agenda',
path: '/meeting/:meetingNumber(\\d+)?/agenda-neue',
path: '/meeting/:meetingNumber(\\d+)?/agenda',
component: () => import('./agenda/Agenda.vue'),
meta: {
hideLeftMenu: true
@ -13,11 +16,18 @@ export default createRouter({
},
{
name: 'floor-plan',
path: '/meeting/:meetingNumber(\\d+)?/floor-plan-neue',
path: '/meeting/:meetingNumber(\\d+)?/floor-plan',
component: () => import('./agenda/FloorPlan.vue'),
meta: {
hideLeftMenu: true
}
},
// -> Redirects
{
path: '/meeting/:meetingNumber(\\d+)?/agenda/personalize',
redirect: to => {
return { name: 'agenda', query: { ...to.query, pick: true } }
}
}
]
})

9
client/shared/store.js Normal file
View file

@ -0,0 +1,9 @@
import { defineStore } from 'pinia'
export const useSiteStore = defineStore('site', {
state: () => ({
criticalError: null,
isMobile: /Mobi/i.test(navigator.userAgent),
viewport: Math.round(window.innerWidth)
})
})

View file

@ -453,7 +453,7 @@ class Room(models.Model):
if not mtg_num:
return None
elif self.floorplan:
base_url = urlreverse('ietf.meeting.views.floor_plan', kwargs=dict(num=mtg_num))
base_url = urlreverse('floor-plan', kwargs=dict(num=mtg_num))
else:
return None
return f'{base_url}?room={xslugify(self.name)}'

File diff suppressed because it is too large Load diff

View file

@ -17,7 +17,7 @@ from pyquery import PyQuery
from lxml.etree import tostring
from io import StringIO, BytesIO
from bs4 import BeautifulSoup
from urllib.parse import urlparse, urlsplit, quote
from urllib.parse import urlparse, urlsplit
from PIL import Image
from pathlib import Path
from tempfile import NamedTemporaryFile
@ -51,7 +51,6 @@ from ietf.name.models import SessionStatusName, ImportantDateName, RoleName, Pro
from ietf.utils.decorators import skip_coverage
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent
from ietf.utils.text import xslugify
from ietf.person.factories import PersonFactory
from ietf.group.factories import GroupFactory, GroupEventFactory, RoleFactory
@ -166,7 +165,7 @@ class MeetingTests(BaseMeetingTestCase):
time_interval = r"%s<span.*/span>-%s" % (slot.utc_start_time().strftime("%H:%M").lstrip("0"), (slot.utc_start_time() + slot.duration).strftime("%H:%M").lstrip("0"))
# Extremely rudementary test of agenda-neue - to be replaced with back-end tests as the front-end tests are developed.
r = self.client.get(urlreverse("agenda-neue", kwargs=dict(num=meeting.number,utc='-utc')))
r = self.client.get(urlreverse("agenda", kwargs=dict(num=meeting.number,utc='-utc')))
self.assertEqual(r.status_code, 200)
# Agenda API tests
@ -213,53 +212,14 @@ class MeetingTests(BaseMeetingTestCase):
}
)
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=meeting.number,utc='-utc')))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
agenda_content = q("#content").html()
self.assertIn(session.group.acronym, agenda_content)
self.assertIn(session.group.name, agenda_content)
self.assertIn(session.group.parent.acronym.upper(), agenda_content)
self.assertIn(slot.location.name, agenda_content)
self.assertRegex(agenda_content, time_interval)
self.assertIsNotNone(q(':input[value="%s"]' % meeting.time_zone),
'Time zone selector should show meeting timezone')
self.assertIsNotNone(q('.nav *:contains("%s")' % meeting.time_zone),
'Time zone indicator should be in nav sidebar')
# plain
time_interval = r"%s<span.*/span>-%s" % (slot.time.strftime("%H:%M").lstrip("0"), (slot.time + slot.duration).strftime("%H:%M").lstrip("0"))
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=meeting.number)))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
agenda_content = q("#content").html()
self.assertIn(session.group.acronym, agenda_content)
self.assertIn(session.group.name, agenda_content)
self.assertIn(session.group.parent.acronym.upper(), agenda_content)
self.assertIn(slot.location.name, agenda_content)
self.assertRegex(agenda_content, time_interval)
self.assertIn(registration_text, agenda_content)
# Make sure there's a frame for the session agenda and it points to the right place
assignment_url = urlreverse('ietf.meeting.views.session_materials', kwargs=dict(session_id=session.pk))
self.assertTrue(
any(
[assignment_url in x.attrib["data-src"]
for x in q('tr div.modal-body div.session-materials')]
)
)
# future meeting, no agenda
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=future_meeting.number)))
self.assertContains(r, "There is no agenda available yet.")
self.assertTemplateUsed(r, 'meeting/no-agenda.html')
# text
# the rest of the results don't have as nicely formatted times
time_interval = "%s-%s" % (slot.time.strftime("%H%M").lstrip("0"), (slot.time + slot.duration).strftime("%H%M").lstrip("0"))
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=meeting.number, ext=".txt")))
r = self.client.get(urlreverse("ietf.meeting.views.agenda_plain", kwargs=dict(num=meeting.number, ext=".txt")))
self.assertContains(r, session.group.acronym)
self.assertContains(r, session.group.name)
self.assertContains(r, session.group.parent.acronym.upper())
@ -267,16 +227,13 @@ class MeetingTests(BaseMeetingTestCase):
self.assertContains(r, time_interval)
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=meeting.number,name=meeting.unofficial_schedule.name,owner=meeting.unofficial_schedule.owner.email())))
self.assertContains(r, 'not the official schedule')
# future meeting, no agenda
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=future_meeting.number, ext=".txt")))
r = self.client.get(urlreverse("ietf.meeting.views.agenda_plain", kwargs=dict(num=future_meeting.number, ext=".txt")))
self.assertContains(r, "There is no agenda available yet.")
self.assertTemplateUsed(r, 'meeting/no-agenda.txt')
# CSV
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=meeting.number, ext=".csv")))
r = self.client.get(urlreverse("ietf.meeting.views.agenda_plain", kwargs=dict(num=meeting.number, ext=".csv")))
self.assertContains(r, session.group.acronym)
self.assertContains(r, session.group.name)
self.assertContains(r, session.group.parent.acronym.upper())
@ -304,30 +261,11 @@ class MeetingTests(BaseMeetingTestCase):
'ietf.meeting.views.session_details',
kwargs=dict(num=meeting.number, acronym=session.group.acronym)),
msg_prefix='ical should contain link to meeting materials page for session')
self.assertContains(
r,
urlreverse(
'ietf.meeting.views.agenda', kwargs=dict(num=meeting.number)
) + f'#row-{session.official_timeslotassignment().slug()}',
msg_prefix='ical should contain link to agenda entry for session')
# week view
r = self.client.get(urlreverse("ietf.meeting.views.week_view", kwargs=dict(num=meeting.number)))
self.assertNotContains(r, 'CANCELLED')
self.assertContains(r, session.group.acronym)
self.assertContains(r, slot.location.name)
self.assertContains(r, registration_text)
# Floor Plan
r = self.client.get(urlreverse('floor-plan', kwargs=dict(num=meeting.number)))
self.assertEqual(r.status_code, 200)
# week view with a cancelled session
SchedulingEvent.objects.create(
session=session,
status=SessionStatusName.objects.get(slug='canceled'),
by=Person.objects.get(name='(System)')
)
r = self.client.get(urlreverse("ietf.meeting.views.week_view", kwargs=dict(num=meeting.number)))
self.assertContains(r, 'CANCELLED')
self.assertContains(r, session.group.acronym)
self.assertContains(r, slot.location.name)
@override_settings(PROCEEDINGS_V1_BASE_URL='https://example.com/{meeting.number}')
def test_agenda_redirects_for_old_meetings(self):
@ -336,7 +274,7 @@ class MeetingTests(BaseMeetingTestCase):
MeetingFactory(type_id='ietf', number='35', populate_schedule=False)
r = self.client.get(
urlreverse(
'ietf.meeting.views.agenda',
'agenda',
kwargs={'num': '35', 'ext': '.html'},
))
self.assertRedirects(r, 'https://example.com/35', fetch_redirect_response=False)
@ -345,7 +283,7 @@ class MeetingTests(BaseMeetingTestCase):
meeting_with_schedule = MeetingFactory(type_id='ietf', number='36', populate_schedule=True)
r = self.client.get(
urlreverse(
'ietf.meeting.views.agenda',
'agenda',
kwargs={'num': '36', 'ext': '.html'},
))
self.assertRedirects(r, 'https://example.com/36', fetch_redirect_response=False)
@ -354,7 +292,7 @@ class MeetingTests(BaseMeetingTestCase):
SessionFactory(meeting=meeting_with_schedule)
r = self.client.get(
urlreverse(
'ietf.meeting.views.agenda',
'agenda',
kwargs={'num': '36', 'ext': '.html'},
))
self.assertRedirects(r, 'https://example.com/36', fetch_redirect_response=False)
@ -364,201 +302,10 @@ class MeetingTests(BaseMeetingTestCase):
# Meetings pre-64 are redirected, but should be a 404 if there is no Meeting instance
r = self.client.get(
urlreverse(
'ietf.meeting.views.agenda',
'agenda',
kwargs={'num': '32', 'ext': '.html'},
))
self.assertEqual(r.status_code, 404)
# Check a post-64 meeting as well
r = self.client.get(
urlreverse(
'ietf.meeting.views.agenda',
kwargs={'num': '150', 'ext': '.html'},
))
self.assertEqual(r.status_code, 404)
def test_meeting_agenda_filters_ignored(self):
"""The agenda view should ignore filter querystrings
(They are handled by javascript on the front end)
"""
meeting = make_meeting_test_data()
expected_items = meeting.schedule.assignments.exclude(timeslot__type__in=['lead','offagenda'])
expected_rows = ['row-%s' % item.slug() for item in expected_items]
r = self.client.get(urlreverse('ietf.meeting.views.agenda'))
for row_id in expected_rows:
self.assertContains(r, row_id)
r = self.client.get(urlreverse('ietf.meeting.views.agenda') + '?show=mars')
for row_id in expected_rows:
self.assertContains(r, row_id)
r = self.client.get(urlreverse('ietf.meeting.views.agenda') + '?show=mars&hide=ames,mars,plenary,ietf,bof')
for row_id in expected_rows:
self.assertContains(r, row_id)
def test_agenda_iab_session(self):
date = datetime.date.today()
meeting = MeetingFactory(type_id='ietf', date=date )
make_meeting_test_data(meeting=meeting)
iab = Group.objects.get(acronym='iab')
venus = Group.objects.create(
name="Three letter acronym",
acronym="venus",
description="This group discusses exploration of Venus",
state_id="active",
type_id="program",
parent=iab,
list_email="venus@ietf.org",
)
venus_session = SessionFactory(
meeting=meeting,
group=venus,
attendees=10,
requested_duration=datetime.timedelta(minutes=60),
add_to_schedule=False,
)
system_person = Person.objects.get(name="(System)")
SchedulingEvent.objects.create(session=venus_session, status_id='schedw', by=system_person)
room = Room.objects.create(meeting=meeting,
name="Aphrodite",
capacity=100,
functional_name="Aphrodite Room")
room.session_types.add('regular')
session_date = meeting.date + datetime.timedelta(days=1)
slot3 = TimeSlot.objects.create(meeting=meeting, type_id='regular', location=room,
duration=datetime.timedelta(minutes=60),
time=datetime.datetime.combine(session_date, datetime.time(13, 30)))
SchedTimeSessAssignment.objects.create(timeslot=slot3, session=venus_session, schedule=meeting.schedule)
url = urlreverse('ietf.meeting.views.agenda', kwargs=dict(num=meeting.number))
r = self.client.get(url)
self.assertContains(r, 'venus')
q = PyQuery(r.content)
venus_row = q('[id*="-iab-"]').html()
self.assertIn('venus', venus_row)
def test_agenda_current_audio(self):
date = datetime.date.today()
meeting = MeetingFactory(type_id='ietf', date=date )
make_meeting_test_data(meeting=meeting)
url = urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=meeting.number))
r = self.client.get(url)
self.assertContains(r, "Audio stream")
def test_agenda_by_room(self):
meeting = make_meeting_test_data()
url = urlreverse("ietf.meeting.views.agenda_by_room",kwargs=dict(num=meeting.number))
login_testing_unauthorized(self,"secretary",url)
r = self.client.get(url)
self.assertTrue(all([x in unicontent(r) for x in ['mars','IESG Breakfast','Test Room','Breakfast Room']]))
url = urlreverse("ietf.meeting.views.agenda_by_room",kwargs=dict(num=meeting.number,name=meeting.unofficial_schedule.name,owner=meeting.unofficial_schedule.owner.email()))
r = self.client.get(url)
self.assertTrue(all([x in unicontent(r) for x in ['mars','Test Room',]]))
self.assertNotContains(r, 'IESG Breakfast')
def test_agenda_by_type(self):
meeting = make_meeting_test_data()
url = urlreverse("ietf.meeting.views.agenda_by_type",kwargs=dict(num=meeting.number))
login_testing_unauthorized(self,"secretary",url)
r = self.client.get(url)
self.assertTrue(all([x in unicontent(r) for x in ['mars','IESG Breakfast','Test Room','Breakfast Room']]))
url = urlreverse("ietf.meeting.views.agenda_by_type",kwargs=dict(num=meeting.number,name=meeting.unofficial_schedule.name,owner=meeting.unofficial_schedule.owner.email()))
r = self.client.get(url)
self.assertTrue(all([x in unicontent(r) for x in ['mars','Test Room',]]))
self.assertNotContains(r, 'IESG Breakfast')
url = urlreverse("ietf.meeting.views.agenda_by_type",kwargs=dict(num=meeting.number,type='regular'))
r = self.client.get(url)
self.assertTrue(all([x in unicontent(r) for x in ['mars','Test Room']]))
self.assertFalse(any([x in unicontent(r) for x in ['IESG Breakfast','Breakfast Room']]))
url = urlreverse("ietf.meeting.views.agenda_by_type",kwargs=dict(num=meeting.number,type='lead'))
r = self.client.get(url)
self.assertFalse(any([x in unicontent(r) for x in ['mars','Test Room']]))
self.assertTrue(all([x in unicontent(r) for x in ['IESG Breakfast','Breakfast Room']]))
url = urlreverse("ietf.meeting.views.agenda_by_type",kwargs=dict(num=meeting.number,type='lead',name=meeting.unofficial_schedule.name,owner=meeting.unofficial_schedule.owner.email()))
r = self.client.get(url)
self.assertFalse(any([x in unicontent(r) for x in ['IESG Breakfast','Breakfast Room']]))
def test_agenda_week_view(self):
meeting = make_meeting_test_data()
url = urlreverse("ietf.meeting.views.week_view",kwargs=dict(num=meeting.number)) + "?show=farfut"
r = self.client.get(url)
self.assertEqual(r.status_code,200)
self.assertTrue(all([x in unicontent(r) for x in ['redraw_weekview', 'draw_calendar', ]]))
# Specifying a time zone should not change the output (time zones are handled by the JS)
url = urlreverse("ietf.meeting.views.week_view",kwargs=dict(num=meeting.number)) + "?show=farfut&" + quote("tz=Asia/Bangkok", safe='=')
r_with_tz = self.client.get(url)
self.assertEqual(r_with_tz.status_code,200)
self.assertEqual(r.content, r_with_tz.content)
def test_agenda_personalize(self):
"""Session selection page should have a checkbox for each session with appropriate keywords"""
meeting = make_meeting_test_data()
url = urlreverse("ietf.meeting.views.agenda_personalize",kwargs=dict(num=meeting.number))
r = self.client.get(url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)
for assignment in SchedTimeSessAssignment.objects.filter(
schedule__in=[meeting.schedule, meeting.schedule.base],
session__on_agenda=True,
):
row = q('#row-{}'.format(assignment.slug()))
self.assertIsNotNone(row, 'No row for assignment {}'.format(assignment))
checkboxes = row('input[type="checkbox"][name="selected-sessions"]')
self.assertEqual(len(checkboxes), 1,
'Row for assignment {} does not have a checkbox input'.format(assignment))
checkbox = checkboxes.eq(0)
kw_token = assignment.session.docname_token_only_for_multiple()
self.assertEqual(
checkbox.attr('data-filter-item'),
assignment.session.group.acronym.lower() + (
'' if kw_token is None else f'-{kw_token}'
)
)
def test_agenda_personalize_updates_urls(self):
"""The correct URLs should be updated when filter settings change on the personalize agenda view
Tests that the expected elements have the necessary classes. The actual update of these fields
is tested in the JS tests
"""
meeting = make_meeting_test_data()
url = urlreverse("ietf.meeting.views.agenda_personalize",kwargs=dict(num=meeting.number))
r = self.client.get(url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)
# Find all the elements expected to be updated
expected_elements = []
nav_tab_anchors = q('ul.nav.nav-tabs > li > a')
for anchor in nav_tab_anchors.items():
text = anchor.text().strip()
if text in ['Agenda (New)', 'Agenda', 'UTC agenda', 'Personalize agenda']:
expected_elements.append(anchor)
for btn in q('.buttonlist a.btn').items():
text = btn.text().strip()
if text in ['View personal agenda', 'Download .ics of filtered agenda', 'Subscribe to filtered agenda']:
expected_elements.append(btn)
# Check that all the expected elements have the correct classes
for elt in expected_elements:
self.assertTrue(elt.has_class('agenda-link'))
self.assertTrue(elt.has_class('filterable'))
# Finally, check that there are no unexpected elements marked to be updated.
# If there are, they should be added to the test above.
self.assertEqual(len(expected_elements),
len(q('.agenda-link.filterable')),
'Unexpected elements updated')
@override_settings(MEETING_MATERIALS_SERVE_LOCALLY=False, MEETING_DOC_HREFS = settings.MEETING_DOC_CDN_HREFS)
def test_materials_through_cdn(self):
@ -822,40 +569,6 @@ class MeetingTests(BaseMeetingTestCase):
self.assertContains(r, t1.time.strftime('%Y%m%dT%H%M%S'))
self.assertNotContains(r, t2.time.strftime('%Y%m%dT%H%M%S'))
def test_meeting_agenda_has_static_ical_links(self):
"""Links to the agenda_ical view must appear on the agenda page
Confirms that these have the correct querystrings. Does not test the JS-based
'Customized schedule' button.
"""
meeting = make_meeting_test_data()
# get the agenda
url = urlreverse('ietf.meeting.views.agenda', kwargs=dict(num=meeting.number))
r = self.client.get(url)
# Check that it has the links we expect
ical_url = urlreverse('ietf.meeting.views.agenda_ical', kwargs=dict(num=meeting.number))
q = PyQuery(r.content)
content = q('#content').html()
assignments = meeting.schedule.assignments.exclude(timeslot__type__in=['lead', 'offagenda'])
# Assume the test meeting is not using historic groups
groups = [a.session.group for a in assignments if a.session is not None]
for g in groups:
if g.parent_id is not None:
self.assertIn('%s?show=%s' % (ical_url, g.parent.acronym.lower()), content)
# The 'non-area events' are those whose keywords are in the last column of buttons
na_col = q('#customize .col-1:last') # find the column
non_area_labels = [e.attrib['data-filter-item']
for e in na_col.find('button.pickview')]
assert len(non_area_labels) > 0 # test setup must produce at least one label for this test
# Should be a 'non-area events' link showing appropriate types
self.assertIn('%s?show=%s' % (ical_url, ','.join(non_area_labels).lower()), content)
def test_parse_agenda_filter_params(self):
def _r(show=(), hide=(), showtypes=(), hidetypes=()):
"""Helper to create expected result dict"""
@ -4008,12 +3721,6 @@ class SessionDetailsTests(TestCase):
self.assertTrue(all([x in unicontent(r) for x in ('slides','agenda','minutes','draft')]))
self.assertNotContains(r, 'deleted')
q = PyQuery(r.content)
self.assertTrue(q('div#session-buttons-%s' % session.id),
'Session detail page does not contain session tool buttons')
self.assertFalse(q('div#session-buttons-%s span.bi-arrows-fullscreen' % session.id),
'The session detail page is incorrectly showing the "Show meeting materials" button')
def test_session_details_has_import_minutes_buttons(self):
group = GroupFactory.create(
type_id='wg',
@ -5684,25 +5391,6 @@ class AjaxTests(TestCase):
self.assertNotIn('error', data)
self.assertEqual(data['utc'], '20:00')
class FloorPlanTests(TestCase):
def test_floor_plan_page(self):
make_meeting_test_data()
meeting = Meeting.objects.filter(type_id='ietf').order_by('id').last()
floorplan = FloorPlanFactory.create(meeting=meeting)
# Extremely rudimentary test of floor-plan-neue
url = urlreverse('floor-plan-neue')
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
url = urlreverse('ietf.meeting.views.floor_plan')
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
url = urlreverse('ietf.meeting.views.floor_plan', kwargs={'floor': xslugify(floorplan.name)} )
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
class IphoneAppJsonTests(TestCase):
def test_iphone_app_json_interim(self):
make_interim_test_data()

View file

@ -7,6 +7,13 @@ from django.conf import settings
from ietf.meeting import views, views_proceedings
from ietf.utils.urls import url
class AgendaRedirectView(RedirectView):
ignore_kwargs = ('owner', 'name')
def get_redirect_url(self, *args, **kwargs):
for kwarg in self.ignore_kwargs:
kwargs.pop(kwarg, None)
return super().get_redirect_url(*args, **kwargs)
safe_for_all_meeting_types = [
url(r'^session/(?P<acronym>[-a-z0-9]+)/?$', views.session_details),
url(r'^session/(?P<session_id>\d+)/drafts$', views.add_session_drafts),
@ -33,16 +40,13 @@ type_ietf_only_patterns = [
url(r'^agenda/%(owner)s/%(schedule_name)s/delete$' % settings.URL_REGEXPS, views.delete_schedule),
url(r'^agenda/%(owner)s/%(schedule_name)s/make_official$' % settings.URL_REGEXPS, views.make_schedule_official),
url(r'^agenda/%(owner)s/%(schedule_name)s(\.(?P<ext>.html))?/?$' % settings.URL_REGEXPS, views.agenda),
url(r'^agenda/%(owner)s/%(schedule_name)s/week-view(?:.html)?/?$' % settings.URL_REGEXPS, views.week_view),
url(r'^agenda/%(owner)s/%(schedule_name)s/by-room/?$' % settings.URL_REGEXPS, views.agenda_by_room),
url(r'^agenda/%(owner)s/%(schedule_name)s/by-type/?$' % settings.URL_REGEXPS, views.agenda_by_type),
url(r'^agenda/%(owner)s/%(schedule_name)s/by-type/(?P<type>[a-z]+)$' % settings.URL_REGEXPS, views.agenda_by_type),
url(r'^agenda/%(owner)s/%(schedule_name)s/week-view(?:.html)?/?$' % settings.URL_REGEXPS, AgendaRedirectView.as_view(pattern_name='agenda', permanent=True)),
url(r'^agenda/%(owner)s/%(schedule_name)s/by-room/?$' % settings.URL_REGEXPS, AgendaRedirectView.as_view(pattern_name='agenda', permanent=True)),
url(r'^agenda/%(owner)s/%(schedule_name)s/by-type/?$' % settings.URL_REGEXPS, AgendaRedirectView.as_view(pattern_name='agenda', permanent=True)),
url(r'^agenda/%(owner)s/%(schedule_name)s/by-type/(?P<type>[a-z]+)$' % settings.URL_REGEXPS, AgendaRedirectView.as_view(pattern_name='agenda', permanent=True)),
url(r'^agenda/%(owner)s/%(schedule_name)s/new/$' % settings.URL_REGEXPS, views.new_meeting_schedule),
url(r'^agenda/by-room$', views.agenda_by_room),
url(r'^agenda/by-type$', views.agenda_by_type),
url(r'^agenda/by-type/(?P<type>[a-z]+)$', views.agenda_by_type),
url(r'^agenda/by-type/(?P<type>[a-z]+)/ics$', views.agenda_by_type_ics),
url(r'^agenda/personalize', views.agenda_personalize),
url(r'^agenda/personalize', views.agenda, name='agenda-personalize'),
url(r'^agendas/list$', views.list_schedules),
url(r'^agendas/edit$', RedirectView.as_view(pattern_name='ietf.meeting.views.list_schedules', permanent=True)),
url(r'^agendas/diff/$', views.diff_schedules),
@ -64,10 +68,9 @@ type_interim_patterns = [
]
type_ietf_only_patterns_id_optional = [
url(r'^agenda(?P<utc>-utc)?(?P<ext>\.html)?/?$', views.agenda),
url(r'^agenda(?P<ext>\.txt)$', views.agenda),
url(r'^agenda(?P<ext>\.csv)$', views.agenda),
url(r'^agenda-neue(?P<utc>-utc)?(?P<ext>\.html)?/?$', views.agenda_neue, name='agenda-neue'),
url(r'^agenda(?P<utc>-utc)?(?P<ext>\.html)?/?$', views.agenda, name='agenda'),
url(r'^agenda(?P<ext>\.txt)$', views.agenda_plain),
url(r'^agenda(?P<ext>\.csv)$', views.agenda_plain),
url(r'^agenda/edit$',
RedirectView.as_view(pattern_name='ietf.meeting.views.edit_meeting_schedule', permanent=True),
name='ietf.meeting.views.edit_meeting_schedule'),
@ -76,11 +79,10 @@ type_ietf_only_patterns_id_optional = [
url(r'^agenda/agenda\.ics$', views.agenda_ical),
url(r'^agenda\.ics$', views.agenda_ical),
url(r'^agenda.json$', views.agenda_json),
url(r'^agenda/week-view(?:.html)?/?$', views.week_view),
url(r'^floor-plan/?$', views.floor_plan),
url(r'^floor-plan-neue/?$', views.agenda_neue, name='floor-plan-neue'),
url(r'^floor-plan/(?P<floor>[-a-z0-9_]+)/?$', views.floor_plan),
url(r'^week-view(?:.html)?/?$', views.week_view),
url(r'^agenda/week-view(?:.html)?/?$', RedirectView.as_view(pattern_name='agenda', permanent=True)),
url(r'^floor-plan/?$', views.agenda, name='floor-plan'),
url(r'^floor-plan/(?P<floor>[-a-z0-9_]+)/?$', RedirectView.as_view(pattern_name='floor-plan', permanent=True)),
url(r'^week-view(?:.html)?/?$', RedirectView.as_view(pattern_name='agenda', permanent=True)),
url(r'^materials(?:.html)?/?$', views.materials),
url(r'^request_minutes/?$', views.request_minutes),
url(r'^materials/%(document)s((?P<ext>\.[a-z0-9]+)|/)?$' % settings.URL_REGEXPS, views.materials_document),

View file

@ -1502,11 +1502,10 @@ def get_assignments_for_agenda(schedule):
@ensure_csrf_cookie
def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""):
def agenda_plain(request, num=None, name=None, base=None, ext=None, owner=None, utc=""):
base = base if base else 'agenda'
ext = ext if ext else '.html'
ext = ext if ext else '.txt'
mimetype = {
".html":"text/html; charset=%s"%settings.DEFAULT_CHARSET,
".txt": "text/plain; charset=%s"%settings.DEFAULT_CHARSET,
".csv": "text/csv; charset=%s"%settings.DEFAULT_CHARSET,
}
@ -1570,7 +1569,7 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
return rendered_page
@ensure_csrf_cookie
def agenda_neue(request, num=None, name=None, base=None, ext=None, owner=None, utc=""):
def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""):
# Get current meeting if not specified
if num is None:
num = get_current_ietf_meeting_num()
@ -1586,7 +1585,7 @@ def agenda_neue(request, num=None, name=None, base=None, ext=None, owner=None, u
else:
return HttpResponseRedirect(f'{settings.PROCEEDINGS_V1_BASE_URL.format(meeting=meeting)}')
return render(request, "meeting/agenda-neue.html", {
return render(request, "meeting/agenda.html", {
"meetingData": {
"meetingNumber": num
}
@ -1834,45 +1833,6 @@ def agenda_csv(schedule, filtered_assignments):
return response
@role_required('Area Director','Secretariat','IAB')
def agenda_by_room(request, num=None, name=None, owner=None):
meeting = get_meeting(num)
if name is None:
schedule = get_schedule(meeting)
else:
person = get_person_by_email(owner)
schedule = get_schedule_by_name(meeting, person, name)
assignments = SchedTimeSessAssignment.objects.filter(
schedule__in=[schedule, schedule.base if schedule else None]
).prefetch_related('timeslot', 'timeslot__location', 'session', 'session__group', 'session__group__parent')
ss_by_day = OrderedDict()
for day in assignments.dates('timeslot__time','day'):
ss_by_day[day]=[]
for ss in assignments.order_by('timeslot__location__functional_name','timeslot__location__name','timeslot__time'):
day = ss.timeslot.time.date()
ss_by_day[day].append(ss)
return render(request,"meeting/agenda_by_room.html",{"meeting":meeting,"schedule":schedule,"ss_by_day":ss_by_day})
@role_required('Area Director','Secretariat','IAB')
def agenda_by_type(request, num=None, type=None, name=None, owner=None):
meeting = get_meeting(num)
if name is None:
schedule = get_schedule(meeting)
else:
person = get_person_by_email(owner)
schedule = get_schedule_by_name(meeting, person, name)
assignments = SchedTimeSessAssignment.objects.filter(
schedule__in=[schedule, schedule.base if schedule else None]
).prefetch_related(
'timeslot', 'timeslot__location', 'session', 'session__group', 'session__group__parent'
).order_by('session__type__slug','timeslot__time','session__group__acronym')
if type:
assignments = assignments.filter(session__type__slug=type)
return render(request,"meeting/agenda_by_type.html",{"meeting":meeting,"schedule":schedule,"assignments":assignments})
@role_required('Area Director','Secretariat','IAB')
def agenda_by_type_ics(request,num=None,type=None):
meeting = get_meeting(num)
@ -1887,42 +1847,6 @@ def agenda_by_type_ics(request,num=None,type=None):
updated = meeting.updated()
return render(request,"meeting/agenda.ics",{"schedule":schedule,"updated":updated,"assignments":assignments},content_type="text/calendar")
def agenda_personalize(request, num):
meeting = get_ietf_meeting(num) # num may be None, which requests the current meeting
if meeting is None or meeting.schedule is None:
raise Http404('No such meeting')
# Select and prepare sessions that should be included
filtered_assignments = preprocess_assignments_for_agenda(
get_assignments_for_agenda(meeting.schedule),
meeting
)
tagger = AgendaKeywordTagger(assignments=filtered_assignments)
tagger.apply() # annotate assignments with filter_keywords attribute
tagger.apply_session_keywords() # annotate assignments with session_keyword attribute
# Now prep the filter UI
filter_organizer = AgendaFilterOrganizer(assignments=filtered_assignments)
is_current_meeting = (num is None) or (num == get_current_ietf_meeting_num())
return render(
request,
"meeting/agenda.html",
{
'personalize': True,
'schedule': meeting.schedule,
'updated': meeting.updated(),
'filtered_assignments': filtered_assignments,
'filter_categories': filter_organizer.get_filter_categories(),
'non_area_labels': filter_organizer.get_non_area_keywords(),
'timezone': meeting.time_zone,
'is_current_meeting': is_current_meeting,
'cache_time': 150 if is_current_meeting else 3600,
}
)
def session_draft_list(num, acronym):
try:
agendas = Document.objects.filter(type="agenda",
@ -2025,70 +1949,6 @@ def session_draft_pdf(request, num, acronym):
os.unlink(pdfn)
return HttpResponse(pdf_contents, content_type="application/pdf")
def week_view(request, num=None, name=None, owner=None):
meeting = get_meeting(num)
if name is None:
schedule = get_schedule(meeting)
else:
person = get_person_by_email(owner)
schedule = get_schedule_by_name(meeting, person, name)
if not schedule:
raise Http404
filtered_assignments = SchedTimeSessAssignment.objects.filter(
schedule__in=[schedule, schedule.base],
session__on_agenda=True,
)
filtered_assignments = preprocess_assignments_for_agenda(filtered_assignments, meeting)
AgendaKeywordTagger(assignments=filtered_assignments).apply()
items = []
for a in filtered_assignments:
# we don't HTML escape any of these as the week-view code is using createTextNode
item = {
"key": str(a.timeslot.pk),
"utc_time": a.timeslot.utc_start_time().strftime("%Y%m%dT%H%MZ"), # ISO8601 compliant
"duration": a.timeslot.duration.seconds,
"type": a.slot_type().name,
"filter_keywords": ",".join(a.filter_keywords),
}
if a.session:
if a.session.historic_group:
item["group"] = a.session.historic_group.acronym
if a.session.name:
item["name"] = a.session.name
elif a.slot_type().slug == "break":
item["name"] = a.timeslot.name
item["area"] = a.slot_type().slug
item["group"] = a.slot_type().slug
elif a.session.historic_group:
item["name"] = a.session.historic_group.name
if a.session.historic_group.state_id == "bof":
item["name"] += " BOF"
item["state"] = a.session.historic_group.state.name
if a.session.historic_group.historic_parent:
item["area"] = a.session.historic_group.historic_parent.acronym
if a.timeslot.show_location:
item["room"] = a.timeslot.get_location()
if a.session and a.session.agenda():
item["agenda"] = a.session.agenda().get_href()
if a.session.current_status == 'canceled':
item["name"] = "CANCELLED - " + item["name"]
items.append(item)
return render(request, "meeting/week-view.html", {
"items": json.dumps(items),
})
def ical_session_status(assignment):
if assignment.session.current_status == 'canceled':
return "CANCELLED"
@ -3694,24 +3554,6 @@ def upcoming_json(request):
response = HttpResponse(json.dumps(data, indent=2, sort_keys=False), content_type='application/json;charset=%s'%settings.DEFAULT_CHARSET)
return response
def floor_plan(request, num=None, floor=None, ):
meeting = get_meeting(num)
schedule = meeting.schedule
floors = FloorPlan.objects.filter(meeting=meeting).order_by('order')
if floor:
floors = [ f for f in floors if xslugify(f.name) == floor ]
for floor in floors:
try:
floor.image.width
except FileNotFoundError:
raise Http404('Missing floorplan image for %s' % floor)
return render(request, 'meeting/floor-plan.html', {
"meeting": meeting,
"schedule": schedule,
"number": num,
"floors": floors,
})
def proceedings(request, num=None):
meeting = get_meeting(num)

View file

@ -1,3 +1,3 @@
<ul>
<li><a href="{% url 'ietf.meeting.views.agenda' num=meeting.number ext='.txt' %}" target="_blank">View Agenda</a>.</li>
<li><a href="{% url 'ietf.meeting.views.agenda_plain' num=meeting.number ext='.txt' %}" target="_blank">View Agenda</a>.</li>
</ul>

View file

@ -236,13 +236,7 @@
{% endif %}
<li>
<a class="dropdown-item {% if flavor != 'top' %}text-wrap link-primary{% endif %}"
href="{% url 'agenda-neue' %}">
Agenda (New)
</a>
</li>
<li>
<a class="dropdown-item {% if flavor != 'top' %}text-wrap link-primary{% endif %}"
href="{% url 'ietf.meeting.views.agenda' %}">
href="{% url 'agenda' %}">
Agenda
</a>
</li>
@ -254,7 +248,7 @@
</li>
<li>
<a class="dropdown-item {% if flavor != 'top' %}text-wrap link-primary{% endif %}"
href="{% url 'ietf.meeting.views.floor_plan' %}">
href="{% url 'floor-plan' %}">
Floor plan
</a>
</li>

View file

@ -1,79 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015-2021, All Rights Reserved #}
{% load origin %}
{% load static %}
{% load ietf_filters %}
{% load textfilters %}
{% load htmlfilters agenda_custom_tags %}
{% load django_vite %}
{% block title %}
IETF {{ meetingData.meetingNumber }} Meeting Agenda
{% endblock %}
{% block pagehead %}
<!-- AGENDA VUE COMPONENT -->
<!-- [html-validate-disable-block void-style, attribute-empty-style] -->
{{ meetingData|json_script:"meeting-data" }}
{% vite_asset 'client/main.js' %}
{% endblock %}
{% block morecss %}
body {
font-family: 'Montserrat', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
@keyframes initspinner {
to { transform: rotate(360deg); }
}
#app-loading {
position: fixed;
top: 60px;
left: 0;
width: 100%;
height: calc(100% - 60px);
background-color: rgba(255,255,255,.75);
z-index: 2000000000;
backdrop-filter: blur(10px);
}
#app-loading:before {
content: '';
box-sizing: border-box;
position: absolute;
top: 50%;
left: 50%;
width: 50px;
height: 50px;
margin-top: -25px;
margin-left: -25px;
border-radius: 50%;
border-top: 2px solid #999;
border-right: 2px solid transparent;
animation: initspinner .6s linear infinite;
z-index: 2000000000;
}
#app-loading:after {
content: 'Loading meeting {{ meetingData.meetingNumber }}...';
position: absolute;
text-align: center;
top: 50%;
margin-top: -100px;
left: 0;
right: 0;
font-weight: 500;
color: #999;
z-index: 2000000000;
}
{% endblock %}
{% block precontent %}
<div class="meeting-switch">
<i class="bi bi-arrow-left-right me-2"></i>
<a href="{% url 'ietf.meeting.views.agenda' num=meetingData.meetingNumber %}">Switch to Legacy Agenda Display</a>
</div>
{% endblock %}
{% block content %}
{% origin %}
<div id="app"></div>
<div id="app-loading"></div>
{% endblock %}

View file

@ -5,529 +5,69 @@
{% load ietf_filters %}
{% load textfilters %}
{% load htmlfilters agenda_custom_tags %}
{% load django_vite %}
{% block title %}
IETF {{ schedule.meeting.number }} Meeting Agenda
{% if "-utc" in request.path %}(UTC){% endif %}
{% if personalize %}Personalization{% endif %}
IETF {{ meetingData.meetingNumber }} Meeting Agenda
{% endblock %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
<!-- AGENDA VUE COMPONENT -->
<!-- [html-validate-disable-block void-style, attribute-empty-style] -->
{{ meetingData|json_script:"meeting-data" }}
{% vite_asset 'client/main.js' %}
{% endblock %}
{% block morecss %}#weekview iframe { height: 25em; }{% endblock %}
{% block precontent %}
<div class="meeting-switch">
<i class="bi bi-arrow-left-right me-2"></i>
<a href="{% url 'agenda-neue' num=schedule.meeting.number %}">Switch to New Agenda Display</a>
</div>
{% block morecss %}
body {
font-family: 'Montserrat', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
@keyframes initspinner {
to { transform: rotate(360deg); }
}
#app-loading {
position: fixed;
top: 60px;
left: 0;
width: 100%;
height: calc(100% - 60px);
background-color: rgba(255,255,255,.75);
z-index: 2000000000;
backdrop-filter: blur(10px);
}
#app-loading:before {
content: '';
box-sizing: border-box;
position: absolute;
top: 50%;
left: 50%;
width: 50px;
height: 50px;
margin-top: -25px;
margin-left: -25px;
border-radius: 50%;
border-top: 2px solid #999;
border-right: 2px solid transparent;
animation: initspinner .6s linear infinite;
z-index: 2000000000;
}
#app-loading:after {
content: 'Loading meeting {{ meetingData.meetingNumber }}...';
position: absolute;
text-align: center;
top: 50%;
margin-top: -100px;
left: 0;
right: 0;
font-weight: 500;
color: #999;
z-index: 2000000000;
}
{% endblock %}
{% block content %}
{% origin %}
{% 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 %}
{# the contents of #extra-nav will be moved into the RH nav panel #}
<div id="extra-nav" class="d-none">
<div class="d-flex flex-column px-2">
{% if now.date <= schedule.meeting.end_date %}
<a class="btn btn-sm btn-primary my-3 now-link" href="#">Jump to current session</a>
{% endif %}
<div class="d-flex">
{% include 'meeting/tz-display.html' with id_suffix="-rh" meeting_timezone=timezone minimal=True only %}
</div>
<div class="small text-muted">
Showing <span class="current-tz">{{ timezone|split:"_"|join:" "|split:"/"|join:" / " }}</span> time
</div>
</div>
</div>
{# cache this part -- it takes 3-6 seconds to generate #}
{% load cache %}
{% cache cache_time ietf_meeting_agenda_utc schedule.meeting.number request.path %}
<h2>
{% if personalize %}
Session selection
{% else %}
Agenda
{% endif %}
</h2>
{% if is_current_meeting %}
<p class="alert alert-info my-3">
<b>Note:</b> IETF agendas are subject to change, up to and during a meeting.
</p>
{% endif %}
{% if schedule.meeting.agenda_info_note %}
<p class="alert alert-info my-3">
{{ schedule.meeting.agenda_info_note|removetags:"h1"|safe }}
</p>
{% endif %}
{% include 'meeting/tz-display.html' with id_suffix="" meeting_timezone=timezone only %}
{% include "meeting/agenda_filter.html" with filter_categories=filter_categories customize_button_text="Filter this agenda view..." always_show=personalize %}
{% include "meeting/agenda_personalize_buttonlist.html" with meeting=schedule.meeting personalize=personalize only %}
<div class="input-group mb-3">
<button class="btn btn-outline-primary dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
{% if filter_categories|length < 3 %}disabled{% endif %}>
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" %}
<li>
<a class="dropdown-item"
href="{% url "ietf.meeting.views.agenda_ical" num=schedule.meeting.number %}?show={{ p.keyword }}">
{{ p.label }}
</a>
</li>
{% endfor %}
{% endif %}
{% endfor %}
</ul>
<a class="btn btn-outline-primary {% if non_area_keywords|length == 0 %}disabled{% endif %}"
href="{% if non_area_keywords %}{% url 'ietf.meeting.views.agenda_ical' num=schedule.meeting.number %}?show={{ non_area_keywords|join:',' }}{% else %}#{% endif %}">
Download non-area events
</a>
</div>
<div id="weekview" class="d-none mt-3">
<h2>
Schedule
{% if schedule.meeting.agenda_warning_note %}
<span class="badge rounded-pill bg-danger">
{{ schedule.meeting.agenda_warning_note|removetags:"h1" |safe }}
</span>
{% endif %}
</h2>
<iframe title="Schedule" class="w-100 overflow-hidden border border-dark"></iframe>
</div>
<h2 class="mt-3">
{% if personalize %}Personalize{% endif %}
Detailed Agenda
{% if schedule.meeting.agenda_warning_note %}
<span class="badge rounded-pill bg-danger">
{{ schedule.meeting.agenda_warning_note|removetags:"h1" |safe }}
</span>
{% endif %}
</h2>
{% if personalize %}
<p>
Check boxes below to select individual sessions.
</p>
{% endif %}
<table id="agenda-table" class="table table-sm tablesorter">
<thead>
<tr>
{% if personalize %}<th scope="col"></th>{% endif %}
<th scope="col"></th>
<th scope="col" data-sort="loc"></th>
<th scope="col" data-sort="group"></th>
<th scope="col" data-sort="area"></th>
<th scope="col" data-sort="desc"></th>
</tr>
</thead>
<tbody>
{% for item in filtered_assignments %}
{% ifchanged item.timeslot.time|date:"Y-m-d" %}
<tr class="table-primary show-with-children">
<th scope="col" colspan="{% if personalize %}6{% else %}5{% endif %}"
id="slot-{{ item.timeslot.time|slugify }}"
class="nav-heading">
{{ item.timeslot.time|date:"l, F j, Y" }}
</th>
</tr>
{% endifchanged %}
{% if item|is_special_agenda_item %}
<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 }}">
{% if personalize %}
<td class="text-center">
{% if item.session_keyword %}
<label class="d-none"
aria-label="Select session"
for="{{ item.session_keyword }}-{{ item.slug }}">
</label>
<input type="checkbox"
class="pickview form-check-input"
title="Select session"
name="selected-sessions"
id="{{ item.session_keyword }}-{{ item.slug }}"
value="{{ item.session_keyword }}"
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-filter-item="{{ item.session_keyword }}">
{% endif %}
</td>
{% endif %}
<td class="text-end">{% include "meeting/timeslot_start_end.html" %}</td>
<td colspan="3">
{% if item.timeslot.show_location and item.timeslot.location %}
{% location_anchor item.timeslot %}
{{ item.timeslot.get_html_location }}
{% end_location_anchor %}
{% endif %}
{% if item.timeslot.show_location and item.timeslot.get_html_location %}
{% with item.timeslot.location.floorplan as floor %}
{% if item.timeslot.location.floorplan %}
<div class="d-none d-sm-block float-end">
<a href="{% url 'ietf.meeting.views.floor_plan' num=schedule.meeting.number %}#floor-{{ floor.name|xslugify }}"
class="float-end"
title="{{ floor.name }}">
<span class="badge rounded-pill bg-secondary">{{ floor.short }}</span>
</a>
</div>
{% endif %}
{% endwith %}
{% endif %}
</td>
<td>
{% agenda_anchor item.session %}
{% assignment_display_name item %}
{% end_agenda_anchor %}
{% if item.session.current_status == 'canceled' %}
<span class="badge rounded-pill bg-danger float-end">CANCELLED</span>
{% else %}
{% if item.slot_type.slug == 'other' %}
{% if item.session.agenda or item.session.remote_instructions or item.session.agenda_note %}
<div class="float-end ps-2">
{% include "meeting/session_buttons_include.html" with show_agenda=True item=item schedule=schedule %}
</div>
{% else %}
<div>
{% for slide in item.session.slides %}
<a href="{{ slide.get_href }}">{{ slide.title|clean_whitespace }}</a>
<br>
{% endfor %}
</div>
{% endif %}
{% endif %}
{% endif %}
</td>
</tr>
{% elif item|is_regular_agenda_item or item|is_plenary_agenda_item %}
{% if item|is_regular_agenda_item %}
{% ifchanged %}
<tr class="table-secondary session-label-row show-with-children"
data-slot-start-ts="{{ item.start_timestamp }}"
data-slot-end-ts="{{ item.end_timestamp }}">
{% if personalize %}<th scope="row" class="text-center"></th>{% endif %}
<th scope="row" class="text-end">{% include "meeting/timeslot_start_end.html" %}</th>
<th scope="row" colspan="4">
{{ item.timeslot.time|date:"l" }}
{{ item.timeslot.name|capfirst_allcaps }}
</th>
</tr>
{% endifchanged %}
{% endif %}
{% if item.session.historic_group %}
<tr id="row-{{ item.slug }}"
{% if item.slot_type.slug == 'plenary' %}class="{{ item.slot_type.slug }}danger"{% endif %}
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-slot-start-ts="{{ item.start_timestamp }}"
data-slot-end-ts="{{ item.end_timestamp }}">
{% if personalize %}
<td class="text-center">
{% if item.session_keyword %}
<label class="d-none"
aria-label="Select session"
for="{{ item.session_keyword }}">
</label>
<input type="checkbox"
class="pickview form-check-input"
title="Select session"
name="selected-sessions"
id="{{ item.session_keyword }}"
value="{{ item.session_keyword }}"
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-filter-item="{{ item.session_keyword }}">
{% endif %}
</td>
{% endif %}
{% if item.slot_type.slug == 'plenary' %}
<td class="text-end">{% include "meeting/timeslot_start_end.html" %}</td>
<td colspan="3">
{% if item.timeslot.show_location and item.timeslot.location %}
{% location_anchor item.timeslot %}
{{ item.timeslot.get_html_location }}
{% end_location_anchor %}
{% endif %}
</td>
{% else %}
<td>
{% with item.timeslot.location.floorplan as floor %}
{% if item.timeslot.location.floorplan %}
<div class="d-none d-sm-block">
<a href="{% url 'ietf.meeting.views.floor_plan' num=schedule.meeting.number %}#floor-{{ floor.name|xslugify }}"
class="float-end"
title="{{ floor.name }}">
<span class="badge rounded-pill bg-secondary">{{ floor.short }}</span>
</a>
</div>
{% endif %}
{% endwith %}
</td>
<td>
{% if item.timeslot.show_location and item.timeslot.location %}
{% location_anchor item.timeslot %}
{{ item.timeslot.get_html_location }}
{% end_location_anchor %}
{% endif %}
</td>
<td>
{% if item.session.historic_group.historic_parent.acronym %}
<div class="d-none d-sm-block">{{ item.session.historic_group.historic_parent.acronym }}</div>
{% endif %}
</td>
<td>
<div class="d-none d-sm-block">
{% 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 %}
</div>
</td>
{% endif %}
<td>
{% if item.session.current_status == 'canceled' %}
<span class="badge rounded-pill bg-danger float-end">Cancelled</span>
{% else %}
<div class="float-end ps-2">
{% include "meeting/session_buttons_include.html" with show_agenda=True session=item.session meeting=schedule.meeting %}
</div>
{% endif %}
<div class="d-sm-none">
{% 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 %}
{% if item.session.historic_group.historic_parent.acronym %}
<span class="text-muted">{{ item.session.historic_group.historic_parent.acronym }}</span>
{% endif %}
</div>
{% agenda_anchor item.session %}
{% assignment_display_name item %}
{% end_agenda_anchor %}
{% if item.session.historic_group.state_id == "bof" %}
<span class="badge rounded-pill bg-success float-end">BOF</span>
{% endif %}
{% if item.session.current_status == 'resched' %}
<div class="badge rounded-pill bg-danger float-end">
Rescheduled
{% if item.session.rescheduled_to %}
TO
<div class="timetooltip reschedtimetooltip">
<div 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" %} data-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 %}
</div>
</div>
{% endif %}
</div>
{% 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 small">{{ item.session.agenda_note }}</span>
{% endif %}
</td>
</tr>
{% endif %}
{% endif %}
{% endfor %}
</tbody>
</table>
{% if personalize %}{# only show second copy of buttons for the personalize tab #}
{% include "meeting/agenda_personalize_buttonlist.html" with meeting=schedule.meeting personalize=personalize only %}
{% endif %}
{% endcache %}
<div id="app"></div>
<div id="app-loading"></div>
{% endblock %}
{% block js %}
<script src="{% static 'ietf/js/agenda_filter.js' %}"></script>
<script>
// Update the agenda display with specified filters
function update_agenda_display(filter_params) {
var agenda_rows=$('[id^="row-"]')
if (!agenda_filter.filtering_is_enabled(filter_params)) {
// When filtering is not enabled, show all sessions
agenda_rows.show();
return;
}
// if groups were selected for filtering, hide all rows by default
agenda_rows.filter(function(index, row) {
return !!$(row).attr('data-filter-keywords');
}).hide();
// loop through the has items and change the UI element and row visibilities accordingly
$.each(filter_params.show, function (i, v) {
// this is a regular item by wg: when present, show these rows
agenda_filter.rows_matching_filter_keyword(agenda_rows, v).show();
});
$.each(filter_params.hide, function (i, v) {
// this is a "negative" item by wg: when present, hide these rows
agenda_filter.rows_matching_filter_keyword(agenda_rows, v).hide();
});
// Now hide any session label rows with no visible sessions. Identify
// by matching on start/end timestamps.
$('tr.session-label-row').each(function(i, e) {
var start_ts = $(e).attr('data-slot-start-ts');
var end_ts = $(e).attr('data-slot-end-ts');
var visible_rows = agenda_rows.filter(
'[data-slot-start-ts="' + start_ts + '"]' +
'[data-slot-end-ts="' + end_ts + '"]' +
':visible'
);
if (visible_rows.length > 0) {
$(e).show();
} else {
$(e).hide();
}
})
}
function update_ical_links(filter_params) {
$(".ical-link").toggleClass("d-none", !agenda_filter.filtering_is_enabled(filter_params));
}
function update_weekview(filter_params) {
var weekview = $("#weekview");
if (agenda_filter.filtering_is_enabled(filter_params)) {
weekview.removeClass("d-none");
} else {
weekview.addClass("d-none");
}
update_weekview_display();
}
function update_weekview_display() {
var weekview = $("#weekview");
if (!weekview.hasClass('d-none')) {
var queryparams = window.location.search;
if (queryparams) {
queryparams += '&tz=' + encodeURIComponent(ietf_timezone.get_current_tz().toLowerCase());
} else {
queryparams = '?tz=' + encodeURIComponent(ietf_timezone.get_current_tz().toLowerCase());
}
var new_url = 'week-view.html' + queryparams;
var wv_iframe = $(weekview).children('iframe');
var wv_window = wv_iframe.contentWindow;
if (wv_iframe.src && wv_window.location.hostname && wv_window.history && wv_window.history.replaceState) {
wv_window.history.replaceState({}, '', new_url);
wv_window.redraw_weekview();
} else {
// either have not yet loaded the iframe or we do not support history replacement
$(wv_iframe).attr("src", new_url);
}
}
}
function update_view(filter_params) {
update_agenda_display(filter_params);
update_weekview(filter_params)
update_ical_links(filter_params)
}
</script>
<script src="{% static 'ietf/js/list.js' %}">
</script>
<script src="{% static 'ietf/js/moment.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_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');
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 %}
$(document).ready(function() {
// Methods/variables here that are not in ietf_timezone or agenda_filter are from agenda_timezone.js
meeting_timezone = '{{ timezone }}';
// First, initialize_moments(). This must be done before calling any of the update methods.
// It does not need timezone info, so safe to call before initializing ietf_timezone.
initialize_moments(); // fills in moments in the agenda data
// Now set up callbacks related to ietf_timezone. This must happen before calling initialize().
// In particular, set_current_tz_cb() must be called before the update methods are called.
set_current_tz_cb(ietf_timezone.get_current_tz); // give agenda_timezone access to this method
ietf_timezone.set_tz_change_callback(function(newtz) {
update_times(newtz);
update_weekview_display();
}
);
// With callbacks in place, call ietf_timezone.initialize(). This will call the tz_change callback
// after setting things up.
{% if "-utc" in request.path %}
ietf_timezone.initialize('UTC');
{% else %}
ietf_timezone.initialize(meeting_timezone);
{% endif %}
// Now make other setup calls from agenda_timezone.js
add_tooltips();
init_timers(speedup);
// 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);
});
document.getElementById('agenda-table')
.addEventListener('click', handleTableClick);
{% else %}
agenda_filter.set_update_callback(update_view);
{% endif %}
agenda_filter.enable();
}
);
</script>
{% endblock %}

View file

@ -23,6 +23,6 @@ DESCRIPTION:{{item.timeslot.name|ics_esc}}\n{% if item.session.agenda_note %}
\n
Session materials: {% absurl 'ietf.meeting.views.session_details' num=schedule.meeting.number acronym=item.session.group.acronym %}\n{% if schedule.meeting.get_number is not None %}
\n{# link agenda for ietf meetings #}
See in schedule: {% absurl 'ietf.meeting.views.agenda' num=schedule.meeting.number %}#row-{{ item.slug }}\n{% endif %}
See in schedule: {% absurl 'agenda' num=schedule.meeting.number %}#row-{{ item.slug }}\n{% endif %}
END:VEVENT
{% endif %}{% endfor %}END:VCALENDAR{% endcache %}{% endautoescape %}

View file

@ -1,27 +0,0 @@
{% extends "base.html" %}
{% block morecss %}
.type-lead:after { content: " (DO NOT POST)"; color:red; }
.type-offagenda:after { content:" (not published on agenda)"; }
{% endblock %}
{% block title %}Agenda for {{ meeting }} by room{% endblock %}
{% block content %}
{% include "meeting/meeting_heading.html" with updated=meeting.updated selected="by-room" title_extra="By room" %}
<div class="daylist">
{% for day,sessions in ss_by_day.items %}
<h2 class="daylistentry mt-5">{{ day|date:'l, j F Y' }}</h2>
{% regroup sessions by timeslot.get_functional_location as room_list %}
<div class="roomlist">
{% for room in room_list %}
<strong class="roomlistentry">{{ room.grouper|default:"Location Unavailable" }}</strong>
<ul class="sessionlist">
{% for ss in room.list %}
<li class="sessionlistentry type-{{ ss.slot_type.slug }} {% if ss.schedule_id != meeting.schedule_id %}from-base-schedule{% endif %}">
{{ ss.timeslot.time|date:"H:i" }}-{{ ss.timeslot.end_time|date:"H:i" }} {{ ss.session.short_name }}
</li>
{% endfor %}
</ul>
{% endfor %}
</div>
{% endfor %}
</div>
{% endblock %}

View file

@ -1,49 +0,0 @@
{% extends "base.html" %}
{% block morecss %}
.type-lead:after { content: " (DO NOT POST)"; color:red; }
.type-offagenda:after { content:" (not published on agenda)"; }
{% endblock %}
{% block title %}Agenda for {{ meeting }} by Session Type{% endblock %}
{% block content %}
{% include "meeting/meeting_heading.html" with updated=meeting.updated selected="by-type" title_extra="By session type" %}
{% regroup assignments by session.type_id as type_list %}
<div class="typelist">
{% for type in type_list %}
<div class="typelistentry">
<h2 class="mt-5">{{ type.grouper|title }}</h2>
{% if schedule == meeting.schedule %}
<a class="btn btn-primary ical-link"
href="{% url "ietf.meeting.views.agenda_by_type_ics" num=meeting.number type=type.grouper %}">
Download to Calendar
</a>
{% endif %}
<div class="daylist">
{% regroup type.list by timeslot.time|date:"l Y-M-d" as daylist %}
{% for day in daylist %}
<div class="daylistentry">
<h3 class="mt-4">{{ day.grouper }}</h3>
<table class="table table-sm table-borderless sessiontable">
<tbody>
{% for ss in day.list %}
<tr {% if ss.schedule_id != meeting.schedule_id %}class="from-base-schedule"{% endif %}>
<td>{{ ss.timeslot.time|date:"H:i" }}-{{ ss.timeslot.end_time|date:"H:i" }}</td>
<td>{{ ss.timeslot.get_hidden_location }}</td>
<td class="type-{{ ss.session.type_id }}">{{ ss.session.short_name }}</td>
<td class="text-end">
{% if ss.session.type_id == 'regular' or ss.session.type_id == 'plenary' or ss.session.type_id == 'other' %}
<a href="{% url 'ietf.meeting.views.session_details' num=meeting.number acronym=ss.session.group.acronym %}">
Materials
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% endblock %}

View file

@ -1,24 +0,0 @@
{% comment %}
Buttons for the agenda_personalize.html template
Required parameters:
meeting - meeting being displayed
personalize - if True, show buttons relevant only for personalize tab
{% endcomment %}
{% load agenda_custom_tags %}
<div class="mb-3 buttonlist">
<a class="btn btn-sm btn-outline-primary ical-link agenda-link filterable"
href="{% webcal_url 'ietf.meeting.views.agenda_ical' num=meeting.number %}">
Subscribe to filtered agenda
</a>
<a class="btn btn-sm btn-outline-primary ical-link agenda-link filterable"
href="{% url "ietf.meeting.views.agenda_ical" num=meeting.number %}">
Download .ics of filtered agenda
</a>
{% if personalize %}
<a class="btn btn-sm btn-outline-primary agenda-link filterable"
href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">
View personal agenda
</a>
{% endif %}
</div>

View file

@ -1,107 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load ietf_filters %}
{% load textfilters %}
{% load static %}
{% block title %}
IETF {{ meeting.number }} meeting agenda
{% if "-utc" in request.path %}(UTC){% endif %}
{% endblock %}
{% block bodyAttrs %}onload="automaticarrow(); checkParams();" onresize="checkParams();"{% endblock %}
{% block precontent %}
<div class="meeting-switch">
<i class="bi bi-arrow-left-right me-2"></i>
<a href="{% url 'floor-plan-neue' num=schedule.meeting.number %}">Switch to New Agenda Display</a>
</div>
{% endblock %}
{% block content %}
{% origin %}
{% include "meeting/meeting_heading.html" with selected="floor-plan" title_extra="Floor Plan" %}
{% for floor in floors %}
<h2 class="mt-4" id="floor-{{ floor.name|xslugify }}">{{ floor.name }}</h2>
<div class="row rooms">
<div class="col-sm-2">
{% for f in floors %}
{% for room in f.room_set.all %}
<a href="javascript: setarrow('room-{{ room.name|xslugify }}')">{{ room.name }}</a>
<br>
{% endfor %}
{% endfor %}
</div>
<div class="col-sm-2">
{% for f in floors %}
{% for room in f.room_set.all %}
{% if room.functional_display_name %}
<a href="javascript: setarrow('room-{{ room.name|xslugify }}')">{{ room.functional_display_name }}</a>
<br>
{% endif %}
{% endfor %}
{% endfor %}
</div>
<div class="col-sm-8">
<div class="floor-plan position-relative">
{% if floor.image %}
<img id="floor-{{ floor.name|xslugify }}-image"
alt="{{ floor.name }} Map"
class="img-fluid w-100"
src="{{ floor.image.url }}">
{# We need as many of these as we can have individual rooms combining into one #}
<div id="floor-{{ floor.name|xslugify }}-arrowdiv0"
class="position-absolute" hidden>
<img alt="Location arrow" src="{% static 'ietf/images/arrow-ani.webp' %}">
</div>
<div id="floor-{{ floor.name|xslugify }}-arrowdiv1"
class="position-absolute" hidden>
<img alt="Location arrow" src="{% static 'ietf/images/arrow-ani.webp' %}">
</div>
<div id="floor-{{ floor.name|xslugify }}-arrowdiv2"
class="position-absolute" hidden>
<img alt="Location arrow" src="{% static 'ietf/images/arrow-ani.webp' %}">
</div>
<div id="floor-{{ floor.name|xslugify }}-arrowdiv3"
class="position-absolute" hidden>
<img alt="Location arrow" src="{% static 'ietf/images/arrow-ani.webp' %}">
</div>
{% else %}
No floor image available yet.
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% endblock %}
{% block js %}
<script src="{% static 'ietf/js/room_params.js' %}"></script>
<script>
// These must match the 'arrowdiv' divs above
var arrowsuffixlist = [ '0', '1', '2', '3' ];
var floorlist = [{% for floor in floors %}{% if not forloop.first %}, {%endif%}'floor-{{floor.name|xslugify}}'{% endfor %}];
function roommap(nm)
{
var c = findroom(nm);
if (c) return nm;
var m = suffixmap(nm);
// console.log("m=" + m);
return m;
}
function findroom(nm)
{
var left = 0, top = 0, right = 0, bottom = 0, floor="", width=0;
if (0) { }
{% for room in meeting.room_set.all %}{% if room.floorplan %}
else if (nm == 'room-{{room.name|xslugify}}') { left = {{room.left}}; top = {{room.top}}; right = {{room.right}}; bottom = {{room.bottom}}; floor='floor-{{room.floorplan.name|xslugify}}'; width={{room.floorplan.image.width}}; }{% endif %}{% endfor %}
{% for room in meeting.room_set.all %}{% if room.functional_display_name %}{% if room.floorplan %}
else if (nm == '{{room.functional_name|xslugify}}') { left = {{room.left}}; top = {{room.top}}; right = {{room.right}}; bottom = {{room.bottom}}; floor='floor-{{room.floorplan.name|xslugify}}'; width={{room.floorplan.image.width}}; }{% endif %}{% endif %}{% endfor %}
else return null;
// console.log("nm=" + nm + ",left=" + left + ",top=" + top + ",r=" + right + ",b=" + bottom);
return [left, top, right, bottom, floor, width];
}
</script>
{% endblock %}

View file

@ -1,85 +0,0 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% origin %}
{# assumes meeting is in context #}
{% load origin %}
{% load ietf_filters htmlfilters %}
{% origin %}
<h1>
IETF {{ meeting.number }} meeting agenda
{% if personalize %}personalization{% endif %}
{% if schedule.meeting.agenda_warning_note %}
<span class="badge rounded-pill bg-danger">
{{ schedule.meeting.agenda_warning_note|removetags:"h1" |safe }}
</span>
{% endif %}
{% if title_extra %}
<br>
<small class="text-muted">{{ title_extra }}</small>
{% endif %}
</h1>
<div class="lead row">
<div class="{% if updated %}col-6{% else %}col-12{% endif %}">
{{ meeting.city|default:"Location TBD" }}, {{ meeting.date|date:"F j" }}{% if meeting.date.month != meeting.end_date.month %} - {{ meeting.end_date|date:"F " }}{% else %}-{% endif %}{{ meeting.end_date|date:"j, Y" }}
</div>
{% if updated %}
<div class="col-6 text-end">
Updated {{ updated|date:"Y-m-d \a\t G:i (T)" }}
</div>
{% endif %}
</div>
{% if schedule != meeting.schedule %}
<div class="alert alert-danger my-3">
This is schedule <b>{{ schedule.owner.email }}/{{ schedule.name }}</b>, not the official schedule.
</div>
{% endif %}
{# a tags with the agenda-link filterable classes will be updated with show/hide parameters #}
<ul class="nav nav-tabs my-3">
<li class="nav-item">
<a class="nav-link agenda-link filterable {% if selected == "agenda" %}active{% endif %}"
href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">
Agenda
</a>
</li>
{% if user|has_role:"Secretariat,Area Director,IAB" %}
{% if schedule != meeting.schedule %}
<li class="nav-item">
<a class="nav-link {% if selected == "by-room" %}active{% endif %}"
href="{% url 'ietf.meeting.views.agenda_by_room' num=meeting.number name=schedule.name owner=schedule.owner.email %}">
By
room
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if selected == "by-type" %}active{% endif %}"
href="{% url 'ietf.meeting.views.agenda_by_type' num=meeting.number name=schedule.name owner=schedule.owner.email %}">
By
type
</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link {% if selected == "by-room" %}active{% endif %}"
href="{% url 'ietf.meeting.views.agenda_by_room' num=meeting.number %}">
By room
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if selected == "by-type" %}active{% endif %}"
href="{% url 'ietf.meeting.views.agenda_by_type' num=meeting.number %}">
By type
</a>
</li>
{% endif %}
{% endif %}
<li class="nav-item">
<a class="nav-link {% if selected == "floor-plan" %}active{% endif %}"
href="{% url 'ietf.meeting.views.floor_plan' num=meeting.number %}">
Floor plan
</a>
</li>
<li class="nav-item">
<a class="nav-link"
href="{% url 'ietf.meeting.views.agenda' num=meeting.number ext='.txt' %}">Plaintext</a>
</li>
</ul>

View file

@ -1,9 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% block title %}IETF {{ meeting.number }} Meeting Agenda{% endblock %}
{% block content %}
{% origin %}
{% include "meeting/meeting_heading.html" with title_extra="" selected="" %}
<div class="alert alert-warning my-3">There is no agenda available yet.</div>
{% endblock %}

View file

@ -37,7 +37,7 @@
<a href="{% url 'ietf.meeting.views.session_details' num=meeting.number acronym=meeting.responsible_group.acronym %}">{{ meeting.number }}</a>
{% if meeting.interim_meeting_cancelled %}<span class="badge rounded-pill bg-warning">Cancelled</span>{% endif %}
{% else %}
<a href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">IETF-{{ meeting.number }}</a>
<a href="{% url 'agenda' num=meeting.number %}">IETF-{{ meeting.number }}</a>
{% endif %}
</td>
<td class="d-none d-sm-table-cell">

View file

@ -11,7 +11,7 @@ This renders the list of links below the title on the meeting proceedings page.
<a href="{% url 'ietf.meeting.views.proceedings_attendees' num=meeting.number %}">Participants</a>
</div>
<div class="proceedings-row">
<a href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">Meeting Agenda</a>
<a href="{% url 'agenda' num=meeting.number %}">Meeting Agenda</a>
</div>
<div class="proceedings-row">
<a href="{% url 'ietf.meeting.views.proceedings_progress_report' num=meeting.number %}">Activity Report</a>

View file

@ -1,379 +0,0 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load static %}
{% load textfilters %}
{% load ietf_filters %}
{% origin %}
{% if item and item|should_show_agenda_session_buttons %}
{% with slug=item.slug %}
{% with session=item.session %}
{% with timeslot=item.timeslot %}
{% with meeting=schedule.meeting %}
{% if session.agenda and show_agenda %}
{# Note: if called with show_agenda=True, calling template must load agenda_materials.js, needed by session_agenda_include.html #}
{% include "meeting/session_agenda_include.html" with slug=slug session=session timeslot=timeslot only %}
{% endif %}
<div class="d-flex">
{% if timeslot.location.video_stream_url or timeslot.location.onsite_tool_url %}
<div id="session-meetecho-buttons-{{ session.pk }}"
role="group"
class="btn-group btn-group-sm d-none d-lg-flex me-1">
{# Video stream (meetecho) #}
{% if timeslot.location.video_stream_url %}
<a class="btn btn-outline-primary"
role="button"
href="{{ timeslot.location.video_stream_url|format:session }}"
aria-label="Full Client with Video"
title="Full Client with Video">
<i class="bi bi-camera-video"></i>
</a>
{% endif %}
{# Onsite tool (meetecho_onsite) #}
{% if timeslot.location.onsite_tool_url %}
<a class="btn btn-outline-primary"
role="button"
href="{{ timeslot.location.onsite_tool_url|format:session }}"
aria-label="Onsite tool"
title="Onsite tool">
<i class="bi bi-phone"></i>
</a>
{% endif %}
</div>
{% endif %}
<div id="session-buttons-{{ session.pk }}"
role="group"
class="btn-group btn-group-sm d-none d-lg-flex">
{% with acronym=session.historic_group.acronym %}
{% if session.agenda and show_agenda %}
{# agenda pop-up button #}
<button class="btn btn-outline-primary"
data-bs-toggle="modal" type="button"
data-bs-target="#modal-{{ slug }}"
aria-label="Show meeting materials"
title="Show meeting materials">
<i class="bi bi-arrows-fullscreen"></i>
</button>
{# materials tar file #}
<a class="btn btn-outline-primary"
role="button"
href="{% url 'ietf.meeting.views.session_draft_tarfile' num=meeting.number acronym=acronym %}"
aria-label="Download meeting materials as .tar archive"
title="Download meeting materials as .tar archive">
<i class="bi bi-file-zip"></i>
</a>
{# materials PDF file #}
<a class="btn btn-outline-primary"
role="button"
href="{% url 'ietf.meeting.views.session_draft_pdf' num=meeting.number acronym=acronym %}"
aria-label="Download meeting materials as PDF file"
title="Download meeting materials as PDF file">
<i class="bi bi-file-pdf"></i>
</a>
{% endif %}
{# HedgeDoc #}
{% if use_codimd %}
<a class="btn btn-outline-primary"
role="button"
href="{{ session.notes_url }}"
aria-label="Notepad for note-takers"
title="Notepad for note-takers">
<i class="bi bi-journal-text"></i>
</a>
{% endif %}
{# show stream buttons up till end of session, then show archive buttons #}
{% if now < timeslot.utc_end_time %}
{# Chat #}
<a class="btn btn-outline-primary"
role="button"
href="{{ session.chat_room_url }}"
aria-label="Chat room for {{ session.chat_room_name }}"
title="Chat room for {{ session.chat_room_name }}">
<i class="bi bi-chat"></i>
</a>
{# Audio stream #}
{% if timeslot.location.audio_stream_url %}
<a class="btn btn-outline-primary"
role="button"
href="{{ timeslot.location.audio_stream_url|format:session }}"
aria-label="Audio stream"
title="Audio stream">
<i class="bi bi-headphones"></i>
</a>
{% endif %}
{# Remote call-in #}
{% if session.agenda_note|first_url|conference_url %}
<a class="btn btn-outline-primary"
role="button"
href="{{ session.agenda_note|first_url }}"
aria-label="Online conference"
title="Online conference">
<i class="bi bi-people"></i>
</a>
{% elif session.remote_instructions|first_url|conference_url %}
<a class="btn btn-outline-primary"
role="button"
href="{{ session.remote_instructions|first_url }}"
aria-label="Online conference"
title="Online conference">
<i class="bi bi-people"></i>
</a>
{% elif timeslot.location.webex_url %}
<a class="btn btn-outline-primary"
role="button"
href="{{ timeslot.location.webex_url|format:session }}"
aria-label="Webex session"
title="Webex session">
<i class="bi bi-people"></i>
</a>
{% endif %}
{# iCalendar item #}
<a class="btn btn-outline-primary"
role="button"
href="{% url 'ietf.meeting.views.agenda_ical' num=meeting.number session_id=session.id %}"
aria-label="icalendar entry for {{ acronym }} session on {{ timeslot.utc_start_time|date:'Y-m-d H:i' }} UTC"
title="icalendar entry for {{ acronym }} session on {{ timeslot.utc_start_time|date:'Y-m-d H:i' }} UTC">
<i class="bi bi-calendar"></i>
</a>
{% else %}
{# Chat logs #}
{% if meeting.number|add:"0" >= 60 %}
<a class="btn btn-outline-primary"
role="button"
href="{{ session.chat_archive_url }}"
aria-label="Chat logs for {{ session.chat_room_name }}"
title="Chat logs for {{ session.chat_room_name }}">
<i class="bi bi-file-text"></i>
</a>
{% endif %}
{# Recordings #}
{% if meeting.number|add:"0" >= 80 %}
{% with session.recordings as recordings %}
{% if recordings %}
{# There's no guaranteed order, so this is a bit messy: #}
{# First, the audio recordings, if any #}
{% for r in recordings %}
{% if r.get_href and 'audio' in r.get_href %}
<a class="btn btn-outline-primary"
role="button"
href="{{ r.get_href }}"
aria-label="{{ r.title }}"
title="{{ r.title }}">
<i class="bi bi-file-play"></i>
</a>
{% endif %}
{% endfor %}
{# Then the youtube recordings #}
{% for r in recordings %}
{% if r.get_href and 'youtu' in r.get_href %}
<a class="btn btn-outline-primary"
role="button"
href="{{ r.get_href }}"
aria-label="{{ r.title }}"
title="{{ r.title }}">
<i class="bi bi-file-slides"></i>
</a>
{% endif %}
{% endfor %}
{# Finally, any other recordings #}
{% for r in recordings %}
{% if r.get_href and not 'audio' in r.get_href and not 'youtu' in r.get_href %}
<a class="btn btn-outline-primary"
role="button"
href="{{ r.get_href }}"
aria-label="{{ r.title }}"
title="{{ r.title }}">
<i class="bi bi-file-play"></i>
</a>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
{% if timeslot.location.video_stream_url %}
<a class="btn btn-outline-primary"
role="button"
href="https://www.meetecho.com/ietf{{ meeting.number }}/recordings#{{ acronym.upper }}"
aria-label="Session recording"
title="Session recording">
<i class="bi bi-file-slides"></i>
</a>
{% endif %}
{% endif %}
{% endif %}
{% endwith %}
</div>
<div class="dropdown d-lg-none">
<button class="btn btn-outline-primary btn-sm dropdown-toggle"
type="button"
aria-label="Info"
id="session-buttons-dropdown-{{ session.pk }}"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="bi bi-info-lg"></i>
</button>
<ul class="dropdown-menu"
aria-labelledby="session-buttons-dropdown-{{ session.pk }}">
{% with acronym=session.historic_group.acronym %}
{% if session.agenda and show_agenda %}
{# agenda pop-up button #}
<li>
<button class="dropdown-item" type="button"
data-bs-toggle="modal"
data-bs-target="#modal-{{ slug }}">
<i class="bi bi-arrows-fullscreen"></i> Show meeting materials
</button>
</li>
{# materials tar file #}
<li>
<a class="dropdown-item"
href="{% url 'ietf.meeting.views.session_draft_tarfile' num=meeting.number acronym=acronym %}">
<i class="bi bi-file-zip"></i> Meeting materials archive
</a>
</li>
{# materials PDF file #}
<li>
<a class="dropdown-item"
href="{% url 'ietf.meeting.views.session_draft_pdf' num=meeting.number acronym=acronym %}">
<i class="bi bi-file-pdf"></i> Meeting materials PDF
</a>
</li>
{% endif %}
{# HedgeDoc #}
{% if use_codimd %}
<li>
<a class="dropdown-item" href="{{ session.notes_url }}">
<i class="bi bi-journal-text"></i> Notepad for note-takers
</a>
</li>
{% endif %}
{# show stream buttons up till end of session, then show archive buttons #}
{% if now < timeslot.utc_end_time %}
{# Chat #}
<li>
<a class="dropdown-item"
href="{{ session.chat_room_url }}">
<i class="bi bi-chat"></i> Chat room
</a>
</li>
{# Video stream (meetecho) #}
{% if timeslot.location.video_stream_url %}
<li>
<a class="dropdown-item"
href="{{ timeslot.location.video_stream_url|format:session }}">
<i class="bi bi-camera-video"></i> Video stream
</a>
</li>
{% endif %}
{# Onsite tool (meetecho_onsite) #}
{% if timeslot.location.onsite_tool_url %}
<li>
<a class="dropdown-item"
href="{{ timeslot.location.onsite_tool_url|format:session }}">
<i class="bi bi-phone"></i> Onsite tool
</a>
</li>
{% endif %}
{# Audio stream #}
{% if timeslot.location.audio_stream_url %}
<li>
<a class="dropdown-item"
href="{{ timeslot.location.audio_stream_url|format:session }}">
<i class="bi bi-headphones"></i> Audio stream
</a>
</li>
{% endif %}
{# Remote call-in #}
{% if session.agenda_note|first_url|conference_url %}
<li>
<a class="dropdown-item" href="{{ session.agenda_note|first_url }}">
<i class="bi bi-people"></i> Online conference
</a>
</li>
{% elif session.remote_instructions|first_url|conference_url %}
<li>
<a class="dropdown-item"
href="{{ session.remote_instructions|first_url }}">
<i class="bi bi-people"></i> Online conference
</a>
</li>
{% elif timeslot.location.webex_url %}
<li>
<a class="dropdown-item"
href="{{ timeslot.location.webex_url|format:session }}">
<i class="bi bi-people"></i> Webex session
</a>
</li>
{% endif %}
{# iCalendar item #}
<li>
<a class="dropdown-item"
href="{% url 'ietf.meeting.views.agenda_ical' num=meeting.number session_id=session.id %}">
<i class="bi bi-calendar"></i> Add to calendar
</a>
</li>
{% else %}
{# Chat logs #}
{% if meeting.number|add:"0" >= 60 %}
<li>
<a class="dropdown-item"
href="{{ session.chat_archive_url }}">
<i class="bi bi-file-text"></i> Chat logs
</a>
</li>
{% endif %}
{# Recordings #}
{% if meeting.number|add:"0" >= 80 %}
{% with session.recordings as recordings %}
{% if recordings %}
{# There's no guaranteed order, so this is a bit messy: #}
{# First, the audio recordings, if any #}
{% for r in recordings %}
{% if r.get_href and 'audio' in r.get_href %}
<li>
<a class="dropdown-item" href="{{ r.get_href }}">
<i class="bi bi-file-play"></i> {{ r.title }}
</a>
</li>
{% endif %}
{% endfor %}
{# Then the youtube recordings #}
{% for r in recordings %}
{% if r.get_href and 'youtu' in r.get_href %}
<li>
<a class="dropdown-item" href="{{ r.get_href }}">
<i class="bi bi-file-slides"></i> {{ r.title }}
</a>
</li>
{% endif %}
{% endfor %}
{# Finally, any other recordings #}
{% for r in recordings %}
{% if r.get_href and not 'audio' in r.get_href and not 'youtu' in r.get_href %}
<li>
<a class="dropdown-item" href="{{ r.get_href }}">
<i class="bi bi-file-play"></i> {{ r.title }}
</a>
</li>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
{% if timeslot.location.video_stream_url %}
<li>
<a class="dropdown-item"
href="https://www.meetecho.com/ietf{{ meeting.number }}/recordings#{{ acronym.upper }}">
<i class="bi bi-file-slides"></i> Session recording
</a>
</li>
{% endif %}
{% endif %}
{% endif %}
{% endwith %}
</ul>
</div>
</div>
{% endwith %}
{% endwith %}
{% endwith %}
{% endwith %}
{% endif %}

View file

@ -21,10 +21,6 @@
{# see note in the included templates re: show_agenda parameter and required JS import #}
{% if meeting.type.slug == 'interim' %}
{% include "meeting/interim_session_buttons.html" with show_agenda=False show_empty=False %}
{% else %}
{% with schedule=meeting.schedule %}
{% include "meeting/session_buttons_include.html" with show_agenda=False %}
{% endwith %}
{% endif %}
</div>
{% endif %}

View file

@ -1,11 +0,0 @@
<div class="timetooltip">
<div class="time"
data-start-time="{{ item.start_timestamp }}"
data-end-time="{{ item.end_timestamp }}">
{% if "-utc" in request.path %}
{{ item.timeslot.utc_start_time|date:"H:i" }}<span class="d-lg-none"><br></span>-{{ item.timeslot.utc_end_time|date:"H:i" }}
{% else %}
{{ item.timeslot.time|date:"H:i" }}<span class="d-lg-none"><br></span>-{{ item.timeslot.end_time|date:"H:i" }}
{% endif %}
</div>
</div>

View file

@ -58,7 +58,7 @@
<td>ietf</td>
<td>
<a class="ietf-meeting-link"
href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">
href="{% url 'agenda' num=meeting.number %}">
IETF {{ meeting.number }}
</a>
</td>
@ -124,7 +124,7 @@
ietf_meeting_number: '{{ meeting.number }}',
start_moment: moment.tz('{{meeting.date}}', '{{ meeting.time_zone }}').startOf('day'),
end_moment: moment.tz('{{meeting.end}}', '{{ meeting.time_zone }}').endOf('day'),
url: '{% url 'ietf.meeting.views.agenda' num=meeting.number %}'
url: '{% url 'agenda' num=meeting.number %}'
}{% if not forloop.last %}, {% endif %}
{% endwith %}
{% else %} {# if it's not a Meeting, it's a Session #}

View file

@ -26,6 +26,6 @@ CLASS:PUBLIC
DTSTART;VALUE=DATE{% if meeting.time_zone %};TZID={{ meeting.time_zone|ics_esc }}{% endif %}:{{ meeting.date|date:"Ymd" }}
DTEND;VALUE=DATE{% if meeting.time_zone %};TZID={{ meeting.time_zone|ics_esc }}{% endif %}:{{ meeting.end_date|date:"Ymd" }}
DTSTAMP:{{ meeting.cached_updated|date:"Ymd" }}T{{ meeting.cached_updated|date:"His" }}Z
URL:{{ request.scheme }}://{{ request.get_host }}{% url 'ietf.meeting.views.agenda' num=meeting.number %}
URL:{{ request.scheme }}://{{ request.get_host }}{% url 'agenda' num=meeting.number %}
END:VEVENT
{% endfor %}END:VCALENDAR{% endautoescape %}

View file

@ -1,40 +0,0 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load static %}
{# FIXME: the weekview only renders correctly in quirks mode, i.e., not in HTML5 with "<!DOCTYPE html>" in the next line; it should be rewritten with fullcalendar #}
{# <!DOCTYPE html> #}
<html lang="en">
{% origin %}
<head>
<title>Weekview</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="{% static 'ietf/js/agenda_filter.js' %}"></script>
<script src="{% static 'ietf/js/moment.js' %}"></script>
<script src="{% static 'ietf/js/week-view.js' %}"></script>
<script>
var all_items = {{ items | safe }};
//===========================================================================
// Set up events for drawing the calendar
function redraw_weekview() {
var query_params = agenda_filter.parse_query_params(window.location.search);
var timezone_name = query_params.tz || 'utc';
items = prepare_items(all_items, timezone_name);
draw_calendar(items, agenda_filter.get_filter_params(query_params));
}
window.addEventListener("resize", redraw_weekview, false);
window.addEventListener("load", redraw_weekview, false);
window.addEventListener("hashchange", redraw_weekview, false);
</script>
</head>
<body>
<p>
Error loading calendar.
</p>
</body>
</html>

View file

@ -51,7 +51,7 @@ test.describe('past - desktop', () => {
// Visit agenda page and await Meeting Data API call to complete
await Promise.all([
page.waitForResponse(`**/api/meeting/${meetingData.meeting.number}/agenda-data`),
page.goto(`/meeting/${meetingData.meeting.number}/agenda-neue`)
page.goto(`/meeting/${meetingData.meeting.number}/agenda`)
])
// Wait for page to be ready
@ -191,7 +191,7 @@ test.describe('past - desktop', () => {
if (event.location?.short) {
// Has floor badge
await expect(row.locator('.agenda-table-cell-room > a')).toContainText(event.room)
await expect(row.locator('.agenda-table-cell-room > a')).toHaveAttribute('href', `/meeting/${meetingData.meeting.number}/floor-plan-neue?room=${xslugify(event.room)}`)
await expect(row.locator('.agenda-table-cell-room > a')).toHaveAttribute('href', `/meeting/${meetingData.meeting.number}/floor-plan?room=${xslugify(event.room)}`)
await expect(row.locator('.agenda-table-cell-room > .badge')).toContainText(event.location.short)
} else {
// No floor badge
@ -1079,7 +1079,7 @@ test.describe('future - desktop', () => {
// Visit agenda page and await Meeting Data API call to complete
await Promise.all([
page.waitForResponse(`**/api/meeting/${meetingData.meeting.number}/agenda-data`),
page.goto(`/meeting/${meetingData.meeting.number}/agenda-neue`)
page.goto(`/meeting/${meetingData.meeting.number}/agenda`)
])
// Wait for page to be ready
@ -1247,7 +1247,7 @@ test.describe('live - desktop', () => {
// Visit agenda page and await Meeting Data API call to complete
await Promise.all([
page.waitForResponse(`**/api/meeting/${meetingData.meeting.number}/agenda-data`),
page.goto(`/meeting/${meetingData.meeting.number}/agenda-neue`)
page.goto(`/meeting/${meetingData.meeting.number}/agenda`)
])
// Wait for page to be ready
@ -1328,7 +1328,7 @@ test.describe('past - small screens', () => {
// Visit agenda page and await Meeting Data API call to complete
await Promise.all([
page.waitForResponse(`**/api/meeting/${meetingData.meeting.number}/agenda-data`),
page.goto(`/meeting/${meetingData.meeting.number}/agenda-neue`)
page.goto(`/meeting/${meetingData.meeting.number}/agenda`)
])
// Wait for page to be ready

View file

@ -12,7 +12,7 @@ seedrandom(TEST_SEED.toString(), { global: true })
faker.seed(TEST_SEED)
// ====================================================================
// FLOOR-PLAN-NEUE | All Viewports
// FLOOR-PLAN | All Viewports
// ====================================================================
test.describe('floor-plan', () => {
@ -42,7 +42,7 @@ test.describe('floor-plan', () => {
// Visit floor plan page and await Meeting Data API call to complete
await Promise.all([
page.waitForResponse(`**/api/meeting/${meetingData.meeting.number}/agenda-data`),
page.goto(`/meeting/${meetingData.meeting.number}/floor-plan-neue`)
page.goto(`/meeting/${meetingData.meeting.number}/floor-plan`)
])
// Wait for page to be ready