You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mi...@apache.org on 2017/01/04 08:32:27 UTC

zeppelin git commit: [ZEPPELIN-1745] View revisions in non-editable mode

Repository: zeppelin
Updated Branches:
  refs/heads/master 70d4ad3b8 -> d9a11a936


[ZEPPELIN-1745] View revisions in non-editable mode

### What is this PR for?
This is to make view of revisions non-editable

### What type of PR is it?
Improvement

### Todos
* [x] - disable action bar buttons
* [x] - disable per paragraph editing actions
* [x] - disable hotkeys

### What is the Jira issue?
[ZEPPELIN-1745](https://issues.apache.org/jira/browse/ZEPPELIN-1745)

### How should this be tested?
1. initialize `GitNotebookRepo` by adding
```
export ZEPPELIN_NOTEBOOK_STORAGE="org.apache.zeppelin.notebook.repo.GitNotebookRepo"
```
in your `conf/zeppelin-env.sh`
2. go to any note and create few revisions using commit menu
3. then switch between revisions, during which you shouldn't be able to modify note

### Screenshots (if appropriate)
![revision_view](https://cloud.githubusercontent.com/assets/1642088/21340828/53131054-c6cc-11e6-960b-abf5db905194.gif)

### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? no
* Does this needs documentation? no

Author: Khalid Huseynov <kh...@gmail.com>

Closes #1775 from khalidhuseynov/feat/revision-view and squashes the following commits:

59a92ea [Khalid Huseynov] Merge branch 'master' into feat/revision-view
bbaf286 [Khalid Huseynov] initialize looknfeel
efe09c0 [Khalid Huseynov] enable report view without save
b3a78c7 [Khalid Huseynov] dont show bingings, perms, hotkeys
843abf4 [Khalid Huseynov] remove escaped single quote inside console
84d2c5a [Khalid Huseynov] disable personalized mode button
fa05bdb [Khalid Huseynov] Merge branch 'master' into feat/revision-view
1a5a6a7 [Khalid Huseynov] render result change without commit
06e2977 [Khalid Huseynov] check newValue for undefined
2823142 [Khalid Huseynov] add group span for ng-if
4bfe560 [Khalid Huseynov] gray out editor on revision view
e9d3ceb [Khalid Huseynov] disable edit on db click md interpreter
aff02b9 [Khalid Huseynov] don't show paragraph level hide output buttton
1eea8ce [Khalid Huseynov] don't show paragraph level hide editor button
46d67af [Khalid Huseynov] disable show/hide output in note action bar
0946377 [Khalid Huseynov] disable show/hide code in note action-bar
81346e9 [Khalid Huseynov] disable editing note name
7751b69 [Khalid Huseynov] disable hotkeys
9b85e05 [Khalid Huseynov] disable editor editing
00fb556 [Khalid Huseynov] make paragraph title non-editable
c55de95 [Khalid Huseynov] don't show run/cancel on paragraph
19cacbd [Khalid Huseynov] don't show paragraph settings menu
7d115b8 [Khalid Huseynov] disable + new paragraph
711c0b3 [Khalid Huseynov] disable scheduler
ab8ab4d [Khalid Huseynov] disable remove note
5d9f877 [Khalid Huseynov] disable revision commit
50d40ad [Khalid Huseynov] disable export note
46238e8 [Khalid Huseynov] disable clone note
8e97800 [Khalid Huseynov] disable clear output
fa1397d [Khalid Huseynov] disable run all
4c83895 [Khalid Huseynov] change var name revisionDisabled -> revisionView


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/d9a11a93
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/d9a11a93
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/d9a11a93

Branch: refs/heads/master
Commit: d9a11a936052a0825613bbc58330305a1a504b4f
Parents: 70d4ad3
Author: Khalid Huseynov <kh...@gmail.com>
Authored: Mon Jan 2 19:49:58 2017 -0800
Committer: Mina Lee <mi...@apache.org>
Committed: Wed Jan 4 17:32:20 2017 +0900

----------------------------------------------------------------------
 .../src/app/notebook/notebook-actionBar.html    |  80 ++++----
 .../src/app/notebook/notebook.controller.js     |  11 +-
 zeppelin-web/src/app/notebook/notebook.html     |   4 +-
 .../notebook/paragraph/paragraph-control.html   | 186 ++++++++++---------
 .../notebook/paragraph/paragraph.controller.js  |  18 +-
 .../src/app/notebook/paragraph/paragraph.html   |   3 +-
 .../paragraph/result/result.controller.js       |  11 +-
 .../components/editor/ace.editor.directive.html |   2 +-
 .../components/editor/codeEditor.directive.js   |   4 +-
 9 files changed, 184 insertions(+), 135 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d9a11a93/zeppelin-web/src/app/notebook/notebook-actionBar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html
index 821dae1..b010f7a 100644
--- a/zeppelin-web/src/app/notebook/notebook-actionBar.html
+++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html
@@ -18,7 +18,7 @@ limitations under the License.
       <input type="text" pu-elastic-input class="form-control2" placeholder="New name" style="min-width: 0px; max-width: 95%;"
            ng-if="input.showEditor" ng-model="input.value" ng-escape="input.showEditor = false" focus-if="input.showEditor"
            ng-blur="updateNoteName(input.value);input.showEditor = false;" ng-enter="updateNoteName(input.value);input.showEditor = false;" />
-      <p class="form-control-static2" ng-click="input.showEditor = true; input.value = note.name" ng-show="!input.showEditor">{{noteName(note)}}</p>
+      <p class="form-control-static2" ng-click="input.showEditor = !revisionView; input.value = note.name" ng-show="!input.showEditor">{{noteName(note)}}</p>
     </div>
     <div style="float: left; padding-bottom: 10px">
       <span class="labelBtn btn-group">
@@ -26,20 +26,23 @@ limitations under the License.
               class="btn btn-default btn-xs"
               ng-click="runNote()"
               ng-class="{'disabled':isNoteRunning()}"
-              tooltip-placement="bottom" tooltip="Run all paragraphs">
+              tooltip-placement="bottom" tooltip="Run all paragraphs"
+              ng-disabled="revisionView">
         <i class="icon-control-play"></i>
       </button>
       <button type="button"
               class="btn btn-default btn-xs"
               ng-click="toggleAllEditor()"
               ng-hide="viewOnly"
-              tooltip-placement="bottom" tooltip="Show/hide the code">
+              tooltip-placement="bottom" tooltip="Show/hide the code"
+              ng-disabled="revisionView">
         <i ng-class="editorToggled ?  'fa icon-size-fullscreen' :'fa icon-size-actual'"></i></button>
       <button type="button"
               class="btn btn-default btn-xs"
               ng-click="toggleAllTable()"
               ng-hide="viewOnly"
-              tooltip-placement="bottom" tooltip="Show/hide the output">
+              tooltip-placement="bottom" tooltip="Show/hide the output"
+              ng-disabled="revisionView">
         <i ng-class="tableToggled ? 'fa icon-notebook' : 'fa icon-book-open'"></i>
       </button>
       <button type="button"
@@ -47,7 +50,8 @@ limitations under the License.
               ng-click="clearAllParagraphOutput(note.id)"
               ng-hide="viewOnly"
               ng-class="{'disabled':isNoteRunning()}"
-              tooltip-placement="bottom" tooltip="Clear output">
+              tooltip-placement="bottom" tooltip="Clear output"
+              ng-disabled="revisionView">
         <i class="fa fa-eraser"></i>
       </button>
 
@@ -56,14 +60,15 @@ limitations under the License.
               ng-hide="viewOnly"
               tooltip-placement="bottom" tooltip="Clone this note" data-source-note-name="{{note.name}}"
               data-toggle="modal" data-target="#noteNameModal" data-clone="true"
-              >
+              ng-disabled="revisionView">
         <i class="fa fa-copy"></i>
       </button>
       <button type="button"
               class="btn btn-default btn-xs"
               ng-hide="viewOnly"
               ng-click="exportNote()"
-              tooltip-placement="bottom" tooltip="Export this note">
+              tooltip-placement="bottom" tooltip="Export this note"
+              ng-disabled="revisionView">
         <i class="fa fa-download"></i>
       </button>
 
@@ -72,7 +77,8 @@ limitations under the License.
               ng-if="ticket.principal && ticket.principal !== 'anonymous'"
               ng-hide="viewOnly || note.config.personalizedMode !== 'true'"
               ng-click="toggleNotePersonalizedMode()"
-              tooltip-placement="bottom" tooltip="Personal mode {{isOwner ? '' : '(owner can change)'}}">
+              tooltip-placement="bottom" tooltip="Personal mode {{isOwner ? '' : '(owner can change)'}}"
+              ng-disabled="revisionView">
         <i class="fa fa-user"></i>
       </button>
       <button type="button"
@@ -80,7 +86,8 @@ limitations under the License.
               ng-if="ticket.principal && ticket.principal !== 'anonymous'"
               ng-hide="viewOnly || note.config.personalizedMode === 'true'"
               ng-click="toggleNotePersonalizedMode()"
-              tooltip-placement="bottom" tooltip="Collaboration mode {{isOwner ? '' : '(owner can change)'}}">
+              tooltip-placement="bottom" tooltip="Collaboration mode {{isOwner ? '' : '(owner can change)'}}"
+              ng-disabled="revisionView">
         <i class="fa fa-users"></i>
       </button>
     </span>
@@ -92,7 +99,8 @@ limitations under the License.
                 id="versionControlDropdown"
                 ng-hide="viewOnly"
                 data-toggle="dropdown"
-                tooltip-placement="bottom" tooltip="Version control">
+                tooltip-placement="bottom" tooltip="Version control"
+                ng-disabled="revisionView">
           <i class="fa fa-file-code-o"></i>
         </button>
         <button type="button"
@@ -100,7 +108,7 @@ limitations under the License.
                 id="setRevision"
                 ng-hide="viewOnly"
                 ng-click="setNoteRevision()"
-                ng-disabled="revisionDisabled"
+                ng-disabled="!revisionView"
                 tooltip-placement="bottom" tooltip="Set revision">
           <i class="fa fa-arrow-circle-o-right"></i>
         </button>
@@ -159,7 +167,8 @@ limitations under the License.
                 class="btn btn-default btn-xs"
                 ng-click="removeNote(note.id)"
                 ng-hide="viewOnly"
-                tooltip-placement="bottom" tooltip="Remove this note permanently">
+                tooltip-placement="bottom" tooltip="Remove this note permanently"
+                ng-disabled="revisionView">
           <i class="icon-trash"></i>
         </button>
         <!-- if the note is not in the trash, move to trash -->
@@ -168,7 +177,8 @@ limitations under the License.
                 class="btn btn-default btn-xs"
                 ng-click="moveNoteToTrash(note.id)"
                 ng-hide="viewOnly"
-                tooltip-placement="bottom" tooltip="Move this note to trash">
+                tooltip-placement="bottom" tooltip="Move this note to trash"
+                ng-disabled="revisionView">
           <i class="icon-trash"></i>
         </button>
       </span>
@@ -179,7 +189,8 @@ limitations under the License.
              type="button"
              data-toggle="dropdown"
              ng-class="{ 'btn-info' : note.config.cron, 'btn-danger' : note.info.cron, 'btn-default' : !note.config.cron}"
-             tooltip-placement="bottom" tooltip="Run scheduler">
+             tooltip-placement="bottom" tooltip="Run scheduler"
+             ng-disabled="revisionView">
           <span class="fa fa-clock-o"></span> {{getCronOptionNameFromValue(note.config.cron)}}
         </div>
         <ul class="dropdown-menu" role="menu" style="width:300px">
@@ -224,27 +235,30 @@ limitations under the License.
     </span>
     </div>
 
+
     <div class="pull-right" style="margin-top:15px; margin-right:15px; margin-left: 15px; margin-bottom: 13px; font-size:15px;">
-      <span class="setting-btn"
-            type="button"
-            data-toggle="modal"
-            data-target="#shortcutModal"
-            tooltip-placement="bottom" tooltip="List of shortcut">
-        <i class="fa fa-keyboard-o"></i>
-      </span>
-      <span class="setting-btn"
-            type="button"
-            ng-click="toggleSetting()"
-            tooltip-placement="bottom" tooltip="Interpreter binding">
-        <i class="fa fa-cog" ng-style="{color: showSetting ? '#3071A9' : 'black' }"></i>
+      <span ng-if="!revisionView">
+        <span class="setting-btn"
+              type="button"
+              data-toggle="modal"
+              data-target="#shortcutModal"
+              tooltip-placement="bottom" tooltip="List of shortcut">
+          <i class="fa fa-keyboard-o"></i>
+        </span>
+        <span class="setting-btn"
+              type="button"
+              ng-click="toggleSetting()"
+              tooltip-placement="bottom" tooltip="Interpreter binding">
+          <i class="fa fa-cog" ng-style="{color: showSetting ? '#3071A9' : 'black' }"></i>
+        </span>
+        <span class="setting-btn"
+              type="button"
+              ng-click="togglePermissions()"
+              tooltip-placement="bottom" tooltip="Note permissions">
+          <i class="fa fa-lock" ng-style="{color: showPermissions ? '#3071A9' : 'black' }"></i>
+        </span>
       </span>
-      <span class="setting-btn"
-            type="button"
-            ng-click="togglePermissions()"
-            tooltip-placement="bottom" tooltip="Note permissions">
-        <i class="fa fa-lock" ng-style="{color: showPermissions ? '#3071A9' : 'black' }"></i>
-      </span>
-
+      
       <span class="btn-group">
         <button type="button" class="btn btn-default btn-xs dropdown-toggle"
                 data-toggle="dropdown">

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d9a11a93/zeppelin-web/src/app/notebook/notebook.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js
index ade9d1d..a1c652b 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -69,7 +69,7 @@
 
     $scope.noteRevisions = [];
     $scope.currentRevision = 'Head';
-    $scope.revisionDisabled = !isRevisionPath($location.path());
+    $scope.revisionView = isRevisionPath($location.path());
 
     $scope.$on('setConnectedStatus', function(event, param) {
       if (connectedOnce && param) {
@@ -165,7 +165,7 @@
 
     $scope.keyboardShortcut = function(keyEvent) {
       // handle keyevent
-      if (!$scope.viewOnly) {
+      if (!$scope.viewOnly && !$scope.revisionView) {
         $scope.$broadcast('keyEvent', keyEvent);
       }
     };
@@ -260,6 +260,7 @@
       console.log('received note revision %o', data);
       if (data.note) {
         $scope.note = data.note;
+        initializeLookAndFeel();
       } else {
         $location.path('/');
       }
@@ -385,7 +386,11 @@
 
     $scope.setLookAndFeel = function(looknfeel) {
       $scope.note.config.looknfeel = looknfeel;
-      $scope.setConfig();
+      if ($scope.revisionView === true) {
+        $rootScope.$broadcast('setLookAndFeel', $scope.note.config.looknfeel);
+      } else {
+        $scope.setConfig();
+      }
     };
 
     /** Set cron expression for this note **/

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d9a11a93/zeppelin-web/src/app/notebook/notebook.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.html b/zeppelin-web/src/app/notebook/notebook.html
index 002e950..35b3dff 100644
--- a/zeppelin-web/src/app/notebook/notebook.html
+++ b/zeppelin-web/src/app/notebook/notebook.html
@@ -113,7 +113,7 @@ limitations under the License.
        ng-init="init(currentParagraph, note)"
        ng-class="columnWidthClass(currentParagraph.config.colWidth)"
        class="paragraph-col">
-    <div class="new-paragraph" ng-click="insertNew('above')" ng-hide="viewOnly || asIframe">
+    <div class="new-paragraph" ng-click="insertNew('above')" ng-hide="viewOnly || asIframe || revisionView">
       <h4 class="plus-sign">&#43;</h4>
     </div>
     <div id="{{currentParagraph.id}}_paragraphColumn"
@@ -123,7 +123,7 @@ limitations under the License.
          ng-hide="currentParagraph.config.tableHide && viewOnly"
          ng-dblclick="paragraphOnDoubleClick(currentParagraph.id);">
     </div>
-    <div class="new-paragraph" ng-click="insertNew('below');" ng-hide="!$last || viewOnly || asIframe ">
+    <div class="new-paragraph" ng-click="insertNew('below');" ng-hide="!$last || viewOnly || asIframe || revisionView">
       <h4 class="plus-sign">&#43;</h4>
     </div>
   </div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d9a11a93/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
index 0bfaf74..644761e 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
@@ -23,98 +23,100 @@ limitations under the License.
   </span>
 
   <!-- Run / Cancel button -->
-  <span class="icon-control-play" style="cursor:pointer;color:#3071A9" tooltip-placement="top" tooltip="Run this paragraph (Shift+Enter)"
-        ng-click="runParagraph(getEditorValue())"
-        ng-show="paragraph.status!='RUNNING' && paragraph.status!='PENDING' && paragraph.config.enabled"></span>
-  <span class="icon-control-pause" style="cursor:pointer;color:#CD5C5C" tooltip-placement="top"
-        tooltip="Cancel (Ctrl+{{ (isMac ? 'Option' : 'Alt') }}+c)"
-        ng-click="cancelParagraph(paragraph)"
-        ng-show="paragraph.status=='RUNNING' || paragraph.status=='PENDING'"></span>
-  <span class="{{paragraph.config.editorHide ? 'icon-size-fullscreen' : 'icon-size-actual'}}" style="cursor:pointer" tooltip-placement="top"
-        tooltip="{{(paragraph.config.editorHide ? 'Show' : 'Hide')}} editor (Ctrl+{{ (isMac ? 'Option' : 'Alt') }}+e)"
-        ng-click="toggleEditor(paragraph)"></span>
-  <span class="{{paragraph.config.tableHide ? 'icon-notebook' : 'icon-book-open'}}" style="cursor:pointer" tooltip-placement="top"
-        tooltip="{{(paragraph.config.tableHide ? 'Show' : 'Hide')}} output (Ctrl+{{ (isMac ? 'Option' : 'Alt') }}+o)"
-        ng-click="toggleOutput(paragraph)"></span>
-  <span class="dropdown navbar-right">
-    <span class="icon-settings" style="cursor:pointer"
-          data-toggle="dropdown"
-          type="button">
-    </span>
+  <span ng-if="!revisionView">
+    <span class="icon-control-play" style="cursor:pointer;color:#3071A9" tooltip-placement="top" tooltip="Run this paragraph (Shift+Enter)"
+          ng-click="runParagraph(getEditorValue())"
+          ng-show="paragraph.status!='RUNNING' && paragraph.status!='PENDING' && paragraph.config.enabled"></span>
+    <span class="icon-control-pause" style="cursor:pointer;color:#CD5C5C" tooltip-placement="top"
+          tooltip="Cancel (Ctrl+{{ (isMac ? 'Option' : 'Alt') }}+c)"
+          ng-click="cancelParagraph(paragraph)"
+          ng-show="paragraph.status=='RUNNING' || paragraph.status=='PENDING'"></span>
+    <span class="{{paragraph.config.editorHide ? 'icon-size-fullscreen' : 'icon-size-actual'}}" style="cursor:pointer" tooltip-placement="top"
+          tooltip="{{(paragraph.config.editorHide ? 'Show' : 'Hide')}} editor (Ctrl+{{ (isMac ? 'Option' : 'Alt') }}+e)"
+          ng-click="toggleEditor(paragraph)"></span>
+    <span class="{{paragraph.config.tableHide ? 'icon-notebook' : 'icon-book-open'}}" style="cursor:pointer" tooltip-placement="top"
+          tooltip="{{(paragraph.config.tableHide ? 'Show' : 'Hide')}} output (Ctrl+{{ (isMac ? 'Option' : 'Alt') }}+o)"
+          ng-click="toggleOutput(paragraph)"></span>
+    <span class="dropdown navbar-right">
+      <span class="icon-settings" style="cursor:pointer"
+            data-toggle="dropdown"
+            type="button">
+      </span>
 
-    <ul class="dropdown-menu dropdown-menu-right" role="menu" style="width:270px;z-index:1002">
-      <li ng-controller="clipboardCtrl" ng-click="$event.stopPropagation()" style="text-align:center;margin-top:4px;">
-        <a  ngclipboard
-            ngclipboard-success="complete($event)"
-            ngclipboard-error="clipError($event)"
-            data-clipboard-text="{{paragraph.id}}"
-            tooltip-placement="top"
-            tooltip="{{tooltip}}">
-          <span>{{paragraph.id}}</span>
-        </a>
-      </li>
-      <li role="separator" class="divider"></li>
-      <li>
-        <a ng-click="$event.stopPropagation()" class="dropdown"><span class="fa fa-arrows-h shortcut-icon"></span>Width
-          <form style="display:inline; margin-left:5px; float:right">
-            <select ng-model="paragraph.config.colWidth"
-                    class="selectpicker"
-                    ng-change="changeColWidth(paragraph, paragraph.config.colWidth)"
-                    ng-options="col for col in colWidthOption"></select>
-          </form>
-        </a>
-      </li>
-      <li>
-        <a ng-click="moveUp(paragraph)" ng-hide="$first"><span class="icon-arrow-up shortcut-icon"></span>Move Up
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+k</span></a>
-      </li>
-      <li>
-        <a ng-click="moveDown(paragraph)" ng-hide="$last"><span class="icon-arrow-down shortcut-icon"></span>Move Down
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+j</span></a>
-      </li>
-      <li>
-        <a ng-click="insertNew('below')"><span class="icon-plus shortcut-icon"></span>Insert New
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+b</span></a>
-      </li>
-      <li>
-        <a ng-click="copyParagraph(getEditorValue())"><span class="fa fa-copy shortcut-icon"></span>Clone paragraph
-          <span class="shortcut-keys">Ctrl+Shift+c</span></a>
-      </li>
-      <li>
-        <!-- paragraph handler -->
-        <a ng-click="hideTitle(paragraph)"
-           ng-show="paragraph.config.title"><span class="fa fa-font shortcut-icon"></span>Hide title
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+t</span></a>
-        <a ng-click="showTitle(paragraph)"
-           ng-show="!paragraph.config.title"><span class="fa fa-font shortcut-icon"></span>Show title
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+t</span></a>
-      </li>
-      <li>
-        <a ng-click="hideLineNumbers(paragraph)"
-           ng-show="paragraph.config.lineNumbers"><span class="fa fa-list-ol shortcut-icon"></span>Hide line numbers
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+m</span></a>
-        <a ng-click="showLineNumbers(paragraph)"
-           ng-show="!paragraph.config.lineNumbers"><span class="fa fa-list-ol shortcut-icon"></span>Show line numbers
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+m</span></a>
-      </li>
-      <li>
-        <a ng-click="toggleEnableDisable(paragraph)"><span class="icon-control-play shortcut-icon"></span>
-          {{paragraph.config.enabled ? "Disable" : "Enable"}} run
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+r</span></a>
-      </li>
-      <li>
-        <a ng-click="goToSingleParagraph()"><span class="icon-share-alt shortcut-icon"></span>Link this paragraph
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+w</span></a>
-      </li>
-      <li>
-        <a ng-click="clearParagraphOutput(paragraph)"><span class="fa fa-eraser shortcut-icon"></span>Clear output
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+l</span></a>
-      </li>
-      <li>
-        <!-- remove paragraph -->
-        <a ng-click="removeParagraph(paragraph)" ng-hide="$last"><span class="fa fa-times shortcut-icon"></span>Remove
-          <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+d</span></a>
-      </li>
-    </ul>
+      <ul class="dropdown-menu dropdown-menu-right" role="menu" style="width:270px;z-index:1002">
+        <li ng-controller="clipboardCtrl" ng-click="$event.stopPropagation()" style="text-align:center;margin-top:4px;">
+          <a  ngclipboard
+              ngclipboard-success="complete($event)"
+              ngclipboard-error="clipError($event)"
+              data-clipboard-text="{{paragraph.id}}"
+              tooltip-placement="top"
+              tooltip="{{tooltip}}">
+            <span>{{paragraph.id}}</span>
+          </a>
+        </li>
+        <li role="separator" class="divider"></li>
+        <li>
+          <a ng-click="$event.stopPropagation()" class="dropdown"><span class="fa fa-arrows-h shortcut-icon"></span>Width
+            <form style="display:inline; margin-left:5px; float:right">
+              <select ng-model="paragraph.config.colWidth"
+                      class="selectpicker"
+                      ng-change="changeColWidth(paragraph, paragraph.config.colWidth)"
+                      ng-options="col for col in colWidthOption"></select>
+            </form>
+          </a>
+        </li>
+        <li>
+          <a ng-click="moveUp(paragraph)" ng-hide="$first"><span class="icon-arrow-up shortcut-icon"></span>Move Up
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+k</span></a>
+        </li>
+        <li>
+          <a ng-click="moveDown(paragraph)" ng-hide="$last"><span class="icon-arrow-down shortcut-icon"></span>Move Down
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+j</span></a>
+        </li>
+        <li>
+          <a ng-click="insertNew('below')"><span class="icon-plus shortcut-icon"></span>Insert New
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+b</span></a>
+        </li>
+        <li>
+          <a ng-click="copyParagraph(getEditorValue())"><span class="fa fa-copy shortcut-icon"></span>Clone paragraph
+            <span class="shortcut-keys">Ctrl+Shift+c</span></a>
+        </li>
+        <li>
+          <!-- paragraph handler -->
+          <a ng-click="hideTitle(paragraph)"
+             ng-show="paragraph.config.title"><span class="fa fa-font shortcut-icon"></span>Hide title
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+t</span></a>
+          <a ng-click="showTitle(paragraph)"
+             ng-show="!paragraph.config.title"><span class="fa fa-font shortcut-icon"></span>Show title
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+t</span></a>
+        </li>
+        <li>
+          <a ng-click="hideLineNumbers(paragraph)"
+             ng-show="paragraph.config.lineNumbers"><span class="fa fa-list-ol shortcut-icon"></span>Hide line numbers
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+m</span></a>
+          <a ng-click="showLineNumbers(paragraph)"
+             ng-show="!paragraph.config.lineNumbers"><span class="fa fa-list-ol shortcut-icon"></span>Show line numbers
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+m</span></a>
+        </li>
+        <li>
+          <a ng-click="toggleEnableDisable(paragraph)"><span class="icon-control-play shortcut-icon"></span>
+            {{paragraph.config.enabled ? "Disable" : "Enable"}} run
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+r</span></a>
+        </li>
+        <li>
+          <a ng-click="goToSingleParagraph()"><span class="icon-share-alt shortcut-icon"></span>Link this paragraph
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+w</span></a>
+        </li>
+        <li>
+          <a ng-click="clearParagraphOutput(paragraph)"><span class="fa fa-eraser shortcut-icon"></span>Clear output
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+l</span></a>
+        </li>
+        <li>
+          <!-- remove paragraph -->
+          <a ng-click="removeParagraph(paragraph)" ng-hide="$last"><span class="fa fa-times shortcut-icon"></span>Remove
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 'Alt'}}+d</span></a>
+        </li>
+      </ul>
+    </span>
   </span>
 </div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d9a11a93/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
index a572af9..529094d 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
@@ -185,6 +185,22 @@
       }
     });
 
+    $scope.getEditor = function() {
+      return $scope.editor;
+    };
+
+    $scope.$watch($scope.getEditor, function(newValue, oldValue) {
+      if (newValue === null || newValue === undefined) {
+        console.log('editor isnt loaded yet, returning');
+        return;
+      }
+      if ($scope.revisionView === true) {
+        $scope.editor.setReadOnly(true);
+      } else {
+        $scope.editor.setReadOnly(false);
+      }
+    });
+
     var isEmpty = function(object) {
       return !object;
     };
@@ -1140,7 +1156,7 @@
 
     $scope.$on('doubleClickParagraph', function(event, paragraphId) {
       if ($scope.paragraph.id === paragraphId && $scope.paragraph.config.editorHide &&
-          $scope.paragraph.config.editorSetting.editOnDblClick) {
+          $scope.paragraph.config.editorSetting.editOnDblClick && $scope.revisionView !== true) {
         var deferred = $q.defer();
         openEditorAndCloseTable($scope.paragraph);
         $timeout(

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d9a11a93/zeppelin-web/src/app/notebook/paragraph/paragraph.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.html b/zeppelin-web/src/app/notebook/paragraph/paragraph.html
index a2fedbc..75c8dc6 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.html
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.html
@@ -29,7 +29,7 @@ limitations under the License.
            ng-blur="setTitle(paragraph); input.showEditor = false"
            ng-enter="setTitle(paragraph); input.showEditor = false"
            focus-if="input.showEditor" />
-    <div ng-click="input.showEditor = !asIframe && !viewOnly; oldTitle = paragraph.title;"
+    <div ng-click="input.showEditor = !asIframe && !viewOnly && !revisionView; oldTitle = paragraph.title;"
          ng-show="!input.showEditor"
          ng-bind-html="paragraph.title || 'Untitled'">
     </div>
@@ -43,6 +43,7 @@ limitations under the License.
         dirty-text="dirtyText"
         original-text="originalText"
         on-load="aceLoaded"
+        revision-view="revisionView"
       ></code-editor>
     </div>
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d9a11a93/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js b/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
index c5360fe..fd9b6ac 100644
--- a/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
@@ -483,7 +483,16 @@ import ScatterchartVisualization from '../../../visualization/builtins/visualiza
       var newParagraphConfig = angular.copy(paragraph.config);
       newParagraphConfig.results = newParagraphConfig.results || [];
       newParagraphConfig.results[resultIndex] = config;
-      websocketMsgSrv.commitParagraph(paragraph.id, title, text, newParagraphConfig, params);
+      if ($scope.revisionView === true) {
+        // local update without commit
+        updateData({
+          type: $scope.type,
+          data: data
+        }, newParagraphConfig.results[resultIndex], paragraph, resultIndex);
+        renderResult($scope.type, true);
+      } else {
+        websocketMsgSrv.commitParagraph(paragraph.id, title, text, newParagraphConfig, params);
+      }
     };
 
     $scope.toggleGraphSetting = function() {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d9a11a93/zeppelin-web/src/components/editor/ace.editor.directive.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/editor/ace.editor.directive.html b/zeppelin-web/src/components/editor/ace.editor.directive.html
index 5bc75cc..4729e4b 100644
--- a/zeppelin-web/src/components/editor/ace.editor.directive.html
+++ b/zeppelin-web/src/components/editor/ace.editor.directive.html
@@ -18,7 +18,7 @@ limitations under the License.
                require : ['ace/ext/language_tools']
              }"
      ng-model="paragraph.text"
-     ng-class="{'paragraph-disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING',
+     ng-class="{'paragraph-disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' || revisionView === true,
            'paragraph-text--dirty' : dirtyText !== originalText && dirtyText !== undefined}">
 
 </div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d9a11a93/zeppelin-web/src/components/editor/codeEditor.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/editor/codeEditor.directive.js b/zeppelin-web/src/components/editor/codeEditor.directive.js
index d131062..7ad74c6 100644
--- a/zeppelin-web/src/components/editor/codeEditor.directive.js
+++ b/zeppelin-web/src/components/editor/codeEditor.directive.js
@@ -23,7 +23,8 @@
         paragraph: '=paragraphContext',
         dirtyText: '=dirtyText',
         originalText: '=originalText',
-        onLoad: '=onLoad'
+        onLoad: '=onLoad',
+        revisionView: '=revisionView'
       },
       link: function(scope, element, attrs, controller) {
         $templateRequest('components/editor/ace.editor.directive.html').then(function(editorHtml) {
@@ -31,6 +32,7 @@
           editor.attr('id', scope.paragraphId + '_editor');
           element.append(editor);
           $compile(editor)(scope);
+          console.log('codeEditor directive revision view is ' + scope.revisionView);
         });
       }
     };