001/* 002 * (C) Copyright 2006-2010 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Thierry Delprat 018 */ 019package org.nuxeo.apidoc.browse; 020 021import java.io.IOException; 022import java.io.Reader; 023import java.io.StringReader; 024import java.io.StringWriter; 025import java.net.URI; 026import java.net.URISyntaxException; 027import java.util.ArrayList; 028import java.util.Collections; 029import java.util.HashMap; 030import java.util.List; 031import java.util.Map; 032import java.util.stream.Collectors; 033 034import javax.ws.rs.FormParam; 035import javax.ws.rs.GET; 036import javax.ws.rs.POST; 037import javax.ws.rs.Path; 038import javax.ws.rs.PathParam; 039import javax.ws.rs.Produces; 040import javax.ws.rs.QueryParam; 041import javax.ws.rs.core.Response; 042 043import org.json.JSONArray; 044import org.json.JSONException; 045import org.json.JSONObject; 046import org.nuxeo.apidoc.api.BundleGroup; 047import org.nuxeo.apidoc.api.BundleGroupFlatTree; 048import org.nuxeo.apidoc.api.BundleGroupTreeHelper; 049import org.nuxeo.apidoc.api.BundleInfo; 050import org.nuxeo.apidoc.api.ComponentInfo; 051import org.nuxeo.apidoc.api.DocumentationItem; 052import org.nuxeo.apidoc.api.ExtensionInfo; 053import org.nuxeo.apidoc.api.ExtensionPointInfo; 054import org.nuxeo.apidoc.api.NuxeoArtifact; 055import org.nuxeo.apidoc.api.OperationInfo; 056import org.nuxeo.apidoc.api.SeamComponentInfo; 057import org.nuxeo.apidoc.api.ServiceInfo; 058import org.nuxeo.apidoc.documentation.DocumentationService; 059import org.nuxeo.apidoc.search.ArtifactSearcher; 060import org.nuxeo.apidoc.snapshot.DistributionSnapshot; 061import org.nuxeo.apidoc.snapshot.SnapshotManager; 062import org.nuxeo.apidoc.tree.TreeHelper; 063import org.nuxeo.ecm.automation.OperationException; 064import org.nuxeo.ecm.core.api.NuxeoException; 065import org.nuxeo.ecm.platform.htmlsanitizer.HtmlSanitizerService; 066import org.nuxeo.ecm.platform.rendering.wiki.WikiSerializer; 067import org.nuxeo.ecm.webengine.model.Resource; 068import org.nuxeo.ecm.webengine.model.WebObject; 069import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException; 070import org.nuxeo.ecm.webengine.model.impl.DefaultObject; 071import org.nuxeo.runtime.api.Framework; 072import org.wikimodel.wem.WikiParserException; 073 074@WebObject(type = "apibrowser") 075public class ApiBrowser extends DefaultObject { 076 077 protected String distributionId; 078 079 protected boolean embeddedMode = false; 080 081 protected SnapshotManager getSnapshotManager() { 082 return Framework.getLocalService(SnapshotManager.class); 083 } 084 085 protected ArtifactSearcher getSearcher() { 086 return Framework.getLocalService(ArtifactSearcher.class); 087 } 088 089 @Override 090 protected void initialize(Object... args) { 091 distributionId = (String) args[0]; 092 if (args.length > 1) { 093 Boolean embed = (Boolean) args[1]; 094 embeddedMode = embed != null && embed; 095 } 096 } 097 098 @GET 099 @Produces("text/plain") 100 @Path("tree") 101 public Object tree(@QueryParam("root") String source) { 102 return TreeHelper.updateTree(getContext(), source); 103 } 104 105 @GET 106 @Produces("text/html") 107 @Path("treeView") 108 public Object treeView() { 109 return getView("tree").arg(Distribution.DIST_ID, ctx.getProperty(Distribution.DIST_ID)); 110 } 111 112 @GET 113 @Produces("text/html") 114 public Object doGet() { 115 if (embeddedMode) { 116 DistributionSnapshot snap = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()); 117 Map<String, Integer> stats = new HashMap<String, Integer>(); 118 stats.put("bundles", snap.getBundleIds().size()); 119 stats.put("jComponents", snap.getJavaComponentIds().size()); 120 stats.put("xComponents", snap.getXmlComponentIds().size()); 121 stats.put("services", snap.getServiceIds().size()); 122 stats.put("xps", snap.getExtensionPointIds().size()); 123 stats.put("contribs", snap.getComponentIds().size()); 124 return getView("indexSimple").arg(Distribution.DIST_ID, ctx.getProperty(Distribution.DIST_ID)).arg("stats", 125 stats); 126 } else { 127 return getView("index").arg(Distribution.DIST_ID, ctx.getProperty(Distribution.DIST_ID)); 128 } 129 } 130 131 @GET 132 @Produces("text/html") 133 @Path("listBundleGroups") 134 public Object getMavenGroups() { 135 BundleGroupTreeHelper bgth = new BundleGroupTreeHelper(getSnapshotManager().getSnapshot(distributionId, 136 ctx.getCoreSession())); 137 List<BundleGroupFlatTree> tree = bgth.getBundleGroupTree(); 138 return getView("listBundleGroups").arg("tree", tree).arg(Distribution.DIST_ID, 139 ctx.getProperty(Distribution.DIST_ID)); 140 } 141 142 public Map<String, DocumentationItem> getDescriptions(String targetType) { 143 DocumentationService ds = Framework.getLocalService(DocumentationService.class); 144 return ds.getAvailableDescriptions(getContext().getCoreSession(), targetType); 145 } 146 147 @GET 148 @Produces("text/html") 149 @Path("listBundles") 150 public Object getBundles() { 151 List<String> bundleIds = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getBundleIds(); 152 return getView("listBundles").arg("bundleIds", bundleIds).arg(Distribution.DIST_ID, 153 ctx.getProperty(Distribution.DIST_ID)); 154 } 155 156 @GET 157 @Produces("text/html") 158 @Path("listComponents") 159 public Object getComponents() { 160 List<String> javaComponentIds = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getJavaComponentIds(); 161 List<ArtifactLabel> javaLabels = new ArrayList<ArtifactLabel>(); 162 for (String id : javaComponentIds) { 163 javaLabels.add(ArtifactLabel.createLabelFromComponent(id)); 164 } 165 166 List<String> xmlComponentIds = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getXmlComponentIds(); 167 List<ArtifactLabel> xmlLabels = new ArrayList<ArtifactLabel>(); 168 for (String id : xmlComponentIds) { 169 xmlLabels.add(ArtifactLabel.createLabelFromComponent(id)); 170 } 171 172 Collections.sort(javaLabels); 173 Collections.sort(xmlLabels); 174 175 return getView("listComponents").arg("javaComponents", javaLabels).arg("xmlComponents", xmlLabels).arg( 176 Distribution.DIST_ID, ctx.getProperty(Distribution.DIST_ID)); 177 } 178 179 @GET 180 @Produces("text/html") 181 @Path("listServices") 182 public Object getServices() { 183 List<String> serviceIds = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getServiceIds(); 184 185 List<ArtifactLabel> serviceLabels = new ArrayList<ArtifactLabel>(); 186 187 for (String id : serviceIds) { 188 serviceLabels.add(ArtifactLabel.createLabelFromService(id)); 189 } 190 Collections.sort(serviceLabels); 191 192 return getView("listServices").arg("services", serviceLabels).arg(Distribution.DIST_ID, 193 ctx.getProperty(Distribution.DIST_ID)); 194 } 195 196 protected Map<String, String> getRenderedDescriptions(String type) { 197 198 Map<String, DocumentationItem> descs = getDescriptions(type); 199 Map<String, String> result = new HashMap<String, String>(); 200 201 for (String key : descs.keySet()) { 202 DocumentationItem docItem = descs.get(key); 203 String content = docItem.getContent(); 204 if ("wiki".equals(docItem.getRenderingType())) { 205 Reader reader = new StringReader(content); 206 WikiSerializer engine = new WikiSerializer(); 207 StringWriter writer = new StringWriter(); 208 try { 209 engine.serialize(reader, writer); 210 } catch (IOException | WikiParserException e) { 211 throw new NuxeoException(e); 212 } 213 content = writer.getBuffer().toString(); 214 } else { 215 content = "<div class='doc'>" + content + "</div>"; 216 } 217 result.put(key, content); 218 } 219 return result; 220 } 221 222 @GET 223 @Produces("text/plain") 224 @Path("feedServices") 225 public String feedServices() throws JSONException { 226 List<String> serviceIds = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getServiceIds(); 227 228 Map<String, String> descs = getRenderedDescriptions("NXService"); 229 230 List<ArtifactLabel> serviceLabels = new ArrayList<ArtifactLabel>(); 231 232 for (String id : serviceIds) { 233 serviceLabels.add(ArtifactLabel.createLabelFromService(id)); 234 } 235 Collections.sort(serviceLabels); 236 237 JSONArray array = new JSONArray(); 238 239 for (ArtifactLabel label : serviceLabels) { 240 JSONObject object = new JSONObject(); 241 object.put("id", label.getId()); 242 object.put("label", label.getLabel()); 243 object.put("desc", descs.get(label.id)); 244 object.put("url", "http://explorer.nuxeo.org/nuxeo/site/distribution/current/service2Bundle/" + label.id); 245 array.put(object); 246 } 247 248 return array.toString(); 249 } 250 251 @GET 252 @Produces("text/plain") 253 @Path("feedExtensionPoints") 254 public String feedExtensionPoints() throws JSONException { 255 List<String> epIds = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getExtensionPointIds(); 256 257 Map<String, String> descs = getRenderedDescriptions("NXExtensionPoint"); 258 259 List<ArtifactLabel> labels = new ArrayList<ArtifactLabel>(); 260 261 for (String id : epIds) { 262 labels.add(ArtifactLabel.createLabelFromExtensionPoint(id)); 263 } 264 Collections.sort(labels); 265 266 JSONArray array = new JSONArray(); 267 268 for (ArtifactLabel label : labels) { 269 JSONObject object = new JSONObject(); 270 object.put("id", label.getId()); 271 object.put("label", label.getLabel()); 272 object.put("desc", descs.get(label.id)); 273 object.put("url", "http://explorer.nuxeo.org/nuxeo/site/distribution/current/extensionPoint2Component/" 274 + label.id); 275 array.put(object); 276 } 277 278 return array.toString(); 279 } 280 281 @GET 282 @Produces("text/html") 283 @Path("listContributions") 284 public Object getContributions() { 285 DistributionSnapshot snapshot = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()); 286 List<String> cIds = snapshot.getContributionIds(); 287 return getView("listContributions").arg("contributions", snapshot.getContributions()).arg( 288 Distribution.DIST_ID, ctx.getProperty(Distribution.DIST_ID)); 289 } 290 291 @GET 292 @Produces("text/html") 293 @Path("listExtensionPoints") 294 public Object getExtensionPoints() { 295 List<String> epIds = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getExtensionPointIds(); 296 297 List<ArtifactLabel> labels = epIds.stream().map(ArtifactLabel::createLabelFromExtensionPoint).collect(Collectors.toList()); 298 299 Collections.sort(labels); 300 return getView("listExtensionPoints").arg("eps", labels).arg(Distribution.DIST_ID, 301 ctx.getProperty(Distribution.DIST_ID)); 302 } 303 304 /** 305 * XXX Not used? 306 */ 307 @POST 308 @Produces("text/html") 309 @Path("filterComponents") 310 public Object filterComponents(@FormParam("fulltext") String fulltext) { 311 List<NuxeoArtifact> artifacts = getSearcher().filterArtifact(getContext().getCoreSession(), distributionId, 312 ComponentInfo.TYPE_NAME, fulltext); 313 314 List<ArtifactLabel> xmlLabels = new ArrayList<>(); 315 List<ArtifactLabel> javaLabels = new ArrayList<>(); 316 317 for (NuxeoArtifact item : artifacts) { 318 ComponentInfo ci = (ComponentInfo) item; 319 if (ci.isXmlPureComponent()) { 320 xmlLabels.add(ArtifactLabel.createLabelFromComponent(ci.getId())); 321 } else { 322 javaLabels.add(ArtifactLabel.createLabelFromComponent(ci.getId())); 323 } 324 } 325 return getView("listComponents").arg("javaComponents", javaLabels).arg("xmlComponents", xmlLabels).arg( 326 Distribution.DIST_ID, ctx.getProperty(Distribution.DIST_ID)).arg("searchFilter", sanitize(fulltext)); 327 } 328 329 /** 330 * XXX Not used? 331 */ 332 @POST 333 @Produces("text/html") 334 @Path("filterBundles") 335 public Object filterBundles(@FormParam("fulltext") String fulltext) { 336 List<NuxeoArtifact> artifacts = getSearcher().filterArtifact(getContext().getCoreSession(), distributionId, 337 BundleInfo.TYPE_NAME, fulltext); 338 List<String> bundleIds = new ArrayList<String>(); 339 for (NuxeoArtifact item : artifacts) { 340 bundleIds.add(item.getId()); 341 } 342 return getView("listBundles").arg("bundleIds", bundleIds).arg(Distribution.DIST_ID, 343 ctx.getProperty(Distribution.DIST_ID)).arg("searchFilter", sanitize(fulltext)); 344 } 345 346 /** 347 * XXX Not used? 348 */ 349 @POST 350 @Produces("text/html") 351 @Path("filterServices") 352 public Object filterServices() { 353 String fulltext = getContext().getForm().getFormProperty("fulltext"); 354 List<NuxeoArtifact> artifacts = getSearcher().filterArtifact(getContext().getCoreSession(), distributionId, 355 ServiceInfo.TYPE_NAME, fulltext); 356 List<String> serviceIds = new ArrayList<String>(); 357 for (NuxeoArtifact item : artifacts) { 358 serviceIds.add(item.getId()); 359 } 360 List<ArtifactLabel> serviceLabels = new ArrayList<ArtifactLabel>(); 361 362 for (String id : serviceIds) { 363 serviceLabels.add(ArtifactLabel.createLabelFromService(id)); 364 } 365 return getView("listServices").arg("services", serviceLabels).arg(Distribution.DIST_ID, 366 ctx.getProperty(Distribution.DIST_ID)).arg("searchFilter", sanitize(fulltext)); 367 } 368 369 @POST 370 @Produces("text/html") 371 @Path("filterExtensionPoints") 372 public Object filterExtensionPoints(@FormParam("fulltext") String fulltext) { 373 List<NuxeoArtifact> artifacts = getSearcher().filterArtifact(getContext().getCoreSession(), distributionId, 374 ExtensionPointInfo.TYPE_NAME, fulltext); 375 List<String> eps = artifacts.stream().map(NuxeoArtifact::getId).collect(Collectors.toList()); 376 List<ArtifactLabel> labels = eps.stream().map(ArtifactLabel::createLabelFromExtensionPoint).collect(Collectors.toList()); 377 return getView("listExtensionPoints").arg("eps", labels).arg(Distribution.DIST_ID, 378 ctx.getProperty(Distribution.DIST_ID)).arg("searchFilter", sanitize(fulltext)); 379 } 380 381 @POST 382 @Produces("text/html") 383 @Path("filterContributions") 384 public Object filterContributions(@FormParam("fulltext") String fulltext) { 385 List<NuxeoArtifact> artifacts = getSearcher().filterArtifact(getContext().getCoreSession(), distributionId, 386 ExtensionInfo.TYPE_NAME, fulltext); 387 return getView("listContributions").arg("contributions", artifacts).arg(Distribution.DIST_ID, 388 ctx.getProperty(Distribution.DIST_ID)).arg("searchFilter", sanitize(fulltext)); 389 } 390 391 @Path("doc") 392 public Resource viewDoc() { 393 return ctx.newObject("documentation"); 394 } 395 396 @GET 397 @Produces("text/html") 398 @Path("service2Bundle/{serviceId}") 399 public Object service2Bundle(@PathParam("serviceId") String serviceId) { 400 401 ServiceInfo si = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getService(serviceId); 402 if (si == null) { 403 return null; 404 } 405 String cid = si.getComponentId(); 406 407 ComponentInfo ci = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getComponent(cid); 408 String bid = ci.getBundle().getId(); 409 410 org.nuxeo.common.utils.Path target = new org.nuxeo.common.utils.Path(getContext().getRoot().getName()); 411 target = target.append(distributionId); 412 target = target.append("viewBundle"); 413 target = target.append(bid + "#Service." + serviceId); 414 try { 415 return Response.seeOther(new URI(target.toString())).build(); 416 } catch (URISyntaxException e) { 417 throw new NuxeoException(e); 418 } 419 } 420 421 @GET 422 @Produces("text/html") 423 @Path("extensionPoint2Component/{epId}") 424 public Object extensionPoint2Component(@PathParam("epId") String epId) { 425 426 ExtensionPointInfo epi = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()).getExtensionPoint( 427 epId); 428 if (epi == null) { 429 return null; 430 } 431 String cid = epi.getComponent().getId(); 432 433 org.nuxeo.common.utils.Path target = new org.nuxeo.common.utils.Path(getContext().getRoot().getName()); 434 target = target.append(distributionId); 435 target = target.append("viewComponent"); 436 target = target.append(cid + "#extensionPoint." + epId); 437 try { 438 return Response.seeOther(new URI(target.toString())).build(); 439 } catch (URISyntaxException e) { 440 throw new NuxeoException(e); 441 } 442 } 443 444 @Path("viewBundle/{bundleId}") 445 public Resource viewBundle(@PathParam("bundleId") String bundleId) { 446 NuxeoArtifactWebObject wo = (NuxeoArtifactWebObject) ctx.newObject("bundle", bundleId); 447 NuxeoArtifact nxItem = wo.getNxArtifact(); 448 if (nxItem == null) { 449 throw new WebResourceNotFoundException(bundleId); 450 } 451 TreeHelper.updateTree(getContext(), nxItem.getHierarchyPath()); 452 return wo; 453 } 454 455 @Path("viewComponent/{componentId}") 456 public Resource viewComponent(@PathParam("componentId") String componentId) { 457 NuxeoArtifactWebObject wo = (NuxeoArtifactWebObject) ctx.newObject("component", componentId); 458 NuxeoArtifact nxItem = wo.getNxArtifact(); 459 if (nxItem == null) { 460 throw new WebResourceNotFoundException(componentId); 461 } 462 TreeHelper.updateTree(getContext(), nxItem.getHierarchyPath()); 463 return wo; 464 } 465 466 @Path("viewSeamComponent/{componentId}") 467 public Resource viewSeamComponent(@PathParam("componentId") String componentId) { 468 return (NuxeoArtifactWebObject) ctx.newObject("seamComponent", componentId); 469 } 470 471 @Path("viewOperation/{opId}") 472 public Resource viewOperation(@PathParam("opId") String opId) { 473 return (NuxeoArtifactWebObject) ctx.newObject("operation", opId); 474 } 475 476 @Path("viewService/{serviceId}") 477 public Resource viewService(@PathParam("serviceId") String serviceId) { 478 NuxeoArtifactWebObject wo = (NuxeoArtifactWebObject) ctx.newObject("service", serviceId); 479 NuxeoArtifact nxItem = wo.getNxArtifact(); 480 if (nxItem == null) { 481 throw new WebResourceNotFoundException(serviceId); 482 } 483 TreeHelper.updateTree(getContext(), nxItem.getHierarchyPath()); 484 return wo; 485 } 486 487 @Path("viewExtensionPoint/{epId}") 488 public Resource viewExtensionPoint(@PathParam("epId") String epId) { 489 NuxeoArtifactWebObject wo = (NuxeoArtifactWebObject) ctx.newObject("extensionPoint", epId); 490 NuxeoArtifact nxItem = wo.getNxArtifact(); 491 if (nxItem == null) { 492 throw new WebResourceNotFoundException(epId); 493 } 494 TreeHelper.updateTree(getContext(), nxItem.getHierarchyPath()); 495 return wo; 496 } 497 498 @Path("viewContribution/{cId}") 499 public Resource viewContribution(@PathParam("cId") String cId) { 500 NuxeoArtifactWebObject wo = (NuxeoArtifactWebObject) ctx.newObject("contribution", cId); 501 NuxeoArtifact nxItem = wo.getNxArtifact(); 502 if (nxItem == null) { 503 throw new WebResourceNotFoundException(cId); 504 } 505 TreeHelper.updateTree(getContext(), nxItem.getHierarchyPath()); 506 return wo; 507 } 508 509 @Path("viewBundleGroup/{gId}") 510 public Resource viewBundleGroup(@PathParam("gId") String gId) { 511 NuxeoArtifactWebObject wo = (NuxeoArtifactWebObject) ctx.newObject("bundleGroup", gId); 512 NuxeoArtifact nxItem = wo.getNxArtifact(); 513 if (nxItem == null) { 514 throw new WebResourceNotFoundException(gId); 515 } 516 TreeHelper.updateTree(getContext(), nxItem.getHierarchyPath()); 517 return wo; 518 } 519 520 @Path("viewArtifact/{id}") 521 public Object viewArtifact(@PathParam("id") String id) { 522 DistributionSnapshot snap = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()); 523 524 BundleGroup bg = snap.getBundleGroup(id); 525 if (bg != null) { 526 return viewBundleGroup(id); 527 } 528 529 BundleInfo bi = snap.getBundle(id); 530 if (bi != null) { 531 return viewBundle(id); 532 } 533 534 ComponentInfo ci = snap.getComponent(id); 535 if (ci != null) { 536 return viewComponent(id); 537 } 538 539 ServiceInfo si = snap.getService(id); 540 if (si != null) { 541 return viewService(id); 542 } 543 544 ExtensionPointInfo epi = snap.getExtensionPoint(id); 545 if (epi != null) { 546 return viewExtensionPoint(id); 547 } 548 549 ExtensionInfo ei = snap.getContribution(id); 550 if (ei != null) { 551 return viewContribution(id); 552 } 553 554 return Response.status(404).build(); 555 } 556 557 public String getLabel(String id) { 558 return null; 559 } 560 561 @GET 562 @Produces("text/html") 563 @Path("listSeamComponents") 564 public Object listSeamComponents() { 565 return dolistSeamComponents("listSeamComponents", false); 566 } 567 568 @GET 569 @Produces("text/html") 570 @Path("listSeamComponentsSimple") 571 public Object listSeamComponentsSimple() { 572 return dolistSeamComponents("listSeamComponentsSimple", true); 573 } 574 575 protected Object dolistSeamComponents(String view, boolean hideNav) { 576 577 getSnapshotManager().initSeamContext(getContext().getRequest()); 578 579 DistributionSnapshot snap = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()); 580 List<SeamComponentInfo> seamComponents = snap.getSeamComponents(); 581 return getView(view).arg("seamComponents", seamComponents).arg(Distribution.DIST_ID, 582 ctx.getProperty(Distribution.DIST_ID)).arg("hideNav", Boolean.valueOf(hideNav)); 583 } 584 585 @GET 586 @Produces("text/html") 587 @Path("listOperations") 588 public Object listOperations() { 589 DistributionSnapshot snap = getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()); 590 List<OperationInfo> operations = snap.getOperations(); 591 return getView("listOperations").arg("operations", operations).arg(Distribution.DIST_ID, 592 ctx.getProperty(Distribution.DIST_ID)).arg("hideNav", Boolean.valueOf(false)); 593 } 594 595 protected String sanitize(String value) { 596 return Framework.getService(HtmlSanitizerService.class).sanitizeString(value, null); 597 } 598 599}