| // Copyright (C) 2008 The Android Open Source Project |
| // |
| // Licensed 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. |
| |
| package com.google.gerrit.client; |
| |
| import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP; |
| import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT; |
| import static com.google.gerrit.common.data.GlobalCapability.VIEW_PLUGINS; |
| import static com.google.gerrit.common.data.HostPageData.XSRF_COOKIE_NAME; |
| |
| import com.google.gerrit.client.account.AccountApi; |
| import com.google.gerrit.client.account.AccountCapabilities; |
| import com.google.gerrit.client.account.EditPreferences; |
| import com.google.gerrit.client.admin.ProjectScreen; |
| import com.google.gerrit.client.api.ApiGlue; |
| import com.google.gerrit.client.api.PluginLoader; |
| import com.google.gerrit.client.change.LocalComments; |
| import com.google.gerrit.client.changes.ChangeListScreen; |
| import com.google.gerrit.client.config.ConfigServerApi; |
| import com.google.gerrit.client.documentation.DocInfo; |
| import com.google.gerrit.client.info.AccountInfo; |
| import com.google.gerrit.client.info.AuthInfo; |
| import com.google.gerrit.client.info.GeneralPreferences; |
| import com.google.gerrit.client.info.ServerInfo; |
| import com.google.gerrit.client.info.TopMenu; |
| import com.google.gerrit.client.info.TopMenuItem; |
| import com.google.gerrit.client.info.TopMenuList; |
| import com.google.gerrit.client.rpc.CallbackGroup; |
| import com.google.gerrit.client.rpc.GerritCallback; |
| import com.google.gerrit.client.rpc.Natives; |
| import com.google.gerrit.client.ui.LinkMenuBar; |
| import com.google.gerrit.client.ui.LinkMenuItem; |
| import com.google.gerrit.client.ui.MorphingTabPanel; |
| import com.google.gerrit.client.ui.ProjectLinkMenuItem; |
| import com.google.gerrit.client.ui.Screen; |
| import com.google.gerrit.common.Nullable; |
| import com.google.gerrit.common.PageLinks; |
| import com.google.gerrit.common.data.HostPageData; |
| import com.google.gerrit.common.data.SystemInfoService; |
| import com.google.gerrit.extensions.client.DiffPreferencesInfo; |
| import com.google.gerrit.extensions.client.EditPreferencesInfo; |
| import com.google.gerrit.extensions.client.GerritTopMenu; |
| import com.google.gerrit.extensions.client.UiType; |
| import com.google.gerrit.reviewdb.client.Project; |
| import com.google.gwt.aria.client.Roles; |
| import com.google.gwt.core.client.EntryPoint; |
| import com.google.gwt.core.client.GWT; |
| import com.google.gwt.dom.client.Document; |
| import com.google.gwt.event.dom.client.ClickEvent; |
| import com.google.gwt.event.dom.client.ClickHandler; |
| import com.google.gwt.event.dom.client.KeyCodes; |
| import com.google.gwt.event.dom.client.KeyDownEvent; |
| import com.google.gwt.event.dom.client.KeyDownHandler; |
| import com.google.gwt.event.logical.shared.ValueChangeEvent; |
| import com.google.gwt.event.logical.shared.ValueChangeHandler; |
| import com.google.gwt.event.shared.EventBus; |
| import com.google.gwt.event.shared.SimpleEventBus; |
| import com.google.gwt.http.client.Request; |
| import com.google.gwt.http.client.RequestBuilder; |
| import com.google.gwt.http.client.RequestCallback; |
| import com.google.gwt.http.client.RequestException; |
| import com.google.gwt.http.client.Response; |
| import com.google.gwt.http.client.URL; |
| import com.google.gwt.http.client.UrlBuilder; |
| import com.google.gwt.user.client.Command; |
| import com.google.gwt.user.client.Cookies; |
| import com.google.gwt.user.client.History; |
| import com.google.gwt.user.client.Window; |
| import com.google.gwt.user.client.Window.Location; |
| import com.google.gwt.user.client.rpc.AsyncCallback; |
| import com.google.gwt.user.client.ui.Anchor; |
| import com.google.gwt.user.client.ui.FlowPanel; |
| import com.google.gwt.user.client.ui.FocusPanel; |
| import com.google.gwt.user.client.ui.Grid; |
| import com.google.gwt.user.client.ui.HTMLTable.CellFormatter; |
| import com.google.gwt.user.client.ui.InlineHTML; |
| import com.google.gwt.user.client.ui.InlineLabel; |
| import com.google.gwt.user.client.ui.RootPanel; |
| import com.google.gwtexpui.clippy.client.CopyableLabel; |
| import com.google.gwtexpui.user.client.UserAgent; |
| import com.google.gwtexpui.user.client.ViewSite; |
| import com.google.gwtjsonrpc.client.JsonDefTarget; |
| import com.google.gwtjsonrpc.client.JsonUtil; |
| import com.google.gwtjsonrpc.client.XsrfManager; |
| import com.google.gwtorm.client.KeyUtil; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| public class Gerrit implements EntryPoint { |
| public static final GerritConstants C = GWT.create(GerritConstants.class); |
| public static final GerritMessages M = GWT.create(GerritMessages.class); |
| public static final GerritResources RESOURCES = GWT.create(GerritResources.class); |
| public static final SystemInfoService SYSTEM_SVC; |
| public static final EventBus EVENT_BUS = GWT.create(SimpleEventBus.class); |
| public static final Themer THEMER = GWT.create(Themer.class); |
| public static final String PROJECT_NAME_MENU_VAR = "${projectName}"; |
| public static final String INDEX = "Documentation/index.html"; |
| |
| private static String myHost; |
| private static ServerInfo myServerInfo; |
| private static AccountInfo myAccount; |
| private static GeneralPreferences myPrefs; |
| private static UrlAliasMatcher urlAliasMatcher; |
| private static boolean hasDocumentation; |
| private static boolean docSearch; |
| private static String docUrl; |
| private static HostPageData.Theme myTheme; |
| private static String defaultScreenToken; |
| private static DiffPreferencesInfo myAccountDiffPref; |
| private static EditPreferencesInfo editPrefs; |
| private static String xGerritAuth; |
| private static boolean isNoteDbEnabled; |
| |
| private static Map<String, LinkMenuBar> menuBars; |
| |
| private static MorphingTabPanel menuLeft; |
| private static LinkMenuBar menuRight; |
| private static RootPanel topMenu; |
| private static RootPanel siteHeader; |
| private static RootPanel siteFooter; |
| private static RootPanel bottomMenu; |
| private static SearchPanel searchPanel; |
| private static final Dispatcher dispatcher = new Dispatcher(); |
| private static ViewSite<Screen> body; |
| private static String lastChangeListToken; |
| private static String lastViewToken; |
| private static Anchor uiSwitcherLink; |
| |
| static { |
| SYSTEM_SVC = GWT.create(SystemInfoService.class); |
| JsonUtil.bind(SYSTEM_SVC, "rpc/SystemInfoService"); |
| } |
| |
| static void upgradeUI(String token) { |
| History.newItem(Dispatcher.RELOAD_UI + token, false); |
| Window.Location.reload(); |
| } |
| |
| public static void displayLastChangeList() { |
| if (lastChangeListToken != null) { |
| display(lastChangeListToken); |
| } else if (isSignedIn()) { |
| display(PageLinks.MINE); |
| } else { |
| display(PageLinks.toChangeQuery("status:open")); |
| } |
| } |
| |
| public static String getPriorView() { |
| return lastViewToken; |
| } |
| |
| /** |
| * Load the screen at the given location, displaying when ready. |
| * |
| * <p>If the URL is not already pointing at this location, a new item will be added to the |
| * browser's history when the screen is fully loaded and displayed on the UI. |
| * |
| * @param token location to parse, load, and render. |
| */ |
| public static void display(final String token) { |
| if (body.getView() == null || !body.getView().displayToken(token)) { |
| dispatcher.display(token); |
| updateUiLink(token); |
| } |
| } |
| |
| /** |
| * Load the screen passed, assuming token can be used to locate it. |
| * |
| * <p>The screen is loaded in the background. When it is ready to be visible a new item will be |
| * added to the browser's history, the screen will be made visible, and the window title may be |
| * updated. |
| * |
| * <p>If {@link Screen#isRequiresSignIn()} is true and the user is not signed in yet the screen |
| * instance will be discarded, sign-in will take place, and will redirect to this location upon |
| * success. |
| * |
| * @param token location that refers to {@code view}. |
| * @param view the view to load. |
| */ |
| public static void display(final String token, final Screen view) { |
| if (view.isRequiresSignIn() && !isSignedIn()) { |
| doSignIn(token); |
| } else { |
| view.setToken(token); |
| if (isSignedIn()) { |
| LocalComments.saveInlineComments(); |
| } |
| body.setView(view); |
| updateUiLink(token); |
| } |
| } |
| |
| public static void selectMenu(LinkMenuBar bar) { |
| menuLeft.selectTab(menuLeft.getWidgetIndex(bar)); |
| } |
| |
| /** |
| * Update the current history token after a screen change. |
| * |
| * <p>The caller has already updated the UI, but wants to publish a different history token for |
| * the current browser state. This really only makes sense if the caller is a {@code TabPanel} and |
| * is firing an event when the tab changed to a different part. |
| * |
| * @param token new location that is already visible. |
| */ |
| public static void updateImpl(final String token) { |
| History.newItem(token, false); |
| dispatchHistoryHooks(token); |
| } |
| |
| public static void setQueryString(String query) { |
| searchPanel.setText(query); |
| } |
| |
| public static void setWindowTitle(final Screen screen, final String text) { |
| if (screen == body.getView()) { |
| if (text == null || text.length() == 0) { |
| Window.setTitle(M.windowTitle1(myHost)); |
| } else { |
| Window.setTitle(M.windowTitle2(text, myHost)); |
| } |
| } |
| } |
| |
| public static int getHeaderFooterHeight() { |
| int h = bottomMenu.getOffsetHeight(); |
| if (topMenu.isVisible()) { |
| h += topMenu.getOffsetHeight(); |
| } |
| if (siteHeader.isVisible()) { |
| h += siteHeader.getOffsetHeight(); |
| } |
| if (siteFooter.isVisible()) { |
| h += siteFooter.getOffsetHeight(); |
| } |
| return h; |
| } |
| |
| public static void setHeaderVisible(boolean visible) { |
| topMenu.setVisible(visible); |
| siteHeader.setVisible(visible && getUserPreferences().showSiteHeader()); |
| } |
| |
| public static boolean isHeaderVisible() { |
| return topMenu.isVisible(); |
| } |
| |
| public static String getDefaultScreenToken() { |
| return defaultScreenToken; |
| } |
| |
| public static RootPanel getBottomMenu() { |
| return bottomMenu; |
| } |
| |
| /** Get the public configuration data used by this Gerrit instance. */ |
| public static ServerInfo info() { |
| return myServerInfo; |
| } |
| |
| public static UrlAliasMatcher getUrlAliasMatcher() { |
| return urlAliasMatcher; |
| } |
| |
| /** Site theme information (site specific colors)/ */ |
| public static HostPageData.Theme getTheme() { |
| return myTheme; |
| } |
| |
| /** @return the currently signed in user's account data; empty account data if no account */ |
| public static AccountInfo getUserAccount() { |
| return myAccount; |
| } |
| |
| /** @return access token to prove user identity during REST API calls. */ |
| @Nullable |
| public static String getXGerritAuth() { |
| return xGerritAuth; |
| } |
| |
| /** |
| * @return the preferences of the currently signed in user, the default preferences if not signed |
| * in |
| */ |
| public static GeneralPreferences getUserPreferences() { |
| return myPrefs; |
| } |
| |
| /** @return the currently signed in users's diff preferences, or default values */ |
| public static DiffPreferencesInfo getDiffPreferences() { |
| return myAccountDiffPref; |
| } |
| |
| public static void setDiffPreferences(DiffPreferencesInfo accountDiffPref) { |
| myAccountDiffPref = accountDiffPref; |
| } |
| |
| /** @return the edit preferences of the current user, null if not signed-in */ |
| public static EditPreferencesInfo getEditPreferences() { |
| return editPrefs; |
| } |
| |
| public static void setEditPreferences(EditPreferencesInfo p) { |
| editPrefs = p; |
| } |
| |
| /** @return true if the user is currently authenticated */ |
| public static boolean isSignedIn() { |
| return xGerritAuth != null; |
| } |
| |
| /** Sign the user into the application. */ |
| public static void doSignIn(String token) { |
| Location.assign(loginRedirect(token)); |
| } |
| |
| public static boolean isNoteDbEnabled() { |
| return isNoteDbEnabled; |
| } |
| |
| public static String loginRedirect(String token) { |
| if (token == null) { |
| token = ""; |
| } else if (token.startsWith("/")) { |
| token = token.substring(1); |
| } |
| |
| return selfRedirect("login/") + URL.encodePathSegment("#/" + token); |
| } |
| |
| public static String selfRedirect(String suffix) { |
| // Clean up the path. Users seem to like putting extra slashes into the URL |
| // which can break redirections by misinterpreting at either client or server. |
| String path = Location.getPath(); |
| if (path == null || path.isEmpty()) { |
| path = "/"; |
| } else { |
| while (path.startsWith("//")) { |
| path = path.substring(1); |
| } |
| while (path.endsWith("//")) { |
| path = path.substring(0, path.length() - 1); |
| } |
| if (!path.endsWith("/")) { |
| path = path + "/"; |
| } |
| } |
| |
| if (suffix != null) { |
| while (suffix.startsWith("/")) { |
| suffix = suffix.substring(1); |
| } |
| path += suffix; |
| } |
| |
| UrlBuilder builder = new UrlBuilder(); |
| builder.setProtocol(Location.getProtocol()); |
| builder.setHost(Location.getHost()); |
| String port = Location.getPort(); |
| if (port != null && !port.isEmpty()) { |
| builder.setPort(Integer.parseInt(port)); |
| } |
| builder.setPath(path); |
| return builder.buildString(); |
| } |
| |
| static void deleteSessionCookie() { |
| myAccount = AccountInfo.create(0, null, null, null); |
| myAccountDiffPref = null; |
| editPrefs = null; |
| myPrefs = GeneralPreferences.createDefault(); |
| urlAliasMatcher.clearUserAliases(); |
| xGerritAuth = null; |
| refreshMenuBar(); |
| |
| // If the cookie was HttpOnly, this request to delete it will |
| // most likely not be successful. We can try anyway though. |
| // |
| Cookies.removeCookie("GerritAccount"); |
| } |
| |
| private void setXsrfToken() { |
| xGerritAuth = Cookies.getCookie(XSRF_COOKIE_NAME); |
| JsonUtil.setDefaultXsrfManager( |
| new XsrfManager() { |
| @Override |
| public String getToken(JsonDefTarget proxy) { |
| return xGerritAuth; |
| } |
| |
| @Override |
| public void setToken(JsonDefTarget proxy, String token) { |
| // Ignore the request, we always rely upon the cookie. |
| } |
| }); |
| } |
| |
| @Override |
| public void onModuleLoad() { |
| if (!canLoadInIFrame()) { |
| UserAgent.assertNotInIFrame(); |
| } |
| setXsrfToken(); |
| |
| KeyUtil.setEncoderImpl( |
| new KeyUtil.Encoder() { |
| @Override |
| public String encode(String e) { |
| e = URL.encodeQueryString(e); |
| e = fixPathImpl(e); |
| e = fixColonImpl(e); |
| e = fixDoubleQuote(e); |
| return e; |
| } |
| |
| @Override |
| public String decode(final String e) { |
| return URL.decodeQueryString(e); |
| } |
| |
| private native String fixPathImpl(String path) |
| /*-{ return path.replace(/%2F/g, "/"); }-*/ ; |
| |
| private native String fixColonImpl(String path) |
| /*-{ return path.replace(/%3A/g, ":"); }-*/ ; |
| |
| private native String fixDoubleQuote(String path) |
| /*-{ return path.replace(/%22/g, '"'); }-*/ ; |
| }); |
| |
| initHostname(); |
| Window.setTitle(M.windowTitle1(myHost)); |
| |
| RpcStatus.INSTANCE = new RpcStatus(); |
| CallbackGroup cbg = new CallbackGroup(); |
| getDocIndex( |
| cbg.add( |
| new GerritCallback<DocInfo>() { |
| @Override |
| public void onSuccess(DocInfo indexInfo) { |
| hasDocumentation = indexInfo != null; |
| docUrl = selfRedirect("/Documentation/"); |
| } |
| })); |
| ConfigServerApi.serverInfo( |
| cbg.add( |
| new GerritCallback<ServerInfo>() { |
| @Override |
| public void onSuccess(ServerInfo info) { |
| myServerInfo = info; |
| urlAliasMatcher = new UrlAliasMatcher(info.urlAliases()); |
| String du = info.gerrit().docUrl(); |
| if (du != null && !du.isEmpty()) { |
| hasDocumentation = true; |
| docUrl = du; |
| } |
| docSearch = info.gerrit().docSearch(); |
| } |
| })); |
| HostPageDataService hpd = GWT.create(HostPageDataService.class); |
| hpd.load( |
| cbg.addFinal( |
| new GerritCallback<HostPageData>() { |
| @Override |
| public void onSuccess(final HostPageData result) { |
| Document.get().getElementById("gerrit_hostpagedata").removeFromParent(); |
| myTheme = result.theme; |
| isNoteDbEnabled = result.isNoteDbEnabled; |
| if (result.accountDiffPref != null) { |
| myAccountDiffPref = result.accountDiffPref; |
| } |
| if (result.accountDiffPref != null) { |
| // TODO: Support options on the GetDetail REST endpoint so that it can |
| // also return the preferences. Then we can fetch everything with a |
| // single request and we don't need the callback group anymore. |
| CallbackGroup cbg = new CallbackGroup(); |
| AccountApi.self() |
| .view("detail") |
| .get( |
| cbg.add( |
| new GerritCallback<AccountInfo>() { |
| @Override |
| public void onSuccess(AccountInfo result) { |
| myAccount = result; |
| } |
| })); |
| AccountApi.self() |
| .view("preferences") |
| .get( |
| cbg.add( |
| new GerritCallback<GeneralPreferences>() { |
| @Override |
| public void onSuccess(GeneralPreferences prefs) { |
| myPrefs = prefs; |
| onModuleLoad2(result); |
| } |
| })); |
| AccountApi.getEditPreferences( |
| cbg.addFinal( |
| new GerritCallback<EditPreferences>() { |
| @Override |
| public void onSuccess(EditPreferences prefs) { |
| EditPreferencesInfo prefsInfo = new EditPreferencesInfo(); |
| prefs.copyTo(prefsInfo); |
| editPrefs = prefsInfo; |
| } |
| })); |
| } else { |
| myAccount = AccountInfo.create(0, null, null, null); |
| myPrefs = GeneralPreferences.createDefault(); |
| editPrefs = null; |
| onModuleLoad2(result); |
| } |
| } |
| })); |
| } |
| |
| private native boolean canLoadInIFrame() /*-{ |
| return $wnd.gerrit_hostpagedata.canLoadInIFrame || false; |
| }-*/; |
| |
| private static void initHostname() { |
| myHost = Location.getHostName(); |
| final int d1 = myHost.indexOf('.'); |
| if (d1 < 0) { |
| return; |
| } |
| final int d2 = myHost.indexOf('.', d1 + 1); |
| if (d2 >= 0) { |
| myHost = myHost.substring(0, d2); |
| } |
| } |
| |
| private static void dispatchHistoryHooks(String token) { |
| ApiGlue.fireEvent("history", token); |
| } |
| |
| private static String getUiSwitcherUrl(String token) { |
| UrlBuilder builder = new UrlBuilder(); |
| builder.setProtocol(Location.getProtocol()); |
| builder.setHost(Location.getHost()); |
| String port = Location.getPort(); |
| if (port != null && !port.isEmpty()) { |
| builder.setPort(Integer.parseInt(port)); |
| } |
| String[] tokens = token.split("@", 2); |
| if (Location.getPath().endsWith("/") && tokens[0].startsWith("/")) { |
| tokens[0] = tokens[0].substring(1); |
| } |
| builder.setPath(Location.getPath() + tokens[0]); |
| if (tokens.length == 2) { |
| builder.setHash(tokens[1]); |
| } |
| builder.setParameter("polygerrit", "1"); |
| return builder.buildString(); |
| } |
| |
| private static void populateBottomMenu(RootPanel btmmenu, HostPageData hpd) { |
| String vs = hpd.version; |
| if (vs == null || vs.isEmpty()) { |
| vs = "dev"; |
| } |
| |
| btmmenu.add(new InlineHTML(M.poweredBy(vs))); |
| |
| if (info().gerrit().webUis().contains(UiType.POLYGERRIT)) { |
| btmmenu.add(new InlineLabel(" | ")); |
| uiSwitcherLink = new Anchor(C.newUi(), getUiSwitcherUrl(History.getToken())); |
| uiSwitcherLink.setStyleName(""); |
| btmmenu.add(uiSwitcherLink); |
| } |
| |
| String reportBugUrl = info().gerrit().reportBugUrl(); |
| if (reportBugUrl != null) { |
| String reportBugText = info().gerrit().reportBugText(); |
| Anchor a = new Anchor(reportBugText == null ? C.reportBug() : reportBugText, reportBugUrl); |
| a.setTarget("_blank"); |
| a.setStyleName(""); |
| btmmenu.add(new InlineLabel(" | ")); |
| btmmenu.add(a); |
| } |
| btmmenu.add(new InlineLabel(" | ")); |
| btmmenu.add(new InlineLabel(C.keyHelp())); |
| } |
| |
| private static void updateUiLink(String token) { |
| if (uiSwitcherLink != null) { |
| uiSwitcherLink.setHref(getUiSwitcherUrl(token)); |
| } |
| } |
| |
| private void onModuleLoad2(HostPageData hpd) { |
| RESOURCES.gwt_override().ensureInjected(); |
| RESOURCES.css().ensureInjected(); |
| |
| topMenu = RootPanel.get("gerrit_topmenu"); |
| final RootPanel gStarting = RootPanel.get("gerrit_startinggerrit"); |
| final RootPanel gBody = RootPanel.get("gerrit_body"); |
| bottomMenu = RootPanel.get("gerrit_btmmenu"); |
| |
| topMenu.setStyleName(RESOURCES.css().gerritTopMenu()); |
| gBody.setStyleName(RESOURCES.css().gerritBody()); |
| |
| final Grid menuLine = new Grid(1, 3); |
| menuLeft = new MorphingTabPanel(); |
| menuRight = new LinkMenuBar(); |
| searchPanel = new SearchPanel(); |
| menuLeft.setStyleName(RESOURCES.css().topmenuMenuLeft()); |
| menuLine.setStyleName(RESOURCES.css().topmenu()); |
| topMenu.add(menuLine); |
| final FlowPanel menuRightPanel = new FlowPanel(); |
| menuRightPanel.setStyleName(RESOURCES.css().topmenuMenuRight()); |
| menuRightPanel.add(searchPanel); |
| menuRightPanel.add(menuRight); |
| menuLine.setWidget(0, 0, menuLeft); |
| menuLine.setWidget(0, 1, new FlowPanel()); |
| menuLine.setWidget(0, 2, menuRightPanel); |
| final CellFormatter fmt = menuLine.getCellFormatter(); |
| fmt.setStyleName(0, 0, RESOURCES.css().topmenuTDmenu()); |
| fmt.setStyleName(0, 1, RESOURCES.css().topmenuTDglue()); |
| fmt.setStyleName(0, 2, RESOURCES.css().topmenuTDmenu()); |
| |
| siteHeader = RootPanel.get("gerrit_header"); |
| siteFooter = RootPanel.get("gerrit_footer"); |
| |
| body = |
| new ViewSite<Screen>() { |
| @Override |
| protected void onShowView(Screen view) { |
| String token = view.getToken(); |
| History.newItem(token, false); |
| dispatchHistoryHooks(token); |
| |
| if (view instanceof ChangeListScreen) { |
| lastChangeListToken = token; |
| } |
| |
| super.onShowView(view); |
| view.onShowView(); |
| lastViewToken = token; |
| } |
| }; |
| gBody.add(body); |
| |
| JsonUtil.addRpcStartHandler(RpcStatus.INSTANCE); |
| JsonUtil.addRpcCompleteHandler(RpcStatus.INSTANCE); |
| |
| gStarting.getElement().getParentElement().removeChild(gStarting.getElement()); |
| RootPanel.detachNow(gStarting); |
| ApiGlue.init(); |
| |
| applyUserPreferences(); |
| populateBottomMenu(bottomMenu, hpd); |
| refreshMenuBar(); |
| |
| History.addValueChangeHandler( |
| new ValueChangeHandler<String>() { |
| @Override |
| public void onValueChange(ValueChangeEvent<String> event) { |
| display(event.getValue()); |
| } |
| }); |
| JumpKeys.register(body); |
| |
| saveDefaultTheme(); |
| if (hpd.messages != null) { |
| new MessageOfTheDayBar(hpd.messages).show(); |
| } |
| PluginLoader.load( |
| hpd.plugins, |
| hpd.pluginsLoadTimeout, |
| new GerritCallback<VoidResult>() { |
| @Override |
| public void onSuccess(VoidResult result) { |
| String token = History.getToken(); |
| if (token.isEmpty()) { |
| token = isSignedIn() ? PageLinks.MINE : PageLinks.toChangeQuery("status:open"); |
| } |
| display(token); |
| } |
| }); |
| } |
| |
| private void saveDefaultTheme() { |
| THEMER.init( |
| Document.get().getElementById("gerrit_sitecss"), |
| Document.get().getElementById("gerrit_header"), |
| Document.get().getElementById("gerrit_footer")); |
| } |
| |
| public static void refreshMenuBar() { |
| menuLeft.clear(); |
| menuRight.clear(); |
| |
| menuBars = new HashMap<>(); |
| |
| boolean signedIn = isSignedIn(); |
| AuthInfo authInfo = info().auth(); |
| LinkMenuBar m; |
| |
| m = new LinkMenuBar(); |
| menuBars.put(GerritTopMenu.ALL.menuName, m); |
| addLink(m, C.menuAllOpen(), PageLinks.toChangeQuery("status:open")); |
| addLink(m, C.menuAllMerged(), PageLinks.toChangeQuery("status:merged")); |
| addLink(m, C.menuAllAbandoned(), PageLinks.toChangeQuery("status:abandoned")); |
| menuLeft.add(m, C.menuAll()); |
| |
| if (signedIn) { |
| LinkMenuBar myBar = new LinkMenuBar(); |
| menuBars.put(GerritTopMenu.MY.menuName, myBar); |
| |
| if (myPrefs.my() != null) { |
| myBar.clear(); |
| String url = null; |
| List<TopMenuItem> myMenuItems = Natives.asList(myPrefs.my()); |
| if (!myMenuItems.isEmpty()) { |
| if (myMenuItems.get(0).getUrl().startsWith("#")) { |
| url = myMenuItems.get(0).getUrl().substring(1); |
| } |
| for (TopMenuItem item : myMenuItems) { |
| addExtensionLink(myBar, item); |
| } |
| } |
| defaultScreenToken = url; |
| } |
| |
| menuLeft.add(myBar, C.menuMine()); |
| menuLeft.selectTab(1); |
| } else { |
| menuLeft.selectTab(0); |
| } |
| |
| final LinkMenuBar projectsBar = new LinkMenuBar(); |
| menuBars.put(GerritTopMenu.PROJECTS.menuName, projectsBar); |
| addLink(projectsBar, C.menuProjectsList(), PageLinks.ADMIN_PROJECTS); |
| projectsBar.addItem(new ProjectLinkMenuItem(C.menuProjectsInfo(), ProjectScreen.INFO)); |
| projectsBar.addItem(new ProjectLinkMenuItem(C.menuProjectsBranches(), ProjectScreen.BRANCHES)); |
| projectsBar.addItem(new ProjectLinkMenuItem(C.menuProjectsTags(), ProjectScreen.TAGS)); |
| projectsBar.addItem(new ProjectLinkMenuItem(C.menuProjectsAccess(), ProjectScreen.ACCESS)); |
| final LinkMenuItem dashboardsMenuItem = |
| new ProjectLinkMenuItem(C.menuProjectsDashboards(), ProjectScreen.DASHBOARDS) { |
| @Override |
| protected boolean match(String token) { |
| return super.match(token) |
| || (!getTargetHistoryToken().isEmpty() |
| && ("/admin" + token).startsWith(getTargetHistoryToken())); |
| } |
| }; |
| projectsBar.addItem(dashboardsMenuItem); |
| menuLeft.add(projectsBar, C.menuProjects()); |
| |
| if (signedIn) { |
| final LinkMenuBar peopleBar = new LinkMenuBar(); |
| menuBars.put(GerritTopMenu.PEOPLE.menuName, peopleBar); |
| final LinkMenuItem groupsListMenuItem = |
| addLink(peopleBar, C.menuPeopleGroupsList(), PageLinks.ADMIN_GROUPS); |
| menuLeft.add(peopleBar, C.menuPeople()); |
| |
| final LinkMenuBar pluginsBar = new LinkMenuBar(); |
| menuBars.put(GerritTopMenu.PLUGINS.menuName, pluginsBar); |
| AccountCapabilities.all( |
| new GerritCallback<AccountCapabilities>() { |
| @Override |
| public void onSuccess(AccountCapabilities result) { |
| if (result.canPerform(CREATE_PROJECT)) { |
| insertLink( |
| projectsBar, |
| C.menuProjectsCreate(), |
| PageLinks.ADMIN_CREATE_PROJECT, |
| projectsBar.getWidgetIndex(dashboardsMenuItem) + 1); |
| } |
| if (result.canPerform(CREATE_GROUP)) { |
| insertLink( |
| peopleBar, |
| C.menuPeopleGroupsCreate(), |
| PageLinks.ADMIN_CREATE_GROUP, |
| peopleBar.getWidgetIndex(groupsListMenuItem) + 1); |
| } |
| if (result.canPerform(VIEW_PLUGINS)) { |
| insertLink(pluginsBar, C.menuPluginsInstalled(), PageLinks.ADMIN_PLUGINS, 0); |
| menuLeft.insert( |
| pluginsBar, C.menuPlugins(), menuLeft.getWidgetIndex(peopleBar) + 1); |
| } |
| } |
| }, |
| CREATE_PROJECT, |
| CREATE_GROUP, |
| VIEW_PLUGINS); |
| } |
| |
| if (hasDocumentation) { |
| m = new LinkMenuBar(); |
| menuBars.put(GerritTopMenu.DOCUMENTATION.menuName, m); |
| addDocLink(m, C.menuDocumentationTOC(), "index.html"); |
| addDocLink(m, C.menuDocumentationSearch(), "user-search.html"); |
| addDocLink(m, C.menuDocumentationUpload(), "user-upload.html"); |
| addDocLink(m, C.menuDocumentationAccess(), "access-control.html"); |
| addDocLink(m, C.menuDocumentationAPI(), "rest-api.html"); |
| addDocLink(m, C.menuDocumentationProjectOwnerGuide(), "intro-project-owner.html"); |
| menuLeft.add(m, C.menuDocumentation()); |
| } |
| |
| if (signedIn) { |
| whoAmI(!authInfo.isClientSslCertLdap()); |
| } else { |
| switch (authInfo.authType()) { |
| case CLIENT_SSL_CERT_LDAP: |
| break; |
| |
| case OPENID: |
| menuRight.addItem( |
| C.menuRegister(), |
| new Command() { |
| @Override |
| public void execute() { |
| String t = History.getToken(); |
| if (t == null) { |
| t = ""; |
| } |
| doSignIn(PageLinks.REGISTER + t); |
| } |
| }); |
| menuRight.addItem( |
| C.menuSignIn(), |
| new Command() { |
| @Override |
| public void execute() { |
| doSignIn(History.getToken()); |
| } |
| }); |
| break; |
| |
| case OAUTH: |
| menuRight.addItem( |
| C.menuSignIn(), |
| new Command() { |
| @Override |
| public void execute() { |
| doSignIn(History.getToken()); |
| } |
| }); |
| break; |
| |
| case OPENID_SSO: |
| menuRight.addItem( |
| C.menuSignIn(), |
| new Command() { |
| @Override |
| public void execute() { |
| doSignIn(History.getToken()); |
| } |
| }); |
| break; |
| |
| case HTTP: |
| case HTTP_LDAP: |
| if (authInfo.loginUrl() != null) { |
| String signinText = |
| authInfo.loginText() == null ? C.menuSignIn() : authInfo.loginText(); |
| menuRight.add(anchor(signinText, authInfo.loginUrl())); |
| } |
| break; |
| |
| case LDAP: |
| case LDAP_BIND: |
| case CUSTOM_EXTENSION: |
| if (authInfo.registerUrl() != null) { |
| String registerText = |
| authInfo.registerText() == null ? C.menuRegister() : authInfo.registerText(); |
| menuRight.add(anchor(registerText, authInfo.registerUrl())); |
| } |
| menuRight.addItem( |
| C.menuSignIn(), |
| new Command() { |
| @Override |
| public void execute() { |
| doSignIn(History.getToken()); |
| } |
| }); |
| break; |
| |
| case DEVELOPMENT_BECOME_ANY_ACCOUNT: |
| menuRight.add(anchor("Become", loginRedirect(""))); |
| break; |
| } |
| } |
| ConfigServerApi.topMenus( |
| new GerritCallback<TopMenuList>() { |
| @Override |
| public void onSuccess(TopMenuList result) { |
| List<TopMenu> topMenuExtensions = Natives.asList(result); |
| for (TopMenu menu : topMenuExtensions) { |
| String name = menu.getName(); |
| LinkMenuBar existingBar = menuBars.get(name); |
| LinkMenuBar bar = existingBar != null ? existingBar : new LinkMenuBar(); |
| for (TopMenuItem item : Natives.asList(menu.getItems())) { |
| addMenuLink(bar, item); |
| } |
| if (existingBar == null) { |
| menuBars.put(name, bar); |
| menuLeft.add(bar, name); |
| } |
| } |
| } |
| }); |
| } |
| |
| public static void refreshUserPreferences() { |
| if (isSignedIn()) { |
| AccountApi.self() |
| .view("preferences") |
| .get( |
| new GerritCallback<GeneralPreferences>() { |
| @Override |
| public void onSuccess(GeneralPreferences prefs) { |
| setUserPreferences(prefs); |
| } |
| }); |
| } else { |
| setUserPreferences(GeneralPreferences.createDefault()); |
| } |
| } |
| |
| public static void setUserPreferences(GeneralPreferences prefs) { |
| myPrefs = prefs; |
| applyUserPreferences(); |
| refreshMenuBar(); |
| } |
| |
| private static void applyUserPreferences() { |
| CopyableLabel.setFlashEnabled(myPrefs.useFlashClipboard()); |
| if (siteHeader != null) { |
| siteHeader.setVisible(myPrefs.showSiteHeader()); |
| } |
| if (siteFooter != null) { |
| siteFooter.setVisible(myPrefs.showSiteHeader()); |
| } |
| FormatUtil.setPreferences(myPrefs); |
| urlAliasMatcher.updateUserAliases(myPrefs.urlAliases()); |
| } |
| |
| public static boolean hasDocSearch() { |
| return docSearch; |
| } |
| |
| private static void getDocIndex(final AsyncCallback<DocInfo> cb) { |
| RequestBuilder req = new RequestBuilder(RequestBuilder.HEAD, GWT.getHostPageBaseURL() + INDEX); |
| req.setCallback( |
| new RequestCallback() { |
| @Override |
| public void onResponseReceived(Request req, Response resp) { |
| switch (resp.getStatusCode()) { |
| case Response.SC_OK: |
| case Response.SC_MOVED_PERMANENTLY: |
| case Response.SC_MOVED_TEMPORARILY: |
| cb.onSuccess(DocInfo.create()); |
| break; |
| default: |
| cb.onSuccess(null); |
| break; |
| } |
| } |
| |
| @Override |
| public void onError(Request request, Throwable e) { |
| cb.onFailure(e); |
| } |
| }); |
| try { |
| req.send(); |
| } catch (RequestException e) { |
| cb.onFailure(e); |
| } |
| } |
| |
| private static void whoAmI(boolean canLogOut) { |
| AccountInfo account = getUserAccount(); |
| final UserPopupPanel userPopup = new UserPopupPanel(account, canLogOut, true); |
| final FlowPanel userSummaryPanel = new FlowPanel(); |
| class PopupHandler implements KeyDownHandler, ClickHandler { |
| private void showHidePopup() { |
| if (userPopup.isShowing() && userPopup.isVisible()) { |
| userPopup.hide(); |
| } else { |
| userPopup.showRelativeTo(userSummaryPanel); |
| } |
| } |
| |
| @Override |
| public void onClick(ClickEvent event) { |
| showHidePopup(); |
| } |
| |
| @Override |
| public void onKeyDown(KeyDownEvent event) { |
| if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { |
| showHidePopup(); |
| event.preventDefault(); |
| } |
| } |
| } |
| final PopupHandler popupHandler = new PopupHandler(); |
| final InlineLabel l = new InlineLabel(FormatUtil.name(account)); |
| l.setStyleName(RESOURCES.css().menuBarUserName()); |
| final AvatarImage avatar = new AvatarImage(account, 26, false); |
| avatar.setStyleName(RESOURCES.css().menuBarUserNameAvatar()); |
| userSummaryPanel.setStyleName(RESOURCES.css().menuBarUserNamePanel()); |
| userSummaryPanel.add(l); |
| userSummaryPanel.add(avatar); |
| // "BLACK DOWN-POINTING SMALL TRIANGLE" |
| userSummaryPanel.add(new InlineLabel(" \u25be")); |
| userPopup.addAutoHidePartner(userSummaryPanel.getElement()); |
| FocusPanel fp = new FocusPanel(userSummaryPanel); |
| fp.setStyleName(RESOURCES.css().menuBarUserNameFocusPanel()); |
| fp.addKeyDownHandler(popupHandler); |
| fp.addClickHandler(popupHandler); |
| menuRight.add(fp); |
| } |
| |
| private static Anchor anchor(final String text, final String to) { |
| final Anchor a = new Anchor(text, to); |
| a.setStyleName(RESOURCES.css().menuItem()); |
| Roles.getMenuitemRole().set(a.getElement()); |
| return a; |
| } |
| |
| private static LinkMenuItem addLink( |
| final LinkMenuBar m, final String text, final String historyToken) { |
| LinkMenuItem i = new LinkMenuItem(text, historyToken); |
| m.addItem(i); |
| return i; |
| } |
| |
| private static void insertLink( |
| final LinkMenuBar m, final String text, final String historyToken, final int beforeIndex) { |
| m.insertItem(new LinkMenuItem(text, historyToken), beforeIndex); |
| } |
| |
| private static LinkMenuItem addProjectLink(LinkMenuBar m, TopMenuItem item) { |
| LinkMenuItem i = |
| new ProjectLinkMenuItem(item.getName(), item.getUrl()) { |
| @Override |
| protected void onScreenLoad(Project.NameKey project) { |
| String p = panel.replace(PROJECT_NAME_MENU_VAR, URL.encodeQueryString(project.get())); |
| if (!panel.startsWith("/x/") && !isAbsolute(panel)) { |
| UrlBuilder builder = new UrlBuilder(); |
| builder.setProtocol(Location.getProtocol()); |
| builder.setHost(Location.getHost()); |
| String port = Location.getPort(); |
| if (port != null && !port.isEmpty()) { |
| builder.setPort(Integer.parseInt(port)); |
| } |
| builder.setPath(Location.getPath()); |
| p = builder.buildString() + p; |
| } |
| getElement().setPropertyString("href", p); |
| } |
| |
| @Override |
| public void go() { |
| String href = getElement().getPropertyString("href"); |
| if (href.startsWith("#")) { |
| super.go(); |
| } else { |
| Window.open(href, getElement().getPropertyString("target"), ""); |
| } |
| } |
| }; |
| if (item.getTarget() != null && !item.getTarget().isEmpty()) { |
| i.getElement().setAttribute("target", item.getTarget()); |
| } |
| if (item.getId() != null) { |
| i.getElement().setAttribute("id", item.getId()); |
| } |
| m.addItem(i); |
| return i; |
| } |
| |
| private static void addDocLink(final LinkMenuBar m, final String text, final String href) { |
| final Anchor atag = anchor(text, docUrl + href); |
| atag.setTarget("_blank"); |
| m.add(atag); |
| } |
| |
| private static void addMenuLink(LinkMenuBar m, TopMenuItem item) { |
| if (item.getUrl().contains(PROJECT_NAME_MENU_VAR)) { |
| addProjectLink(m, item); |
| } else { |
| addExtensionLink(m, item); |
| } |
| } |
| |
| private static void addExtensionLink(LinkMenuBar m, TopMenuItem item) { |
| if (item.getUrl().startsWith("#") && (item.getTarget() == null || item.getTarget().isEmpty())) { |
| LinkMenuItem a = new LinkMenuItem(item.getName(), item.getUrl().substring(1)); |
| if (item.getId() != null) { |
| a.getElement().setAttribute("id", item.getId()); |
| } |
| m.addItem(a); |
| } else { |
| Anchor atag = |
| anchor( |
| item.getName(), |
| isAbsolute(item.getUrl()) ? item.getUrl() : selfRedirect(item.getUrl())); |
| if (item.getTarget() != null && !item.getTarget().isEmpty()) { |
| atag.setTarget(item.getTarget()); |
| } |
| if (item.getId() != null) { |
| atag.getElement().setAttribute("id", item.getId()); |
| } |
| m.add(atag); |
| } |
| } |
| |
| private static boolean isAbsolute(String url) { |
| return url.matches("^https?://.*"); |
| } |
| } |