فهرست منبع

HUE-2131 [core] Add sharing to pig, hive, rdbms

Karissa McKelvey 11 سال پیش
والد
کامیت
871583c

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 575 - 571
apps/beeswax/src/beeswax/templates/execute.mako


+ 3 - 2
apps/beeswax/src/beeswax/views.py

@@ -138,7 +138,6 @@ def _save_design(user, design, type_, design_obj, explicit_save, name=None, desc
 
   return design
 
-
 def delete_design(request):
   if request.method == 'POST':
     ids = request.POST.getlist('designs_selection')
@@ -401,6 +400,7 @@ def execute_query(request, design_id=None, query_history_id=None):
     design = safe_get_design(request, query_type, design_id)
     query_history = None
 
+  doc = design and design.id and design.doc.get()
   context = {
     'design': design,
     'query': query_history, # Backward
@@ -408,7 +408,8 @@ def execute_query(request, design_id=None, query_history_id=None):
     'autocomplete_base_url': reverse(get_app_name(request) + ':api_autocomplete_databases', kwargs={}),
     'autocomplete_base_url_hive': reverse('beeswax:api_autocomplete_databases', kwargs={}),
     'can_edit_name': design and design.id and not design.is_auto,
-    'can_edit': design and design.id and design.doc.get().can_write(request.user),
+    'doc_id': doc and doc.id or -1,
+    'can_edit': doc and doc.can_write(request.user),
     'action': action,
     'on_success_url': request.GET.get('on_success_url'),
     'has_metastore': 'metastore' in get_apps_dict(request.user)

+ 3 - 2
apps/pig/src/pig/models.py

@@ -65,7 +65,7 @@ class PigScript(Document):
     for attr in PigScript._ATTRIBUTES:
       if attrs.get(attr) is not None:
         data_dict[attr] = attrs[attr]
-        
+
     if 'name' in attrs:
       self.doc.update(name=attrs['name'])
 
@@ -106,7 +106,7 @@ def create_or_update_script(id, name, script, user, parameters, resources, hadoo
 
 
 def get_scripts(user, is_design=None):
-  scripts = []  
+  scripts = []
   data = Doc.objects.available(PigScript, user)
 
   if is_design is not None:
@@ -116,6 +116,7 @@ def get_scripts(user, is_design=None):
     data = script.dict
     massaged_script = {
       'id': script.id,
+      'docId': script.doc.get().id,
       'name': data['name'],
       'script': data['script'],
       'parameters': data['parameters'],

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 635 - 624
apps/pig/src/pig/templates/app.mako


+ 2 - 0
apps/pig/src/pig/views.py

@@ -80,6 +80,7 @@ def save(request):
 
   response = {
     'id': pig_script.id,
+    'docId': pig_script.doc.get().id
   }
 
   return HttpResponse(json.dumps(response), content_type="text/plain")
@@ -167,6 +168,7 @@ def copy(request):
 
   response = {
     'id': script_copy.id,
+    'docId': copy_doc.id,
     'name': name,
     'script': script,
     'parameters': parameters,

+ 10 - 3
apps/pig/static/js/pig.ko.js

@@ -35,6 +35,7 @@ var PigScript = function (pigScript) {
   var self = this;
 
   self.id = ko.observable(pigScript.id);
+  self.docId = ko.observable(pigScript.docId);
   self.isDesign = ko.observable(pigScript.isDesign);
   self.name = ko.observable(pigScript.name);
   self.can_write = ko.observable(pigScript.can_write);
@@ -201,6 +202,7 @@ var PigViewModel = function (props) {
 
   var _defaultScript = {
     id: -1,
+    docId: -1,
     name: self.LABELS.NEW_SCRIPT_NAME,
     script: self.LABELS.NEW_SCRIPT_CONTENT,
     parameters: self.LABELS.NEW_SCRIPT_PARAMETERS,
@@ -489,6 +491,11 @@ var PigViewModel = function (props) {
     });
   }
 
+  function updateScript(script, data) {
+    script.id(data.id);
+    script.docId(data.docId);
+  }
+
   function callSave(script) {
     $(document).trigger("saving");
     $.post(self.SAVE_URL,
@@ -501,9 +508,9 @@ var PigViewModel = function (props) {
           hadoopProperties: ko.toJSON(script.hadoopProperties()),
         },
         function (data) {
-          self.currentScript().id(data.id);
-          $(document).trigger("saved");
+          updateScript(self.currentScript(), data);
           self.updateScripts();
+          $(document).trigger("saved");
         }, "json").fail( function(xhr, textStatus, errorThrown) {
           $(document).trigger("error", xhr.responseText);
         });
@@ -529,7 +536,7 @@ var PigViewModel = function (props) {
         },
         function (data) {
           if (data.id && self.currentScript().id() != data.id){
-            script.id(data.id);
+            updateScript(self.currentScript(), data);
             $(document).trigger("loadEditor");
           }
           script.isRunning(true);

+ 145 - 136
apps/rdbms/src/rdbms/templates/execute.mako

@@ -14,7 +14,7 @@
 ## See the License for the specific language governing permissions and
 ## limitations under the License.
 <%!
-  from desktop.views import commonheader, commonfooter
+  from desktop.views import commonheader, commonfooter, commonshare
   from django.utils.translation import ugettext as _
 %>
 
@@ -24,174 +24,179 @@ ${ commonheader(_('Query'), app_name, user) | n,unicode }
 
 <%common:navbar></%common:navbar>
 
-<div class="container-fluid">
-  <div class="row-fluid">
-    <div class="span2" id="navigator">
-      <ul class="nav nav-tabs" style="margin-bottom: 0">
-        <li class="active"><a href="#navigatorTab" data-toggle="tab" class="sidetab">${ _('Assist') }</a></li>
-      </ul>
-
-      <div class="tab-content">
-    <div class="tab-pane active" id="navigatorTab">
-      <div class="card card-small card-tab" style="min-height: 470px;">
-        <div class="card-body" style="margin-top: 0">
-          <a href="#" title="${_('Double click on a table name or field to insert it in the editor')}" rel="tooltip" data-placement="left" class="pull-right" style="margin:10px;margin-left: 0"><i class="fa fa-question-circle"></i></a>
-          <a id="refreshNavigator" href="#" title="${_('Manually refresh the table list')}" rel="tooltip" data-placement="left" class="pull-right" style="margin:10px"><i class="fa fa-refresh"></i></a>
-
-          <ul class="nav nav-list" style="border: none; padding: 0; background-color: #FFF">
-            <li class="nav-header">${_('server')}</li>
-          </ul>
-
-          <select data-bind="options: $root.servers, value: $root.chosenServer, optionsText: 'nice_name'" class="input-medium chosen-select chosen-server" data-placeholder="${_('Choose a database...')}"></select>
-
-          <ul class="nav nav-list" style="border: none; padding: 0; background-color: #FFF">
-            <li class="nav-header">${_('database')}</li>
-          </ul>
-
-          <select data-bind="options: $root.databases, value: $root.chosenDatabase" class="input-medium chosen-select chosen-db" data-placeholder="${_('Choose a database...')}"></select>
-
-          <input id="navigatorSearch" type="text" placeholder="${ _('Table name...') }" style="width:90%; margin-top: 20px"/>
-          <div id="navigatorNoTables" style="margin-top: 20px">${_('The selected database has no tables.')}</div>
-          <ul id="navigatorTables" class="unstyled"></ul>
-          <div id="navigatorLoader">
-            <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #DDD"></i><!--<![endif]-->
-            <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
+<div id="rdbms-query-editor">
+  <div class="container-fluid">
+    <div class="row-fluid">
+      <div class="span2" id="navigator">
+        <ul class="nav nav-tabs" style="margin-bottom: 0">
+          <li class="active"><a href="#navigatorTab" data-toggle="tab" class="sidetab">${ _('Assist') }</a></li>
+        </ul>
+
+        <div class="tab-content">
+      <div class="tab-pane active" id="navigatorTab">
+        <div class="card card-small card-tab" style="min-height: 470px;">
+          <div class="card-body" style="margin-top: 0">
+            <a href="#" title="${_('Double click on a table name or field to insert it in the editor')}" rel="tooltip" data-placement="left" class="pull-right" style="margin:10px;margin-left: 0"><i class="fa fa-question-circle"></i></a>
+            <a id="refreshNavigator" href="#" title="${_('Manually refresh the table list')}" rel="tooltip" data-placement="left" class="pull-right" style="margin:10px"><i class="fa fa-refresh"></i></a>
+
+            <ul class="nav nav-list" style="border: none; padding: 0; background-color: #FFF">
+              <li class="nav-header">${_('server')}</li>
+            </ul>
+
+            <select data-bind="options: $root.servers, value: $root.chosenServer, optionsText: 'nice_name'" class="input-medium chosen-select chosen-server" data-placeholder="${_('Choose a database...')}"></select>
+
+            <ul class="nav nav-list" style="border: none; padding: 0; background-color: #FFF">
+              <li class="nav-header">${_('database')}</li>
+            </ul>
+
+            <select data-bind="options: $root.databases, value: $root.chosenDatabase" class="input-medium chosen-select chosen-db" data-placeholder="${_('Choose a database...')}"></select>
+
+            <input id="navigatorSearch" type="text" placeholder="${ _('Table name...') }" style="width:90%; margin-top: 20px"/>
+            <div id="navigatorNoTables" style="margin-top: 20px">${_('The selected database has no tables.')}</div>
+            <ul id="navigatorTables" class="unstyled"></ul>
+            <div id="navigatorLoader">
+              <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #DDD"></i><!--<![endif]-->
+              <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
+            </div>
           </div>
         </div>
       </div>
-    </div>
 
-  </div>
+    </div>
 
 
-    </div>
+      </div>
 
-    <div class="span10">
-      <div id="query">
-        <div class="card card-small">
-          <div style="margin-bottom: 10px">
-            <h1 class="card-heading simple">
-              <a id="collapse-editor" href="javascript:void(0)" class="pull-right"><i class="fa fa-caret-up"></i></a>
-              ${ _('Query Editor') }
-              % if can_edit_name:
-                :
-                <a href="javascript:void(0);"
-                   id="query-name"
-                   data-type="text"
-                   data-name="name"
-                   data-value="${design.name}"
-                   data-original-title="${ _('Query name') }"
-                   data-placement="right">
-                </a>
-              %endif
-            </h1>
-            % if can_edit_name:
-              <p style="margin-left: 20px">
-                <a href="javascript:void(0);"
-                   id="query-description"
-                   data-type="textarea"
-                   data-name="description"
-                   data-value="${design.desc}"
-                   data-original-title="${ _('Query description') }"
-                   data-placement="right">
-                </a>
-              </p>
-            % endif
-          </div>
-          <div class="card-body">
-            <div class="tab-content">
-              <div id="queryPane">
-
-                <div data-bind="css: {'hide': query.errors().length == 0}" class="hide alert alert-error">
-                  <p><strong>${_('Your query has the following error(s):')}</strong></p>
-                  <div data-bind="foreach: { 'data': query.errors, 'afterRender': resizeTable }">
-                    <p data-bind="text: $data" class="queryErrorMessage"></p>
+      <div class="span10">
+        <div id="query">
+          <div class="card card-small">
+            <div style="margin-bottom: 10px">
+              <h1 class="card-heading simple">
+                <a id="collapse-editor" href="javascript:void(0)" class="pull-right"><i class="fa fa-caret-up"></i></a>
+                % if can_edit_name:
+
+                  <a class="share-link" rel="tooltip" data-placement="bottom" style="padding-left:10px; padding-right: 10px" data-bind="click: openShareModal,
+                    attr: {'data-original-title': '${ _("Share") } '+name},
+                    css: {'baseShared': true, 'isShared': isShared()}">
+                    <i class="fa fa-users"></i>
+                  </a>
+                  <a href="javascript:void(0);"
+                     id="query-name"
+                     data-type="text"
+                     data-name="name"
+                     data-value="${design.name}"
+                     data-original-title="${ _('Query name') }"
+                     data-placement="right">
+                  </a>
+                  <a href="javascript:void(0);"
+                     id="query-description"
+                     data-type="textarea"
+                     data-name="description"
+                     data-value="${design.desc}"
+                     data-original-title="${ _('Query description') }"
+                     data-placement="right" style="font-size: 14px; margin-left: 10px">
+                  </a>
+              </h1>
+              % endif
+            </div>
+            <div class="card-body">
+              <div class="tab-content">
+                <div id="queryPane">
+
+                  <div data-bind="css: {'hide': query.errors().length == 0}" class="hide alert alert-error">
+                    <p><strong>${_('Your query has the following error(s):')}</strong></p>
+                    <div data-bind="foreach: { 'data': query.errors, 'afterRender': resizeTable }">
+                      <p data-bind="text: $data" class="queryErrorMessage"></p>
+                    </div>
                   </div>
-                </div>
 
-                <textarea class="hide" tabindex="1" name="query" id="queryField"></textarea>
+                  <textarea class="hide" tabindex="1" name="query" id="queryField"></textarea>
 
-                <div class="actions">
-                  <button data-bind="click: tryExecuteQuery" type="button" id="executeQuery" class="btn btn-primary disable-feedback" tabindex="2">${_('Execute')}</button>
-                  <button data-bind="click: trySaveQuery, css: {'hide': !$root.query.id() || $root.query.id() == -1}" type="button" class="btn hide">${_('Save')}</button>
-                  <button data-bind="click: trySaveAsQuery" type="button" class="btn">${_('Save as...')}</button>
-                  <button data-bind="click: tryExplainQuery" type="button" id="explainQuery" class="btn">${_('Explain')}</button>
-                  &nbsp; ${_('or create a')} &nbsp;<a type="button" class="btn" href="${ url('rdbms:execute_query') }">${_('New query')}</a>
-                  <br /><br />
-              </div>
+                  <div class="actions">
+                    <button data-bind="click: tryExecuteQuery" type="button" id="executeQuery" class="btn btn-primary disable-feedback" tabindex="2">${_('Execute')}</button>
+                    <button data-bind="click: trySaveQuery, css: {'hide': !$root.query.id() || $root.query.id() == -1}" type="button" class="btn hide">${_('Save')}</button>
+                    <button data-bind="click: trySaveAsQuery" type="button" class="btn">${_('Save as...')}</button>
+                    <button data-bind="click: tryExplainQuery" type="button" id="explainQuery" class="btn">${_('Explain')}</button>
+                    &nbsp; ${_('or create a')} &nbsp;<a type="button" class="btn" href="${ url('rdbms:execute_query') }">${_('New query')}</a>
+                    <br /><br />
+                </div>
 
+                </div>
               </div>
             </div>
           </div>
         </div>
-      </div>
-      <div data-bind="css: {'hide': rows().length == 0}" class="hide">
-        <div class="card card-small scrollable">
-          <table id="resultTable" class="table table-striped table-condensed resultTable" cellpadding="0" cellspacing="0" data-tablescroller-min-height-disable="true" data-tablescroller-enforce-height="true">
-            <thead>
-              <tr data-bind="foreach: columns">
-                <th data-bind="text: $data"></th>
-              </tr>
-            </thead>
-          </table>
+        <div data-bind="css: {'hide': rows().length == 0}" class="hide">
+          <div class="card card-small scrollable">
+            <table id="resultTable" class="table table-striped table-condensed resultTable" cellpadding="0" cellspacing="0" data-tablescroller-min-height-disable="true" data-tablescroller-enforce-height="true">
+              <thead>
+                <tr data-bind="foreach: columns">
+                  <th data-bind="text: $data"></th>
+                </tr>
+              </thead>
+            </table>
+          </div>
         </div>
-      </div>
 
-      <div data-bind="css: {'hide': !resultsEmpty()}" class="hide">
-        <div class="card card-small scrollable">
-          <div class="row-fluid">
-            <div class="span10 offset1 center empty-wrapper">
-              <i class="fa fa-frown-o"></i>
-              <h1>${_('The server returned no results.')}</h1>
-              <br />
+        <div data-bind="css: {'hide': !resultsEmpty()}" class="hide">
+          <div class="card card-small scrollable">
+            <div class="row-fluid">
+              <div class="span10 offset1 center empty-wrapper">
+                <i class="fa fa-frown-o"></i>
+                <h1>${_('The server returned no results.')}</h1>
+                <br />
+              </div>
             </div>
           </div>
         </div>
-      </div>
 
-      <div data-bind="css: {'hide': !isExecuting()}" class="hide">
-        <div class="card card-small scrollable">
-          <div class="row-fluid">
-            <div class="span10 offset1 center" style="padding: 30px">
-              <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 60px; color: #DDD"></i><!--<![endif]-->
-              <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
+        <div data-bind="css: {'hide': !isExecuting()}" class="hide">
+          <div class="card card-small scrollable">
+            <div class="row-fluid">
+              <div class="span10 offset1 center" style="padding: 30px">
+                <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 60px; color: #DDD"></i><!--<![endif]-->
+                <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
+              </div>
             </div>
           </div>
         </div>
       </div>
-    </div>
 
+    </div>
   </div>
-</div>
 
 
-<div id="saveAsQueryModal" class="modal hide fade">
-  <div class="modal-header">
-    <a href="#" class="close" data-dismiss="modal">&times;</a>
-    <h3>${_('Save your query')}</h3>
-  </div>
-  <div class="modal-body">
-    <form class="form-horizontal">
-      <div class="control-group" id="saveas-query-name">
-        <label class="control-label">${_('Name')}</label>
-        <div class="controls">
-          <input data-bind="value: $root.query.name" type="text" class="input-xlarge">
+  <div id="saveAsQueryModal" class="modal hide fade">
+    <div class="modal-header">
+      <a href="#" class="close" data-dismiss="modal">&times;</a>
+      <h3>${_('Save your query')}</h3>
+    </div>
+    <div class="modal-body">
+      <form class="form-horizontal">
+        <div class="control-group" id="saveas-query-name">
+          <label class="control-label">${_('Name')}</label>
+          <div class="controls">
+            <input data-bind="value: $root.query.name" type="text" class="input-xlarge">
+          </div>
         </div>
-      </div>
-      <div class="control-group">
-        <label class="control-label">${_('Description')}</label>
-        <div class="controls">
-          <input data-bind="value: $root.query.description" type="text" class="input-xlarge">
+        <div class="control-group">
+          <label class="control-label">${_('Description')}</label>
+          <div class="controls">
+            <input data-bind="value: $root.query.description" type="text" class="input-xlarge">
+          </div>
         </div>
-      </div>
-    </form>
-  </div>
-  <div class="modal-footer">
-    <button class="btn" data-dismiss="modal">${_('Cancel')}</button>
-    <button data-bind="click: modalSaveAsQuery" class="btn btn-primary">${_('Save')}</button>
+      </form>
+    </div>
+    <div class="modal-footer">
+      <button class="btn" data-dismiss="modal">${_('Cancel')}</button>
+      <button data-bind="click: modalSaveAsQuery" class="btn btn-primary">${_('Save')}</button>
+    </div>
   </div>
 </div>
 
+${ commonshare() | n,unicode }
+
+
 
 <style type="text/css">
   h1 {
@@ -323,6 +328,7 @@ ${ commonheader(_('Query'), app_name, user) | n,unicode }
 <script src="/static/ext/js/knockout-min.js" type="text/javascript" charset="utf-8"></script>
 <script src="/static/ext/js/knockout.mapping-2.3.2.js" type="text/javascript" charset="utf-8"></script>
 <script src="/rdbms/static/js/rdbms.vm.js"></script>
+<script src="/static/js/share.vm.js"></script>
 <script src="/static/ext/js/codemirror-3.11.js"></script>
 <link rel="stylesheet" href="/static/ext/css/codemirror.css">
 <script src="/static/ext/js/codemirror-sql.js"></script>
@@ -666,6 +672,9 @@ ${ commonheader(_('Query'), app_name, user) | n,unicode }
 
   // Knockout
   viewModel = new RdbmsViewModel();
+  shareViewModel = setupSharing("#documentShareModal");
+  shareViewModel.setDocId(${doc_id});
+
   viewModel.fetchServers();
   viewModel.database.subscribe((function() {
     // First call skipped to avoid reset of hueRdbmsLastDatabase
@@ -697,7 +706,7 @@ ${ commonheader(_('Query'), app_name, user) | n,unicode }
 
     $("#executeQuery").button("reset");
   }
-  ko.applyBindings(viewModel);
+  ko.applyBindings(viewModel, $("#rdbms-query-editor")[0]);
 
   function resetNavigator() {
     renderNavigator();

+ 1 - 0
apps/rdbms/src/rdbms/views.py

@@ -70,6 +70,7 @@ def execute_query(request, design_id=None, query_history_id=None):
 
   return render('execute.mako', request, {
     'action': action,
+    'doc_id': design.id and design.doc.get().id,
     'design': design,
     'autocomplete_base_url': reverse('rdbms:api_autocomplete_databases', kwargs={}),
     'can_edit_name': design.id and not design.is_auto,

+ 53 - 55
desktop/core/src/desktop/api.py

@@ -124,6 +124,29 @@ def massaged_tags(tag, tag_doc_mapping):
     'docs': [doc.id for doc in tag_doc_mapping[tag]] # Could get with one request groupy
   }
 
+def massage_permissions(document):
+  """
+  Returns the permissions for a given document as a dictionary
+  """
+  read_perms = document.list_permissions(perm='read')
+  write_perms = document.list_permissions(perm='write')
+  return {
+    'perms': {
+        'read': {
+          'users': [{'id': perm_user.id, 'username': perm_user.username} \
+                     for perm_user in read_perms.users.all()],
+          'groups': [{'id': perm_group.id, 'name': perm_group.name} \
+                     for perm_group in read_perms.groups.all()]
+        },
+        'write': {
+          'users': [{'id': perm_user.id, 'username': perm_user.username} \
+                     for perm_user in write_perms.users.all()],
+          'groups': [{'id': perm_group.id, 'name': perm_group.name} \
+                     for perm_group in write_perms.groups.all()]
+        }
+      }
+    }
+
 def massaged_documents_for_json(documents, user):
   """
   var DOCUMENTS_DEFAULTS = {
@@ -162,65 +185,40 @@ def massaged_documents_for_json(documents, user):
     except:
       # If app of document is disabled
       url = ''
-    read_perms = document.list_permissions(perm='read')
-    write_perms = document.list_permissions(perm='write')
-    docs[document.id] = {
-      'id': document.id,
-      'contentType': html.conditional_escape(document.content_type.name),
-      'icon': document.icon,
-      'name': html.conditional_escape(document.name),
-      'url': html.conditional_escape(url),
-      'description': html.conditional_escape(document.description),
-      'tags': [{'id': tag.id, 'name': html.conditional_escape(tag.tag)} \
-               for tag in document.tags.all()],
-      'perms': {
-        'read': {
-          'users': [{'id': perm_user.id, 'username': perm_user.username} \
-                    for perm_user in read_perms.users.all().only('id', 'username')],
-          'groups': [{'id': perm_group.id, 'name': perm_group.name} \
-                     for perm_group in read_perms.groups.all().only('id', 'name')]
-        },
-        'write': {
-          'users': [{'id': perm_user.id, 'username': perm_user.username} for perm_user in write_perms.users.all()],
-          'groups': [{'id': perm_group.id, 'name': perm_group.name} for perm_group in write_perms.groups.all()]
-        }
-      },
-      'owner': document.owner.username,
-      'isMine': document.owner == user,
-      'lastModified': document.last_modified.strftime("%x %X"),
-      'lastModifiedInMillis': time.mktime(document.last_modified.timetuple())
-    }
+    docs[document.id] = massage_doc_for_json(document, user, url)
 
   return docs
 
+def get_document(request):
+  if request.method == 'POST':
+    return Http404()
+  elif request.method == 'GET':
+    doc_id = request.GET['id']
+    doc = Document.objects.get(id=doc_id)
+    response = massage_doc_for_json(doc, request.user)
+    return HttpResponse(json.dumps(response), mimetype="application/json")
+
+def massage_doc_for_json(document, user, url=''):
+  read_perms = document.list_permissions(perm='read')
+  write_perms = document.list_permissions(perm='write')
+  massaged_doc = {
+    'id': document.id,
+    'contentType': html.conditional_escape(document.content_type.name),
+    'icon': document.icon,
+    'name': html.conditional_escape(document.name),
+    'url': html.conditional_escape(url),
+    'description': html.conditional_escape(document.description),
+    'tags': [{'id': tag.id, 'name': html.conditional_escape(tag.tag)} \
+             for tag in document.tags.all()],
+    'owner': document.owner.username,
+    'isMine': document.owner == user,
+    'lastModified': document.last_modified.strftime("%x %X"),
+    'lastModifiedInMillis': time.mktime(document.last_modified.timetuple())
+  }
 
-def massage_doc_for_json(doc, user):
-  read_perms = doc.list_permissions(perm='read')
-  write_perms = doc.list_permissions(perm='write')
-  return {
-      'id': doc.id,
-      'contentType': doc.content_type.name,
-      'icon': doc.icon,
-      'name': html.conditional_escape(doc.name),
-      'url': html.conditional_escape(doc.content_object.get_absolute_url()),
-      'description': html.conditional_escape(doc.description),
-      'tags': [{'id': tag.id, 'name': html.conditional_escape(tag.tag)} \
-               for tag in doc.tags.all()],
-      'perms': {
-        'read': {
-          'users': [{'id': perm_user.id, 'username': perm_user.username} for perm_user in read_perms.users.all()],
-          'groups': [{'id': perm_group.id, 'name': perm_group.name} for perm_group in read_perms.groups.all()]
-        },
-        'write': {
-          'users': [{'id': perm_user.id, 'username': perm_user.username} for perm_user in write_perms.users.all()],
-          'groups': [{'id': perm_group.id, 'name': perm_group.name} for perm_group in write_perms.groups.all()]
-        }
-      },
-      'owner': doc.owner.username,
-      'isMine': doc.owner.username == user.username,
-      'lastModified': doc.last_modified.strftime("%x %X"),
-      'lastModifiedInMillis': time.mktime(doc.last_modified.timetuple())
-    }
+  permissions = massage_permissions(document)
+  massaged_doc.update(permissions)
+  return massaged_doc
 
 
 def add_tag(request):

+ 1 - 1
desktop/core/src/desktop/templates/common_header.mako

@@ -355,7 +355,7 @@ from django.utils.translation import ugettext as _
     <a class="brand nav-tooltip pull-left" title="${_('About Hue')}" rel="navigator-tooltip" href="/about"><img src="/static/art/hue-logo-mini-white.png" data-orig="/static/art/hue-logo-mini-white.png" data-hover="/static/art/hue-logo-mini-white-hover.png" /></a>
     % if user.is_authenticated() and section != 'login':
      <ul class="nav nav-pills pull-left">
-       <li><a title="${_('My documents')}" rel="navigator-tooltip" href="${ url('desktop.views.home') }" style="padding-bottom:2px!important"><i class="fa fa-home" style="font-size: 19px"></i></a></li>        
+       <li><a title="${_('My documents')}" rel="navigator-tooltip" href="${ url('desktop.views.home') }" style="padding-bottom:2px!important"><i class="fa fa-home" style="font-size: 19px"></i></a></li>
        <%
          query_apps = count_apps(apps, ['beeswax', 'impala', 'rdbms', 'pig', 'jobsub', 'spark']);
        %>

+ 1 - 1
desktop/core/src/desktop/templates/common_share.mako

@@ -51,7 +51,7 @@ from django.utils.translation import ugettext as _
       <div class="clearfix"></div>
       <div style="margin-top: 20px">
         <div class="input-append">
-          <input id="documentShareTypeahead" type="text" style="width: 460px" placeholder="${_('Type a username, a group, or "Everyone"')}">
+          <input id="documentShareTypeahead" type="text" style="width: 460px" placeholder="${_('Type a username or a group name')}">
           <div class="btn-group">
             <a id="documentShareAddBtn" class="btn"><i class="fa fa-plus-circle"></i> <span data-bind="text: selectedPermLabel"></span></a>
             <a class="btn dropdown-toggle" data-toggle="dropdown">

+ 1 - 16
desktop/core/src/desktop/templates/home.mako

@@ -67,10 +67,6 @@ ${ commonheader(_('Welcome Home'), "home", user) | n,unicode }
     margin-bottom: 6px;
   }
 
-  .trash-share, .share-link {
-    cursor: pointer;
-  }
-
   .white {
     padding: 9px 18px;
     margin-top: 1px;
@@ -81,17 +77,6 @@ ${ commonheader(_('Welcome Home'), "home", user) | n,unicode }
     text-overflow: ellipsis;
   }
 
-  .baseShared {
-    color: #999;
-  }
-
-  .isShared {
-    color: #338bb8!important;
-  }
-
-  #documentShareModal .modal-body {
-    overflow-y: initial;
-  }
 
 </style>
 
@@ -347,7 +332,7 @@ ${ commonshare() | n,unicode }
     viewModel = new HomeViewModel(JSON_TAGS, JSON_DOCS);
     ko.applyBindings(viewModel, $('#documentList')[0]);
 
-    shareViewModel = setupSharing(viewModel.updateDoc, "#documentShareModal");
+    shareViewModel = setupSharing("#documentShareModal", viewModel.updateDoc);
 
     var selectedUserOrGroup, map, dropdown = null;
 

+ 1 - 0
desktop/core/src/desktop/urls.py

@@ -85,6 +85,7 @@ dynamic_patterns += patterns('desktop.api',
   (r'^desktop/api/tag/remove_tag$', 'remove_tag'),
   (r'^desktop/api/doc/tag$', 'tag'),
   (r'^desktop/api/doc/update_tags$', 'update_tags'),
+  (r'^desktop/api/doc/get$', 'get_document'),
 
   # Permissions
   (r'^desktop/api/doc/update_permissions', 'update_permissions'),

+ 23 - 0
desktop/core/static/css/hue3.css

@@ -1682,6 +1682,29 @@ border-bottom-left-radius: 0px;
   height: 16px;
 }
 
+/*********************************************************
+ *
+ * Sharing styles
+ *
+ ********************************************************/
+
+.baseShared {
+  color: #999;
+}
+
+.isShared {
+  color: #338bb8!important;
+}
+
+#documentShareModal .modal-body {
+  overflow-y: initial;
+}
+
+.trash-share, .share-link {
+  cursor: pointer;
+}
+
+
 /*********************************************************
  *
  * Misc styles that can be reused as needed

+ 35 - 26
desktop/core/static/js/share.vm.js

@@ -1,3 +1,5 @@
+var shareViewModel;
+
 function ShareViewModel(updateDocF) {
   var self = this;
 
@@ -24,6 +26,25 @@ function ShareViewModel(updateDocF) {
   }))
 
   self.updateDoc = updateDocF
+
+  self.setDocId = function(docId) {
+    if (docId == -1) { return false; }
+    $.get('/desktop/api/doc/get', { id : docId },
+      function (data) {
+        shareViewModel.selectedDoc(data)
+    });
+  }
+}
+
+
+function openShareModal() {
+  $("#documentShareModal").modal("show");
+}
+
+function isShared() {
+  if (!shareViewModel) { return false; }
+  read = shareViewModel.selectedDoc().perms.read;
+  return read.users.length + read.groups.length > 0
 }
 
 function prettifyUsername(userId) {
@@ -39,7 +60,10 @@ function prettifyUsername(userId) {
   return "";
 }
 
-function setupSharing(updateFunc, id) {
+function setupSharing(id, updateFunc) {
+  if(!updateFunc) {
+    updateFunc = function () {}
+  }
   shareViewModel = new ShareViewModel(updateFunc);
   ko.applyBindings(shareViewModel, $(id)[0]);
 
@@ -104,45 +128,30 @@ function setupSharing(updateFunc, id) {
   return shareViewModel;
 }
 
-
-function removeUserReadShare(user) {
-  $(shareViewModel.selectedDoc().perms.read.users).each(function (cnt, item) {
+function updateSharePerm(perms, user) {
+  $(perms).each(function (cnt, item) {
     if (item.id == user.id) {
-      shareViewModel.selectedDoc().perms.read.users.splice(cnt, 1);
+      perms.splice(cnt, 1);
     }
   });
   shareViewModel.selectedDoc.valueHasMutated();
   shareDocFinal();
 }
 
+function removeUserReadShare(user) {
+  updateSharePerm(shareViewModel.selectedDoc().perms.read.users, user);
+}
+
 function removeUserWriteShare(user) {
-  $(shareViewModel.selectedDoc().perms.write.users).each(function (cnt, item) {
-    if (item.id == user.id) {
-      shareViewModel.selectedDoc().perms.write.users.splice(cnt, 1);
-    }
-  });
-  shareViewModel.selectedDoc.valueHasMutated();
-  shareDocFinal();
+  updateSharePerm(shareViewModel.selectedDoc().perms.write.users, user);
 }
 
 function removeGroupReadShare(group) {
-  $(shareViewModel.selectedDoc().perms.read.groups).each(function (cnt, item) {
-    if (item.id == group.id) {
-      shareViewModel.selectedDoc().perms.read.groups.splice(cnt, 1);
-    }
-  });
-  shareViewModel.selectedDoc.valueHasMutated();
-  shareDocFinal();
+  updateSharePerm(shareViewModel.selectedDoc().perms.read.groups, group);
 }
 
 function removeGroupWriteShare(group) {
-  $(shareViewModel.selectedDoc().perms.write.groups).each(function (cnt, item) {
-    if (item.id == group.id) {
-      shareViewModel.selectedDoc().perms.write.groups.splice(cnt, 1);
-    }
-  });
-  shareViewModel.selectedDoc.valueHasMutated();
-  shareDocFinal();
+  updateSharePerm(shareViewModel.selectedDoc().perms.write.groups, group);
 }
 
 function changeDocumentSharePerm(perm) {

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است