Jelajahi Sumber

HUE-2786 [hbase] Show current version in the cell history and allow revert to previous version

In the full editor for a cell it now shows the current version in the cell history as well as an indicator of when the current version is edited. I've also made it possible to revert to a previous version by selecting it and pressing revert.
Johan Ahlen 10 tahun lalu
induk
melakukan
d5d5cac32d

+ 57 - 4
apps/hbase/src/hbase/static/hbase/js/utils.js

@@ -105,6 +105,21 @@ function launchModal(modal, data) {
           lineNumbers: true,
           readOnly: $(target).is(":disabled")
         });
+        data.codeMirror = cm;
+
+        var updateTimeout = 0;
+        cm.on("change", function(cm) {
+          clearTimeout(updateTimeout);
+          updateTimeout = setTimeout(function () {
+            data.value(cm.getValue());
+          }, 100);
+        });
+
+        data.showingCurrent.subscribe(function(showingCurrent) {
+          cm.options.readOnly = ! showingCurrent;
+          cm.refresh();
+        });
+
         setTimeout(function () {
           cm.refresh()
         }, 401); //CM invis bug workaround
@@ -124,6 +139,7 @@ function launchModal(modal, data) {
           multiple: false,
           onComplete: function (id, fileName, response) {
             data.content.reload();
+            data.value(data.content.value);
           }
         });
       }
@@ -158,12 +174,49 @@ function launchModal(modal, data) {
   logGA(modal.slice(0, modal.indexOf('_modal') != -1 ? modal.indexOf('_modal') : modal.length));
 }
 
+function showFullEditor(cellContent) {
+  var currentValue = ko.isObservable(cellContent.value) ? cellContent.value() : cellContent.value;
+  launchModal('cell_edit_modal', {
+    originalValue: currentValue,
+    currentValue: ko.observable(currentValue),
+    value: ko.observable(currentValue),
+    showingCurrent: ko.observable(true),
+    readOnly: ko.observable(false),
+    timestamp: ko.observable(cellContent.timestamp),
+    content: cellContent,
+    mime: detectMimeType(currentValue),
+
+    updateCodeMirror: function() {
+      if (this.codeMirror) {
+        this.codeMirror.setValue(this.value());
+        this.codeMirror.refresh()
+      }
+    },
+
+    switchToHistorical: function(item) {
+      if (this.showingCurrent()) {
+        this.currentValue(this.value());
+      }
+      this.value(item.value);
+      this.updateCodeMirror();
+      this.showingCurrent(false);
+      this.timestamp(item.timestamp)
+    },
+
+    switchToCurrent: function() {
+      if (!this.showingCurrent()) {
+        this.value(this.currentValue());
+        this.showingCurrent(true);
+        this.timestamp(this.content.timestamp);
+        this.updateCodeMirror();
+      }
+    }
+  });
+}
+
 function editCell($data) {
   if ($data.value().length > 146) {
-    launchModal('cell_edit_modal', {
-      content: $data,
-      mime: detectMimeType($data.value())
-    });
+    showFullEditor($data);
   } else {
     $data.editing(true);
   }

+ 17 - 12
apps/hbase/src/hbase/templates/app.mako

@@ -95,7 +95,7 @@ ${ commonheader(None, "hbase", user) | n,unicode }
             % if can_write:
               <a class="corner-btn btn" data-bind="click: $data.drop, clickBubble: false"><i class="fa fa-trash-o"></i></a>
             % endif
-            <a class="corner-btn btn" data-bind="event: { mousedown: function(){launchModal('cell_edit_modal', {content:$data, mime: detectMimeType($data.value())})} }"><i class="fa fa-pencil"></i> ${_('Full Editor')}</a>
+            <a class="corner-btn btn" data-bind="event: { mousedown: function(){ showFullEditor($data) } }"><i class="fa fa-pencil"></i> ${_('Full Editor')}</a>
             <pre data-bind="text: ($data.value().length > 146 ? $data.value().substring(0, 144) + '...' : $data.value()).replace(/(\r\n|\n|\r)/gm,''), click: editCell.bind(null, $data), clickBubble: false, visible: ! $data.isLoading() && ! $data.editing()"></pre>
             <textarea data-bind="visible: !$data.isLoading() && $data.editing(), disable: ! app.views.tabledata.canWrite(), hasfocus: $data.editing, value: $data.value, click:function(){}, clickBubble: false"></textarea>
             <img src="${ static('desktop/art/spinner.gif') }" data-bind="visible: $data.isLoading() " />
@@ -365,7 +365,7 @@ ${ commonheader(None, "hbase", user) | n,unicode }
     <script id="cell_edit_modal_template" type="text/html">
       <div class="modal-header">
         <a class="close pointer" data-dismiss="modal" aria-hidden="true">&times;</a>
-        <h3>${_('Edit Cell')} - <span data-bind="text: content.name || formatTimestamp(content.timestamp)"></span> <code data-bind="text: mime, visible: mime !== 'type/null'"></code> <small><i class="fa fa-clock-o"></i> <span data-bind="text: formatTimestamp($data.content.timestamp)"></span></small></h3>
+        <h3>${_('Edit Cell')} - <span data-bind="text: content.name || formatTimestamp(timestamp())"></span> <code data-bind="text: mime, visible: mime !== 'type/null'"></code> <small><i class="fa fa-clock-o"></i> <span data-bind="text: formatTimestamp(timestamp())"></span></small></h3>
       </div>
       <div class="modal-body container-fluid">
           <div class="row-fluid">
@@ -382,8 +382,9 @@ ${ commonheader(None, "hbase", user) | n,unicode }
             <div class="span3">
               <ul class="nav nav-list cell-history">
                 <li class="nav-header">${_('Cell History:')}</li>
+                <li data-bind="css: { 'active': showingCurrent }"><a data-bind="click: switchToCurrent()" class="pointer">${_('Current Version')}<span data-bind="if: (originalValue !== value() && showingCurrent()) || (originalValue !== currentValue() && !showingCurrent())"> (${_('Edited')})</span></a></li>
                 <!-- ko foreach: $data.content.history.items() -->
-                  <li data-bind="css: { 'active': $data.timestamp == $parent.content.timestamp }"><a data-bind="click: $parent.content.history.pickHistory.bind(null, $data), text: formatTimestamp($data.timestamp)" class="pointer"></a></li>
+                  <li data-bind="css: { 'active': $data.timestamp == $parent.timestamp() }"><a data-bind="click: function() { $root.switchToHistorical($data) }, text: formatTimestamp($data.timestamp)" class="pointer"></a></li>
                 <!-- /ko -->
                 <li data-bind="visible: $data.content.history.loading()"><img src="${ static('desktop/art/spinner.gif') }" /></li>
               </ul>
@@ -391,31 +392,35 @@ ${ commonheader(None, "hbase", user) | n,unicode }
           </div>
         </div>
       </div>
-      <div class="modal-footer" data-bind="if: ! $data.readonly">
-        % if can_write:
+      <div class="modal-footer">
+        <!-- ko if: showingCurrent -->
           <button class="btn" data-dismiss="modal" aria-hidden="true">${_('Cancel')}</button>
           <a id="file-upload-btn" class="btn fileChooserBtn" aria-hidden="true"><i class="fa fa-upload"></i> ${_('Upload')}</a>
           <input data-bind="visible: mime.split('/')[0].toLowerCase() != 'application' && mime.split('/')[0].toLowerCase() != 'image'" type="submit" class="btn btn-primary" value="${_('Save')}">
-        % else:
-          <button class="btn" data-dismiss="modal" aria-hidden="true">${_('OK')}</button>
-        % endif
+        <!-- /ko -->
+        <!-- ko ifnot: showingCurrent -->
+          <button class="btn" data-dismiss="modal" aria-hidden="true">${_('Cancel')}</button>
+          <input type="submit" class="btn btn-primary" value="${_('Revert')}">
+        <!-- /ko -->
       </div>
     </script>
 
     <script id="cell_image_template" type="text/html">
-      <img data-bind="attr: {src: 'data:image/' + $data.mime + ';base64,' + $data.content.value()}"/>
+      <img data-bind="attr: {src: 'data:image/' + $data.mime + ';base64,' + value()}"/>
+      <input type="hidden" data-bind="value: value"/>
     </script>
 
     <script id="cell_text_template" type="text/html">
-      <textarea id="codemirror_target" data-bind="text: $data.content.value, disable: ! app.views.tabledata.canWrite()" data-use-post="true"></textarea>
+      <textarea id="codemirror_target" data-bind="text: value, disable: !showingCurrent()" data-use-post="true"></textarea>
     </script>
 
     <script id="cell_application_template" type="text/html">
-      <iframe width="100%" height="100%" data-bind="attr: {src: 'data:' + $data.mime + ';base64,' + $data.content.value()}"></iframe>
+      <iframe width="100%" height="100%" data-bind="attr: {src: 'data:' + $data.mime + ';base64,' + value()}"></iframe>
+      <input type="hidden" data-bind="value: value"/>
     </script>
 
     <script id="cell_type_template" type="text/html">
-      <textarea style="width:100%; height: 450px;" data-bind="text: $data.content.value, disable: ! app.views.tabledata.canWrite()" data-use-post="true"></textarea>
+      <textarea style="width:100%; height: 450px;" data-bind="textInput: value, disable: !showingCurrent()" data-use-post="true"></textarea>
     </script>
   </div>