001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki.pages; 020 021import org.apache.commons.lang3.ArrayUtils; 022import org.apache.logging.log4j.LogManager; 023import org.apache.logging.log4j.Logger; 024import org.apache.wiki.WikiBackgroundThread; 025import org.apache.wiki.api.core.Acl; 026import org.apache.wiki.api.core.AclEntry; 027import org.apache.wiki.api.core.Attachment; 028import org.apache.wiki.api.core.Context; 029import org.apache.wiki.api.core.Engine; 030import org.apache.wiki.api.core.Page; 031import org.apache.wiki.api.exceptions.NoRequiredPropertyException; 032import org.apache.wiki.api.exceptions.ProviderException; 033import org.apache.wiki.api.exceptions.WikiException; 034import org.apache.wiki.api.providers.PageProvider; 035import org.apache.wiki.api.providers.WikiProvider; 036import org.apache.wiki.api.spi.Wiki; 037import org.apache.wiki.attachment.AttachmentManager; 038import org.apache.wiki.auth.WikiPrincipal; 039import org.apache.wiki.auth.WikiSecurityException; 040import org.apache.wiki.auth.acl.AclManager; 041import org.apache.wiki.auth.user.UserProfile; 042import org.apache.wiki.diff.DifferenceManager; 043import org.apache.wiki.event.WikiEvent; 044import org.apache.wiki.event.WikiEventManager; 045import org.apache.wiki.event.WikiPageEvent; 046import org.apache.wiki.event.WikiSecurityEvent; 047import org.apache.wiki.providers.RepositoryModifiedException; 048import org.apache.wiki.references.ReferenceManager; 049import org.apache.wiki.tasks.TasksManager; 050import org.apache.wiki.ui.CommandResolver; 051import org.apache.wiki.util.ClassUtil; 052import org.apache.wiki.util.TextUtil; 053import org.apache.wiki.workflow.Decision; 054import org.apache.wiki.workflow.DecisionRequiredException; 055import org.apache.wiki.workflow.Fact; 056import org.apache.wiki.workflow.Step; 057import org.apache.wiki.workflow.Workflow; 058import org.apache.wiki.workflow.WorkflowBuilder; 059import org.apache.wiki.workflow.WorkflowManager; 060 061import java.io.IOException; 062import java.security.Permission; 063import java.security.Principal; 064import java.util.ArrayList; 065import java.util.Collection; 066import java.util.Collections; 067import java.util.Date; 068import java.util.Enumeration; 069import java.util.Iterator; 070import java.util.List; 071import java.util.NoSuchElementException; 072import java.util.Properties; 073import java.util.Set; 074import java.util.TreeSet; 075import java.util.concurrent.ConcurrentHashMap; 076 077 078/** 079 * Manages the WikiPages. This class functions as an unified interface towards the page providers. It handles initialization 080 * and management of the providers, and provides utility methods for accessing the contents. 081 * <p/> 082 * Saving a page is a two-stage Task; first the pre-save operations and then the actual save. See the descriptions of the tasks 083 * for further information. 084 * 085 * @since 2.0 086 */ 087public class DefaultPageManager implements PageManager { 088 089 private static final Logger LOG = LogManager.getLogger( DefaultPageManager.class ); 090 091 private final PageProvider m_provider; 092 093 private final Engine m_engine; 094 095 protected final ConcurrentHashMap< String, PageLock > m_pageLocks = new ConcurrentHashMap<>(); 096 097 private final int m_expiryTime; 098 099 private LockReaper m_reaper; 100 101 private final PageSorter pageSorter = new PageSorter(); 102 103 /** 104 * Creates a new PageManager. 105 * 106 * @param engine Engine instance 107 * @param props Properties to use for initialization 108 * @throws NoSuchElementException {@value #PROP_PAGEPROVIDER} property not found on Engine properties 109 * @throws WikiException If anything goes wrong, you get this. 110 */ 111 public DefaultPageManager(final Engine engine, final Properties props) throws NoSuchElementException, WikiException { 112 m_engine = engine; 113 final String classname; 114 final boolean useCache = "true".equals( props.getProperty( PROP_USECACHE ) ); 115 m_expiryTime = TextUtil.parseIntParameter( props.getProperty( PROP_LOCKEXPIRY ), 60 ); 116 117 // If user wants to use a cache, then we'll use the CachingProvider. 118 if( useCache ) { 119 classname = "org.apache.wiki.providers.CachingProvider"; 120 } else { 121 classname = TextUtil.getRequiredProperty( props, PROP_PAGEPROVIDER ); 122 } 123 124 pageSorter.initialize( props ); 125 126 try { 127 LOG.debug("Page provider class: '" + classname + "'"); 128 final Class<?> providerclass = ClassUtil.findClass("org.apache.wiki.providers", classname); 129 m_provider = ( PageProvider ) providerclass.newInstance(); 130 131 LOG.debug("Initializing page provider class " + m_provider); 132 m_provider.initialize(m_engine, props); 133 } catch (final ClassNotFoundException e) { 134 LOG.error("Unable to locate provider class '" + classname + "' (" + e.getMessage() + ")", e); 135 throw new WikiException("No provider class. (" + e.getMessage() + ")", e); 136 } catch (final InstantiationException e) { 137 LOG.error("Unable to create provider class '" + classname + "' (" + e.getMessage() + ")", e); 138 throw new WikiException("Faulty provider class. (" + e.getMessage() + ")", e); 139 } catch (final IllegalAccessException e) { 140 LOG.error("Illegal access to provider class '" + classname + "' (" + e.getMessage() + ")", e); 141 throw new WikiException("Illegal provider class. (" + e.getMessage() + ")", e); 142 } catch (final NoRequiredPropertyException e) { 143 LOG.error("Provider did not found a property it was looking for: " + e.getMessage(), e); 144 throw e; // Same exception works. 145 } catch (final IOException e) { 146 LOG.error("An I/O exception occurred while trying to create a new page provider: " + classname, e); 147 throw new WikiException("Unable to start page provider: " + e.getMessage(), e); 148 } 149 150 } 151 152 /** 153 * {@inheritDoc} 154 * @see org.apache.wiki.pages.PageManager#getProvider() 155 */ 156 @Override 157 public PageProvider getProvider() { 158 return m_provider; 159 } 160 161 /** 162 * {@inheritDoc} 163 * @see org.apache.wiki.pages.PageManager#getAllPages() 164 */ 165 @Override 166 public Collection< Page > getAllPages() throws ProviderException { 167 return m_provider.getAllPages(); 168 } 169 170 /** 171 * {@inheritDoc} 172 * @see org.apache.wiki.pages.PageManager#getPageText(java.lang.String, int) 173 */ 174 @Override 175 public String getPageText( final String pageName, final int version ) throws ProviderException { 176 if (pageName == null || pageName.isEmpty()) { 177 throw new ProviderException( "Illegal page name" ); 178 } 179 String text; 180 181 try { 182 text = m_provider.getPageText( pageName, version ); 183 } catch ( final RepositoryModifiedException e ) { 184 // This only occurs with the latest version. 185 LOG.info( "Repository has been modified externally while fetching page " + pageName ); 186 187 // Empty the references and yay, it shall be recalculated 188 final Page p = m_provider.getPageInfo( pageName, version ); 189 190 m_engine.getManager( ReferenceManager.class ).updateReferences( p ); 191 fireEvent( WikiPageEvent.PAGE_REINDEX, p.getName() ); 192 text = m_provider.getPageText( pageName, version ); 193 } 194 195 return text; 196 } 197 198 /** 199 * {@inheritDoc} 200 * @see org.apache.wiki.pages.PageManager#getPureText(String, int) 201 */ 202 @Override 203 public String getPureText( final String page, final int version ) { 204 String result = null; 205 try { 206 result = getPageText( page, version ); 207 } catch( final ProviderException e ) { 208 LOG.error( "ProviderException getPureText for page " + page + " [version " + version + "]", e ); 209 } finally { 210 if( result == null ) { 211 result = ""; 212 } 213 } 214 return result; 215 } 216 217 /** 218 * {@inheritDoc} 219 * @see org.apache.wiki.pages.PageManager#getText(String, int) 220 */ 221 @Override 222 public String getText( final String page, final int version ) { 223 final String result = getPureText( page, version ); 224 return TextUtil.replaceEntities( result ); 225 } 226 227 @Override 228 public void saveText( final Context context, final String text ) throws WikiException { 229 // Check if page data actually changed; bail if not 230 final Page page = context.getPage(); 231 final String oldText = getPureText( page ); 232 final String proposedText = TextUtil.normalizePostData( text ); 233 if ( oldText != null && oldText.equals( proposedText ) ) { 234 return; 235 } 236 237 // Check if creation of empty pages is allowed; bail if not 238 final boolean allowEmpty = TextUtil.getBooleanProperty( m_engine.getWikiProperties(), 239 Engine.PROP_ALLOW_CREATION_OF_EMPTY_PAGES, 240 false ); 241 if ( !allowEmpty && !wikiPageExists( page ) && text.trim().equals( "" ) ) { 242 return; 243 } 244 245 // Create approval workflow for page save; add the diffed, proposed and old text versions as 246 // Facts for the approver (if approval is required). If submitter is authenticated, any reject 247 // messages will appear in his/her workflow inbox. 248 final WorkflowBuilder builder = WorkflowBuilder.getBuilder( m_engine ); 249 final Principal submitter = context.getCurrentUser(); 250 final Step prepTask = m_engine.getManager( TasksManager.class ).buildPreSaveWikiPageTask( proposedText ); 251 final Step completionTask = m_engine.getManager( TasksManager.class ).buildSaveWikiPageTask(); 252 final String diffText = m_engine.getManager( DifferenceManager.class ).makeDiff( context, oldText, proposedText ); 253 final boolean isAuthenticated = context.getWikiSession().isAuthenticated(); 254 final Fact[] facts = new Fact[ 5 ]; 255 facts[ 0 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_PAGE_NAME, page.getName() ); 256 facts[ 1 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_DIFF_TEXT, diffText ); 257 facts[ 2 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_PROPOSED_TEXT, proposedText ); 258 facts[ 3 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_CURRENT_TEXT, oldText); 259 facts[ 4 ] = new Fact( WorkflowManager.WF_WP_SAVE_FACT_IS_AUTHENTICATED, isAuthenticated ); 260 final String rejectKey = isAuthenticated ? WorkflowManager.WF_WP_SAVE_REJECT_MESSAGE_KEY : null; 261 final Workflow workflow = builder.buildApprovalWorkflow( submitter, 262 WorkflowManager.WF_WP_SAVE_APPROVER, 263 prepTask, 264 WorkflowManager.WF_WP_SAVE_DECISION_MESSAGE_KEY, 265 facts, 266 completionTask, 267 rejectKey ); 268 workflow.start( context ); 269 270 // Let callers know if the page-save requires approval 271 if ( workflow.getCurrentStep() instanceof Decision ) { 272 throw new DecisionRequiredException( "The page contents must be approved before they become active." ); 273 } 274 } 275 276 /** 277 * Returns the Engine to which this PageManager belongs to. 278 * 279 * @return The Engine object. 280 */ 281 protected Engine getEngine() { 282 return m_engine; 283 } 284 285 /** 286 * {@inheritDoc} 287 * @see org.apache.wiki.pages.PageManager#putPageText(org.apache.wiki.api.core.Page, java.lang.String) 288 */ 289 @Override 290 public void putPageText( final Page page, final String content ) throws ProviderException { 291 if (page == null || page.getName() == null || page.getName().isEmpty()) { 292 throw new ProviderException("Illegal page name"); 293 } 294 295 m_provider.putPageText(page, content); 296 } 297 298 /** 299 * {@inheritDoc} 300 * @see org.apache.wiki.pages.PageManager#lockPage(org.apache.wiki.api.core.Page, java.lang.String) 301 */ 302 @Override 303 public PageLock lockPage( final Page page, final String user ) { 304 if( m_reaper == null ) { 305 // Start the lock reaper lazily. We don't want to start it in the constructor, because starting threads in constructors 306 // is a bad idea when it comes to inheritance. Besides, laziness is a virtue. 307 m_reaper = new LockReaper( m_engine ); 308 m_reaper.start(); 309 } 310 311 fireEvent( WikiPageEvent.PAGE_LOCK, page.getName() ); // prior to or after actual lock? 312 PageLock lock = m_pageLocks.get( page.getName() ); 313 314 if( lock == null ) { 315 // 316 // Lock is available, so make a lock. 317 // 318 final Date d = new Date(); 319 lock = new PageLock( page, user, d, new Date( d.getTime() + m_expiryTime * 60 * 1000L ) ); 320 m_pageLocks.put( page.getName(), lock ); 321 LOG.debug( "Locked page " + page.getName() + " for " + user ); 322 } else { 323 LOG.debug( "Page " + page.getName() + " already locked by " + lock.getLocker() ); 324 lock = null; // Nothing to return 325 } 326 327 return lock; 328 } 329 330 /** 331 * {@inheritDoc} 332 * @see org.apache.wiki.pages.PageManager#unlockPage(org.apache.wiki.pages.PageLock) 333 */ 334 @Override 335 public void unlockPage( final PageLock lock ) { 336 if (lock == null) { 337 return; 338 } 339 340 m_pageLocks.remove( lock.getPage() ); 341 LOG.debug( "Unlocked page " + lock.getPage() ); 342 343 fireEvent( WikiPageEvent.PAGE_UNLOCK, lock.getPage() ); 344 } 345 346 /** 347 * {@inheritDoc} 348 * @see org.apache.wiki.pages.PageManager#getCurrentLock(org.apache.wiki.api.core.Page) 349 */ 350 @Override 351 public PageLock getCurrentLock( final Page page ) { 352 return m_pageLocks.get( page.getName() ); 353 } 354 355 /** 356 * {@inheritDoc} 357 * @see org.apache.wiki.pages.PageManager#getActiveLocks() 358 */ 359 @Override 360 public List< PageLock > getActiveLocks() { 361 return new ArrayList<>( m_pageLocks.values() ); 362 } 363 364 /** 365 * {@inheritDoc} 366 * @see org.apache.wiki.pages.PageManager#getPage(java.lang.String) 367 */ 368 @Override 369 public Page getPage( final String pagereq ) { 370 return getPage( pagereq, PageProvider.LATEST_VERSION ); 371 } 372 373 /** 374 * {@inheritDoc} 375 * @see org.apache.wiki.pages.PageManager#getPage(java.lang.String, int) 376 */ 377 @Override 378 public Page getPage( final String pagereq, final int version ) { 379 try { 380 Page p = getPageInfo( pagereq, version ); 381 if( p == null ) { 382 p = m_engine.getManager( AttachmentManager.class ).getAttachmentInfo( null, pagereq ); 383 } 384 385 return p; 386 } catch( final ProviderException e ) { 387 LOG.error( "Unable to fetch page info for " + pagereq + " [version " + version + "]", e ); 388 return null; 389 } 390 } 391 392 /** 393 * {@inheritDoc} 394 * @see org.apache.wiki.pages.PageManager#getPageInfo(java.lang.String, int) 395 */ 396 @Override 397 public Page getPageInfo( final String pageName, final int version) throws ProviderException { 398 if( pageName == null || pageName.isEmpty() ) { 399 throw new ProviderException( "Illegal page name '" + pageName + "'" ); 400 } 401 402 Page page; 403 404 try { 405 page = m_provider.getPageInfo( pageName, version ); 406 } catch( final RepositoryModifiedException e ) { 407 // This only occurs with the latest version. 408 LOG.info( "Repository has been modified externally while fetching info for " + pageName ); 409 page = m_provider.getPageInfo( pageName, version ); 410 if( page != null ) { 411 m_engine.getManager( ReferenceManager.class ).updateReferences( page ); 412 } else { 413 m_engine.getManager( ReferenceManager.class ).pageRemoved( Wiki.contents().page( m_engine, pageName ) ); 414 } 415 } 416 417 return page; 418 } 419 420 /** 421 * {@inheritDoc} 422 * @see org.apache.wiki.pages.PageManager#getVersionHistory(java.lang.String) 423 */ 424 @Override @SuppressWarnings( "unchecked" ) 425 public < T extends Page > List< T > getVersionHistory( final String pageName ) { 426 List< T > c = null; 427 428 try { 429 if( pageExists( pageName ) ) { 430 c = ( List< T > )m_provider.getVersionHistory( pageName ); 431 } 432 433 if( c == null ) { 434 c = ( List< T > )m_engine.getManager( AttachmentManager.class ).getVersionHistory( pageName ); 435 } 436 } catch( final ProviderException e ) { 437 LOG.error( "ProviderException requesting version history for " + pageName, e ); 438 } 439 440 return c; 441 } 442 443 /** 444 * {@inheritDoc} 445 * @see org.apache.wiki.pages.PageManager#getCurrentProvider() 446 */ 447 @Override 448 public String getCurrentProvider() { 449 return getProvider().getClass().getName(); 450 } 451 452 /** 453 * {@inheritDoc} 454 * 455 * @see org.apache.wiki.pages.PageManager#getProviderDescription() 456 */ 457 @Override 458 public String getProviderDescription() { 459 return m_provider.getProviderInfo(); 460 } 461 462 /** 463 * {@inheritDoc} 464 * @see org.apache.wiki.pages.PageManager#getTotalPageCount() 465 */ 466 @Override 467 public int getTotalPageCount() { 468 try { 469 return m_provider.getAllPages().size(); 470 } catch( final ProviderException e ) { 471 LOG.error( "Unable to count pages: ", e ); 472 return -1; 473 } 474 } 475 476 /** 477 * {@inheritDoc} 478 * @see org.apache.wiki.pages.PageManager#getRecentChanges() 479 */ 480 @Override 481 public Set< Page > getRecentChanges() { 482 try { 483 final TreeSet< Page > sortedPages = new TreeSet<>( new PageTimeComparator() ); 484 sortedPages.addAll( getAllPages() ); 485 sortedPages.addAll( m_engine.getManager( AttachmentManager.class ).getAllAttachments() ); 486 487 return sortedPages; 488 } catch( final ProviderException e ) { 489 LOG.error( "Unable to fetch all pages: ", e ); 490 return Collections.emptySet(); 491 } 492 } 493 494 /** 495 * {@inheritDoc} 496 * @see org.apache.wiki.pages.PageManager#pageExists(java.lang.String) 497 */ 498 @Override 499 public boolean pageExists( final String pageName ) throws ProviderException { 500 if (pageName == null || pageName.isEmpty()) { 501 throw new ProviderException("Illegal page name"); 502 } 503 504 return m_provider.pageExists(pageName); 505 } 506 507 /** 508 * {@inheritDoc} 509 * @see org.apache.wiki.pages.PageManager#pageExists(java.lang.String, int) 510 */ 511 @Override 512 public boolean pageExists( final String pageName, final int version ) throws ProviderException { 513 if( pageName == null || pageName.isEmpty() ) { 514 throw new ProviderException( "Illegal page name" ); 515 } 516 517 if( version == WikiProvider.LATEST_VERSION ) { 518 return pageExists( pageName ); 519 } 520 521 return m_provider.pageExists( pageName, version ); 522 } 523 524 /** 525 * {@inheritDoc} 526 * @see org.apache.wiki.pages.PageManager#wikiPageExists(java.lang.String) 527 */ 528 @Override 529 public boolean wikiPageExists( final String page ) { 530 if( m_engine.getManager( CommandResolver.class ).getSpecialPageReference( page ) != null ) { 531 return true; 532 } 533 534 Attachment att = null; 535 try { 536 if( m_engine.getFinalPageName( page ) != null ) { 537 return true; 538 } 539 540 att = m_engine.getManager( AttachmentManager.class ).getAttachmentInfo( null, page ); 541 } catch( final ProviderException e ) { 542 LOG.debug( "pageExists() failed to find attachments", e ); 543 } 544 545 return att != null; 546 } 547 548 /** 549 * {@inheritDoc} 550 * @see org.apache.wiki.pages.PageManager#wikiPageExists(java.lang.String, int) 551 */ 552 @Override 553 public boolean wikiPageExists( final String page, final int version ) throws ProviderException { 554 if( m_engine.getManager( CommandResolver.class ).getSpecialPageReference( page ) != null ) { 555 return true; 556 } 557 558 boolean isThere = false; 559 final String finalName = m_engine.getFinalPageName( page ); 560 if( finalName != null ) { 561 isThere = pageExists( finalName, version ); 562 } 563 564 if( !isThere ) { 565 // Go check if such an attachment exists. 566 try { 567 isThere = m_engine.getManager( AttachmentManager.class ).getAttachmentInfo( null, page, version ) != null; 568 } catch( final ProviderException e ) { 569 LOG.debug( "wikiPageExists() failed to find attachments", e ); 570 } 571 } 572 573 return isThere; 574 } 575 576 /** 577 * {@inheritDoc} 578 * @see org.apache.wiki.pages.PageManager#deleteVersion(org.apache.wiki.api.core.Page) 579 */ 580 @Override 581 public void deleteVersion( final Page page ) throws ProviderException { 582 if( page instanceof Attachment ) { 583 m_engine.getManager( AttachmentManager.class ).deleteVersion( ( Attachment )page ); 584 } else { 585 m_provider.deleteVersion( page.getName(), page.getVersion() ); 586 // FIXME: If this was the latest, reindex Lucene, update RefMgr 587 } 588 } 589 590 /** 591 * {@inheritDoc} 592 * @see org.apache.wiki.pages.PageManager#deletePage(java.lang.String) 593 */ 594 @Override 595 public void deletePage( final String pageName ) throws ProviderException { 596 final Page p = getPage( pageName ); 597 if( p != null ) { 598 if( p instanceof Attachment ) { 599 m_engine.getManager( AttachmentManager.class ).deleteAttachment( ( Attachment )p ); 600 } else { 601 final Collection< String > refTo = m_engine.getManager( ReferenceManager.class ).findRefersTo( pageName ); 602 // May return null, if the page does not exist or has not been indexed yet. 603 604 if( m_engine.getManager( AttachmentManager.class ).hasAttachments( p ) ) { 605 final List< Attachment > attachments = m_engine.getManager( AttachmentManager.class ).listAttachments( p ); 606 for( final Attachment attachment : attachments ) { 607 if( refTo != null ) { 608 refTo.remove( attachment.getName() ); 609 } 610 611 m_engine.getManager( AttachmentManager.class ).deleteAttachment( attachment ); 612 } 613 } 614 deletePage( p ); 615 fireEvent( WikiPageEvent.PAGE_DELETED, pageName ); 616 } 617 } 618 } 619 620 /** 621 * {@inheritDoc} 622 * @see org.apache.wiki.pages.PageManager#deletePage(org.apache.wiki.api.core.Page) 623 */ 624 @Override 625 public void deletePage( final Page page ) throws ProviderException { 626 fireEvent( WikiPageEvent.PAGE_DELETE_REQUEST, page.getName() ); 627 m_provider.deletePage( page.getName() ); 628 fireEvent( WikiPageEvent.PAGE_DELETED, page.getName() ); 629 } 630 631 /** 632 * This is a simple reaper thread that runs roughly every minute 633 * or so (it's not really that important, as long as it runs), 634 * and removes all locks that have expired. 635 */ 636 private class LockReaper extends WikiBackgroundThread { 637 /** 638 * Create a LockReaper for a given engine. 639 * 640 * @param engine Engine to own this thread. 641 */ 642 public LockReaper( final Engine engine) { 643 super( engine, 60 ); 644 setName( "JSPWiki Lock Reaper" ); 645 } 646 647 @Override 648 public void backgroundTask() { 649 final Collection< PageLock > entries = m_pageLocks.values(); 650 for( final Iterator<PageLock> i = entries.iterator(); i.hasNext(); ) { 651 final PageLock p = i.next(); 652 653 if ( p.isExpired() ) { 654 i.remove(); 655 656 LOG.debug( "Reaped lock: " + p.getPage() + 657 " by " + p.getLocker() + 658 ", acquired " + p.getAcquisitionTime() + 659 ", and expired " + p.getExpiryTime() ); 660 } 661 } 662 } 663 } 664 665 // events processing ....................................................... 666 667 /** 668 * Fires a WikiPageEvent of the provided type and page name 669 * to all registered listeners. 670 * 671 * @param type the event type to be fired 672 * @param pagename the wiki page name as a String 673 * @see org.apache.wiki.event.WikiPageEvent 674 */ 675 protected final void fireEvent( final int type, final String pagename ) { 676 if( WikiEventManager.isListening( this ) ) { 677 WikiEventManager.fireEvent( this, new WikiPageEvent( m_engine, type, pagename ) ); 678 } 679 } 680 681 /** 682 * Listens for {@link org.apache.wiki.event.WikiSecurityEvent#PROFILE_NAME_CHANGED} 683 * events. If a user profile's name changes, each page ACL is inspected. If an entry contains 684 * a name that has changed, it is replaced with the new one. No events are emitted 685 * as a consequence of this method, because the page contents are still the same; it is 686 * only the representations of the names within the ACL that are changing. 687 * 688 * @param event The event 689 */ 690 @Override 691 public void actionPerformed( final WikiEvent event ) { 692 if( !( event instanceof WikiSecurityEvent ) ) { 693 return; 694 } 695 696 final WikiSecurityEvent se = ( WikiSecurityEvent ) event; 697 if( se.getType() == WikiSecurityEvent.PROFILE_NAME_CHANGED ) { 698 final UserProfile[] profiles = (UserProfile[]) se.getTarget(); 699 final Principal[] oldPrincipals = new Principal[] { new WikiPrincipal( profiles[ 0 ].getLoginName() ), 700 new WikiPrincipal( profiles[ 0 ].getFullname()), 701 new WikiPrincipal( profiles[ 0 ].getWikiName() ) }; 702 final Principal newPrincipal = new WikiPrincipal( profiles[ 1 ].getFullname() ); 703 704 // Examine each page ACL 705 try { 706 int pagesChanged = 0; 707 final Collection< Page > pages = getAllPages(); 708 for( final Page page : pages ) { 709 final boolean aclChanged = changeAcl( page, oldPrincipals, newPrincipal ); 710 if( aclChanged ) { 711 // If the Acl needed changing, change it now 712 try { 713 m_engine.getManager( AclManager.class ).setPermissions( page, page.getAcl() ); 714 } catch( final WikiSecurityException e ) { 715 LOG.error("Could not change page ACL for page " + page.getName() + ": " + e.getMessage(), e); 716 } 717 pagesChanged++; 718 } 719 } 720 LOG.info( "Profile name change for '" + newPrincipal + "' caused " + pagesChanged + " page ACLs to change also." ); 721 } catch( final ProviderException e ) { 722 // Oooo! This is really bad... 723 LOG.error( "Could not change user name in Page ACLs because of Provider error:" + e.getMessage(), e ); 724 } 725 } 726 } 727 728 /** 729 * For a single wiki page, replaces all Acl entries matching a supplied array of Principals with a new Principal. 730 * 731 * @param page the wiki page whose Acl is to be modified 732 * @param oldPrincipals an array of Principals to replace; all AclEntry objects whose {@link AclEntry#getPrincipal()} method returns 733 * one of these Principals will be replaced 734 * @param newPrincipal the Principal that should receive the old Principals' permissions 735 * @return <code>true</code> if the Acl was actually changed; <code>false</code> otherwise 736 */ 737 protected boolean changeAcl( final Page page, final Principal[] oldPrincipals, final Principal newPrincipal ) { 738 final Acl acl = page.getAcl(); 739 boolean pageChanged = false; 740 if( acl != null ) { 741 final Enumeration< AclEntry > entries = acl.aclEntries(); 742 final Collection< AclEntry > entriesToAdd = new ArrayList<>(); 743 final Collection< AclEntry > entriesToRemove = new ArrayList<>(); 744 while( entries.hasMoreElements() ) { 745 final AclEntry entry = entries.nextElement(); 746 if( ArrayUtils.contains( oldPrincipals, entry.getPrincipal() ) ) { 747 // Create new entry 748 final AclEntry newEntry = Wiki.acls().entry(); 749 newEntry.setPrincipal( newPrincipal ); 750 final Enumeration< Permission > permissions = entry.permissions(); 751 while( permissions.hasMoreElements() ) { 752 final Permission permission = permissions.nextElement(); 753 newEntry.addPermission( permission ); 754 } 755 pageChanged = true; 756 entriesToRemove.add( entry ); 757 entriesToAdd.add( newEntry ); 758 } 759 } 760 for( final AclEntry entry : entriesToRemove ) { 761 acl.removeEntry( entry ); 762 } 763 for( final AclEntry entry : entriesToAdd ) { 764 acl.addEntry( entry ); 765 } 766 } 767 return pageChanged; 768 } 769 770 /** 771 * {@inheritDoc} 772 * @see org.apache.wiki.pages.PageManager#getPageSorter() 773 */ 774 @Override 775 public PageSorter getPageSorter() { 776 return pageSorter; 777 } 778 779}