Skip to content

Commit

Permalink
Merge 2.7.1 development branch (#785)
Browse files Browse the repository at this point in the history
* Add locale-dependent handling of first day of week

The Intl.Locale is a proposed standard not yet supported by Firefox so
in Firefox the first day of week will default to Monday (as specified
in ISO-8601).

* Set top frame document title when Vue updates

* Update template guide for 2.7

* Drop Python 3.6 and add 3.10 in test CI

* Allow either JS mimetype in test_add_static

* Add convenience build script for Vue UI

* Add build flag to docker compose example

* Fix Vue app issue with redirect_to_exact: false

Fixes #779

Undated URLs were resulting in a broken calendar and timeline in the
Vue app when redirect_to_exact was set to false. This was due to
TopFrameView using the current datetime if no timestamp was included,
which caused a failed snapshot lookup in the Vue app.

This commit changes the default timestamp in TopFrameView to None and
adds additional logic in the Vue app to use the last snapshot's
timestamp as the default if one is not present to match the snapshot
that pywb loads by default under the same conditions.

* Add filter instead of submitting form when pressing enter in the filtering expression field

* Make filter expressions translatable

* Add missing tooltip strings to vue_loc

* Add changelog

* Bump version to 2.7.1

* Use empty string as default template timestamp

* Bump wombat to 3.3.13

Co-authored-by: Jonas Linde <[email protected]>
  • Loading branch information
tw4l and krakan authored Dec 8, 2022
1 parent 6cc9cdc commit 2d19b6b
Show file tree
Hide file tree
Showing 19 changed files with 168 additions and 80 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
max-parallel: 3
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: ['3.7', '3.8', '3.9', '3.10']

steps:
- name: checkout
Expand Down
10 changes: 10 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
pywb 2.7.1 changelist
~~~~~~~~~~~~~~~~~~~~~

* Add locale-dependent handling of first day of week by @krakan in https://github.com/webrecorder/pywb/pull/781
* Make filter expressions translatable by @krakan in https://github.com/webrecorder/pywb/pull/783
* Add title to top frame in framed replay
* Add missing tooltip translation strings
* Fix calendar and timeline rendering for replay URLs without a timestamp
* Update template documentation

pywb 2.7.0 changelist
~~~~~~~~~~~~~~~~~~~~~

Expand Down
8 changes: 3 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,11 @@ The first time you run this command, it make take some time to build.
Changes to the [Vue](https://vuejs.org/) frontend components require rebuilding the Vue bundle (`pywb/static/vue/vueui.js`) to take effect. After making changes to one or more Vue components, you can rebuild the static bundle and view the changes in your development environment like so:

```bash
cd pywb/vueui
yarn run build
cd ../..
docker compose up -d --force-recreate
./build-vue-ui.sh
docker compose up -d --build --force-recreate
```

Changes that modify pywb's Python dependencies or the operating system may require rebuilding the container:
Changes that modify pywb's Python dependencies or the operating system also require rebuilding the container:

```bash
docker compose up -d --build --force-recreate
Expand Down
6 changes: 6 additions & 0 deletions build-vue-ui.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

CURR_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

cd $CURR_DIR/pywb/vueui/
yarn run build
28 changes: 20 additions & 8 deletions docs/manual/template-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ Base Templates (and supporting templates)

File: ``base.html``

This template includes the HTML added to all other pages, replay and non-replay. Shared JS and CSS includes can be added here.
For theming all pywb UI, it may be useful to modify this template.
This template includes the HTML added to all pages other than framed replay. Shared JS and CSS includes meant for pages other than framed replay can be added here.

To customize the default pywb UI across multiple pages, the following additional templates
can also be overriden:
Expand All @@ -61,7 +60,7 @@ can also be overriden:
* ``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.
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. Content such as styles or JS code (for example for analytics) must be added to the ``frame_insert.html`` template as well (details on that template below) to also be included in framed replay.


The ``base.html`` template also provides five blocks that can be supplied by templates that extend it.
Expand Down Expand Up @@ -172,9 +171,7 @@ Banner Template

File: ``banner.html``

This template is used to render the banner and is used both in framed replay and frameless replay.

In framed replay, the template is only rendered in the top/outer frame, while in frameless replay, it is added to every page.
This template is used to render the banner for framed replay. It is rendered only rendered in the top/outer frame.

Template variables:

Expand All @@ -192,7 +189,17 @@ Template variables:

* ``{{ ui }}`` - an optional ``ui`` dictionary from ``config.yaml``, if any.

The default banner creates the UI dynamically in JavaScript using Vue.
The default banner creates the UI dynamically in JavaScript using Vue in the ``frame_insert.html`` template.


Custom Banner Template
^^^^^^^^^^^^^^^^^^^^^^

File: ``custom_banner.html``

This template can be used to render a custom banner for frameless replay. It is blank by default.

In frameless replay, the content of this template is injected into the ``head_insert.html`` template to render the banner.


Head Insert Template
Expand All @@ -204,7 +211,7 @@ This template represents the HTML injected into every replay page to add support

This template is part of the core pywb replay, and modifying this template is not recommended.

For customizing the banner, modify the ``banner.html`` template instead.
For customizing the banner, modify the ``banner.html`` (framed replay) or ``custom_banner.html`` (frameless replay) template instead.


Top Frame Template
Expand All @@ -221,16 +228,21 @@ This template is responsible for creating the iframe that will render the conten
This template only renders the banner and is designed *not* to set the encoding to allow the browser to 'detect' the encoding for the containing iframe.
For this reason, the template should only contain ASCII text, and %-encode any non-ASCII characters.

Content such as analytics code that is desired in the top frame of framed replay pages should be added to this template.

Template variables:

* ``{{ url }}`` - the URL being replayed.

* ``{{ timestamp }}`` - the timestamp being replayed, e.g. ``20211226`` in ``http://localhost:8080/pywb/20211226/mp_/https://example.com/``

* ``{{ wb_url }}`` - A complete ``WbUrl`` object, which contains the ``url``, ``timestamp`` and ``mod`` properties, representing the replay url.

* ``{{ 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)

* ``{{ ui }}`` - an optional ``ui`` dictionary from ``config.yaml``, if any.


.. _custom-top-frame:
Expand Down
3 changes: 1 addition & 2 deletions pywb/rewrite/templateview.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,10 +405,9 @@ def get_top_frame(self, wb_url,

embed_url = wb_url.to_str(mod=replay_mod)

timestamp = ''
if wb_url.timestamp:
timestamp = wb_url.timestamp
else:
timestamp = timestamp_now()

is_proxy = 'wsgiprox.proxy_host' in env

Expand Down
13 changes: 2 additions & 11 deletions pywb/static/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,6 @@ function RenderCalendar(init) {
};
// regex for extracting the filter constraints and filter mods to human explanation
this.filterRE = /filter([^a-z]+)([a-z]+):(.+)/i;
this.filterMods = {
'=': 'Contains',
'==': 'Matches Exactly',
'=~': 'Matches Regex',
'=!': 'Does Not Contains',
'=!=': 'Is Not',
'=!~': 'Does Not Begins With'
};
this.text = init.text;
this.versionString = null;
}
Expand Down Expand Up @@ -433,7 +425,6 @@ RenderCalendar.prototype.createContainers = function() {
return;
}
// create the advanced results query info DOM structure
var forString = ' for ';
var forElems;

if (this.queryInfo.searchParams.matchType) {
Expand Down Expand Up @@ -503,7 +494,7 @@ RenderCalendar.prototype.createContainers = function() {
{
tag: 'p',
className: 'text-center mb-0 mt-1',
innerText: 'Filtering by'
innerText: filteringBy
},
{
tag: 'ul',
Expand Down Expand Up @@ -950,7 +941,7 @@ RenderCalendar.prototype.niceFilterDisplay = function() {
filterList.push({
tag: 'li',
className: 'list-group-item',
innerText: match[2] + ' ' + this.filterMods[match[1]] + ' ' + match[3]
innerText: match[2] + ' ' + filterMods[match[1]] + ' "' + match[3] + '"'
});
}
}
Expand Down
51 changes: 31 additions & 20 deletions pywb/static/search.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
var dtRE = /^\d{4,14}$/;
var didSetWasValidated = false;
var showBadDateTimeClass = 'show-optional-bad-input';
var filterMods = {
'=': 'Contains',
'==': 'Matches Exactly',
'=~': 'Matches Regex',
'=!': 'Does Not Contains',
'=!=': 'Is Not',
'=!~': 'Does Not Begins With'
};

var elemIds = {
filtering: {
Expand Down Expand Up @@ -65,7 +57,7 @@ function makeCheckDateRangeChecker(dtInputId, dtBadNotice) {

function createAndAddNoFilter(filterList) {
var nothing = document.createElement('li');
nothing.innerText = 'No Filter';
nothing.innerText = noFilter;
nothing.id = elemIds.filtering.nothing;
filterList.appendChild(nothing);
}
Expand All @@ -78,19 +70,24 @@ function addFilter(event) {
if (!expr) return;
var filterExpr = 'filter' + modifier + by + ':' + expr;
var filterList = document.getElementById(elemIds.filtering.list);
var previousFilters = filterList.children;
for (var i = 0; i < previousFilters.length; ++i) {
var filterData = previousFilters[i].dataset;
if (filterData && filterData.filter && filterData.filter == filterExpr) return;
}
var filterNothing = document.getElementById(elemIds.filtering.nothing);
if (filterNothing) {
filterList.removeChild(filterNothing);
}
var li = document.createElement('li');
li.innerText =
'By ' +
by[0].toUpperCase() +
by.substr(1) +
' ' +
filterMods[modifier] +
' ' +
expr;
' "' +
expr +
'"';
li.dataset.filter = filterExpr;
var nukeButton = document.createElement('button');
nukeButton.type = 'button';
Expand All @@ -110,6 +107,7 @@ function addFilter(event) {
};
li.appendChild(nukeButton);
filterList.appendChild(li);
return true;
}

function clearFilters(event) {
Expand Down Expand Up @@ -166,6 +164,17 @@ function validateFields(form) {
}
}

function submitForm(event, form, searchURLInput) {
event.preventDefault();
event.stopPropagation();
var url = searchURLInput.value;
if (!url) {
validateFields(form);
return;
}
performQuery(url);
}

$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip({
container: 'body',
Expand All @@ -184,16 +193,18 @@ $(document).ready(function() {
var searchURLInput = document.getElementById(elemIds.url);
var form = document.getElementById(elemIds.form);
form.addEventListener('submit', function(event) {
event.preventDefault();
event.stopPropagation();
var url = searchURLInput.value;
if (!url) {
validateFields(form);
return;
}
performQuery(url);
submitForm(event, form, searchURLInput);
});
document.getElementById(elemIds.advancedOptions).onclick = function() {
validateFields(form);
}
var filteringExpression = document.getElementById(elemIds.filtering.expression);
filteringExpression.addEventListener("keypress", function(event) {
if (event.key === "Enter") {
event.preventDefault();
if (! addFilter()) {
submitForm(event, form, searchURLInput);
}
}
});
});
Loading

0 comments on commit 2d19b6b

Please sign in to comment.