Преглед изворни кода

HUE-880 [core] Hue works out of the box

Hue admins are shown a wizard for helping to configure and get started with Hue.
Normal users do not see/don't have access to it.
Beeswax examples are mande re-installable.
Beeswax queries can be deleted by admin.
A link to HUE-1141 is missing for now.
Romain Rigaux пре 12 година
родитељ
комит
f69e2c6

+ 3 - 0
apps/about/src/about/settings.py

@@ -13,7 +13,10 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+
 DJANGO_APPS = [ "about" ]
 NICE_NAME = "About"
 REQUIRES_HADOOP = False
 MENU_INDEX = 80
+
+IS_URL_NAMESPACED = True

+ 182 - 0
apps/about/src/about/templates/admin_wizard.mako

@@ -0,0 +1,182 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+<%!
+from desktop.views import commonheader, commonfooter
+from django.utils.translation import ugettext as _
+%>
+
+${ commonheader(_('About Hue'), "quick_start", user, "100px") | n,unicode }
+
+% if user.is_superuser:
+  <div class="row-fluid">
+    <div class="subnav subnav-fixed">
+      <div class="container-fluid">
+        <ul class="nav nav-pills">
+          <li class="active"><a href="${url("about:admin_wizard")}">${_('Quick start')}</a></li>
+          <li><a href="${url("desktop.views.dump_config")}">${_('Configuration')}</a></li>
+          <li><a href="${url("desktop.views.log_view")}">${_('Server Logs')}</a></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+% endif
+
+<div class="container-fluid">
+
+  <div class="row-fluid">
+    <h2>Hue ${version} <img src="/static/art/hue-login-logo.png"/></h2>
+  </div>
+
+  % if user.is_superuser:
+  <br/>
+  ${ _('Configure Hue through this wizard.') }
+  <br/>
+  <br/>
+
+  <div class="row-fluid">
+   <div id="properties" class="section">
+    <ul class="nav nav-tabs">
+      <li class="active"><a href="#step1" class="step">${ _('Step 1: Check Configuration') }</a></li>
+      <li><a href="#step2" class="step">${ _('Step 2: Examples') }</a></li>
+      <li><a href="#step3" class="step">${ _('Step 3: Users') }</a></li>
+      <li><a href="/beeswax" class="step">${ _('Step 4: Use Hue') }</a></li>
+    </ul>
+
+    <div class="steps" >
+      <div id="step1" class="stepDetails">
+       ${ check_config.content | n,unicode }
+    </div>
+
+    <div id="step2" class="stepDetails hide">
+      <p>
+      ${ _('Install or re-install available app examples') }:
+      </p>
+      <ul class="nav nav-tabs nav-stacked">
+        % if 'beeswax' in app_names:
+          <li>
+            <button type="button" class="btn" data-loading-text="Installing..." data-url="${ url('beeswax:install_examples') }">
+             <i class="icon-download-alt"></i> Beeswax
+            </button>
+          </li>
+        % endif
+        % if 'oozie' in app_names:
+          <li>
+            <button type="button" class="btn" data-loading-text="Installing..." data-url="${ url('oozie:setup_app') }">
+              <i class="icon-download-alt"></i> Oozie
+            </button>
+          </li>
+        % endif
+      </ul>
+    </div>
+
+    <div id="step3" class="stepDetails hide">
+      ${ _('Go create or import users in the') }
+      <a class="btn" href="${ url('useradmin.views.list_users') }" target="_blank">${ _('User Admin') } <img src="/useradmin/static/art/icon_useradmin_24.png"></a>
+      ${ _('application') }.
+    </div>
+  </div>
+
+    <div class="form-actions">
+      <a id="backBtn" class="btn disabled">${ _('Back') }</a>
+      <a id="nextBtn" class="btn btn-primary disable-feedback">${ _('Next') }</a>
+    </div>
+  </div>
+  % endif
+</div>
+
+% if user.is_superuser:
+<style type="text/css">
+  .steps {
+    min-height: 400px;
+    padding-top: 20px;
+  }
+
+  input[type=submit] {
+    margin-left: 50px;
+  }
+</style>
+
+<script src="/static/ext/js/routie-0.3.0.min.js" type="text/javascript" charset="utf-8"></script>
+
+<script type="text/javascript" charset="utf-8">
+$(document).ready(function(){
+
+  $("button").click(function() {
+    var button = $(this);
+    $(button).button('loading');
+    $.post($(this).data("url"), function(data) {
+    })
+    .always(function(data) {
+      $.jHueNotify.info("${ _('Done!') }");
+      $(button).button('reset');
+    });
+  });
+
+  var currentStep = "step1";
+
+  routie({
+    "step1":function () {
+      showStep("step1");
+    },
+    "step2":function () {
+      showStep("step2");
+    },
+    "step3":function () {
+      showStep("step3");
+    },
+    "step4":function () {
+      showStep("step4");
+    }
+  });
+
+  function showStep(step) {
+    currentStep = step;
+    if (step != "step1") {
+      $("#backBtn").removeClass("disabled");
+    } else {
+      $("#backBtn").addClass("disabled");
+    }
+
+    if (step != $(".stepDetails:last").attr("id")) {
+      $("#nextBtn").removeClass("disabled");
+    } else {
+      $("#nextBtn").addClass("disabled");
+    }
+
+    $("a.step").parent().removeClass("active");
+    $("a.step[href=#" + step + "]").parent().addClass("active");
+    $(".stepDetails").hide();
+    $("#" + step).show();
+  }
+
+  $("#backBtn").click(function () {
+    var nextStep = (currentStep.substr(4) * 1 - 1);
+    if (nextStep >= 1) {
+      routie("step" + nextStep);
+    }
+  });
+
+  $("#nextBtn").click(function () {
+    var nextStep = (currentStep.substr(4) * 1 + 1);
+    if (nextStep <= $(".step").length) {
+      routie("step" + nextStep);
+    }
+  });
+});
+</script>
+% endif
+
+${ commonfooter(messages) | n,unicode }

+ 0 - 40
apps/about/src/about/templates/index.mako

@@ -1,40 +0,0 @@
-## Licensed to Cloudera, Inc. under one
-## or more contributor license agreements.  See the NOTICE file
-## distributed with this work for additional information
-## regarding copyright ownership.  Cloudera, Inc. licenses this file
-## to you under the Apache License, Version 2.0 (the
-## "License"); you may not use this file except in compliance
-## with the License.  You may obtain a copy of the License at
-##
-##     http://www.apache.org/licenses/LICENSE-2.0
-##
-## Unless required by applicable law or agreed to in writing, software
-## distributed under the License is distributed on an "AS IS" BASIS,
-## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-## See the License for the specific language governing permissions and
-## limitations under the License.
-<%!
-from desktop.views import commonheader, commonfooter
-from django.utils.translation import ugettext as _
-%>
-
-${ commonheader(_('About Hue'), "about", user, "100px") | n,unicode }
-
-% if user.is_superuser:
-<div class="subnav subnav-fixed">
-  <div class="container-fluid">
-    <ul class="nav nav-pills">
-      <li><a href="${url("desktop.views.dump_config")}">${_('Configuration')}</a></li>
-      <li><a href="${url("desktop.views.check_config")}">${_('Check for misconfiguration')}</a></li>
-      <li><a href="${url("desktop.views.log_view")}">${_('Server Logs')}</a></li>
-    </ul>
-  </div>
-</div>
-% endif
-
-<div class="container-fluid">
-  <img src="/static/art/hue-login-logo.png" />
-  <p>Hue ${version}</p>
-</div>
-
-${ commonfooter(messages) | n,unicode }

+ 3 - 2
apps/about/src/about/urls.py

@@ -17,6 +17,7 @@
 
 from django.conf.urls.defaults import patterns, url
 
-urlpatterns = patterns('about',
-  url(r'^$', 'views.index'),
+urlpatterns = patterns('about.views',
+  url(r'^$', 'admin_wizard', name='index'),
+  url(r'^admin_wizard$', 'admin_wizard', name='admin_wizard'),
 )

+ 14 - 4
apps/about/src/about/views.py

@@ -15,10 +15,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from desktop.lib.django_util import render
 from django.conf import settings
 
-def index(request):
-  return render('index.mako', request,
-    dict(version=settings.HUE_DESKTOP_VERSION))
+from desktop.lib.django_util import render
+from desktop.views import check_config
+from desktop import appmanager
+
+
+def admin_wizard(request):
+  apps = appmanager.get_apps(request.user)
+  app_names = [app.name for app in sorted(apps, key=lambda app: app.menu_index)]
+
+  return render('admin_wizard.mako', request, {
+      'version': settings.HUE_DESKTOP_VERSION,
+      'check_config': check_config(request),
+      'app_names': app_names
+  })
 

+ 11 - 7
apps/beeswax/src/beeswax/management/commands/beeswax_install_examples.py

@@ -77,11 +77,7 @@ class Command(NoArgsCommand):
   """
 
   def handle_noargs(self, **options):
-    """Main entry point to install examples. May raise InstallException"""
-    if self._check_installed():
-      msg = _('Beeswax examples already installed.')
-      LOG.error(msg)
-      raise InstallException(msg)
+    """Main entry point to install or re-install examples. May raise InstallException"""
 
     try:
       user = self._install_user()
@@ -130,7 +126,11 @@ class Command(NoArgsCommand):
 
     for table_dict in table_list:
       table = SampleTable(table_dict)
-      table.install(django_user)
+      try:
+        table.install(django_user)
+      except Exception, ex:
+        LOG.exception(ex)
+        LOG.error('Could not install table: %s' % (ex,))
     LOG.info('Successfully created sample tables with data')
 
   def _install_queries(self, django_user):
@@ -143,7 +143,11 @@ class Command(NoArgsCommand):
 
     for design_dict in design_list:
       design = SampleDesign(design_dict)
-      design.install(django_user)
+      try:
+        design.install(django_user)
+      except Exception, ex:
+        LOG.exception(ex)
+        LOG.error('Could not install query: %s' % (ex,))
     LOG.info('Successfully installed all sample queries')
 
 

+ 1 - 1
apps/beeswax/src/beeswax/templates/list_designs.mako

@@ -60,7 +60,7 @@ ${layout.menubar(section='saved queries')}
       %>
       % for design in page.object_list:
         <%
-          may_edit = user == design.owner
+          may_edit = user == design.owner or user.is_superuser
         %>
       <tr>
         <td data-row-selector-exclude="true">

+ 5 - 5
apps/beeswax/src/beeswax/views.py

@@ -825,10 +825,7 @@ def install_examples(request):
   """
   Handle installing sample data and example queries.
   """
-  if request.method == 'GET':
-    return render('confirm.html', request,
-                  dict(url=request.path, title=_('Install sample tables and Beeswax examples?')))
-  elif request.method == 'POST':
+  if request.method == 'POST':
     result = {}
     result['creationSucceeded'] = False
     result['message'] = ''
@@ -839,8 +836,11 @@ def install_examples(request):
     except Exception, err:
       LOG.exception(err)
       result['message'] = str(err)
-
     return HttpResponse(json.dumps(result), mimetype="application/json")
+  else:
+    return render('confirm.html', request,
+                  {'url': request.path, 'title': _('Install sample tables and Beeswax examples?')})
+
 
 
 @login_notrequired

+ 1 - 1
desktop/core/src/desktop/auth/views_test.py

@@ -88,4 +88,4 @@ class TestLogin(object):
     client.get('/accounts/logout')
     # Login
     response = client.post('/accounts/login/', dict(username="test", password="test"), follow=True)
-    assert_equal(response.template, 'index.mako')
+    assert_true('admin_wizard.mako' in response.template, response.template) # Go to superuser wizard

+ 2 - 2
desktop/core/src/desktop/require_login_test.py

@@ -36,8 +36,8 @@ def test_require_login():
   # AllowAllBackend should let us in.
   c.login(username="test", password="test")
   # And now we shouldn't need to be redirected.
-  response = c.get('/')
-  assert_equal(200, response.status_code, "Expected ok status.")
+  response = c.get('/', follow=True)
+  assert_equal(200, response.status_code)
 
 
 def test_ajax_require_login():

+ 9 - 10
desktop/core/src/desktop/templates/about_layout.mako

@@ -28,14 +28,13 @@ def is_selected(section, matcher):
 %>
 
 <%def name="menubar(section='')">
-	<div class="subnav subnav-fixed">
-		<div class="container-fluid">
-		<ul class="nav nav-pills">
-			<li class="${is_selected(section, 'dump_config')}"><a href="${url("desktop.views.dump_config")}">${_('Configuration')}</a></li>
-			<li class="${is_selected(section, 'check_config')}"><a href="${url("desktop.views.check_config")}">${_('Check for misconfiguration')}</a></li>
-			<li class="${is_selected(section, 'log_view')}"><a href="${url("desktop.views.log_view")}">${_('Server Logs')}</a></li>
-		</ul>
-		</div>
-	</div>
+  <div class="subnav subnav-fixed">
+    <div class="container-fluid">
+      <ul class="nav nav-pills">
+        <li class="${is_selected(section, 'quick_start')}"><a href="${url("about:admin_wizard")}">${_('Quick start')}</a></li>
+        <li class="${is_selected(section, 'dump_config')}"><a href="${url("desktop.views.dump_config")}">${_('Configuration')}</a></li>
+        <li class="${is_selected(section, 'log_view')}"><a href="${url("desktop.views.log_view")}">${_('Server Logs')}</a></li>
+      </ul>
+    </div>
+  </div>
 </%def>
-

+ 1 - 9
desktop/core/src/desktop/templates/check_config.mako

@@ -15,20 +15,14 @@
 ## limitations under the License.
 <%!
 from desktop.lib.conf import BoundConfig
-from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 %>
 
-<%namespace name="layout" file="about_layout.mako" />
-${ commonheader(_('About'), "about", user, "100px") | n,unicode }
-
-${layout.menubar(section='check_config')}
-
 <div class="container-fluid">
     ${_('Configuration files located in')} <code>${conf_dir}</code>
     <br/><br/>
     % if error_list:
-      <h2>${_('Potential misconfiguration detected. Fix and restart Hue.')}</h2>
+      <div class="alert alert-error">${_('Potential misconfiguration detected. Fix and restart Hue.')}</div>
       <br/>
         <table class="table table-striped">
       % for confvar, error in error_list:
@@ -56,5 +50,3 @@ ${layout.menubar(section='check_config')}
       <h2>${_('All OK. Configuration check passed.')}</h2>
     % endif
 </div>
-
-${ commonfooter(messages) | n,unicode }

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

@@ -110,7 +110,7 @@ from django.utils.translation import ugettext as _
         $(this).closest("form").submit();
       });
       % if user.is_superuser:
-      $("#checkConfig").load("/debug/check_config_ajax");
+        $("#checkConfig").load("/debug/check_config_ajax");
       % endif
       $(".navbar .nav-tooltip").tooltip({
         delay:0,

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

@@ -17,7 +17,7 @@
 from django.utils.translation import ugettext as _
 %>
 % if error_list:
-  <a href="${url('desktop.views.check_config')}" title="${_('Misconfiguration detected')}" alt="${_('Misconfiguration detected')}">
+  <a href="${url('about.views.index')}" title="${_('Misconfiguration detected')}" alt="${_('Misconfiguration detected')}">
     <img src="/static/art/error.png"/>
   </a>
 % endif

+ 0 - 28
desktop/core/src/desktop/templates/index.mako

@@ -1,28 +0,0 @@
-## Licensed to Cloudera, Inc. under one
-## or more contributor license agreements.  See the NOTICE file
-## distributed with this work for additional information
-## regarding copyright ownership.  Cloudera, Inc. licenses this file
-## to you under the Apache License, Version 2.0 (the
-## "License"); you may not use this file except in compliance
-## with the License.  You may obtain a copy of the License at
-##
-##     http://www.apache.org/licenses/LICENSE-2.0
-##
-## Unless required by applicable law or agreed to in writing, software
-## distributed under the License is distributed on an "AS IS" BASIS,
-## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-## See the License for the specific language governing permissions and
-## limitations under the License.
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-	<meta http-equiv="refresh" content="0; url=/beeswax">
-</head>
-
-<body>
-</body>
-</html>
-

+ 5 - 5
desktop/core/src/desktop/tests.py

@@ -409,8 +409,8 @@ def test_config_check():
     assert_true('Encoding not supported' in resp.content, resp)
 
     # Alert present in the status bar
-    resp = cli.get('/status_bar/')
-    assert_true('Misconfiguration' in resp.content)
+    resp = cli.get('/about', follow=True)
+    assert_true('misconfiguration' in resp.content, resp.content)
   finally:
     for old_conf in reset:
       old_conf()
@@ -444,8 +444,8 @@ def test_ui_customizations():
 
   try:
     c = make_logged_in_client()
-    resp = c.get('/debug/check_config')
-    assert_true(custom_banner in resp.content)
+    resp = c.get('/about', follow=True)
+    assert_true(custom_banner in resp.content, resp)
   finally:
     for old_conf in reset:
-      old_conf()
+      old_conf()

+ 7 - 5
desktop/core/src/desktop/views.py

@@ -25,7 +25,9 @@ import zipfile
 
 from django.shortcuts import render_to_response
 from django.http import HttpResponse
+from django.core.urlresolvers import reverse
 from django.core.servers.basehttp import FileWrapper
+from django.shortcuts import redirect
 from django.utils.translation import ugettext as _
 import django.views.debug
 
@@ -216,12 +218,11 @@ def threads(request):
 def jasmine(request):
   return render('jasmine.mako', request, None)
 
-@login_notrequired
 def index(request):
-  return render("index.mako", request, dict(
-    feedback_url=desktop.conf.FEEDBACK_URL.get(),
-    send_dbug_messages=desktop.conf.SEND_DBUG_MESSAGES.get()
-  ))
+  if request.user.is_superuser:
+    return redirect(reverse('about:index'))
+  else:
+    return redirect(reverse('beeswax:index'))
 
 def serve_404_error(request, *args, **kwargs):
   """Registered handler for 404. We just return a simple error"""
@@ -352,6 +353,7 @@ def check_config(request):
   """Check config and view for the list of errors"""
   if not request.user.is_superuser:
     return HttpResponse(_("You must be a superuser."))
+
   conf_dir = os.path.realpath(get_desktop_root('conf'))
   return render('check_config.mako', request, dict(
                     error_list=_get_config_errors(cache=False),