aboutsummaryrefslogtreecommitdiff
path: root/solr-8.3.1/server/solr-webapp/webapp/js/angular
diff options
context:
space:
mode:
authorkimdahey <claire_kim1@brown.edu>2020-01-16 11:31:41 -0500
committerkimdahey <claire_kim1@brown.edu>2020-01-16 11:31:41 -0500
commit6be0e19ed0bd13f3796f542affa5a2e52674650c (patch)
tree1be222ea9341ecd8020fad3149035fa650a8a07f /solr-8.3.1/server/solr-webapp/webapp/js/angular
parent5cde81d8c6b4dcd8d0796f8669b668763957f395 (diff)
parente410cde0e430553002d4e1a2f64364b57b65fdbc (diff)
merged w master
Diffstat (limited to 'solr-8.3.1/server/solr-webapp/webapp/js/angular')
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/app.js561
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js27
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js201
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js1021
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js62
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js39
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js289
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js93
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js180
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js302
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js137
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js100
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js97
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js45
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js158
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js317
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js167
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js120
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js235
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js611
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js99
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js239
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js50
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js37
-rw-r--r--solr-8.3.1/server/solr-webapp/webapp/js/angular/services.js339
25 files changed, 5526 insertions, 0 deletions
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/app.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/app.js
new file mode 100644
index 000000000..784ea6e41
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/app.js
@@ -0,0 +1,561 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+var solrAdminApp = angular.module("solrAdminApp", [
+ "ngResource",
+ "ngRoute",
+ "ngCookies",
+ "ngtimeago",
+ "solrAdminServices",
+ "localytics.directives",
+ "ab-base64"
+]);
+
+solrAdminApp.config([
+ '$routeProvider', function($routeProvider) {
+ $routeProvider.
+ when('/', {
+ templateUrl: 'partials/index.html',
+ controller: 'IndexController'
+ }).
+ when('/unknown', {
+ templateUrl: 'partials/unknown.html',
+ controller: 'UnknownController'
+ }).
+ when('/login', {
+ templateUrl: 'partials/login.html',
+ controller: 'LoginController'
+ }).
+ when('/login/:route', {
+ templateUrl: 'partials/login.html',
+ controller: 'LoginController'
+ }).
+ when('/~logging', {
+ templateUrl: 'partials/logging.html',
+ controller: 'LoggingController'
+ }).
+ when('/~logging/level', {
+ templateUrl: 'partials/logging-levels.html',
+ controller: 'LoggingLevelController'
+ }).
+ when('/~cloud', {
+ templateUrl: 'partials/cloud.html',
+ controller: 'CloudController'
+ }).
+ when('/~cores', {
+ templateUrl: 'partials/cores.html',
+ controller: 'CoreAdminController'
+ }).
+ when('/~cores/:corename', {
+ templateUrl: 'partials/cores.html',
+ controller: 'CoreAdminController'
+ }).
+ when('/~collections', {
+ templateUrl: 'partials/collections.html',
+ controller: 'CollectionsController'
+ }).
+ when('/~collections/:collection', {
+ templateUrl: 'partials/collections.html',
+ controller: 'CollectionsController'
+ }).
+ when('/~threads', {
+ templateUrl: 'partials/threads.html',
+ controller: 'ThreadsController'
+ }).
+ when('/~java-properties', {
+ templateUrl: 'partials/java-properties.html',
+ controller: 'JavaPropertiesController'
+ }).
+ when('/~cluster-suggestions', {
+ templateUrl: 'partials/cluster_suggestions.html',
+ controller: 'ClusterSuggestionsController'
+ }).
+ when('/:core/core-overview', {
+ templateUrl: 'partials/core_overview.html',
+ controller: 'CoreOverviewController'
+ }).
+ when('/:core/alias-overview', {
+ templateUrl: 'partials/alias_overview.html',
+ controller: 'AliasOverviewController'
+ }).
+ when('/:core/collection-overview', {
+ templateUrl: 'partials/collection_overview.html',
+ controller: 'CollectionOverviewController'
+ }).
+ when('/:core/analysis', {
+ templateUrl: 'partials/analysis.html',
+ controller: 'AnalysisController'
+ }).
+ when('/:core/dataimport', {
+ templateUrl: 'partials/dataimport.html',
+ controller: 'DataImportController'
+ }).
+ when('/:core/dataimport/:handler*', {
+ templateUrl: 'partials/dataimport.html',
+ controller: 'DataImportController'
+ }).
+ when('/:core/documents', {
+ templateUrl: 'partials/documents.html',
+ controller: 'DocumentsController'
+ }).
+ when('/:core/files', {
+ templateUrl: 'partials/files.html',
+ controller: 'FilesController'
+ }).
+ when('/:core/plugins', {
+ templateUrl: 'partials/plugins.html',
+ controller: 'PluginsController',
+ reloadOnSearch: false
+ }).
+ when('/:core/plugins/:legacytype', {
+ templateUrl: 'partials/plugins.html',
+ controller: 'PluginsController',
+ reloadOnSearch: false
+ }).
+ when('/:core/query', {
+ templateUrl: 'partials/query.html',
+ controller: 'QueryController'
+ }).
+ when('/:core/stream', {
+ templateUrl: 'partials/stream.html',
+ controller: 'StreamController'
+ }).
+ when('/:core/replication', {
+ templateUrl: 'partials/replication.html',
+ controller: 'ReplicationController'
+ }).
+ when('/:core/dataimport', {
+ templateUrl: 'partials/dataimport.html',
+ controller: 'DataImportController'
+ }).
+ when('/:core/dataimport/:handler*', {
+ templateUrl: 'partials/dataimport.html',
+ controller: 'DataImportController'
+ }).
+ when('/:core/schema', {
+ templateUrl: 'partials/schema.html',
+ controller: 'SchemaController'
+ }).
+ when('/:core/segments', {
+ templateUrl: 'partials/segments.html',
+ controller: 'SegmentsController'
+ }).
+ otherwise({
+ templateUrl: 'partials/unknown.html',
+ controller: 'UnknownController'
+ });
+}])
+.constant('Constants', {
+ IS_ROOT_PAGE: 1,
+ IS_CORE_PAGE: 2,
+ IS_COLLECTION_PAGE: 3,
+ ROOT_URL: "/"
+})
+.filter('uriencode', function() {
+ return window.encodeURIComponent;
+})
+.filter('highlight', function($sce) {
+ return function(input, lang) {
+ if (lang && input && lang!="txt" && lang!="csv") return hljs.highlight(lang, input).value;
+ return input;
+ }
+})
+.filter('unsafe', function($sce) { return $sce.trustAsHtml; })
+.directive('loadingStatusMessage', function() {
+ return {
+ link: function($scope, $element, attrs) {
+ var show = function() {$element.css('display', 'block')};
+ var hide = function() {$element.css('display', 'none')};
+ $scope.$on('loadingStatusActive', show);
+ $scope.$on('loadingStatusInactive', hide);
+ }
+ };
+})
+.directive('escapePressed', function () {
+ return function (scope, element, attrs) {
+ element.bind("keydown keypress", function (event) {
+ if(event.which === 27) {
+ scope.$apply(function (){
+ scope.$eval(attrs.escapePressed);
+ });
+ event.preventDefault();
+ }
+ });
+ };
+})
+.directive('focusWhen', function($timeout) {
+ return {
+ link: function(scope, element, attrs) {
+ scope.$watch(attrs.focusWhen, function(value) {
+ if(value === true) {
+ $timeout(function() {
+ element[0].focus();
+ }, 100);
+ }
+ });
+ }
+ };
+})
+.directive('scrollableWhenSmall', function($window) {
+ return {
+ link: function(scope, element, attrs) {
+ var w = angular.element($window);
+
+ var checkFixedMenu = function() {
+ var shouldScroll = w.height() < (element.height() + $('#header').height() + 40);
+ element.toggleClass( 'scroll', shouldScroll);
+ };
+ w.bind('resize', checkFixedMenu);
+ w.bind('load', checkFixedMenu);
+ }
+ }
+})
+.filter('readableSeconds', function() {
+ return function(input) {
+ seconds = parseInt(input||0, 10);
+ var minutes = Math.floor( seconds / 60 );
+ var hours = Math.floor( minutes / 60 );
+
+ var text = [];
+ if( 0 !== hours ) {
+ text.push( hours + 'h' );
+ seconds -= hours * 60 * 60;
+ minutes -= hours * 60;
+ }
+
+ if( 0 !== minutes ) {
+ text.push( minutes + 'm' );
+ seconds -= minutes * 60;
+ }
+
+ if( 0 !== seconds ) {
+ text.push( ( '0' + seconds ).substr( -2 ) + 's' );
+ }
+ return text.join(' ');
+ };
+})
+.filter('number', function($locale) {
+ return function(input) {
+ var sep = {
+ 'de_CH' : '\'',
+ 'de' : '.',
+ 'en' : ',',
+ 'es' : '.',
+ 'it' : '.',
+ 'ja' : ',',
+ 'sv' : ' ',
+ 'tr' : '.',
+ '_' : '' // fallback
+ };
+
+ var browser = {};
+ var match = $locale.id.match( /^(\w{2})([-_](\w{2}))?$/ );
+ if (match[1]) {
+ browser.language = match[1].toLowerCase();
+ }
+ if (match[1] && match[3]) {
+ browser.locale = match[1] + '_' + match[3];
+ }
+
+ return ( input || 0 ).toString().replace(/\B(?=(\d{3})+(?!\d))/g,
+ sep[ browser.locale ] || sep[ browser.language ] || sep['_']);
+ };
+})
+.filter('orderObjectBy', function() {
+ return function(items, field, reverse) {
+ var filtered = [];
+ angular.forEach(items, function(item) {
+ filtered.push(item);
+ });
+ filtered.sort(function (a, b) {
+ return (a[field] > b[field] ? 1 : -1);
+ });
+ if(reverse) filtered.reverse();
+ return filtered;
+ };
+})
+.directive('jstree', function($parse) {
+ return {
+ restrict: 'EA',
+ scope: {
+ data: '=',
+ onSelect: '&'
+ },
+ link: function(scope, element, attrs) {
+ scope.$watch("data", function(newValue, oldValue) {
+ if (newValue) {
+ var treeConfig = {
+ "plugins" : [ "themes", "json_data", "ui" ],
+ "json_data" : {
+ "data" : scope.data,
+ "progressive_render" : true
+ },
+ "core" : {
+ "animation" : 0
+ }
+ };
+
+ var tree = $(element).jstree(treeConfig);
+ tree.jstree('open_node','li:first');
+ if (tree) {
+ element.bind("select_node.jstree", function (event, data) {
+ scope.$apply(function() {
+ scope.onSelect({url: data.args[0].href, data: data});
+ });
+ });
+ }
+ }
+ }, true);
+ }
+ };
+})
+.directive('connectionMessage', function() {
+ return {
+ link: function($scope, $element, attrs) {
+ var show = function() {$element.css('display', 'block')};
+ var hide = function() {$element.css('display', 'none')};
+ $scope.$on('connectionStatusActive', show);
+ $scope.$on('connectionStatusInactive', hide);
+ }
+ };
+})
+.factory('httpInterceptor', function($q, $rootScope, $location, $timeout, $injector) {
+ var activeRequests = 0;
+
+ var started = function(config) {
+ if (activeRequests == 0) {
+ $rootScope.$broadcast('loadingStatusActive');
+ }
+ if ($rootScope.exceptions[config.url]) {
+ delete $rootScope.exceptions[config.url];
+ }
+ activeRequests++;
+ if (sessionStorage.getItem("auth.header")) {
+ config.headers['Authorization'] = sessionStorage.getItem("auth.header");
+ }
+ config.timeout = 10000;
+ return config || $q.when(config);
+ };
+
+ var ended = function(response) {
+ activeRequests--;
+ if (activeRequests == 0) {
+ $rootScope.$broadcast('loadingStatusInactive');
+ }
+ if ($rootScope.retryCount>0) {
+ $rootScope.connectionRecovered = true;
+ $rootScope.retryCount=0;
+ $timeout(function() {
+ $rootScope.connectionRecovered=false;
+ $rootScope.$broadcast('connectionStatusInactive');
+ },2000);
+ }
+ if (!$location.path().startsWith('/login') && !$location.path().startsWith('/unknown')) {
+ sessionStorage.removeItem("http401");
+ sessionStorage.removeItem("auth.state");
+ sessionStorage.removeItem("auth.statusText");
+ }
+ return response || $q.when(response);
+ };
+
+ var failed = function(rejection) {
+ activeRequests--;
+ if (activeRequests == 0) {
+ $rootScope.$broadcast('loadingStatusInactive');
+ }
+ if (rejection.config.headers.doNotIntercept) {
+ return rejection;
+ }
+ if (rejection.status === 0) {
+ $rootScope.$broadcast('connectionStatusActive');
+ if (!$rootScope.retryCount) $rootScope.retryCount=0;
+ $rootScope.retryCount ++;
+ var $http = $injector.get('$http');
+ var result = $http(rejection.config);
+ return result;
+ } else if (rejection.status === 401) {
+ // Authentication redirect
+ var headers = rejection.headers();
+ var wwwAuthHeader = headers['www-authenticate'];
+ sessionStorage.setItem("auth.wwwAuthHeader", wwwAuthHeader);
+ sessionStorage.setItem("auth.authDataHeader", headers['x-solr-authdata']);
+ sessionStorage.setItem("auth.statusText", rejection.statusText);
+ sessionStorage.setItem("http401", "true");
+ sessionStorage.removeItem("auth.scheme");
+ sessionStorage.removeItem("auth.realm");
+ sessionStorage.removeItem("auth.username");
+ sessionStorage.removeItem("auth.header");
+ sessionStorage.removeItem("auth.state");
+ if ($location.path().includes('/login')) {
+ if (!sessionStorage.getItem("auth.location")) {
+ sessionStorage.setItem("auth.location", "/");
+ }
+ } else {
+ sessionStorage.setItem("auth.location", $location.path());
+ $location.path('/login');
+ }
+ } else {
+ $rootScope.exceptions[rejection.config.url] = rejection.data.error;
+ }
+ return $q.reject(rejection);
+ };
+
+ return {request: started, response: ended, responseError: failed};
+})
+.config(function($httpProvider) {
+ $httpProvider.interceptors.push("httpInterceptor");
+ // Force BasicAuth plugin to serve us a 'Authorization: xBasic xxxx' header so browser will not pop up login dialogue
+ $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
+})
+.directive('fileModel', function ($parse) {
+ return {
+ restrict: 'A',
+ link: function(scope, element, attrs) {
+ var model = $parse(attrs.fileModel);
+ var modelSetter = model.assign;
+
+ element.bind('change', function(){
+ scope.$apply(function(){
+ modelSetter(scope, element[0].files[0]);
+ });
+ });
+ }
+ };
+});
+
+solrAdminApp.controller('MainController', function($scope, $route, $rootScope, $location, Cores, Collections, System, Ping, Constants) {
+
+ $rootScope.exceptions={};
+
+ $rootScope.toggleException = function() {
+ $scope.showException=!$scope.showException;
+ };
+
+ $scope.refresh = function() {
+ $scope.cores = [];
+ $scope.collections = [];
+ $scope.aliases = [];
+ }
+
+ $scope.refresh();
+ $scope.resetMenu = function(page, pageType) {
+ Cores.list(function(data) {
+ $scope.cores = [];
+ var currentCoreName = $route.current.params.core;
+ delete $scope.currentCore;
+ for (key in data.status) {
+ var core = data.status[key];
+ $scope.cores.push(core);
+ if ((!$scope.isSolrCloud || pageType == Constants.IS_CORE_PAGE) && core.name == currentCoreName) {
+ $scope.currentCore = core;
+ }
+ }
+ $scope.showInitFailures = Object.keys(data.initFailures).length>0;
+ $scope.initFailures = data.initFailures;
+ });
+
+ System.get(function(data) {
+ $scope.isCloudEnabled = data.mode.match( /solrcloud/i );
+
+ var currentCollectionName = $route.current.params.core;
+ delete $scope.currentCollection;
+ if ($scope.isCloudEnabled) {
+ Collections.list(function (cdata) {
+ Collections.listaliases(function (adata) {
+ $scope.aliases = [];
+ for (var key in adata.aliases) {
+ props = {};
+ if (key in adata.properties) {
+ props = adata.properties[key];
+ }
+ var alias = {name: key, collections: adata.aliases[key], type: 'alias', properties: props};
+ $scope.aliases.push(alias);
+ if (pageType == Constants.IS_COLLECTION_PAGE && alias.name == currentCollectionName) {
+ $scope.currentCollection = alias;
+ }
+ }
+ $scope.collections = [];
+ for (key in cdata.collections) {
+ var collection = {name: cdata.collections[key], type: 'collection'};
+ $scope.collections.push(collection);
+ if (pageType == Constants.IS_COLLECTION_PAGE && collection.name == currentCollectionName) {
+ $scope.currentCollection = collection;
+ }
+ }
+
+ $scope.aliases_and_collections = $scope.aliases;
+ if ($scope.aliases.length > 0) {
+ $scope.aliases_and_collections = $scope.aliases_and_collections.concat({name:'-----'});
+ }
+ $scope.aliases_and_collections = $scope.aliases_and_collections.concat($scope.collections);
+ });
+ });
+ }
+
+ $scope.showEnvironment = data.environment !== undefined;
+ if (data.environment) {
+ $scope.environment = data.environment;
+ var env_labels = {'prod': 'Production', 'stage': 'Staging', 'test': 'Test', 'dev': 'Development'};
+ $scope.environment_label = env_labels[data.environment];
+ if (data.environment_label) {
+ $scope.environment_label = data.environment_label;
+ }
+ if (data.environment_color) {
+ $scope.environment_color = data.environment_color;
+ }
+ }
+ });
+
+ $scope.showingLogging = page.lastIndexOf("logging", 0) === 0;
+ $scope.showingCloud = page.lastIndexOf("cloud", 0) === 0;
+ $scope.page = page;
+ $scope.currentUser = sessionStorage.getItem("auth.username");
+ $scope.http401 = sessionStorage.getItem("http401");
+ };
+
+ $scope.isMultiDestAlias = function(selectedColl) {
+ return selectedColl && selectedColl.type === 'alias' && selectedColl.collections.includes(',');
+ };
+
+ $scope.ping = function() {
+ Ping.ping({core: $scope.currentCore.name}, function(data) {
+ $scope.showPing = true;
+ $scope.pingMS = data.responseHeader.QTime;
+ });
+ // @todo .attr( 'title', '/admin/ping is not configured (' + xhr.status + ': ' + error_thrown + ')' );
+ };
+
+ $scope.dumpCloud = function() {
+ $scope.$broadcast("cloud-dump");
+ }
+
+ $scope.showCore = function(core) {
+ $location.url("/" + core.name + "/core-overview");
+ }
+
+ $scope.showCollection = function(collection) {
+ if (collection.type === 'collection') {
+ $location.url("/" + collection.name + "/collection-overview")
+ } else if (collection.type === 'alias') {
+ $location.url("/" + collection.name + "/alias-overview")
+ }
+ };
+
+ $scope.$on('$routeChangeStart', function() {
+ $rootScope.exceptions = {};
+ });
+});
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js
new file mode 100644
index 000000000..6fbca386f
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js
@@ -0,0 +1,27 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('AliasOverviewController',
+function($scope, $routeParams, Collections, Constants) {
+ $scope.resetMenu("collection-overview", Constants.IS_COLLECTION_PAGE);
+
+ $scope.refresh = function() {
+ $scope.selectedCollection = $scope.currentCollection;
+ };
+
+ $scope.refresh();
+});
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js
new file mode 100644
index 000000000..5fff59caa
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js
@@ -0,0 +1,201 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('AnalysisController',
+ function($scope, $location, $routeParams, Luke, Analysis, Constants) {
+ $scope.resetMenu("analysis", Constants.IS_COLLECTION_PAGE);
+
+ $scope.refresh = function() {
+ Luke.schema({core: $routeParams.core}, function(data) {
+ $scope.fieldsAndTypes = [];
+ for (var field in data.schema.fields) {
+ $scope.fieldsAndTypes.push({
+ group: "Fields",
+ value: "fieldname=" + field,
+ label: field});
+ }
+ for (var type in data.schema.types) {
+ $scope.fieldsAndTypes.push({
+ group: "Types",
+ value: "fieldtype=" + type,
+ label: type});
+ }
+ $scope.core = $routeParams.core;
+ });
+
+ $scope.parseQueryString();
+ // @todo - set defaultSearchField either to context["analysis.fieldname"] or context["analysis.fieldtype"]
+
+ };
+ $scope.verbose = true;
+
+ var getShortComponentName = function(longname) {
+ var short = -1 !== longname.indexOf( '$' )
+ ? longname.split( '$' )[1]
+ : longname.split( '.' ).pop();
+ return short.match( /[A-Z]/g ).join( '' );
+ };
+
+ var getCaptionsForComponent = function(data) {
+ var captions = [];
+ for (var key in data[0]) {
+ key = key.replace(/.*#/,'');
+ if (key != "match" && key!="positionHistory") {
+ captions.push(key.replace(/.*#/,''));
+ }
+ }
+ return captions;
+ };
+
+ var getTokensForComponent = function(data) {
+ var tokens = [];
+ var previousPosition = 0;
+ var index=0;
+ for (var i in data) {
+ var tokenhash = data[i];
+ var positionDifference = tokenhash.position - previousPosition;
+ for (var j=positionDifference; j>1; j--) {
+ tokens.push({position: tokenhash.position - j+1, blank:true, index:index++});
+ }
+
+ var token = {position: tokenhash.position, keys:[], index:index++};
+
+ for (key in tokenhash) {
+ if (key == "match" || key=="positionHistory") {
+ //skip, to not display these keys in the UI
+ } else {
+ var tokenInfo = new Object();
+ tokenInfo.name = key;
+ tokenInfo.value = tokenhash[key];
+ if ('text' === key || 'raw_bytes' === key ) {
+ if (tokenhash.match) {
+ tokenInfo.extraclass = 'match'; //to highlight matching text strings
+ }
+ }
+ token.keys.push(tokenInfo);
+ }
+ }
+ tokens.push(token);
+ previousPosition = tokenhash.position;
+ }
+ return tokens;
+ };
+
+ var extractComponents = function(data, result, name) {
+ if (data) {
+ result[name] = [];
+ for (var i = 0; i < data.length; i += 2) {
+ var component = {
+ name: data[i],
+ short: getShortComponentName(data[i]),
+ captions: getCaptionsForComponent(data[i + 1]),
+ tokens: getTokensForComponent(data[i + 1])
+ };
+ result[name].push(component);
+ }
+ }
+ };
+
+ var processAnalysisData = function(analysis, fieldOrType) {
+ var fieldname;
+ for (fieldname in analysis[fieldOrType]) {console.log(fieldname);break;}
+ var response = {};
+ extractComponents(analysis[fieldOrType][fieldname].index, response, "index");
+ extractComponents(analysis[fieldOrType][fieldname].query, response, "query");
+ return response;
+ };
+
+ $scope.updateQueryString = function() {
+
+ var parts = $scope.fieldOrType.split("=");
+ var fieldOrType = parts[0];
+ var name = parts[1];
+
+ if ($scope.indexText) {
+ $location.search("analysis.fieldvalue", $scope.indexText);
+ } else if ($location.search()["analysis.fieldvalue"]) {
+ $location.search("analysis.fieldvalue", null);
+ }
+ if ($scope.queryText) {
+ $location.search("analysis.query", $scope.queryText);
+ } else if ($location.search()["analysis.query"]) {
+ $location.search("analysis.query", null);
+ }
+
+ if (fieldOrType == "fieldname") {
+ $location.search("analysis.fieldname", name);
+ $location.search("analysis.fieldtype", null);
+ } else {
+ $location.search("analysis.fieldtype", name);
+ $location.search("analysis.fieldname", null);
+ }
+ $location.search("verbose_output", $scope.verbose ? "1" : "0");
+ };
+
+ $scope.parseQueryString = function () {
+ var params = {};
+ var search = $location.search();
+
+ if (Object.keys(search).length == 0) {
+ return;
+ }
+ for (var key in search) {
+ params[key]=search[key];
+ }
+ $scope.indexText = search["analysis.fieldvalue"];
+ $scope.queryText = search["analysis.query"];
+ if (search["analysis.fieldname"]) {
+ $scope.fieldOrType = "fieldname=" + search["analysis.fieldname"];
+ $scope.schemaBrowserUrl = "field=" + search["analysis.fieldname"];
+ } else {
+ $scope.fieldOrType = "fieldtype=" + search["analysis.fieldtype"];
+ $scope.schemaBrowserUrl = "type=" + search["analysis.fieldtype"];
+ }
+ if (search["verbose_output"] == undefined) {
+ $scope.verbose = true;
+ } else {
+ $scope.verbose = search["verbose_output"] == "1";
+ }
+
+ if ($scope.fieldOrType || $scope.indexText || $scope.queryText) {
+ params.core = $routeParams.core;
+ var parts = $scope.fieldOrType.split("=");
+ var fieldOrType = parts[0] == "fieldname" ? "field_names" : "field_types";
+
+ Analysis.field(params, function(data) {
+ $scope.result = processAnalysisData(data.analysis, fieldOrType);
+ });
+ }
+ };
+
+ $scope.changeFieldOrType = function() {
+ var parts = $scope.fieldOrType.split("=");
+ if (parts[0]=='fieldname') {
+ $scope.schemaBrowserUrl = "field=" + parts[1];
+ } else {
+ $scope.schemaBrowserUrl = "type=" + parts[1];
+ }
+ };
+
+ $scope.toggleVerbose = function() {
+ $scope.verbose = !$scope.verbose;
+ $scope.updateQueryString();
+ };
+
+ $scope.refresh();
+ }
+);
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js
new file mode 100644
index 000000000..b0ba4217d
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js
@@ -0,0 +1,1021 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('CloudController',
+ function($scope, $location, Zookeeper, Constants, Collections, System, Metrics, ZookeeperStatus) {
+
+ $scope.showDebug = false;
+
+ $scope.$on("cloud-dump", function(event) {
+ $scope.showDebug = true;
+ });
+
+ $scope.closeDebug = function() {
+ $scope.showDebug = false;
+ };
+
+ var view = $location.search().view ? $location.search().view : "nodes";
+ if (view === "tree") {
+ $scope.resetMenu("cloud-tree", Constants.IS_ROOT_PAGE);
+ treeSubController($scope, Zookeeper);
+ } else if (view === "graph") {
+ $scope.resetMenu("cloud-graph", Constants.IS_ROOT_PAGE);
+ graphSubController($scope, Zookeeper, false);
+ } else if (view === "nodes") {
+ $scope.resetMenu("cloud-nodes", Constants.IS_ROOT_PAGE);
+ nodesSubController($scope, Collections, System, Metrics);
+ } else if (view === "zkstatus") {
+ $scope.resetMenu("cloud-zkstatus", Constants.IS_ROOT_PAGE);
+ zkStatusSubController($scope, ZookeeperStatus, false);
+ }
+ }
+);
+
+function getOrCreateObj(name, object) {
+ if (name in object) {
+ entry = object[name];
+ } else {
+ entry = {};
+ object[name] = entry;
+ }
+ return entry;
+}
+
+function getOrCreateList(name, object) {
+ if (name in object) {
+ entry = object[name];
+ } else {
+ entry = [];
+ object[name] = entry;
+ }
+ return entry;
+}
+
+function ensureInList(string, list) {
+ if (list.indexOf(string) === -1) {
+ list.push(string);
+ }
+}
+
+/* Puts a node name into the hosts structure */
+function ensureNodeInHosts(node_name, hosts) {
+ var hostName = node_name.split(":")[0];
+ var host = getOrCreateObj(hostName, hosts);
+ var hostNodes = getOrCreateList("nodes", host);
+ ensureInList(node_name, hostNodes);
+}
+
+// from http://scratch99.com/web-development/javascript/convert-bytes-to-mb-kb/
+function bytesToSize(bytes) {
+ var sizes = ['b', 'Kb', 'Mb', 'Gb', 'Tb'];
+ if (bytes === 0) return '0b';
+ var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
+ if (bytes === 0) return bytes + '' + sizes[i];
+ return (bytes / Math.pow(1024, i)).toFixed(1) + '' + sizes[i];
+}
+
+function numDocsHuman(docs) {
+ var sizes = ['', 'k', 'mn', 'bn', 'tn'];
+ if (docs === 0) return '0';
+ var i = parseInt(Math.floor(Math.log(docs) / Math.log(1000)));
+ if (i === 0) return docs + '' + sizes[i];
+ return (docs / Math.pow(1000, i)).toFixed(1) + '' + sizes[i];
+}
+
+/* Returns a style class depending on percentage */
+var styleForPct = function (pct) {
+ if (pct < 60) return "pct-normal";
+ if (pct < 80) return "pct-warn";
+ return "pct-critical"
+};
+
+function isNumeric(n) {
+ return !isNaN(parseFloat(n)) && isFinite(n);
+}
+
+function coreNameToLabel(name) {
+ return name.replace(/(.*?)_shard((\d+_?)+)_replica_?[ntp]?(\d+)/, '\$1_s\$2r\$4');
+}
+
+var nodesSubController = function($scope, Collections, System, Metrics) {
+ $scope.pageSize = 10;
+ $scope.showNodes = true;
+ $scope.showTree = false;
+ $scope.showGraph = false;
+ $scope.showData = false;
+ $scope.showAllDetails = false;
+ $scope.showDetails = {};
+ $scope.from = 0;
+ $scope.to = $scope.pageSize - 1;
+ $scope.filterType = "node"; // Pre-initialize dropdown
+
+ $scope.toggleAllDetails = function() {
+ $scope.showAllDetails = !$scope.showAllDetails;
+ for (var node in $scope.nodes) {
+ $scope.showDetails[node] = $scope.showAllDetails;
+ }
+ for (var host in $scope.hosts) {
+ $scope.showDetails[host] = $scope.showAllDetails;
+ }
+ };
+
+ $scope.toggleDetails = function(key) {
+ $scope.showDetails[key] = !$scope.showDetails[key] === true;
+ };
+
+ $scope.toggleHostDetails = function(key) {
+ $scope.showDetails[key] = !$scope.showDetails[key] === true;
+ for (var nodeId in $scope.hosts[key].nodes) {
+ var node = $scope.hosts[key].nodes[nodeId];
+ $scope.showDetails[node] = $scope.showDetails[key];
+ }
+ };
+
+ $scope.nextPage = function() {
+ $scope.from += parseInt($scope.pageSize);
+ $scope.reload();
+ };
+
+ $scope.previousPage = function() {
+ $scope.from = Math.max(0, $scope.from - parseInt($scope.pageSize));
+ $scope.reload();
+ };
+
+ // Checks if this node is the first (alphabetically) for a given host. Used to decide rowspan in table
+ $scope.isFirstNodeForHost = function(node) {
+ var hostName = node.split(":")[0];
+ var nodesInHost = $scope.filteredNodes.filter(function (node) {
+ return node.startsWith(hostName);
+ });
+ return nodesInHost[0] === node;
+ };
+
+ // Returns the first live node for this host, to make sure we pick host-level metrics from a live node
+ $scope.firstLiveNodeForHost = function(key) {
+ var hostName = key.split(":")[0];
+ var liveNodesInHost = $scope.filteredNodes.filter(function (key) {
+ return key.startsWith(hostName);
+ }).filter(function (key) {
+ return $scope.live_nodes.includes(key);
+ });
+ return liveNodesInHost.length > 0 ? liveNodesInHost[0] : key;
+ };
+
+ // Initializes the cluster state, list of nodes, collections etc
+ $scope.initClusterState = function() {
+ var nodes = {};
+ var hosts = {};
+ var live_nodes = [];
+
+ // We build a node-centric view of the cluster state which we can easily consume to render the table
+ Collections.status(function (data) {
+ // Fetch cluster state from collections API and invert to a nodes structure
+ for (var name in data.cluster.collections) {
+ var collection = data.cluster.collections[name];
+ collection.name = name;
+ var shards = collection.shards;
+ collection.shards = [];
+ for (var shardName in shards) {
+ var shard = shards[shardName];
+ shard.name = shardName;
+ shard.collection = collection.name;
+ var replicas = shard.replicas;
+ shard.replicas = [];
+ for (var replicaName in replicas) {
+ var core = replicas[replicaName];
+ core.name = replicaName;
+ core.label = coreNameToLabel(core['core']);
+ core.collection = collection.name;
+ core.shard = shard.name;
+ core.shard_state = shard.state;
+
+ var node_name = core['node_name'];
+ var node = getOrCreateObj(node_name, nodes);
+ var cores = getOrCreateList("cores", node);
+ cores.push(core);
+ node['base_url'] = core.base_url;
+ node['id'] = core.base_url.replace(/[^\w\d]/g, '');
+ node['host'] = node_name.split(":")[0];
+ var collections = getOrCreateList("collections", node);
+ ensureInList(core.collection, collections);
+ ensureNodeInHosts(node_name, hosts);
+ }
+ }
+ }
+
+ live_nodes = data.cluster.live_nodes;
+ for (n in data.cluster.live_nodes) {
+ node = data.cluster.live_nodes[n];
+ if (!(node in nodes)) {
+ var hostName = node.split(":")[0];
+ nodes[node] = {};
+ nodes[node]['host'] = hostName;
+ }
+ ensureNodeInHosts(node, hosts);
+ }
+
+ // Make sure nodes are sorted alphabetically to align with rowspan in table
+ for (var host in hosts) {
+ hosts[host].nodes.sort();
+ }
+
+ $scope.nodes = nodes;
+ $scope.hosts = hosts;
+ $scope.live_nodes = live_nodes;
+
+ $scope.Math = window.Math;
+ $scope.reload();
+ });
+ };
+
+ $scope.filterInput = function() {
+ $scope.from = 0;
+ $scope.to = $scope.pageSize - 1;
+ $scope.reload();
+ };
+
+ /*
+ Reload will fetch data for the current page of the table and thus refresh numbers.
+ It is also called whenever a filter or paging action is executed
+ */
+ $scope.reload = function() {
+ var nodes = $scope.nodes;
+ var node_keys = Object.keys(nodes);
+ var hosts = $scope.hosts;
+ var live_nodes = $scope.live_nodes;
+ var hostNames = Object.keys(hosts);
+ hostNames.sort();
+ var pageSize = isNumeric($scope.pageSize) ? $scope.pageSize : 10;
+
+ // Calculate what nodes that will show on this page
+ var nodesToShow = [];
+ var nodesParam;
+ var hostsToShow = [];
+ var filteredNodes;
+ var filteredHosts;
+ var isFiltered = false;
+ switch ($scope.filterType) {
+ case "node": // Find what nodes match the node filter
+ if ($scope.nodeFilter) {
+ filteredNodes = node_keys.filter(function (node) {
+ return node.indexOf($scope.nodeFilter) !== -1;
+ });
+ }
+ break;
+
+ case "collection": // Find what collections match the collection filter and what nodes that have these collections
+ if ($scope.collectionFilter) {
+ candidateNodes = {};
+ nodesCollections = [];
+ for (var i = 0 ; i < node_keys.length ; i++) {
+ var node_name = node_keys[i];
+ var node = nodes[node_name];
+ nodeColl = {};
+ nodeColl['node'] = node_name;
+ collections = {};
+ node.cores.forEach(function(core) {
+ collections[core.collection] = true;
+ });
+ nodeColl['collections'] = Object.keys(collections);
+ nodesCollections.push(nodeColl);
+ }
+ nodesCollections.forEach(function(nc) {
+ matchingColls = nc['collections'].filter(function (collection) {
+ return collection.indexOf($scope.collectionFilter) !== -1;
+ });
+ if (matchingColls.length > 0) {
+ candidateNodes[nc.node] = true;
+ }
+ });
+ filteredNodes = Object.keys(candidateNodes);
+ }
+ break;
+
+ case "health":
+
+ }
+
+ if (filteredNodes) {
+ // If filtering is active, calculate what hosts contain the nodes that match the filters
+ isFiltered = true;
+ filteredHosts = filteredNodes.map(function (node) {
+ return node.split(":")[0];
+ }).filter(function (item, index, self) {
+ return self.indexOf(item) === index;
+ });
+ } else {
+ filteredNodes = node_keys;
+ filteredHosts = hostNames;
+ }
+ filteredNodes.sort();
+ filteredHosts.sort();
+
+ // Find what hosts & nodes (from the filtered set) that should be displayed on current page
+ for (var id = $scope.from ; id < $scope.from + pageSize && filteredHosts[id] ; id++) {
+ var hostName = filteredHosts[id];
+ hostsToShow.push(hostName);
+ if (isFiltered) { // Only show the nodes per host matching active filter
+ nodesToShow = nodesToShow.concat(filteredNodes.filter(function (node) {
+ return node.startsWith(hostName);
+ }));
+ } else {
+ nodesToShow = nodesToShow.concat(hosts[hostName]['nodes']);
+ }
+ }
+ nodesParam = nodesToShow.filter(function (node) {
+ return live_nodes.includes(node);
+ }).join(',');
+ var deadNodes = nodesToShow.filter(function (node) {
+ return !live_nodes.includes(node);
+ });
+ deadNodes.forEach(function (node) {
+ nodes[node]['dead'] = true;
+ });
+ $scope.nextEnabled = $scope.from + pageSize < filteredHosts.length;
+ $scope.prevEnabled = $scope.from - pageSize >= 0;
+ nodesToShow.sort();
+ hostsToShow.sort();
+
+ /*
+ Fetch system info for all selected nodes
+ Pick the data we want to display and add it to the node-centric data structure
+ */
+ System.get({"nodes": nodesParam}, function (systemResponse) {
+ for (var node in systemResponse) {
+ if (node in nodes) {
+ var s = systemResponse[node];
+ nodes[node]['system'] = s;
+ var memTotal = s.system.totalPhysicalMemorySize;
+ var memFree = s.system.freePhysicalMemorySize;
+ var memPercentage = Math.floor((memTotal - memFree) / memTotal * 100);
+ nodes[node]['memUsedPct'] = memPercentage;
+ nodes[node]['memUsedPctStyle'] = styleForPct(memPercentage);
+ nodes[node]['memTotal'] = bytesToSize(memTotal);
+ nodes[node]['memFree'] = bytesToSize(memFree);
+ nodes[node]['memUsed'] = bytesToSize(memTotal - memFree);
+
+ var heapTotal = s.jvm.memory.raw.total;
+ var heapFree = s.jvm.memory.raw.free;
+ var heapPercentage = Math.floor((heapTotal - heapFree) / heapTotal * 100);
+ nodes[node]['heapUsed'] = bytesToSize(heapTotal - heapFree);
+ nodes[node]['heapUsedPct'] = heapPercentage;
+ nodes[node]['heapUsedPctStyle'] = styleForPct(heapPercentage);
+ nodes[node]['heapTotal'] = bytesToSize(heapTotal);
+ nodes[node]['heapFree'] = bytesToSize(heapFree);
+
+ var jvmUptime = s.jvm.jmx.upTimeMS / 1000; // Seconds
+ nodes[node]['jvmUptime'] = secondsForHumans(jvmUptime);
+ nodes[node]['jvmUptimeSec'] = jvmUptime;
+
+ nodes[node]['uptime'] = s.system.uptime.replace(/.*up (.*?,.*?),.*/, "$1");
+ nodes[node]['loadAvg'] = Math.round(s.system.systemLoadAverage * 100) / 100;
+ nodes[node]['cpuPct'] = Math.ceil(s.system.processCpuLoad);
+ nodes[node]['cpuPctStyle'] = styleForPct(Math.ceil(s.system.processCpuLoad));
+ nodes[node]['maxFileDescriptorCount'] = s.system.maxFileDescriptorCount;
+ nodes[node]['openFileDescriptorCount'] = s.system.openFileDescriptorCount;
+ }
+ }
+ });
+
+ /*
+ Fetch metrics for all selected nodes. Only pull the metrics that we'll show to save bandwidth
+ Pick the data we want to display and add it to the node-centric data structure
+ */
+ Metrics.get({
+ "nodes": nodesParam,
+ "prefix": "CONTAINER.fs,org.eclipse.jetty.server.handler.DefaultHandler.get-requests,INDEX.sizeInBytes,SEARCHER.searcher.numDocs,SEARCHER.searcher.deletedDocs,SEARCHER.searcher.warmupTime"
+ },
+ function (metricsResponse) {
+ for (var node in metricsResponse) {
+ if (node in nodes) {
+ var m = metricsResponse[node];
+ nodes[node]['metrics'] = m;
+ var diskTotal = m.metrics['solr.node']['CONTAINER.fs.totalSpace'];
+ var diskFree = m.metrics['solr.node']['CONTAINER.fs.usableSpace'];
+ var diskPercentage = Math.floor((diskTotal - diskFree) / diskTotal * 100);
+ nodes[node]['diskUsedPct'] = diskPercentage;
+ nodes[node]['diskUsedPctStyle'] = styleForPct(diskPercentage);
+ nodes[node]['diskTotal'] = bytesToSize(diskTotal);
+ nodes[node]['diskFree'] = bytesToSize(diskFree);
+
+ var r = m.metrics['solr.jetty']['org.eclipse.jetty.server.handler.DefaultHandler.get-requests'];
+ nodes[node]['req'] = r.count;
+ nodes[node]['req1minRate'] = Math.floor(r['1minRate'] * 100) / 100;
+ nodes[node]['req5minRate'] = Math.floor(r['5minRate'] * 100) / 100;
+ nodes[node]['req15minRate'] = Math.floor(r['15minRate'] * 100) / 100;
+ nodes[node]['reqp75_ms'] = Math.floor(r['p75_ms']);
+ nodes[node]['reqp95_ms'] = Math.floor(r['p95_ms']);
+ nodes[node]['reqp99_ms'] = Math.floor(r['p99_ms']);
+
+ var cores = nodes[node]['cores'];
+ var indexSizeTotal = 0;
+ var docsTotal = 0;
+ var graphData = [];
+ if (cores) {
+ for (coreId in cores) {
+ var core = cores[coreId];
+ var keyName = "solr.core." + core['core'].replace(/(.*?)_(shard(\d+_?)+)_(replica.*?)/, '\$1.\$2.\$4');
+ var nodeMetric = m.metrics[keyName];
+ var size = nodeMetric['INDEX.sizeInBytes'];
+ size = (typeof size !== 'undefined') ? size : 0;
+ core['sizeInBytes'] = size;
+ core['size'] = bytesToSize(size);
+ if (core['shard_state'] !== 'active' || core['state'] !== 'active') {
+ // If core state is not active, display the real state, or if shard is inactive, display that
+ var labelState = (core['state'] !== 'active') ? core['state'] : core['shard_state'];
+ core['label'] += "_(" + labelState + ")";
+ }
+ indexSizeTotal += size;
+ var numDocs = nodeMetric['SEARCHER.searcher.numDocs'];
+ numDocs = (typeof numDocs !== 'undefined') ? numDocs : 0;
+ core['numDocs'] = numDocs;
+ core['numDocsHuman'] = numDocsHuman(numDocs);
+ core['avgSizePerDoc'] = bytesToSize(numDocs === 0 ? 0 : size / numDocs);
+ var deletedDocs = nodeMetric['SEARCHER.searcher.deletedDocs'];
+ deletedDocs = (typeof deletedDocs !== 'undefined') ? deletedDocs : 0;
+ core['deletedDocs'] = deletedDocs;
+ core['deletedDocsHuman'] = numDocsHuman(deletedDocs);
+ var warmupTime = nodeMetric['SEARCHER.searcher.warmupTime'];
+ warmupTime = (typeof warmupTime !== 'undefined') ? warmupTime : 0;
+ core['warmupTime'] = warmupTime;
+ docsTotal += core['numDocs'];
+ }
+ for (coreId in cores) {
+ core = cores[coreId];
+ var graphObj = {};
+ graphObj['label'] = core['label'];
+ graphObj['size'] = core['sizeInBytes'];
+ graphObj['sizeHuman'] = core['size'];
+ graphObj['pct'] = (core['sizeInBytes'] / indexSizeTotal) * 100;
+ graphData.push(graphObj);
+ }
+ cores.sort(function (a, b) {
+ return b.sizeInBytes - a.sizeInBytes
+ });
+ } else {
+ cores = {};
+ }
+ graphData.sort(function (a, b) {
+ return b.size - a.size
+ });
+ nodes[node]['graphData'] = graphData;
+ nodes[node]['numDocs'] = numDocsHuman(docsTotal);
+ nodes[node]['sizeInBytes'] = indexSizeTotal;
+ nodes[node]['size'] = bytesToSize(indexSizeTotal);
+ nodes[node]['sizePerDoc'] = docsTotal === 0 ? '0b' : bytesToSize(indexSizeTotal / docsTotal);
+
+ // Build the d3 powered bar chart
+ $('#chart' + nodes[node]['id']).empty();
+ var chart = d3.select('#chart' + nodes[node]['id']).append('div').attr('class', 'chart');
+
+ // Add one div per bar which will group together both labels and bars
+ var g = chart.selectAll('div')
+ .data(nodes[node]['graphData']).enter()
+ .append('div');
+
+ // Add the bars
+ var bars = g.append("div")
+ .attr("class", "rect")
+ .text(function (d) {
+ return d.label + ':\u00A0\u00A0' + d.sizeHuman;
+ });
+
+ // Execute the transition to show the bars
+ bars.transition()
+ .ease('elastic')
+ .style('width', function (d) {
+ return d.pct + '%';
+ });
+ }
+ }
+ });
+ $scope.nodes = nodes;
+ $scope.hosts = hosts;
+ $scope.live_nodes = live_nodes;
+ $scope.nodesToShow = nodesToShow;
+ $scope.hostsToShow = hostsToShow;
+ $scope.filteredNodes = filteredNodes;
+ $scope.filteredHosts = filteredHosts;
+ };
+ $scope.initClusterState();
+};
+
+var zkStatusSubController = function($scope, ZookeeperStatus) {
+ $scope.showZkStatus = true;
+ $scope.showNodes = false;
+ $scope.showTree = false;
+ $scope.showGraph = false;
+ $scope.tree = {};
+ $scope.showData = false;
+ $scope.showDetails = false;
+
+ $scope.toggleDetails = function() {
+ $scope.showDetails = !$scope.showDetails === true;
+ };
+
+ $scope.initZookeeper = function() {
+ ZookeeperStatus.monitor({}, function(data) {
+ $scope.zkState = data.zkStatus;
+ $scope.mainKeys = ["ok", "clientPort", "secureClientPort", "zk_server_state", "zk_version",
+ "zk_approximate_data_size", "zk_znode_count", "zk_num_alive_connections"];
+ $scope.detailKeys = ["dataDir", "dataLogDir",
+ "zk_avg_latency", "zk_max_file_descriptor_count", "zk_watch_count",
+ "zk_packets_sent", "zk_packets_received",
+ "tickTime", "maxClientCnxns", "minSessionTimeout", "maxSessionTimeout"];
+ $scope.ensembleMainKeys = ["serverId", "electionPort", "quorumPort"];
+ $scope.ensembleDetailKeys = ["peerType", "electionAlg", "initLimit", "syncLimit",
+ "zk_followers", "zk_synced_followers", "zk_pending_syncs",
+ "server.1", "server.2", "server.3", "server.4", "server.5"];
+ $scope.notEmptyRow = function(key) {
+ for (hostId in $scope.zkState.details) {
+ if (key in $scope.zkState.details[hostId]) return true;
+ }
+ return false;
+ };
+ });
+ };
+
+ $scope.initZookeeper();
+};
+
+var treeSubController = function($scope, Zookeeper) {
+ $scope.showZkStatus = false;
+ $scope.showTree = true;
+ $scope.showGraph = false;
+ $scope.tree = {};
+ $scope.showData = false;
+
+ $scope.showTreeLink = function(link) {
+ var path = decodeURIComponent(link.replace(/.*[\\?&]path=([^&#]*).*/, "$1"));
+ Zookeeper.detail({path: path}, function(data) {
+ $scope.znode = data.znode;
+ var path = data.znode.path.split( '.' );
+ if(path.length >1) {
+ $scope.lang = path.pop();
+ } else {
+ var lastPathElement = data.znode.path.split( '/' ).pop();
+ if (lastPathElement == "managed-schema") {
+ $scope.lang = "xml";
+ }
+ }
+ $scope.showData = true;
+ });
+ };
+
+ $scope.hideData = function() {
+ $scope.showData = false;
+ };
+
+ $scope.initTree = function() {
+ Zookeeper.simple(function(data) {
+ $scope.tree = data.tree;
+ });
+ };
+
+ $scope.initTree();
+};
+
+/**
+ * Translates seconds into human readable format of seconds, minutes, hours, days, and years
+ *
+ * @param {number} seconds The number of seconds to be processed
+ * @return {string} The phrase describing the the amount of time
+ */
+function secondsForHumans ( seconds ) {
+ var levels = [
+ [Math.floor(seconds / 31536000), 'y'],
+ [Math.floor((seconds % 31536000) / 86400), 'd'],
+ [Math.floor(((seconds % 31536000) % 86400) / 3600), 'h'],
+ [Math.floor((((seconds % 31536000) % 86400) % 3600) / 60), 'm']
+ ];
+ var returntext = '';
+
+ for (var i = 0, max = levels.length; i < max; i++) {
+ if ( levels[i][0] === 0 ) continue;
+ returntext += ' ' + levels[i][0] + levels[i][1];
+ }
+ return returntext.trim() === '' ? '0m' : returntext.trim();
+}
+
+var graphSubController = function ($scope, Zookeeper) {
+ $scope.showZkStatus = false;
+ $scope.showTree = false;
+ $scope.showGraph = true;
+
+ $scope.filterType = "status";
+
+ $scope.helperData = {
+ protocol: [],
+ host: [],
+ hostname: [],
+ port: [],
+ pathname: [],
+ replicaType: [],
+ base_url: [],
+ core: [],
+ node_name: [],
+ state: [],
+ core_node: []
+ };
+
+ $scope.next = function() {
+ $scope.pos += $scope.rows;
+ $scope.initGraph();
+ };
+
+ $scope.previous = function() {
+ $scope.pos = Math.max(0, $scope.pos - $scope.rows);
+ $scope.initGraph();
+ };
+
+ $scope.resetGraph = function() {
+ $scope.pos = 0;
+ $scope.initGraph();
+ };
+
+ $scope.initGraph = function() {
+ Zookeeper.liveNodes(function (data) {
+ var live_nodes = {};
+ for (var c in data.tree[0].children) {
+ live_nodes[data.tree[0].children[c].data.title] = true;
+ }
+
+ var params = {view: "graph"};
+ if ($scope.rows) {
+ params.start = $scope.pos;
+ params.rows = $scope.rows;
+ }
+
+ var filter = ($scope.filterType=='status') ? $scope.pagingStatusFilter : $scope.pagingFilter;
+
+ if (filter) {
+ params.filterType = $scope.filterType;
+ params.filter = filter;
+ }
+
+ Zookeeper.clusterState(params, function (data) {
+ eval("var state=" + data.znode.data); // @todo fix horrid means to parse JSON
+
+ var leaf_count = 0;
+ var graph_data = {
+ name: null,
+ children: []
+ };
+
+ for (var c in state) {
+ var shards = [];
+ for (var s in state[c].shards) {
+ var shard_status = state[c].shards[s].state;
+ shard_status = shard_status == 'inactive' ? 'shard-inactive' : shard_status;
+ var nodes = [];
+ for (var n in state[c].shards[s].replicas) {
+ leaf_count++;
+ var replica = state[c].shards[s].replicas[n]
+
+ var uri = replica.base_url;
+ var parts = uri.match(/^(\w+:)\/\/(([\w\d\.-]+)(:(\d+))?)(.+)$/);
+ var uri_parts = {
+ protocol: parts[1],
+ host: parts[2],
+ hostname: parts[3],
+ port: parseInt(parts[5] || 80, 10),
+ pathname: parts[6],
+ replicaType: replica.type,
+ base_url: replica.base_url,
+ core: replica.core,
+ node_name: replica.node_name,
+ state: replica.state,
+ core_node: n
+ };
+
+ $scope.helperData.protocol.push(uri_parts.protocol);
+ $scope.helperData.host.push(uri_parts.host);
+ $scope.helperData.hostname.push(uri_parts.hostname);
+ $scope.helperData.port.push(uri_parts.port);
+ $scope.helperData.pathname.push(uri_parts.pathname);
+ $scope.helperData.replicaType.push(uri_parts.replicaType);
+ $scope.helperData.base_url.push(uri_parts.base_url);
+ $scope.helperData.core.push(uri_parts.core);
+ $scope.helperData.node_name.push(uri_parts.node_name);
+ $scope.helperData.state.push(uri_parts.state);
+ $scope.helperData.core_node.push(uri_parts.core_node);
+
+ var replica_status = replica.state;
+
+ if (!live_nodes[replica.node_name]) {
+ replica_status = 'gone';
+ } else if(shard_status=='shard-inactive') {
+ replica_status += ' ' + shard_status;
+ }
+
+ var node = {
+ name: uri,
+ data: {
+ type: 'node',
+ state: replica_status,
+ leader: 'true' === replica.leader,
+ uri: uri_parts
+ }
+ };
+ nodes.push(node);
+ }
+
+ var shard = {
+ name: shard_status == "shard-inactive" ? s + ' (inactive)' : s,
+ data: {
+ type: 'shard',
+ state: shard_status,
+ range: state[c].shards[s].range
+
+ },
+ children: nodes
+ };
+ shards.push(shard);
+ }
+
+ var collection = {
+ name: c,
+ data: {
+ type: 'collection',
+ pullReplicas: state[c].pullReplicas,
+ replicationFactor: state[c].replicationFactor,
+ router: state[c].router.name,
+ maxShardsPerNode: state[c].maxShardsPerNode,
+ autoAddReplicas: state[c].autoAddReplicas,
+ nrtReplicas: state[c].nrtReplicas,
+ tlogReplicas: state[c].tlogReplicas,
+ numShards: shards.length
+ },
+ children: shards
+ };
+ graph_data.children.push(collection);
+ }
+ $scope.helperData.protocol = $.unique($scope.helperData.protocol);
+ $scope.helperData.host = $.unique($scope.helperData.host);
+ $scope.helperData.hostname = $.unique($scope.helperData.hostname);
+ $scope.helperData.port = $.unique($scope.helperData.port);
+ $scope.helperData.pathname = $.unique($scope.helperData.pathname);
+ $scope.helperData.replicaType = $.unique($scope.helperData.replicaType);
+ $scope.helperData.base_url = $.unique($scope.helperData.base_url);
+ $scope.helperData.core = $.unique($scope.helperData.core);
+ $scope.helperData.node_name = $.unique($scope.helperData.node_name);
+ $scope.helperData.state = $.unique($scope.helperData.state);
+ $scope.helperData.core_node = $.unique($scope.helperData.core_node);
+
+ if (data.znode && data.znode.paging) {
+ $scope.showPaging = true;
+
+ var parr = data.znode.paging.split('|');
+ if (parr.length < 3) {
+ $scope.showPaging = false;
+ } else {
+ $scope.start = Math.max(parseInt(parr[0]), 0);
+ $scope.prevEnabled = ($scope.start > 0);
+ $scope.rows = parseInt(parr[1]);
+ $scope.total = parseInt(parr[2]);
+
+ if ($scope.rows == -1) {
+ $scope.showPaging = false;
+ } else {
+ var filterType = parr.length > 3 ? parr[3] : '';
+
+ if (filterType == '' || filterType == 'none') filterType = 'status';
+
+ +$('#cloudGraphPagingFilterType').val(filterType);
+
+ var filter = parr.length > 4 ? parr[4] : '';
+ var page = Math.floor($scope.start / $scope.rows) + 1;
+ var pages = Math.ceil($scope.total / $scope.rows);
+ $scope.last = Math.min($scope.start + $scope.rows, $scope.total);
+ $scope.nextEnabled = ($scope.last < $scope.total);
+ }
+ }
+ }
+ else {
+ $scope.showPaging = false;
+ }
+ $scope.graphData = graph_data;
+ $scope.leafCount = leaf_count;
+ });
+ });
+ };
+
+ $scope.initGraph();
+ $scope.pos = 0;
+};
+
+solrAdminApp.directive('graph', function(Constants) {
+ return {
+ restrict: 'EA',
+ scope: {
+ data: "=",
+ leafCount: "=",
+ helperData: "=",
+ },
+ link: function (scope, element, attrs) {
+ var helper_path_class = function (p) {
+ var classes = ['link'];
+ classes.push('lvl-' + p.target.depth);
+
+ if (p.target.data && p.target.data.leader) {
+ classes.push('leader');
+ }
+
+ if (p.target.data && p.target.data.state) {
+ classes.push(p.target.data.state);
+ }
+
+ return classes.join(' ');
+ };
+
+ var helper_node_class = function (d) {
+ var classes = ['node'];
+ classes.push('lvl-' + d.depth);
+
+ if (d.data && d.data.leader) {
+ classes.push('leader');
+ }
+
+ if (d.data && d.data.state) {
+ if(!(d.data.type=='shard' && d.data.state=='active')){
+ classes.push(d.data.state);
+ }
+ }
+
+ return classes.join(' ');
+ };
+
+ var helper_tooltip_text = function (d) {
+ if (!d.data) {
+ return tooltip;
+ }
+ var tooltip;
+
+ if (! d.data.type) {
+ return tooltip;
+ }
+
+
+ if (d.data.type == 'collection') {
+ tooltip = d.name + " {<br/> ";
+ tooltip += "numShards: [" + d.data.numShards + "],<br/>";
+ tooltip += "maxShardsPerNode: [" + d.data.maxShardsPerNode + "],<br/>";
+ tooltip += "router: [" + d.data.router + "],<br/>";
+ tooltip += "autoAddReplicas: [" + d.data.autoAddReplicas + "],<br/>";
+ tooltip += "replicationFactor: [" + d.data.replicationFactor + "],<br/>";
+ tooltip += "nrtReplicas: [" + d.data.nrtReplicas + "],<br/>";
+ tooltip += "pullReplicas: [" + d.data.pullReplicas + "],<br/>";
+ tooltip += "tlogReplicas: [" + d.data.tlogReplicas + "],<br/>";
+ tooltip += "}";
+ } else if (d.data.type == 'shard') {
+ tooltip = d.name + " {<br/> ";
+ tooltip += "range: [" + d.data.range + "],<br/>";
+ tooltip += "state: [" + d.data.state + "],<br/>";
+ tooltip += "}";
+ } else if (d.data.type == 'node') {
+ tooltip = d.data.uri.core_node + " {<br/>";
+
+ if (0 !== scope.helperData.core.length) {
+ tooltip += "core: [" + d.data.uri.core + "],<br/>";
+ }
+
+ if (0 !== scope.helperData.node_name.length) {
+ tooltip += "node_name: [" + d.data.uri.node_name + "],<br/>";
+ }
+ tooltip += "}";
+ }
+
+ return tooltip;
+ };
+
+ var helper_node_text = function (d) {
+ if (!d.data || !d.data.uri) {
+ return d.name;
+ }
+
+ var name = d.data.uri.hostname;
+ if (1 !== scope.helperData.protocol.length) {
+ name = d.data.uri.protocol + '//' + name;
+ }
+
+ if (1 !== scope.helperData.port.length) {
+ name += ':' + d.data.uri.port;
+ }
+
+ if (1 !== scope.helperData.pathname.length) {
+ name += d.data.uri.pathname;
+ }
+
+ if(0 !== scope.helperData.replicaType.length) {
+ name += ' (' + d.data.uri.replicaType[0] + ')';
+ }
+
+ return name;
+ };
+
+ scope.$watch("data", function(newValue, oldValue) {
+ if (newValue) {
+ flatGraph(element, scope.data, scope.leafCount);
+ }
+
+ $('text').tooltip({
+ content: function() {
+ return $(this).attr('title');
+ }
+ });
+ });
+
+
+ function setNodeNavigationBehavior(node, view){
+ node
+ .attr('data-href', function (d) {
+ if (d.type == "node"){
+ return getNodeUrl(d, view);
+ }
+ else{
+ return "";
+ }
+ })
+ .on('click', function(d) {
+ if (d.data.type == "node"){
+ location.href = getNodeUrl(d, view);
+ }
+ });
+ }
+
+ function getNodeUrl(d, view){
+ var url = d.name + Constants.ROOT_URL + "#/~cloud";
+ if (view != undefined){
+ url += "?view=" + view;
+ }
+ return url;
+ }
+
+ var flatGraph = function(element, graphData, leafCount) {
+ var w = element.width(),
+ h = leafCount * 20;
+
+ var tree = d3.layout.tree().size([h, w - 400]);
+
+ var diagonal = d3.svg.diagonal().projection(function (d) {
+ return [d.y, d.x];
+ });
+
+ d3.select('#canvas', element).html('');
+ var vis = d3.select('#canvas', element).append('svg')
+ .attr('width', w)
+ .attr('height', h)
+ .append('g')
+ .attr('transform', 'translate(100, 0)');
+
+ var nodes = tree.nodes(graphData);
+
+ var link = vis.selectAll('path.link')
+ .data(tree.links(nodes))
+ .enter().append('path')
+ .attr('class', helper_path_class)
+ .attr('d', diagonal);
+
+ var node = vis.selectAll('g.node')
+ .data(nodes)
+ .enter().append('g')
+ .attr('class', helper_node_class)
+ .attr('transform', function (d) {
+ return 'translate(' + d.y + ',' + d.x + ')';
+ })
+
+ node.append('circle')
+ .attr('r', 4.5);
+
+ node.append('text')
+ .attr('dx', function (d) {
+ return 0 === d.depth ? -8 : 8;
+ })
+ .attr('dy', function (d) {
+ return 5;
+ })
+ .attr('text-anchor', function (d) {
+ return 0 === d.depth ? 'end' : 'start';
+ })
+ .attr("title", helper_tooltip_text)
+ .text(helper_node_text);
+
+ setNodeNavigationBehavior(node);
+ };
+ }
+ };
+});
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js
new file mode 100644
index 000000000..01e1964e0
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js
@@ -0,0 +1,62 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+solrAdminApp.controller('ClusterSuggestionsController',
+function($scope, $http, Constants) {
+ $scope.resetMenu("cluster-suggestion", Constants.IS_COLLECTION_PAGE);
+ $scope.data={};
+ var dataArr =[];
+ var dataJson = {};
+ //function to display suggestion
+ $http({
+ method: 'GET',
+ url: '/api/cluster/autoscaling/suggestions'
+ }).then(function successCallback(response) {
+ $scope.data = response.data;
+ $scope.parsedData = $scope.data.suggestions;
+ }, function errorCallback(response) {
+ });
+ //function to perform operation
+ $scope.postdata = function (x) {
+ x.loading = true;
+ var path=x.operation.path;
+ var command=x.operation.command;
+ var fullPath='/api/'+path;
+ console.log(fullPath);
+ console.log(command);
+ $http.post(fullPath, JSON.stringify(command)).then(function (response) {
+ if (response.data)
+ console.log(response.data);
+ x.loading = false;
+ x.done = true;
+ x.run=true;
+ $scope.msg = "Command Submitted Successfully!";
+ }, function (response) {
+ x.failed=true;
+ $scope.msg = "Service does not exist";
+ $scope.statusval = response.status;
+ $scope.statustext = response.statusText;
+ $scope.headers = response.headers();
+ });
+ };
+ $scope.showPopover = function() {
+ $scope.popup = true;
+ };
+
+ $scope.hidePopover = function () {
+ $scope.popup = false;
+ };
+});
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js
new file mode 100644
index 000000000..d1834b2bd
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js
@@ -0,0 +1,39 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('CollectionOverviewController',
+function($scope, $routeParams, Collections, Constants) {
+ $scope.resetMenu("collection-overview", Constants.IS_COLLECTION_PAGE);
+
+ $scope.refresh = function() {
+ Collections.status({}, function(data) {
+ $scope.selectedCollection = data.cluster.collections[$routeParams.core];
+ $scope.selectedCollection.name = $routeParams.core;
+ $scope.rootUrl = Constants.ROOT_URL;
+ });
+ };
+
+ $scope.showReplica = function(replica) {
+ replica.show = !replica.show;
+ }
+
+ $scope.hideShard = function(shard) {
+ shard.hide = !shard.hide;
+ }
+
+ $scope.refresh();
+});
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js
new file mode 100644
index 000000000..978fc71e6
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js
@@ -0,0 +1,289 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('CollectionsController',
+ function($scope, $routeParams, $location, $timeout, Collections, Zookeeper, Constants){
+ $scope.resetMenu("collections", Constants.IS_ROOT_PAGE);
+
+ $scope.refresh = function() {
+
+ $scope.rootUrl = Constants.ROOT_URL + "#/~collections/" + $routeParams.collection;
+
+ Collections.status(function (data) {
+ $scope.collections = [];
+ for (var name in data.cluster.collections) {
+ var collection = data.cluster.collections[name];
+ collection.name = name;
+ collection.type = 'collection';
+ var shards = collection.shards;
+ collection.shards = [];
+ for (var shardName in shards) {
+ var shard = shards[shardName];
+ shard.name = shardName;
+ shard.collection = collection.name;
+ var replicas = shard.replicas;
+ shard.replicas = [];
+ for (var replicaName in replicas) {
+ var replica = replicas[replicaName];
+ replica.name = replicaName;
+ replica.collection = collection.name;
+ replica.shard = shard.name;
+ shard.replicas.push(replica);
+ }
+ collection.shards.push(shard);
+ }
+ $scope.collections.push(collection);
+ if ($routeParams.collection == name) {
+ $scope.collection = collection;
+ }
+ }
+ // Fetch aliases using LISTALIASES to get properties
+ Collections.listaliases(function (adata) {
+ // TODO: Population of aliases array duplicated in app.js
+ $scope.aliases = [];
+ for (var key in adata.aliases) {
+ props = {};
+ if (key in adata.properties) {
+ props = adata.properties[key];
+ }
+ var alias = {name: key, collections: adata.aliases[key], type: 'alias', properties: props};
+ $scope.aliases.push(alias);
+ if ($routeParams.collection == 'alias_' + key) {
+ $scope.collection = alias;
+ }
+ }
+ // Decide what is selected in list
+ if ($routeParams.collection && !$scope.collection) {
+ alert("No collection or alias called " + $routeParams.collection);
+ $location.path("/~collections");
+ }
+ });
+
+ $scope.liveNodes = data.cluster.liveNodes;
+ });
+ Zookeeper.configs(function(data) {
+ $scope.configs = [];
+ var items = data.tree[0].children;
+ for (var i in items) {
+ $scope.configs.push({name: items[i].data.title});
+ }
+ });
+ };
+
+ $scope.hideAll = function() {
+ $scope.showRename = false;
+ $scope.showAdd = false;
+ $scope.showDelete = false;
+ $scope.showSwap = false;
+ $scope.showCreateAlias = false;
+ $scope.showDeleteAlias = false;
+ };
+
+ $scope.showAddCollection = function() {
+ $scope.hideAll();
+ $scope.showAdd = true;
+ $scope.newCollection = {
+ name: "",
+ routerName: "compositeId",
+ numShards: 1,
+ configName: "",
+ replicationFactor: 1,
+ maxShardsPerNode: 1,
+ autoAddReplicas: 'false'
+ };
+ };
+
+ $scope.toggleCreateAlias = function() {
+ $scope.hideAll();
+ $scope.showCreateAlias = true;
+ }
+
+ $scope.toggleDeleteAlias = function() {
+ $scope.hideAll();
+ $scope.showDeleteAlias = true;
+ }
+
+ $scope.cancelCreateAlias = $scope.cancelDeleteAlias = function() {
+ $scope.hideAll();
+ }
+
+ $scope.createAlias = function() {
+ var collections = [];
+ for (var i in $scope.aliasCollections) {
+ collections.push($scope.aliasCollections[i].name);
+ }
+ Collections.createAlias({name: $scope.aliasToCreate, collections: collections.join(",")}, function(data) {
+ $scope.cancelCreateAlias();
+ $scope.resetMenu("collections", Constants.IS_ROOT_PAGE);
+ $location.path("/~collections/alias_" + $scope.aliasToCreate);
+ });
+ }
+ $scope.deleteAlias = function() {
+ Collections.deleteAlias({name: $scope.collection.name}, function(data) {
+ $scope.hideAll();
+ $scope.resetMenu("collections", Constants.IS_ROOT_PAGE);
+ $location.path("/~collections/");
+ });
+
+ };
+ $scope.addCollection = function() {
+ if (!$scope.newCollection.name) {
+ $scope.addMessage = "Please provide a core name";
+ } else if (false) { //@todo detect whether core exists
+ $scope.AddMessage = "A core with that name already exists";
+ } else {
+ var coll = $scope.newCollection;
+ var params = {
+ name: coll.name,
+ "router.name": coll.routerName,
+ numShards: coll.numShards,
+ "collection.configName": coll.configName,
+ replicationFactor: coll.replicationFactor,
+ maxShardsPerNode: coll.maxShardsPerNode,
+ autoAddReplicas: coll.autoAddReplicas
+ };
+ if (coll.shards) params.shards = coll.shards;
+ if (coll.routerField) params["router.field"] = coll.routerField;
+ Collections.add(params, function(data) {
+ $scope.cancelAddCollection();
+ $scope.resetMenu("collections", Constants.IS_ROOT_PAGE);
+ $location.path("/~collections/" + $scope.newCollection.name);
+ });
+ }
+ };
+
+ $scope.cancelAddCollection = function() {
+ delete $scope.addMessage;
+ $scope.showAdd = false;
+ };
+
+ $scope.showDeleteCollection = function() {
+ $scope.hideAll();
+ if ($scope.collection) {
+ $scope.showDelete = true;
+ } else {
+ alert("No collection selected.");
+ }
+ };
+
+ $scope.deleteCollection = function() {
+ if ($scope.collection.name == $scope.collectionDeleteConfirm) {
+ Collections.delete({name: $scope.collection.name}, function (data) {
+ $location.path("/~collections");
+ });
+ } else {
+ $scope.deleteMessage = "Collection names do not match.";
+ }
+ };
+
+ $scope.reloadCollection = function() {
+ if (!$scope.collection) {
+ alert("No collection selected.");
+ return;
+ }
+ Collections.reload({name: $scope.collection.name},
+ function(successData) {
+ $scope.reloadSuccess = true;
+ $timeout(function() {$scope.reloadSuccess=false}, 1000);
+ },
+ function(failureData) {
+ $scope.reloadFailure = true;
+ $timeout(function() {$scope.reloadFailure=false}, 1000);
+ $location.path("/~collections");
+ });
+ };
+
+ $scope.toggleAddReplica = function(shard) {
+ $scope.hideAll();
+ shard.showAdd = !shard.showAdd;
+ delete $scope.addReplicaMessage;
+
+ Zookeeper.liveNodes({}, function(data) {
+ $scope.nodes = [];
+ var children = data.tree[0].children;
+ for (var child in children) {
+ $scope.nodes.push(children[child].data.title);
+ }
+ });
+ };
+
+ $scope.toggleRemoveReplica = function(replica) {
+ $scope.hideAll();
+ replica.showRemove = !replica.showRemove;
+ };
+
+ $scope.toggleRemoveShard = function(shard) {
+ $scope.hideAll();
+ shard.showRemove = !shard.showRemove;
+ };
+
+ $scope.deleteShard = function(shard) {
+ Collections.deleteShard({collection: shard.collection, shard:shard.name}, function(data) {
+ shard.deleted = true;
+ $timeout(function() {
+ $scope.refresh();
+ }, 2000);
+ });
+ }
+
+ $scope.deleteReplica = function(replica) {
+ Collections.deleteReplica({collection: replica.collection, shard:replica.shard, replica:replica.name}, function(data) {
+ replica.deleted = true;
+ $timeout(function() {
+ $scope.refresh();
+ }, 2000);
+ });
+ }
+ $scope.addReplica = function(shard) {
+ var params = {
+ collection: shard.collection,
+ shard: shard.name,
+ }
+ if (shard.replicaNodeName && shard.replicaNodeName != "") {
+ params.node = shard.replicaNodeName;
+ }
+ Collections.addReplica(params, function(data) {
+ shard.replicaAdded = true;
+ $timeout(function () {
+ shard.replicaAdded = false;
+ shard.showAdd = false;
+ $$scope.refresh();
+ }, 2000);
+ });
+ };
+
+ $scope.toggleShard = function(shard) {
+ shard.show = !shard.show;
+ }
+
+ $scope.toggleReplica = function(replica) {
+ replica.show = !replica.show;
+ }
+
+ $scope.refresh();
+ }
+);
+
+var flatten = function(data) {
+ var list = [];
+ for (var name in data) {
+ var entry = data[name];
+ entry.name = name;
+ list.push(entry);
+ }
+ return list;
+}
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js
new file mode 100644
index 000000000..0e2b3d2a6
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js
@@ -0,0 +1,93 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('CoreOverviewController',
+function($scope, $rootScope, $routeParams, Luke, CoreSystem, Update, Replication, Ping, Constants) {
+ $scope.resetMenu("overview", Constants.IS_CORE_PAGE);
+ $scope.refreshIndex = function() {
+ Luke.index({core: $routeParams.core},
+ function(data) {
+ $scope.index = data.index;
+ delete $scope.statsMessage;
+ },
+ function(error) {
+ $scope.statsMessage = "Luke is not configured";
+ }
+ );
+ };
+
+ $scope.refreshReplication = function() {
+ Replication.details({core: $routeParams.core},
+ function(data) {
+ $scope.isSlave = data.details.isSlave == "true";
+ $scope.isMaster = data.details.isMaster == "true";
+ $scope.replication = data.details;
+ },
+ function(error) {
+ $scope.replicationMessage = "Replication is not configured";
+ });
+ };
+
+ $scope.refreshSystem = function() {
+ CoreSystem.get({core: $routeParams.core},
+ function(data) {
+ $scope.core = data.core;
+ delete $scope.systemMessage;
+ },
+ function(error) {
+ $scope.systemMessage = "/admin/system Handler is not configured";
+ }
+ );
+ };
+
+ $scope.refreshPing = function() {
+ Ping.status({core: $routeParams.core}, function(data) {
+ if (data.error) {
+ $scope.healthcheckStatus = false;
+ if (data.error.code == 503) {
+ $scope.healthcheckMessage = 'Ping request handler is not configured with a healthcheck file.';
+ }
+ } else {
+ $scope.healthcheckStatus = data.status == "enabled";
+ }
+ });
+ };
+
+ $scope.toggleHealthcheck = function() {
+ if ($scope.healthcheckStatus) {
+ Ping.disable(
+ function(data) {$scope.healthcheckStatus = false},
+ function(error) {$scope.healthcheckMessage = error}
+ );
+ } else {
+ Ping.enable(
+ function(data) {$scope.healthcheckStatus = true},
+ function(error) {$scope.healthcheckMessage = error}
+ );
+ }
+ };
+
+ $scope.refresh = function() {
+ $scope.refreshIndex();
+ $scope.refreshReplication();
+ $scope.refreshSystem();
+ $scope.refreshPing();
+ };
+
+ $scope.refresh();
+});
+
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js
new file mode 100644
index 000000000..202b6224c
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js
@@ -0,0 +1,180 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('CoreAdminController',
+ function($scope, $routeParams, $location, $timeout, $route, Cores, Update, Constants){
+ $scope.resetMenu("cores", Constants.IS_ROOT_PAGE);
+ $scope.selectedCore = $routeParams.corename; // use 'corename' not 'core' to distinguish from /solr/:core/
+ $scope.refresh = function() {
+ Cores.get(function(data) {
+ var coreCount = 0;
+ var cores = data.status;
+ for (_obj in cores) coreCount++;
+ $scope.hasCores = coreCount >0;
+ if (!$scope.selectedCore && coreCount==0) {
+ $scope.showAddCore();
+ return;
+ } else if (!$scope.selectedCore) {
+ for (firstCore in cores) break;
+ $scope.selectedCore = firstCore;
+ $location.path("/~cores/" + $scope.selectedCore).replace();
+ }
+ $scope.core = cores[$scope.selectedCore];
+ $scope.corelist = [];
+ $scope.swapCorelist = [];
+ for (var core in cores) {
+ $scope.corelist.push(cores[core]);
+ if (cores[core] != $scope.core) {
+ $scope.swapCorelist.push(cores[core]);
+ }
+ }
+ if ($scope.swapCorelist.length>0) {
+ $scope.swapOther = $scope.swapCorelist[0].name;
+ }
+ });
+ };
+ $scope.showAddCore = function() {
+ $scope.hideAll();
+ $scope.showAdd = true;
+ $scope.newCore = {
+ name: "new_core",
+ dataDir: "data",
+ instanceDir: "new_core",
+ config: "solrconfig.xml",
+ schema: "schema.xml",
+ collection: "",
+ shard: ""
+ };
+ };
+
+ $scope.addCore = function() {
+ if (!$scope.newCore.name) {
+ $scope.addMessage = "Please provide a core name";
+ } else if (false) { //@todo detect whether core exists
+ $scope.AddMessage = "A core with that name already exists";
+ } else {
+ var params = {
+ name: $scope.newCore.name,
+ instanceDir: $scope.newCore.instanceDir,
+ config: $scope.newCore.config,
+ schema: $scope.newCore.schema,
+ dataDir: $scope.newCore.dataDir
+ };
+ if ($scope.isCloud) {
+ params.collection = $scope.newCore.collection;
+ params.shard = $scope.newCore.shard;
+ }
+ Cores.add(params, function(data) {
+ $location.path("/~cores/" + $scope.newCore.name);
+ $scope.cancelAddCore();
+ });
+ }
+ };
+
+ $scope.cancelAddCore = function() {
+ delete $scope.addMessage;
+ $scope.showAdd = false
+ };
+
+ $scope.unloadCore = function() {
+ var answer = confirm( 'Do you really want to unload Core "' + $scope.selectedCore + '"?' );
+ if( !answer ) return;
+ Cores.unload({core: $scope.selectedCore}, function(data) {
+ $location.path("/~cores");
+ });
+ };
+
+ $scope.showRenameCore = function() {
+ $scope.hideAll();
+ $scope.showRename = true;
+ };
+
+ $scope.renameCore = function() {
+ if (!$scope.other) {
+ $scope.renameMessage = "Please provide a new name for the " + $scope.selectedCore + " core";
+ } else if ($scope.other == $scope.selectedCore) {
+ $scope.renameMessage = "New name must be different from the current one";
+ } else {
+ Cores.rename({core:$scope.selectedCore, other: $scope.other}, function(data) {
+ $location.path("/~cores/" + $scope.other);
+ $scope.cancelRename();
+ });
+ }
+ };
+
+ $scope.cancelRenameCore = function() {
+ $scope.showRename = false;
+ delete $scope.renameMessage;
+ $scope.other = "";
+ };
+
+ $scope.showSwapCores = function() {
+ $scope.hideAll();
+ $scope.showSwap = true;
+ };
+
+ $scope.swapCores = function() {
+ if (!$scope.swapOther) {
+ $scope.swapMessage = "Please select a core to swap with";
+ } else if ($scope.swapOther == $scope.selectedCore) {
+ $scope.swapMessage = "Cannot swap with the same core";
+ } else {
+ Cores.swap({core: $scope.selectedCore, other: $scope.swapOther}, function(data) {
+ $location.path("/~cores/" + $scope.swapOther);
+ delete $scope.swapOther;
+ $scope.cancelSwapCores();
+ });
+ }
+ };
+
+ $scope.cancelSwapCores = function() {
+ delete $scope.swapMessage;
+ $scope.showSwap = false;
+ }
+
+ $scope.reloadCore = function() {
+ if ($scope.initFailures[$scope.selectedCore]) {
+ delete $scope.initFailures[$scope.selectedCore];
+ $scope.showInitFailures = Object.keys(data.initFailures).length>0;
+ }
+ Cores.reload({core: $scope.selectedCore},
+ function(data) {
+ if (data.error) {
+ $scope.reloadFailure = true;
+ $timeout(function() {
+ $scope.reloadFailure = false;
+ $route.reload();
+ }, 1000);
+ } else {
+ $scope.reloadSuccess = true;
+ $timeout(function () {
+ $scope.reloadSuccess = false;
+ $route.reload();
+ }, 1000);
+ }
+ });
+ };
+
+ $scope.hideAll = function() {
+ $scope.showRename = false;
+ $scope.showAdd = false;
+ $scope.showSwap = false;
+ };
+
+ $scope.refresh();
+ }
+);
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js
new file mode 100644
index 000000000..c31b6f0fb
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js
@@ -0,0 +1,302 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+var dataimport_timeout = 2000;
+
+solrAdminApp.controller('DataImportController',
+ function($scope, $rootScope, $routeParams, $location, $timeout, $interval, $cookies, Mbeans, DataImport, Constants) {
+ $scope.resetMenu("dataimport", Constants.IS_COLLECTION_PAGE);
+
+ $scope.refresh = function () {
+ Mbeans.info({core: $routeParams.core, cat: 'QUERY'}, function (data) {
+ var mbeans = data['solr-mbeans'][1];
+ $scope.handlers = [];
+ for (var key in mbeans) {
+ if (mbeans[key]['class'] !== key && mbeans[key]['class'] === 'org.apache.solr.handler.dataimport.DataImportHandler') {
+ $scope.handlers.push(key);
+ }
+ }
+ $scope.hasHandlers = $scope.handlers.length > 0;
+
+ if (!$routeParams.handler) {
+ $location.path("/" + $routeParams.core + "/dataimport/" + $scope.handlers[0]);
+ } else {
+ $scope.currentHandler = $routeParams.handler;
+ }
+ });
+
+ $scope.handler = $routeParams.handler;
+ if ($scope.handler && $scope.handler[0]=="/") {
+ $scope.handler = $scope.handler.substr(1);
+ }
+ if ($scope.handler) {
+ DataImport.config({core: $routeParams.core, name: $scope.handler}, function (data) {
+ try {
+ $scope.config = data.config;
+ var xml = $.parseXML(data.config);
+ $scope.entities = [];
+ $('document > entity', xml).each(function (i, element) {
+ $scope.entities.push($(element).attr('name'));
+ });
+ $scope.refreshStatus();
+ } catch (err) {
+ console.log(err);
+ }
+ });
+ }
+ $scope.lastUpdate = "unknown";
+ $scope.lastUpdateUTC = "";
+ };
+
+ $scope.toggleDebug = function () {
+ $scope.isDebugMode = !$scope.isDebugMode;
+ if ($scope.isDebugMode) {
+ // also enable Debug checkbox
+ $scope.form.showDebug = true;
+ }
+ $scope.showConfiguration = true;
+ }
+
+ $scope.toggleConfiguration = function () {
+ $scope.showConfiguration = !$scope.showConfiguration;
+ }
+
+ $scope.toggleRawStatus = function () {
+ $scope.showRawStatus = !$scope.showRawStatus;
+ }
+
+ $scope.toggleRawDebug = function () {
+ $scope.showRawDebug = !$scope.showRawDebug;
+ }
+
+ $scope.reload = function () {
+ DataImport.reload({core: $routeParams.core, name: $scope.handler}, function () {
+ $scope.reloaded = true;
+ $timeout(function () {
+ $scope.reloaded = false;
+ }, 5000);
+ $scope.refresh();
+ });
+ }
+
+ $scope.form = {
+ command: "full-import",
+ verbose: false,
+ clean: false,
+ commit: true,
+ showDebug: false,
+ custom: "",
+ core: $routeParams.core
+ };
+
+ $scope.submit = function () {
+ var params = {};
+ for (var key in $scope.form) {
+ if (key == "showDebug") {
+ if ($scope.form.showDebug) {
+ params["debug"] = true;
+ }
+ } else {
+ params[key] = $scope.form[key];
+ }
+ }
+ if (params.custom.length) {
+ var customParams = $scope.form.custom.split("&");
+ for (var i in customParams) {
+ var parts = customParams[i].split("=");
+ params[parts[0]] = parts[1];
+ }
+ }
+ delete params.custom;
+
+ if ($scope.isDebugMode) {
+ params.dataConfig = $scope.config;
+ }
+
+ params.core = $routeParams.core;
+ params.name = $scope.handler;
+
+ DataImport.post(params, function (data) {
+ $scope.rawResponse = JSON.stringify(data, null, 2);
+ $scope.refreshStatus();
+ });
+ };
+
+ $scope.abort = function () {
+ $scope.isAborting = true;
+ DataImport.abort({core: $routeParams.core, name: $scope.handler}, function () {
+ $timeout(function () {
+ $scope.isAborting = false;
+ $scope.refreshStatus();
+ }, 4000);
+ });
+ }
+
+ $scope.refreshStatus = function () {
+
+ console.log("Refresh Status");
+
+ $scope.isStatusLoading = true;
+ DataImport.status({core: $routeParams.core, name: $scope.handler}, function (data) {
+ if (data[0] == "<") {
+ $scope.hasHandlers = false;
+ return;
+ }
+
+ var now = new Date();
+ $scope.lastUpdate = now.toTimeString().split(' ').shift();
+ $scope.lastUpdateUTC = now.toUTCString();
+ var messages = data.statusMessages;
+ var messagesCount = 0;
+ for( var key in messages ) { messagesCount++; }
+
+ if (data.status == 'busy') {
+ $scope.status = "indexing";
+
+ $scope.timeElapsed = data.statusMessages['Time Elapsed'];
+ $scope.elapsedSeconds = parseSeconds($scope.timeElapsed);
+
+ var info = $scope.timeElapsed ? 'Indexing since ' + $scope.timeElapsed : 'Indexing ...';
+ $scope.info = showInfo(messages, true, info, $scope.elapsedSeconds);
+
+ } else if (messages.RolledBack) {
+ $scope.status = "failure";
+ $scope.info = showInfo(messages, true);
+ } else if (messages.Aborted) {
+ $scope.status = "aborted";
+ $scope.info = showInfo(messages, true, 'Aborting current Import ...');
+ } else if (data.status == "idle" && messagesCount != 0) {
+ $scope.status = "success";
+ $scope.info = showInfo(messages, true);
+ } else {
+ $scope.status = "idle";
+ $scope.info = showInfo(messages, false, 'No information available (idle)');
+ }
+
+ delete data.$promise;
+ delete data.$resolved;
+
+ $scope.rawStatus = JSON.stringify(data, null, 2);
+
+ $scope.isStatusLoading = false;
+ $scope.statusUpdated = true;
+ $timeout(function () {
+ $scope.statusUpdated = false;
+ }, dataimport_timeout / 2);
+ });
+ };
+
+ $scope.updateAutoRefresh = function () {
+ $scope.autorefresh = !$scope.autorefresh;
+ $cookies.dataimport_autorefresh = $scope.autorefresh ? true : null;
+ if ($scope.autorefresh) {
+ $scope.refreshTimeout = $interval($scope.refreshStatus, dataimport_timeout);
+ var onRouteChangeOff = $scope.$on('$routeChangeStart', function() {
+ $interval.cancel($scope.refreshTimeout);
+ onRouteChangeOff();
+ });
+
+ } else if ($scope.refreshTimeout) {
+ $interval.cancel($scope.refreshTimeout);
+ }
+ $scope.refreshStatus();
+ };
+
+ $scope.refresh();
+
+});
+
+var showInfo = function (messages, showFull, info_text, elapsed_seconds) {
+
+ var info = {};
+ if (info_text) {
+ info.text = info_text;
+ } else {
+ info.text = messages[''] || '';
+ // format numbers included in status nicely
+ /* @todo this pretty printing is hard to work out how to do in an Angularesque way:
+ info.text = info.text.replace(/\d{4,}/g,
+ function (match, position, string) {
+ return app.format_number(parseInt(match, 10));
+ }
+ );
+ */
+
+ var time_taken_text = messages['Time taken'];
+ info.timeTaken = parseSeconds(time_taken_text);
+ }
+ info.showDetails = false;
+
+ if (showFull) {
+ if (!elapsed_seconds) {
+ var time_taken_text = messages['Time taken'];
+ elapsed_seconds = parseSeconds(time_taken_text);
+ }
+
+ info.showDetails = true;
+
+ var document_config = {
+ 'Requests': 'Total Requests made to DataSource',
+ 'Fetched': 'Total Rows Fetched',
+ 'Skipped': 'Total Documents Skipped',
+ 'Processed': 'Total Documents Processed'
+ };
+
+ info.docs = [];
+ for (var key in document_config) {
+ var value = parseInt(messages[document_config[key]], 10);
+ var doc = {desc: document_config[key], name: key, value: value};
+ if (elapsed_seconds && key != 'Skipped') {
+ doc.speed = Math.round(value / elapsed_seconds);
+ }
+ info.docs.push(doc);
+ }
+
+ var dates_config = {
+ 'Started': 'Full Dump Started',
+ 'Aborted': 'Aborted',
+ 'Rolledback': 'Rolledback'
+ };
+
+ info.dates = [];
+ for (var key in dates_config) {
+ var value = messages[dates_config[key]];
+ if (value) {
+ value = value.replace(" ", "T")+".000Z";
+ console.log(value);
+ var date = {desc: dates_config[key], name: key, value: value};
+ info.dates.push(date);
+ }
+ }
+ }
+ return info;
+}
+
+var parseSeconds = function(time) {
+ var seconds = 0;
+ var arr = new String(time || '').split('.');
+ var parts = arr[0].split(':').reverse();
+
+ for (var i = 0; i < parts.length; i++) {
+ seconds += ( parseInt(parts[i], 10) || 0 ) * Math.pow(60, i);
+ }
+
+ if (arr[1] && 5 <= parseInt(arr[1][0], 10)) {
+ seconds++; // treat more or equal than .5 as additional second
+ }
+ return seconds;
+}
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js
new file mode 100644
index 000000000..d38265a05
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js
@@ -0,0 +1,137 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+ */
+//helper for formatting JSON and others
+
+var DOC_PLACEHOLDER = '<doc>\n' +
+ '<field name="id">change.me</field>' +
+ '<field name="title">change.me</field>' +
+ '</doc>';
+
+var ADD_PLACEHOLDER = '<add>\n' + DOC_PLACEHOLDER + '</add>\n';
+
+solrAdminApp.controller('DocumentsController',
+ function($scope, $rootScope, $routeParams, $location, Luke, Update, FileUpload, Constants) {
+ $scope.resetMenu("documents", Constants.IS_COLLECTION_PAGE);
+
+ $scope.refresh = function () {
+ Luke.schema({core: $routeParams.core}, function(data) {
+ //TODO: handle dynamic fields
+ delete data.schema.fields._version_;
+ $scope.fields = Object.keys(data.schema.fields);
+ });
+ $scope.document = "";
+ $scope.handler = "/update";
+ $scope.type = "json";
+ $scope.commitWithin = 1000;
+ $scope.overwrite = true;
+ };
+
+ $scope.refresh();
+
+ $scope.changeDocumentType = function () {
+ $scope.placeholder = "";
+ if ($scope.type == 'json') {
+ $scope.placeholder = '{"id":"change.me","title":"change.me"}';
+ } else if ($scope.type == 'csv') {
+ $scope.placeholder = "id,title\nchange.me,change.me";
+ } else if ($scope.type == 'solr') {
+ $scope.placeholder = ADD_PLACEHOLDER;
+ } else if ($scope.type == 'xml') {
+ $scope.placeholder = DOC_PLACEHOLDER;
+ }
+ };
+
+ $scope.addWizardField = function () {
+ if ($scope.document == "") $scope.document = "{}";
+ var doc = JSON.parse($scope.document);
+ doc[$scope.fieldName] = $scope.fieldData;
+ $scope.document = JSON.stringify(doc, null, '\t');
+ $scope.fieldData = "";
+ };
+
+ $scope.submit = function () {
+ var contentType = "";
+ var postData = "";
+ var params = {};
+ var doingFileUpload = false;
+
+ if ($scope.handler[0] == '/') {
+ params.handler = $scope.handler.substring(1);
+ } else {
+ params.handler = 'update';
+ params.qt = $scope.handler;
+ }
+
+ params.commitWithin = $scope.commitWithin;
+ params.overwrite = $scope.overwrite;
+ params.core = $routeParams.core;
+ params.wt = "json";
+
+ if ($scope.type == "json" || $scope.type == "wizard") {
+ postData = "[" + $scope.document + "]";
+ contentType = "json";
+ } else if ($scope.type == "csv") {
+ postData = $scope.document;
+ contentType = "csv";
+ } else if ($scope.type == "xml") {
+ postData = "<add>" + $scope.document + "</add>";
+ contentType = "xml";
+ } else if ($scope.type == "upload") {
+ doingFileUpload = true;
+ params.raw = $scope.literalParams;
+ } else if ($scope.type == "solr") {
+ postData = $scope.document;
+ if (postData[0] == "<") {
+ contentType = "xml";
+ } else if (postData[0] == "{" || postData[0] == '[') {
+ contentType = "json";
+ } else {
+ alert("Cannot identify content type")
+ }
+ }
+ if (!doingFileUpload) {
+ var callback = function (success) {
+ $scope.responseStatus = "success";
+ delete success.$promise;
+ delete success.$resolved;
+ $scope.response = JSON.stringify(success, null, ' ');
+ };
+ var failure = function (failure) {
+ $scope.responseStatus = failure;
+ };
+ if (contentType == "json") {
+ Update.postJson(params, postData, callback, failure);
+ } else if (contentType == "xml") {
+ Update.postXml(params, postData, callback, failure);
+ } else if (contentType == "csv") {
+ Update.postCsv(params, postData, callback, failure);
+ }
+ } else {
+ var file = $scope.fileUpload;
+ console.log('file is ' + JSON.stringify(file));
+ var uploadUrl = "/fileUpload";
+ FileUpload.upload(params, $scope.fileUpload, function (success) {
+ $scope.responseStatus = "success";
+ $scope.response = JSON.stringify(success, null, ' ');
+ }, function (failure) {
+ $scope.responseStatus = "failure";
+ $scope.response = JSON.stringify(failure, null, ' ');
+ });
+ }
+ }
+ });
+
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js
new file mode 100644
index 000000000..c1d7abfe2
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js
@@ -0,0 +1,100 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+var contentTypeMap = { xml : 'text/xml', html : 'text/html', js : 'text/javascript', json : 'application/json', 'css' : 'text/css' };
+var languages = {js: "javascript", xml:"xml", xsl:"xml", vm: "xml", html: "xml", json: "json", css: "css"};
+
+solrAdminApp.controller('FilesController',
+ function($scope, $rootScope, $routeParams, $location, Files, Constants) {
+ $scope.resetMenu("files", Constants.IS_COLLECTION_PAGE);
+
+ $scope.file = $location.search().file;
+ $scope.content = null;
+
+ $scope.baseurl = $location.absUrl().substr(0,$location.absUrl().indexOf("#")); // Including /solr/ context
+
+ $scope.refresh = function () {
+
+ var process = function (path, tree) {
+ var params = {core: $routeParams.core};
+ if (path.slice(-1) == '/') {
+ params.file = path.slice(0, -1);
+ } else if (path!='') {
+ params.file = path;
+ }
+
+ Files.list(params, function (data) {
+ var filenames = Object.keys(data.files);
+ filenames.sort();
+ for (var i in filenames) {
+ var file = filenames[i];
+ var filedata = data.files[file];
+ var state = undefined;
+ var children = undefined;
+
+ if (filedata.directory) {
+ file = file + "/";
+ if ($scope.file && $scope.file.indexOf(path + file) == 0) {
+ state = "open";
+ } else {
+ state = "closed";
+ }
+ children = [];
+ process(path + file, children);
+ }
+ tree.push({
+ data: {
+ title: file,
+ attr: { id: path + file}
+ },
+ children: children,
+ state: state
+ });
+ }
+ });
+ }
+ $scope.tree = [];
+ process("", $scope.tree);
+
+ if ($scope.file && $scope.file != '' && $scope.file.split('').pop()!='/') {
+ var extension;
+ if ($scope.file == "managed-schema") {
+ extension = contentTypeMap['xml'];
+ } else {
+ extension = $scope.file.match( /\.(\w+)$/)[1] || '';
+ }
+ var contentType = (contentTypeMap[extension] || 'text/plain' ) + ';charset=utf-8';
+
+ Files.get({core: $routeParams.core, file: $scope.file, contentType: contentType}, function(data) {
+ $scope.content = data.data;
+ $scope.url = data.config.url + "?" + $.param(data.config.params); // relative URL
+ if (contentType.indexOf("text/plain") && (data.data.indexOf("<?xml")>=0) || data.data.indexOf("<!--")>=0) {
+ $scope.lang = "xml";
+ } else {
+ $scope.lang = languages[extension] || "txt";
+ }
+ });
+ }
+ };
+
+ $scope.showTreeLink = function(data) {
+ var file = data.args[0].id;
+ $location.search({file:file});
+ };
+
+ $scope.refresh();
+ });
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js
new file mode 100644
index 000000000..f7ce5a8d2
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js
@@ -0,0 +1,97 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('IndexController', function($scope, System, Cores, Constants) {
+ $scope.resetMenu("index", Constants.IS_ROOT_PAGE);
+ $scope.reload = function() {
+ System.get(function(data) {
+ $scope.system = data;
+
+ // load average
+ var load_average = ( data.system.uptime || '' ).match( /load averages?: (\d+[.,]\d\d),? (\d+[.,]\d\d),? (\d+[.,]\d\d)/ );
+ if (load_average) {
+ for (var i=0;i<2;i++) {
+ load_average[i]=load_average[i].replace(",","."); // for European users
+ }
+ $scope.load_average = load_average.slice(1);
+ }
+
+ // physical memory
+ var memoryMax = parse_memory_value(data.system.totalPhysicalMemorySize);
+ $scope.memoryTotal = parse_memory_value(data.system.totalPhysicalMemorySize - data.system.freePhysicalMemorySize);
+ $scope.memoryPercentage = ($scope.memoryTotal / memoryMax * 100).toFixed(1)+ "%";
+ $scope.memoryMax = pretty_print_bytes(memoryMax);
+ $scope.memoryTotalDisplay = pretty_print_bytes($scope.memoryTotal);
+
+ // swap space
+ var swapMax = parse_memory_value(data.system.totalSwapSpaceSize);
+ $scope.swapTotal = parse_memory_value(data.system.totalSwapSpaceSize - data.system.freeSwapSpaceSize);
+ $scope.swapPercentage = ($scope.swapTotal / swapMax * 100).toFixed(1)+ "%";
+ $scope.swapMax = pretty_print_bytes(swapMax);
+ $scope.swapTotalDisplay = pretty_print_bytes($scope.swapTotal);
+
+ // file handles
+ $scope.fileDescriptorPercentage = (data.system.openFileDescriptorCount / data.system.maxFileDescriptorCount *100).toFixed(1) + "%";
+
+ // java memory
+ var javaMemoryMax = parse_memory_value(data.jvm.memory.raw.max || data.jvm.memory.max);
+ $scope.javaMemoryTotal = parse_memory_value(data.jvm.memory.raw.total || data.jvm.memory.total);
+ $scope.javaMemoryUsed = parse_memory_value(data.jvm.memory.raw.used || data.jvm.memory.used);
+ $scope.javaMemoryTotalPercentage = ($scope.javaMemoryTotal / javaMemoryMax *100).toFixed(1) + "%";
+ $scope.javaMemoryUsedPercentage = ($scope.javaMemoryUsed / $scope.javaMemoryTotal *100).toFixed(1) + "%";
+ $scope.javaMemoryPercentage = ($scope.javaMemoryUsed / javaMemoryMax * 100).toFixed(1) + "%";
+ $scope.javaMemoryTotalDisplay = pretty_print_bytes($scope.javaMemoryTotal);
+ $scope.javaMemoryUsedDisplay = pretty_print_bytes($scope.javaMemoryUsed); // @todo These should really be an AngularJS Filter: {{ javaMemoryUsed | bytes }}
+ $scope.javaMemoryMax = pretty_print_bytes(javaMemoryMax);
+
+ // no info bar:
+ $scope.noInfo = !(
+ data.system.totalPhysicalMemorySize && data.system.freePhysicalMemorySize &&
+ data.system.totalSwapSpaceSize && data.system.freeSwapSpaceSize &&
+ data.system.openFileDescriptorCount && data.system.maxFileDescriptorCount);
+
+ // command line args:
+ $scope.commandLineArgs = data.jvm.jmx.commandLineArgs.sort();
+ });
+ };
+ $scope.reload();
+});
+
+var parse_memory_value = function( value ) {
+ if( value !== Number( value ) )
+ {
+ var units = 'BKMGTPEZY';
+ var match = value.match( /^(\d+([,\.]\d+)?) (\w).*$/ );
+ var value = parseFloat( match[1] ) * Math.pow( 1024, units.indexOf( match[3].toUpperCase() ) );
+ }
+
+ return value;
+};
+
+var pretty_print_bytes = function(byte_value) {
+ var unit = null;
+
+ byte_value /= 1024;
+ byte_value /= 1024;
+ unit = 'MB';
+
+ if( 1024 <= byte_value ) {
+ byte_value /= 1024;
+ unit = 'GB';
+ }
+ return byte_value.toFixed( 2 ) + ' ' + unit;
+};
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js
new file mode 100644
index 000000000..001edfb1a
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js
@@ -0,0 +1,45 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('JavaPropertiesController',
+ function($scope, Properties, Constants){
+ $scope.resetMenu("java-props", Constants.IS_ROOT_PAGE);
+ $scope.refresh = function() {
+ Properties.get(function(data) {
+ var sysprops = data["system.properties"];
+ var sep = sysprops["path.separator"]
+ var props = [];
+ for (var key in sysprops) {
+ var value = sysprops[key];
+ var key = key.replace(/\./g, '.&#8203;');
+ if (key.indexOf(".path")!=-1 || key.indexOf(".dirs")) {
+ var values = [];
+ var parts = value.split(sep);
+ for (var i in parts) {
+ values.push({pos:i, value:parts[i]})
+ }
+ props.push({name: key, values: values});
+ } else {
+ props.push({name: key, values: [value]});
+ }
+ }
+ $scope.props = props;
+ });
+ };
+
+ $scope.refresh();
+ });
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js
new file mode 100644
index 000000000..585c90a21
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js
@@ -0,0 +1,158 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+var format_time_content = function( time, timeZone ) {
+ var format_time_options = {};
+ if (timeZone && timeZone!="Local") {
+ format_time_options.timeZone = timeZone;
+ }
+ return time.toLocaleString( undefined, format_time_options );
+}
+
+solrAdminApp.controller('LoggingController',
+ function($scope, $timeout, $cookies, Logging, Constants){
+ $scope.resetMenu("logging", Constants.IS_ROOT_PAGE);
+ $scope.timezone = $cookies.logging_timezone || "Local";
+ $scope.refresh = function() {
+ Logging.events(function(data) {
+ $scope.since = new Date();
+ $scope.sinceDisplay = format_time_content($scope.since, "Local");
+ var events = data.history.docs;
+ for (var i=0; i<events.length; i++) {
+ var event = events[i];
+ var time = new Date(event.time);
+ event.local_time = format_time_content(time, "Local");
+ event.utc_time = format_time_content(time, "UTC");
+ event.loggerBase = event.logger.split( '.' ).pop();
+
+ if( !event.trace ) {
+ var lines = event.message.split( "\n" );
+ if( lines.length > 1) {
+ event.trace = event.message;
+ event.message = lines[0];
+ }
+ }
+ event.message = event.message.replace(/,/g, ',&#8203;');
+ event.showTrace = false;
+ }
+ $scope.events = events;
+ $scope.watcher = data.watcher;
+ /* @todo sticky_mode
+ // state element is in viewport
+ sticky_mode = ( state.position().top <= $( window ).scrollTop() + $( window ).height() - ( $( 'body' ).height() - state.position().top ) );
+ // initial request
+ if( 0 === since ) {
+ sticky_mode = true;
+ }
+ $scope.loggingEvents = events;
+
+ if( sticky_mode )
+ {
+ $( 'body' )
+ .animate
+ (
+ { scrollTop: state.position().top },
+ 1000
+ );
+ }
+ */
+ });
+ $scope.timeout = $timeout($scope.refresh, 10000);
+ var onRouteChangeOff = $scope.$on('$routeChangeStart', function() {
+ $timeout.cancel($scope.timeout);
+ onRouteChangeOff();
+ });
+ };
+ $scope.refresh();
+ $scope.toggleRefresh = function() {
+ if(!$scope.stopped) {
+ $scope.stopped = true;
+ $timeout.cancel($scope.timeout);
+ } else {
+ $scope.stopped = false;
+ $scope.timeout = $timeout($scope.refresh, 10000);
+ }
+ };
+ $scope.toggleTimezone = function() {
+ $scope.timezone = ($scope.timezone=="Local") ? "UTC":"Local";
+ $cookies.logging_timezone = $scope.timezone;
+ }
+ $scope.toggleRow = function(event) {
+ event.showTrace =! event.showTrace;
+ };
+ }
+)
+
+.controller('LoggingLevelController',
+ function($scope, Logging) {
+ $scope.resetMenu("logging-levels");
+
+ var packageOf = function(logger) {
+ var parts = logger.name.split(".");
+ return !parts.pop() ? "" : parts.join(".");
+ };
+
+ var shortNameOf = function(logger) {return logger.name.split(".").pop();}
+
+ var makeTree = function(loggers, packag) {
+ var tree = [];
+ for (var i=0; i<loggers.length; i++) {
+ var logger = loggers[i];
+ logger.packag = packageOf(logger);
+ logger.short = shortNameOf(logger);
+ if (logger.packag == packag) {
+ logger.children = makeTree(loggers, logger.name);
+ tree.push(logger);
+ }
+ }
+ return tree;
+ };
+
+ $scope.refresh = function() {
+ Logging.levels(function(data) {
+ $scope.logging = makeTree(data.loggers, "");
+ $scope.watcher = data.watcher;
+ $scope.levels = [];
+ for (level in data.levels) {
+ $scope.levels.push({name:data.levels[level], pos:level});
+ }
+ });
+ };
+
+ $scope.toggleOptions = function(logger) {
+ if (logger.showOptions) {
+ logger.showOptions = false;
+ delete $scope.currentLogger;
+ } else {
+ if ($scope.currentLogger) {
+ $scope.currentLogger.showOptions = false;
+ }
+ logger.showOptions = true;
+ $scope.currentLogger = logger;
+ }
+ };
+
+ $scope.setLevel = function(logger, newLevel) {
+ var setString = logger.name + ":" + newLevel;
+ logger.showOptions = false;
+ Logging.setLevel({set: setString}, function(data) {
+ $scope.refresh();
+ });
+ };
+
+ $scope.refresh();
+ });
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js
new file mode 100644
index 000000000..8127c6fda
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js
@@ -0,0 +1,317 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('LoginController',
+ ['$scope', '$routeParams', '$rootScope', '$location', '$window', 'AuthenticationService', 'Constants',
+ function ($scope, $routeParams, $rootScope, $location, $window, AuthenticationService, Constants) {
+ $scope.resetMenu("login", Constants.IS_ROOT_PAGE);
+ $scope.subPath = $routeParams.route;
+ $rootScope.exceptions = {};
+
+ // Session variables set in app.js 401 interceptor
+ var wwwAuthHeader = sessionStorage.getItem("auth.wwwAuthHeader");
+ var authScheme = sessionStorage.getItem("auth.scheme");
+ if (wwwAuthHeader) {
+ // Parse www-authenticate header
+ var wwwHeader = wwwAuthHeader.match(/(\w+)(\s+)?(.*)/);
+ authScheme = "unknown";
+ var authParams = {};
+ if (wwwHeader && wwwHeader.length >= 1)
+ authScheme = wwwHeader[1];
+ if (wwwHeader && wwwHeader.length >= 3)
+ authParams = www_auth_parse_params(wwwHeader[3]);
+ if (typeof authParams === 'string' || authParams instanceof String) {
+ $scope.authParamsError = authParams;
+ } else {
+ $scope.authParamsError = undefined;
+ }
+ var realm = authParams['realm'];
+ sessionStorage.setItem("auth.realm", realm);
+ if (authScheme === 'Basic' || authScheme === 'xBasic') {
+ authScheme = 'Basic';
+ }
+ sessionStorage.setItem("auth.scheme", authScheme);
+ }
+
+ var supportedSchemes = ['Basic', 'Bearer', 'Negotiate'];
+ $scope.authSchemeSupported = supportedSchemes.includes(authScheme);
+
+ if (authScheme === 'Bearer') {
+ // Check for OpenId redirect response
+ var errorText = "";
+ $scope.isCallback = false;
+ if ($scope.subPath === 'callback') {
+ $scope.isCallback = true;
+ var hash = $location.hash();
+ var hp = AuthenticationService.decodeHashParams(hash);
+ var expectedState = sessionStorage.getItem("auth.stateRandom") + "_" + sessionStorage.getItem("auth.location");
+ sessionStorage.setItem("auth.state", "error");
+ if (hp['access_token'] && hp['token_type'] && hp['state']) {
+ // Validate state
+ if (hp['state'] !== expectedState) {
+ $scope.error = "Problem with auth callback";
+ console.log("Expected state param " + expectedState + " but got " + hp['state']);
+ errorText += "Invalid values in state parameter. ";
+ }
+ // Validate token type
+ if (hp['token_type'].toLowerCase() !== "bearer") {
+ console.log("Expected token_type param 'bearer', but got " + hp['token_type']);
+ errorText += "Invalid values in token_type parameter. ";
+ }
+ // Unpack ID token and validate nonce, get username
+ if (hp['id_token']) {
+ var idToken = hp['id_token'].split(".");
+ if (idToken.length === 3) {
+ var payload = AuthenticationService.decodeJwtPart(idToken[1]);
+ if (!payload['nonce'] || payload['nonce'] !== sessionStorage.getItem("auth.nonce")) {
+ errorText += "Invalid 'nonce' value, possible attack detected. Please log in again. ";
+ }
+
+ if (errorText === "") {
+ sessionStorage.setItem("auth.username", payload['sub']);
+ sessionStorage.setItem("auth.header", "Bearer " + hp['access_token']);
+ sessionStorage.removeItem("auth.statusText");
+ sessionStorage.removeItem("auth.stateRandom");
+ sessionStorage.removeItem("auth.wwwAuthHeader");
+ console.log("User " + payload['sub'] + " is logged in");
+ var redirectTo = sessionStorage.getItem("auth.location");
+ console.log("Redirecting to stored location " + redirectTo);
+ sessionStorage.setItem("auth.state", "authenticated");
+ sessionStorage.removeItem("http401");
+ $location.path(redirectTo).hash("");
+ }
+ } else {
+ console.log("Expected JWT compact id_token param but got " + idToken);
+ errorText += "Invalid values in id_token parameter. ";
+ }
+ } else {
+ console.log("Callback was missing the id_token parameter, could not validate nonce.");
+ errorText += "Callback was missing the id_token parameter, could not validate nonce. ";
+ }
+ if (hp['access_token'].split(".").length !== 3) {
+ console.log("Expected JWT compact access_token param but got " + hp['access_token']);
+ errorText += "Invalid values in access_token parameter. ";
+ }
+ if (errorText !== "") {
+ $scope.error = "Problems with OpenID callback";
+ $scope.errorDescription = errorText;
+ $scope.http401 = "true";
+ }
+ // End callback processing
+ } else if (hp['error']) {
+ // The callback had errors
+ console.log("Error received from idp: " + hp['error']);
+ var errorDescriptions = {};
+ errorDescriptions['invalid_request'] = "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.";
+ errorDescriptions['unauthorized_client'] = "The client is not authorized to request an access token using this method.";
+ errorDescriptions['access_denied'] = "The resource owner or authorization server denied the request.";
+ errorDescriptions['unsupported_response_type'] = "The authorization server does not support obtaining an access token using this method.";
+ errorDescriptions['invalid_scope'] = "The requested scope is invalid, unknown, or malformed.";
+ errorDescriptions['server_error'] = "The authorization server encountered an unexpected condition that prevented it from fulfilling the request.";
+ errorDescriptions['temporarily_unavailable'] = "The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.";
+ $scope.error = "Callback from Id Provider contained error. ";
+ if (hp['error_description']) {
+ $scope.errorDescription = decodeURIComponent(hp['error_description']);
+ } else {
+ $scope.errorDescription = errorDescriptions[hp['error']];
+ }
+ if (hp['error_uri']) {
+ $scope.errorDescription += " More information at " + hp['error_uri'] + ". ";
+ }
+ if (hp['state'] !== expectedState) {
+ $scope.errorDescription += "The state parameter returned from ID Provider did not match the one we sent.";
+ }
+ sessionStorage.setItem("auth.state", "error");
+ }
+ }
+ }
+
+ if (errorText === "" && !$scope.error && authParams) {
+ $scope.error = authParams['error'];
+ $scope.errorDescription = authParams['error_description'];
+ $scope.authData = AuthenticationService.getAuthDataHeader();
+ }
+
+ $scope.authScheme = sessionStorage.getItem("auth.scheme");
+ $scope.authRealm = sessionStorage.getItem("auth.realm");
+ $scope.wwwAuthHeader = sessionStorage.getItem("auth.wwwAuthHeader");
+ $scope.statusText = sessionStorage.getItem("auth.statusText");
+ $scope.authLocation = sessionStorage.getItem("auth.location");
+ $scope.authLoggedinUser = sessionStorage.getItem("auth.username");
+ $scope.authHeader = sessionStorage.getItem("auth.header");
+
+ $scope.login = function () {
+ AuthenticationService.SetCredentials($scope.username, $scope.password);
+ $location.path($scope.authLocation); // Redirect to the location that caused the login prompt
+ };
+
+ $scope.logout = function() {
+ // reset login status
+ AuthenticationService.ClearCredentials();
+ $location.path("/");
+ };
+
+ $scope.jwtLogin = function () {
+ var stateRandom = Math.random().toString(36).substr(2);
+ sessionStorage.setItem("auth.stateRandom", stateRandom);
+ var authState = stateRandom + "_" + sessionStorage.getItem("auth.location");
+ var authNonce = Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2);
+ sessionStorage.setItem("auth.nonce", authNonce);
+ var params = {
+ "response_type" : "id_token token",
+ "client_id" : $scope.authData['client_id'],
+ "redirect_uri" : $window.location.href.split('#')[0],
+ "scope" : "openid " + $scope.authData['scope'],
+ "state" : authState,
+ "nonce" : authNonce
+ };
+
+ var endpointBaseUrl = $scope.authData['authorizationEndpoint'];
+ var loc = endpointBaseUrl + "?" + paramsToString(params);
+ console.log("Redirecting to " + loc);
+ sessionStorage.setItem("auth.state", "expectCallback");
+ $window.location.href = loc;
+
+ function paramsToString(params) {
+ var arr = [];
+ for (var p in params) {
+ if( params.hasOwnProperty(p) ) {
+ arr.push(p + "=" + encodeURIComponent(params[p]));
+ }
+ }
+ return arr.join("&");
+ }
+ };
+
+ $scope.jwtIsLoginNode = function() {
+ var redirect = $scope.authData ? $scope.authData['redirect_uris'] : undefined;
+ if (redirect && Array.isArray(redirect) && redirect.length > 0) {
+ var isLoginNode = false;
+ redirect.forEach(function(uri) { // Check that current node URL is among the configured callback URIs
+ if ($window.location.href.startsWith(uri)) isLoginNode = true;
+ });
+ return isLoginNode;
+ } else {
+ return true; // no redirect UIRs configured, all nodes are potential login nodes
+ }
+ };
+
+ $scope.jwtFindLoginNode = function() {
+ var redirect = $scope.authData ? $scope.authData['redirect_uris'] : undefined;
+ if (redirect && Array.isArray(redirect) && redirect.length > 0) {
+ var loginNode = redirect[0];
+ redirect.forEach(function(uri) { // if current node is in list, return its callback uri
+ if ($window.location.href.startsWith(uri)) loginNode = uri;
+ });
+ return loginNode;
+ } else {
+ return $window.location.href.split('#')[0]; // Return base url of current URL as the url to use
+ }
+ };
+
+ // Redirect to login node if this is not a valid one
+ $scope.jwtGotoLoginNode = function() {
+ if (!$scope.jwtIsLoginNode()) {
+ $window.location.href = $scope.jwtFindLoginNode();
+ }
+ };
+
+ $scope.jwtLogout = function() {
+ // reset login status
+ AuthenticationService.ClearCredentials();
+ $location.path("/");
+ };
+
+ $scope.isLoggedIn = function() {
+ return (sessionStorage.getItem("auth.username") !== null);
+ };
+ }]);
+
+// This function is copied and adapted from MIT-licensed https://github.com/randymized/www-authenticate/blob/master/lib/parsers.js
+www_auth_parse_params= function (header) {
+ // This parser will definitely fail if there is more than one challenge
+ var params = {};
+ var tok, last_tok, _i, _len, key, value;
+ var state= 0; //0: token,
+ var m= header.split(/([",=])/);
+ for (_i = 0, _len = m.length; _i < _len; _i++) {
+ last_tok= tok;
+ tok = m[_i];
+ if (!tok.length) continue;
+ switch (state) {
+ case 0: // token
+ key= tok.trim();
+ state= 1; // expect equals
+ continue;
+ case 1: // expect equals
+ if ('=' != tok) return 'Equal sign was expected after '+key;
+ state= 2;
+ continue;
+ case 2: // expect value
+ if ('"' == tok) {
+ value= '';
+ state= 3; // expect quoted
+ continue;
+ }
+ else {
+ params[key]= value= tok.trim();
+ state= 9; // expect comma or end
+ continue;
+ }
+ case 3: // handling quoted string
+ if ('"' == tok) {
+ state= 8; // end quoted
+ continue;
+ }
+ else {
+ value+= tok;
+ state= 3; // continue accumulating quoted string
+ continue;
+ }
+ case 8: // end quote encountered
+ if ('"' == tok) {
+ // double quoted
+ value+= '"';
+ state= 3; // back to quoted string
+ continue;
+ }
+ if (',' == tok) {
+ params[key]= value;
+ state= 0;
+ continue;
+ }
+ else {
+ return 'Unexpected token ('+tok+') after '+value+'"';
+ }
+ continue;
+ case 9: // expect commma
+ if (',' != tok) return 'Comma expected after '+value;
+ state= 0;
+ continue;
+ }
+ }
+ switch (state) { // terminal state
+ case 0: // Empty or ignoring terminal comma
+ case 9: // Expecting comma or end of header
+ return params;
+ case 8: // Last token was end quote
+ params[key]= value;
+ return params;
+ default:
+ return 'Unexpected end of www-authenticate value.';
+ }
+};
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js
new file mode 100644
index 000000000..a537b37d7
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js
@@ -0,0 +1,167 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('PluginsController',
+ function($scope, $rootScope, $routeParams, $location, Mbeans, Constants) {
+ $scope.resetMenu("plugins", Constants.IS_CORE_PAGE);
+
+ if ($routeParams.legacytype) {
+ // support legacy URLs. Angular cannot change #path without reloading controller
+ $location.path("/"+$routeParams.core+"/plugins");
+ $location.search("type", $routeParams.legacytype);
+ return;
+ }
+
+ $scope.refresh = function() {
+ Mbeans.stats({core: $routeParams.core}, function (data) {
+ var type = $location.search().type;
+ $scope.types = getPluginTypes(data, type);
+ $scope.type = getSelectedType($scope.types, type);
+
+ if ($scope.type && $routeParams.entry) {
+ $scope.plugins = $routeParams.entry.split(",");
+ openPlugins($scope.type, $scope.plugins);
+ } else {
+ $scope.plugins = [];
+ }
+ });
+ };
+
+ $scope.selectPluginType = function(type) {
+ $location.search({entry:null, type: type.lower});
+ $scope.type = type;
+ };
+
+ $scope.selectPlugin = function(plugin) {
+ plugin.open = !plugin.open;
+
+ if (plugin.open) {
+ $scope.plugins.push(plugin.name);
+ } else {
+ $scope.plugins.splice($scope.plugins.indexOf(plugin.name), 1);
+ }
+
+ if ($scope.plugins.length==0) {
+ $location.search("entry", null);
+ } else {
+ $location.search("entry", $scope.plugins.join(','));
+ }
+ }
+
+ $scope.startRecording = function() {
+ $scope.isRecording = true;
+ Mbeans.reference({core: $routeParams.core}, function(data) {
+ $scope.reference = data.reference;
+ console.log($scope.reference);
+ })
+ }
+
+ $scope.stopRecording = function() {
+ $scope.isRecording = false;
+ console.log($scope.reference);
+ Mbeans.delta({core: $routeParams.core}, $scope.reference, function(data) {
+ parseDelta($scope.types, data);
+ });
+ }
+
+ $scope.refresh();
+ });
+
+var getPluginTypes = function(data, selected) {
+ var keys = [];
+ var mbeans = data["solr-mbeans"];
+ for (var i=0; i<mbeans.length; i+=2) {
+ var key = mbeans[i];
+ var lower = key.toLowerCase();
+ var plugins = getPlugins(mbeans[i+1]);
+ if (plugins.length == 0) continue;
+ keys.push({name: key,
+ selected: lower == selected,
+ changes: 0,
+ lower: lower,
+ plugins: plugins
+ });
+ }
+ keys.sort(function(a,b) {return a.name > b.name});
+ return keys;
+};
+
+var getPlugins = function(data) {
+ var plugins = [];
+ for (var key in data) {
+ var pluginProperties = data[key];
+ var stats = pluginProperties.stats;
+ delete pluginProperties.stats;
+ for (var stat in stats) {
+ // add breaking space after a bracket or @ to handle wrap long lines:
+ stats[stat] = new String(stats[stat]).replace( /([\(@])/g, '$1&#8203;');
+ }
+ plugin = {name: key, changed: false, stats: stats, open:false};
+ plugin.properties = pluginProperties;
+ plugins.push(plugin);
+ }
+ plugins.sort(function(a,b) {return a.name > b.name});
+ return plugins;
+};
+
+var getSelectedType = function(types, selected) {
+ if (selected) {
+ for (var i in types) {
+ if (types[i].lower == selected) {
+ return types[i];
+ }
+ }
+ }
+};
+
+var parseDelta = function(types, data) {
+
+ var getByName = function(list, name) {
+ for (var i in list) {
+ if (list[i].name == name) return list[i];
+ }
+ }
+
+ var mbeans = data["solr-mbeans"]
+ for (var i=0; i<mbeans.length; i+=2) {
+ var typeName = mbeans[i];
+ var type = getByName(types, typeName);
+ var plugins = mbeans[i+1];
+ for (var key in plugins) {
+ var changedPlugin = plugins[key];
+ if (changedPlugin._changed_) {
+ var plugin = getByName(type.plugins, key);
+ var stats = changedPlugin.stats;
+ delete changedPlugin.stats;
+ plugin.properties = changedPlugin;
+ for (var stat in stats) {
+ // add breaking space after a bracket or @ to handle wrap long lines:
+ plugin.stats[stat] = new String(stats[stat]).replace( /([\(@])/g, '$1&#8203;');
+ }
+ plugin.changed = true;
+ type.changes++;
+ }
+ }
+ }
+};
+
+var openPlugins = function(type, selected) {
+ for (var i in type.plugins) {
+ var plugin = type.plugins[i];
+ plugin.open = selected.indexOf(plugin.name)>=0;
+ }
+}
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js
new file mode 100644
index 000000000..e8a38707a
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js
@@ -0,0 +1,120 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('QueryController',
+ function($scope, $routeParams, $location, Query, Constants){
+ $scope.resetMenu("query", Constants.IS_COLLECTION_PAGE);
+
+ // @todo read URL parameters into scope
+ $scope.query = {q:'*:*'};
+ $scope.filters = [{fq:""}];
+ $scope.dismax = {defType: "dismax"};
+ $scope.edismax = {defType: "edismax", stopwords: true, lowercaseOperators: false};
+ $scope.hl = {hl:"on"};
+ $scope.facet = {facet: "on"};
+ $scope.spatial = {};
+ $scope.spellcheck = {spellcheck:"on"};
+ $scope.debugQuery = {debugQuery: "on"};
+ $scope.qt = "/select";
+
+ $scope.doQuery = function() {
+ var params = {};
+
+ var set = function(key, value) {
+ if (params[key]) {
+ params[key].push(value);
+ } else {
+ params[key] = [value];
+ }
+ }
+ var copy = function(params, query) {
+ for (var key in query) {
+ terms = query[key];
+ // Booleans have no length property - only set them if true
+ if (((typeof(terms) == typeof(true) && terms) || terms.length > 0) && key[0]!="$") {
+ set(key, terms);
+ }
+ }
+ };
+
+ copy(params, $scope.query);
+
+ if ($scope.isDismax) copy(params, $scope.dismax);
+ if ($scope.isEdismax) copy(params, $scope.edismax);
+ if ($scope.isHighlight) copy(params, $scope.hl);
+ if ($scope.isFacet) copy(params, $scope.facet);
+ if ($scope.isSpatial) copy(params, $scope.spatial);
+ if ($scope.isSpellcheck) copy(params, $scope.spellcheck);
+ if ($scope.isDebugQuery) copy(params, $scope.debugQuery);
+
+ if ($scope.rawParams) {
+ var rawParams = $scope.rawParams.split(/[&\n]/);
+ for (var i in rawParams) {
+ var param = rawParams[i];
+ var equalPos = param.indexOf("=");
+ if (equalPos > -1) {
+ set(param.substring(0, equalPos), param.substring(equalPos+1));
+ } else {
+ set(param, ""); // Use empty value for params without "="
+ }
+ }
+ }
+
+ var qt = $scope.qt ? $scope.qt : "/select";
+
+ for (var filter in $scope.filters) {
+ copy(params, $scope.filters[filter]);
+ }
+
+ params.core = $routeParams.core;
+ if (qt[0] == '/') {
+ params.handler = qt.substring(1);
+ } else { // Support legacy style handleSelect=true configs
+ params.handler = "select";
+ set("qt", qt);
+ }
+ var url = Query.url(params);
+ Query.query(params, function(data) {
+ $scope.lang = $scope.query.wt;
+ if ($scope.lang == undefined || $scope.lang == '') {
+ $scope.lang = "json";
+ }
+ $scope.response = data;
+ // Use relative URL to make it also work through proxies that may have a different host/port/context
+ $scope.url = url;
+ $scope.hostPortContext = $location.absUrl().substr(0,$location.absUrl().indexOf("#")); // For display only
+ });
+ };
+
+ if ($location.search().q) {
+ $scope.query.q = $location.search()["q"];
+ $scope.doQuery();
+ }
+
+ $scope.removeFilter = function(index) {
+ if ($scope.filters.length === 1) {
+ $scope.filters = [{fq: ""}];
+ } else {
+ $scope.filters.splice(index, 1);
+ }
+ };
+
+ $scope.addFilter = function(index) {
+ $scope.filters.splice(index+1, 0, {fq:""});
+ };
+ }
+);
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js
new file mode 100644
index 000000000..9f7ac3e41
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js
@@ -0,0 +1,235 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('ReplicationController',
+ function($scope, $rootScope, $routeParams, $interval, $timeout, Replication, Constants) {
+ $scope.resetMenu("replication", Constants.IS_CORE_PAGE);
+
+ $scope.iterationCount = 1;
+
+ $scope.refresh = function() {
+ Replication.details({core:$routeParams.core}, function(response) {
+ var timeout;
+ var interval;
+ if ($scope.interval) $interval.cancel($scope.interval);
+ $scope.isSlave = (response.details.isSlave === 'true');
+ if ($scope.isSlave) {
+ $scope.progress = getProgressDetails(response.details.slave);
+ $scope.iterations = getIterations(response.details.slave);
+ $scope.versions = getSlaveVersions(response.details);
+ $scope.settings = getSlaveSettings(response.details);
+ if ($scope.settings.isReplicating) {
+ timeout = $timeout($scope.refresh, 1000);
+ } else if(!$scope.settings.isPollingDisabled && $scope.settings.pollInterval) {
+ interval = $scope.interval = $interval(function() {
+ $scope.settings.tick--;
+ }, 1000, $scope.settings.tick);
+ timeout = $timeout($scope.refresh, 1000*(1+$scope.settings.tick));
+ }
+ } else {
+ $scope.versions = getMasterVersions(response.details);
+ }
+ $scope.master = getMasterSettings(response.details, $scope.isSlave);
+
+ var onRouteChangeOff = $scope.$on('$routeChangeStart', function() {
+ if (interval) $interval.cancel(interval);
+ if (timeout) $timeout.cancel(timeout);
+ onRouteChangeOff();
+ });
+ });
+
+ };
+
+ $scope.execute = function(command) {
+ Replication.command({core:$routeParams.core, command:command}, function(data){$scope.refresh()});
+ }
+
+ $scope.showIterations = function() { $scope.iterationCount = 100000}; // limitTo should accept undefined, but doesn't work.
+ $scope.hideIterations = function() { $scope.iterationCount = 1};
+
+ $scope.refresh();
+ });
+
+var getProgressDetails = function(progress) {
+
+ progress.timeRemaining = parseSeconds(progress.timeRemaining);
+ progress.totalPercent = parseInt(progress.totalPercent);
+ if (progress.totalPercent === 0) {
+ progress.totalPercentWidth = "1px";
+ } else {
+ progress.totalPercentWidth = progress.totalPercent + "%";
+ }
+ progress.currentFileSizePercent = parseInt(progress.currentFileSizePercent);
+
+ if (!progress.indexReplicatedAtList) {
+ progress.indexReplicatedAtList = [];
+ }
+
+ if (!progress.replicationFailedAtList) {
+ progress.replicationFailedAtList = [];
+ }
+ return progress;
+};
+
+var getIterations = function(slave) {
+
+ var iterations = [];
+
+ var find = function(list, date) {
+ return list.filter(function(e) {return e.date == date});
+ };
+
+ for (var i in slave.indexReplicatedAtList) {
+ var date = slave.indexReplicatedAtList[i];
+ var iteration = {date:date, status:"replicated", latest: false};
+ if (date == slave.indexReplicatedAt) {
+ iteration.latest = true;
+ }
+ iterations.push(iteration);
+ }
+
+ for (var i in slave.replicationFailedAtList) {
+ var failedDate = slave.replicationFailedAtList[i];
+ var matchingIterations = find(iterations, failedDate);
+ if (matchingIterations[0]) {
+ iteration = matchingIterations[0];
+ iteration.status = "failed";
+ } else {
+ iteration = {date: failedDate, status:"failed", latest:false};
+ iterations.push(iteration);
+ }
+ if (failedDate == slave.replicationFailedAt) {
+ iteration.latest = true;
+ }
+ }
+ iterations.sort(function(a,b){ return a.date> b.date;}).reverse();
+ return iterations;
+};
+
+var getMasterVersions = function(data) {
+ versions = {masterSearch:{}, master:{}};
+
+ versions.masterSearch.version = data.indexVersion;
+ versions.masterSearch.generation = data.generation;
+ versions.masterSearch.size = data.indexSize;
+
+ versions.master.version = data.master.replicableVersion || '-';
+ versions.master.generation = data.master.replicableGeneration || '-';
+ versions.master.size = '-';
+
+ return versions;
+};
+
+var getSlaveVersions = function(data) {
+ versions = {masterSearch: {}, master: {}, slave: {}};
+
+ versions.slave.version = data.indexVersion;
+ versions.slave.generation = data.generation;
+ versions.slave.size = data.indexSize;
+
+ versions.master.version = data.slave.masterDetails.replicableVersion || '-';
+ versions.master.generation = data.slave.masterDetails.replicableGeneration || '-';
+ versions.master.size = '-';
+
+ versions.masterSearch.version = data.slave.masterDetails.indexVersion;
+ versions.masterSearch.generation = data.slave.masterDetails.generation;
+ versions.masterSearch.size = data.slave.masterDetails.indexSize;
+
+ versions.changedVersion = data.indexVersion !== data.slave.masterDetails.indexVersion;
+ versions.changedGeneration = data.generation !== data.slave.masterDetails.generation;
+
+ return versions;
+};
+
+var parseDateToEpoch = function(date) {
+ // ["Sat Mar 03 11:00:00 CET 2012", "Sat", "Mar", "03", "11:00:00", "CET", "2012"]
+ var parts = date.match( /^(\w+)\s+(\w+)\s+(\d+)\s+(\d+\:\d+\:\d+)\s+(\w+)\s+(\d+)$/ );
+
+ // "Sat Mar 03 2012 10:37:33"
+ var d = new Date( parts[1] + ' ' + parts[2] + ' ' + parts[3] + ' ' + parts[6] + ' ' + parts[4] );
+ return d.getTime();
+}
+
+var parseSeconds = function(time) {
+ var seconds = 0;
+ var arr = new String(time || '').split('.');
+ var parts = arr[0].split(':').reverse();
+
+ for (var i = 0; i < parts.length; i++) {
+ seconds += ( parseInt(parts[i], 10) || 0 ) * Math.pow(60, i);
+ }
+
+ if (arr[1] && 5 <= parseInt(arr[1][0], 10)) {
+ seconds++; // treat more or equal than .5 as additional second
+
+ }
+
+ return seconds;
+}
+
+var getSlaveSettings = function(data) {
+ var settings = {};
+ settings.masterUrl = data.slave.masterUrl;
+ settings.isPollingDisabled = data.slave.isPollingDisabled == 'true';
+ settings.pollInterval = data.slave.pollInterval;
+ settings.isReplicating = data.slave.isReplicating == 'true';
+ settings.nextExecutionAt = data.slave.nextExecutionAt;
+
+ if(settings.isReplicating) {
+ settings.isApprox = true;
+ settings.tick = parseSeconds(settings.pollInterval);
+ } else if (!settings.isPollingDisabled && settings.pollInterval) {
+ if( settings.nextExecutionAt ) {
+ settings.nextExecutionAtEpoch = parseDateToEpoch(settings.nextExecutionAt);
+ settings.currentTime = parseDateToEpoch(data.slave.currentDate);
+
+ if( settings.nextExecutionAtEpoch > settings.currentTime) {
+ settings.isApprox = false;
+ settings.tick = ( settings.nextExecutionAtEpoch - settings.currentTime) / 1000;
+ }
+ }
+ }
+ return settings;
+};
+
+var getMasterSettings = function(details, isSlave) {
+ var master = {};
+ var masterData = isSlave ? details.slave.masterDetails.master : details.master;
+ master.replicationEnabled = masterData.replicationEnabled == "true";
+ master.replicateAfter = masterData.replicateAfter.join(", ");
+
+ if (masterData.confFiles) {
+ master.files = [];
+ var confFiles = masterData.confFiles.split(',');
+ for (var i=0; i<confFiles.length; i++) {
+ var file = confFiles[i];
+ var short = file;
+ var title = file;
+ if (file.indexOf(":")>=0) {
+ title = file.replace(':', ' » ');
+ var parts = file.split(':');
+ if (isSlave) {
+ short = parts[1];
+ } else {
+ short = parts[0];
+ }
+ }
+ master.files.push({title:title, name:short});
+ }
+ }
+ return master;
+}
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js
new file mode 100644
index 000000000..8a80d4297
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js
@@ -0,0 +1,611 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+var cookie_schema_browser_autoload = 'schema-browser_autoload';
+
+solrAdminApp.controller('SchemaController',
+ function($scope, $routeParams, $location, $cookies, $timeout, Luke, Constants, Schema, Config) {
+ $scope.resetMenu("schema", Constants.IS_COLLECTION_PAGE);
+
+ $scope.refresh = function () {
+ Luke.schema({core: $routeParams.core}, function (schema) {
+ Luke.raw({core: $routeParams.core}, function (index) {
+ var data = mergeIndexAndSchemaData(index, schema.schema);
+
+ $scope.fieldsAndTypes = getFieldsAndTypes(data);
+ $scope.is = {};
+
+ var search = $location.search();
+ leftbar = {};
+ $scope.isField = $scope.isDynamicField = $scope.isType = false;
+ $scope.showing = true;
+ if (search.field) {
+ $scope.selectedType = "Field";
+ $scope.is.field = true;
+ $scope.name = search.field;
+ leftbar.fields = [$scope.name];
+ var field = data.fields[$scope.name];
+ leftbar.types = [field.type];
+ if (field.dynamicBase) leftbar.dynamicFields = [field.dynamicBase];
+ if (field.copySources && field.copySources.length>0) {
+ leftbar.copyFieldSources = sortedObjectArray(field.copySources.sort());
+ }
+ if (field.copyDests && field.copyDests.length>0) {
+ leftbar.copyFieldDests = sortedObjectArray(field.copyDests.sort());
+ }
+ $scope.fieldOrType = "field=" + $scope.name;
+ } else if (search["dynamic-field"]) {
+ $scope.selectedType = "Dynamic Field";
+ $scope.is.dynamicField = true;
+ $scope.name = search["dynamic-field"];
+ leftbar.dynamicFields = [$scope.name];
+ leftbar.types = [data.dynamic_fields[$scope.name].type];
+ $scope.fieldOrType = "dynamic-field=" + $scope.name;
+ } else if (search.type) {
+ $scope.selectedType = "Type";
+ $scope.is.type = true;
+ $scope.name = search.type;
+ leftbar.types = [$scope.name];
+ leftbar.fields = filterFields("fields", data, $scope.name);
+ leftbar.dynamicFields = filterFields("dynamic_fields", data, $scope.name);
+ $scope.fieldOrType = "type=" + $scope.name;
+ } else {
+ $scope.showing = false;
+ }
+ $scope.leftbar = leftbar;
+ $scope.core = $routeParams.core;
+ $scope.uniqueKeyField = data.unique_key_field;
+ $scope.similarity = data.similarity;
+ if ($scope.similarity && $scope.similarity.className) {
+ $scope.similarity.className = shortenPackages($scope.similarity.className);
+ }
+ $scope.isUniqueKeyField = ($scope.selectedType == "Field" && $scope.name == $scope.uniqueKeyField);
+
+ $scope.display = getFieldProperties(data, $routeParams.core, $scope.is, $scope.name);
+ $scope.analysis = getAnalysisInfo(data, $scope.is, $scope.name);
+
+ $scope.isAutoload = $cookies[cookie_schema_browser_autoload] == "true";
+ if ($scope.isAutoload) {
+ $scope.toggleTerms();
+ }
+
+ $scope.types = Object.keys(schema.schema.types);
+ });
+ });
+ Config.get({core: $routeParams.core}, function(data) {
+ $scope.isSchemaUpdatable = (data.config.hasOwnProperty('schemaFactory') == false || data.config.schemaFactory.class == "ManagedIndexSchemaFactory");
+ });
+ };
+ $scope.refresh();
+
+ $scope.selectFieldOrType = function() {
+ $location.search($scope.fieldOrType);
+ }
+
+ $scope.toggleAnalyzer = function(analyzer) {
+ analyzer.show = !analyzer.show;
+ }
+
+ $scope.loadTermInfo = function() {
+ var params = {fl: $scope.name, core: $routeParams.core};
+ if ($scope.topTermsCount) {
+ params.numTerms = $scope.topTermsCount;
+ }
+ $scope.isLoadingTerms = true;
+ Luke.field(params, function (data) {
+ $scope.isLoadingTerms = false;
+ $scope.termInfo = getTermInfo(data.fields[$scope.name]);
+ if (!$scope.topTermsCount) {
+ $scope.topTermsCount = $scope.termInfo.termCount;
+ }
+ });
+ }
+
+ $scope.toggleTerms = function() {
+ $scope.showTerms = !$scope.showTerms;
+
+ if ($scope.showTerms) {
+ $scope.loadTermInfo();
+ }
+ }
+
+ $scope.loadAllTerms = function() {
+ $scope.topTermsCount = $scope.termInfo.maxTerms;
+ $scope.loadTermInfo();
+ }
+
+ $scope.toggleAutoload = function() {
+ $scope.isAutoload = !$scope.isAutoload;
+ $cookies[cookie_schema_browser_autoload] = $scope.isAutoload;
+ console.log("cookie: " + $cookies[cookie_schema_browser_autoload]);
+ }
+
+ $scope.hideAll = function() {
+ $scope.showAddField = false;
+ $scope.showAddDynamicField = false;
+ $scope.showAddCopyField = false;
+ }
+
+ $scope.toggleAddField = function() {
+ if ($scope.showAddField && $scope.adding == "field") {
+ $scope.hideAll();
+ } else {
+ $scope.hideAll();
+ $scope.showAddField = true;
+ $scope.adding = "field";
+
+ $scope.newField = {
+ stored: "true",
+ indexed: "true",
+ uninvertible: "true"
+ }
+ delete $scope.addErrors;
+ }
+ }
+
+ $scope.addField = function() {
+ delete $scope.addErrors;
+ var data = {"add-field": $scope.newField};
+ Schema.post({core: $routeParams.core}, data, function(data) {
+ if (data.errors) {
+ $scope.addErrors = data.errors[0].errorMessages;
+ if (typeof $scope.addErrors === "string") {
+ $scope.addErrors = [$scope.addErrors];
+ }
+ } else {
+ $scope.added = true;
+ $timeout(function() {
+ $scope.showAddField = false;
+ $scope.added = false;
+ $scope.refresh();
+ }, 1500);
+ }
+ });
+ }
+
+ $scope.toggleAddDynamicField = function() {
+ if ($scope.showAddField && $scope.adding == "dynamicField") {
+ $scope.hideAll();
+ } else {
+ $scope.hideAll();
+ $scope.showAddField = true;
+ $scope.adding = "dynamicField";
+
+ $scope.newField = {
+ stored: "true",
+ indexed: "true"
+ }
+ delete $scope.addErrors;
+ }
+ }
+
+ $scope.addDynamicField = function() {
+ delete $scope.addErrors;
+ var data = {"add-dynamic-field": $scope.newField};
+ Schema.post({core: $routeParams.core}, data, function(data) {
+ if (data.errors) {
+ $scope.addErrors = data.errors[0].errorMessages;
+ if (typeof $scope.addErrors === "string") {
+ $scope.addErrors = [$scope.addErrors];
+ }
+ } else {
+ $scope.added = true;
+ $timeout(function() {
+ $scope.showAddField = false;
+ $scope.added = false;
+ $scope.refresh();
+ }, 1500);
+ }
+ });
+ }
+
+ $scope.toggleAddCopyField = function() {
+ if ($scope.showAddCopyField) {
+ $scope.hideAll();
+ } else {
+ $scope.hideAll();
+ $scope.showAddCopyField = true;
+
+ $scope.copyField = {};
+ delete $scope.addCopyFieldErrors;
+ }
+ }
+ $scope.addCopyField = function() {
+ delete $scope.addCopyFieldErrors;
+ var data = {"add-copy-field": $scope.copyField};
+ Schema.post({core: $routeParams.core}, data, function(data) {
+ if (data.errors) {
+ $scope.addCopyFieldErrors = data.errors[0].errorMessages;
+ if (typeof $scope.addCopyFieldErrors === "string") {
+ $scope.addCopyFieldErrors = [$scope.addCopyFieldErrors];
+ }
+ } else {
+ $scope.showAddCopyField = false;
+ $timeout($scope.refresh, 1500);
+ }
+ });
+ }
+
+ $scope.toggleDelete = function() {
+ if ($scope.showDelete) {
+ $scope.showDelete = false;
+ } else {
+ if ($scope.is.field) {
+ $scope.deleteData = {'delete-field': {name: $scope.name}};
+ } else if ($scope.is.dynamicField) {
+ $scope.deleteData = {'delete-dynamic-field': {name: $scope.name}};
+ } else {
+ alert("TYPE NOT KNOWN");
+ }
+ $scope.showDelete = true;
+ }
+ }
+
+ $scope.delete = function() {
+ Schema.post({core: $routeParams.core}, $scope.deleteData, function(data) {
+ if (data.errors) {
+ $scope.deleteErrors = data.errors[0].errorMessages;
+ if (typeof $scope.deleteErrors === "string") {
+ $scope.deleteErrors = [$scope.deleteErrors];
+ }
+ } else {
+ $scope.deleted = true;
+ $timeout(function() {
+ $location.search("");
+ }, 1500
+ );
+ }
+ });
+ }
+ $scope.toggleDeleteCopyField = function(field) {
+ field.show = !field.show;
+ delete field.errors;
+ }
+ $scope.deleteCopyField = function(field, source, dest) {
+ data = {'delete-copy-field': {source: source, dest: dest}};
+ Schema.post({core: $routeParams.core}, data, function(data) {
+ if (data.errors) {
+ field.errors = data.errors[0].errorMessages;
+ if (typeof $scope.deleteErrors === "string") {
+ field.errors = [field.errors];
+ }
+ } else {
+ field.deleted = true;
+ $timeout($scope.refresh, 1500);
+ }
+ });
+ }
+ }
+);
+
+var getFieldsAndTypes = function(data) {
+ var fieldsAndTypes = [];
+ var fields = Object.keys(data.fields).sort();
+ for (var i in fields) {
+ fieldsAndTypes.push({
+ group: "Fields",
+ value: "field=" + fields[i],
+ label: fields[i]
+ });
+ }
+ var dynamic_fields = Object.keys(data.dynamic_fields).sort();
+ for (var i in dynamic_fields) {
+ fieldsAndTypes.push({
+ group: "Dynamic Fields",
+ value: "dynamic-field=" + dynamic_fields[i],
+ label: dynamic_fields[i]
+ });
+ }
+ var types = Object.keys(data.types).sort();
+ for (var i in types) {
+ fieldsAndTypes.push({
+ group: "Types",
+ value: "type=" + types[i],
+ label: types[i]
+ });
+ }
+ return fieldsAndTypes;
+};
+
+var filterFields = function(type, data, name) {
+ var fields = [];
+ for (var i in data.types[name].fields) {
+ var field = data.types[name].fields[i];
+ if (data[type][field]) {
+ fields.push(field)
+ }
+ }
+ return fields.sort();
+}
+
+var mergeIndexAndSchemaData = function(index, schema) {
+
+ var data = {
+ unique_key_field: null,
+ similarity: null,
+ key: {},
+ fields: {},
+ dynamic_fields: {},
+ types: {},
+ relations: {
+ f_df: {},
+ f_t: {},
+ df_f: {},
+ df_t: {},
+ t_f: {},
+ t_df: {}
+ }
+ };
+
+ data.fields = index.fields;
+
+ data.key = index.info.key;
+
+ data.unique_key_field = schema.uniqueKeyField;
+ data.similarity = schema.similarity;
+
+ data.dynamic_fields = schema.dynamicFields;
+ data.types = schema.types;
+
+ for (var field in schema.fields) {
+ data.fields[field] =
+ $.extend({}, data.fields[field], schema.fields[field]);
+ }
+
+ for (var field in data.fields) {
+ var copy_dests = data.fields[field].copyDests;
+ for (var i in copy_dests) {
+ var copy_dest = copy_dests[i];
+ if (!data.fields[copy_dest]) {
+ data.fields[copy_dest] = {
+ partial: true,
+ copySources: []
+ };
+ }
+
+ if (data.fields[copy_dest].partial) {
+ data.fields[copy_dest].copySources.push(field);
+ }
+ }
+
+ var copy_sources = data.fields[field].copySources;
+ for (var i in copy_sources) {
+ var copy_source = copy_sources[i];
+ if (!data.fields[copy_source]) {
+ data.fields[copy_source] = {
+ partial: true,
+ copyDests: []
+ };
+ }
+
+ if (data.fields[copy_source].partial) {
+ data.fields[copy_source].copyDests.push(field);
+ }
+ }
+
+ data.relations.f_t[field] = data.fields[field].type;
+
+ if (!data.relations.t_f[data.fields[field].type]) {
+ data.relations.t_f[data.fields[field].type] = [];
+ }
+ data.relations.t_f[data.fields[field].type].push(field);
+
+ if (data.fields[field].dynamicBase) {
+ data.relations.f_df[field] = data.fields[field].dynamicBase;
+
+ if (!data.relations.df_f[data.fields[field].dynamicBase]) {
+ data.relations.df_f[data.fields[field].dynamicBase] = [];
+ }
+ data.relations.df_f[data.fields[field].dynamicBase].push(field);
+ }
+ }
+
+ for (var dynamic_field in data.dynamic_fields) {
+ data.relations.df_t[dynamic_field] = data.dynamic_fields[dynamic_field].type;
+
+ if (!data.relations.t_df[data.dynamic_fields[dynamic_field].type]) {
+ data.relations.t_df[data.dynamic_fields[dynamic_field].type] = [];
+ }
+ data.relations.t_df[data.dynamic_fields[dynamic_field].type].push(dynamic_field);
+ }
+ return data;
+};
+
+var getFieldProperties = function(data, core, is, name) {
+
+ var display = {};
+
+ display.partialState = is.field && !!data.fields[name].partial;
+
+ display.columns = [];
+ display.rows = [];
+ var allFlags = "";
+
+ var addRow = function(name, flags) {
+ if (flags[0]!='(') {
+ display.rows.push({name:name, flags:flags});
+ for (var i in flags) {
+ if (flags[i]!="-" && allFlags.indexOf(flags[i])<0) {
+ allFlags+=flags[i];
+ }
+ }
+ } else {
+ display.rows.push({name:name, comment:flags});
+ }
+ }
+
+ // Identify the rows for our field property table
+ if (is.field && data.fields[name]) {
+ if (data.fields[name].flags) {
+ addRow('Properties', data.fields[name].flags);
+ }
+ if (data.fields[name].schema) {
+ addRow('Schema', data.fields[name].schema);
+ }
+ if (data.fields[name].index) {
+ addRow('Index', data.fields[name].index);
+ }
+ display.docs = data.fields[name].docs;
+ display.docsUrl = "#/" + core + "/query?q=" + name + ":[* TO *]";
+ display.distinct = data.fields[name].distinct;
+ display.positionIncrementGap = data.fields[name].positionIncrementGap;
+ if (data.types[data.fields[name].type]) {
+ display.similarity = data.types[data.fields[name].type].similarity;
+ } else {
+ display.similarity = null;
+ }
+ } else if (is.dynamicField && data.dynamic_fields[name] && data.dynamic_fields[name].flags) {
+ addRow('Properties', data.dynamic_fields[name].flags);
+ display.similarity = data.types[data.dynamic_fields[name].type].similarity;
+ } else if (is.type && data.types[name]) {
+ display.similarity = data.types[name].similarity;
+ }
+ if (display.similarity && display.similarity.className) {
+ display.similarity.className = shortenPackages(display.similarity.className);
+ }
+
+ // identify columns in field property table:
+ for (var key in data.key) {
+ if (allFlags.indexOf(key)>=0) {
+ display.columns.push({key: key, name: data.key[key]});
+ }
+ }
+
+ // identify rows and cell values in field property table:
+ for (var i in display.rows) {
+ var row = display.rows[i];
+ row.cells = [];
+
+ if (!row.flags) {
+ continue; // Match the special case in the LukeRequestHandler
+ }
+
+ for (var j in display.columns) {
+ var flag = display.columns[j].key;
+ row.cells.push({key: flag, value: row.flags.indexOf(flag)>=0});
+ }
+ }
+
+ return display;
+};
+
+var getAnalysisInfo = function(data, is, name) {
+
+ var analysis = {};
+
+ if (is.field) {
+ var type = data.relations.f_t[name];
+ analysis.query = "analysis.fieldname=" + name;
+ }
+ else if (is.dynamicField) {
+ var type = data.relations.df_t[name];
+ analysis.query = "analysis.fieldtype=" + type;
+ }
+ else if (is.type) {
+ var type = name;
+ analysis.query = "analysis.fieldtype=" + name;
+ }
+
+ var processComponentType = function (label, key, componentTypeData) {
+ if (componentTypeData) {
+ var components = [];
+ for (var componentName in componentTypeData) {
+ var componentData = componentTypeData[componentName];
+ var component = {className: componentData.className, args:[]};
+ if (componentData.args) {
+ for (var argName in componentData.args) {
+ var argValue = componentData.args[argName];
+ if (argValue == "1" || argValue == "true") {
+ component.args.push({name: argName, booleanValue:true});
+ } else if (argValue == "0" || argValue == "false") {
+ component.args.push({name: argName, booleanValue:false});
+ } else {
+ component.args.push({name: argName, value:argValue});
+ }
+ }
+ }
+ components.push(component);
+ }
+ return {label: label, key: key, components: components};
+ } else {
+ return {label: label, key: key};
+ }
+ }
+
+ var buildAnalyzer = function (analyzerData) {
+ var analyzer = {};
+ analyzer.className = analyzerData.className;
+ analyzer.componentTypes = [];
+ if (analyzerData.tokenizer) {
+ analyzer.componentTypes.push(processComponentType("Char Filters", "charFilters", analyzerData.charFilters));
+ analyzer.componentTypes.push(processComponentType("Tokenizer", "tokenizer", {tokenizer: analyzerData.tokenizer}));
+ analyzer.componentTypes.push(processComponentType("Token Filters", "tokenFilters", analyzerData.filters));
+ }
+ return analyzer;
+ }
+
+ analysis.data = data.types[type];
+ if (analysis.data) {
+ analysis.analyzers = [
+ {key: "index", name: "Index", detail: buildAnalyzer(analysis.data.indexAnalyzer)},
+ {key: "query", name: "Query", detail: buildAnalyzer(analysis.data.queryAnalyzer)}
+ ];
+ }
+ return analysis;
+}
+
+var getTermInfo = function(data) {
+
+ var termInfo = {};
+ if (data && data.topTerms) {
+ termInfo.topTerms = [];
+
+ var currentGroup = {count: 0}
+ for (var i = 0; i < data.topTerms.length; i += 2) {
+ var count = data.topTerms[i + 1];
+ if (currentGroup.count != count) {
+ currentGroup = {count: count, terms: []};
+ termInfo.topTerms.push(currentGroup);
+ }
+ currentGroup.terms.push(data.topTerms[i]);
+ }
+ termInfo.termCount = data.topTerms.length / 2;
+ termInfo.maxTerms = data.distinct;
+ }
+
+ if(data && data.histogram) {
+ termInfo.histogram = [];
+ termInfo.histogramMax = 0;
+ for (var i = 0; i < data.histogram.length; i += 2) {
+ termInfo.histogram.push({key: data.histogram[i], value: data.histogram[i + 1]});
+ termInfo.histogramMax = Math.max(termInfo.histogramMax, data.histogram[i + 1]);
+ }
+ }
+ return termInfo;
+};
+
+var sortedObjectArray = function(list) {
+ var objarr = [];
+ for (var i in list) {
+ objarr.push({"name": list[i]});
+ }
+ return objarr;
+};
+
+var shortenPackages = function(className) {
+ return className.replace("org.apache.solr", "o.a.s").replace("org.apache.lucene", "o.a.l");
+};
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js
new file mode 100644
index 000000000..e835cc084
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js
@@ -0,0 +1,99 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+var MB_FACTOR = 1024*1024;
+
+solrAdminApp.controller('SegmentsController', function($scope, $routeParams, $interval, Segments, Constants) {
+ $scope.resetMenu("segments", Constants.IS_CORE_PAGE);
+
+ $scope.refresh = function() {
+
+ Segments.get({core: $routeParams.core}, function(data) {
+ var segments = data.segments;
+
+ var segmentSizeInBytesMax = getLargestSegmentSize(segments);
+ $scope.segmentMB = Math.floor(segmentSizeInBytesMax / MB_FACTOR);
+ $scope.xaxis = calculateXAxis(segmentSizeInBytesMax);
+
+ $scope.documentCount = 0;
+ $scope.deletionCount = 0;
+
+ $scope.segments = [];
+ for (var name in segments) {
+ var segment = segments[name];
+
+ var segmentSizeInBytesLog = Math.log(segment.sizeInBytes);
+ var segmentSizeInBytesMaxLog = Math.log(segmentSizeInBytesMax);
+
+ segment.totalSize = Math.floor((segmentSizeInBytesLog / segmentSizeInBytesMaxLog ) * 100);
+
+ segment.deletedDocSize = Math.floor((segment.delCount / segment.size) * segment.totalSize);
+ if (segment.delDocSize <= 0.001) delete segment.deletedDocSize;
+
+ segment.aliveDocSize = segment.totalSize - segment.deletedDocSize;
+
+ $scope.segments.push(segment);
+
+ $scope.documentCount += segment.size;
+ $scope.deletionCount += segment.delCount;
+ }
+ $scope.deletionsPercentage = calculateDeletionsPercentage($scope.documentCount, $scope.deletionCount);
+ });
+ };
+
+ $scope.toggleAutoRefresh = function() {
+ $scope.autorefresh = !$scope.autorefresh;
+ if ($scope.autorefresh) {
+ $scope.interval = $interval($scope.refresh, 1000);
+ var onRouteChangeOff = $scope.$on('$routeChangeStart', function() {
+ $interval.cancel($scope.interval);
+ onRouteChangeOff();
+ });
+
+ } else if ($scope.interval) {
+ $interval.cancel($scope.interval);
+ }
+ };
+ $scope.refresh();
+});
+
+var calculateXAxis = function(segmentInBytesMax) {
+ var steps = [];
+ var log = Math.log(segmentInBytesMax);
+
+ for (var j=0, step=log/4; j<3; j++, step+=log/4) {
+ steps.push({pos:j, value:Math.floor((Math.pow(Math.E, step))/MB_FACTOR)})
+ }
+ return steps;
+};
+
+var getLargestSegmentSize = function(segments) {
+ var max = 0;
+ for (var name in segments) {
+ max = Math.max(max, segments[name].sizeInBytes);
+ }
+ return max;
+};
+
+var calculateDeletionsPercentage = function(docCount, delCount) {
+ if (docCount == 0) {
+ return 0;
+ } else {
+ var percent = delCount / docCount * 100;
+ return Math.round(percent * 100) / 100;
+ }
+};
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js
new file mode 100644
index 000000000..92440c360
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js
@@ -0,0 +1,239 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+ */
+solrAdminApp.controller('StreamController',
+ function($scope, $routeParams, $location, Query, Constants) {
+
+ $scope.resetMenu("stream", Constants.IS_COLLECTION_PAGE);
+
+ $scope.stream = {
+ wt: 'json',
+ expr: $scope.expr,
+ indent: 'on'
+ };
+ $scope.qt = "stream";
+ $scope.doExplanation = false
+
+ $scope.doStream = function() {
+
+ var params = {};
+ params.core = $routeParams.core;
+ params.handler = $scope.qt;
+ params.expr = [$scope.expr]
+ if($scope.doExplanation){
+ params.explain = [$scope.doExplanation]
+ }
+
+ $scope.lang = "json";
+ $scope.response = null;
+ $scope.url = "";
+
+ var url = Query.url(params);
+
+ Query.query(params, function(data) {
+
+ var jsonData = JSON.parse(data.toJSON().data);
+ if (undefined != jsonData["explanation"]) {
+ $scope.showExplanation = true;
+
+ streamGraphSubController($scope, jsonData["explanation"])
+ delete jsonData["explanation"]
+ } else {
+ $scope.showExplanation = false;
+ }
+
+ data.data = JSON.stringify(jsonData,null,2);
+
+ $scope.lang = "json";
+ $scope.response = data;
+ $scope.url = url;
+ $scope.hostPortContext = $location.absUrl().substr(0,$location.absUrl().indexOf("#")); // For display only
+
+ });
+ };
+
+ if ($location.search().expr) {
+ $scope.expr = $location.search()["expr"];
+ $scope.doStream();
+ }
+
+ }
+);
+
+var streamGraphSubController = function($scope, explanation) {
+ $scope.showGraph = true;
+ $scope.pos = 0;
+ $scope.rows = 8;
+
+ $scope.resetGraph = function() {
+ $scope.pos = 0;
+ $scope.initGraph();
+ }
+
+ $scope.initGraph = function(explanation) {
+
+ data = explanation
+
+ var leafCount = 0;
+ var maxDepth = 0;
+ var rootNode = {};
+
+ leafCount = 0;
+
+ let recurse = function(dataNode, depth) {
+
+ if (depth > maxDepth) {
+ maxDepth = depth;
+ }
+
+ let graphNode = {
+ name: dataNode.expressionNodeId,
+ implementingClass: 'unknown',
+ data: {}
+ };
+
+ ["expressionNodeId", "expressionType", "functionName", "implementingClass", "expression", "note", "helpers"].forEach(function(key) {
+ graphNode.data[key] = dataNode[key];
+ });
+
+ if (dataNode.children && dataNode.children.length > 0) {
+ graphNode.children = [];
+ dataNode.children.forEach(function(n) {
+ graphNode.children.push(recurse(n, depth + 1));
+ });
+ } else {
+ ++leafCount;
+ }
+
+ return graphNode;
+ }
+
+ $scope.showPaging = false;
+ $scope.isRadial = false;
+ $scope.explanationData = recurse(data, 1);
+
+ $scope.depth = maxDepth + 1;
+ $scope.leafCount = leafCount;
+ };
+
+ $scope.initGraph(explanation);
+};
+
+solrAdminApp.directive('explanationGraph', function(Constants) {
+ return {
+ restrict: 'EA',
+ scope: {
+ data: "=",
+ leafCount: "=",
+ depth: "="
+ },
+ link: function(scope, element, attrs) {
+
+ var helper_path_class = function(p) {
+ var classes = ['link'];
+
+ return classes.join(' ');
+ };
+
+ var helper_node_class = function(d) {
+ var classes = ['node'];
+
+ if (d.data && d.data.expressionType) {
+ classes.push(d.data.expressionType);
+ }
+
+ return classes.join(' ');
+ };
+
+ var helper_node_text = function(d) {
+ if (d.data && d.data.functionName) {
+ return d.data.functionName;
+ }
+
+ return d.name
+ };
+
+ var helper_tooltip = function(d) {
+
+ return [
+ "Function: " + d.data.functionName,
+ "Type: " + d.data.expressionType,
+ "Class: " + d.data.implementingClass.replace("org.apache.solr.client.solrj.io", "o.a.s.c.s.i"),
+ "=============",
+ d.data.expression
+ ].join("\n");
+ }
+
+ scope.$watch("data", function(newValue, oldValue) {
+ if (newValue) {
+ flatGraph(element, scope.data, scope.depth, scope.leafCount);
+ }
+ });
+
+ var flatGraph = function(element, graphData, depth, leafCount) {
+ var w = 100 + (depth * 100),
+ h = leafCount * 40;
+
+ var tree = d3.layout.tree().size([h, w]);
+
+ var diagonal = d3.svg.diagonal().projection(function(d) {
+ return [d.y * .7, d.x];
+ });
+
+ d3.select('#canvas', element).html('');
+ var vis = d3.select('#canvas', element).append('svg')
+ .attr('width', w)
+ .attr('height', h)
+ .append('g')
+ .attr('transform', 'translate(25, 0)');
+
+ var nodes = tree.nodes(graphData);
+
+ var link = vis.selectAll('path.link')
+ .data(tree.links(nodes))
+ .enter().append('path')
+ .attr('class', helper_path_class)
+ .attr('d', diagonal);
+
+ var node = vis.selectAll('g.node')
+ .data(nodes)
+ .enter().append('g')
+ .attr('class', helper_node_class)
+ .attr('transform', function(d) {
+ return 'translate(' + d.y * .7 + ',' + d.x + ')';
+ })
+
+ node.append('circle')
+ .attr('r', 4.5);
+
+ node.append('title')
+ .text(helper_tooltip);
+
+ node.append('text')
+ .attr('dx', function(d) {
+ return 8;
+ })
+ .attr('dy', function(d) {
+ return 5;
+ })
+ .attr('text-anchor', function(d) {
+ return 'start';
+ })
+ .text(helper_node_text)
+ };
+ }
+ };
+})
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js
new file mode 100644
index 000000000..22a900295
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js
@@ -0,0 +1,50 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+solrAdminApp.controller('ThreadsController',
+ function($scope, Threads, Constants){
+ $scope.resetMenu("threads", Constants.IS_ROOT_PAGE);
+ $scope.refresh = function() {
+ Threads.get(function(data) {
+ var threadDump = data.system.threadDump;
+ var threads = [];
+ for (var i=1; i<threadDump.length; i+=2) {
+ var thread = threadDump[i];
+ if (!!thread.stackTrace) {
+ var stackTrace = [];
+ for (var j=0; j<thread.stackTrace.length; j++) {
+ var trace = thread.stackTrace[j].replace("(", "\u200B("); // allow wrapping to happen, \u200B is a zero-width space
+ stackTrace.push({id:thread.id + ":" + j, trace: trace});
+ }
+ thread.stackTrace = stackTrace;
+ }
+ threads.push(thread);
+ }
+ $scope.threads = threads;
+ });
+ };
+ $scope.toggleStacktrace = function(thread) {
+ thread.showStackTrace = !thread.showStackTrace;
+ };
+ $scope.toggleStacktraces = function() {
+ $scope.showAllStacktraces = !$scope.showAllStacktraces;
+ for (var i=0; i<$scope.threads.length; i++) {
+ $scope.threads[i].showStackTrace = $scope.showAllStacktraces;
+ }
+ };
+ $scope.refresh();
+});
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js
new file mode 100644
index 000000000..2d959e6ce
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js
@@ -0,0 +1,37 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+/**
+ * This controller is called whenever no other routes match.
+ * It is a place to intercept to look for special flows such as authentication callbacks (that do not support fragment in URL).
+ * Normal action is to redirect to dashboard "/" if no login is in progress
+ */
+solrAdminApp.controller('UnknownController',
+ ['$scope', '$window', '$routeParams', '$location', 'Constants', 'AuthenticationService',
+ function($scope, $window, $routeParams, $location, Constants, AuthenticationService) {
+ var fragment = $window.location.hash.startsWith("#/") ? $window.location.hash.substring(2) : $window.location.hash;
+ // Check if the URL is actually a callback from Identiy provider
+ if (AuthenticationService.isJwtCallback(fragment)) {
+ console.log("Detected an authentication callback, redirecting to /#/login/callback");
+ $location.path("/login/callback").hash(fragment);
+ } else {
+ console.log("Redirecting from unknown path " + fragment + " to Dashboard");
+ $location.path("/").hash("");
+ }
+ }
+ ]
+);
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/services.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/services.js
new file mode 100644
index 000000000..8b371b6ff
--- /dev/null
+++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/services.js
@@ -0,0 +1,339 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+var solrAdminServices = angular.module('solrAdminServices', ['ngResource']);
+
+solrAdminServices.factory('System',
+ ['$resource', function($resource) {
+ return $resource('admin/info/system', {"wt":"json", "nodes": "@nodes", "_":Date.now()});
+ }])
+.factory('Metrics',
+ ['$resource', function($resource) {
+ return $resource('admin/metrics', {"wt":"json", "nodes": "@nodes", "prefix":"@prefix", "_":Date.now()});
+ }])
+.factory('Collections',
+ ['$resource', function($resource) {
+ return $resource('admin/collections',
+ {'wt':'json', '_':Date.now()}, {
+ "list": {params:{action: "LIST"}},
+ "listaliases": {params:{action: "LISTALIASES"}},
+ "status": {params:{action: "CLUSTERSTATUS"}},
+ "add": {params:{action: "CREATE"}},
+ "delete": {params:{action: "DELETE"}},
+ "rename": {params:{action: "RENAME"}},
+ "createAlias": {params:{action: "CREATEALIAS"}},
+ "deleteAlias": {params:{action: "DELETEALIAS"}},
+ "deleteReplica": {params:{action: "DELETEREPLICA"}},
+ "addReplica": {params:{action: "ADDREPLICA"}},
+ "deleteShard": {params:{action: "DELETESHARD"}},
+ "reload": {method: "GET", params:{action:"RELOAD", core: "@core"}}
+ });
+ }])
+.factory('Cores',
+ ['$resource', function($resource) {
+ return $resource('admin/cores',
+ {'wt':'json', '_':Date.now()}, {
+ "query": {},
+ "list": {params:{indexInfo: false}},
+ "add": {params:{action: "CREATE"}},
+ "unload": {params:{action: "UNLOAD", core: "@core"}},
+ "rename": {params:{action: "RENAME"}},
+ "swap": {params:{action: "SWAP"}},
+ "reload": {method: "GET", params:{action:"RELOAD", core: "@core"}, headers:{doNotIntercept: "true"}}
+ });
+ }])
+.factory('Logging',
+ ['$resource', function($resource) {
+ return $resource('admin/info/logging', {'wt':'json', '_':Date.now()}, {
+ "events": {params: {since:'0'}},
+ "levels": {},
+ "setLevel": {}
+ });
+ }])
+.factory('Zookeeper',
+ ['$resource', function($resource) {
+ return $resource('admin/zookeeper', {wt:'json', _:Date.now()}, {
+ "simple": {},
+ "liveNodes": {params: {path: '/live_nodes'}},
+ "clusterState": {params: {detail: "true", path: "/clusterstate.json"}},
+ "detail": {params: {detail: "true", path: "@path"}},
+ "configs": {params: {detail:false, path: "/configs/"}},
+ "aliases": {params: {detail: "true", path: "/aliases.json"}, transformResponse:function(data) {
+ var znode = $.parseJSON(data).znode;
+ if (znode.data) {
+ return {aliases: $.parseJSON(znode.data).collection};
+ } else {
+ return {aliases: {}};
+ }
+ }}
+ });
+ }])
+.factory('ZookeeperStatus',
+ ['$resource', function($resource) {
+ return $resource('admin/zookeeper/status', {wt:'json', _:Date.now()}, {
+ "monitor": {}
+ });
+ }])
+.factory('Properties',
+ ['$resource', function($resource) {
+ return $resource('admin/info/properties', {'wt':'json', '_':Date.now()});
+ }])
+.factory('Threads',
+ ['$resource', function($resource) {
+ return $resource('admin/info/threads', {'wt':'json', '_':Date.now()});
+ }])
+.factory('Properties',
+ ['$resource', function($resource) {
+ return $resource('admin/info/properties', {'wt':'json', '_':Date.now()});
+ }])
+.factory('Replication',
+ ['$resource', function($resource) {
+ return $resource(':core/replication', {'wt':'json', core: "@core", '_':Date.now()}, {
+ "details": {params: {command: "details"}},
+ "command": {params: {}}
+ });
+ }])
+.factory('CoreSystem',
+ ['$resource', function($resource) {
+ return $resource(':core/admin/system', {wt:'json', core: "@core", _:Date.now()});
+ }])
+.factory('Update',
+ ['$resource', function($resource) {
+ return $resource(':core/:handler', {core: '@core', wt:'json', _:Date.now(), handler:'update'}, {
+ "commit": {params: {commit: "true"}},
+ "post": {headers: {'Content-type': 'application/json'}, method: "POST", params: {handler: '@handler'}},
+ "postJson": {headers: {'Content-type': 'application/json'}, method: "POST", params: {handler: '@handler'}},
+ "postXml": {headers: {'Content-type': 'text/xml'}, method: "POST", params: {handler: '@handler'}},
+ "postCsv": {headers: {'Content-type': 'application/csv'}, method: "POST", params: {handler: '@handler'}}
+ });
+ }])
+.service('FileUpload', function ($http) {
+ this.upload = function(params, file, success, error){
+ var url = "" + params.core + "/" + params.handler + "?";
+ raw = params.raw;
+ delete params.core;
+ delete params.handler;
+ delete params.raw;
+ url += $.param(params);
+ if (raw && raw.length>0) {
+ if (raw[0] != "&") raw = "&" + raw;
+ url += raw;
+ }
+ var fd = new FormData();
+ fd.append('file', file);
+ $http.post(url, fd, {
+ transformRequest: angular.identity,
+ headers: {'Content-Type': undefined}
+ }).success(success).error(error);
+ }
+})
+.filter('splitByComma', function() {
+ return function(input) {
+ return input === undefined ? input : input.split(',');
+ }
+})
+.factory('Luke',
+ ['$resource', function($resource) {
+ return $resource(':core/admin/luke', {core: '@core', wt:'json', _:Date.now()}, {
+ "index": {params: {numTerms: 0, show: 'index'}},
+ "raw": {params: {numTerms: 0}},
+ "schema": {params: {show:'schema'}},
+ "field": {},
+ "fields": {params: {show:'schema'}, interceptor: {
+ response: function(response) {
+ var fieldsAndTypes = [];
+ for (var field in response.data.schema.fields) {
+ fieldsAndTypes.push({group: "Fields", label: field, value: "fieldname=" + field});
+ }
+ for (var type in response.data.schema.types) {
+ fieldsAndTypes.push({group: "Types", label: type, value: "fieldtype=" + type});
+ }
+ return fieldsAndTypes;
+ }
+ }}
+ });
+ }])
+.factory('Analysis',
+ ['$resource', function($resource) {
+ return $resource(':core/analysis/field', {core: '@core', wt:'json', _:Date.now()}, {
+ "field": {params: {"analysis.showmatch": true}}
+ });
+ }])
+.factory('DataImport',
+ ['$resource', function($resource) {
+ return $resource(':core/:name', {core: '@core', name: '@name', indent:'on', wt:'json', _:Date.now()}, {
+ "config": {params: {command: "show-config"}, headers: {doNotIntercept: "true"},
+ transformResponse: function(data) {
+ return {config: data};
+ }
+ },
+ "status": {params: {command: "status"}, headers: {doNotIntercept: "true"}},
+ "reload": {params: {command: "reload-config"}},
+ "post": {method: "POST",
+ headers: {'Content-type': 'application/x-www-form-urlencoded'},
+ transformRequest: function(data) { return $.param(data) }}
+ });
+ }])
+.factory('Ping',
+ ['$resource', function($resource) {
+ return $resource(':core/admin/ping', {wt:'json', core: '@core', ts:Date.now(), _:Date.now()}, {
+ "ping": {},
+ "status": {params:{action:"status"}, headers: {doNotIntercept: "true"}
+ }});
+ }])
+.factory('Mbeans',
+ ['$resource', function($resource) {
+ return $resource(':core/admin/mbeans', {'wt':'json', core: '@core', '_':Date.now()}, {
+ stats: {params: {stats: true}},
+ info: {},
+ reference: {
+ params: {wt: "xml", stats: true}, transformResponse: function (data) {
+ return {reference: data}
+ }
+ },
+ delta: {method: "POST",
+ params: {stats: true, diff:true},
+ headers: {'Content-type': 'application/x-www-form-urlencoded'},
+ transformRequest: function(data) {
+ return "stream.body=" + encodeURIComponent(data);
+ }
+ }
+ });
+ }])
+.factory('Files',
+ ['$resource', function($resource) {
+ return $resource(':core/admin/file', {'wt':'json', core: '@core', '_':Date.now()}, {
+ "list": {},
+ "get": {method: "GET", interceptor: {
+ response: function(config) {return config;}
+ }, transformResponse: function(data) {
+ return data;
+ }}
+ });
+ }])
+.factory('Query',
+ ['$resource', function($resource) {
+ var resource = $resource(':core/:handler', {core: '@core', handler: '@handler', '_':Date.now()}, {
+ "query": {
+ method: "GET",
+ transformResponse: function (data) {
+ return {data: data}
+ },
+ headers: {doNotIntercept: "true"}
+ }
+ });
+ resource.url = function(params) {
+ var qs = [];
+ for (key in params) {
+ if (key != "core" && key != "handler") {
+ for (var i in params[key]) {
+ qs.push(key + "=" + encodeURIComponent(params[key][i]));
+ }
+ }
+ }
+ return "" + params.core + "/" + params.handler + "?" + qs.sort().join("&");
+ }
+ return resource;
+}])
+.factory('Segments',
+ ['$resource', function($resource) {
+ return $resource(':core/admin/segments', {'wt':'json', core: '@core', _:Date.now()}, {
+ get: {}
+ });
+}])
+.factory('Schema',
+ ['$resource', function($resource) {
+ return $resource(':core/schema', {wt: 'json', core: '@core', _:Date.now()}, {
+ get: {method: "GET"},
+ check: {method: "GET", headers: {doNotIntercept: "true"}},
+ post: {method: "POST"}
+ });
+}])
+.factory('Config',
+ ['$resource', function($resource) {
+ return $resource(':core/config', {wt: 'json', core: '@core', _:Date.now()}, {
+ get: {method: "GET"}
+ })
+}])
+.factory('AuthenticationService',
+ ['base64', function (base64) {
+ var service = {};
+
+ service.SetCredentials = function (username, password) {
+ var authdata = base64.encode(username + ':' + password);
+
+ sessionStorage.setItem("auth.header", "Basic " + authdata);
+ sessionStorage.setItem("auth.username", username);
+ };
+
+ service.ClearCredentials = function () {
+ sessionStorage.removeItem("auth.header");
+ sessionStorage.removeItem("auth.scheme");
+ sessionStorage.removeItem("auth.realm");
+ sessionStorage.removeItem("auth.username");
+ sessionStorage.removeItem("auth.wwwAuthHeader");
+ sessionStorage.removeItem("auth.statusText");
+ localStorage.removeItem("auth.stateRandom");
+ sessionStorage.removeItem("auth.nonce");
+ };
+
+ service.getAuthDataHeader = function () {
+ try {
+ var header64 = sessionStorage.getItem("auth.authDataHeader");
+ var headerJson = base64.decode(header64);
+ return JSON.parse(headerJson);
+ } catch (e) {
+ console.log("WARN: Wrong or missing X-Solr-AuthData header on 401 response " + e);
+ return null;
+ }
+ };
+
+ service.decodeJwtPart = function (jwtPart) {
+ try {
+ return JSON.parse(base64.urldecode(jwtPart));
+ } catch (e) {
+ console.log("WARN: Invalid format of JWT part: " + e);
+ return {};
+ }
+ };
+
+ service.isJwtCallback = function (hash) {
+ var hp = this.decodeHashParams(hash);
+ // console.log("Decoded hash as " + JSON.stringify(hp, undefined, 2)); // For debugging callbacks
+ return (hp['access_token'] && hp['token_type'] && hp['state']) || hp['error'];
+ };
+
+ service.decodeHashParams = function(hash) {
+ // access_token, token_type, expires_in, state
+ if (hash == null || hash.length === 0) {
+ return {};
+ }
+ var params = {};
+ var parts = hash.split("&");
+ for (var p in parts) {
+ var kv = parts[p].split("=");
+ if (kv.length === 2) {
+ params[kv[0]] = decodeURIComponent(kv[1]);
+ } else {
+ console.log("Invalid callback URI, got parameter " + parts[p] + " but expected key=value");
+ }
+ }
+ return params;
+ };
+
+ return service;
+ }]);