Răsfoiți Sursa

HUE-1387 [core] Follow me tour plugin

The question mark is not shown if no tours are available
Preference checkbox added on the admin wizard
Enrico Berti 12 ani în urmă
părinte
comite
b625deb

+ 18 - 4
apps/about/src/about/templates/admin_wizard.mako

@@ -182,7 +182,20 @@ ${ commonheader(_('Quick Start'), "quickstart", user, "100px") | n,unicode }
         </div>
       </div>
 
-      <br/>
+      <div class="widget-box">
+        <div class="widget-title">
+          <span class="icon">
+            <i class="icon-th-list"></i>
+          </span>
+          <h5>${ _('Tours and tutorials') }</h5>
+        </div>
+        <div class="widget-content" style="padding-left: 14px">
+          <label class="checkbox">
+            <input class="updatePreferences" type="checkbox" name="tours_and_tutorials" style="margin-right: 10px" title="${ ('Check to enable the tours and tutorials') }" ${ tours_and_tutorials and "checked" }/>
+            ${ ('Display the "Available Tours" question mark when tours are available for a specific page.') }
+          </label>
+        </div>
+      </div>
 
       <div class="widget-box">
         <div class="widget-title">
@@ -193,7 +206,7 @@ ${ commonheader(_('Quick Start'), "quickstart", user, "100px") | n,unicode }
         </div>
         <div class="widget-content" style="padding-left: 14px">
           <label class="checkbox">
-            <input id="collectUsageBtn" type="checkbox" name="collect_usage" style="margin-right: 10px" title="${ ('Check to enable usage analytics') }" ${ collect_usage and "checked" }/>
+            <input class="updatePreferences" type="checkbox" name="collect_usage" style="margin-right: 10px" title="${ ('Check to enable usage analytics') }" ${ collect_usage and "checked" }/>
             ${ ('Help improve Hue with anonymous usage analytics.') }
             <a href="javascript:void(0)" style="display: inline" data-trigger="hover" data-toggle="popover" data-placement="right" rel="popover"
                title="${_('How does it work?') }"
@@ -331,8 +344,8 @@ $(document).ready(function(){
     }
   });
 
-  $("#collectUsageBtn").click(function () {
-    $.post("${ url('about:collect_usage') }", $("input").serialize(), function(data) {
+  $(".updatePreferences").click(function () {
+    $.post("${ url('about:update_preferences') }", $("input").serialize(), function(data) {
       if (data.status == 0) {
         $.jHueNotify.info('${ _("Configuration updated") }');
       } else {
@@ -340,6 +353,7 @@ $(document).ready(function(){
       }
     });
   });
+
 });
 </script>
 % endif

+ 4 - 2
apps/about/src/about/tests.py

@@ -59,18 +59,20 @@ class TestAboutWithNoCluster(TestAboutBase):
 
   def test_collect_usage(self):
     collect_usage = Settings.get_settings().collect_usage
+    tours_and_tutorials = Settings.get_settings().tours_and_tutorials
 
     try:
-      response = self.client.post(reverse('about:collect_usage'), {'collect_usage': False})
+      response = self.client.post(reverse('about:update_preferences'), {'collect_usage': False})
       data = json.loads(response.content)
       assert_equal(data['status'], 0)
       assert_false(data['collect_usage'] == True) # Weird but works
 
-      response = self.client.post(reverse('about:collect_usage'), {'collect_usage': True})
+      response = self.client.post(reverse('about:update_preferences'), {'collect_usage': True})
       data = json.loads(response.content)
       assert_equal(data['status'], 0)
       assert_true(data['collect_usage'])
     finally:
       settings = Settings.get_settings()
       settings.collect_usage = collect_usage
+      settings.tours_and_tutorials = tours_and_tutorials
       settings.save()

+ 1 - 1
apps/about/src/about/urls.py

@@ -21,5 +21,5 @@ urlpatterns = patterns('about.views',
   url(r'^$', 'admin_wizard', name='index'),
   url(r'^admin_wizard$', 'admin_wizard', name='admin_wizard'),
 
-  url(r'^collect_usage$', 'collect_usage', name='collect_usage'),
+  url(r'^update_preferences$', 'update_preferences', name='update_preferences'),
 )

+ 5 - 1
apps/about/src/about/views.py

@@ -37,6 +37,7 @@ def admin_wizard(request):
   app_names = [app.name for app in sorted(apps, key=lambda app: app.menu_index)]
 
   collect_usage = Settings.get_settings().collect_usage
+  tours_and_tutorials = Settings.get_settings().tours_and_tutorials
 
   return render('admin_wizard.mako', request, {
       'version': settings.HUE_DESKTOP_VERSION,
@@ -44,20 +45,23 @@ def admin_wizard(request):
       'apps': dict([(app.name, app) for app in apps]),
       'app_names': app_names,
       'collect_usage': collect_usage,
+      'tours_and_tutorials': tours_and_tutorials,
       'trash_enabled': get_trash_interval()
   })
 
 
-def collect_usage(request):
+def update_preferences(request):
   response = {'status': -1, 'data': ''}
 
   if request.method == 'POST':
     try:
       settings = Settings.get_settings()
       settings.collect_usage = request.POST.get('collect_usage', False)
+      settings.tours_and_tutorials = request.POST.get('tours_and_tutorials', False)
       settings.save()
       response['status'] = 0
       response['collect_usage'] = settings.collect_usage
+      response['tours_and_tutorials'] = settings.tours_and_tutorials
     except Exception, e:
       response['data'] = str(e)
   else:

+ 73 - 0
desktop/core/src/desktop/migrations/0006_settings_add_tour.py

@@ -0,0 +1,73 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Settings'
+        db.add_column('desktop_settings', 'tours_and_tutorials', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True, blank=True))
+        db.send_create_signal('desktop', ['Settings'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'Settings'
+        db.delete_column('desktop_settings', 'tours_and_tutorials')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'desktop.settings': {
+            'Meta': {'object_name': 'Settings'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'collect_usage': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'tours_and_tutorials': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'})
+        },
+        'desktop.userpreferences': {
+            'Meta': {'object_name': 'UserPreferences'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'value': ('django.db.models.fields.TextField', [], {'max_length': '4096'})
+        }
+    }
+
+    complete_apps = ['desktop']

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

@@ -28,6 +28,7 @@ class UserPreferences(models.Model):
 
 class Settings(models.Model):
   collect_usage = models.BooleanField(db_index=True, default=True)
+  tours_and_tutorials = models.BooleanField(db_index=True, default=True)
 
   @classmethod
   def get_settings(cls):

+ 4 - 2
desktop/core/src/desktop/templates/common_footer.html

@@ -90,7 +90,7 @@ limitations under the License.
             }
           }, 200);
         }
-
+        {% if tours_and_tutorials %}
         $.jHueTour({});
         if ($.totalStorage("jHueTourExtras") != null) {
           $.jHueTour({tours: $.totalStorage("jHueTourExtras")});
@@ -105,6 +105,7 @@ limitations under the License.
                   results = regex.exec(_qs);
           return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
         }
+        {% endif %}
       });
 
       function resetPrimaryButtonsStatus() {
@@ -163,7 +164,8 @@ limitations under the License.
       {% endif %}
 
     </script>
-
+    {% if tours_and_tutorials %}
     <script src="/static/js/Source/jHue/available.tours.js"></script>
+    {% endif %}
   </body>
 </html>

+ 2 - 1
desktop/core/src/desktop/views.py

@@ -329,7 +329,8 @@ def commonfooter(messages=None):
   return render_to_string("common_footer.html", {
     'messages': messages,
     'version': settings.HUE_DESKTOP_VERSION,
-    'collect_usage': hue_settings.collect_usage
+    'collect_usage': hue_settings.collect_usage,
+    'tours_and_tutorials': hue_settings.tours_and_tutorials
   })
 
 

+ 31 - 24
desktop/core/static/js/Source/jHue/jquery.tour.js

@@ -68,7 +68,8 @@
       },
       tours: [],
       showRemote: false,
-      placement: "right"
+      questionMarkPlacement: "right",
+      hideIfNoneAvailable: true
     };
 
   function Plugin(element, options) {
@@ -98,10 +99,6 @@
     var _this = this;
     _this.initQuestionMark();
     var _tourMask = $("<div>").attr("id", "jHueTourMask");
-    _tourMask.addClass("jHueTourBadge");
-    if (_this.options.placement == "left"){
-      _tourMask.addClass("jHueTourBadgeLeft");
-    }
     _tourMask.width($(document).width()).height($(document).height())
     _tourMask.click(function () {
       _this.closeCurtains();
@@ -120,24 +117,20 @@
     var _this = this;
     $("#jHueTourQuestion").remove();
     var _questionMark = $("<div>").attr("id", "jHueTourQuestion").html('<i class="icon-question"></i>').addClass("jHueTourBadge");
-    _questionMark.click(function () {
-      if ($.totalStorage("jHueTourExtras") != null) {
-        var _newTours = [];
-        $.each(_this.options.tours, function (cnt, tour) {
-          if (tour.remote == undefined || !tour.remote) {
-            _newTours.push(tour);
-          }
-        });
-        _this.options.tours = _newTours.concat($.totalStorage("jHueTourExtras"));
-      }
-
-      var _closeBtn = $("<a>");
-      _closeBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-remove"></i>').css("float", "right").css("margin-top", "-4px").css("margin-right", "-6px");
-      _closeBtn.click(function () {
-        $(".popover").remove();
+    if (_this.options.questionMarkPlacement == "left"){
+      _questionMark.addClass("jHueTourBadgeLeft");
+    }
+    if ($.totalStorage("jHueTourExtras") != null) {
+      var _newTours = [];
+      $.each(_this.options.tours, function (cnt, tour) {
+        if (tour.remote == undefined || !tour.remote) {
+          _newTours.push(tour);
+        }
       });
+      _this.options.tours = _newTours.concat($.totalStorage("jHueTourExtras"));
+    }
 
-      var _toursHtml = '<ul class="nav nav-pills nav-stacked">'
+    var _toursHtml = '<ul class="nav nav-pills nav-stacked">'
       var _added = 0;
       $.each(_this.options.tours, function (ctn, tour) {
         if (tour.path === undefined || RegExp(tour.path).test(location.pathname)) {
@@ -161,7 +154,12 @@
         }
       });
       if (_added == 0) {
-        _toursHtml += '<li>' + _this.options.labels.NO_AVAILABLE_TOURS + '</li>';
+        if (_this.options.hideIfNoneAvailable){
+          _questionMark.css("display", "none");
+        }
+        else {
+          _toursHtml += '<li>' + _this.options.labels.NO_AVAILABLE_TOURS + '</li>';
+        }
       }
       if (_this.options.showRemote){
         _toursHtml += '<li>' +
@@ -172,19 +170,28 @@
           ' </div>' +
           '</li>';
       }
-      _toursHtml += '</ul>';
+    _toursHtml += '</ul>';
+
+    _questionMark.click(function () {
+
+      var _closeBtn = $("<a>");
+      _closeBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-remove"></i>').css("float", "right").css("margin-top", "-4px").css("margin-right", "-6px");
+      _closeBtn.click(function () {
+        $(".popover").remove();
+      });
 
       _questionMark.popover("destroy").popover({
         title: _this.options.labels.AVAILABLE_TOURS,
         content: _toursHtml,
         html: true,
         trigger: "manual",
-        placement: "left"
+        placement: _this.options.questionMarkPlacement == "left"?"right":"left"
       }).popover("show");
       if ($(".popover").position().top <= 0) {
         $(".popover").css("top", "10px");
       }
       _closeBtn.prependTo($(".popover-title"));
+
     });
     _questionMark.appendTo($("body"));
   };