| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| package org.apache.wiki.pages; |
| |
| import org.apache.commons.lang3.ArrayUtils; |
| import org.apache.log4j.Logger; |
| import org.apache.wiki.WikiBackgroundThread; |
| import org.apache.wiki.api.core.Acl; |
| import org.apache.wiki.api.core.AclEntry; |
| import org.apache.wiki.api.core.Attachment; |
| import org.apache.wiki.api.core.Context; |
| import org.apache.wiki.api.core.Engine; |
| import org.apache.wiki.api.core.Page; |
| import org.apache.wiki.api.exceptions.NoRequiredPropertyException; |
| import org.apache.wiki.api.exceptions.ProviderException; |
| import org.apache.wiki.api.exceptions.WikiException; |
| import org.apache.wiki.api.providers.PageProvider; |
| import org.apache.wiki.api.providers.WikiProvider; |
| import org.apache.wiki.api.spi.Wiki; |
| import org.apache.wiki.attachment.AttachmentManager; |
| import org.apache.wiki.auth.WikiPrincipal; |
| import org.apache.wiki.auth.WikiSecurityException; |
| import org.apache.wiki.auth.acl.AclManager; |
| import org.apache.wiki.auth.user.UserProfile; |
| import org.apache.wiki.diff.DifferenceManager; |
| import org.apache.wiki.event.WikiEvent; |
| import org.apache.wiki.event.WikiEventManager; |
| import org.apache.wiki.event.WikiPageEvent; |
| import org.apache.wiki.event.WikiSecurityEvent; |
| import org.apache.wiki.providers.RepositoryModifiedException; |
| import org.apache.wiki.references.ReferenceManager; |
| import org.apache.wiki.search.SearchManager; |
| import org.apache.wiki.tasks.TasksManager; |
| import org.apache.wiki.ui.CommandResolver; |
| import org.apache.wiki.util.ClassUtil; |
| import org.apache.wiki.util.TextUtil; |
| import org.apache.wiki.workflow.Decision; |
| import org.apache.wiki.workflow.DecisionRequiredException; |
| import org.apache.wiki.workflow.Fact; |
| import org.apache.wiki.workflow.Step; |
| import org.apache.wiki.workflow.Workflow; |
| import org.apache.wiki.workflow.WorkflowBuilder; |
| import org.apache.wiki.workflow.WorkflowManager; |
| |
| import java.io.IOException; |
| import java.security.Permission; |
| import java.security.Principal; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| |
| /** |
| * Manages the WikiPages. This class functions as an unified interface towards the page providers. It handles initialization |
| * and management of the providers, and provides utility methods for accessing the contents. |
| * <p/> |
| * Saving a page is a two-stage Task; first the pre-save operations and then the actual save. See the descriptions of the tasks |
| * for further information. |
| * |
| * @since 2.0 |
| */ |
| public class DefaultPageManager implements PageManager { |
| |
| private static final Logger LOG = Logger.getLogger( DefaultPageManager.class ); |
| |
| private PageProvider m_provider; |
| |
| private Engine m_engine; |
| |
| protected ConcurrentHashMap< String, PageLock > m_pageLocks = new ConcurrentHashMap<>(); |
| |
| private int m_expiryTime; |
| |
| private LockReaper m_reaper = null; |
| |
| private PageSorter pageSorter = new PageSorter(); |
| |
| /** |
| * Creates a new PageManager. |
| * |
| * @param engine Engine instance |
| * @param props Properties to use for initialization |
| * @throws NoSuchElementException {@value #PROP_PAGEPROVIDER} property not found on Engine properties |
| * @throws WikiException If anything goes wrong, you get this. |
| */ |
| public DefaultPageManager(final Engine engine, final Properties props) throws NoSuchElementException, WikiException { |
| m_engine = engine; |
| final String classname; |
| final boolean useCache = "true".equals( props.getProperty( PROP_USECACHE ) ); |
| m_expiryTime = TextUtil.parseIntParameter( props.getProperty( PROP_LOCKEXPIRY ), 60 ); |
| |
| // If user wants to use a cache, then we'll use the CachingProvider. |
| if( useCache ) { |
| classname = "org.apache.wiki.providers.CachingProvider"; |
| } else { |
| classname = TextUtil.getRequiredProperty( props, PROP_PAGEPROVIDER ); |
| } |
| |
| pageSorter.initialize( props ); |
| |
| try { |
| LOG.debug("Page provider class: '" + classname + "'"); |
| final Class<?> providerclass = ClassUtil.findClass("org.apache.wiki.providers", classname); |
| m_provider = ( PageProvider ) providerclass.newInstance(); |
| |
| LOG.debug("Initializing page provider class " + m_provider); |
| m_provider.initialize(m_engine, props); |
| } catch (final ClassNotFoundException e) { |
| LOG.error("Unable to locate provider class '" + classname + "' (" + e.getMessage() + ")", e); |
| throw new WikiException("No provider class. (" + e.getMessage() + ")", e); |
| } catch (final InstantiationException e) { |
| LOG.error("Unable to create provider class '" + classname + "' (" + e.getMessage() + ")", e); |
| throw new WikiException("Faulty provider class. (" + e.getMessage() + ")", e); |
| } catch (final IllegalAccessException e) { |
| LOG.error("Illegal access to provider class '" + classname + "' (" + e.getMessage() + ")", e); |
| throw new WikiException("Illegal provider class. (" + e.getMessage() + ")", e); |
| } catch (final NoRequiredPropertyException e) { |
| LOG.error("Provider did not found a property it was looking for: " + e.getMessage(), e); |
| throw e; // Same exception works. |
| } catch (final IOException e) { |
| LOG.error("An I/O exception occurred while trying to create a new page provider: " + classname, e); |
| throw new WikiException("Unable to start page provider: " + e.getMessage(), e); |
| } |
| |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getProvider() |
| */ |
| @Override |
| public PageProvider getProvider() { |
| return m_provider; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getAllPages() |
| */ |
| @Override |
| public Collection< Page > getAllPages() throws ProviderException { |
| return m_provider.getAllPages(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getPageText(java.lang.String, int) |
| */ |
| @Override |
| public String getPageText( final String pageName, final int version ) throws ProviderException { |
| if (pageName == null || pageName.length() == 0) { |
| throw new ProviderException( "Illegal page name" ); |
| } |
| String text; |
| |
| try { |
| text = m_provider.getPageText( pageName, version ); |
| } catch ( final RepositoryModifiedException e ) { |
| // This only occurs with the latest version. |
| LOG.info( "Repository has been modified externally while fetching page " + pageName ); |
| |
| // Empty the references and yay, it shall be recalculated |
| final Page p = m_provider.getPageInfo( pageName, version ); |
| |
| m_engine.getManager( ReferenceManager.class ).updateReferences( p ); |
| m_engine.getManager( SearchManager.class ).reindexPage( p ); |
| text = m_provider.getPageText( pageName, version ); |
| } |
| |
| return text; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getPureText(String, int) |
| */ |
| @Override |
| public String getPureText( final String page, final int version ) { |
| String result = null; |
| try { |
| result = getPageText( page, version ); |
| } catch( final ProviderException e ) { |
| LOG.error( "ProviderException getPureText for page " + page + " [version " + version + "]", e ); |
| } finally { |
| if( result == null ) { |
| result = ""; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getText(String, int) |
| */ |
| @Override |
| public String getText( final String page, final int version ) { |
| final String result = getPureText( page, version ); |
| return TextUtil.replaceEntities( result ); |
| } |
| |
| @Override |
| public void saveText( final Context context, final String text ) throws WikiException { |
| // Check if page data actually changed; bail if not |
| final Page page = context.getPage(); |
| final String oldText = getPureText( page ); |
| final String proposedText = TextUtil.normalizePostData( text ); |
| if ( oldText != null && oldText.equals( proposedText ) ) { |
| return; |
| } |
| |
| // Check if creation of empty pages is allowed; bail if not |
| final boolean allowEmpty = TextUtil.getBooleanProperty( m_engine.getWikiProperties(), |
| Engine.PROP_ALLOW_CREATION_OF_EMPTY_PAGES, |
| false ); |
| if ( !allowEmpty && !wikiPageExists( page ) && text.trim().equals( "" ) ) { |
| return; |
| } |
| |
| // Create approval workflow for page save; add the diffed, proposed and old text versions as |
| // Facts for the approver (if approval is required). If submitter is authenticated, any reject |
| // messages will appear in his/her workflow inbox. |
| final WorkflowBuilder builder = WorkflowBuilder.getBuilder( m_engine ); |
| final Principal submitter = context.getCurrentUser(); |
| final Step prepTask = m_engine.getManager( TasksManager.class ).buildPreSaveWikiPageTask( context, proposedText ); |
| final Step completionTask = m_engine.getManager( TasksManager.class ).buildSaveWikiPageTask(); |
| final String diffText = m_engine.getManager( DifferenceManager.class ).makeDiff( context, oldText, proposedText ); |
| final boolean isAuthenticated = context.getWikiSession().isAuthenticated(); |
| final Fact[] facts = new Fact[ 5 ]; |
| facts[ 0 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_PAGE_NAME, page.getName() ); |
| facts[ 1 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_DIFF_TEXT, diffText ); |
| facts[ 2 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_PROPOSED_TEXT, proposedText ); |
| facts[ 3 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_CURRENT_TEXT, oldText); |
| facts[ 4 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_IS_AUTHENTICATED, isAuthenticated ); |
| final String rejectKey = isAuthenticated ? WorkflowManager.WF_WP_SAVE_REJECT_MESSAGE_KEY : null; |
| final Workflow workflow = builder.buildApprovalWorkflow( submitter, |
| WorkflowManager.WF_WP_SAVE_APPROVER, |
| prepTask, |
| WorkflowManager.WF_WP_SAVE_DECISION_MESSAGE_KEY, |
| facts, |
| completionTask, |
| rejectKey ); |
| m_engine.getManager( WorkflowManager.class ).start( workflow ); |
| |
| // Let callers know if the page-save requires approval |
| if ( workflow.getCurrentStep() instanceof Decision ) { |
| throw new DecisionRequiredException( "The page contents must be approved before they become active." ); |
| } |
| } |
| |
| /** |
| * Returns the Engine to which this PageManager belongs to. |
| * |
| * @return The Engine object. |
| */ |
| protected Engine getEngine() { |
| return m_engine; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#putPageText(org.apache.wiki.api.core.Page, java.lang.String) |
| */ |
| @Override |
| public void putPageText( final Page page, final String content ) throws ProviderException { |
| if (page == null || page.getName() == null || page.getName().length() == 0) { |
| throw new ProviderException("Illegal page name"); |
| } |
| |
| m_provider.putPageText(page, content); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#lockPage(org.apache.wiki.api.core.Page, java.lang.String) |
| */ |
| @Override |
| public PageLock lockPage( final Page page, final String user ) { |
| if( m_reaper == null ) { |
| // Start the lock reaper lazily. We don't want to start it in the constructor, because starting threads in constructors |
| // is a bad idea when it comes to inheritance. Besides, laziness is a virtue. |
| m_reaper = new LockReaper( m_engine ); |
| m_reaper.start(); |
| } |
| |
| fireEvent( WikiPageEvent.PAGE_LOCK, page.getName() ); // prior to or after actual lock? |
| PageLock lock = m_pageLocks.get( page.getName() ); |
| |
| if( lock == null ) { |
| // |
| // Lock is available, so make a lock. |
| // |
| final Date d = new Date(); |
| lock = new PageLock( page, user, d, new Date( d.getTime() + m_expiryTime * 60 * 1000L ) ); |
| m_pageLocks.put( page.getName(), lock ); |
| LOG.debug( "Locked page " + page.getName() + " for " + user ); |
| } else { |
| LOG.debug( "Page " + page.getName() + " already locked by " + lock.getLocker() ); |
| lock = null; // Nothing to return |
| } |
| |
| return lock; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#unlockPage(org.apache.wiki.pages.PageLock) |
| */ |
| @Override |
| public void unlockPage( final PageLock lock ) { |
| if (lock == null) { |
| return; |
| } |
| |
| m_pageLocks.remove( lock.getPage() ); |
| LOG.debug( "Unlocked page " + lock.getPage() ); |
| |
| fireEvent( WikiPageEvent.PAGE_UNLOCK, lock.getPage() ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getCurrentLock(org.apache.wiki.api.core.Page) |
| */ |
| @Override |
| public PageLock getCurrentLock( final Page page ) { |
| return m_pageLocks.get( page.getName() ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getActiveLocks() |
| */ |
| @Override |
| public List< PageLock > getActiveLocks() { |
| return new ArrayList<>( m_pageLocks.values() ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getPage(java.lang.String) |
| */ |
| @Override |
| public Page getPage( final String pagereq ) { |
| return getPage( pagereq, PageProvider.LATEST_VERSION ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getPage(java.lang.String, int) |
| */ |
| @Override |
| public Page getPage( final String pagereq, final int version ) { |
| try { |
| Page p = getPageInfo( pagereq, version ); |
| if( p == null ) { |
| p = m_engine.getManager( AttachmentManager.class ).getAttachmentInfo( null, pagereq ); |
| } |
| |
| return p; |
| } catch( final ProviderException e ) { |
| LOG.error( "Unable to fetch page info for " + pagereq + " [version " + version + "]", e ); |
| return null; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getPageInfo(java.lang.String, int) |
| */ |
| @Override |
| public Page getPageInfo( final String pageName, final int version) throws ProviderException { |
| if( pageName == null || pageName.length() == 0 ) { |
| throw new ProviderException( "Illegal page name '" + pageName + "'" ); |
| } |
| |
| Page page; |
| |
| try { |
| page = m_provider.getPageInfo( pageName, version ); |
| } catch( final RepositoryModifiedException e ) { |
| // This only occurs with the latest version. |
| LOG.info( "Repository has been modified externally while fetching info for " + pageName ); |
| page = m_provider.getPageInfo( pageName, version ); |
| if( page != null ) { |
| m_engine.getManager( ReferenceManager.class ).updateReferences( page ); |
| } else { |
| m_engine.getManager( ReferenceManager.class ).pageRemoved( Wiki.contents().page( m_engine, pageName ) ); |
| } |
| } |
| |
| return page; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getVersionHistory(java.lang.String) |
| */ |
| @Override @SuppressWarnings( "unchecked" ) |
| public < T extends Page > List< T > getVersionHistory( final String pageName ) { |
| List< T > c = null; |
| |
| try { |
| if( pageExists( pageName ) ) { |
| c = ( List< T > )m_provider.getVersionHistory( pageName ); |
| } |
| |
| if( c == null ) { |
| c = ( List< T > )m_engine.getManager( AttachmentManager.class ).getVersionHistory( pageName ); |
| } |
| } catch( final ProviderException e ) { |
| LOG.error( "ProviderException requesting version history for " + pageName, e ); |
| } |
| |
| return c; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getCurrentProvider() |
| */ |
| @Override |
| public String getCurrentProvider() { |
| return getProvider().getClass().getName(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.apache.wiki.pages.PageManager#getProviderDescription() |
| */ |
| @Override |
| public String getProviderDescription() { |
| return m_provider.getProviderInfo(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getTotalPageCount() |
| */ |
| @Override |
| public int getTotalPageCount() { |
| try { |
| return m_provider.getAllPages().size(); |
| } catch( final ProviderException e ) { |
| LOG.error( "Unable to count pages: ", e ); |
| return -1; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getRecentChanges() |
| */ |
| @Override |
| public Set< Page > getRecentChanges() { |
| try { |
| final TreeSet< Page > sortedPages = new TreeSet<>( new PageTimeComparator() ); |
| sortedPages.addAll( getAllPages() ); |
| sortedPages.addAll( m_engine.getManager( AttachmentManager.class ).getAllAttachments() ); |
| |
| return sortedPages; |
| } catch( final ProviderException e ) { |
| LOG.error( "Unable to fetch all pages: ", e ); |
| return Collections.emptySet(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#pageExists(java.lang.String) |
| */ |
| @Override |
| public boolean pageExists( final String pageName ) throws ProviderException { |
| if (pageName == null || pageName.length() == 0) { |
| throw new ProviderException("Illegal page name"); |
| } |
| |
| return m_provider.pageExists(pageName); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#pageExists(java.lang.String, int) |
| */ |
| @Override |
| public boolean pageExists( final String pageName, final int version ) throws ProviderException { |
| if( pageName == null || pageName.length() == 0 ) { |
| throw new ProviderException( "Illegal page name" ); |
| } |
| |
| if( version == WikiProvider.LATEST_VERSION ) { |
| return pageExists( pageName ); |
| } |
| |
| return m_provider.pageExists( pageName, version ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#wikiPageExists(java.lang.String) |
| */ |
| @Override |
| public boolean wikiPageExists( final String page ) { |
| if( m_engine.getManager( CommandResolver.class ).getSpecialPageReference( page ) != null ) { |
| return true; |
| } |
| |
| Attachment att = null; |
| try { |
| if( m_engine.getFinalPageName( page ) != null ) { |
| return true; |
| } |
| |
| att = m_engine.getManager( AttachmentManager.class ).getAttachmentInfo( null, page ); |
| } catch( final ProviderException e ) { |
| LOG.debug( "pageExists() failed to find attachments", e ); |
| } |
| |
| return att != null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#wikiPageExists(java.lang.String, int) |
| */ |
| @Override |
| public boolean wikiPageExists( final String page, final int version ) throws ProviderException { |
| if( m_engine.getManager( CommandResolver.class ).getSpecialPageReference( page ) != null ) { |
| return true; |
| } |
| |
| boolean isThere = false; |
| final String finalName = m_engine.getFinalPageName( page ); |
| if( finalName != null ) { |
| isThere = pageExists( finalName, version ); |
| } |
| |
| if( !isThere ) { |
| // Go check if such an attachment exists. |
| try { |
| isThere = m_engine.getManager( AttachmentManager.class ).getAttachmentInfo( null, page, version ) != null; |
| } catch( final ProviderException e ) { |
| LOG.debug( "wikiPageExists() failed to find attachments", e ); |
| } |
| } |
| |
| return isThere; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#deleteVersion(org.apache.wiki.api.core.Page) |
| */ |
| @Override |
| public void deleteVersion( final Page page ) throws ProviderException { |
| if( page instanceof Attachment ) { |
| m_engine.getManager( AttachmentManager.class ).deleteVersion( ( Attachment )page ); |
| } else { |
| m_provider.deleteVersion( page.getName(), page.getVersion() ); |
| // FIXME: If this was the latest, reindex Lucene, update RefMgr |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#deletePage(java.lang.String) |
| */ |
| @Override |
| public void deletePage( final String pageName ) throws ProviderException { |
| final Page p = getPage( pageName ); |
| if( p != null ) { |
| if( p instanceof Attachment ) { |
| m_engine.getManager( AttachmentManager.class ).deleteAttachment( ( Attachment )p ); |
| } else { |
| final Collection< String > refTo = m_engine.getManager( ReferenceManager.class ).findRefersTo( pageName ); |
| // May return null, if the page does not exist or has not been indexed yet. |
| |
| if( m_engine.getManager( AttachmentManager.class ).hasAttachments( p ) ) { |
| final List< Attachment > attachments = m_engine.getManager( AttachmentManager.class ).listAttachments( p ); |
| for( final Attachment attachment : attachments ) { |
| if( refTo != null ) { |
| refTo.remove( attachment.getName() ); |
| } |
| |
| m_engine.getManager( AttachmentManager.class ).deleteAttachment( attachment ); |
| } |
| } |
| deletePage( p ); |
| fireEvent( WikiPageEvent.PAGE_DELETED, pageName ); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#deletePage(org.apache.wiki.api.core.Page) |
| */ |
| @Override |
| public void deletePage( final Page page ) throws ProviderException { |
| fireEvent( WikiPageEvent.PAGE_DELETE_REQUEST, page.getName() ); |
| m_provider.deletePage( page.getName() ); |
| fireEvent( WikiPageEvent.PAGE_DELETED, page.getName() ); |
| } |
| |
| /** |
| * This is a simple reaper thread that runs roughly every minute |
| * or so (it's not really that important, as long as it runs), |
| * and removes all locks that have expired. |
| */ |
| private class LockReaper extends WikiBackgroundThread { |
| /** |
| * Create a LockReaper for a given engine. |
| * |
| * @param engine Engine to own this thread. |
| */ |
| public LockReaper( final Engine engine) { |
| super( engine, 60 ); |
| setName( "JSPWiki Lock Reaper" ); |
| } |
| |
| @Override |
| public void backgroundTask() { |
| final Collection< PageLock > entries = m_pageLocks.values(); |
| for( final Iterator<PageLock> i = entries.iterator(); i.hasNext(); ) { |
| final PageLock p = i.next(); |
| |
| if ( p.isExpired() ) { |
| i.remove(); |
| |
| LOG.debug( "Reaped lock: " + p.getPage() + |
| " by " + p.getLocker() + |
| ", acquired " + p.getAcquisitionTime() + |
| ", and expired " + p.getExpiryTime() ); |
| } |
| } |
| } |
| } |
| |
| // events processing ....................................................... |
| |
| /** |
| * Fires a WikiPageEvent of the provided type and page name |
| * to all registered listeners. |
| * |
| * @param type the event type to be fired |
| * @param pagename the wiki page name as a String |
| * @see org.apache.wiki.event.WikiPageEvent |
| */ |
| protected final void fireEvent( final int type, final String pagename ) { |
| if( WikiEventManager.isListening( this ) ) { |
| WikiEventManager.fireEvent( this, new WikiPageEvent( m_engine, type, pagename ) ); |
| } |
| } |
| |
| /** |
| * Listens for {@link org.apache.wiki.event.WikiSecurityEvent#PROFILE_NAME_CHANGED} |
| * events. If a user profile's name changes, each page ACL is inspected. If an entry contains |
| * a name that has changed, it is replaced with the new one. No events are emitted |
| * as a consequence of this method, because the page contents are still the same; it is |
| * only the representations of the names within the ACL that are changing. |
| * |
| * @param event The event |
| */ |
| @Override |
| public void actionPerformed( final WikiEvent event ) { |
| if( !( event instanceof WikiSecurityEvent ) ) { |
| return; |
| } |
| |
| final WikiSecurityEvent se = ( WikiSecurityEvent ) event; |
| if( se.getType() == WikiSecurityEvent.PROFILE_NAME_CHANGED ) { |
| final UserProfile[] profiles = (UserProfile[]) se.getTarget(); |
| final Principal[] oldPrincipals = new Principal[] { new WikiPrincipal( profiles[ 0 ].getLoginName() ), |
| new WikiPrincipal( profiles[ 0 ].getFullname()), |
| new WikiPrincipal( profiles[ 0 ].getWikiName() ) }; |
| final Principal newPrincipal = new WikiPrincipal( profiles[ 1 ].getFullname() ); |
| |
| // Examine each page ACL |
| try { |
| int pagesChanged = 0; |
| final Collection< Page > pages = getAllPages(); |
| for( final Page page : pages ) { |
| final boolean aclChanged = changeAcl( page, oldPrincipals, newPrincipal ); |
| if( aclChanged ) { |
| // If the Acl needed changing, change it now |
| try { |
| m_engine.getManager( AclManager.class ).setPermissions( page, page.getAcl() ); |
| } catch( final WikiSecurityException e ) { |
| LOG.error("Could not change page ACL for page " + page.getName() + ": " + e.getMessage(), e); |
| } |
| pagesChanged++; |
| } |
| } |
| LOG.info( "Profile name change for '" + newPrincipal.toString() + "' caused " + pagesChanged + " page ACLs to change also." ); |
| } catch( final ProviderException e ) { |
| // Oooo! This is really bad... |
| LOG.error( "Could not change user name in Page ACLs because of Provider error:" + e.getMessage(), e ); |
| } |
| } |
| } |
| |
| /** |
| * For a single wiki page, replaces all Acl entries matching a supplied array of Principals with a new Principal. |
| * |
| * @param page the wiki page whose Acl is to be modified |
| * @param oldPrincipals an array of Principals to replace; all AclEntry objects whose {@link AclEntry#getPrincipal()} method returns |
| * one of these Principals will be replaced |
| * @param newPrincipal the Principal that should receive the old Principals' permissions |
| * @return <code>true</code> if the Acl was actually changed; <code>false</code> otherwise |
| */ |
| protected boolean changeAcl( final Page page, final Principal[] oldPrincipals, final Principal newPrincipal ) { |
| final Acl acl = page.getAcl(); |
| boolean pageChanged = false; |
| if( acl != null ) { |
| final Enumeration< AclEntry > entries = acl.aclEntries(); |
| final Collection< AclEntry > entriesToAdd = new ArrayList<>(); |
| final Collection< AclEntry > entriesToRemove = new ArrayList<>(); |
| while( entries.hasMoreElements() ) { |
| final AclEntry entry = entries.nextElement(); |
| if( ArrayUtils.contains( oldPrincipals, entry.getPrincipal() ) ) { |
| // Create new entry |
| final AclEntry newEntry = Wiki.acls().entry(); |
| newEntry.setPrincipal( newPrincipal ); |
| final Enumeration< Permission > permissions = entry.permissions(); |
| while( permissions.hasMoreElements() ) { |
| final Permission permission = permissions.nextElement(); |
| newEntry.addPermission( permission ); |
| } |
| pageChanged = true; |
| entriesToRemove.add( entry ); |
| entriesToAdd.add( newEntry ); |
| } |
| } |
| for( final AclEntry entry : entriesToRemove ) { |
| acl.removeEntry( entry ); |
| } |
| for( final AclEntry entry : entriesToAdd ) { |
| acl.addEntry( entry ); |
| } |
| } |
| return pageChanged; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @see org.apache.wiki.pages.PageManager#getPageSorter() |
| */ |
| @Override |
| public PageSorter getPageSorter() { |
| return pageSorter; |
| } |
| |
| } |