001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki;
021:
022: import java.io.IOException;
023: import java.security.Permission;
024: import java.security.Principal;
025: import java.util.*;
026:
027: import org.apache.commons.lang.ArrayUtils;
028: import org.apache.log4j.Logger;
029:
030: import com.ecyrd.jspwiki.auth.WikiPrincipal;
031: import com.ecyrd.jspwiki.auth.WikiSecurityException;
032: import com.ecyrd.jspwiki.auth.acl.Acl;
033: import com.ecyrd.jspwiki.auth.acl.AclEntry;
034: import com.ecyrd.jspwiki.auth.acl.AclEntryImpl;
035: import com.ecyrd.jspwiki.auth.user.UserProfile;
036: import com.ecyrd.jspwiki.event.*;
037: import com.ecyrd.jspwiki.filters.FilterException;
038: import com.ecyrd.jspwiki.modules.ModuleManager;
039: import com.ecyrd.jspwiki.providers.CachingProvider;
040: import com.ecyrd.jspwiki.providers.ProviderException;
041: import com.ecyrd.jspwiki.providers.RepositoryModifiedException;
042: import com.ecyrd.jspwiki.providers.WikiPageProvider;
043: import com.ecyrd.jspwiki.util.ClassUtil;
044: import com.ecyrd.jspwiki.util.WikiBackgroundThread;
045: import com.ecyrd.jspwiki.workflow.Outcome;
046: import com.ecyrd.jspwiki.workflow.Task;
047: import com.ecyrd.jspwiki.workflow.Workflow;
048:
049: /**
050: * Manages the WikiPages. This class functions as an unified interface towards
051: * the page providers. It handles initialization and management of the providers,
052: * and provides utility methods for accessing the contents.
053: *
054: * @author Janne Jalkanen
055: * @since 2.0
056: */
057: // FIXME: This class currently only functions just as an extra layer over providers,
058: // complicating things. We need to move more provider-specific functionality
059: // from WikiEngine (which is too big now) into this class.
060: public class PageManager extends ModuleManager implements
061: WikiEventListener {
062: private static final long serialVersionUID = 1L;
063:
064: /** The property value for setting the current page provider. Value is {@value}. */
065: public static final String PROP_PAGEPROVIDER = "jspwiki.pageProvider";
066:
067: /** The property value for setting the cache on/off. Value is {@value}. */
068: public static final String PROP_USECACHE = "jspwiki.usePageCache";
069:
070: /** The property value for setting the amount of time before the page locks expire.
071: * Value is {@value}.
072: */
073: public static final String PROP_LOCKEXPIRY = "jspwiki.lockExpiryTime";
074:
075: public static final String PRESAVE_TASK_MESSAGE_KEY = "task.preSaveWikiPage";
076: public static final String PRESAVE_WIKI_CONTEXT = "wikiContext";
077: public static final String SAVE_APPROVER = "workflow.saveWikiPage";
078: public static final String SAVE_DECISION_MESSAGE_KEY = "decision.saveWikiPage";
079: public static final String SAVE_REJECT_MESSAGE_KEY = "notification.saveWikiPage.reject";
080: public static final String SAVE_TASK_MESSAGE_KEY = "task.saveWikiPage";
081:
082: /** Fact name for storing the page name. Value is {@value}. */
083: public static final String FACT_PAGE_NAME = "fact.pageName";
084:
085: /** Fact name for storing a diff text. Value is {@value}. */
086: public static final String FACT_DIFF_TEXT = "fact.diffText";
087:
088: /** Fact name for storing the current text. Value is {@value}. */
089: public static final String FACT_CURRENT_TEXT = "fact.currentText";
090:
091: /** Fact name for storing the proposed (edited) text. Value is {@value}. */
092: public static final String FACT_PROPOSED_TEXT = "fact.proposedText";
093:
094: public static final String FACT_IS_AUTHENTICATED = "fact.isAuthenticated";
095:
096: static Logger log = Logger.getLogger(PageManager.class);
097:
098: private WikiPageProvider m_provider;
099:
100: protected HashMap m_pageLocks = new HashMap();
101:
102: private WikiEngine m_engine;
103:
104: private int m_expiryTime = 60;
105:
106: private LockReaper m_reaper = null;
107:
108: /**
109: * Creates a new PageManager.
110: *
111: * @param engine WikiEngine instance
112: * @param props Properties to use for initialization
113: * @throws WikiException If anything goes wrong, you get this.
114: */
115: public PageManager(WikiEngine engine, Properties props)
116: throws WikiException {
117: super (engine);
118:
119: String classname;
120:
121: m_engine = engine;
122:
123: boolean useCache = "true".equals(props
124: .getProperty(PROP_USECACHE));
125:
126: m_expiryTime = TextUtil.parseIntParameter(props
127: .getProperty(PROP_LOCKEXPIRY), 60);
128:
129: //
130: // If user wants to use a cache, then we'll use the CachingProvider.
131: //
132: if (useCache) {
133: classname = "com.ecyrd.jspwiki.providers.CachingProvider";
134: } else {
135: classname = WikiEngine.getRequiredProperty(props,
136: PROP_PAGEPROVIDER);
137: }
138:
139: try {
140: log.debug("Page provider class: '" + classname + "'");
141:
142: Class providerclass = ClassUtil.findClass(
143: "com.ecyrd.jspwiki.providers", classname);
144:
145: m_provider = (WikiPageProvider) providerclass.newInstance();
146:
147: log.debug("Initializing page provider class " + m_provider);
148: m_provider.initialize(m_engine, props);
149: } catch (ClassNotFoundException e) {
150: log.error("Unable to locate provider class '" + classname
151: + "'", e);
152: throw new WikiException("no provider class");
153: } catch (InstantiationException e) {
154: log.error("Unable to create provider class '" + classname
155: + "'", e);
156: throw new WikiException("faulty provider class");
157: } catch (IllegalAccessException e) {
158: log.error("Illegal access to provider class '" + classname
159: + "'", e);
160: throw new WikiException("illegal provider class");
161: } catch (NoRequiredPropertyException e) {
162: log.error(
163: "Provider did not found a property it was looking for: "
164: + e.getMessage(), e);
165: throw e; // Same exception works.
166: } catch (IOException e) {
167: log.error(
168: "An I/O exception occurred while trying to create a new page provider: "
169: + classname, e);
170: throw new WikiException("Unable to start page provider: "
171: + e.getMessage());
172: }
173:
174: }
175:
176: /**
177: * Returns the page provider currently in use.
178: *
179: * @return A WikiPageProvider instance.
180: */
181: public WikiPageProvider getProvider() {
182: return m_provider;
183: }
184:
185: /**
186: * Returns all pages in some random order. If you need just the page names,
187: * please see {@link ReferenceManager#findCreated()}, which is probably a lot
188: * faster. This method may cause repository access.
189: *
190: * @return A Collection of WikiPage objects.
191: * @throws ProviderException If the backend has problems.
192: */
193: public Collection getAllPages() throws ProviderException {
194: return m_provider.getAllPages();
195: }
196:
197: /**
198: * Fetches the page text from the repository. This method also does some sanity checks,
199: * like checking for the pageName validity, etc. Also, if the page repository has been
200: * modified externally, it is smart enough to handle such occurrences.
201: *
202: * @param pageName The name of the page to fetch.
203: * @param version The version to find
204: * @return The page content as a raw string
205: * @throws ProviderException If the backend has issues.
206: */
207: public String getPageText(String pageName, int version)
208: throws ProviderException {
209: if (pageName == null || pageName.length() == 0) {
210: throw new ProviderException("Illegal page name");
211: }
212:
213: String text = null;
214:
215: try {
216: text = m_provider.getPageText(pageName, version);
217: } catch (RepositoryModifiedException e) {
218: //
219: // This only occurs with the latest version.
220: //
221: log
222: .info("Repository has been modified externally while fetching page "
223: + pageName);
224:
225: //
226: // Empty the references and yay, it shall be recalculated
227: //
228: //WikiPage p = new WikiPage( pageName );
229: WikiPage p = m_provider.getPageInfo(pageName, version);
230:
231: m_engine.updateReferences(p);
232:
233: if (p != null) {
234: m_engine.getSearchManager().reindexPage(p);
235: text = m_provider.getPageText(pageName, version);
236: } else {
237: //
238: // Make sure that it no longer exists in internal data structures either.
239: //
240: WikiPage dummy = new WikiPage(m_engine, pageName);
241: m_engine.getSearchManager().pageRemoved(dummy);
242: m_engine.getReferenceManager().pageRemoved(dummy);
243: }
244: }
245:
246: return text;
247: }
248:
249: /**
250: * Returns the WikiEngine to which this PageManager belongs to.
251: *
252: * @return The WikiEngine object.
253: */
254: public WikiEngine getEngine() {
255: return m_engine;
256: }
257:
258: /**
259: * Puts the page text into the repository. Note that this method does NOT update
260: * JSPWiki internal data structures, and therefore you should always use WikiEngine.saveText()
261: *
262: * @param page Page to save
263: * @param content Wikimarkup to save
264: * @throws ProviderException If something goes wrong in the saving phase
265: */
266: public void putPageText(WikiPage page, String content)
267: throws ProviderException {
268: if (page == null || page.getName() == null
269: || page.getName().length() == 0) {
270: throw new ProviderException("Illegal page name");
271: }
272:
273: m_provider.putPageText(page, content);
274: }
275:
276: /**
277: * Locks page for editing. Note, however, that the PageManager
278: * will in no way prevent you from actually editing this page;
279: * the lock is just for information.
280: *
281: * @param page WikiPage to lock
282: * @param user Username to use for locking
283: * @return null, if page could not be locked.
284: */
285: public PageLock lockPage(WikiPage page, String user) {
286: PageLock lock = null;
287:
288: if (m_reaper == null) {
289: //
290: // Start the lock reaper lazily. We don't want to start it in
291: // the constructor, because starting threads in constructors
292: // is a bad idea when it comes to inheritance. Besides,
293: // laziness is a virtue.
294: //
295: m_reaper = new LockReaper(m_engine);
296: m_reaper.start();
297: }
298:
299: synchronized (m_pageLocks) {
300: fireEvent(WikiPageEvent.PAGE_LOCK, page.getName()); // prior to or after actual lock?
301:
302: lock = (PageLock) m_pageLocks.get(page.getName());
303:
304: if (lock == null) {
305: //
306: // Lock is available, so make a lock.
307: //
308: Date d = new Date();
309: lock = new PageLock(page, user, d, new Date(d.getTime()
310: + m_expiryTime * 60 * 1000L));
311:
312: m_pageLocks.put(page.getName(), lock);
313:
314: log.debug("Locked page " + page.getName() + " for "
315: + user);
316: } else {
317: log.debug("Page " + page.getName()
318: + " already locked by " + lock.getLocker());
319: lock = null; // Nothing to return
320: }
321: }
322:
323: return lock;
324: }
325:
326: /**
327: * Marks a page free to be written again. If there has not been a lock,
328: * will fail quietly.
329: *
330: * @param lock A lock acquired in lockPage(). Safe to be null.
331: */
332: public void unlockPage(PageLock lock) {
333: if (lock == null)
334: return;
335:
336: synchronized (m_pageLocks) {
337: m_pageLocks.remove(lock.getPage());
338:
339: log.debug("Unlocked page " + lock.getPage());
340: }
341:
342: fireEvent(WikiPageEvent.PAGE_UNLOCK, lock.getPage());
343: }
344:
345: /**
346: * Returns the current lock owner of a page. If the page is not
347: * locked, will return null.
348: *
349: * @param page The page to check the lock for
350: * @return Current lock, or null, if there is no lock
351: */
352: public PageLock getCurrentLock(WikiPage page) {
353: PageLock lock = null;
354:
355: synchronized (m_pageLocks) {
356: lock = (PageLock) m_pageLocks.get(page.getName());
357: }
358:
359: return lock;
360: }
361:
362: /**
363: * Returns a list of currently applicable locks. Note that by the time you get the list,
364: * the locks may have already expired, so use this only for informational purposes.
365: *
366: * @return List of PageLock objects, detailing the locks. If no locks exist, returns
367: * an empty list.
368: * @since 2.0.22.
369: */
370: public List getActiveLocks() {
371: ArrayList result = new ArrayList();
372:
373: synchronized (m_pageLocks) {
374: for (Iterator i = m_pageLocks.values().iterator(); i
375: .hasNext();) {
376: result.add(i.next());
377: }
378: }
379:
380: return result;
381: }
382:
383: /**
384: * Finds a WikiPage object describing a particular page and version.
385: *
386: * @param pageName The name of the page
387: * @param version A version number
388: * @return A WikiPage object, or null, if the page does not exist
389: * @throws ProviderException If there is something wrong with the page
390: * name or the repository
391: */
392: public WikiPage getPageInfo(String pageName, int version)
393: throws ProviderException {
394: if (pageName == null || pageName.length() == 0) {
395: throw new ProviderException("Illegal page name '"
396: + pageName + "'");
397: }
398:
399: WikiPage page = null;
400:
401: try {
402: page = m_provider.getPageInfo(pageName, version);
403: } catch (RepositoryModifiedException e) {
404: //
405: // This only occurs with the latest version.
406: //
407: log
408: .info("Repository has been modified externally while fetching info for "
409: + pageName);
410:
411: WikiPage p = new WikiPage(m_engine, pageName);
412:
413: m_engine.updateReferences(p);
414:
415: page = m_provider.getPageInfo(pageName, version);
416: }
417:
418: //
419: // Should update the metadata.
420: //
421: /*
422: if( page != null && !page.hasMetadata() )
423: {
424: WikiContext ctx = new WikiContext(m_engine,page);
425: m_engine.textToHTML( ctx, getPageText(pageName,version) );
426: }
427: */
428: return page;
429: }
430:
431: /**
432: * Gets a version history of page. Each element in the returned
433: * List is a WikiPage.
434: *
435: * @param pageName The name of the page to fetch history for
436: * @return If the page does not exist, returns null, otherwise a List
437: * of WikiPages.
438: * @throws ProviderException If the repository fails.
439: */
440: public List getVersionHistory(String pageName)
441: throws ProviderException {
442: if (pageExists(pageName)) {
443: return m_provider.getVersionHistory(pageName);
444: }
445:
446: return null;
447: }
448:
449: /**
450: * Returns a human-readable description of the current provider.
451: *
452: * @return A human-readable description.
453: */
454: public String getProviderDescription() {
455: return m_provider.getProviderInfo();
456: }
457:
458: /**
459: * Returns the total count of all pages in the repository. This
460: * method is equivalent of calling getAllPages().size(), but
461: * it swallows the ProviderException and returns -1 instead of
462: * any problems.
463: *
464: * @return The number of pages, or -1, if there is an error.
465: */
466: public int getTotalPageCount() {
467: try {
468: return m_provider.getAllPages().size();
469: } catch (ProviderException e) {
470: log.error("Unable to count pages: ", e);
471: return -1;
472: }
473: }
474:
475: /**
476: * Returns true, if the page exists (any version).
477: *
478: * @param pageName Name of the page.
479: * @return A boolean value describing the existence of a page
480: * @throws ProviderException If the backend fails or the name is illegal.
481: */
482: public boolean pageExists(String pageName) throws ProviderException {
483: if (pageName == null || pageName.length() == 0) {
484: throw new ProviderException("Illegal page name");
485: }
486:
487: return m_provider.pageExists(pageName);
488: }
489:
490: /**
491: * Checks for existence of a specific page and version.
492: *
493: * @since 2.3.29
494: * @param pageName Name of the page
495: * @param version The version to check
496: * @return <code>true</code> if the page exists, <code>false</code> otherwise
497: * @throws ProviderException If backend fails or name is illegal
498: */
499: public boolean pageExists(String pageName, int version)
500: throws ProviderException {
501: if (pageName == null || pageName.length() == 0) {
502: throw new ProviderException("Illegal page name");
503: }
504:
505: if (version == WikiProvider.LATEST_VERSION)
506: return pageExists(pageName);
507:
508: if (m_provider instanceof CachingProvider) {
509: return ((CachingProvider) m_provider).pageExists(pageName,
510: version);
511: }
512:
513: return m_provider.getPageInfo(pageName, version) != null;
514: }
515:
516: /**
517: * Deletes only a specific version of a WikiPage.
518: *
519: * @param page The page to delete.
520: * @throws ProviderException if the page fails
521: */
522: public void deleteVersion(WikiPage page) throws ProviderException {
523: m_provider.deleteVersion(page.getName(), page.getVersion());
524:
525: // FIXME: If this was the latest, reindex Lucene
526: // FIXME: Update RefMgr
527: }
528:
529: /**
530: * Deletes an entire page, all versions, all traces.
531: *
532: * @param page The WikiPage to delete
533: * @throws ProviderException If the repository operation fails
534: */
535: public void deletePage(WikiPage page) throws ProviderException {
536: fireEvent(WikiPageEvent.PAGE_DELETE_REQUEST, page.getName());
537:
538: m_provider.deletePage(page.getName());
539:
540: fireEvent(WikiPageEvent.PAGE_DELETED, page.getName());
541: }
542:
543: /**
544: * This is a simple reaper thread that runs roughly every minute
545: * or so (it's not really that important, as long as it runs),
546: * and removes all locks that have expired.
547: */
548: private class LockReaper extends WikiBackgroundThread {
549: public LockReaper(WikiEngine engine) {
550: super (engine, 60);
551: setName("JSPWiki Lock Reaper");
552: }
553:
554: public void backgroundTask() throws Exception {
555: synchronized (m_pageLocks) {
556: Collection entries = m_pageLocks.values();
557:
558: Date now = new Date();
559:
560: for (Iterator i = entries.iterator(); i.hasNext();) {
561: PageLock p = (PageLock) i.next();
562:
563: if (now.after(p.getExpiryTime())) {
564: i.remove();
565:
566: log.debug("Reaped lock: " + p.getPage()
567: + " by " + p.getLocker()
568: + ", acquired "
569: + p.getAcquisitionTime()
570: + ", and expired " + p.getExpiryTime());
571: }
572: }
573: }
574: }
575: }
576:
577: // workflow task inner classes....................................................
578:
579: /**
580: * Inner class that handles the page pre-save actions. If the proposed page
581: * text is the same as the current version, the {@link #execute()} method
582: * returns {@link com.ecyrd.jspwiki.workflow.Outcome#STEP_ABORT}. Any
583: * WikiExceptions thrown by page filters will be re-thrown, and the workflow
584: * will abort.
585: *
586: * @author Andrew Jaquith
587: */
588: public static class PreSaveWikiPageTask extends Task {
589: private final WikiContext m_context;
590: private final String m_proposedText;
591:
592: public PreSaveWikiPageTask(WikiContext context,
593: String proposedText) {
594: super (PRESAVE_TASK_MESSAGE_KEY);
595: m_context = context;
596: m_proposedText = proposedText;
597: }
598:
599: public Outcome execute() throws WikiException {
600: // Retrieve attributes
601: WikiEngine engine = m_context.getEngine();
602: Workflow workflow = getWorkflow();
603:
604: // Get the wiki page
605: WikiPage page = m_context.getPage();
606:
607: // Figure out who the author was. Prefer the author
608: // set programmatically; otherwise get from the
609: // current logged in user
610: if (page.getAuthor() == null) {
611: Principal wup = m_context.getCurrentUser();
612:
613: if (wup != null)
614: page.setAuthor(wup.getName());
615: }
616:
617: // Run the pre-save filters. If any exceptions, add error to list, abort, and redirect
618: String saveText;
619: try {
620: saveText = engine.getFilterManager()
621: .doPreSaveFiltering(m_context, m_proposedText);
622: } catch (FilterException e) {
623: throw e;
624: }
625:
626: // Stash the wiki context, old and new text as workflow attributes
627: workflow.setAttribute(PRESAVE_WIKI_CONTEXT, m_context);
628: workflow.setAttribute(FACT_PROPOSED_TEXT, saveText);
629: return Outcome.STEP_COMPLETE;
630: }
631: }
632:
633: /**
634: * Inner class that handles the actual page save and post-save actions. Instances
635: * of this class are assumed to have been added to an approval workflow via
636: * {@link com.ecyrd.jspwiki.workflow.WorkflowBuilder#buildApprovalWorkflow(Principal, String, Task, String, com.ecyrd.jspwiki.workflow.Fact[], Task, String)};
637: * they will not function correctly otherwise.
638: *
639: * @author Andrew Jaquith
640: */
641: public static class SaveWikiPageTask extends Task {
642: public SaveWikiPageTask() {
643: super (SAVE_TASK_MESSAGE_KEY);
644: }
645:
646: public Outcome execute() throws WikiException {
647: // Retrieve attributes
648: WikiContext context = (WikiContext) getWorkflow()
649: .getAttribute(PRESAVE_WIKI_CONTEXT);
650: String proposedText = (String) getWorkflow().getAttribute(
651: FACT_PROPOSED_TEXT);
652:
653: WikiEngine engine = context.getEngine();
654: WikiPage page = context.getPage();
655:
656: // Let the rest of the engine handle actual saving.
657: engine.getPageManager().putPageText(page, proposedText);
658:
659: // Refresh the context for post save filtering.
660: engine.getPage(page.getName());
661: engine.textToHTML(context, proposedText);
662: engine.getFilterManager().doPostSaveFiltering(context,
663: proposedText);
664:
665: return Outcome.STEP_COMPLETE;
666: }
667: }
668:
669: // events processing .......................................................
670:
671: /**
672: * Fires a WikiPageEvent of the provided type and page name
673: * to all registered listeners.
674: *
675: * @see com.ecyrd.jspwiki.event.WikiPageEvent
676: * @param type the event type to be fired
677: * @param pagename the wiki page name as a String
678: */
679: protected final void fireEvent(int type, String pagename) {
680: if (WikiEventManager.isListening(this )) {
681: WikiEventManager.fireEvent(this , new WikiPageEvent(
682: m_engine, type, pagename));
683: }
684: }
685:
686: /**
687: * {@inheritDoc}
688: */
689: public Collection modules() {
690: // TODO Auto-generated method stub
691: return null;
692: }
693:
694: /**
695: * Listens for {@link com.ecyrd.jspwiki.event.WikiSecurityEvent#PROFILE_NAME_CHANGED}
696: * events. If a user profile's name changes, each page ACL is inspected. If an entry contains
697: * a name that has changed, it is replaced with the new one. No events are emitted
698: * as a consequence of this method, because the page contents are still the same; it is
699: * only the representations of the names within the ACL that are changing.
700: *
701: * @param event The event
702: */
703: public void actionPerformed(WikiEvent event) {
704: if (!(event instanceof WikiSecurityEvent)) {
705: return;
706: }
707:
708: WikiSecurityEvent se = (WikiSecurityEvent) event;
709: if (se.getType() == WikiSecurityEvent.PROFILE_NAME_CHANGED) {
710: UserProfile[] profiles = (UserProfile[]) se.getTarget();
711: Principal[] oldPrincipals = new Principal[] {
712: new WikiPrincipal(profiles[0].getLoginName()),
713: new WikiPrincipal(profiles[0].getFullname()),
714: new WikiPrincipal(profiles[0].getWikiName()) };
715: Principal newPrincipal = new WikiPrincipal(profiles[1]
716: .getFullname());
717:
718: // Examine each page ACL
719: try {
720: int pagesChanged = 0;
721: Collection pages = getAllPages();
722: for (Iterator it = pages.iterator(); it.hasNext();) {
723: WikiPage page = (WikiPage) it.next();
724: boolean aclChanged = changeAcl(page, oldPrincipals,
725: newPrincipal);
726: if (aclChanged) {
727: // If the Acl needed changing, change it now
728: try {
729: m_engine.getAclManager().setPermissions(
730: page, page.getAcl());
731: } catch (WikiSecurityException e) {
732: log
733: .error("Could not change page ACL for page "
734: + page.getName()
735: + ": "
736: + e.getMessage());
737: }
738: pagesChanged++;
739: }
740: }
741: log.info("Profile name change for '"
742: + newPrincipal.toString() + "' caused "
743: + pagesChanged + " page ACLs to change also.");
744: } catch (ProviderException e) {
745: // Oooo! This is really bad...
746: log
747: .error("Could not change user name in Page ACLs because of Provider error:"
748: + e.getMessage());
749: }
750: }
751: }
752:
753: /**
754: * For a single wiki page, replaces all Acl entries matching a supplied array of Principals
755: * with a new Principal.
756: *
757: * @param page the wiki page whose Acl is to be modified
758: * @param oldPrincipals an array of Principals to replace; all AclEntry objects whose
759: * {@link AclEntry#getPrincipal()} method returns one of these Principals will be replaced
760: * @param newPrincipal the Principal that should receive the old Principals' permissions
761: * @return <code>true</code> if the Acl was actually changed; <code>false</code> otherwise
762: */
763: protected boolean changeAcl(WikiPage page,
764: Principal[] oldPrincipals, Principal newPrincipal) {
765: Acl acl = page.getAcl();
766: boolean pageChanged = false;
767: if (acl != null) {
768: Enumeration entries = acl.entries();
769: Collection entriesToAdd = new ArrayList();
770: Collection entriesToRemove = new ArrayList();
771: while (entries.hasMoreElements()) {
772: AclEntry entry = (AclEntry) entries.nextElement();
773: if (ArrayUtils.contains(oldPrincipals, entry
774: .getPrincipal())) {
775: // Create new entry
776: AclEntry newEntry = new AclEntryImpl();
777: newEntry.setPrincipal(newPrincipal);
778: Enumeration permissions = entry.permissions();
779: while (permissions.hasMoreElements()) {
780: Permission permission = (Permission) permissions
781: .nextElement();
782: newEntry.addPermission(permission);
783: }
784: pageChanged = true;
785: entriesToRemove.add(entry);
786: entriesToAdd.add(newEntry);
787: }
788: }
789: for (Iterator ix = entriesToRemove.iterator(); ix.hasNext();) {
790: AclEntry entry = (AclEntry) ix.next();
791: acl.removeEntry(entry);
792: }
793: for (Iterator ix = entriesToAdd.iterator(); ix.hasNext();) {
794: AclEntry entry = (AclEntry) ix.next();
795: acl.addEntry(entry);
796: }
797: }
798: return pageChanged;
799: }
800:
801: }
|