Allow the commit message to be edited in the change view

Bug: Issue 4135
Change-Id: I8ccb2f8d9875a277aa8500be183defa0eee37982
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 25eacc2..d8c85ea 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -21,6 +21,7 @@
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-change-star/gr-change-star.html">
 <link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
+<link rel="import" href="../../shared/gr-editable-content/gr-editable-content.html">
 <link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
 <link rel="import" href="../../shared/gr-linked-text/gr-linked-text.html">
 <link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
@@ -260,10 +261,19 @@
         </div>
         <div class="changeInfo-column commitAndRelated">
           <div class="commitMessage">
-            <h4>Commit message</h4>
-            <gr-linked-text pre
-                content="[[_commitInfo.message]]"
-                config="[[_projectConfig.commentlinks]]"></gr-linked-text>
+            <h4>
+              Commit message
+              <gr-button link
+                  on-tap="_handleEditCommitMessage"
+                  hidden$="[[_hideEditCommitMessage]]">Edit</gr-button>
+            </h4>
+            <gr-editable-content id="commitMessageEditor"
+                editing="[[_editingCommitMessage]]"
+                content="{{_commitInfo.message}}">
+              <gr-linked-text pre
+                  content="[[_commitInfo.message]]"
+                  config="[[_projectConfig.commentlinks]]"></gr-linked-text>
+            </gr-editable-content>
           </div>
           <div class="relatedChanges">
             <gr-related-changes-list id="relatedChanges"
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index d6f4cec..347352a 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -59,6 +59,15 @@
         type: Object,
         value: function() { return {}; },
       },
+      _editingCommitMessage: {
+        type: Boolean,
+        value: false,
+      },
+      _hideEditCommitMessage: {
+        type: Boolean,
+        computed: '_computeHideEditCommitMessage(_loggedIn, ' +
+            '_editingCommitMessage, _change.*, _patchRange.patchNum)',
+      },
       _patchRange: Object,
       _allPatchSets: {
         type: Array,
@@ -96,6 +105,10 @@
       this.addEventListener('comment-save', this._handleCommentSave.bind(this));
       this.addEventListener('comment-discard',
           this._handleCommentDiscard.bind(this));
+      this.addEventListener('editable-content-save',
+          this._handleCommitMessageSave.bind(this));
+      this.addEventListener('editable-content-cancel',
+          this._handleCommitMessageCancel.bind(this));
       this.listen(window, 'scroll', '_handleBodyScroll');
     },
 
@@ -126,6 +139,59 @@
       el.classList.remove('pinned');
     },
 
+    _handleEditCommitMessage: function(e) {
+      this._editingCommitMessage = true;
+      this.$.commitMessageEditor.focusTextarea();
+    },
+
+    _handleCommitMessageSave: function(e) {
+      var message = e.detail.content;
+
+      this.$.commitMessageEditor.disabled = true;
+      this._saveCommitMessage(message).then(function(resp) {
+        this.$.commitMessageEditor.disabled = false;
+        if (!resp.ok) { return; }
+
+        this.set('_commitInfo.message', message);
+        this._editingCommitMessage = false;
+        this._reloadWindow();
+      }.bind(this)).catch(function(err) {
+        this.$.commitMessageEditor.disabled = false;
+      }.bind(this));
+    },
+
+    _reloadWindow: function() {
+      window.location.reload();
+    },
+
+    _handleCommitMessageCancel: function(e) {
+      this._editingCommitMessage = false;
+    },
+
+    _saveCommitMessage: function(message) {
+      return this.$.restAPI.saveChangeCommitMessageEdit(
+          this._changeNum, message).then(function(resp) {
+            if (!resp.ok) { return resp; }
+
+            return this.$.restAPI.publishChangeEdit(this._changeNum);
+          }.bind(this));
+    },
+
+    _computeHideEditCommitMessage: function(loggedIn, editing, changeRecord,
+        patchNum) {
+      if (!changeRecord || !loggedIn || editing) { return true; }
+
+      patchNum = parseInt(patchNum, 10);
+      if (isNaN(patchNum)) { return true; }
+
+      var change = changeRecord.base;
+      if (change.revisions[change.current_revision]._number !== patchNum) {
+        return true;
+      }
+
+      return false;
+    },
+
     _handleCommentSave: function(e) {
       if (!e.target.comment.__draft) { return; }
 
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index f4a0a52..0f7fab5 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -230,5 +230,25 @@
       var status = element._computeChangeStatus(element._change, '2');
       assert.equal(status, '(draft)');
     });
+
+    test('show commit message edit button', function() {
+      var changeRecord = {
+        base: {
+          revisions: {
+            rev1: {_number: 1},
+            rev2: {_number: 2},
+          },
+          current_revision: 'rev2',
+        },
+      };
+      assert.isTrue(element._computeHideEditCommitMessage(
+          false, false, changeRecord, '2'));
+      assert.isTrue(element._computeHideEditCommitMessage(
+          true, true, changeRecord, '2'));
+      assert.isTrue(element._computeHideEditCommitMessage(
+          true, false, changeRecord, '1'));
+      assert.isFalse(element._computeHideEditCommitMessage(
+          true, false, changeRecord, '2'));
+    });
   });
 </script>