Bug 512289 - Add sign in and sign out in help menu
Adds a new menu and toolbar for managing USS-related activities,
under Help > Eclipse User Storage. Menu has ID
org.eclipse.userstorage.accounts and provides slots for
`actions` and `additions`.
Change-Id: I43c6f15df1b8165c6a2e388c635e490e2a9f15e6
diff --git a/org.eclipse.userstorage.oauth/about.html b/org.eclipse.userstorage.oauth/about.html
index d35d5ae..c0e66f7 100644
--- a/org.eclipse.userstorage.oauth/about.html
+++ b/org.eclipse.userstorage.oauth/about.html
@@ -24,5 +24,21 @@
indicated below, the terms and conditions of the EPL still apply to any source code in the Content
and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+<h3>Third Party Content</h3>
+
+<p>The Content includes items that have been sourced from third parties
+as set out below. If you did not receive this Content directly from
+the Eclipse Foundation, the following is provided for informational
+purposes only, and you should look to the Redistributor's license
+for terms and conditions of use.</p>
+
+
+<h4>FontAwesome 4.5.0</h4>
+
+<p>This plugin includes images created from rasterized glyphs from
+<a href="https://fortawesome.github.io/Font-Awesome/">Font Awesome
+4.5.0</a> at different resolutions. FontAwesome is available under
+the <a href="http://scripts.sil.org/OFL">SIL Open Font License</a>.</p>
+
</body>
</html>
diff --git a/org.eclipse.userstorage.oauth/icons/UserAccount.png b/org.eclipse.userstorage.oauth/icons/UserAccount.png
new file mode 100644
index 0000000..2bc2505
--- /dev/null
+++ b/org.eclipse.userstorage.oauth/icons/UserAccount.png
Binary files differ
diff --git a/org.eclipse.userstorage.oauth/icons/UserAccount@2x.png b/org.eclipse.userstorage.oauth/icons/UserAccount@2x.png
new file mode 100644
index 0000000..7770d21
--- /dev/null
+++ b/org.eclipse.userstorage.oauth/icons/UserAccount@2x.png
Binary files differ
diff --git a/org.eclipse.userstorage.oauth/plugin.xml b/org.eclipse.userstorage.oauth/plugin.xml
index 14d440a..b85897a 100644
--- a/org.eclipse.userstorage.oauth/plugin.xml
+++ b/org.eclipse.userstorage.oauth/plugin.xml
@@ -11,5 +11,60 @@
name="Linked Accounts">
</page>
</extension>
-
+
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="menu:help?after=additions">
+ <menu
+ id="org.eclipse.userstorage.accounts"
+ label="Eclipse User Storage">
+ <command
+ commandId="org.eclipse.ui.browser.openBrowser"
+ label="Open My Account"
+ style="push"
+ tooltip="Open the Eclipse Account page">
+ <parameter
+ name="url"
+ value="https://accounts.eclipse.org">
+ </parameter>
+ </command>
+ <separator
+ name="actions">
+ </separator>
+ <separator
+ name="accounts"
+ visible="true">
+ </separator>
+ <dynamic
+ class="org.eclipse.userstorage.internal.oauth.ui.AccountDetails"
+ id="org.eclipse.userstorage.ui.oauth.signout">
+ </dynamic>
+ <separator
+ name="additions"
+ visible="true">
+ </separator>
+ </menu>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="toolbar:org.eclipse.ui.main.toolbar?after=additions">
+ <toolbar
+ id="org.eclipse.userstorage.accounts"
+ label="Eclipse User Storage">
+ <command
+ commandId="org.eclipse.userstorage.ui.showDropDown"
+ id="org.eclipse.userstorage.accounts"
+ icon="icons/UserAccount.png"
+ tooltip="Eclipse User Storage"
+ style="pulldown">
+ <parameter
+ name="intoolbar"
+ value="true">
+ </parameter>
+ </command>
+ </toolbar>
+ </menuContribution>
+ </extension>
</plugin>
diff --git a/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/internal/oauth/ui/AccountDetails.java b/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/internal/oauth/ui/AccountDetails.java
new file mode 100644
index 0000000..0f5c59b
--- /dev/null
+++ b/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/internal/oauth/ui/AccountDetails.java
@@ -0,0 +1,142 @@
+package org.eclipse.userstorage.internal.oauth.ui;
+
+import org.eclipse.userstorage.internal.oauth.OAuthCredentialsPersistence;
+import org.eclipse.userstorage.internal.oauth.OAuthCredentialsPersistence.LinkedAccount;
+
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.jface.action.ContributionItem;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.menus.IWorkbenchContribution;
+import org.eclipse.ui.services.IServiceLocator;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Display a dynamic menu area with a USS sign-out item
+ * and a list of items showing currently-logged in apps.
+ */
+public class AccountDetails extends ContributionItem implements IWorkbenchContribution
+{
+ private OAuthCredentialsPersistence persister;
+
+ private Collection<OAuthApplicationData> appData;
+
+ private IServiceLocator serviceLocator;
+
+ @Override
+ public void initialize(IServiceLocator serviceLocator)
+ {
+ this.serviceLocator = serviceLocator;
+ IExtensionRegistry registry = serviceLocator.getService(IExtensionRegistry.class);
+ persister = OAuthCredentialsPersistence.standard();
+ appData = OAuthApplicationData.load(registry);
+ }
+
+ @Override
+ public void fill(final Menu menu, int index)
+ {
+ List<LinkedAccount> eclipseAccounts = getEclipseAccounts();
+ if (!eclipseAccounts.isEmpty())
+ {
+ MenuItem item = new MenuItem(menu, SWT.PUSH, index++);
+ item.setText("Authorized Applications");
+ item.addSelectionListener(new SelectionListener()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(menu.getShell(), OAuthAccountsPreferencePage.PAGE_ID, null, null);
+ dialog.open();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ widgetSelected(e);
+ }
+ });
+
+ for (LinkedAccount account : eclipseAccounts)
+ {
+ MenuItem accountItem = new MenuItem(menu, SWT.PUSH, index++);
+ accountItem.setText(" " + getApplicationName(account));
+ accountItem.setToolTipText(account.email);
+ accountItem.setEnabled(false);
+ }
+ }
+
+ MenuItem item = new MenuItem(menu, SWT.PUSH, index++);
+ item.setText("Sign Out");
+ item.setToolTipText("Discard all USS logins");
+ if (eclipseAccounts.isEmpty())
+ {
+ item.setEnabled(false);
+ new MenuItem(menu, SWT.SEPARATOR, ++index);
+ return;
+ }
+ item.addSelectionListener(new SelectionListener()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ signOut();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ signOut();
+ }
+ });
+ }
+
+ @Override
+ public boolean isDynamic()
+ {
+ return true;
+ }
+
+ private void signOut()
+ {
+ persister.removeLinkedAccounts(getEclipseAccounts());
+ }
+
+ private List<LinkedAccount> getEclipseAccounts()
+ {
+ List<LinkedAccount> eclipseAccounts = new ArrayList<LinkedAccount>();
+ for (LinkedAccount account : persister.getLinkedAccounts())
+ {
+ if ("https://accounts.eclipse.org/".equals(account.authURI))
+ {
+ eclipseAccounts.add(account);
+ }
+ }
+ return eclipseAccounts;
+ }
+
+ private OAuthApplicationData lookup(LinkedAccount account)
+ {
+ for (OAuthApplicationData app : appData)
+ {
+ if (account.clientId.equals(app.getClientId()) && account.authURI.equals(app.getAuthURI()))
+ {
+ return app;
+ }
+ }
+ return null;
+ }
+
+ private String getApplicationName(LinkedAccount account)
+ {
+ OAuthApplicationData app = lookup(account);
+ return app != null ? app.getApplicationName() : account.authURI + " - " + account.clientId;
+ }
+}
diff --git a/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/internal/oauth/ui/OAuthAccountsPreferencePage.java b/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/internal/oauth/ui/OAuthAccountsPreferencePage.java
index 9316e91..5179b31 100644
--- a/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/internal/oauth/ui/OAuthAccountsPreferencePage.java
+++ b/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/internal/oauth/ui/OAuthAccountsPreferencePage.java
@@ -41,6 +41,8 @@
public class OAuthAccountsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage
{
+ static final String PAGE_ID = "org.eclipse.userstorage.ui.oauth.credentials";
+
private IWorkbench workbench;
private Collection<OAuthApplicationData> appData;
diff --git a/org.eclipse.userstorage.ui/plugin.xml b/org.eclipse.userstorage.ui/plugin.xml
index bbb7894..fd23151 100644
--- a/org.eclipse.userstorage.ui/plugin.xml
+++ b/org.eclipse.userstorage.ui/plugin.xml
@@ -23,5 +23,20 @@
id="org.eclipse.userstorage.ui.ServicesPreferencePage"
name="User Storage Service"/>
</extension>
+
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ id="org.eclipse.userstorage.ui.showPullDown"
+ categoryId="org.eclipse.ui.category.help"
+ name="Show Pull Down Menu"
+ defaultHandler="org.eclipse.userstorage.ui.internal.ShowPullDownMenu">
+ <commandParameter
+ id="intoolbar"
+ name="In Tool Bar"
+ optional="false">
+ </commandParameter>
+ </command>
+ </extension>
</plugin>
diff --git a/org.eclipse.userstorage.ui/src/org/eclipse/userstorage/ui/internal/ShowPullDownMenu.java b/org.eclipse.userstorage.ui/src/org/eclipse/userstorage/ui/internal/ShowPullDownMenu.java
new file mode 100644
index 0000000..b2033ab
--- /dev/null
+++ b/org.eclipse.userstorage.ui/src/org/eclipse/userstorage/ui/internal/ShowPullDownMenu.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2017 Manumitting Technologies Inc and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Manumitting Technologies Inc - initial API and implementation
+ */
+package org.eclipse.userstorage.ui.internal;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.action.ContributionItem;
+import org.eclipse.jface.action.IMenuListener2;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.ui.menus.IMenuService;
+import org.eclipse.ui.services.IServiceLocator;
+
+/**
+ * A simple handler that opens a pulldown menu associated with a tool item.
+ * Required as Eclipse only opens the drop-down menu when clicking on a tool item's
+ * drop-down indicator.
+ * <ul>
+ * <li> As is the norm for a {@code <command style="pulldown">} items, must
+ * set the {@code id} attribute to the id of a menu contribution to show.</li>
+ * <li> Must provide an {@code intoolbar} parameter to any value, required to
+ * prevent the command from appearing within the Quick Access.</li>
+ * </ul>
+ * <pre>
+ * <extension point="org.eclipse.ui.menus">
+ * <menuContribution allPopups="false"
+ * locationURI="toolbar:org.eclipse.ui.main.toolbar?after=additions">
+ * <toolbar id="my.toolbar">
+ * <command
+ * commandId="org.eclipse.userstorage.ui.showPullDown"
+ * id="<b>org.eclipse.userstorage.ui.accounts</b>"
+ * tooltip="Linked Accounts"
+ * icon="icons/UserAccount.png"
+ * style="pulldown" />
+ * </toolbar>
+ * </menuContribution>
+ * <menuContribution allPopups="false"
+ * locationURI="menu:<b>org.eclipse.userstorage.ui.accounts</b>">
+ * <!-- menu definition -->
+ * </menuContribution>
+ * </extension>
+ * </pre>
+ */
+public class ShowPullDownMenu extends AbstractHandler
+{
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ final IMenuService menuService = getService(event, IMenuService.class);
+
+ PopupMenu popup = new PopupMenu();
+ popup.configure(menuService, event);
+ popup.show();
+ return null;
+ }
+
+ /* @NonNull */
+ private <T> T getService(ExecutionEvent event, Class<T> clazz) throws ExecutionException
+ {
+ T service;
+ IServiceLocator locator = HandlerUtil.getActiveSite(event);
+ if (locator != null && (service = locator.getService(clazz)) != null)
+ {
+ return service;
+ }
+ locator = HandlerUtil.getActiveWorkbenchWindow(event);
+ if (locator != null && (service = locator.getService(clazz)) != null)
+ {
+ return service;
+ }
+ throw new ExecutionException("Unable to locate service " + clazz);
+ }
+
+ private static class PopupMenu
+ {
+ private IMenuService menuService;
+
+ private String menuId;
+
+ private Control parent;
+
+ private Point location;
+
+ /** Configure the popup menu id and location. */
+ private void configure(IMenuService menuService, ExecutionEvent event) throws ExecutionException
+ {
+ this.menuService = menuService;
+
+ // Support use of this command in org.eclipse.ui.menus's
+ // <command style=pulldown> and find the command id from the
+ // corresponding ContributionItem
+ if (!(event.getTrigger() instanceof Event) || ((Event)event.getTrigger()).type != SWT.Selection
+ || !(((Event)event.getTrigger()).widget instanceof ToolItem))
+ {
+ throw new ExecutionException("Unable to determine menu id");
+ }
+ ToolItem toolItem = (ToolItem)((Event)event.getTrigger()).widget;
+ if (!(toolItem.getData() instanceof ContributionItem))
+ {
+ throw new ExecutionException("Unable to determine menu id");
+ }
+ menuId = ((ContributionItem)toolItem.getData()).getId();
+
+ // place menu near the drop-down indicator on right-side
+ parent = toolItem.getParent();
+ Rectangle itemBounds = toolItem.getBounds(); // relative to toolbar
+ location = parent.toDisplay(new Point(itemBounds.x, itemBounds.y + itemBounds.height));
+ }
+
+ private void show()
+ {
+ final MenuManager menuManager = new MenuManager();
+ Menu menu = menuManager.createContextMenu(parent);
+ menuManager.addMenuListener(new IMenuListener2()
+ {
+ @Override
+ public void menuAboutToHide(IMenuManager manager)
+ {
+ parent.getDisplay().asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ menuService.releaseContributions(menuManager);
+ menuManager.dispose();
+ }
+ });
+ }
+
+ @Override
+ public void menuAboutToShow(IMenuManager manager)
+ {
+ menuService.populateContributionManager(menuManager, "menu:" + menuId);
+ }
+ });
+ menu.setLocation(location);
+ menu.setVisible(true);
+ }
+ }
+}