From b4d27599b5e2ef027c81f054e5815226ac9228d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 12 Jan 2021 15:39:09 +0100
Subject: [PATCH] snapshots: add navigation links (#50010)

---
 tests/test_snapshots.py | 19 ++++++++++++++++
 wcs/admin/utils.py      | 11 ++++++++++
 wcs/snapshots.py        | 48 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 78 insertions(+)

diff --git a/tests/test_snapshots.py b/tests/test_snapshots.py
index 0f137b16..3533b908 100644
--- a/tests/test_snapshots.py
+++ b/tests/test_snapshots.py
@@ -339,6 +339,25 @@ def test_form_snapshot_browse(pub, formdef_with_history):
     resp = resp.click(href='%s/view/' % snapshot.id)
     assert 'This form is readonly' in resp.text
     assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
+
+    assert 'Lt' not in resp.text
+    assert 'LT' not in resp.text
+    assert '<a href="../../%s/view/">&GT;</a>' % (snapshot.id - 1) in resp.text
+    assert '<a href="../../%s/view/">&Gt;</a>' % (snapshot.id - 6) in resp.text
+
+    resp = resp.click(href='../../%s/view/' % (snapshot.id - 1))
+    assert '<a href="../../%s/view/">&Lt;</a>' % snapshot.id in resp.text
+    assert '<a href="../../%s/view/">&LT;</a>' % snapshot.id in resp.text
+    assert '<a href="../../%s/view/">&GT;</a>' % (snapshot.id - 2) in resp.text
+    assert '<a href="../../%s/view/">&Gt;</a>' % (snapshot.id - 6) in resp.text
+
+    resp = resp.click(href='../../%s/view/' % (snapshot.id - 6))
+    assert '<a href="../../%s/view/">&Lt;</a>' % snapshot.id in resp.text
+    assert '<a href="../../%s/view/">&LT;</a>' % (snapshot.id - 5) in resp.text
+    assert 'GT' not in resp.text
+    assert 'Gt' not in resp.text
+
+    resp = resp.click(href='../../%s/view/' % snapshot.id)
     resp = resp.click('Description')
     assert resp.form['description'].value == 'this is a description (5)'
     assert [x[0].name for x in resp.form.fields.values() if x[0].tag == 'button'] == ['cancel']
diff --git a/wcs/admin/utils.py b/wcs/admin/utils.py
index 7a5b3136..102fd250 100644
--- a/wcs/admin/utils.py
+++ b/wcs/admin/utils.py
@@ -62,4 +62,15 @@ def snapshot_info_block(snapshot):
         parts.append(misc.localstrftime(snapshot.timestamp))
     r += htmltext('<br />').join(parts)
     r += htmltext('</p>')
+    if snapshot.previous or snapshot.next:
+        r += htmltext('<p>')
+        if snapshot.id != snapshot.first:
+            r += htmltext(' <a href="../../%s/view/">&Lt;</a>') % (snapshot.first)
+        if snapshot.previous:
+            r += htmltext(' <a href="../../%s/view/">&LT;</a>') % (snapshot.previous)
+        if snapshot.next:
+            r += htmltext(' <a href="../../%s/view/">&GT;</a>') % (snapshot.next)
+        if snapshot.id != snapshot.last:
+            r += htmltext(' <a href="../../%s/view/">&Gt;</a>') % (snapshot.last)
+        r += htmltext('</p>')
     return r.getvalue()
diff --git a/wcs/snapshots.py b/wcs/snapshots.py
index dd4bbffa..70cae5a7 100644
--- a/wcs/snapshots.py
+++ b/wcs/snapshots.py
@@ -102,6 +102,54 @@ class Snapshot:
                 self._user = UnknownUser()
         return self._user
 
+    def load_history(self):
+        if not self.instance:
+            self._history = []
+            return
+        history = get_publisher().snapshot_class.select_object_history(self.instance)
+        self._history = [s.id for s in history]
+
+    @property
+    def previous(self):
+        if not hasattr(self, '_history'):
+            self.load_history()
+
+        try:
+            idx = self._history.index(self.id)
+        except ValueError:
+            return None
+        if idx == 0:
+            return None
+        return self._history[idx - 1]
+
+    @property
+    def next(self):
+        if not hasattr(self, '_history'):
+            self.load_history()
+
+        try:
+            idx = self._history.index(self.id)
+        except ValueError:
+            return None
+        try:
+            return self._history[idx + 1]
+        except IndexError:
+            return None
+
+    @property
+    def first(self):
+        if not hasattr(self, '_history'):
+            self.load_history()
+
+        return self._history[0]
+
+    @property
+    def last(self):
+        if not hasattr(self, '_history'):
+            self.load_history()
+
+        return self._history[-1]
+
     def restore(self, as_new=True):
         instance = self.instance
         if as_new:
-- 
2.29.2

