mirror of
https://github.com/webrecorder/pywb.git
synced 2025-03-15 00:03:28 +01:00
ui/templates: fixes based on feedback from @ldko!
templates: add placeholder templates (footer.html, head.html) templates: allow 'base_html', 'footer_html', 'head_html', 'header_html' to be added via wb-manager 'add-template' cmd ui: fix logo path, support linking ui: make url on banner an input field docs: clarify docs around templates, paths
This commit is contained in:
parent
29860bcb24
commit
6260b226ce
@ -60,6 +60,15 @@ When using the custom banner, it is possible to configure a logo by setting ``ui
|
|||||||
|
|
||||||
If omitted, the standard pywb logo will be used by default.
|
If omitted, the standard pywb logo will be used by default.
|
||||||
|
|
||||||
|
If set, the logo should point to a file in the static directory (default is ``static`` but can be changed via the ``static_dir`` config option).
|
||||||
|
|
||||||
|
For example, to use the file ``./static/my-logo.png`` as the logo, set:
|
||||||
|
|
||||||
|
.. code:: yaml
|
||||||
|
|
||||||
|
ui:
|
||||||
|
logo: my-logo.png
|
||||||
|
|
||||||
|
|
||||||
Updating the Vue UI
|
Updating the Vue UI
|
||||||
-------------------
|
-------------------
|
||||||
|
@ -61,6 +61,9 @@ can also be overriden:
|
|||||||
* ``footer.html`` -- Template for adding content as the "footer" of the ``<body>`` tag of the ``base`` template
|
* ``footer.html`` -- Template for adding content as the "footer" of the ``<body>`` tag of the ``base`` template
|
||||||
|
|
||||||
|
|
||||||
|
Note: The default pywb ``head.html`` and ``footer.html`` are currently blank. They can be populated to customize the rendering, add analytics, etc... as needed.
|
||||||
|
|
||||||
|
|
||||||
The ``base.html`` template also provides five blocks that can be supplied by templates that extend it.
|
The ``base.html`` template also provides five blocks that can be supplied by templates that extend it.
|
||||||
|
|
||||||
* ``title`` -- Block for supplying the title for the page
|
* ``title`` -- Block for supplying the title for the page
|
||||||
@ -157,7 +160,7 @@ Template variables:
|
|||||||
|
|
||||||
* ``{{ ui }}`` - an optional ``ui`` dictionary from ``config.yaml``, if any
|
* ``{{ ui }}`` - an optional ``ui`` dictionary from ``config.yaml``, if any
|
||||||
|
|
||||||
* ``{{ static_prefix }}`` - the prefix from which static files will be accessed from, e.g. ``http://localhost:8080/static/``
|
* ``{{ static_prefix }}`` - the prefix from which static files will be accessed from, e.g. ``http://localhost:8080/static/``.
|
||||||
|
|
||||||
|
|
||||||
Replay and Banner Templates
|
Replay and Banner Templates
|
||||||
@ -186,6 +189,8 @@ Template variables:
|
|||||||
|
|
||||||
* ``{{ wb_prefix }}`` - the collection prefix, e.g. ``http://localhost:8080/pywb/``
|
* ``{{ wb_prefix }}`` - the collection prefix, e.g. ``http://localhost:8080/pywb/``
|
||||||
|
|
||||||
|
* ``{{ host_prefix }}`` - the pywb server origin, e.g. ``http://localhost:8080``
|
||||||
|
|
||||||
* ``{{ config }}`` - provides the contents of the ``config.yaml`` as a dictionary.
|
* ``{{ config }}`` - provides the contents of the ``config.yaml`` as a dictionary.
|
||||||
|
|
||||||
* ``{{ ui }}`` - an optional ``ui`` dictionary from ``config.yaml``, if any.
|
* ``{{ ui }}`` - an optional ``ui`` dictionary from ``config.yaml``, if any.
|
||||||
@ -232,10 +237,10 @@ Template variables:
|
|||||||
|
|
||||||
* ``{{ wb_url }}`` - A complete ``WbUrl`` object, which contains the ``url``, ``timestamp`` and ``mod`` properties, representing the replay url.
|
* ``{{ wb_url }}`` - A complete ``WbUrl`` object, which contains the ``url``, ``timestamp`` and ``mod`` properties, representing the replay url.
|
||||||
|
|
||||||
* ``{{ is_framed }}`` - true/false if currently in framed mode.
|
|
||||||
|
|
||||||
* ``{{ wb_prefix }}`` - the collection prefix, e.g. ``http://localhost:8080/pywb/``
|
* ``{{ wb_prefix }}`` - the collection prefix, e.g. ``http://localhost:8080/pywb/``
|
||||||
|
|
||||||
|
* ``{{ is_proxy }}`` - set to true if page is being loaded via an HTTP/S proxy (checks if WSGI env has ``wsgiprox.proxy_host`` set)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _custom-top-frame:
|
.. _custom-top-frame:
|
||||||
@ -332,7 +337,7 @@ The following template variables are available to all templates.
|
|||||||
|
|
||||||
* ``{{ env.pywb_proxy_magic }}`` - if set, indicates pywb is accessed via proxy. See :ref:`https-proxy`
|
* ``{{ env.pywb_proxy_magic }}`` - if set, indicates pywb is accessed via proxy. See :ref:`https-proxy`
|
||||||
|
|
||||||
* ``{{ static_prefix }}`` - path to use for loading static files.
|
* ``{{ static_prefix }}`` - URL path to use for loading static files.
|
||||||
|
|
||||||
|
|
||||||
UI Configuration
|
UI Configuration
|
||||||
|
@ -25,12 +25,13 @@ To enable the logo set the ``ui.logo`` property in ``config.yaml`` to point to t
|
|||||||
|
|
||||||
The URL can be any image URL, including a URL served from the static directory.
|
The URL can be any image URL, including a URL served from the static directory.
|
||||||
|
|
||||||
For example, to add the default pywb logo to the banner, use the following in the config:
|
For example, to add the default pywb logo to the banner, use the following in the config, which will
|
||||||
|
load the logo from ``./static/pywb-logo-sm.png``
|
||||||
|
|
||||||
.. code:: yaml
|
.. code:: yaml
|
||||||
|
|
||||||
ui:
|
ui:
|
||||||
logo: /static/pywb-logo-sm.png
|
logo: pywb-logo-sm.png
|
||||||
|
|
||||||
|
|
||||||
New Vue-based UI (Alpha)
|
New Vue-based UI (Alpha)
|
||||||
@ -66,7 +67,7 @@ It is possible to change these settings via ``config.yaml``:
|
|||||||
|
|
||||||
* ``static_prefix`` - sets the URL path used in pywb to serve static content (default ``static``)
|
* ``static_prefix`` - sets the URL path used in pywb to serve static content (default ``static``)
|
||||||
|
|
||||||
* ``static_dir`` - sets the directory name used to read static files (default ``static``)
|
* ``static_dir`` - sets the directory name used to read static files on disk (default ``static``)
|
||||||
|
|
||||||
While pywb can serve static files, it is recommended to use an existing web server to serve static files, especially if already using it in production.
|
While pywb can serve static files, it is recommended to use an existing web server to serve static files, especially if already using it in production.
|
||||||
|
|
||||||
|
@ -15,6 +15,11 @@ banner_html: banner.html
|
|||||||
head_insert_html: head_insert.html
|
head_insert_html: head_insert.html
|
||||||
frame_insert_html: frame_insert.html
|
frame_insert_html: frame_insert.html
|
||||||
|
|
||||||
|
base_html: base.html
|
||||||
|
header_html: header.html
|
||||||
|
footer_html: footer.html
|
||||||
|
head_html: head.html
|
||||||
|
|
||||||
query_html: query.html
|
query_html: query.html
|
||||||
search_html: search.html
|
search_html: search.html
|
||||||
not_found_html: not_found.html
|
not_found_html: not_found.html
|
||||||
@ -39,6 +44,12 @@ html_templates:
|
|||||||
- not_found_html
|
- not_found_html
|
||||||
|
|
||||||
- home_html
|
- home_html
|
||||||
|
|
||||||
|
- base_html
|
||||||
|
- header_html
|
||||||
|
- head_html
|
||||||
|
- footer_html
|
||||||
|
|
||||||
- error_html
|
- error_html
|
||||||
- proxy_cert_download_html
|
- proxy_cert_download_html
|
||||||
- proxy_select_html
|
- proxy_select_html
|
||||||
|
@ -237,17 +237,20 @@ directory structure expected by pywb
|
|||||||
v = defaults[n]
|
v = defaults[n]
|
||||||
print('- {0}: (pywb/{1})'.format(n, v))
|
print('- {0}: (pywb/{1})'.format(n, v))
|
||||||
|
|
||||||
def _confirm_overwrite(self, full_path, msg):
|
def _confirm_overwrite(self, full_path, msg, ignore=False):
|
||||||
if not os.path.isfile(full_path):
|
if not os.path.isfile(full_path):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if ignore:
|
||||||
|
return False
|
||||||
|
|
||||||
res = get_input(msg)
|
res = get_input(msg)
|
||||||
try:
|
try:
|
||||||
res = strtobool(res)
|
res = strtobool(res)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
res = False
|
res = False
|
||||||
|
|
||||||
if not res:
|
if not res and not ignore:
|
||||||
raise IOError('Skipping, {0} already exists'.format(full_path))
|
raise IOError('Skipping, {0} already exists'.format(full_path))
|
||||||
|
|
||||||
def _get_template_path(self, template_name, verb):
|
def _get_template_path(self, template_name, verb):
|
||||||
@ -268,7 +271,7 @@ directory structure expected by pywb
|
|||||||
|
|
||||||
return full_path, filename
|
return full_path, filename
|
||||||
|
|
||||||
def add_template(self, template_name, force=False):
|
def add_template(self, template_name, force=False, ignore=False):
|
||||||
full_path, filename = self._get_template_path(template_name, 'add')
|
full_path, filename = self._get_template_path(template_name, 'add')
|
||||||
|
|
||||||
msg = ('Template file "{0}" ({1}) already exists. ' +
|
msg = ('Template file "{0}" ({1}) already exists. ' +
|
||||||
@ -276,7 +279,11 @@ directory structure expected by pywb
|
|||||||
msg = msg.format(full_path, template_name)
|
msg = msg.format(full_path, template_name)
|
||||||
|
|
||||||
if not force:
|
if not force:
|
||||||
self._confirm_overwrite(full_path, msg)
|
res = self._confirm_overwrite(full_path, msg, ignore)
|
||||||
|
if ignore and not res:
|
||||||
|
return
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
||||||
|
|
||||||
data = resource_string('pywb', filename)
|
data = resource_string('pywb', filename)
|
||||||
with open(full_path, 'w+b') as fh:
|
with open(full_path, 'w+b') as fh:
|
||||||
@ -286,6 +293,9 @@ directory structure expected by pywb
|
|||||||
msg = 'Copied default template "{0}" to "{1}"'
|
msg = 'Copied default template "{0}" to "{1}"'
|
||||||
print(msg.format(filename, full_path))
|
print(msg.format(filename, full_path))
|
||||||
|
|
||||||
|
if template_name != "base_html":
|
||||||
|
self.add_template("base_html", force=False, ignore=True)
|
||||||
|
|
||||||
def remove_template(self, template_name, force=False):
|
def remove_template(self, template_name, force=False):
|
||||||
full_path, filename = self._get_template_path(template_name, 'remove')
|
full_path, filename = self._get_template_path(template_name, 'remove')
|
||||||
|
|
||||||
|
@ -164,8 +164,9 @@ This file is part of pywb, https://github.com/webrecorder/pywb
|
|||||||
logo.setAttribute("class", "_wb_linked_logo");
|
logo.setAttribute("class", "_wb_linked_logo");
|
||||||
|
|
||||||
var logoContents = "";
|
var logoContents = "";
|
||||||
logoContents += "<img src='" + window.banner_info.logoImg + "' alt='" + window.banner_info.logoAlt + "'>";
|
var logoUrl = window.banner_info.staticPrefix + "/" + window.banner_info.logoImg;
|
||||||
logoContents += "<img src='" + window.banner_info.logoImg + "' class='_wb_mobile' alt='" + window.banner_info.logoAlt + "'>";
|
logoContents += "<img src='" + logoUrl + "' alt='" + window.banner_info.logoAlt + "'>";
|
||||||
|
logoContents += "<img src='" + logoUrl + "' class='_wb_mobile' alt='" + window.banner_info.logoAlt + "'>";
|
||||||
|
|
||||||
logo.innerHTML = logoContents;
|
logo.innerHTML = logoContents;
|
||||||
this.banner.appendChild(logo);
|
this.banner.appendChild(logo);
|
||||||
|
File diff suppressed because one or more lines are too long
@ -34,7 +34,7 @@ window.banner_info = {
|
|||||||
<link rel='stylesheet' href='{{ static_prefix }}/vue_banner.css'/>
|
<link rel='stylesheet' href='{{ static_prefix }}/vue_banner.css'/>
|
||||||
<script src="{{ static_prefix }}/vue/vueui.js"></script>
|
<script src="{{ static_prefix }}/vue/vueui.js"></script>
|
||||||
<script>
|
<script>
|
||||||
VueUI.main("{{ static_prefix }}", "{{ url }}", "{{ wb_prefix }}", "{{ timestamp }}");
|
VueUI.main("{{ static_prefix }}", "{{ url }}", "{{ wb_prefix }}", "{{ timestamp }}", "{{ ui.logo }}");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% if ui.vue_timeline_banner %}
|
{% if ui.vue_timeline_banner %}
|
||||||
|
2
pywb/templates/footer.html
Normal file
2
pywb/templates/footer.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{# place content to be added at the very end of the <body> tag in this file below #}
|
||||||
|
|
1
pywb/templates/head.html
Normal file
1
pywb/templates/head.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
{# place optional content to be injected into the <head> of every page in this file below #}
|
@ -1,3 +1,4 @@
|
|||||||
|
{# place content to be added at the very beginning of the <body> tag in this file below #}
|
||||||
<header>
|
<header>
|
||||||
{% if not err_msg and locales|length > 1 %}
|
{% if not err_msg and locales|length > 1 %}
|
||||||
<div class="language-select">
|
<div class="language-select">
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
{% for route in routes %}
|
{% for route in routes %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ env['pywb.app_prefix'] + ('/' + env.pywb_lang if env.pywb_lang else '') + '/' + route }}">{{ '/' + route }}</a>
|
<a href="{{ env['pywb.app_prefix'] + ('/' + env.pywb_lang if env.pywb_lang else '') + '/' + route }}">{{ '/' + route }}</a>
|
||||||
{% if all_metadata and all_metadata[route] %}
|
{% if all_metadata and all_metadata[route] and all_metadata[route].title %}
|
||||||
({{ all_metadata[route].title }})
|
({{ all_metadata[route].title }})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
renderCal.init();
|
renderCal.init();
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
VueUI.main("{{ static_prefix }}", "{{ url }}", "{{ prefix }}");
|
VueUI.main("{{ static_prefix }}", "{{ url }}", "{{ prefix }}", undefined, "{{ ui.logo }}");
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
__version__ = '2.6.9'
|
__version__ = '2.7.0b1'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print(__version__)
|
print(__version__)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="app" :class="{expanded: showTimelineView}" data-app="webrecorder-replay-app">
|
<div class="app" :class="{expanded: showTimelineView}" data-app="webrecorder-replay-app">
|
||||||
<div class="banner">
|
<div class="banner">
|
||||||
<div class="line">
|
<div class="line">
|
||||||
<div class="logo"><img :src="config.logoImg" /></div>
|
<div class="logo"><a href="/"><img :src="config.logoImg" style="max-width: 80px" /></a></div>
|
||||||
<div class="timeline-wrap">
|
<div class="timeline-wrap">
|
||||||
<div class="line">
|
<div class="line">
|
||||||
<div class="breadcrumbs-wrap">
|
<div class="breadcrumbs-wrap">
|
||||||
@ -35,13 +35,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="snapshot-title">
|
<div class="snapshot-title">
|
||||||
<div>{{ config.url }}</div>
|
<form @submit="gotoUrl">
|
||||||
|
<input id="theurl" type="text" :value="config.url"></input>
|
||||||
|
</form>
|
||||||
<div v-if="currentSnapshot && !showFullView">
|
<div v-if="currentSnapshot && !showFullView">
|
||||||
<span v-if="config.title">{{ config.title }}</span>
|
<span v-if="config.title">{{ config.title }}</span>
|
||||||
Current capture: {{currentSnapshot.getTimeDateFormatted()}}
|
Current capture: {{currentSnapshot.getTimeDateFormatted()}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CalendarYear v-if="showFullView"
|
<CalendarYear v-if="showFullView && currentPeriod && currentPeriod.children.length"
|
||||||
:period="currentPeriod"
|
:period="currentPeriod"
|
||||||
:current-snapshot="currentSnapshot"
|
:current-snapshot="currentSnapshot"
|
||||||
@goto-period="gotoPeriod">
|
@goto-period="gotoPeriod">
|
||||||
@ -124,6 +126,13 @@ export default {
|
|||||||
this.$emit("show-snapshot", snapshot);
|
this.$emit("show-snapshot", snapshot);
|
||||||
this.showFullView = false;
|
this.showFullView = false;
|
||||||
},
|
},
|
||||||
|
gotoUrl(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const newUrl = document.querySelector("#theurl").value;
|
||||||
|
if (newUrl !== this.url) {
|
||||||
|
window.location.href = this.config.prefix + "*/" + newUrl;
|
||||||
|
}
|
||||||
|
},
|
||||||
init() {
|
init() {
|
||||||
this.config.url = this.config.initialView.url;
|
this.config.url = this.config.initialView.url;
|
||||||
if (this.config.initialView.title) {
|
if (this.config.initialView.title) {
|
||||||
@ -134,8 +143,10 @@ export default {
|
|||||||
this.showTimelineView = true;
|
this.showTimelineView = true;
|
||||||
} else {
|
} else {
|
||||||
this.showFullView = false;
|
this.showFullView = false;
|
||||||
this.showFullView = true;
|
this.showTimelineView = true;
|
||||||
this.setSnapshot(this.config.initialView);
|
if (this.currentPeriod.children.length) {
|
||||||
|
this.setSnapshot(this.config.initialView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (window.sessionStorage) {
|
if (window.sessionStorage) {
|
||||||
const currentPeriodId = window.sessionStorage.getItem(this.sessionStorageUrlKey);
|
const currentPeriodId = window.sessionStorage.getItem(this.sessionStorageUrlKey);
|
||||||
@ -242,4 +253,7 @@ export default {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
#theurl {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -6,16 +6,17 @@ import Vue from "vue/dist/vue.esm.browser";
|
|||||||
|
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
export function main(staticPrefix, url, prefix, timestamp) {
|
export function main(staticPrefix, url, prefix, timestamp, logoUrl) {
|
||||||
new CDXLoader(staticPrefix, url, prefix, timestamp);
|
new CDXLoader(staticPrefix, url, prefix, timestamp, logoUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
class CDXLoader {
|
class CDXLoader {
|
||||||
constructor(staticPrefix, url, prefix, timestamp) {
|
constructor(staticPrefix, url, prefix, timestamp, logoUrl) {
|
||||||
this.opts = {};
|
this.opts = {};
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
this.staticPrefix = staticPrefix;
|
this.staticPrefix = staticPrefix;
|
||||||
|
this.logoUrl = logoUrl;
|
||||||
|
|
||||||
this.isReplay = (timestamp !== undefined);
|
this.isReplay = (timestamp !== undefined);
|
||||||
|
|
||||||
@ -44,8 +45,7 @@ class CDXLoader {
|
|||||||
|
|
||||||
this.opts.initialView = {url, timestamp};
|
this.opts.initialView = {url, timestamp};
|
||||||
|
|
||||||
// TODO: make configurable
|
this.opts.logoImg = this.staticPrefix + "/" + (this.logoUrl ? this.logoUrl : "pywb-logo-sm.png");
|
||||||
this.opts.logoImg = staticPrefix + "/pywb-logo-sm.png";
|
|
||||||
|
|
||||||
this.loadCDX(queryURL).then((cdxList) => {
|
this.loadCDX(queryURL).then((cdxList) => {
|
||||||
this.app = this.initApp(cdxList, this.opts, (snapshot) => this.loadSnapshot(snapshot));
|
this.app = this.initApp(cdxList, this.opts, (snapshot) => this.loadSnapshot(snapshot));
|
||||||
@ -60,7 +60,7 @@ class CDXLoader {
|
|||||||
app.$set(app, "snapshots", pywbData.snapshots);
|
app.$set(app, "snapshots", pywbData.snapshots);
|
||||||
app.$set(app, "currentPeriod", pywbData.timeline);
|
app.$set(app, "currentPeriod", pywbData.timeline);
|
||||||
|
|
||||||
app.$set(app, "config", {...app.config, ...config});
|
app.$set(app, "config", {...app.config, ...config, prefix: this.prefix});
|
||||||
|
|
||||||
app.$mount("#app");
|
app.$mount("#app");
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user