Resolved FELIX-1441 /Search manifest entries of bundles/
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@924190 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
index f09b6d8..b868234 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
@@ -112,7 +112,6 @@
// templates
private final String TEMPLATE_MAIN;
- private final String TEMPLATE_UPLOAD;
/** Default constructor */
public BundlesServlet()
@@ -121,7 +120,6 @@
// load templates
TEMPLATE_MAIN = readTemplateFile( "/templates/bundles.html" );
- TEMPLATE_UPLOAD = readTemplateFile( "/templates/bundles_upload.html" );
}
/**
@@ -462,20 +460,13 @@
vars.put( "drawDetails", reqInfo.bundleRequested ? Boolean.TRUE : Boolean.FALSE );
vars.put( "currentBundle", (reqInfo.bundleRequested && reqInfo.bundle != null ? String.valueOf(reqInfo.bundle.getBundleId()) : "null"));
- if ( "upload".equals(reqInfo.pathInfo) )
- {
- response.getWriter().print(TEMPLATE_UPLOAD);
- }
- else
- {
- final String pluginRoot = ( String ) request.getAttribute( WebConsoleConstants.ATTR_PLUGIN_ROOT );
- final String servicesRoot = getServicesRoot ( request );
- StringWriter w = new StringWriter();
- writeJSON(w, reqInfo.bundle, pluginRoot, servicesRoot, request.getLocale() );
- vars.put( "__bundles__", w.toString());
+ final String pluginRoot = ( String ) request.getAttribute( WebConsoleConstants.ATTR_PLUGIN_ROOT );
+ final String servicesRoot = getServicesRoot ( request );
+ StringWriter w = new StringWriter();
+ writeJSON(w, reqInfo.bundle, pluginRoot, servicesRoot, request.getLocale() );
+ vars.put( "__bundles__", w.toString());
- response.getWriter().print(TEMPLATE_MAIN);
- }
+ response.getWriter().print(TEMPLATE_MAIN);
}
private void renderJSON( final HttpServletResponse response, final Bundle bundle, final String pluginRoot, final String servicesRoot, final Locale locale )
@@ -617,6 +608,10 @@
jw.value( bundle.getBundleId() );
jw.key( "name" );
jw.value( Util.getName( bundle, locale ) );
+ jw.key( "fragment" );
+ jw.value( isFragmentBundle(bundle) );
+ jw.key( "stateRaw" );
+ jw.value( bundle.getState() );
jw.key( "state" );
jw.value( toStateString( bundle ) );
jw.key( "version" );
@@ -624,25 +619,6 @@
jw.key( "symbolicName" );
jw.value( Util.getHeaderValue(bundle, Constants.BUNDLE_SYMBOLICNAME) );
- jw.key( "actions" );
- jw.array();
-
- if ( bundle.getBundleId() != 0 )
- {
- if ( hasStart(bundle) )
- {
- action( jw, hasStart( bundle ), "start", "Start", "start" );
- }
- else
- {
- action( jw, hasStop( bundle ), "stop", "Stop", "stop" );
- }
- action( jw, true, "refresh", "Refresh Package Imports", "refresh" );
- action( jw, true, "update", "Update", "update" );
- action( jw, hasUninstall( bundle ), "uninstall", "Uninstall", "delete" );
- }
- jw.endArray();
-
if ( details )
{
bundleDetails( jw, bundle, pluginRoot, servicesRoot, locale );
@@ -683,50 +659,11 @@
}
}
-
- private void action( JSONWriter jw, boolean enabled, String op, String opLabel, String image ) throws JSONException
- {
- jw.object();
- jw.key( "enabled" ).value( enabled );
- jw.key( "name" ).value( opLabel );
- jw.key( "link" ).value( op );
- jw.key( "image" ).value( image );
- jw.endObject();
- }
-
private final boolean isFragmentBundle( Bundle bundle)
{
return getPackageAdmin().getBundleType( bundle ) == PackageAdmin.BUNDLE_TYPE_FRAGMENT;
}
- private final boolean hasStart( Bundle bundle )
- {
- if ( isFragmentBundle(bundle) )
- {
- return false;
- }
- return bundle.getState() == Bundle.INSTALLED || bundle.getState() == Bundle.RESOLVED;
- }
-
-
- private final boolean hasStop( Bundle bundle )
- {
- if ( isFragmentBundle(bundle) )
- {
- return false;
- }
- return bundle.getState() == Bundle.ACTIVE;
- }
-
-
- private static final boolean hasUninstall( Bundle bundle )
- {
- return bundle.getState() == Bundle.INSTALLED || bundle.getState() == Bundle.RESOLVED
- || bundle.getState() == Bundle.ACTIVE;
-
- }
-
-
private final void bundleDetails( JSONWriter jw, Bundle bundle, final String pluginRoot, final String servicesRoot, final Locale locale)
throws JSONException
{
diff --git a/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties b/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
index d9d2b86..d12234d 100644
--- a/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
+++ b/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
@@ -128,7 +128,7 @@
bundles.classpath=Bundle Classpath
bundles.pkg.exported=Предотставяни пакети
bundles.pkg.imported=Използвани пакети
-bundles.pkg.importingBundles=Използвани бъндъли
+bundles.pkg.importingBundles=Използващи бъндъли
bundles.manifest.headers=Manifest Headers
bundles.hosts=Host Bundles
bundles.framents=Закачени фрагменти
@@ -140,6 +140,19 @@
bundles.upload.caption=Качване/инсталиране на бъндъли
bundles.upload.start=Стартиране
bundles.upload.level=Стартиращо ниво
+# filter
+bundles.filter.apply=Прилагане филтър
+bundles.filter.clear=Изчистване филтър
+bundles.filter.help=Низ или регулярен израз, който се съдържа в ID-то, името, символното име или версията на бъндъла.
+# states
+bundles.state.1=Деинсталиран
+bundles.state.2=Инсталиран
+bundles.state.4=Ресолвнат
+bundles.state.8=Стартиран
+bundles.state.16=Спрян
+bundles.state.32=Активен
+bundles.state.unknown=Непознат статус: {0}
+bundles.state.fragment=Фрагмент
# Components plugin
diff --git a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
index 1e2e4fd..cd33fd6 100644
--- a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
+++ b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -140,6 +140,19 @@
bundles.upload.caption=Upload / Install Bundles
bundles.upload.start=Start Bundle
bundles.upload.level=Start Level
+# filter
+bundles.filter.apply=Apply Filter
+bundles.filter.clear=Clear Filter
+bundles.filter.help=A string or RegExp expression which is matched againse bundle id, name, symbolic name or version.
+# states
+bundles.state.1=Uninstalled
+bundles.state.2=Installed
+bundles.state.4=Resolved
+bundles.state.8=Starting
+bundles.state.16=Stopping
+bundles.state.32=Active
+bundles.state.unknown=Unknown State: {0}
+bundles.state.fragment=Fragment
# Components plugin
diff --git a/webconsole/src/main/resources/res/ui/bundles.css b/webconsole/src/main/resources/res/ui/bundles.css
index f6adee8..086a196 100644
--- a/webconsole/src/main/resources/res/ui/bundles.css
+++ b/webconsole/src/main/resources/res/ui/bundles.css
@@ -22,3 +22,5 @@
.col_Status { width: 50px; }
.col_Actions { width: 121px; }
+.filterBox { float: left; margin-left: 1em }
+#uploadDialog, div.ui-dialog-buttonpane button { font-size: 50% }
diff --git a/webconsole/src/main/resources/res/ui/bundles.js b/webconsole/src/main/resources/res/ui/bundles.js
index 2660b74..b7467d3 100644
--- a/webconsole/src/main/resources/res/ui/bundles.js
+++ b/webconsole/src/main/resources/res/ui/bundles.js
@@ -14,14 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+// ui elements
+var uploadDialog = false;
+var bundlesTable = false;
+var bundlesBody = false;
+var bundlesTemplate = false;
-function renderData( eventData ) {
+function renderData( eventData, filter ) {
+ lastBundleData = eventData;
var s = eventData.s;
- $(".statline").html(i18n.statline.msgFormat(s[0], s[1], s[2], s[3], s[4]));
- $("#plugin_table > tbody > tr").remove();
+ $('.statline').html(i18n.statline.msgFormat(s[0], s[1], s[2], s[3], s[4]));
+ bundlesBody.empty();
for ( var idx in eventData.data ) {
if ( currentBundle == null || !drawDetails || currentBundle == eventData.data[idx].id) {
- entry( eventData.data[idx] );
+ entry( eventData.data[idx], filter );
}
}
if ( drawDetails && eventData.data.length == 1 ) {
@@ -34,94 +40,66 @@
initStaticWidgets();
}
-function entry( /* Object */ dataEntry ) {
- var trElement = tr( null, { id: "entry" + dataEntry.id } );
- entryInternal( trElement, dataEntry );
- $("#plugin_table > tbody").append(trElement);
+function entry( /* Object */ bundle, filter ) {
+ var matches = !(filter && typeof filter.test == 'function') ? true :
+ filter.test(bundle.id) || filter.test(bundle.name) || filter.test(bundle.symbolicName) || filter.test(bundle.version);
+
+ if (matches) entryInternal( bundle ).appendTo(bundlesBody);
}
-function actionButton( /* Element */ parent, /* string */ id, /* Obj */ action ) {
- if ( !action.enabled ) {
- return;
- }
- var enabled = action.enabled;
- var op = action.link;
- var opLabel = action.name;
- var img = action.image;
- // fixup JQuery UI icons
- if(img == "start" ) img = "play";
- if(img == "update") img = "transferthick-e-w";
- if(img == "delete") img = "trash";
-
- // apply i18n
- opLabel = i18n[opLabel] ? i18n[opLabel] : opLabel;
-
- var input = createElement('li', 'dynhover', {
- title: opLabel
- });
- $(input)
- .html('<span class="ui-icon ui-icon-'+img+'"></span>')
- .click(function() {changeDataEntryState(id, op)});
-
- if (!enabled) {
- $(input).attr("disabled", true).addClass("ui-state-disabled");
- }
- parent.appendChild( input );
+function hasStart(b) { return (!b.fragment) && (b.stateRaw == 2 || b.stateRaw == 4) } // !isFragment && (installed | resolved)
+function hasStop(b) { return (!b.fragment) && (b.stateRaw == 32) } // !isFragment && active
+function hasUninstall(b) { return b.stateRaw == 2 || b.stateRaw == 4 || b.stateRaw == 32 } // installed | resolved | active
+function stateString(b) {
+ var s = b.stateRaw;
+ return b.fragment && s == 4 ?
+ i18n.state.fragment : // fragment & resolved
+ i18n.state[s] ? i18n.state[s] : i18n.state.unknown.msgFormat(s)
}
-function entryInternal( /* Element */ parent, /* Object */ dataEntry ) {
- var id = dataEntry.id;
- var name = dataEntry.name;
- var state = dataEntry.state;
+function entryInternal( /* Object */ bundle ) {
+ var tr = bundlesTemplate.clone();
+ var id = bundle.id;
+ var name = bundle.name;
- // right arrow
- var inputElement = createElement('span', 'ui-icon ui-icon-triangle-1-e', {
- title: "Details",
- id: 'img' + id,
- style: {display: "inline-block"}
- });
- $(inputElement).click(function() {showDetails(id)});
- var titleElement;
- if ( drawDetails ) {
- titleElement = text(name);
- } else {
- titleElement = createElement ("a", null, {
- href: window.location.pathname + "/" + id
- });
- titleElement.appendChild(text(name));
- }
-
- parent.appendChild( td( null, null, [ text( id ) ] ) );
- parent.appendChild( td( null, null, [ inputElement, text(" "), titleElement ] ) );
- parent.appendChild( td( null, null, [ text( dataEntry.version ) ] ) );
- parent.appendChild( td( null, null, [ text( dataEntry.symbolicName ) ] ) );
- parent.appendChild( td( null, null, [ text( state ) ] ) );
- var actionsTd = td( null, null );
- var div = createElement('ul', 'icons ui-widget');
- actionsTd.appendChild(div);
-
- for ( var a in dataEntry.actions ) {
- actionButton( div, id, dataEntry.actions[a] );
- }
- parent.appendChild( actionsTd );
+ tr.attr('id', 'entry'+id);
+ tr.find('td:eq(0)').text(id);
+ tr.find('td:eq(1) span:eq(0)').attr('id', 'img'+id).click(function() {showDetails(id)});
+ tr.find('td:eq(1) span:eq(1)').html( drawDetails ? name : '<a href="' + pluginRoot + '/' + id + '">' + name + '</a>' );
+ tr.find('td:eq(2)').text( bundle.version );
+ tr.find('td:eq(3)').text( bundle.symbolicName );
+ tr.find('td:eq(4)').text( stateString(bundle) );
+ if (id == 0) { // system bundle has no actions
+ tr.find('td:eq(5) ul').addClass('ui-helper-hidden');
+ } else {
+ var start = tr.find('td:eq(5) ul li:eq(0)');
+ var stop = tr.find('td:eq(5) ul li:eq(1)');
+ var refresh = tr.find('td:eq(5) ul li:eq(2)').click(function() {changeDataEntryState(id, 'refresh')});
+ var update = tr.find('td:eq(5) ul li:eq(3)').click(function() {changeDataEntryState(id, 'update')});
+ var remove = tr.find('td:eq(5) ul li:eq(4)');
+ start = hasStart(bundle) ?
+ start.click(function() {changeDataEntryState(id, 'start')}) :
+ start.addClass('ui-helper-hidden');
+ stop = hasStop(bundle) ?
+ stop.click(function() {changeDataEntryState(id, 'stop')}) :
+ stop.addClass('ui-helper-hidden');
+ remove = hasUninstall(bundle) ?
+ remove.click(function() {changeDataEntryState(id, 'uninstall')}) :
+ remove.addClass('ui-helper-hidden');
+ }
+ return tr;
}
function loadData() {
- $.get(pluginRoot + "/.json", null, function(data) {
- renderData(data);
- }, "json");
+ $.get(pluginRoot + "/.json", null, renderData, "json");
}
function changeDataEntryState(/* long */ id, /* String */ action) {
- $.post(pluginRoot + "/" + id, {"action":action}, function(data) {
- renderData(data);
- }, "json");
+ $.post(pluginRoot + "/" + id, {"action":action}, renderData, "json");
}
function refreshPackages() {
- $.post(window.location.pathname, {"action": "refreshPackages"}, function(data) {
- renderData(data);
- }, "json");
+ $.post(pluginRoot, {"action": "refreshPackages"}, renderData, "json");
}
function showDetails( id ) {
@@ -196,18 +174,47 @@
}
}
+
$(document).ready(function(){
- $(".refreshPackages").click(refreshPackages);
- $(".reloadButton").click(loadData);
- $(".installButton").click(function() {
- document.location = pluginRoot + "/upload";
+ $('.refreshPackages').click(refreshPackages);
+ $('.reloadButton').click(loadData);
+ $('.installButton').click(function() {
+ uploadDialog.dialog('open');
+ return false;
});
- renderData(__bundles__);
+
+ // filter
+ $('input.filter').click(function() {$(this).val('')});
+ $('.filterApply').click(function() {
+ var el = $(this).parent().find('input.filter');
+ var filter = el.length && el.val() ? new RegExp(el.val()) : false;
+ renderData(lastBundleData, filter);
+ });
+ $('.filterForm').submit(function() {
+ $(this).find('.filterApply').click();
+ return false;
+ });
+ $('.filterClear').click(function() {
+ $('input.filter').val('');
+ renderData(lastBundleData);
+ });
+
+ // upload dialog
+ var uploadDialogButtons = {};
+ uploadDialogButtons[i18n.install_update] = function() {
+ $(this).find('form').submit();
+ }
+ uploadDialog = $('#uploadDialog').dialog({
+ autoOpen: false,
+ modal : true,
+ width : '50%',
+ buttons : uploadDialogButtons
+ });
// check for cookie
var cv = $.cookies.get("webconsolebundlelist");
var lo = (cv ? cv.split(",") : [1,0]);
- $("#plugin_table").tablesorter({
+ bundlesTable = $("#plugin_table").tablesorter({
headers: {
0: { sorter:"digit" },
5: { sorter: false }
@@ -215,8 +222,12 @@
textExtraction:mixedLinksExtraction,
sortList: cv ? [lo] : false
}).bind("sortEnd", function() {
- var table = $("#plugin_table").eq(0).attr("config");
- $.cookies.set("webconsolebundlelist", table.sortList.toString());
+ bundlesTable.eq(0).attr("config");
+ $.cookies.set("webconsolebundlelist", bundlesTable.sortList.toString());
});
+ bundlesBody = bundlesTable.find('tbody');
+ bundlesTemplate = bundlesBody.find('tr').clone();
+
+ renderData(lastBundleData);
});
diff --git a/webconsole/src/main/resources/templates/bundles.html b/webconsole/src/main/resources/templates/bundles.html
index 4221f30..a62da40 100644
--- a/webconsole/src/main/resources/templates/bundles.html
+++ b/webconsole/src/main/resources/templates/bundles.html
@@ -1,10 +1,11 @@
-<script type="text/javascript" src="${appRoot}/res/ui/bundles.js"></script>
+<script type="text/javascript" src="${appRoot}/res/lib/jquery.multifile-1.4.6.js"></script>
+<script type="text/javascript" src="${appRoot}/res/ui/bundles.js"></script>
<script type="text/javascript">
// <![CDATA[
var startLevel = ${startLevel};
var drawDetails = ${drawDetails};
var currentBundle = ${currentBundle};
-var __bundles__ = ${__bundles__};
+var lastBundleData = ${__bundles__};
var i18n = {
'Symbolic Name' : '${bundles.name.symb}',
'Version' : '${version}',
@@ -30,7 +31,18 @@
'Uninstall' : '${bundles.uninstall}',
'Refresh Package Imports' : '${bundles.refreshImports}',
//
- statline : '${bundles.statline}'
+ statline : '${bundles.statline}',
+ install_update : '${bundles.install_or_update}',
+ state : {
+ 1 : '${bundles.state.1}', // uninstalled
+ 2 : '${bundles.state.2}', // installed
+ 4 : '${bundles.state.4}', // resolved
+ 8 : '${bundles.state.8}', // starting
+ 16 : '${bundles.state.16}', // stopping
+ 32 : '${bundles.state.32}', // active
+ 'unknown' : '${bundles.state.unknown}', // Unknown State: {0}
+ 'fragment' : '${bundles.state.fragment}' // Fragment
+ }
}
// ]]>
</script>
@@ -39,16 +51,16 @@
<p class="statline"> </p>
<!-- top header -->
-<form method="post" enctype="multipart/form-data" action="">
+<form method="post" enctype="multipart/form-data" action="" class="filterForm">
<div class="ui-widget-header ui-corner-top buttonGroup">
- <input name="action" value="install" type="hidden" />
- <input name="bundlestart" value="start" type="hidden" />
- <input name="bundlestartlevel" value="5" type="hidden" />
- <input name="bundlefile" style="margin-left: 10px;" type="file" />
- <input value="${bundles.install_or_update}" style="margin-left: 10px;" type="submit" />
- <button class="reloadButton" type="button" name="reload" style="margin-left: 60px;">${reload}</button>
- <button class="installButton" type="button" name="install">${bundles.install_update}</button>
- <button class="refreshPackages" type="button" name="refresh">${bundles.refreshPkg}</button>
+ <div class="filterBox">
+ <input class="filter" value="^Apache" title="${bundles.filter.help}" />
+ <button class="filterApply" type="button">${bundles.filter.apply}</button>
+ <button class="filterClear" type="button">${bundles.filter.clear}</button>
+ </div>
+ <button class="reloadButton" type="button" style="margin-left: 60px;">${reload}</button>
+ <button class="installButton" type="button">${bundles.install_update}</button>
+ <button class="refreshPackages" type="button">${bundles.refreshPkg}</button>
</div>
</form>
@@ -64,23 +76,65 @@
</tr>
</thead>
<tbody>
- <tr><td colspan="6"> </td></tr>
+ <tr><!-- template -->
+ <td> </td><!-- ID -->
+ <td>
+ <span class="ui-icon ui-icon-triangle-1-e" style="display: inline-block" title="Show Details"> </span>
+ <span> </span> <!-- here goest bundle name/link -->
+ </td>
+ <td> </td><!-- version -->
+ <td> </td><!-- symbolic name -->
+ <td> </td><!-- status -->
+ <td><!-- actions -->
+ <ul class="icons ui-widget">
+ <li class="dynhover" title="${start}"><span class="ui-icon ui-icon-play"> </span></li>
+ <li class="dynhover" title="${stop}"><span class="ui-icon ui-icon-stop"> </span></li>
+ <li class="dynhover" title="${bundles.refreshImports}"><span class="ui-icon ui-icon-refresh"> </span></li>
+ <li class="dynhover" title="${bundles.update}"><span class="ui-icon ui-icon-transferthick-e-w"> </span></li>
+ <li class="dynhover" title="${bundles.uninstall}"><span class="ui-icon ui-icon-trash"> </span></li>
+ </ul>
+ </td>
+ </tr>
</tbody>
</table>
<!-- bottom header -->
-<form method="post" enctype="multipart/form-data" action="">
+<form method="post" enctype="multipart/form-data" action="" class="filterForm">
<div class="ui-widget-header ui-corner-bottom buttonGroup">
- <input name="action" value="install" type="hidden" />
- <input name="bundlestart" value="start" type="hidden" />
- <input name="bundlestartlevel" value="5" type="hidden" />
- <input name="bundlefile" style="margin-left: 10px;" type="file" />
- <input value="${bundles.install_or_update}" style="margin-left: 10px;" type="submit" />
- <button class="reloadButton" type="button" name="reload" style="margin-left: 60px;">${reload}</button>
- <button class="installButton" type="button" name="install">${bundles.install_update}</button>
- <button class="refreshPackages" type="button" name="refresh">${bundles.refreshPkg}</button>
+ <div class="filterBox">
+ <input class="filter" value="^Apache" title="${bundles.filter.help}" />
+ <button class="filterApply" type="button">${bundles.filter.apply}</button>
+ <button class="filterClear" type="button">${bundles.filter.clear}</button>
+ </div>
+ <button class="reloadButton" type="button" style="margin-left: 60px;">${reload}</button>
+ <button class="installButton" type="button">${bundles.install_update}</button>
+ <button class="refreshPackages" type="button">${bundles.refreshPkg}</button>
</div>
</form>
<!-- status line -->
<p class="statline"> </p>
+
+<div id="uploadDialog" class="ui-helper-hidden" title="${bundles.upload.caption}">
+ <form method="post" enctype="multipart/form-data" action="${pluginRoot}">
+ <table class="nicetable">
+ <tr>
+ <td style="text-align:right">${bundles.upload.start}</td>
+ <td>
+ <input type="hidden" name="action" value="install"/>
+ <input type="checkbox" name="bundlestart" value="start"/>
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right">${bundles.upload.level}</td>
+ <td><input type="text" name="bundlestartlevel" id="bundlestartlevel" value="${startLevel}" size="4"/></td>
+ </tr>
+ <tr>
+ <td> </td>
+ <td>
+ <input class="multi" accept="jar" type="file" name="bundlefile" />
+ </td>
+ </tr>
+ </table>
+ </form>
+</div>