001 // This file is part of AceWiki. 002 // Copyright 2008-2012, AceWiki developers. 003 // 004 // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU 005 // Lesser General Public License as published by the Free Software Foundation, either version 3 of 006 // the License, or (at your option) any later version. 007 // 008 // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 009 // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 010 // Lesser General Public License for more details. 011 // 012 // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If 013 // not, see http://www.gnu.org/licenses/. 014 015 package ch.uzh.ifi.attempto.acewiki; 016 017 import java.io.InputStream; 018 import java.util.ArrayList; 019 import java.util.List; 020 import java.util.Map; 021 import java.util.Properties; 022 import java.util.Random; 023 import java.util.Stack; 024 025 import javax.servlet.http.Cookie; 026 027 import nextapp.echo.app.Alignment; 028 import nextapp.echo.app.ApplicationInstance; 029 import nextapp.echo.app.Color; 030 import nextapp.echo.app.Column; 031 import nextapp.echo.app.Component; 032 import nextapp.echo.app.ContentPane; 033 import nextapp.echo.app.Extent; 034 import nextapp.echo.app.Font; 035 import nextapp.echo.app.Insets; 036 import nextapp.echo.app.ResourceImageReference; 037 import nextapp.echo.app.Row; 038 import nextapp.echo.app.SplitPane; 039 import nextapp.echo.app.TaskQueueHandle; 040 import nextapp.echo.app.WindowPane; 041 import nextapp.echo.app.event.ActionEvent; 042 import nextapp.echo.app.event.ActionListener; 043 import nextapp.echo.app.layout.ColumnLayoutData; 044 import nextapp.echo.webcontainer.ContainerContext; 045 import ch.uzh.ifi.attempto.acewiki.core.AceWikiDataExporter; 046 import ch.uzh.ifi.attempto.acewiki.core.AceWikiEngine; 047 import ch.uzh.ifi.attempto.acewiki.core.AceWikiStorage; 048 import ch.uzh.ifi.attempto.acewiki.core.LanguageHandler; 049 import ch.uzh.ifi.attempto.acewiki.core.LexiconTableExporter; 050 import ch.uzh.ifi.attempto.acewiki.core.Ontology; 051 import ch.uzh.ifi.attempto.acewiki.core.OntologyElement; 052 import ch.uzh.ifi.attempto.acewiki.core.OntologyExportManager; 053 import ch.uzh.ifi.attempto.acewiki.core.OntologyExporter; 054 import ch.uzh.ifi.attempto.acewiki.core.OntologyTextElement; 055 import ch.uzh.ifi.attempto.acewiki.core.StatementTableExporter; 056 import ch.uzh.ifi.attempto.acewiki.core.User; 057 import ch.uzh.ifi.attempto.acewiki.core.UserBase; 058 import ch.uzh.ifi.attempto.acewiki.gui.AboutPage; 059 import ch.uzh.ifi.attempto.acewiki.gui.ArticlePage; 060 import ch.uzh.ifi.attempto.acewiki.gui.ExportWindow; 061 import ch.uzh.ifi.attempto.acewiki.gui.FormPane; 062 import ch.uzh.ifi.attempto.acewiki.gui.IconButton; 063 import ch.uzh.ifi.attempto.acewiki.gui.IndexPage; 064 import ch.uzh.ifi.attempto.acewiki.gui.ListItem; 065 import ch.uzh.ifi.attempto.acewiki.gui.LoginWindow; 066 import ch.uzh.ifi.attempto.acewiki.gui.SearchPage; 067 import ch.uzh.ifi.attempto.acewiki.gui.StartPage; 068 import ch.uzh.ifi.attempto.acewiki.gui.Title; 069 import ch.uzh.ifi.attempto.acewiki.gui.UserWindow; 070 import ch.uzh.ifi.attempto.acewiki.gui.WikiPage; 071 import ch.uzh.ifi.attempto.base.Logger; 072 import ch.uzh.ifi.attempto.echocomp.HSpace; 073 import ch.uzh.ifi.attempto.echocomp.Label; 074 import ch.uzh.ifi.attempto.echocomp.MessageWindow; 075 import ch.uzh.ifi.attempto.echocomp.SmallButton; 076 import ch.uzh.ifi.attempto.echocomp.SolidLabel; 077 import ch.uzh.ifi.attempto.echocomp.Style; 078 import ch.uzh.ifi.attempto.echocomp.TextAreaWindow; 079 import ch.uzh.ifi.attempto.echocomp.TextField; 080 import ch.uzh.ifi.attempto.echocomp.VSpace; 081 import ch.uzh.ifi.attempto.preditor.PreditorWindow; 082 import ch.uzh.ifi.attempto.preditor.WordEditorWindow; 083 import echopoint.externalevent.ExternalEvent; 084 import echopoint.externalevent.ExternalEventListener; 085 import echopoint.externalevent.ExternalEventMonitor; 086 087 /** 088 * This class represents an AceWiki wiki instance (including its graphical user interface). There 089 * is such a wiki object for every wiki user. 090 * 091 * @author Tobias Kuhn 092 */ 093 public class Wiki implements ActionListener, ExternalEventListener { 094 095 private static final long serialVersionUID = 2777443689044226043L; 096 097 private Map<String, String> parameters; 098 099 private final Ontology ontology; 100 private final AceWikiEngine engine; 101 private String language; 102 private User user; 103 private OntologyExportManager ontologyExportManager; 104 private static AceWikiStorage storage; 105 106 private WikiPage currentPage; 107 private Column pageCol; 108 private ContentPane contentPane = new ContentPane(); 109 private Row navigationButtons = new Row(); 110 private Logger logger; 111 private SplitPane wikiPane; 112 private Row loginBackground; 113 114 private IconButton backButton = new IconButton("Back", this); 115 private IconButton forwardButton = new IconButton("Forward", this); 116 private IconButton refreshButton = new IconButton("Refresh", this); 117 private IconButton userButton = new IconButton("User", this); 118 private IconButton logoutButton = new IconButton("Logout", this); 119 private IconButton searchButton = new IconButton("Search", this); 120 private TextField searchTextField = new TextField(170, this); 121 private Label userLabel = new SolidLabel("Anonymous", Font.ITALIC); 122 123 private SmallButton homeButton = new SmallButton("Main Page", this, 12); 124 private SmallButton indexButton = new SmallButton("Index", this, 12); 125 private SmallButton searchButton2 = new SmallButton("Search", this, 12); 126 private SmallButton aboutButton = new SmallButton("About", this, 12); 127 private SmallButton randomButton = new SmallButton("Random Article", this, 12); 128 private SmallButton newButton = new SmallButton("New Word...", this, 12); 129 private SmallButton exportButton = new SmallButton("Export...", this, 12); 130 131 private StartPage startPage; 132 133 private Stack<WikiPage> history = new Stack<WikiPage>(); 134 private Stack<WikiPage> forward = new Stack<WikiPage>(); 135 136 private TaskQueueHandle taskQueue; 137 private MessageWindow waitWindow; 138 private List<Task> strongTasks = new ArrayList<Task>(); 139 private List<Task> weakTasks = new ArrayList<Task>(); 140 141 private ExternalEventMonitor externalEventMonitor; 142 143 private AceWikiApp application; 144 145 private static Properties properties; 146 147 private boolean disposed = false; 148 149 private boolean locked = false; 150 private ActionListener lockedListener; 151 152 /** 153 * Creates a new wiki instance. 154 * 155 * @param backend The backend object contains ontology of the wiki. 156 * @param parameters A set of parameters in the form of name/value pairs. 157 * @param sessionID The session id. 158 */ 159 Wiki(Backend backend, Map<String, String> parameters, int sessionID) { 160 this.parameters = parameters; 161 162 storage = backend.getStorage(); 163 ontology = backend.getOntology(); 164 165 engine = ontology.getEngine(); 166 language = getParameter("language"); 167 if (language == null || language.equals("")) { 168 language = engine.getLanguages()[0]; 169 } 170 logger = new Logger(getParameter("context:logdir") + "/" + ontology.getName(), "anon", sessionID); 171 application = (AceWikiApp) ApplicationInstance.getActive(); 172 taskQueue = application.createTaskQueue(); 173 174 ontologyExportManager = new OntologyExportManager(ontology); 175 for (OntologyExporter o : engine.getExporters()) { 176 ontologyExportManager.addExporter(o); 177 } 178 ontologyExportManager.addExporter(new LexiconTableExporter()); 179 ontologyExportManager.addExporter(new StatementTableExporter(language)); 180 ontologyExportManager.addExporter(new AceWikiDataExporter()); 181 182 SplitPane splitPane1 = new SplitPane(SplitPane.ORIENTATION_VERTICAL_TOP_BOTTOM); 183 splitPane1.setSeparatorPosition(new Extent(50)); 184 splitPane1.setSeparatorHeight(new Extent(0)); 185 186 SplitPane splitPane2 = new SplitPane(SplitPane.ORIENTATION_HORIZONTAL_RIGHT_LEFT); 187 splitPane2.setSeparatorPosition(new Extent(215)); 188 splitPane2.setSeparatorWidth(new Extent(0)); 189 190 navigationButtons.setInsets(new Insets(5)); 191 navigationButtons.setBackground(Style.shadedBackground); 192 193 navigationButtons.add(backButton); 194 navigationButtons.add(new HSpace(5)); 195 navigationButtons.add(forwardButton); 196 navigationButtons.add(new HSpace(5)); 197 navigationButtons.add(refreshButton); 198 navigationButtons.add(new HSpace(30)); 199 Row userRow = new Row(); 200 userRow.add(userButton); 201 userRow.add(new HSpace(3)); 202 userLabel.setForeground(Color.DARKGRAY); 203 userRow.add(userLabel); 204 logoutButton.setVisible(false); 205 userRow.add(logoutButton); 206 userRow.setVisible(isLoginEnabled()); 207 navigationButtons.add(userRow); 208 209 ContentPane menuBar = new ContentPane(); 210 menuBar.setBackground(Style.shadedBackground); 211 menuBar.add(navigationButtons); 212 213 Row searchRow = new Row(); 214 searchRow.setInsets(new Insets(5)); 215 searchRow.setBackground(Style.shadedBackground); 216 searchRow.add(searchButton); 217 searchRow.add(new HSpace(5)); 218 searchRow.add(searchTextField); 219 220 ContentPane searchBar = new ContentPane(); 221 searchBar.setBackground(Style.shadedBackground); 222 searchBar.add(searchRow); 223 224 wikiPane = new SplitPane( 225 SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT, 226 new Extent(145) 227 ); 228 wikiPane.setSeparatorHeight(new Extent(0)); 229 230 ContentPane sideBar = new ContentPane(); 231 sideBar.setBackground(Style.shadedBackground); 232 Column sideCol = new Column(); 233 sideCol.setInsets(new Insets(10, 10)); 234 sideCol.setCellSpacing(new Extent(1)); 235 236 Label logo = new Label(new ResourceImageReference( 237 "ch/uzh/ifi/attempto/acewiki/gui/img/AceWikiLogoSmall.png" 238 )); 239 sideCol.add(logo); 240 241 sideCol.add(new VSpace(10)); 242 243 ColumnLayoutData layout = new ColumnLayoutData(); 244 layout.setAlignment(Alignment.ALIGN_CENTER); 245 246 String title = getParameter("title"); 247 if (title != null && title.length() > 0) { 248 Label titleLabel = new Label(title, Font.ITALIC, 14); 249 sideCol.add(titleLabel); 250 titleLabel.setLayoutData(layout); 251 sideCol.add(new VSpace(5)); 252 } 253 254 if (isReadOnly()) { 255 SolidLabel rolabel = new SolidLabel("— READ ONLY —", Font.ITALIC); 256 rolabel.setFont(new Font(Style.fontTypeface, Font.ITALIC, new Extent(10))); 257 rolabel.setLayoutData(layout); 258 sideCol.add(rolabel); 259 sideCol.add(new VSpace(5)); 260 } 261 262 sideCol.add(new VSpace(20)); 263 264 SolidLabel label1 = new SolidLabel("Navigation:", Font.ITALIC); 265 label1.setFont(new Font(Style.fontTypeface, Font.ITALIC, new Extent(10))); 266 sideCol.add(label1); 267 sideCol.add(new ListItem(homeButton)); 268 sideCol.add(new ListItem(indexButton)); 269 sideCol.add(new ListItem(searchButton2)); 270 sideCol.add(new ListItem(aboutButton)); 271 sideCol.add(new ListItem(randomButton)); 272 273 sideCol.add(new VSpace(10)); 274 275 SolidLabel label2 = new SolidLabel("Actions:", Font.ITALIC); 276 label2.setFont(new Font(Style.fontTypeface, Font.ITALIC, new Extent(10))); 277 sideCol.add(label2); 278 if (!isReadOnly() && getEngine().getLexicalTypes().length > 0) { 279 sideCol.add(new ListItem(newButton)); 280 } 281 sideCol.add(new ListItem(exportButton)); 282 283 externalEventMonitor = new ExternalEventMonitor(); 284 externalEventMonitor.addExternalEventListener(this); 285 sideCol.add(externalEventMonitor); 286 287 //sideCol.add(new VSpace(20)); 288 //sideCol.add(new ItalicLabel("Session ID: " + sessionID)); 289 290 sideBar.add(sideCol); 291 292 SplitPane splitPane3 = new SplitPane(SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT); 293 splitPane3.setSeparatorWidth(new Extent(1)); 294 splitPane3.setSeparatorColor(Color.BLACK); 295 splitPane3.setSeparatorPosition(new Extent(0)); 296 splitPane3.add(new Label()); 297 298 SplitPane splitPane4 = new SplitPane(SplitPane.ORIENTATION_VERTICAL_TOP_BOTTOM); 299 splitPane4.setSeparatorHeight(new Extent(1)); 300 splitPane4.setSeparatorColor(Color.BLACK); 301 splitPane4.setSeparatorPosition(new Extent(0)); 302 splitPane4.add(new Label()); 303 304 splitPane3.add(splitPane4); 305 pageCol = new Column(); 306 splitPane4.add(pageCol); 307 308 splitPane2.add(searchBar); 309 splitPane2.add(menuBar); 310 311 splitPane1.add(splitPane2); 312 splitPane1.add(splitPane3); 313 314 wikiPane.add(sideBar); 315 wikiPane.add(splitPane1); 316 317 contentPane.add(wikiPane); 318 319 startPage = new StartPage(this); 320 321 // auto login 322 if (isLoginEnabled()) { 323 String userName = getCookie("lastusername"); 324 boolean stayLoggedIn = getCookie("stayloggedin").equals("true"); 325 if (getUserBase().containsUser(userName) && stayLoggedIn) { 326 String clientToken = getCookie("stayloggedintoken"); 327 if (clientToken.length() > 0) { 328 log("syst", "try auto login..."); 329 user = getUserBase().autoLogin(userName, clientToken); 330 if (user != null) { 331 log("syst", "auto login successful: " + user.getName()); 332 setUser(user); 333 } else { 334 log("syst", "auto login failed: " + userName); 335 clearCookie("stayloggedintoken"); 336 } 337 } 338 } 339 } 340 341 String p = null; 342 try { 343 p = ((String[]) getContainerContext().getInitialRequestParameterMap() 344 .get("showpage"))[0]; 345 } catch (Exception ex) {} 346 347 if (p != null && ontology.getElement(p) != null) { 348 setCurrentPage(ArticlePage.create(ontology.getElement(p), this)); 349 } else { 350 setCurrentPage(startPage); 351 } 352 353 // This thread checks regularly for pending tasks and executes them. Strong tasks take 354 // precedence over weak ones. 355 Thread asyncThread = new Thread() { 356 357 public void run() { 358 while (true) { 359 try { 360 sleep(500); 361 } catch (InterruptedException ex) {} 362 363 if (disposed) { 364 break; 365 } 366 367 Task task = null; 368 if (strongTasks.size() > 0) { 369 task = strongTasks.remove(0); 370 } else if (weakTasks.size() > 0) { 371 task = weakTasks.remove(0); 372 } 373 374 final Task fTask = task; 375 if (fTask != null) { 376 task.run(); 377 application.enqueueTask(taskQueue, new Runnable() { 378 public synchronized void run() { 379 fTask.updateGUI(); 380 if (waitWindow != null) { 381 removeWindow(waitWindow); 382 waitWindow = null; 383 } 384 } 385 }); 386 } 387 } 388 } 389 390 }; 391 asyncThread.setPriority(Thread.MIN_PRIORITY); 392 asyncThread.start(); 393 394 update(); 395 } 396 397 /** 398 * Returns the content pane containing the wiki GUI. 399 * 400 * @return The content pane. 401 */ 402 public ContentPane getContentPane() { 403 return contentPane; 404 } 405 406 /** 407 * Returns the application instance object of this wiki. 408 * 409 * @return The application instance. 410 */ 411 public ApplicationInstance getApplication() { 412 return application; 413 } 414 415 /** 416 * Returns the value of the given parameter. These parameters are defined in the web.xml file 417 * of the web application. 418 * 419 * @param paramName The parameter name. 420 * @return The value of the parameter. 421 */ 422 public String getParameter(String paramName) { 423 return parameters.get(paramName); 424 } 425 426 /** 427 * Returns whether the login features are enabled. 428 * 429 * @return true if login is enabled. 430 */ 431 public boolean isLoginEnabled() { 432 return "yes".equals(getParameter("login")); 433 } 434 435 /** 436 * Returns whether login is required for viewing the wiki data. 437 * 438 * @return true if login is required for viewing. 439 */ 440 public boolean isLoginRequiredForViewing() { 441 if (!isLoginEnabled()) return false; 442 return "yes".equals(getParameter("login_required")); 443 } 444 445 /** 446 * Returns whether login is required for editing the wiki data. 447 * 448 * @return true if login is required for editing. 449 */ 450 public boolean isLoginRequiredForEditing() { 451 if (!isLoginEnabled()) return false; 452 if (isLoginRequiredForViewing()) return true; 453 return "edit".equals(getParameter("login_required")); 454 } 455 456 /** 457 * Returns whether the user registration is open to everyone. 458 * 459 * @return true if the user registration is open. 460 */ 461 public boolean isUserRegistrationOpen() { 462 if (!isLoginEnabled()) return false; 463 return !"no".equals(getParameter("register")); 464 } 465 466 /** 467 * Returns whether the wiki is in the current situation editable. This depends on the fact 468 * whether a user is logged in and whether login is required for editing the wiki data. 469 * 470 * @return true if the wiki is editable. 471 */ 472 public boolean isEditable() { 473 return (user != null || !isLoginRequiredForEditing()); 474 } 475 476 /** 477 * Returns true if this wiki is set to be read-only. 478 * 479 * @return true if this wiki is read-only. 480 */ 481 public boolean isReadOnly() { 482 return "on".equals(parameters.get("readonly")); 483 } 484 485 /** 486 * Shows the window. 487 * 488 * @param window The window to be shown. 489 */ 490 public void showWindow(WindowPane window) { 491 cleanWindows(); 492 if (window instanceof WordEditorWindow || 493 window instanceof PreditorWindow || 494 window instanceof TextAreaWindow) { 495 int c = getContentPane().getComponentCount() - 1; 496 window.setPositionX(new Extent(50 + (c % 5)*40)); 497 window.setPositionY(new Extent(50 + (c % 5)*20)); 498 } 499 getContentPane().add(window); 500 } 501 502 /** 503 * Shows a word editor window. 504 * 505 * @param element The ontology element to be edited. 506 */ 507 public void showEditorWindow(OntologyElement element) { 508 WordEditorWindow editorWindow = new WordEditorWindow("Word Editor"); 509 editorWindow.addTab(new FormPane(element, editorWindow, this)); 510 showWindow(editorWindow); 511 } 512 513 /** 514 * Shows a word creator window for the given word type and number. 515 * 516 * @param type The word type. 517 * @param wordNumber The word number. 518 * @param actionListener The actionlistener. 519 */ 520 public void showCreatorWindow(String type, int wordNumber, ActionListener actionListener) { 521 WordEditorWindow creatorWindow = new WordEditorWindow("Word Creator"); 522 creatorWindow.addTab(new FormPane(type, wordNumber, creatorWindow, this, actionListener)); 523 showWindow(creatorWindow); 524 } 525 526 /** 527 * Removes the window. 528 * 529 * @param window The window to be removed. 530 */ 531 public void removeWindow(WindowPane window) { 532 window.setVisible(false); 533 window.dispose(); 534 cleanWindows(); 535 } 536 537 private void cleanWindows() { 538 for (Component c : getContentPane().getComponents()) { 539 if (!c.isVisible()) { 540 getContentPane().remove(c); 541 } 542 } 543 } 544 545 /** 546 * Shows the login window. 547 */ 548 public void showLoginWindow() { 549 if (isLoginRequiredForViewing()) { 550 getContentPane().removeAll(); 551 loginBackground = new Row(); 552 loginBackground.setInsets(new Insets(10, 10)); 553 loginBackground.setCellSpacing(new Extent(30)); 554 Label loginBgLogo = new Label(getImage("AceWikiLogoSmall.png")); 555 loginBackground.add(loginBgLogo); 556 loginBackground.add(new Title(getParameter("title"), true)); 557 getContentPane().add(loginBackground); 558 getContentPane().setBackground(new Color(230, 230, 230)); 559 } 560 showWindow(new LoginWindow(this)); 561 } 562 563 /** 564 * Switches to the given page. 565 * 566 * @param page The page to switch to. 567 */ 568 public void showPage(WikiPage page) { 569 if (!currentPage.equals(page)) { 570 history.push(currentPage); 571 if (history.size() > 20) history.remove(0); 572 forward.clear(); 573 } 574 setCurrentPage(page); 575 log("navi", "goto: " + page); 576 update(); 577 } 578 579 /** 580 * Switches to the page of the given ontology element. 581 * 582 * @param e The ontology element the page of which should be shown. 583 */ 584 public void showPage(OntologyElement e) { 585 showPage(ArticlePage.create(e, this)); 586 } 587 588 /** 589 * Go to the previous page in the history. 590 */ 591 public void back() { 592 if (history.isEmpty()) return; 593 forward.push(currentPage); 594 if (forward.size() > 20) forward.remove(0); 595 WikiPage page = history.pop(); 596 setCurrentPage(page); 597 log("navi", "back: " + page); 598 update(); 599 } 600 601 /** 602 * Go to the next page in the history. 603 */ 604 public void forward() { 605 if (forward.isEmpty()) return; 606 history.push(currentPage); 607 if (history.size() > 20) history.remove(0); 608 WikiPage page = forward.pop(); 609 setCurrentPage(page); 610 log("navi", "forw: " + page); 611 update(); 612 } 613 614 /** 615 * Show the start page. 616 */ 617 public void showStartPage() { 618 showPage(startPage); 619 } 620 621 /** 622 * Show the index page. 623 */ 624 public void showIndexPage() { 625 showPage(new IndexPage(this)); 626 } 627 628 /** 629 * Show the search page. 630 */ 631 public void showSearchPage() { 632 showPage(new SearchPage(this, "")); 633 } 634 635 /** 636 * Show the about page. 637 */ 638 public void showAboutPage() { 639 showPage(new AboutPage(this)); 640 } 641 642 /** 643 * Returns the ontology; 644 * 645 * @return The ontology. 646 */ 647 public Ontology getOntology() { 648 return ontology; 649 } 650 651 /** 652 * Returns the ontology export manager. 653 * 654 * @return The ontology export manager. 655 */ 656 public OntologyExportManager getOntologyExportManager() { 657 return ontologyExportManager; 658 } 659 660 /** 661 * Returns the user base for this wiki. 662 * 663 * @return The user base. 664 */ 665 public UserBase getUserBase() { 666 return storage.getUserBase(ontology); 667 } 668 669 /** 670 * Returns all ontology elements. The list is a copy of the internal list. 671 * 672 * @return A list of all ontology elements. 673 */ 674 public List<OntologyElement> getOntologyElements() { 675 return ontology.getOntologyElements(); 676 } 677 678 /** 679 * Updates the GUI. 680 */ 681 public void update() { 682 pageCol.removeAll(); 683 pageCol.add(currentPage); 684 685 removeExpiredPages(history); 686 removeExpiredPages(forward); 687 backButton.setEnabled(!history.isEmpty()); 688 forwardButton.setEnabled(!forward.isEmpty()); 689 690 // The commented-out code below checks at every GUI update whether the ontology is consistent or not. 691 // If not, a red AceWiki logo is shown. Usually, this case should never occur because we check for 692 // consistency after every new statement. 693 //if (ontology.isConsistent()) { 694 // logo.setIcon(new ResourceImageReference("ch/uzh/ifi/attempto/acewiki/gui/img/AceWikiLogoSmall.png")); 695 //} else { 696 // logo.setIcon(new ResourceImageReference("ch/uzh/ifi/attempto/acewiki/gui/img/AceWikiLogoSmallRed.png")); 697 //} 698 } 699 700 private void removeExpiredPages(Stack<WikiPage> stack) { 701 WikiPage previousPage = null; 702 for (WikiPage page : new ArrayList<WikiPage>(stack)) { 703 if (page.isExpired() || page.equals(previousPage)) { 704 stack.remove(page); 705 } else { 706 previousPage = page; 707 } 708 } 709 if (stack.size() > 0 && currentPage.equals(stack.peek())) { 710 stack.pop(); 711 } 712 } 713 714 private void setCurrentPage(WikiPage currentPage) { 715 this.currentPage = currentPage; 716 refresh(); 717 } 718 719 /** 720 * Refreshes the current page. 721 */ 722 public void refresh() { 723 currentPage.update(); 724 } 725 726 public void actionPerformed(ActionEvent e) { 727 Object src = e.getSource(); 728 String c = e.getActionCommand(); 729 730 if (locked) { 731 if (lockedListener != null) { 732 lockedListener.actionPerformed(new ActionEvent(this, "locked")); 733 } 734 return; 735 } 736 737 if (src == backButton) { 738 log("page", "pressed: back"); 739 back(); 740 } else if (src == forwardButton) { 741 log("page", "pressed: forward"); 742 forward(); 743 } else if (src == indexButton) { 744 log("page", "pressed: index"); 745 showIndexPage(); 746 } else if (src == aboutButton) { 747 log("page", "pressed: about"); 748 showAboutPage(); 749 } else if (src == homeButton) { 750 log("page", "pressed: main page"); 751 showStartPage(); 752 } else if (src == randomButton) { 753 log("page", "pressed: random page"); 754 List<OntologyElement> elements = ontology.getOntologyElements(); 755 if (elements.size() > 0) { 756 int r = (new Random()).nextInt(elements.size()); 757 showPage(elements.get(r)); 758 } else { 759 showStartPage(); 760 } 761 } else if (src == refreshButton) { 762 log("page", "pressed: refresh"); 763 update(); 764 refresh(); 765 } else if (src == newButton) { 766 log("page", "pressed: new word"); 767 if (!isEditable()) { 768 showLoginWindow(); 769 } else { 770 WordEditorWindow w = new WordEditorWindow("Word Creator"); 771 for (String t : getEngine().getLexicalTypes()) { 772 w.addTab(new FormPane(t, w, this)); 773 } 774 showWindow(w); 775 } 776 } else if (src == searchButton || src == searchTextField || src == searchButton2) { 777 log("page", "pressed: search '" + searchTextField.getText() + "'"); 778 String s = searchTextField.getText(); 779 searchTextField.setText(""); 780 OntologyElement el = ontology.getElement(s.replace(' ', '_')); 781 if (el == null) { 782 showPage(new SearchPage(this, s)); 783 } else { 784 showPage(el); 785 } 786 } else if (src == exportButton) { 787 showWindow(new ExportWindow(this)); 788 } else if (src == logoutButton) { 789 showWindow(new MessageWindow( 790 "Logout", 791 "Do you really want to log out?", 792 null, 793 this, 794 "Yes", "No" 795 )); 796 } else if (src == userButton) { 797 if (user == null) { 798 showLoginWindow(); 799 } else { 800 showWindow(new UserWindow(this)); 801 } 802 } else if (src instanceof MessageWindow && c.equals("Yes")) { 803 logout(); 804 } else if (src instanceof OntologyTextElement) { 805 // for newly generated elements 806 OntologyTextElement te = (OntologyTextElement) src; 807 log("edit", "new word: " + te.getOntologyElement().getWord()); 808 showPage(te.getOntologyElement()); 809 } 810 } 811 812 public void externalEvent(ExternalEvent e) { 813 OntologyElement oe = ontology.getElement(e.getParameter("page")); 814 if (oe != null) showPage(oe); 815 } 816 817 /** 818 * Writes the log entry to the log file. 819 * 820 * @param type The type of the log entry. 821 * @param text The text of the log entry. 822 */ 823 public void log(String type, String text) { 824 logger.log(type, text); 825 } 826 827 /** 828 * Logs in the given user. 829 * 830 * @param user The user to log in. 831 * @param stayLoggedIn Defines whether the user should stay logged in or not. 832 */ 833 public void login(User user, boolean stayLoggedIn) { 834 log("syst", "login"); 835 user.setUserData("stayloggedin", stayLoggedIn + ""); 836 setCookie("stayloggedin", stayLoggedIn + ""); 837 String stayloggedintoken; 838 if (stayLoggedIn) { 839 stayloggedintoken = (new Random()).nextLong() + ""; 840 } else { 841 stayloggedintoken = ""; 842 } 843 user.setUserData("stayloggedintoken", stayloggedintoken); 844 setCookie("stayloggedintoken", stayloggedintoken); 845 setUser(user); 846 } 847 848 /** 849 * Logs out the current user. 850 */ 851 public void logout() { 852 log("syst", "logout"); 853 user.setUserData("stayloggedintoken", ""); 854 setCookie("stayloggedintoken", ""); 855 application.logout(); 856 } 857 858 /** 859 * Returns the user of this wiki object. 860 * 861 * @return The user. 862 */ 863 public User getUser() { 864 return user; 865 } 866 867 /** 868 * Sets the user. 869 * 870 * @param user The user. 871 */ 872 public void setUser(User user) { 873 this.user = user; 874 logger.setUsername(user.getName()); 875 userLabel.setForeground(Color.BLACK); 876 userLabel.setText(user.getName()); 877 logoutButton.setVisible(true); 878 if (loginBackground != null) { 879 getContentPane().removeAll(); 880 getContentPane().add(wikiPane); 881 getContentPane().setBackground(Color.WHITE); 882 loginBackground = null; 883 } 884 setCookie("lastusername", user.getName()); 885 } 886 887 /** 888 * Sets a cookie on the client. 889 * 890 * @param name The name of the cookie. 891 * @param value The value of the cookie. 892 */ 893 public void setCookie(String name, String value) { 894 Cookie cookie = new Cookie(name, value); 895 cookie.setMaxAge(1000000000); 896 getContainerContext().addCookie(cookie); 897 } 898 899 /** 900 * Clears the given cookie on the client. 901 * 902 * @param name The name of the cookie. 903 */ 904 public void clearCookie(String name) { 905 getContainerContext().addCookie(new Cookie(name, null)); 906 } 907 908 /** 909 * Returns the value of the cookie on the client, or "" if there is no such cookie. 910 * 911 * @param name The name of the cookie. 912 * @return The value of the cookie. 913 */ 914 public String getCookie(String name) { 915 for (Cookie cookie : getContainerContext().getCookies()) { 916 if ((name + "").equals(cookie.getName())) { 917 String value = cookie.getValue(); 918 if (value == null) return ""; 919 return value; 920 } 921 } 922 return ""; 923 } 924 925 private ContainerContext getContainerContext() { 926 return (ContainerContext) application.getContextProperty( 927 ContainerContext.CONTEXT_PROPERTY_NAME 928 ); 929 } 930 931 /** 932 * Returns the AceWiki engine. 933 * 934 * @return The AceWiki engine. 935 */ 936 public AceWikiEngine getEngine() { 937 return engine; 938 } 939 940 /** 941 * Returns the language of this wiki instance. 942 * 943 * @return The name of the language. 944 */ 945 public String getLanguage() { 946 return language; 947 } 948 949 /** 950 * Returns the language handler. 951 * 952 * @return The language handler. 953 */ 954 public LanguageHandler getLanguageHandler() { 955 return engine.getLanguageHandler(language); 956 } 957 958 /** 959 * Returns the logger object. 960 * 961 * @return The logger object. 962 */ 963 public Logger getLogger() { 964 return logger; 965 } 966 967 /** 968 * Runs the task without showing a wait window while it is executed. 969 * 970 * @param task The task. 971 */ 972 public void enqueueTask(Runnable task) { 973 application.enqueueTask(taskQueue, task); 974 } 975 976 /** 977 * Runs the task in an asynchronous way and shows a wait window while it is executed. The task 978 * is treated as a strong task that takes precedence over weak tasks. 979 * 980 * @param title The title of the wait window. 981 * @param message The message of the wait window. 982 * @param task The task. 983 */ 984 public void enqueueStrongAsyncTask(String title, String message, Task task) { 985 waitWindow = new MessageWindow( 986 title, 987 new ResourceImageReference("ch/uzh/ifi/attempto/acewiki/gui/img/wait.gif"), 988 message, 989 null, 990 null 991 ); 992 waitWindow.setClosable(false); 993 showWindow(waitWindow); 994 995 strongTasks.add(task); 996 } 997 998 /** 999 * Runs the task in an asynchronous way without showing a wait window. The task is treated as a 1000 * weak task that can be overtaken by strong tasks. 1001 * 1002 * @param task The task. 1003 */ 1004 public void enqueueWeakAsyncTask(Task task) { 1005 weakTasks.add(task); 1006 } 1007 1008 /** 1009 * Returns information about AceWiki, like the version number and the release date. This 1010 * information is read from the file "acewiki.properties". 1011 * 1012 * @param key The key string. 1013 * @return The value for the given key. 1014 */ 1015 public static String getInfo(String key) { 1016 if (properties == null) { 1017 String f = "ch/uzh/ifi/attempto/acewiki/acewiki.properties"; 1018 InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(f); 1019 properties = new Properties(); 1020 try { 1021 properties.load(in); 1022 } catch (Exception ex) { 1023 ex.printStackTrace(); 1024 } 1025 } 1026 1027 return properties.getProperty(key); 1028 } 1029 1030 /** 1031 * Cleans up when the object is no longer used. 1032 */ 1033 public void dispose() { 1034 disposed = true; 1035 externalEventMonitor.removeExternalEventListener(this); 1036 externalEventMonitor.dispose(); 1037 } 1038 1039 /** 1040 * This methods locks the general buttons of the wiki interface. When one of these buttons 1041 * is pressed, the locked-listener is called. 1042 * 1043 * @param lockedListener The listener to be called when one of the buttons is pressed. 1044 */ 1045 public void lock(ActionListener lockedListener) { 1046 if (locked) return; 1047 locked = true; 1048 this.lockedListener = lockedListener; 1049 } 1050 1051 /** 1052 * Unlocks the wiki interface, if it has been locked before. 1053 */ 1054 public void unlock() { 1055 locked = false; 1056 } 1057 1058 /** 1059 * Returns an image reference for a file in the AceWiki image directory with the given file 1060 * name. 1061 * 1062 * @param fileName The name of the image file. 1063 * @return The image reference. 1064 */ 1065 public static ResourceImageReference getImage(String fileName) { 1066 return Style.getImage("ch/uzh/ifi/attempto/acewiki/gui/img/" + fileName); 1067 } 1068 1069 }