From cb5adb3a31f63d8ce18e9a3eae66c0cf0a0516cb Mon Sep 17 00:00:00 2001
From: Virgil Grigoras <v.grigoras@vg-dev.de>
Date: Sun, 9 Sep 2018 21:54:51 +0200
Subject: [PATCH] Display "what's new" information for newer versions than the
 current one

---
 cps/static/js/main.js    | 37 +++++++++++++------
 cps/templates/admin.html | 17 +++++++--
 cps/web.py               | 77 +++++++++++++++++++++++++++++++++-------
 3 files changed, 106 insertions(+), 25 deletions(-)

diff --git a/cps/static/js/main.js b/cps/static/js/main.js
index 3fdd42ae..38a7fb87 100644
--- a/cps/static/js/main.js
+++ b/cps/static/js/main.js
@@ -110,18 +110,33 @@ $(function() {
             url: window.location.pathname + "/../../get_update_status",
             success: function success(data) {
                 $this.html(buttonText);
-                if (data.status === true) {
-                    $("#check_for_update").addClass("hidden");
-                    $("#perform_update").removeClass("hidden");
-                    $("#update_info")
-                        .removeClass("hidden")
-                        .find("span").html(data.commit);
-                }
-                if (data.error.length != 0) {
-                    $("#update_error")
-                        .removeClass("hidden")
-                        .find("span").html(data.error);
+
+                var cssClass = '';
+                var message = ''
+
+                if (data.success === true) {
+                    if (data.update === true) {
+                        $("#check_for_update").addClass("hidden");
+                        $("#perform_update").removeClass("hidden");
+                        $("#update_info")
+                            .removeClass("hidden")
+                            .find("span").html(data.commit);
+
+                        data.history.reverse().forEach((entry, index) => {
+                            $("<tr><td>" + entry[0] + "</td><td>" + entry[1] + "</td></tr>").appendTo($("#update_table"));
+                        });
+                        cssClass = 'alert-warning'
+                    } else {
+                        cssClass = 'alert-success'
+                    }
+                } else {
+                    cssClass = 'alert-danger'
                 }
+
+                message = '<div class="alert ' + cssClass
+                    + ' fade in"><a href="#" class="close" data-dismiss="alert">&times;</a>' + data.message + '</div>';
+
+                $(message).insertAfter($("#update_table"));
             }
         });
     });
diff --git a/cps/templates/admin.html b/cps/templates/admin.html
index 20e5ec29..f46e6f0a 100644
--- a/cps/templates/admin.html
+++ b/cps/templates/admin.html
@@ -113,8 +113,21 @@
   <div class="row">
     <div class="col">
       <h2>{{_('Update')}}</h2>
-      <div>{{_('Current commit timestamp')}}: <span>{{commit}} </span></div>
-      <div class="hidden" id="update_info">{{_('Newest commit timestamp')}}: <span></span></div>
+      <table class="table table-striped" id="update_table">
+        <thead>
+          <tr>
+              <th class="col-xs-3">{{_('Version')}}</th>
+              <th class="col-xl-8">{{_('Details')}}</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr id="current_version">
+            <td>{{commit}} <i>({{_('current version')}})</i></td>
+            <td>/</td>
+          </tr>
+        </tbody>
+      </table>
+
       <div class="hidden" id="update_error"> <span>{{update_error}}</span></div>
       <div class="btn btn-default" id="check_for_update">{{_('Check for update')}}</div>
       <div class="btn btn-default hidden" id="perform_update" data-toggle="modal" data-target="#UpdateprogressDialog">{{_('Perform Update')}}</div>
diff --git a/cps/web.py b/cps/web.py
index 07791daf..c25759cf 100644
--- a/cps/web.py
+++ b/cps/web.py
@@ -1085,7 +1085,9 @@ def get_matching_tags():
 @login_required_if_no_ano
 def get_update_status():
     status = {
-        'status': False,
+        'update': False,
+        'success': False,
+        'message': '',
         'current_commit_hash': ''
     }
     repository_url = 'https://api.github.com/repos/janeczku/calibre-web'
@@ -1106,15 +1108,16 @@ def get_update_status():
             r.raise_for_status()
             commit = r.json()
         except requests.exceptions.HTTPError as ex:
-            status['error'] = _(u'HTTP Error') + ' ' + str(ex)
+            status['message'] = _(u'HTTP Error') + ' ' + str(ex)
         except requests.exceptions.ConnectionError:
-            status['error'] = _(u'Connection error')
+            status['message'] = _(u'Connection error')
         except requests.exceptions.Timeout:
-            status['error'] = _(u'Timeout while establishing connection')
+            status['message'] = _(u'Timeout while establishing connection')
         except requests.exceptions.RequestException:
-            status['error'] = _(u'General error')
+            status['message'] = _(u'General error')
 
-        if 'error' in status:
+        if status['message'] != '':
+            status['success'] = False
             return json.dumps(status)
 
         if 'object' in commit and commit['object']['sha'] != status['current_commit_hash']:
@@ -1132,17 +1135,67 @@ def get_update_status():
             except requests.exceptions.RequestException:
                 status['error'] = _(u'General error')
 
-            if 'error' in status:
+            if status['message'] != '':
+                status['success'] = False
                 return json.dumps(status)
 
-            if 'committer' in update_data:
-                status['status'] = True
+            if 'committer' in update_data and 'message' in update_data:
+                parents = []
+
+                status['update'] = True
+                status['success'] = True
+                status['message'] = _(u'A new update is available. Click on the button below update to the latest version.')
+
                 new_commit_date = datetime.datetime.strptime(
                     update_data['committer']['date'], '%Y-%m-%dT%H:%M:%SZ') - tz
-                status['commit'] = format_datetime(new_commit_date, format='short', locale=get_locale())
-            else:
-                status['error'] = _(u'Could not fetch update information')
+                parents.append(
+                    [
+                        format_datetime(new_commit_date, format='short', locale=get_locale()),
+                        update_data['message'],
+                        update_data['sha']
+                    ]
+                )
 
+                # it only makes sense to analyze the parents if we know the current commit hash
+                if status['current_commit_hash'] != '':
+                    try:
+                        parent_commit = update_data['parents'][0]
+                        # limit the maximum search depth
+                        remaining_parents_cnt = 10
+                    except IndexError:
+                        remaining_parents_cnt = None
+
+                    if remaining_parents_cnt is not None:
+                        while True:
+                            if remaining_parents_cnt == 0:
+                                break
+
+                            # check if we are more than one update behind if so, go up the tree
+                            if parent_commit['sha'] != status['current_commit_hash']:
+                                try:
+                                    r = requests.get(parent_commit['url'])
+                                    r.raise_for_status()
+                                    parent_data = r.json()
+
+                                    parent_commit_date = datetime.datetime.strptime(
+                                        parent_data['committer']['date'], '%Y-%m-%dT%H:%M:%SZ') - tz
+                                    parent_commit_date = format_datetime(
+                                        parent_commit_date, format='short', locale=get_locale())
+
+                                    parents.append([parent_commit_date, parent_data['message'], parent_data['sha']])
+                                    parent_commit = parent_data['parents'][0]
+                                    remaining_parents_cnt -= 1
+                                except Exception:
+                                    # it isn't crucial if we can't get information about the parent
+                                    break
+                            else:
+                                # parent is our current version
+                                break
+            else:
+                status['success'] = False
+                status['message'] = _(u'Could not fetch update information')
+
+    status['history'] = parents
     return json.dumps(status)