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 }