This is an automated email from the ASF dual-hosted git repository.
jongyoul pushed a commit to branch branch-0.12
in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/branch-0.12 by this push:
new b05f247dd6 [ZEPPELIN-6085] Add configuration option for setting the
default UI
b05f247dd6 is described below
commit b05f247dd6c017d2aa8fa0887a1073d6ec40d3c1
Author: ChanHo Lee <[email protected]>
AuthorDate: Wed Oct 9 15:50:16 2024 +0900
[ZEPPELIN-6085] Add configuration option for setting the default UI
### What is this PR for?
This PR introduces a new configuration option `zeppelin.default.ui` to set
the default UI for Zeppelin.
The available options for this setting are `new` and `classic`.
By default, the value is set to `new`, meaning the new UI will be served at
the root path (`"/"`), while the classic UI will be available at `"/classic"`.
If the configuration is set to `classic`, the classic UI will be served at
`"/"`, and the new UI at `"/new"`.
### What type of PR is it?
Improvement
### Todos
* [ ] - Task
### What is the Jira issue?
* Open an issue on Jira https://issues.apache.org/jira/browse/ZEPPELIN/6085
* Put link here, and add [ZEPPELIN-*Jira number*] in PR title, eg.
[ZEPPELIN-533]
### How should this be tested?
- Testing the `new` option :
- Set the `zeppelin.default.ui` config to `new` (the default).
- Verify that the new UI is served at the root (`"/"`) and the classic UI
is available at `"/classic"`.
- Verify that navigating between pages works without any issue.
- Verify that the UI switching button works properly in both UIs.
- Testing `classic` option :
- Similar steps as the above, except the new UI is served at `"/classic"`
and the classic UI is available at the root (`"/"`)
### Screenshots (if appropriate)
### Questions:
* Does the license files need to update? No
* Is there breaking changes for older versions? No
* Does this needs documentation? Yes, documentation for the new
configuration option has been added.
Closes #4824 from tbonelee/add-default-ui-config.
Signed-off-by: Jongyoul Lee <[email protected]>
(cherry picked from commit 00b7c553694050d6d2ee4824a236bd76a7135646)
Signed-off-by: Jongyoul Lee <[email protected]>
---
conf/zeppelin-site.xml.template | 6 +++++
docs/quickstart/explore_ui.md | 15 +++++++++++
docs/setup/operation/configuration.md | 6 +++++
.../zeppelin/conf/ZeppelinConfiguration.java | 11 ++++++++
.../apache/zeppelin/server/IndexHtmlServlet.java | 29 +++++++++++++++++++++-
.../org/apache/zeppelin/server/ZeppelinServer.java | 29 +++++++++++++++++-----
.../zeppelin/server/IndexHtmlServletTest.java | 4 +--
zeppelin-web-angular/package.json | 2 +-
.../src/app/share/header/header.component.html | 2 +-
.../src/app/share/header/header.component.ts | 10 ++++++++
zeppelin-web-angular/src/index.html | 9 ++++++-
.../src/components/navbar/navbar.controller.js | 8 ++++++
zeppelin-web/src/components/navbar/navbar.html | 2 +-
zeppelin-web/src/index.html | 9 ++++++-
14 files changed, 128 insertions(+), 14 deletions(-)
diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template
index 9c719920ce..91b7a15029 100755
--- a/conf/zeppelin-site.xml.template
+++ b/conf/zeppelin-site.xml.template
@@ -49,6 +49,12 @@
<description>Location of jetty temporary directory</description>
</property>
+<property>
+ <name>zeppelin.default.ui</name>
+ <value>new</value>
+ <description>Default UI for Zeppelin. Options: classic or new</description>
+</property>
+
<property>
<name>zeppelin.notebook.dir</name>
<value>notebook</value>
diff --git a/docs/quickstart/explore_ui.md b/docs/quickstart/explore_ui.md
index 73156f39e7..5a466385e8 100644
--- a/docs/quickstart/explore_ui.md
+++ b/docs/quickstart/explore_ui.md
@@ -35,6 +35,21 @@ Afterward, you can switch to the classic UI via the `Swtich
to Classic UI` butto
<img
src="{{BASE_PATH}}/assets/themes/zeppelin/img/ui-img/switch_to_classic_ui.png"
width="130" />
+
+### Configuring the default UI
+
+Zeppelin allows you to configure the default, especially for users who prefer
the classic UI.
+
+To set the default UI to classic, add the following property to the
`zeppelin-site.xml` file:
+
+```xml
+<property>
+ <name>zeppelin.default.ui</name>
+ <value>classic</value>
+ <description>Default UI for Zeppelin. Options: classic or new. Default
configuration is 'new'</description>
+</property>
+```
+
## Main home
The first time you connect to Zeppelin ([default installations start on
http://localhost:8080](http://localhost:8080/)), you'll land at the main page
similar to the below screen capture.
diff --git a/docs/setup/operation/configuration.md
b/docs/setup/operation/configuration.md
index e0c769202d..a2184314d2 100644
--- a/docs/setup/operation/configuration.md
+++ b/docs/setup/operation/configuration.md
@@ -217,6 +217,12 @@ Sources descending by priority:
<td>webapps</td>
<td>Location of the jetty temporary directory</td>
</tr>
+ <tr>
+ <td><h6 class="properties">ZEPPELIN_DEFAULT_UI</h6></td>
+ <td><h6 class="properties">zeppelin.default.ui</h6></td>
+ <td>new</td>
+ <td>Default UI for Zeppelin. <br />Options: <code>classic</code> or
<code>new</code></td>
+ </tr>
<tr>
<td><h6 class="properties">ZEPPELIN_NOTEBOOK_DIR</h6></td>
<td><h6 class="properties">zeppelin.notebook.dir</h6></td>
diff --git
a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
index 5ac7587f29..b590fa8dda 100644
---
a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
+++
b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
@@ -79,6 +79,11 @@ public class ZeppelinConfiguration {
DOCKER
}
+ public enum DEFAULT_UI {
+ NEW,
+ CLASSIC
+ }
+
// private constructor, so that it is singleton.
private ZeppelinConfiguration(@Nullable String filename) {
try {
@@ -882,6 +887,10 @@ public class ZeppelinConfiguration {
return getBoolean(ConfVars.ZEPPELIN_METRIC_ENABLE_PROMETHEUS);
}
+ public DEFAULT_UI getDefaultUi() {
+ return
DEFAULT_UI.valueOf(getString(ConfVars.ZEPPELIN_DEFAULT_UI).toUpperCase());
+ }
+
/**
* This method return the complete configuration map
* @return
@@ -942,6 +951,8 @@ public class ZeppelinConfiguration {
ZEPPELIN_WAR("zeppelin.war", "zeppelin-web/dist"),
ZEPPELIN_ANGULAR_WAR("zeppelin.angular.war",
"zeppelin-web-angular/dist/zeppelin"),
ZEPPELIN_WAR_TEMPDIR("zeppelin.war.tempdir", "webapps"),
+ // "new" or "classic"
+ ZEPPELIN_DEFAULT_UI("zeppelin.default.ui", "new"),
ZEPPELIN_JMX_ENABLE("zeppelin.jmx.enable", false),
ZEPPELIN_JMX_PORT("zeppelin.jmx.port", 9996),
diff --git
a/zeppelin-server/src/main/java/org/apache/zeppelin/server/IndexHtmlServlet.java
b/zeppelin-server/src/main/java/org/apache/zeppelin/server/IndexHtmlServlet.java
index bf32063a5d..b79149a2ba 100644
---
a/zeppelin-server/src/main/java/org/apache/zeppelin/server/IndexHtmlServlet.java
+++
b/zeppelin-server/src/main/java/org/apache/zeppelin/server/IndexHtmlServlet.java
@@ -46,10 +46,12 @@ public class IndexHtmlServlet extends HttpServlet {
final String bodyAddon;
final String headAddon;
+ final String baseHref;
- public IndexHtmlServlet(ZeppelinConfiguration zConf) {
+ public IndexHtmlServlet(ZeppelinConfiguration zConf, String contextPath) {
this.bodyAddon = zConf.getHtmlBodyAddon();
this.headAddon = zConf.getHtmlHeadAddon();
+ this.baseHref = getBaseHref(contextPath);
}
@Override
@@ -79,6 +81,21 @@ public class IndexHtmlServlet extends HttpServlet {
"Unable to process Head html addon. Could not find proper anchor
in index.html.");
}
}
+ // process base href
+ if (baseHref != null) {
+ String baseTag = "<base href=\".*\">";
+ if (content.matches(baseTag)) {
+ content = content.replaceAll(baseTag, baseHref);
+ } else if (content.contains(TAG_HEAD_CLOSING)) {
+ content = content.replace(TAG_HEAD_CLOSING, baseHref +
TAG_HEAD_CLOSING);
+ } else if (content.contains(TAG_BODY_OPENING)) {
+ content = content.replace(TAG_BODY_OPENING, baseHref +
TAG_BODY_OPENING);
+ } else {
+ LOGGER.error(
+ "Unable to process base href. Could not find proper anchor in
index.html.");
+ }
+ }
+
resp.setContentType("text/html");
resp.setStatus(HttpServletResponse.SC_OK);
resp.getWriter().append(content);
@@ -86,4 +103,14 @@ public class IndexHtmlServlet extends HttpServlet {
LOGGER.error("Error rendering index.html.", e);
}
}
+
+ private String getBaseHref(String contextPath) {
+ if (contextPath == null) {
+ return null;
+ }
+ if (!contextPath.endsWith("/")) {
+ contextPath = contextPath + "/";
+ }
+ return "<base href=\"" + contextPath + "\">";
+ }
}
diff --git
a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
index e41b9b6f94..3a5ef4bc31 100644
---
a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
+++
b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
@@ -62,6 +62,7 @@ import org.apache.shiro.web.env.EnvironmentLoaderListener;
import org.apache.shiro.web.servlet.ShiroFilter;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
+import org.apache.zeppelin.conf.ZeppelinConfiguration.DEFAULT_UI;
import org.apache.zeppelin.display.AngularObjectRegistryListener;
import org.apache.zeppelin.healthcheck.HealthChecks;
import org.apache.zeppelin.helium.ApplicationEventListener;
@@ -127,7 +128,8 @@ import org.slf4j.LoggerFactory;
/** Main class of Zeppelin. */
public class ZeppelinServer implements AutoCloseable {
private static final Logger LOGGER =
LoggerFactory.getLogger(ZeppelinServer.class);
- private static final String WEB_APP_CONTEXT_CLASSIC = "/classic";
+ private static final String NON_DEFAULT_NEW_UI_WEB_APP_CONTEXT_PATH = "/new";
+ private static final String NON_DEFAULT_CLASSIC_UI_WEB_APP_CONTEXT_PATH =
"/classic";
public static final String DEFAULT_SERVICE_LOCATOR_NAME = "shared-locator";
private final AtomicBoolean duringShutdown = new AtomicBoolean(false);
@@ -221,11 +223,22 @@ public class ZeppelinServer implements AutoCloseable {
});
// Multiple Web UI
- final WebAppContext defaultWebApp = setupWebAppContext(contexts, zConf,
zConf.getString(ConfVars.ZEPPELIN_ANGULAR_WAR), zConf.getServerContextPath());
- final WebAppContext classicWebApp = setupWebAppContext(contexts, zConf,
zConf.getString(ConfVars.ZEPPELIN_WAR), WEB_APP_CONTEXT_CLASSIC);
+ String classicUiWebAppContextPath;
+ String newUiWebAppContextPath;
+ if (isNewUiDefault(zConf)) {
+ classicUiWebAppContextPath = NON_DEFAULT_CLASSIC_UI_WEB_APP_CONTEXT_PATH;
+ newUiWebAppContextPath = zConf.getServerContextPath();
+ } else {
+ classicUiWebAppContextPath = zConf.getServerContextPath();
+ newUiWebAppContextPath = NON_DEFAULT_NEW_UI_WEB_APP_CONTEXT_PATH;
+ }
+ final WebAppContext newUiWebApp = setupWebAppContext(contexts, zConf,
zConf.getString(ConfVars.ZEPPELIN_ANGULAR_WAR),
+ newUiWebAppContextPath);
+ final WebAppContext classicUiWebApp = setupWebAppContext(contexts, zConf,
zConf.getString(ConfVars.ZEPPELIN_WAR),
+ classicUiWebAppContextPath);
- initWebApp(defaultWebApp);
- initWebApp(classicWebApp);
+ initWebApp(newUiWebApp);
+ initWebApp(classicUiWebApp);
NotebookRepo repo =
ServiceLocatorUtilities.getService(sharedServiceLocator,
NotebookRepo.class.getName());
@@ -592,7 +605,7 @@ public class ZeppelinServer implements AutoCloseable {
webApp.setTempDirectory(warTempDirectory);
}
// Explicit bind to root
- webApp.addServlet(new ServletHolder(new IndexHtmlServlet(zConf)),
"/index.html");
+ webApp.addServlet(new ServletHolder(new IndexHtmlServlet(zConf,
contextPath)), "/index.html");
contexts.addHandler(webApp);
webApp.addFilter(new FilterHolder(new CorsFilter(zConf)), "/*",
EnumSet.allOf(DispatcherType.class));
@@ -629,6 +642,10 @@ public class ZeppelinServer implements AutoCloseable {
setupNotebookServer(webApp);
}
+ private static boolean isNewUiDefault(ZeppelinConfiguration zConf) {
+ return zConf.getDefaultUi() == DEFAULT_UI.NEW;
+ }
+
@Override
public void close() throws Exception {
shutdown();
diff --git
a/zeppelin-server/src/test/java/org/apache/zeppelin/server/IndexHtmlServletTest.java
b/zeppelin-server/src/test/java/org/apache/zeppelin/server/IndexHtmlServletTest.java
index 76d8b8d711..3bab703eda 100644
---
a/zeppelin-server/src/test/java/org/apache/zeppelin/server/IndexHtmlServletTest.java
+++
b/zeppelin-server/src/test/java/org/apache/zeppelin/server/IndexHtmlServletTest.java
@@ -56,7 +56,7 @@ class IndexHtmlServletTest {
.thenReturn(new URL("file:" + FILE_PATH_INDEX_HTML_ZEPPELIN_WEB));
when(sc.getServletContext()).thenReturn(ctx);
- IndexHtmlServlet servlet = new IndexHtmlServlet(zConf);
+ IndexHtmlServlet servlet = new IndexHtmlServlet(zConf, null);
servlet.init(sc);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
@@ -90,7 +90,7 @@ class IndexHtmlServletTest {
.thenReturn(new URL("file:" +
FILE_PATH_INDEX_HTML_ZEPPELIN_WEB_ANGULAR));
when(sc.getServletContext()).thenReturn(ctx);
- IndexHtmlServlet servlet = new IndexHtmlServlet(zConf);
+ IndexHtmlServlet servlet = new IndexHtmlServlet(zConf, null);
servlet.init(sc);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
diff --git a/zeppelin-web-angular/package.json
b/zeppelin-web-angular/package.json
index a9dc4f4a53..383fe3d3ce 100644
--- a/zeppelin-web-angular/package.json
+++ b/zeppelin-web-angular/package.json
@@ -5,7 +5,7 @@
"postinstall": "npm run build:projects",
"ng": "./node_modules/.bin/ng",
"start": "ng serve --proxy-config proxy.conf.js --extra-webpack-config
webpack.partial.js",
- "build": "ng build --prod --extra-webpack-config webpack.partial.js
--base-href /",
+ "build": "ng build --prod --extra-webpack-config webpack.partial.js",
"build:projects": "npm run build-project:sdk && npm run build-project:vis
&& npm run build-project:helium",
"build-helium-vis-example": " ng build --project helium-vis-example",
"build-project:sdk": " ng build --project zeppelin-sdk",
diff --git a/zeppelin-web-angular/src/app/share/header/header.component.html
b/zeppelin-web-angular/src/app/share/header/header.component.html
index 55b659f1f0..65376b617d 100644
--- a/zeppelin-web-angular/src/app/share/header/header.component.html
+++ b/zeppelin-web-angular/src/app/share/header/header.component.html
@@ -67,7 +67,7 @@
<li nz-menu-item (click)="logout()">Logout</li>
</ng-container>
<li nz-menu-divider></li>
- <li nz-menu-item><a href="/classic">Switch to Classic UI</a></li>
+ <li nz-menu-item><a [href]="classicUiHref">Switch to Classic
UI</a></li>
</ul>
</nz-dropdown-menu>
</div>
diff --git a/zeppelin-web-angular/src/app/share/header/header.component.ts
b/zeppelin-web-angular/src/app/share/header/header.component.ts
index b4c20c242c..ce0bc901b7 100644
--- a/zeppelin-web-angular/src/app/share/header/header.component.ts
+++ b/zeppelin-web-angular/src/app/share/header/header.component.ts
@@ -34,6 +34,7 @@ export class HeaderComponent extends MessageListenersManager
implements OnInit,
connectStatus = 'error';
noteListVisible = false;
queryStr: string | null = null;
+ classicUiHref: string;
about() {
this.nzModalService.create({
@@ -69,6 +70,7 @@ export class HeaderComponent extends MessageListenersManager
implements OnInit,
private cdr: ChangeDetectorRef
) {
super(messageService);
+ this.classicUiHref = this.resolveClassicUiHref();
}
ngOnInit() {
@@ -98,4 +100,12 @@ export class HeaderComponent extends
MessageListenersManager implements OnInit,
this.destroy$.complete();
super.ngOnDestroy();
}
+
+ private resolveClassicUiHref() {
+ if (location.pathname === '/') {
+ return '/classic';
+ } else {
+ return '/';
+ }
+ }
}
diff --git a/zeppelin-web-angular/src/index.html
b/zeppelin-web-angular/src/index.html
index 9ad5caa094..f0c70594df 100644
--- a/zeppelin-web-angular/src/index.html
+++ b/zeppelin-web-angular/src/index.html
@@ -17,7 +17,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="./assets/images/zeppelin.png" type="image/x-icon">
<title>Zeppelin</title>
- <base href="/">
</head>
<body>
<zeppelin-root>
@@ -44,6 +43,14 @@
"HTML-CSS": { availableFonts: ["TeX"] },
messageStyle: "none"
}
+ const origin = window.location.origin;
+ const pathname = window.location.pathname;
+ config.root = window.location.origin + (
+ // remove trailing slash
+ pathname.charAt(pathname.length - 1) === '/' ?
+ pathname.slice(0, -1) :
+ pathname
+ );
MathJax.Hub.Config(config);
</script>
</body>
diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js
b/zeppelin-web/src/components/navbar/navbar.controller.js
index 9713e50207..503b9cd9aa 100644
--- a/zeppelin-web/src/components/navbar/navbar.controller.js
+++ b/zeppelin-web/src/components/navbar/navbar.controller.js
@@ -270,6 +270,14 @@ function NavCtrl($scope, $rootScope, $http, $routeParams,
$location,
}
});
+ $scope.resolveNewUiHref = function() {
+ if (location.pathname === '/') {
+ return '/new';
+ } else {
+ return '/';
+ }
+ };
+
$rootScope.isRevisionSupported = function() {
return revisionSupported;
};
diff --git a/zeppelin-web/src/components/navbar/navbar.html
b/zeppelin-web/src/components/navbar/navbar.html
index 990da03cd5..cfda971f71 100644
--- a/zeppelin-web/src/components/navbar/navbar.html
+++ b/zeppelin-web/src/components/navbar/navbar.html
@@ -106,7 +106,7 @@ limitations under the License.
<li ng-if="ticket.principal && ticket.principal !== 'anonymous'"
role="separator" style="margin: 5px 0;" class="divider"></li>
<li ng-if="ticket.principal && ticket.principal !==
'anonymous'"><a ng-click="navbar.logout()">Logout</a></li>
<li role="separator" style="margin: 5px 0;" class="divider"></li>
- <li><a href="/">Switch to Default UI</a></li>
+ <li><a ng-href="{{ resolveNewUiHref() }}">Switch to Default
UI</a></li>
</ul>
</div>
</li>
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index a149af0db6..9cb60c3f82 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -159,7 +159,14 @@ limitations under the License.
}
// add root only if it's not dev mode
if (Number(location.port) !== WEB_PORT) {
- config.root = '.';
+ const origin = window.location.origin;
+ const pathname = window.location.pathname;
+ config.root = window.location.origin + (
+ // remove trailing slash
+ pathname.charAt(pathname.length - 1) === '/' ?
+ pathname.slice(0, -1) :
+ pathname
+ );
}
MathJax.Hub.Config(config);
</script>