mirror of
https://github.com/webrecorder/pywb.git
synced 2025-03-15 00:03:28 +01:00
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>
This commit is contained in:
parent
2d19b6b18d
commit
3c94da04a2
12
CHANGES.rst
12
CHANGES.rst
@ -1,3 +1,15 @@
|
|||||||
|
pywb 2.7.2 changelist
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Fix regression introduced by improper wombat update in 2.7.1
|
||||||
|
* Fix `redirect_to_exact: false` functionality: if not set, UI will stay on current timestamp, but will display info on actual capture.
|
||||||
|
* Location bar nav now keeps current timestamp instead of defaulting to calendar view.
|
||||||
|
* 'Live' mode fixes, no longer cache live cdx entry, don't add timestamp when navigating in live mode without timestamp
|
||||||
|
* Calendar dropdown on replay now scrollable.
|
||||||
|
* Timeline toggle on replay is 'sticky', will stay on if toggled on replay.
|
||||||
|
* Capture text: use '|' as in 'Current Capture: [title] | [capture date]'
|
||||||
|
* Document title: Add 'Archived Page: ' prefix to avoid confusion with live pages.
|
||||||
|
|
||||||
pywb 2.7.1 changelist
|
pywb 2.7.1 changelist
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -414,6 +414,14 @@ class FrontEndApp(object):
|
|||||||
# if coll == self.all_coll:
|
# if coll == self.all_coll:
|
||||||
# coll = '*'
|
# coll = '*'
|
||||||
|
|
||||||
|
config = self.warcserver.get_coll_config(coll)
|
||||||
|
is_live = config.get("index") == "$live"
|
||||||
|
|
||||||
|
if is_live:
|
||||||
|
cache_control = "no-store, no-cache"
|
||||||
|
else:
|
||||||
|
cache_control = "max-age=86400, must-revalidate"
|
||||||
|
|
||||||
cdx_url = base_url.format(coll=coll)
|
cdx_url = base_url.format(coll=coll)
|
||||||
|
|
||||||
if environ.get('QUERY_STRING'):
|
if environ.get('QUERY_STRING'):
|
||||||
@ -433,7 +441,7 @@ class FrontEndApp(object):
|
|||||||
return WbResponse.bin_stream(StreamIter(res.raw),
|
return WbResponse.bin_stream(StreamIter(res.raw),
|
||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
status=status_line,
|
status=status_line,
|
||||||
headers=[("Cache-Control", "max-age=86400, must-revalidate")])
|
headers=[("Cache-Control", cache_control)])
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return WbResponse.text_response('Error: ' + str(e), status='400 Bad Request')
|
return WbResponse.text_response('Error: ' + str(e), status='400 Bad Request')
|
||||||
|
@ -408,6 +408,8 @@ class TopFrameView(BaseInsertView):
|
|||||||
timestamp = ''
|
timestamp = ''
|
||||||
if wb_url.timestamp:
|
if wb_url.timestamp:
|
||||||
timestamp = wb_url.timestamp
|
timestamp = wb_url.timestamp
|
||||||
|
#else:
|
||||||
|
# timestamp = timestamp_now()
|
||||||
|
|
||||||
is_proxy = 'wsgiprox.proxy_host' in env
|
is_proxy = 'wsgiprox.proxy_host' in env
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -61,6 +61,7 @@
|
|||||||
"capture": "{{ _Q('capture') }}",
|
"capture": "{{ _Q('capture') }}",
|
||||||
"captures": "{{ _Q('captures') }}",
|
"captures": "{{ _Q('captures') }}",
|
||||||
"from {hour1} to {hour2}": "{{ _Q('from {hour1} to {hour2}') }}",
|
"from {hour1} to {hour2}": "{{ _Q('from {hour1} to {hour2}') }}",
|
||||||
"no captures": "{{ _Q('no captures') }}"
|
"no captures": "{{ _Q('no captures') }}",
|
||||||
|
"Archived Page: ": "{{ _Q('Archived Page: ') }}"
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
__version__ = '2.7.1'
|
__version__ = '2.7.2'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print(__version__)
|
print(__version__)
|
||||||
|
@ -69,7 +69,7 @@
|
|||||||
class="btn btn-sm"
|
class="btn btn-sm"
|
||||||
:class="{active: showTimelineView, 'btn-outline-light': lightButtons, 'btn-outline-dark': !lightButtons}"
|
:class="{active: showTimelineView, 'btn-outline-light': lightButtons, 'btn-outline-dark': !lightButtons}"
|
||||||
:aria-pressed="showTimelineView"
|
:aria-pressed="showTimelineView"
|
||||||
@click="showTimelineView = !showTimelineView"
|
@click="toggleTimelineView"
|
||||||
:title="(showTimelineView ? _('Hide timeline') : _('Show timeline'))">
|
:title="(showTimelineView ? _('Hide timeline') : _('Show timeline'))">
|
||||||
<i class="far fa-chart-bar"></i>
|
<i class="far fa-chart-bar"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -113,7 +113,7 @@
|
|||||||
{{ config.title }}
|
{{ config.title }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="mr-1" v-if="config.title">,</span>
|
<span class="mr-1" v-if="config.title">|</span>
|
||||||
{{currentSnapshot.getTimeDateFormatted()}}
|
{{currentSnapshot.getTimeDateFormatted()}}
|
||||||
</span>
|
</span>
|
||||||
</nav>
|
</nav>
|
||||||
@ -142,7 +142,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Calendar -->
|
<!-- Calendar -->
|
||||||
<div class="card" v-if="currentPeriod && showFullView && currentPeriod.children.length">
|
<div class="card" id="calendar-card" v-if="currentPeriod && showFullView && currentPeriod.children.length">
|
||||||
<div class="card-body" id="calendar-card-body">
|
<div class="card-body" id="calendar-card-body">
|
||||||
<CalendarYear
|
<CalendarYear
|
||||||
:period="currentPeriod"
|
:period="currentPeriod"
|
||||||
@ -173,8 +173,8 @@ export default {
|
|||||||
currentSnapshot: null,
|
currentSnapshot: null,
|
||||||
currentSnapshotIndex: null,
|
currentSnapshotIndex: null,
|
||||||
msgs: [],
|
msgs: [],
|
||||||
showFullView: true,
|
showFullView: false,
|
||||||
showTimelineView: true,
|
showTimelineView: false,
|
||||||
maxTimelineZoomLevel: PywbPeriod.Type.day,
|
maxTimelineZoomLevel: PywbPeriod.Type.day,
|
||||||
config: {
|
config: {
|
||||||
title: "",
|
title: "",
|
||||||
@ -194,7 +194,7 @@ export default {
|
|||||||
},
|
},
|
||||||
updated: function() {
|
updated: function() {
|
||||||
// set top frame title equal to value pulled from replay frame
|
// set top frame title equal to value pulled from replay frame
|
||||||
document.title = this.config.title;
|
document.title = this._("Archived Page: ") + this.config.title;
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
sessionStorageUrlKey() {
|
sessionStorageUrlKey() {
|
||||||
@ -281,7 +281,7 @@ export default {
|
|||||||
if (reloadIFrame !== false) {
|
if (reloadIFrame !== false) {
|
||||||
this.$emit("show-snapshot", snapshot);
|
this.$emit("show-snapshot", snapshot);
|
||||||
}
|
}
|
||||||
this.hideBannerUtilities();
|
this.initBannerState(true);
|
||||||
},
|
},
|
||||||
gotoPreviousSnapshot() {
|
gotoPreviousSnapshot() {
|
||||||
let periodToChangeTo = this.currentPeriod.findByFullId(this.previousSnapshot.getFullId());
|
let periodToChangeTo = this.currentPeriod.findByFullId(this.previousSnapshot.getFullId());
|
||||||
@ -294,10 +294,15 @@ export default {
|
|||||||
gotoUrl(event) {
|
gotoUrl(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const newUrl = document.querySelector("#theurl").value;
|
const newUrl = document.querySelector("#theurl").value;
|
||||||
if (newUrl !== this.url) {
|
if (newUrl !== this.config.url) {
|
||||||
window.location.href = this.config.prefix + "*/" + newUrl;
|
const ts = this.config.timestamp === undefined ? "*" : this.config.timestamp;
|
||||||
|
window.location.href = this.config.prefix + ts + (ts ? "/" : "") + newUrl;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
toggleTimelineView() {
|
||||||
|
this.showTimelineView = !this.showTimelineView;
|
||||||
|
window.localStorage.setItem("showTimelineView", this.showTimelineView ? "1" : "0");
|
||||||
|
},
|
||||||
setData(/** @type {PywbData} data */ data) {
|
setData(/** @type {PywbData} data */ data) {
|
||||||
|
|
||||||
// data-set will usually happen at App INIT (from parent caller)
|
// data-set will usually happen at App INIT (from parent caller)
|
||||||
@ -321,6 +326,10 @@ export default {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
setSnapshot(view) {
|
setSnapshot(view) {
|
||||||
|
if (!this.currentPeriod) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// turn off calendar (aka full) view
|
// turn off calendar (aka full) view
|
||||||
this.showFullView = false;
|
this.showFullView = false;
|
||||||
|
|
||||||
@ -332,18 +341,20 @@ export default {
|
|||||||
let periodToChangeTo = this.currentPeriod.findByFullId(snapshot.getFullId());
|
let periodToChangeTo = this.currentPeriod.findByFullId(snapshot.getFullId());
|
||||||
if (periodToChangeTo) {
|
if (periodToChangeTo) {
|
||||||
this.gotoPeriod(periodToChangeTo, false /* onlyZoomToPeriod */);
|
this.gotoPeriod(periodToChangeTo, false /* onlyZoomToPeriod */);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
setTimelineView() {
|
initBannerState(isReplay) {
|
||||||
this.showTimelineView = !this.showTimelineView;
|
// if not replay, always show both
|
||||||
if (this.showTimelineView === true) {
|
if (!isReplay) {
|
||||||
|
this.showFullView = true;
|
||||||
|
this.showTimelineView = true;
|
||||||
|
} else {
|
||||||
this.showFullView = false;
|
this.showFullView = false;
|
||||||
|
this.showTimelineView = window.localStorage.getItem("showTimelineView") === "1";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hideBannerUtilities() {
|
|
||||||
this.showFullView = false;
|
|
||||||
this.showTimelineView = false;
|
|
||||||
},
|
|
||||||
updateTitle(title) {
|
updateTitle(title) {
|
||||||
this.config.title = title;
|
this.config.title = title;
|
||||||
}
|
}
|
||||||
@ -361,7 +372,10 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.app.expanded {
|
.app.expanded {
|
||||||
height: 130px;
|
/*height: 130px;*/
|
||||||
|
max-height: calc(100vh - 90px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.full-view {
|
.full-view {
|
||||||
/*position: fixed;*/
|
/*position: fixed;*/
|
||||||
@ -449,6 +463,10 @@ export default {
|
|||||||
div.timeline-wrap div.card {
|
div.timeline-wrap div.card {
|
||||||
margin-top: 55px;
|
margin-top: 55px;
|
||||||
}
|
}
|
||||||
|
#calendar-card {
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
div.timeline-wrap div.card-body {
|
div.timeline-wrap div.card-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -459,6 +477,7 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#calendar-card-body {
|
#calendar-card-body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ export default {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding-bottom: 1em;
|
padding-bottom: 1em;
|
||||||
|
@ -23,7 +23,8 @@ class CDXLoader {
|
|||||||
this.logoUrl = logoUrl;
|
this.logoUrl = logoUrl;
|
||||||
this.navbarBackground = navbarBackground;
|
this.navbarBackground = navbarBackground;
|
||||||
this.navbarColor = navbarColor;
|
this.navbarColor = navbarColor;
|
||||||
this.navbarLightButtons = navbarLightButtons
|
this.navbarLightButtons = navbarLightButtons;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
|
||||||
this.isReplay = (timestamp !== undefined);
|
this.isReplay = (timestamp !== undefined);
|
||||||
|
|
||||||
@ -35,11 +36,10 @@ class CDXLoader {
|
|||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
if (this.isReplay) {
|
if (this.isReplay) {
|
||||||
window.WBBanner = new VueBannerWrapper(this, url);
|
window.WBBanner = new VueBannerWrapper(this, url, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
let queryURL;
|
let queryURL;
|
||||||
let isQueryURL = window.location.href.indexOf("*") > -1 ? true : false;
|
|
||||||
|
|
||||||
// query form *?=url...
|
// query form *?=url...
|
||||||
if (window.location.href.indexOf("*?") > 0) {
|
if (window.location.href.indexOf("*?") > 0) {
|
||||||
@ -60,9 +60,10 @@ class CDXLoader {
|
|||||||
|
|
||||||
const logoImg = this.staticPrefix + "/" + (this.logoUrl ? this.logoUrl : "pywb-logo-sm.png");
|
const logoImg = this.staticPrefix + "/" + (this.logoUrl ? this.logoUrl : "pywb-logo-sm.png");
|
||||||
|
|
||||||
this.app = this.initApp({logoImg, navbarBackground, navbarColor, navbarLightButtons, url, allLocales});
|
this.app = this.initApp({logoImg, navbarBackground, navbarColor, navbarLightButtons, url, allLocales, timestamp});
|
||||||
|
|
||||||
this.loadCDX(queryURL).then((cdxList) => {
|
this.loadCDX(queryURL).then((cdxList) => {
|
||||||
this.setAppData(cdxList, url, isQueryURL, timestamp);
|
this.setAppData(cdxList, url, this.timestamp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,19 +74,7 @@ class CDXLoader {
|
|||||||
|
|
||||||
app.$mount("#app");
|
app.$mount("#app");
|
||||||
|
|
||||||
// TODO (Ilya): make this work with in-page snapshot/capture/replay updates!
|
app.$on("show-snapshot", (snapshot) => this.loadSnapshot(snapshot));
|
||||||
// app.$on("show-snapshot", snapshot => {
|
|
||||||
// const replayUrl = app.config.url;
|
|
||||||
// const url = location.href.replace('/'+replayUrl, '').replace(/\d+$/, '') + snapshot.id + '/' + replayUrl;
|
|
||||||
// window.history.pushState({url: replayUrl, timestamp: snapshot.id}, document.title, url);
|
|
||||||
// if (!window.onpopstate) {
|
|
||||||
// window.onpopstate = (ev) => {
|
|
||||||
// updateSnapshot(ev.state.url, ev.state.timestamp);
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
app.$on("show-snapshot", this.loadSnapshot.bind(this));
|
|
||||||
app.$on("data-set-and-render-completed", () => {
|
app.$on("data-set-and-render-completed", () => {
|
||||||
if (this.loadingSpinner) {
|
if (this.loadingSpinner) {
|
||||||
this.loadingSpinner.setOff(); // only turn off loading-spinner AFTER app has told us it is DONE DONE
|
this.loadingSpinner.setOff(); // only turn off loading-spinner AFTER app has told us it is DONE DONE
|
||||||
@ -101,31 +90,37 @@ class CDXLoader {
|
|||||||
params.set("url", url);
|
params.set("url", url);
|
||||||
params.set("output", "json");
|
params.set("output", "json");
|
||||||
const queryURL = this.prefix + "cdx?" + params.toString();
|
const queryURL = this.prefix + "cdx?" + params.toString();
|
||||||
let isQueryURL = window.location.href.indexOf("*") > -1 ? true : false;
|
|
||||||
|
|
||||||
const cdxList = await this.loadCDX(queryURL);
|
const cdxList = await this.loadCDX(queryURL);
|
||||||
|
|
||||||
this.setAppData(cdxList, url, isQueryURL, timestamp);
|
this.setAppData(cdxList, url, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAppData(cdxList, url, isQueryURL, timestamp="") {
|
async updateTimestamp(url, timestamp) {
|
||||||
this.app.setData(new PywbData(cdxList));
|
this.timestamp = timestamp;
|
||||||
|
|
||||||
// if this is a capture but we don't have a timestamp (e.g. if redirect_to_exact is false)
|
if (this.cdxLoading) {
|
||||||
// set the timestamp to the latest capture
|
return;
|
||||||
if ((!timestamp) && (!isQueryURL)) {
|
|
||||||
const lastSnapshot = cdxList[cdxList.length - 1];
|
|
||||||
timestamp = lastSnapshot.timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.app.setSnapshot({url, timestamp});
|
||||||
|
}
|
||||||
|
|
||||||
|
setAppData(cdxList, url, timestamp) {
|
||||||
|
this.app.setData(new PywbData(cdxList));
|
||||||
|
|
||||||
|
this.app.initBannerState(this.isReplay);
|
||||||
|
|
||||||
|
// if set on initial load, may not have timestamp yet
|
||||||
|
// will be updated later
|
||||||
if (timestamp) {
|
if (timestamp) {
|
||||||
this.app.hideBannerUtilities();
|
this.updateTimestamp(url, timestamp);
|
||||||
this.app.setSnapshot({url, timestamp});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadCDX(queryURL) {
|
async loadCDX(queryURL) {
|
||||||
// this.loadingSpinner.setOn(); // start loading-spinner when CDX loading begins
|
// this.loadingSpinner.setOn(); // start loading-spinner when CDX loading begins
|
||||||
|
this.cdxLoading = true;
|
||||||
const queryWorker = new Worker(this.staticPrefix + "/queryWorker.js");
|
const queryWorker = new Worker(this.staticPrefix + "/queryWorker.js");
|
||||||
|
|
||||||
const p = new Promise((resolve) => {
|
const p = new Promise((resolve) => {
|
||||||
@ -139,6 +134,7 @@ class CDXLoader {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "finished":
|
case "finished":
|
||||||
|
this.cdxLoading = false;
|
||||||
resolve(cdxList);
|
resolve(cdxList);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -162,7 +158,10 @@ class CDXLoader {
|
|||||||
if (!this.isReplay) {
|
if (!this.isReplay) {
|
||||||
window.location.href = this.prefix + snapshot.id + "/" + snapshot.url;
|
window.location.href = this.prefix + snapshot.id + "/" + snapshot.url;
|
||||||
} else if (window.cframe) {
|
} else if (window.cframe) {
|
||||||
window.cframe.load_url(snapshot.url, snapshot.id + "", reloadIFrame);
|
const ts = snapshot.id + "";
|
||||||
|
if (ts !== this.timestamp) {
|
||||||
|
window.cframe.load_url(snapshot.url, ts, reloadIFrame);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,9 +170,10 @@ class CDXLoader {
|
|||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
class VueBannerWrapper
|
class VueBannerWrapper
|
||||||
{
|
{
|
||||||
constructor(loader, url) {
|
constructor(loader, url, ts) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.lastSurt = this.getSurt(url);
|
this.lastSurt = this.getSurt(url);
|
||||||
|
this.lastTs = ts;
|
||||||
this.loader = loader;
|
this.loader = loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +200,9 @@ class VueBannerWrapper
|
|||||||
if (surt !== this.lastSurt) {
|
if (surt !== this.lastSurt) {
|
||||||
this.loader.updateSnapshot(event.data.url, event.data.ts);
|
this.loader.updateSnapshot(event.data.url, event.data.ts);
|
||||||
this.lastSurt = surt;
|
this.lastSurt = surt;
|
||||||
|
} else if (event.data.ts !== this.lastTs) {
|
||||||
|
this.loader.updateTimestamp(event.data.url, event.data.ts);
|
||||||
|
this.lastTs = event.data.ts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
wombat
2
wombat
@ -1 +1 @@
|
|||||||
Subproject commit 74a6087d41d335c371e3a1f52f0f008944705118
|
Subproject commit 04ca325f3a59e7efc8ad0fa5abe25ec1bc9d9620
|
Loading…
x
Reference in New Issue
Block a user