Skip to content

Commit 3c94da0

Browse files
tw4likreymer
andauthored
2.7.2 patch release (#787)
* Fix 2.7.1 regressions * Bump to 2.7.2 * fix redirect-to-exact false: - check if current loaded timestamp is the same as to-redirected to timestamp, and avoid reload * additional ui fixes: - location bar: reload with current timestamp, instead of going to calendar - ensure calendar popup on replay view is scrollable - 'Live' mode fixes: don't cache live cdx entry, don't add timestamp when navigating in live mode without timestamp - remember timeline view toggle on replay - title: add 'Archived Page: ' prefix to document.title, consistent with old version - ensure 'Archived Page: ' text is localizable - ui: change ',' to '|' on capture display * update CHANGES for 2.7.2 Co-authored-by: Ilya Kreymer <ikreymer@gmail.com>
1 parent 2d19b6b commit 3c94da0

File tree

11 files changed

+170
-115
lines changed

11 files changed

+170
-115
lines changed

CHANGES.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
pywb 2.7.2 changelist
2+
~~~~~~~~~~~~~~~~~~~~~
3+
4+
* Fix regression introduced by improper wombat update in 2.7.1
5+
* Fix `redirect_to_exact: false` functionality: if not set, UI will stay on current timestamp, but will display info on actual capture.
6+
* Location bar nav now keeps current timestamp instead of defaulting to calendar view.
7+
* 'Live' mode fixes, no longer cache live cdx entry, don't add timestamp when navigating in live mode without timestamp
8+
* Calendar dropdown on replay now scrollable.
9+
* Timeline toggle on replay is 'sticky', will stay on if toggled on replay.
10+
* Capture text: use '|' as in 'Current Capture: [title] | [capture date]'
11+
* Document title: Add 'Archived Page: ' prefix to avoid confusion with live pages.
12+
113
pywb 2.7.1 changelist
214
~~~~~~~~~~~~~~~~~~~~~
315

pywb/apps/frontendapp.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,14 @@ def serve_cdx(self, environ, coll='$root'):
414414
# if coll == self.all_coll:
415415
# coll = '*'
416416

417+
config = self.warcserver.get_coll_config(coll)
418+
is_live = config.get("index") == "$live"
419+
420+
if is_live:
421+
cache_control = "no-store, no-cache"
422+
else:
423+
cache_control = "max-age=86400, must-revalidate"
424+
417425
cdx_url = base_url.format(coll=coll)
418426

419427
if environ.get('QUERY_STRING'):
@@ -433,7 +441,7 @@ def serve_cdx(self, environ, coll='$root'):
433441
return WbResponse.bin_stream(StreamIter(res.raw),
434442
content_type=content_type,
435443
status=status_line,
436-
headers=[("Cache-Control", "max-age=86400, must-revalidate")])
444+
headers=[("Cache-Control", cache_control)])
437445

438446
except Exception as e:
439447
return WbResponse.text_response('Error: ' + str(e), status='400 Bad Request')

pywb/rewrite/templateview.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ def get_top_frame(self, wb_url,
408408
timestamp = ''
409409
if wb_url.timestamp:
410410
timestamp = wb_url.timestamp
411+
#else:
412+
# timestamp = timestamp_now()
411413

412414
is_proxy = 'wsgiprox.proxy_host' in env
413415

pywb/static/vue/vueui.js

Lines changed: 71 additions & 61 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pywb/static/wombat.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pywb/templates/vue_loc.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"capture": "{{ _Q('capture') }}",
6262
"captures": "{{ _Q('captures') }}",
6363
"from {hour1} to {hour2}": "{{ _Q('from {hour1} to {hour2}') }}",
64-
"no captures": "{{ _Q('no captures') }}"
64+
"no captures": "{{ _Q('no captures') }}",
65+
"Archived Page: ": "{{ _Q('Archived Page: ') }}"
6566
}
6667
</script>

pywb/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = '2.7.1'
1+
__version__ = '2.7.2'
22

33
if __name__ == '__main__':
44
print(__version__)

pywb/vueui/src/App.vue

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
class="btn btn-sm"
7070
:class="{active: showTimelineView, 'btn-outline-light': lightButtons, 'btn-outline-dark': !lightButtons}"
7171
:aria-pressed="showTimelineView"
72-
@click="showTimelineView = !showTimelineView"
72+
@click="toggleTimelineView"
7373
:title="(showTimelineView ? _('Hide timeline') : _('Show timeline'))">
7474
<i class="far fa-chart-bar"></i>
7575
</button>
@@ -113,7 +113,7 @@
113113
{{ config.title }}
114114
</span>
115115
</span>
116-
<span class="mr-1" v-if="config.title">,</span>
116+
<span class="mr-1" v-if="config.title">|</span>
117117
{{currentSnapshot.getTimeDateFormatted()}}
118118
</span>
119119
</nav>
@@ -142,7 +142,7 @@
142142
</div>
143143

144144
<!-- Calendar -->
145-
<div class="card" v-if="currentPeriod && showFullView && currentPeriod.children.length">
145+
<div class="card" id="calendar-card" v-if="currentPeriod && showFullView && currentPeriod.children.length">
146146
<div class="card-body" id="calendar-card-body">
147147
<CalendarYear
148148
:period="currentPeriod"
@@ -173,8 +173,8 @@ export default {
173173
currentSnapshot: null,
174174
currentSnapshotIndex: null,
175175
msgs: [],
176-
showFullView: true,
177-
showTimelineView: true,
176+
showFullView: false,
177+
showTimelineView: false,
178178
maxTimelineZoomLevel: PywbPeriod.Type.day,
179179
config: {
180180
title: "",
@@ -194,7 +194,7 @@ export default {
194194
},
195195
updated: function() {
196196
// set top frame title equal to value pulled from replay frame
197-
document.title = this.config.title;
197+
document.title = this._("Archived Page: ") + this.config.title;
198198
},
199199
computed: {
200200
sessionStorageUrlKey() {
@@ -281,7 +281,7 @@ export default {
281281
if (reloadIFrame !== false) {
282282
this.$emit("show-snapshot", snapshot);
283283
}
284-
this.hideBannerUtilities();
284+
this.initBannerState(true);
285285
},
286286
gotoPreviousSnapshot() {
287287
let periodToChangeTo = this.currentPeriod.findByFullId(this.previousSnapshot.getFullId());
@@ -294,10 +294,15 @@ export default {
294294
gotoUrl(event) {
295295
event.preventDefault();
296296
const newUrl = document.querySelector("#theurl").value;
297-
if (newUrl !== this.url) {
298-
window.location.href = this.config.prefix + "*/" + newUrl;
297+
if (newUrl !== this.config.url) {
298+
const ts = this.config.timestamp === undefined ? "*" : this.config.timestamp;
299+
window.location.href = this.config.prefix + ts + (ts ? "/" : "") + newUrl;
299300
}
300301
},
302+
toggleTimelineView() {
303+
this.showTimelineView = !this.showTimelineView;
304+
window.localStorage.setItem("showTimelineView", this.showTimelineView ? "1" : "0");
305+
},
301306
setData(/** @type {PywbData} data */ data) {
302307
303308
// data-set will usually happen at App INIT (from parent caller)
@@ -321,6 +326,10 @@ export default {
321326
}.bind(this));
322327
},
323328
setSnapshot(view) {
329+
if (!this.currentPeriod) {
330+
return false;
331+
}
332+
324333
// turn off calendar (aka full) view
325334
this.showFullView = false;
326335
@@ -332,18 +341,20 @@ export default {
332341
let periodToChangeTo = this.currentPeriod.findByFullId(snapshot.getFullId());
333342
if (periodToChangeTo) {
334343
this.gotoPeriod(periodToChangeTo, false /* onlyZoomToPeriod */);
344+
return true;
335345
}
346+
return false;
336347
},
337-
setTimelineView() {
338-
this.showTimelineView = !this.showTimelineView;
339-
if (this.showTimelineView === true) {
348+
initBannerState(isReplay) {
349+
// if not replay, always show both
350+
if (!isReplay) {
351+
this.showFullView = true;
352+
this.showTimelineView = true;
353+
} else {
340354
this.showFullView = false;
355+
this.showTimelineView = window.localStorage.getItem("showTimelineView") === "1";
341356
}
342357
},
343-
hideBannerUtilities() {
344-
this.showFullView = false;
345-
this.showTimelineView = false;
346-
},
347358
updateTitle(title) {
348359
this.config.title = title;
349360
}
@@ -361,7 +372,10 @@ export default {
361372
width: 100%;
362373
}
363374
.app.expanded {
364-
height: 130px;
375+
/*height: 130px;*/
376+
max-height: calc(100vh - 90px);
377+
display: flex;
378+
flex-direction: column;
365379
}
366380
.full-view {
367381
/*position: fixed;*/
@@ -449,6 +463,10 @@ export default {
449463
div.timeline-wrap div.card {
450464
margin-top: 55px;
451465
}
466+
#calendar-card {
467+
overflow-y: auto;
468+
max-height: 100%;
469+
}
452470
div.timeline-wrap div.card-body {
453471
display: flex;
454472
align-items: center;
@@ -459,6 +477,7 @@ export default {
459477
align-items: center;
460478
justify-content: center;
461479
}
480+
462481
#calendar-card-body {
463482
padding: 0;
464483
}

pywb/vueui/src/components/CalendarYear.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export default {
163163
flex: 1;
164164
flex-wrap: wrap;
165165
z-index: 10;
166-
overflow-y: scroll;
166+
overflow-y: auto;
167167
width: 100%;
168168
background-color: white;
169169
padding-bottom: 1em;

pywb/vueui/src/index.js

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class CDXLoader {
2323
this.logoUrl = logoUrl;
2424
this.navbarBackground = navbarBackground;
2525
this.navbarColor = navbarColor;
26-
this.navbarLightButtons = navbarLightButtons
26+
this.navbarLightButtons = navbarLightButtons;
27+
this.timestamp = timestamp;
2728

2829
this.isReplay = (timestamp !== undefined);
2930

@@ -35,11 +36,10 @@ class CDXLoader {
3536
}, 500);
3637

3738
if (this.isReplay) {
38-
window.WBBanner = new VueBannerWrapper(this, url);
39+
window.WBBanner = new VueBannerWrapper(this, url, timestamp);
3940
}
4041

4142
let queryURL;
42-
let isQueryURL = window.location.href.indexOf("*") > -1 ? true : false;
4343

4444
// query form *?=url...
4545
if (window.location.href.indexOf("*?") > 0) {
@@ -60,9 +60,10 @@ class CDXLoader {
6060

6161
const logoImg = this.staticPrefix + "/" + (this.logoUrl ? this.logoUrl : "pywb-logo-sm.png");
6262

63-
this.app = this.initApp({logoImg, navbarBackground, navbarColor, navbarLightButtons, url, allLocales});
63+
this.app = this.initApp({logoImg, navbarBackground, navbarColor, navbarLightButtons, url, allLocales, timestamp});
64+
6465
this.loadCDX(queryURL).then((cdxList) => {
65-
this.setAppData(cdxList, url, isQueryURL, timestamp);
66+
this.setAppData(cdxList, url, this.timestamp);
6667
});
6768
}
6869

@@ -73,19 +74,7 @@ class CDXLoader {
7374

7475
app.$mount("#app");
7576

76-
// TODO (Ilya): make this work with in-page snapshot/capture/replay updates!
77-
// app.$on("show-snapshot", snapshot => {
78-
// const replayUrl = app.config.url;
79-
// const url = location.href.replace('/'+replayUrl, '').replace(/\d+$/, '') + snapshot.id + '/' + replayUrl;
80-
// window.history.pushState({url: replayUrl, timestamp: snapshot.id}, document.title, url);
81-
// if (!window.onpopstate) {
82-
// window.onpopstate = (ev) => {
83-
// updateSnapshot(ev.state.url, ev.state.timestamp);
84-
// };
85-
// }
86-
// });
87-
88-
app.$on("show-snapshot", this.loadSnapshot.bind(this));
77+
app.$on("show-snapshot", (snapshot) => this.loadSnapshot(snapshot));
8978
app.$on("data-set-and-render-completed", () => {
9079
if (this.loadingSpinner) {
9180
this.loadingSpinner.setOff(); // only turn off loading-spinner AFTER app has told us it is DONE DONE
@@ -101,31 +90,37 @@ class CDXLoader {
10190
params.set("url", url);
10291
params.set("output", "json");
10392
const queryURL = this.prefix + "cdx?" + params.toString();
104-
let isQueryURL = window.location.href.indexOf("*") > -1 ? true : false;
10593

10694
const cdxList = await this.loadCDX(queryURL);
10795

108-
this.setAppData(cdxList, url, isQueryURL, timestamp);
96+
this.setAppData(cdxList, url, timestamp);
10997
}
11098

111-
setAppData(cdxList, url, isQueryURL, timestamp="") {
112-
this.app.setData(new PywbData(cdxList));
99+
async updateTimestamp(url, timestamp) {
100+
this.timestamp = timestamp;
113101

114-
// if this is a capture but we don't have a timestamp (e.g. if redirect_to_exact is false)
115-
// set the timestamp to the latest capture
116-
if ((!timestamp) && (!isQueryURL)) {
117-
const lastSnapshot = cdxList[cdxList.length - 1];
118-
timestamp = lastSnapshot.timestamp;
102+
if (this.cdxLoading) {
103+
return;
119104
}
120105

106+
this.app.setSnapshot({url, timestamp});
107+
}
108+
109+
setAppData(cdxList, url, timestamp) {
110+
this.app.setData(new PywbData(cdxList));
111+
112+
this.app.initBannerState(this.isReplay);
113+
114+
// if set on initial load, may not have timestamp yet
115+
// will be updated later
121116
if (timestamp) {
122-
this.app.hideBannerUtilities();
123-
this.app.setSnapshot({url, timestamp});
117+
this.updateTimestamp(url, timestamp);
124118
}
125119
}
126120

127121
async loadCDX(queryURL) {
128122
// this.loadingSpinner.setOn(); // start loading-spinner when CDX loading begins
123+
this.cdxLoading = true;
129124
const queryWorker = new Worker(this.staticPrefix + "/queryWorker.js");
130125

131126
const p = new Promise((resolve) => {
@@ -139,6 +134,7 @@ class CDXLoader {
139134
break;
140135

141136
case "finished":
137+
this.cdxLoading = false;
142138
resolve(cdxList);
143139
break;
144140
}
@@ -162,7 +158,10 @@ class CDXLoader {
162158
if (!this.isReplay) {
163159
window.location.href = this.prefix + snapshot.id + "/" + snapshot.url;
164160
} else if (window.cframe) {
165-
window.cframe.load_url(snapshot.url, snapshot.id + "", reloadIFrame);
161+
const ts = snapshot.id + "";
162+
if (ts !== this.timestamp) {
163+
window.cframe.load_url(snapshot.url, ts, reloadIFrame);
164+
}
166165
}
167166
}
168167
}
@@ -171,9 +170,10 @@ class CDXLoader {
171170
// ===========================================================================
172171
class VueBannerWrapper
173172
{
174-
constructor(loader, url) {
173+
constructor(loader, url, ts) {
175174
this.loading = true;
176175
this.lastSurt = this.getSurt(url);
176+
this.lastTs = ts;
177177
this.loader = loader;
178178
}
179179

@@ -200,6 +200,9 @@ class VueBannerWrapper
200200
if (surt !== this.lastSurt) {
201201
this.loader.updateSnapshot(event.data.url, event.data.ts);
202202
this.lastSurt = surt;
203+
} else if (event.data.ts !== this.lastTs) {
204+
this.loader.updateTimestamp(event.data.url, event.data.ts);
205+
this.lastTs = event.data.ts;
203206
}
204207
}
205208
}

0 commit comments

Comments
 (0)