]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1927 Dashboards are global instead of detached
authorDavid Gageot <david@gageot.net>
Fri, 4 May 2012 15:00:27 +0000 (17:00 +0200)
committerDavid Gageot <david@gageot.net>
Fri, 4 May 2012 16:13:09 +0000 (18:13 +0200)
21 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/GlobalWidget.java
plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties
sonar-core/src/main/java/org/sonar/core/dashboard/DashboardDto.java
sonar-core/src/main/resources/org/sonar/core/dashboard/DashboardMapper-oracle.xml
sonar-core/src/main/resources/org/sonar/core/dashboard/DashboardMapper.xml
sonar-core/src/main/resources/org/sonar/core/persistence/schema-derby.ddl
sonar-core/src/test/java/org/sonar/core/dashboard/DashboardDaoTest.java
sonar-core/src/test/resources/org/sonar/core/dashboard/DashboardDaoTest/shouldInsert-result.xml
sonar-core/src/test/resources/org/sonar/core/dashboard/DashboardDaoTest/shouldInsertWithNullableColumns-result.xml
sonar-core/src/test/resources/org/sonar/core/dashboard/DashboardDaoTest/shouldSelectGlobalDashboard.xml
sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetCategory.java
sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetGlobal.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboard_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboards_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb
sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/_create.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/dashboards/index.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
sonar-server/src/main/webapp/WEB-INF/db/migrate/300_add_global_to_dashboards.rb
sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java

index 54ae6c6ad06bb2057de87964f8a06fc8c55d9b89..c806bee357bae47628f173ed31b74d70d361d332 100644 (file)
  */
 package org.sonar.plugins.core.widgets;
 
+import org.sonar.api.web.WidgetGlobal;
+
 import org.sonar.api.web.AbstractRubyTemplate;
 import org.sonar.api.web.RubyRailsWidget;
 import org.sonar.api.web.UserRole;
 import org.sonar.api.web.WidgetCategory;
 
-@WidgetCategory(value = "Information", detached = true)
+@WidgetCategory("Information")
+@WidgetGlobal
 @UserRole(UserRole.USER)
 public class GlobalWidget extends AbstractRubyTemplate implements RubyRailsWidget {
 
index 24e5c535281a42d4af70675791dfa32b7c5a9640..d7472712dd0ba6f2600e26d793e3163b6825e43e 100644 (file)
@@ -55,6 +55,7 @@ file=File
 files=Files
 filter_verb=Filter
 follow=Follow
+global=Global
 hide=Hide
 identifier_abbreviated=Id
 info=Info
index 4b5c2136fcbb52ef114b098fbcc242b477b8d9f6..5365786f7ff771fffb13592c0ec945b5f880b0d2 100644 (file)
@@ -33,7 +33,7 @@ public final class DashboardDto {
   private String description;
   private String columnLayout;
   private boolean shared;
-  private boolean detached;
+  private boolean global;
   private Date createdAt;
   private Date updatedAt;
   private List<WidgetDto> widgetDtos = Lists.newArrayList();
@@ -92,12 +92,12 @@ public final class DashboardDto {
     return this;
   }
 
-  public boolean getDetached() {
-    return detached;
+  public boolean getGlobal() {
+    return global;
   }
 
-  public DashboardDto setDetached(boolean detached) {
-    this.detached = detached;
+  public DashboardDto setGlobal(boolean global) {
+    this.global = global;
     return this;
   }
 
index 234105361aa7d14faa93d7b14fc8222702e28b34..c31679db2325c0b9d02e763f02ea4cade275a2f5 100644 (file)
@@ -4,7 +4,7 @@
 <mapper namespace="org.sonar.core.dashboard.DashboardMapper">
 
   <select id="selectGlobalDashboard" parameterType="string" resultType="Dashboard">
-    select id, user_id as "userId", name, description, column_layout as "columnLayout", shared, detached, created_at as "createdAt", updated_at as "updatedAt"
+    select id, user_id as "userId", name, description, column_layout as "columnLayout", shared, is_global, created_at as "createdAt", updated_at as "updatedAt"
     from dashboards WHERE name=#{id} and user_id is null
   </select>
 
@@ -12,9 +12,9 @@
     <selectKey order="BEFORE" resultType="Long" keyProperty="id">
       select dashboards_seq.NEXTVAL from DUAL
     </selectKey>
-    INSERT INTO dashboards (id, user_id, name, description, column_layout, shared, detached, created_at, updated_at)
+    INSERT INTO dashboards (id, user_id, name, description, column_layout, shared, is_global, created_at, updated_at)
     VALUES (#{id}, #{userId, jdbcType=FLOAT}, #{name, jdbcType=VARCHAR}, #{description, jdbcType=VARCHAR},
-    #{columnLayout, jdbcType=INTEGER}, #{shared}, #{detached}, #{createdAt, jdbcType=TIMESTAMP}, #{updatedAt, jdbcType=TIMESTAMP})
+    #{columnLayout, jdbcType=INTEGER}, #{shared}, #{global}, #{createdAt, jdbcType=TIMESTAMP}, #{updatedAt, jdbcType=TIMESTAMP})
   </insert>
 
 </mapper>
index 26f977c3275d2097905267cd51cf0450d4cd4d80..00e32a3785f5bdc4d1ed22290520000902492420 100644 (file)
@@ -4,14 +4,14 @@
 <mapper namespace="org.sonar.core.dashboard.DashboardMapper">
 
   <select id="selectGlobalDashboard" parameterType="string" resultType="Dashboard">
-    select id, user_id as "userId", name, description, column_layout as "columnLayout", shared, detached, created_at as "createdAt", updated_at as "updatedAt"
+    select id, user_id as "userId", name, description, column_layout as "columnLayout", shared, is_global, created_at as "createdAt", updated_at as "updatedAt"
     from dashboards WHERE name=#{id} and user_id is null
   </select>
 
   <insert id="insert" parameterType="Dashboard" useGeneratedKeys="true" keyProperty="id">
-    INSERT INTO dashboards (user_id, name, description, column_layout, shared, detached, created_at, updated_at)
+    INSERT INTO dashboards (user_id, name, description, column_layout, shared, is_global, created_at, updated_at)
     VALUES (#{userId, jdbcType=FLOAT}, #{name, jdbcType=VARCHAR}, #{description, jdbcType=VARCHAR},
-    #{columnLayout, jdbcType=INTEGER}, #{shared}, #{detached}, #{createdAt, jdbcType=TIMESTAMP}, #{updatedAt, jdbcType=TIMESTAMP})
+    #{columnLayout, jdbcType=INTEGER}, #{shared}, #{global}, #{createdAt, jdbcType=TIMESTAMP}, #{updatedAt, jdbcType=TIMESTAMP})
   </insert>
 
 </mapper>
index 527c103da98e59096b48ae91bcce59e56249bbaa..01c32cb84a8e157f46e21a2feddeac0f3964d1e7 100644 (file)
@@ -413,7 +413,7 @@ CREATE TABLE "DASHBOARDS" (
   "DESCRIPTION" VARCHAR(1000),
   "COLUMN_LAYOUT" VARCHAR(20),
   "SHARED" BOOLEAN,
-  "DETACHED" BOOLEAN,
+  "IS_GLOBAL" BOOLEAN,
   "CREATED_AT" TIMESTAMP,
   "UPDATED_AT" TIMESTAMP
 );
index 1988e73bf50ae8803ed39cd6f9c186408b8a71fb..36b464f67786dfb56ecd07bf7a8fc3fbd47b3809 100644 (file)
@@ -60,6 +60,7 @@ public class DashboardDaoTest extends DaoTestCase {
     dashboardDto.setDescription("This is a dashboard");
     dashboardDto.setColumnLayout("100%");
     dashboardDto.setShared(true);
+    dashboardDto.setGlobal(true);
     dashboardDto.setCreatedAt(aDate);
     dashboardDto.setUpdatedAt(aDate);
 
@@ -94,6 +95,7 @@ public class DashboardDaoTest extends DaoTestCase {
     dashboardDto.setDescription(null);
     dashboardDto.setColumnLayout(null);
     dashboardDto.setShared(true);
+    dashboardDto.setGlobal(false);
     dashboardDto.setCreatedAt(null);
     dashboardDto.setUpdatedAt(null);
 
index 9654b21fad73dbbaad89019b7ff6770aea9e5a7f..68bd31b0ba29ed9ee6b50eb6e1b1bd9907dee70f 100644 (file)
@@ -7,7 +7,7 @@
     description="This is a dashboard"
     column_layout="100%"
     shared="[true]"
-    detached="[false]"
+    is_global="[true]"
   />
 
   <widgets
index d6a70cc2a25f697546f573cfc61c57003b751779..eb27da61a04cc099f646f8e9c3e25d327fa32e9b 100644 (file)
@@ -7,7 +7,7 @@
     description="[null]"
     column_layout="[null]"
     shared="[true]"
-    detached="[false]"
+    is_global="[false]"
     created_at="[null]"
     updated_at="[null]"
   />
index ac1a6f041a62ba562d7387ed6b0c3467fdf571fd..110e760f9ad647612d52379da7921c392e9b3e95 100644 (file)
@@ -7,7 +7,6 @@
     description="User SQALE dashboard"
     column_layout="100%"
     shared="[true]"
-    detached="[false]"
     />
 
   <dashboards
@@ -17,7 +16,6 @@
     description="Global SQALE dashboard"
     column_layout="100%"
     shared="[true]"
-    detached="[false]"
     />
 
 </dataset>
index 86c631aeedb17b350acdeb0a48f3e026fe89a850..409c9bca7ce02be91889c4d50ea555814c2d005e 100644 (file)
@@ -31,12 +31,4 @@ import java.lang.annotation.Target;
 @Target(ElementType.TYPE)
 public @interface WidgetCategory {
   String[] value();
-
-  /**
-   * Is the widget detached from any project?
-   * Before version 3.1 no widget was detached.
-   *
-   * @since 3.1
-   */
-  boolean detached() default false;
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetGlobal.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetGlobal.java
new file mode 100644 (file)
index 0000000..5fd2dbe
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.web;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A widget is global when it can only ne displayed on Global Dashboards.
+ * It doesn't display information from a projet but rather more general information.
+ * <p>Before version 3.1 no widget was global.</p>
+ *
+ * @since 3.1
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface WidgetGlobal {
+}
index 547e61e31dbace9b215e8df0ce945d0f006cad5f..85a74b4591226300b58afb3acb945bc63cb61462 100644 (file)
@@ -47,7 +47,7 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
   private WidgetLayoutType widgetLayout = WidgetLayoutType.DEFAULT;
   private boolean isDefaultTab = false;
   private boolean isWidget = false;
-  private boolean isDetached = false;
+  private boolean isGlobal = false;
   private String[] mandatoryMeasures = {};
   private String[] needOneOfMeasures = {};
 
@@ -64,7 +64,7 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
     initWidgetProperties(view);
     initWidgetCategory(view);
     initWidgetLayout(view);
-    initWidgetDetached(view);
+    initWidgetGlobal(view);
     initRequiredMeasures(view);
 
     isWidget = (view instanceof Widget);
@@ -92,11 +92,8 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
     }
   }
 
-  private void initWidgetDetached(final V view) {
-    WidgetCategory categAnnotation = AnnotationUtils.getClassAnnotation(view, WidgetCategory.class);
-    if (categAnnotation != null) {
-      isDetached = categAnnotation.detached();
-    }
+  private void initWidgetGlobal(final V view) {
+    isGlobal = null != AnnotationUtils.getClassAnnotation(view, WidgetGlobal.class);
   }
 
   private void initWidgetProperties(final V view) {
@@ -251,8 +248,8 @@ public class ViewProxy<V extends View> implements Comparable<ViewProxy> {
     return isWidget;
   }
 
-  public boolean isDetached() {
-    return isDetached;
+  public boolean isGlobal() {
+    return isGlobal;
   }
 
   public boolean isGwt() {
index b9014a082d00915aa19af739f146cd107d52deaf..370564bd344f0324eb72e9927d11532ebfdfde28 100644 (file)
@@ -191,10 +191,7 @@ class DashboardController < ApplicationController
   end
 
   def load_widget_definitions(dashboard, filter_on_category=nil)
-    @widget_definitions=java_facade.getWidgets()
-    if dashboard.detached
-      @widget_definitions=@widget_definitions.select(&:isDetached)
-    end
+    @widget_definitions=java_facade.getWidgets().select { |w| w.isGlobal() == dashboard.global}
 
     @widget_categories=@widget_definitions.map(&:getWidgetCategories).flatten.uniq.sort
     unless filter_on_category.blank?
index e7a004396db3da49bc04385bfed0aec527fb81be..ca2d9141c9b8fee3ee39767b3bd82fa43fae656c 100644 (file)
@@ -171,6 +171,7 @@ class DashboardsController < ApplicationController
   def load_dashboard_from_params(dashboard)
     dashboard.name=params[:name]
     dashboard.description=params[:description]
+    dashboard.global=(params[:global].present?)
     dashboard.shared=(params[:shared].present? && is_admin?)
     dashboard.user_id=current_user.id
     dashboard.column_layout=Dashboard::DEFAULT_LAYOUT if !dashboard.column_layout
@@ -186,4 +187,4 @@ class DashboardsController < ApplicationController
   end
 
 
-end
\ No newline at end of file
+end
index b16a16e0412aa1c972e19a2762a9070b042f263f..2fde1aa85440e698f7db6382e045495c710ca381 100644 (file)
@@ -34,83 +34,87 @@ class Dashboard < ActiveRecord::Base
   before_destroy :check_not_default_before_destroy
 
   def name(l10n=false)
-    n=read_attribute(:name)
-    if l10n
-      Api::Utils.message("dashboard.#{n}.name", :default => n)
-    else
-      n
-    end
+       n=read_attribute(:name)
+       if l10n
+         Api::Utils.message("dashboard.#{n}.name", :default => n)
+       else
+         n
+       end
   end
 
   def shared?
-    read_attribute(:shared) || false
+       read_attribute(:shared) || false
+  end
+
+  def global?
+    read_attribute(:is_global) || false
   end
 
   def layout
-    column_layout
+       column_layout
   end
 
   def user_name
-    user_id ? user.name : nil
+       user_id ? user.name : nil
   end
 
   def editable_by?(user)
-    (user && self.user_id==user.id) || (user_id.nil? && user.has_role?(:admin))
+       (user && self.user_id==user.id) || (user_id.nil? && user.has_role?(:admin))
   end
 
   def owner?(user)
-    self.user_id==user.id
+       self.user_id==user.id
   end
 
   def number_of_columns
-    column_layout.split('-').size
+       column_layout.split('-').size
   end
 
   def column_size(column_index)
-    last_widget=widgets.select { |w| w.column_index==column_index }.max { |x, y| x.row_index <=> y.row_index }
-    last_widget ? last_widget.row_index : 0
+       last_widget=widgets.select { |w| w.column_index==column_index }.max { |x, y| x.row_index <=> y.row_index }
+       last_widget ? last_widget.row_index : 0
   end
 
   def deep_copy()
-    dashboard=Dashboard.new(attributes)
-    dashboard.shared=false
-    self.widgets.each do |child|
-      new_widget = Widget.create(child.attributes)
-
-      child.properties.each do |prop|
-        widget_prop = WidgetProperty.create(prop.attributes)
-        new_widget.properties << widget_prop
-      end
-
-      new_widget.save
-      dashboard.widgets << new_widget
-    end
-    dashboard.save
-    dashboard
+       dashboard=Dashboard.new(attributes)
+       dashboard.shared=false
+       self.widgets.each do |child|
+         new_widget = Widget.create(child.attributes)
+
+         child.properties.each do |prop|
+               widget_prop = WidgetProperty.create(prop.attributes)
+               new_widget.properties << widget_prop
+         end
+
+         new_widget.save
+         dashboard.widgets << new_widget
+       end
+       dashboard.save
+       dashboard
   end
 
   def provided_programmatically?
-    shared && user_id.nil?
+       shared && user_id.nil?
   end
 
   protected
   def check_not_default_before_destroy
-    if shared?
-      default_actives = active_dashboards.select { |ad| ad.default? }
-      return default_actives.size==0
-    end
-    true
+       if shared?
+         default_actives = active_dashboards.select { |ad| ad.default? }
+         return default_actives.size==0
+       end
+       true
   end
 
   def validate_on_update
-    # Check that not used as default dashboard when unsharing
-    if shared_was && !shared
-      # unsharing
-      default_actives = active_dashboards.select { |ad| ad.default? }
-
-      unless default_actives.empty?
-        errors.add_to_base(Api::Utils.message('dashboard.error_unshare_default'))
-      end
-    end
+       # Check that not used as default dashboard when unsharing
+       if shared_was && !shared
+         # unsharing
+         default_actives = active_dashboards.select { |ad| ad.default? }
+
+         unless default_actives.empty?
+               errors.add_to_base(Api::Utils.message('dashboard.error_unshare_default'))
+         end
+       end
   end
-end
\ No newline at end of file
+end
index 1b9833d93ac8ffb531c545af16e15a563d288bc9..57e76dfc193739eff1323a4908196f0832c4eb82 100644 (file)
         <%= message('description') -%>:<br/><input type="text" name="description" size="30" maxlength="1000">
       </td>
     </tr>
+    <tr>
+      <td class="left" valign="top">
+        <%= message('global') -%>:<br/><input type="checkbox" name="global" value="true">
+      </td>
+    </tr>
     <% if is_admin? %>
       <tr>
         <td class="left" valign="top">
@@ -31,4 +36,4 @@
 </table>
 <script>
   $('create-dashboard-form').name.focus();
-</script>
\ No newline at end of file
+</script>
index 1c4a69329db68bb4d7af12ccf3fe85c401124740..e876fad2cc1b91f49bb08b01567775ee97e016b8 100644 (file)
@@ -8,6 +8,7 @@
         <thead>
         <tr>
           <th><%= message('name') -%></th>
+          <th><%= message('global') -%></th>
           <% if is_admin %>
             <th><%= message('shared') -%></th>
           <% end %>
                   <p class="small"><%= h active.dashboard.description -%></p>
                 <% end %>
               </td>
+              <td>
+                <%= boolean_icon(active.dashboard.global?) -%>
+              </td>
               <% if is_admin %>
                 <td>
-                  <%= boolean_icon(active.dashboard.shared, {:display_false => false}) -%>
+                  <%= boolean_icon(active.dashboard.shared?) -%>
                 </td>
               <% end %>
               <td>
index aeb2e3fa7aa188094b05405d23d66f94f055f8b1..d9be65e9e0b14c8ed66115696725dee814d1e757 100644 (file)
@@ -42,7 +42,7 @@
             <li class="<%= 'selected' if controller.controller_path=='dependencies' -%>">
               <a href="<%= ApplicationController.root_context -%>/dependencies/index"><%= message('dependencies.page') -%></a></li>
 
-            <% ActiveDashboard.user_dashboards(current_user).select { |active_dashboard| active_dashboard.dashboard.detached }.each do |active_dashboard| %>
+            <% ActiveDashboard.user_dashboards(current_user).select { |active_dashboard| active_dashboard.dashboard.global? }.each do |active_dashboard| %>
               <li class="<%= 'selected' if @dashboard && controller.controller_path=='dashboard' && active_dashboard.dashboard_id==@dashboard.id -%>">
                 <a href="<%= ApplicationController.root_context -%>/dashboard/index/?did=<%= active_dashboard.dashboard_id -%>"><%= active_dashboard.dashboard.name(true) -%></a>
               </li>
@@ -57,7 +57,7 @@
             <% end %>
 
           <% elsif selected_section==Navigation::SECTION_RESOURCE %>
-            <% ActiveDashboard.user_dashboards(current_user).select { |active_dashboard| !active_dashboard.dashboard.detached }.each do |active_dashboard| %>
+            <% ActiveDashboard.user_dashboards(current_user).select { |active_dashboard| !active_dashboard.dashboard.global? }.each do |active_dashboard| %>
               <li class="<%= 'selected' if @dashboard && controller.controller_path=='dashboard' && active_dashboard.dashboard_id==@dashboard.id -%>">
                 <a href="<%= ApplicationController.root_context -%>/dashboard/index/<%= @project.id -%>?did=<%= active_dashboard.dashboard_id -%><%= "&"+period_param if period_param -%>"><%= active_dashboard.dashboard.name(true) -%></a>
               </li>
index 9cb0c87b338d31f3b707613ea8cb61b0b02f3560..fd729d731898b051769ced9b70236b7430f5533e 100644 (file)
@@ -24,7 +24,7 @@
 class AddGlobalToDashboards < ActiveRecord::Migration
 
   def self.up
-    add_column 'dashboards', 'detached', :boolean, :null => false, :default => false
+    add_column 'dashboards', 'global', :boolean, :null => false, :default => false
   end
 
 end
index 461b9afd0d884008ea0e9c8d2579a2b0e4937fcb..64771b1a8345631014f3b3eeeb5c8eb6cf1f1397 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.server.ui;
 
+import org.sonar.api.web.WidgetGlobal;
+
 import org.junit.Test;
 import org.sonar.api.web.DefaultTab;
 import org.sonar.api.web.NavigationSection;
@@ -26,7 +28,6 @@ import org.sonar.api.web.RequiredMeasures;
 import org.sonar.api.web.UserRole;
 import org.sonar.api.web.View;
 import org.sonar.api.web.Widget;
-import org.sonar.api.web.WidgetCategory;
 import org.sonar.api.web.WidgetProperties;
 import org.sonar.api.web.WidgetProperty;
 import org.sonar.api.web.WidgetPropertyType;
@@ -133,17 +134,17 @@ public class ViewProxyTest {
   }
 
   @Test
-  public void widget_should_not_be_detached() {
+  public void widget_should_not_be_global_by_default() {
     ViewProxy proxy = new ViewProxy<Widget>(new EditableWidget());
 
-    assertThat(proxy.isDetached()).isFalse();
+    assertThat(proxy.isGlobal()).isFalse();
   }
 
   @Test
-  public void widget_should_be_detached() {
-    ViewProxy proxy = new ViewProxy<Widget>(new DetachedWidget());
+  public void widget_should_be_global() {
+    ViewProxy proxy = new ViewProxy<Widget>(new GlobalWidget());
 
-    assertThat(proxy.isDetached()).isTrue();
+    assertThat(proxy.isGlobal()).isTrue();
   }
 
   @Test
@@ -250,14 +251,14 @@ class EditableWidget implements Widget {
   }
 }
 
-@WidgetCategory(value = "", detached = true)
-class DetachedWidget implements Widget {
+@WidgetGlobal
+class GlobalWidget implements Widget {
   public String getId() {
-    return "detached";
+    return "global";
   }
 
   public String getTitle() {
-    return "Detached";
+    return "Global";
   }
 }