001/* 002 * ModeShape (http://www.modeshape.org) 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 */ 016package org.modeshape.web.client.contents; 017 018import com.google.gwt.user.client.rpc.AsyncCallback; 019import com.smartgwt.client.util.BooleanCallback; 020import com.smartgwt.client.util.SC; 021import com.smartgwt.client.widgets.Canvas; 022import com.smartgwt.client.widgets.layout.VLayout; 023import java.util.Date; 024import java.util.HashMap; 025import org.modeshape.web.client.Console; 026import org.modeshape.web.client.JcrServiceAsync; 027import org.modeshape.web.shared.BaseCallback; 028import org.modeshape.web.shared.RemoteException; 029import org.modeshape.web.shared.JcrNode; 030import org.modeshape.web.shared.JcrPermission; 031import org.modeshape.web.shared.JcrProperty; 032 033/** 034 * Displays contents. 035 * 036 * |--------------------------------| 037 * | session & workspace | 038 * |--------------------------------| 039 * | path & buttons | 040 * |--------------------------------| 041 * | child nodes, binary & props | 042 * |--------------------------------| 043 * 044 * 045 * 046 * @author kulikov 047 */ 048@SuppressWarnings("synthetic-access") 049public class Contents extends VLayout { 050 051 private final static String ROOT_PATH = "/"; 052 053 private Console console; 054 055 // 056 private String repository; 057 private JcrNode node; 058 private String path; 059 060 private WorkspacePanel wsp = new WorkspacePanel(this); 061 private PathControl pathLabel = new PathControl(this); 062 063 //frames 064 private ChildrenEditor childrenEditor = new ChildrenEditor(this); 065 private PropertiesEditor propertiesEditor = new PropertiesEditor(this); 066 private PermissionsEditor permissionsEditor = new PermissionsEditor(this); 067 private BinaryEditor binaryEditor = new BinaryEditor(); 068 069 //layouts 070 private DetailsLayout details = new DetailsLayout(propertiesEditor, permissionsEditor); 071 private ContentsLayout contentsLayout = new ContentsLayout(childrenEditor, binaryEditor, details); 072 073 //dialog 074 private final AddNodeDialog addNodeDialog = new AddNodeDialog(this); 075 private final ExportDialog exportDialog = new ExportDialog(this); 076 private final ImportDialog importDialog = new ImportDialog(this); 077 078 @SuppressWarnings("unchecked") 079 private final HashMap<String, Session> sessions = new HashMap(); 080 081 /** 082 * Creates contents instance. 083 * 084 * @param console 085 */ 086 public Contents(final Console console) { 087 this.console = console; 088 089 addMember(description()); 090 addMember(new Spacer(20)); 091 092 addMember(wsp); 093 addMember(new Spacer(20)); 094 addMember(pathLabel); 095 addMember(contentsLayout); 096 } 097 098 private Canvas description() { 099 Canvas text = new Canvas(); 100 text.setContents("ModeShape is a distributed, hierarchical, transactional, and consistent data store with support for queries, full-text search, events, versioning, references, and flexible and dynamic schemas. It is very fast, highly available, extremely scalable, and it is 100% open source and written in Java. Clients use the JSR-283 standard Java API for content repositories (aka, JCR) or ModeShape's REST API, and can query content through JDBC and SQL."); 101 text.setWidth100(); 102 text.setAutoHeight(); 103 text.setStyleName("caption"); 104 return text; 105 } 106 107 private Session session() { 108 Session s = sessions.get(repository() + "$" + workspace()); 109 if (s == null) { 110 s = new Session(); 111 sessions.put(repository() + "$" + workspace(), s); 112 } 113 return s; 114 } 115 116 /** 117 * Expose interface to the server side. 118 * 119 * @return the service 120 */ 121 public JcrServiceAsync jcrService() { 122 return this.console.jcrService(); 123 } 124 125 private void showLoadIcon() { 126 pathLabel.setVisible(false); 127 contentsLayout.setVisible(false); 128 console.showLoadingIcon(); 129 } 130 131 private void hideLoadIcon() { 132 console.hideLoadingIcon(); 133 pathLabel.setVisible(true); 134 contentsLayout.setVisible(true); 135 } 136 137 protected void toggleDetails() { 138 contentsLayout.setShowDetails(!contentsLayout.showDetails()); 139 } 140 141 /** 142 * Displays root node of the specified workspace. 143 * 144 * @param name the name of the workspace. 145 */ 146 public void changeWorkspace(final String name) { 147 this.getAndDisplayNode(ROOT_PATH, true); 148 updateControls(); 149 } 150 151 /** 152 * Shows content of the root node of the first reachable workspace of the 153 * given repository. 154 * 155 * @param repository the name of the given repository. 156 * @param changeHistory if true then this action of navigation will be 157 * reflected in the browser URL and will don't touch URL in case of false 158 * value. 159 */ 160 public void show(String repository, final boolean changeHistory) { 161 this.repository = repository; 162 refreshWorkspacesAndReloadNode(null, ROOT_PATH, changeHistory); 163 } 164 165 /** 166 * Shows nodes identified by repository, workspace and path to node. 167 * 168 * @param repository the name of the repository 169 * @param workspace the name of the workspace 170 * @param path the path to node 171 * @param changeHistory true if this action should be reflected in browser 172 * history. 173 */ 174 public void show(final String repository, final String workspace, 175 final String path, final boolean changeHistory) { 176 this.repository = repository; 177 this.refreshWorkspacesAndReloadNode(null, path, changeHistory); 178 } 179 180 /** 181 * Reloads values of the combo box with workspace names. 182 * 183 * Gets values from server side, assigns to combo box and select given name. 184 * 185 * @param name the name to be selected. 186 * @param path the path 187 * @param changeHistory true if the history is to 188 */ 189 private void refreshWorkspacesAndReloadNode(final String name, final String path, 190 final boolean changeHistory) { 191 showLoadIcon(); 192 console.jcrService().getWorkspaces(repository, new AsyncCallback<String[]>() { 193 @Override 194 public void onFailure(Throwable caught) { 195 hideLoadIcon(); 196 RemoteException e = (RemoteException) caught; 197 SC.say(caught.getMessage()); 198 if (e.code() == RemoteException.SECURITY_ERROR) { 199 console.loadRepositoriesList(); 200 } 201 } 202 203 @Override 204 public void onSuccess(String[] workspaces) { 205 wsp.setWorkspaceNames(workspaces); 206 getAndDisplayNode(path, changeHistory); 207 hideLoadIcon(); 208 } 209 }); 210 } 211 212 /** 213 * Reads node with given path and selected repository and workspace. 214 * 215 * @param path the path to the node. 216 * @param changeHistory if true then path will be reflected in browser 217 * history. 218 */ 219 public void getAndDisplayNode(final String path, final boolean changeHistory) { 220 showLoadIcon(); 221 console.jcrService().node(repository(), workspace(), path, new AsyncCallback<JcrNode>() { 222 @Override 223 public void onFailure(Throwable caught) { 224 hideLoadIcon(); 225 SC.say(caught.getMessage()); 226 } 227 228 @Override 229 public void onSuccess(JcrNode node) { 230 displayNode(node); 231 console.changeWorkspaceInURL(workspace(), changeHistory); 232 console.changePathInURL(path, changeHistory); 233 hideLoadIcon(); 234 } 235 }); 236 } 237 238 /** 239 * Displays specified node. 240 * 241 * @param node the node being displayed. 242 */ 243 private void displayNode(JcrNode node) { 244 this.node = node; 245 this.path = node.getPath(); 246 247 pathLabel.display(node.getPath()); 248 249 //display childs, properties and ACLs 250 childrenEditor.show(node); 251 propertiesEditor.show(node); 252 permissionsEditor.show(node); 253 254 displayBinaryContent(node); 255 256 //bring this page on top 257// console.display(Contents.this); 258 } 259 260 private void displayBinaryContent(JcrNode node) { 261 //check for binary content 262 binaryEditor.setVisible(false); 263 for (JcrProperty property : node.getProperties()) { 264 if (property.isBinary()) { 265 binaryEditor.setVisible(true); 266 binaryEditor.setValue(node, property.getName(), property.getValue()); 267 } 268 } 269 } 270 271 /** 272 * Save session's changes. 273 */ 274 public void save() { 275 SC.ask("Do you want to save changes", new BooleanCallback() { 276 @Override 277 public void execute(Boolean yesSelected) { 278 if (yesSelected) { 279 jcrService().save(repository(), workspace(), new BaseCallback<Object>() { 280 @Override 281 public void onSuccess(Object result) { 282 session().setHasChanges(false); 283 updateControls(); 284 } 285 }); 286 } 287 } 288 }); 289 } 290 291 292 public void refreshSession(boolean keepChanges) { 293 console.jcrService().refreshSession(repository(), workspace(), keepChanges, new AsyncCallback() { 294 @Override 295 public void onFailure(Throwable caught) { 296 SC.say(caught.getMessage()); 297 } 298 299 @Override 300 public void onSuccess(Object result) { 301 getAndDisplayNode(path(), true); 302 } 303 }); 304 } 305 306 /** 307 * Prepares dialog for creating new node. 308 * 309 */ 310 public void showAddNodeDialog() { 311 jcrService().getPrimaryTypes(node.getRepository(), 312 node.getWorkspace(), 313 null, 314 false, new AsyncCallback<String[]>() { 315 @Override 316 public void onFailure(Throwable caught) { 317 SC.say(caught.getMessage()); 318 } 319 320 @Override 321 public void onSuccess(String[] result) { 322 addNodeDialog.setPrimaryTypes(result); 323 addNodeDialog.showModal(); 324 } 325 }); 326 } 327 328 /** 329 * Initiates export dialog 330 */ 331 public void showExportDialog() { 332 exportDialog.showModal(); 333 } 334 335 /** 336 * Shows import node dialog 337 */ 338 public void showImportDialog() { 339 importDialog.showModal(); 340 } 341 342 /** 343 * Exports contents to the given file. 344 * 345 * @param name the name of the file. 346 * @param skipBinary 347 * @param noRecurse 348 */ 349 public void exportXML(String name, boolean skipBinary, boolean noRecurse) { 350 console.jcrService().export(repository, workspace(), path(), name, true, true, new AsyncCallback<Object>() { 351 @Override 352 public void onFailure(Throwable caught) { 353 SC.say(caught.getMessage()); 354 } 355 356 @Override 357 public void onSuccess(Object result) { 358 SC.say("Complete"); 359 } 360 }); 361 } 362 363 /** 364 * Imports contents from the given file. 365 * 366 * @param name 367 * @param option 368 */ 369 public void importXML(String name, int option) { 370 console.jcrService().importXML(repository, workspace(), path(), name, 371 option, new AsyncCallback<Object>() { 372 @Override 373 public void onFailure(Throwable caught) { 374 SC.say(caught.getMessage()); 375 } 376 377 @Override 378 public void onSuccess(Object result) { 379 SC.say("Complete"); 380 } 381 }); 382 } 383 384 public void removeNode(JcrNode node) { 385 final String parent = parent(node.getPath()); 386 console.jcrService().removeNode(repository(), workspace(), node.getPath(), new AsyncCallback<Object>() { 387 @Override 388 public void onFailure(Throwable caught) { 389 SC.say(caught.getMessage()); 390 } 391 392 @Override 393 public void onSuccess(Object result) { 394 session().setHasChanges(true); 395 getAndDisplayNode(parent, true); 396 updateControls(); 397 } 398 }); 399 } 400 401 public void addMixin(String name) { 402 console.jcrService().addMixin(repository(), workspace(), path(), name, new AsyncCallback<Object>() { 403 @Override 404 public void onFailure(Throwable caught) { 405 SC.say(caught.getMessage()); 406 } 407 408 @Override 409 public void onSuccess(Object result) { 410 session().setHasChanges(true); 411 show(); 412 updateControls(); 413 } 414 }); 415 } 416 417 public void removeMixin(String name) { 418 console.jcrService().removeMixin(repository(), workspace(), path(), name, new AsyncCallback<Object>() { 419 @Override 420 public void onFailure(Throwable caught) { 421 SC.say(caught.getMessage()); 422 } 423 424 @Override 425 public void onSuccess(Object result) { 426 session().setHasChanges(true); 427 show(); 428 updateControls(); 429 } 430 }); 431 } 432 433 public void setNodeProperty(JcrNode node, String name, Boolean value) { 434 console.jcrService().setProperty(node, name, value, new AsyncCallback<Object>() { 435 @Override 436 public void onFailure(Throwable caught) { 437 SC.say(caught.getMessage()); 438 } 439 440 @Override 441 public void onSuccess(Object result) { 442 session().setHasChanges(true); 443 show(); 444 updateControls(); 445 } 446 }); 447 } 448 449 public void setNodeProperty(JcrNode node, String name, Date value) { 450 console.jcrService().setProperty(node, name, value, new AsyncCallback<Object>() { 451 @Override 452 public void onFailure(Throwable caught) { 453 SC.say(caught.getMessage()); 454 } 455 456 @Override 457 public void onSuccess(Object result) { 458 session().setHasChanges(true); 459 show(); 460 updateControls(); 461 } 462 }); 463 } 464 465 public void setNodeProperty(JcrNode node, String name, String value) { 466 console.jcrService().setProperty(node, name, value, new AsyncCallback<Object>() { 467 @Override 468 public void onFailure(Throwable caught) { 469 SC.say(caught.getMessage()); 470 } 471 472 @Override 473 public void onSuccess(Object result) { 474 session().setHasChanges(true); 475 show(); 476 updateControls(); 477 } 478 }); 479 } 480 481 protected void addNode(String name, String primaryType) { 482 console.jcrService().addNode(repository(), workspace(), path(), name, primaryType, new AsyncCallback<JcrNode>() { 483 @Override 484 public void onFailure(Throwable caught) { 485 SC.say(caught.getMessage()); 486 } 487 488 @Override 489 public void onSuccess(JcrNode node) { 490 session().setHasChanges(true); 491 getAndDisplayNode(path(), false); 492 updateControls(); 493 } 494 }); 495 } 496 497 protected void renameNode(JcrNode node, String name) { 498 console.jcrService().renameNode(repository(), workspace(), node.getPath(), name, new AsyncCallback<Object>() { 499 @Override 500 public void onFailure(Throwable caught) { 501 SC.say(caught.getMessage()); 502 } 503 504 @Override 505 public void onSuccess(Object result) { 506 session().setHasChanges(true); 507 getAndDisplayNode(path(), false); 508 updateControls(); 509 } 510 }); 511 } 512 513 public void addAccessList(String name) { 514 console.jcrService().addAccessList(repository(), workspace(), path(), name, new AsyncCallback() { 515 @Override 516 public void onFailure(Throwable caught) { 517 SC.say(caught.getMessage()); 518 } 519 520 @Override 521 public void onSuccess(Object result) { 522 session().setHasChanges(true); 523 getAndDisplayNode(path(), false); 524 updateControls(); 525 } 526 }); 527 } 528 529 public void updateAccessList(String principal, JcrPermission permission, boolean enabled) { 530 console.jcrService().updateAccessList(repository, workspace(), path(), principal, permission, enabled, new AsyncCallback<Object>() { 531 @Override 532 public void onFailure(Throwable caught) { 533 SC.say(caught.getMessage()); 534 } 535 536 @Override 537 public void onSuccess(Object result) { 538 session().setHasChanges(true); 539 getAndDisplayNode(path(), false); 540 updateControls(); 541 } 542 }); 543 } 544 545 public void removeAccessList(String name) { 546 console.jcrService().removeAccessList(repository(), workspace(), path(), name, new AsyncCallback() { 547 548 @Override 549 public void onFailure(Throwable caught) { 550 SC.say(caught.getMessage()); 551 } 552 553 @Override 554 public void onSuccess(Object result) { 555 session().setHasChanges(true); 556 getAndDisplayNode(path(), false); 557 updateControls(); 558 } 559 }); 560 } 561 562 public String repository() { 563 return repository; 564 } 565 566 public String workspace() { 567 return wsp.getSelectedWorkspace(); 568 } 569 570 public JcrNode node() { 571 return node; 572 } 573 574 public String path() { 575 return path == null ? "/" : path; 576 } 577 578 private String parent(String path) { 579 if (path == null) { 580 return "/"; 581 } 582 583 path = path.substring(0, path.lastIndexOf('/')); 584 if (path.length() == 0) { 585 return "/"; 586 } 587 588 return path; 589 } 590 591 private void updateControls() { 592 wsp.setEnabled(session().hasChanges()); 593 } 594 595 private class Spacer extends VLayout { 596 public Spacer(int size) { 597 super(); 598 setHeight(size); 599 } 600 } 601}