Full Council
Scheduled for Tuesday, 25 March 2025 at 7:00 PM
An error occurred while processing the template.
The following has evaluated to null or missing:
==> video_id [in template "12768602#20123#258052" at line 61, column 76]
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: #assign VOID = http_options.setLocati... [in template "12768602#20123#258052" at line 61, column 3]
----
1<#function formatFileSize bytes>
2 <#assign units = ["B", "KB", "MB", "GB", "TB", "PB"] />
3 <#assign size = bytes?number />
4 <#assign unitIndex = 0 />
5
6 <#list 1..10 as i>
7 <#if size gte 1024 && unitIndex lt units?size - 1>
8 <#assign size = size / 1024 />
9 <#assign unitIndex = unitIndex + 1 />
10 <#else>
11 <#break>
12 </#if>
13 </#list>
14
15 <#return size?string("0.#") + " " + units[unitIndex]>
16</#function>
17
18
19
20<#assign UserLocalService = serviceLocator.findService("com.liferay.portal.kernel.service.UserLocalService")/>
21
22<#if UserLocalService.hasRoleUser(1751340, user.getUserId())>
23<div class="shadow" style="position: fixed; top: 4.5rem; right: 1.5rem; background-color: red; color: white; font-weight: bold; padding: 0.75rem 1rem; z-index: 500; border: 2px solid white; text-align: center; border-radius: 0.375rem; max-width: 12rem;"><span class="d-block">You are viewing this as a logged in user</span></div>
24</#if>
25
26<#if (agenda.getData())?has_content || (minutes.getData())?has_content>
27<h3 class="lccc-summary-list__header">Documents available for this meeting:</h3>
28<ul class="list-unstyled spaced-2 mb-5">
29<#if (agenda.getData())?has_content>
30<li><a href="${agenda.getData()}">Agenda <#if getterUtil.getBoolean(reportsIncluded.getData())>and Reports </#if>(PDF)</a></li>
31</#if>
32<#if (minutes.getData())?has_content>
33<li><a href="${minutes.getData()}">Minutes (PDF)</a></li>
34</#if>
35</ul>
36</#if>
37
38
39<#assign dateInMilliseconds = date.getData()?datetime("yyyy-MM-dd")?long >
40<#assign timeInMilliseconds = (timeHours.getData()?number * 3600000 + timeMinutes.getData()?number * 60000) >
41<#assign dateAndTimeInMilliseconds = dateInMilliseconds + timeInMilliseconds >
42
43<div class="meeting">
44
45<#if audioVideoURL.getData()?? && (getterUtil.getBoolean(showAudioVideo.getData()) || UserLocalService.hasRoleUser(1751340, user.getUserId())) && audioVideoURL.getData()?contains("vimeo.com")>
46 <#assign videoURL = audioVideoURL.getData()>
47 <#if videoURL?contains("iframe")>
48 <#assign videoURL = videoURL?keep_after("src=\"")?keep_before("\"")>
49 <#assign video_id = videoURL?keep_after("vimeo.com/video/")?keep_before("?")>
50 </#if>
51 <h3 class="lccc-summary-list__header">Watch this meeting:</h3>
52 <#if !getterUtil.getBoolean(showAudioVideo.getData())><span style="display: inline-block; padding: 0.25rem 0.5rem; background-color: red; color: white; font-weight: bold; border-radius: 0.25rem; margin-bottom: 1rem;">NOT CURRENTLY VISIBLE TO THE PUBLIC</span></#if>
53 <div style="padding:56.25% 0 0 0;position:relative;"><iframe src="${videoURL}" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%;" title="Video of meeting"></iframe></div>
54 <script src="https://player.vimeo.com/api/player.js"></script>
55
56
57 <#assign httpServletRequest = digitalplace_serviceContext.getRequest()/>
58 <#assign httpSession = httpServletRequest.getSession() />
59 <#assign http_options = staticUtil["com.liferay.portal.kernel.util.InstanceFactory"].newInstance("com.liferay.portal.kernel.util.Http$Options")/>
60 <#assign VOID = http_options.addHeader("Authorization", "Bearer 1577416bbc0e511d47e6e871fc80f065")/>
61 <#assign VOID = http_options.setLocation("https://api.vimeo.com/videos/${video_id}?fields=download")/>
62
63 <#if (httpSession.getAttribute("vimeo.video.download")?has_content) && ((httpSession.getAttribute("vimeo.video.download.expires")!0 - .now?long!0) > 3600000) && ((httpSession.getAttribute("vimeo.video.id")!"") == video_id) >
64
65 <#assign download = httpSession.getAttribute("vimeo.video.download")/>
66
67 <#else>
68
69 <#assign download = jsonFactoryUtil.looseDeserialize(httpUtil.URLtoString(http_options))/>
70
71 </#if>
72
73 <#if download?has_content && download?size gt 0>
74
75<script>console.log("download has content");</script>
76
77 <#list download.download?filter(x -> x.rendition == "720p") as x>
78 <#assign link = x.link />
79 <#assign expires = x.expires?datetime.iso?long />
80 <#assign size = x.size!0 />
81 </#list>
82
83 <#if link?has_content>
84 <div class="download-container"><a href="${link}" class="btn btn-primary my-3">Download video <span class="small">(${formatFileSize(size)})</span></a></div>
85 <#else>
86 <div class="download-container"></div>
87 </#if>
88
89 <#assign VOID = httpSession.setAttribute("vimeo.video.download", download)/>
90 <#assign VOID = httpSession.setAttribute("vimeo.video.download.expires", expires)/>
91 <#assign VOID = httpSession.setAttribute("vimeo.video.id", video_id)/>
92
93 </#if>
94
95
96
97</#if>
98
99<#if audioVideoURL.getData()?? && (getterUtil.getBoolean(showAudioVideo.getData()) || UserLocalService.hasRoleUser(1751340, user.getUserId())) && audioVideoURL.getData()?contains("soundcloud.com")>
100 <#assign audioURL = audioVideoURL.getData()>
101 <#if audioURL?contains("iframe")>
102 <#assign audioURL = audioURL?keep_after("?url=")?keep_before("&color")>
103 </#if>
104 <h3 class="lccc-summary-list__header">Listen to this meeting:</h3>
105 <#if !getterUtil.getBoolean(showAudioVideo.getData())><span style="display: inline-block; padding: 0.25rem 0.5rem; background-color: red; color: white; font-weight: bold; border-radius: 0.25rem; margin-bottom: 1rem;">NOT CURRENTLY VISIBLE TO THE PUBLIC</span></#if>
106 <script src="https://w.soundcloud.com/player/api.js"></script>
107 <iframe allow="autoplay" title="audio player" id="audio" src="https://w.soundcloud.com/player/?url=${audioURL}&color=53c3e9&auto_play=false&hide_related=true&show_artwork=false&show_comments=false&show_user=false&show_reposts=false&download=true&liking=false" width="100%" style="border: 0; max-width: 48rem; border: 1px solid var(--primary);"></iframe>
108</#if>
109
110<#if (getterUtil.getBoolean(showChapters.getData()) || UserLocalService.hasRoleUser(1751340, user.getUserId())) && chapters.getSiblings()?has_content>
111 <div class="mt-4 mb-4" style="max-width: 48rem;">
112 <h4 class="inline-block mt-4 mb-4">Contents:</h4>
113 <p>Use the links below to skip to a specific part of the recording.</p>
114 <#if !getterUtil.getBoolean(showChapters.getData())><span style="display: inline-block; padding: 0.25rem 0.5rem; background-color: red; color: white; font-weight: bold; border-radius: 0.25rem; margin-bottom: 1rem;">NOT CURRENTLY VISIBLE TO THE PUBLIC</span></#if>
115 <table>
116 <thead>
117 <tr style="line-height: 1.1;">
118 <th style="width: 12%">Item number</th>
119 <th>Item title</th>
120 <th>Time</th>
121 </tr>
122 </thead>
123 <tbody>
124 <#list chapters.getSiblings() as item>
125
126 <tr>
127 <#if !getterUtil.getBoolean(item.confidential.getData())>
128 <td>${item.itemNumber.getData()}</td>
129 <td><a href="#" class="track-link align-top" data-timestamp="${item.timestamp.getData()}">${item.itemTitle.getData()}</a></td>
130 <td>${item.timestamp.getData()}</td>
131 <#else>
132 <td class="confidential">${item.itemNumber.getData()}</td>
133 <td colspan="2" class="confidential"><span>${item.itemTitle.getData()}</span><span class="badge badge-dark float-right mr-0">Confidential</span></td>
134 </#if>
135 </tr>
136
137 </#list>
138 </tbody>
139 </table>
140 </div>
141</#if>
142
143<script>
144
145function convertTimeToMs(time) {
146 var timeSplit = time.split(':');
147 var hours = 0;
148 var minutes = 0;
149 var seconds = 0;
150 var s = 0;
151 var ms = 0;
152 hours = parseInt(timeSplit[0]);
153 minutes = parseInt(timeSplit[1]);
154 seconds = parseInt(timeSplit[2]);
155 ms = hours*(3600000) + minutes*(60000) + seconds*(1000);
156
157 return ms;
158}
159
160function convertTimeToSeconds(time) {
161 var timeSplit = time.split(':');
162 var hours = 0;
163 var minutes = 0;
164 var seconds = 0;
165 var s = 0;
166 hours = parseInt(timeSplit[0]);
167 minutes = parseInt(timeSplit[1]);
168 seconds = parseInt(timeSplit[2]);
169 s = hours*(3600) + minutes*(60) + seconds;
170
171 return s;
172}
173
174</script>
175
176
177<#if audioVideoURL.getData()?contains("soundcloud.com")>
178<!-- BEGIN - code for audio playback -->
179<script>
180
181var widget = SC.Widget(document.getElementById('audio'));
182
183widget.bind(SC.Widget.Events.READY, function() {
184
185 var trackLinks = document.querySelectorAll('.track-link');
186
187 for (var i=0; i<trackLinks.length; i++) {
188 trackLinks[i].addEventListener('click', function(e) {
189 e.preventDefault();
190
191 // Note: there seems to be a weird unexpected behavior in that
192 // the first click doesn't actually seek if the widget isn't
193 // playing. I confirmed this in Soundcloud's API playground
194 // so if we detect we are paused, we want to trigger a play first
195 // before we seekTo otherwise it doesn't work at all
196
197 widget.isPaused(function(e) {
198 if (e) {
199 widget.play();
200 }
201 });
202
203 widget.seekTo(convertTimeToMs(this.dataset.timestamp));
204 });
205 }
206
207});
208
209</script>
210
211</#if>
212
213
214
215
216<#if audioVideoURL.getData()?contains("vimeo.com")>
217<!-- BEGIN - code for video playback -->
218<script>
219
220 var iframe = document.querySelector('iframe');
221 var player = new Vimeo.Player(iframe);
222 var trackLinks = document.querySelectorAll('.track-link');
223
224 for (var i=0; i<trackLinks.length; i++) {
225 trackLinks[i].addEventListener('click', function(e) {
226 e.preventDefault();
227
228 player.setCurrentTime(convertTimeToSeconds(this.dataset.timestamp)).then(function(seconds) {
229 player.play();
230 }).catch(function(error) {
231 switch (error.name) {
232 case 'RangeError':
233 // The time is less than 0 or greater than the video's duration
234 break;
235 default:
236 // Some other error occurred
237 break;
238 }
239 });
240 });
241 }
242
243</script>
244<!-- END - code for video playback -->
245</#if>
246
247</div>