/**
	 * Override for preSaveTransform. Enables quick post publish by signing
	 * the article using the standard --~~~~ marker. This causes the signature
	 * marker to be replaced by a {{wl-publish:...}} parser function call,
	 * that is then saved to the database and causes the post to be published.
	 */
	public function preSaveTransform( $text, User $user = null, ParserOptions $popts = null ) {
		global $wgParser, $wgUser;
		$user = is_null( $user ) ? $wgUser : $user;

		if ( $popts === null ) {
			$popts = ParserOptions::newFromUser( $user );
		}

		$t = WikilogUtils::getPublishParameters();
		$date_txt = $t['date'];
		$user_txt = $t['user'];

		$sigs = array(
			'/\n?(--)?~~~~~\n?/m' => "\n{{wl-publish: {$date_txt} }}\n",
			'/\n?(--)?~~~~\n?/m' => "\n{{wl-publish: {$date_txt} | {$user_txt} }}\n",
			'/\n?(--)?~~~\n?/m' => "\n{{wl-author: {$user_txt} }}\n"
		);

		if ( !StubObject::isRealObject( $wgParser ) ) {
			$wgParser->_unstub();
		}
		$wgParser->startExternalParse( $this->mTitle, $popts, Parser::OT_WIKI );

		$text = $wgParser->replaceVariables( $text );
		$text = preg_replace( array_keys( $sigs ), array_values( $sigs ), $text );
		$text = $wgParser->mStripState->unstripBoth( $text );

		return parent::preSaveTransform( $text, $user, $popts );
	}
	/**
	 * Summary tag parser hook handler.
	 */
	public static function summary( $text, $params, $parser ) {
		$mwHidden =& MagicWord::get( 'wlk-hidden' );

		# Remove extra space to make block rendering easier.
		$text = trim( $text );
		self::trySetSummary( $parser, $text );

		$hidden = WikilogUtils::arrayMagicKeyGet( $params, $mwHidden );
		return $hidden ? '<!-- -->' : $parser->recursiveTagParse( $text );
	}
	/**
	 * Generates and returns a "post new comment" form for the user to fill in
	 * and submit.
	 *
	 * @param $parent If provided, generates a "post reply" form to reply to
	 *   the given comment.
	 */
	public function getPostCommentForm( $parent = null ) {
		global $wgUser, $wgTitle, $wgScript, $wgRequest;
		global $wgWikilogModerateAnonymous;

		$comment = $this->mPostedComment;
		$opts = $this->mFormOptions;

		$preview = '';
		$pid = $parent ? $parent->mID : null;
		if ( $comment && $comment->mParent == $pid ) {
			$check = $this->validateComment( $comment );
			if ( $check ) {
				$preview = Xml::wrapClass( wfMsg( $check ), 'mw-warning', 'div' );
			} else {
				$preview = $this->mFormatter->formatComment( $this->mPostedComment );
			}
			$header = wfMsgHtml( 'wikilog-form-preview' );
			$preview = "<b>{$header}</b>{$preview}<hr/>";
		}

		$form =
			Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) .
			Html::hidden( 'action', 'wikilog' ) .
			Html::hidden( 'wpEditToken', $wgUser->editToken() ) .
			( $parent ? Html::hidden( 'wlParent', $parent->mID ) : '' );

		$fields = array();

		if ( $wgUser->isLoggedIn() ) {
			$fields[] = array(
				wfMsg( 'wikilog-form-name' ),
				$this->mSkin->userLink( $wgUser->getId(), $wgUser->getName() )
			);
		} else {
			$loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
			$loginLink = $this->mSkin->link( $loginTitle,
				wfMsgHtml( 'loginreqlink' ), array(),
				array( 'returnto' => $wgTitle->getPrefixedUrl() )
			);
			$message = wfMsg( 'wikilog-posting-anonymously', $loginLink );
			$fields[] = array(
				Xml::label( wfMsg( 'wikilog-form-name' ), 'wl-name' ),
				Xml::input( 'wlAnonName', 25, $opts->consumeValue( 'wlAnonName' ),
					array( 'id' => 'wl-name', 'maxlength' => 255 ) ) .
					"<p>{$message}</p>"
			);
		}

		$autofocus = $parent ? array( 'autofocus' => 'autofocus' ) : array();
		$fields[] = array(
			Xml::label( wfMsg( 'wikilog-form-comment' ), 'wl-comment' ),
			Xml::textarea( 'wlComment', $opts->consumeValue( 'wlComment' ),
				40, 5, array( 'id' => 'wl-comment' ) + $autofocus )
		);

		if ( $this->mCaptchaForm ) {
			$fields[] = array( '', $this->mCaptchaForm );
		}

		if ( $wgWikilogModerateAnonymous && $wgUser->isAnon() ) {
			$fields[] = array( '', wfMsg( 'wikilog-anonymous-moderated' ) );
		}

		$fields[] = array( '',
			Xml::submitbutton( wfMsg( 'wikilog-submit' ), array( 'name' => 'wlActionCommentSubmit' ) ) . WL_NBSP .
			Xml::submitbutton( wfMsg( 'wikilog-preview' ), array( 'name' => 'wlActionCommentPreview' ) )
		);

		$form .= WikilogUtils::buildForm( $fields );

		foreach ( $opts->getUnconsumedValues() as $key => $value ) {
			$form .= Html::hidden( $key, $value );
		}

		$form = Xml::tags( 'form', array(
			'action' => "{$wgScript}#wl-comment-form",
			'method' => 'post'
		), $form );

		$msgid = ( $parent ? 'wikilog-post-reply' : 'wikilog-post-comment' );
		return Xml::fieldset( wfMsg( $msgid ), $preview . $form,
			array( 'id' => 'wl-comment-form' ) ) . "\n";
	}
	/**
	 * Format and return the navigation bar.
	 * @param $limit integer  Number of itens being displayed.
	 * @return string  HTML-formatted navigation bar.
	 */
	public function getNavigationBar( $limit ) {
		global $wgLang;

		$limit = $wgLang->formatNum( $limit );
		$opts = array( 'parsemag', 'escapenoentities' );
		$linkTexts = $disabledTexts = array();
		foreach ( self::$linkTextMsgs[$this->mType] as $type => $msg ) {
			$label = wfMsgExt( $msg, $opts, $limit );
			$linkTexts[$type] = wfMsgReplaceArgs( self::$pagingLabels[$type], array( $label ) );
			$disabledTexts[$type] = Xml::wrapClass( $linkTexts[$type], 'disabled' );
		}

		$pagingLinks = $this->mPager->getPagingLinks( $linkTexts, $disabledTexts );
// 		$limitLinks = $this->mPager->getLimitLinks(); // XXX: Not used yet.
		$ellipsis = wfMsg( 'ellipsis' );
		$html = "{$pagingLinks['first']} {$pagingLinks['prev']} {$ellipsis} {$pagingLinks['next']} {$pagingLinks['last']}";
		$html = WikilogUtils::wrapDiv( 'wl-pagination', $html );

		$dir = $wgLang->getDir();

		return Xml::tags( 'div',
			array(
				'class' => 'wl-navbar',
				'dir' => $dir
			),
			$html
		);
	}
	/**
	 * Start a new comment thread. Should be called before formatComment()
	 * when formatting comments in threads. Comments must be displayed in
	 * correct thread sequence when using this function, which means that
	 * the 'wlc_thread' column should be used to sort the query results from
	 * the 'wikilog_comments' table. After the last comment,
	 * closeCommentThreads() must be called.
	 *
	 * @param $comment Comment to be formatted.
	 * @return Generated HTML.
	 */
	public function startCommentThread( $comment ) {
		$top = count( $this->mThreadStack );
		$thread = count( $comment->mThread );

		# Find common ancestors.
		$common = min( $top, $thread );
		for ( $i = 0; $i < $common; $i++ ) {
			if ( $this->mThreadStack[$i] != $comment->mThread[$i] )
				break;
		}

		# Close previous threads.
		$html = str_repeat( "</div>", $top - $i );
		array_splice( $this->mThreadStack, $i );

		# Create omitted comment thread(s).
		for ( ; $i < $thread-1; $i++ ) {
			$msg = wfMsgExt( 'wikilog-comment-omitted-x', array( 'parseinline' ), $comment->mThread[$i] );
			$msg = WikilogUtils::wrapDiv( 'wl-comment-placeholder', $msg );
			$msg = WikilogUtils::wrapDiv( 'wl-comment wl-comment-omitted', $msg );
			$html .= '<div class="wl-thread">' . $msg;
			array_push( $this->mThreadStack, $comment->mThread[$i] );
		}

		# Open the new comment thread.
		$html .= '<div class="wl-thread">';
		array_push( $this->mThreadStack, $comment->mThread[$i] );
		return $html;
	}
	/**
	 * Returns an array with common header and footer system message
	 * parameters.
	 */
	public function getMsgParams( $extended = false, $pout = null ) {
		global $wgContLang, $wgWikilogEnableTags;

		$authors = array_keys( $this->mAuthors );
		$authorsFmt = WikilogUtils::authorList( $authors );
		$commentsFmt = WikilogUtils::getCommentsWikiText( $this );

		$categories = array();
		$categoriesFmt = '';
		$tags = array();
		$tagsFmt = '';

		if ( $extended ) {
			if ( $pout !== null ) {
				$categories = $pout->getCategoryLinks();
				if ( count( $categories ) > 0 ) {
					$categoriesFmt = wfMsgExt( 'wikilog-summary-categories',
						array( 'content', 'parsemag' ),
						count( $categories ),
						WikilogUtils::categoryList( $categories )
					);
				} else {
					$categoriesFmt = wfMsgExt( 'wikilog-summary-uncategorized',
						array( 'content', 'parsemag' )
					);
				}
			}
			if ( $wgWikilogEnableTags ) {
				$tags = array_keys( $this->mTags );
				$tagsFmt = WikilogUtils::tagList( $tags );
			}
		}

		list( $date, $time, $tz ) = WikilogUtils::getLocalDateTime( $this->mPubDate );

		/*
		 * This is probably the largest amount of parameters to a
		 * system message in MediaWiki. This is the price of allowing
		 * the user to customize the presentation of wikilog articles.
		 */
		return array(
			/* $1  */ $this->mParentTitle->getPrefixedURL(),
			/* $2  */ $this->mParentName,
			/* $3  */ $this->mTitle->getPrefixedURL(),
			/* $4  */ $this->mName,
			/* $5  */ count( $authors ),
			/* $6  */ ( count( $authors ) > 0 ? $authors[0] : '' ),
			/* $7  */ $authorsFmt,
			/* $8  */ $date,
			/* $9  */ $time,
			/* $10 */ $commentsFmt,
			/* $11 */ count( $categories ),
			/* $12 */ $categoriesFmt,
			/* $13 */ count( $tags ),
			/* $14 */ $tagsFmt,
			/* $15 */ $tz
		);
	}
	/**
	 * Generates and returns a single feed entry.
	 * @param $row The wikilog comment database entry.
	 * @return A new WlSyndicationEntry object.
	 */
	function formatFeedEntry( $row ) {
		global $wgMimeType;

		# Create comment object.
		$item = $this->mSingleItem ? $this->mSingleItem : WikilogItem::newFromRow( $row );
		$comment = WikilogComment::newFromRow( $item, $row );

		# Prepare some strings.
		if ( $comment->mUserID ) {
			$usertext = $comment->mUserText;
		} else {
			$usertext = wfMsgForContent( 'wikilog-comment-anonsig',
				$comment->mUserText, ''/*talk*/, $comment->mAnonName
			);
		}
		if ( $this->mSingleItem ) {
			$title = wfMsgForContent( 'wikilog-comment-feed-title1',
				$comment->mID, $usertext
			);
		} else {
			$title = wfMsgForContent( 'wikilog-comment-feed-title2',
				$comment->mID, $usertext, $comment->mItem->mName
			);
		}

		# Create new syndication entry.
		$entry = new WlSyndicationEntry(
			self::makeEntryId( $comment ),
			$title,
			$comment->mUpdated,
			$comment->getCommentArticleTitle()->getFullUrl()
		);

		# Comment text.
		if ( $comment->mCommentRev ) {
			list( $article, $parserOutput ) = WikilogUtils::parsedArticle( $comment->mCommentTitle, true );
			$content = Sanitizer::removeHTMLcomments( $parserOutput->getText() );
			if ( $content ) {
				$entry->setContent( new WlTextConstruct( 'html', $content ) );
			}
		}

		# Author.
		$usertitle = Title::makeTitle( NS_USER, $comment->mUserText );
		$useruri = $usertitle->exists() ? $usertitle->getFullUrl() : null;
		$entry->addAuthor( $usertext, $useruri );

		# Timestamp
		$entry->setPublished( $comment->mTimestamp );

		return $entry;
	}
	function formatRow( $row ) {
		global $wgParser;

		# Retrieve article parser output and other data.
		$item = WikilogItem::newFromRow( $row );
		list( $article, $parserOutput ) = WikilogUtils::parsedArticle( $item->mTitle );
		list( $summary, $content ) = WikilogUtils::splitSummaryContent( $parserOutput );
		if ( empty( $summary ) ) {
			$summary = $content;
			$hasMore = false;
		} else {
			$hasMore = true;
		}

		# Some general data.
		$authors = WikilogUtils::authorList( array_keys( $item->mAuthors ) );
		$tags = implode( wfMsgForContent( 'comma-separator' ), array_keys( $item->mTags ) );
		$comments = WikilogUtils::getCommentsWikiText( $item );
		$divclass = 'wl-entry' . ( $item->getIsPublished() ? '' : ' wl-draft' );

		$itemPubdate = $item->getPublishDate();
		list( $publishedDate, $publishedTime, $publishedTz ) =
				WikilogUtils::getLocalDateTime( $itemPubdate );

		$itemUpdated = $item->getUpdatedDate();
		list( $updatedDate, $updatedTime, ) =
				WikilogUtils::getLocalDateTime( $itemUpdated );

		# Template parameters.
		$vars = array(
			'class'         => $divclass,
			'wikilogTitle'  => $item->mParentName,
			'wikilogPage'   => $item->mParentTitle->getPrefixedText(),
			'title'         => $item->mName,
			'page'          => $item->mTitle->getPrefixedText(),
			'authors'       => $authors,
			'tags'          => $tags,
			'published'     => $item->getIsPublished() ? '*' : '',
			'date'          => $publishedDate,
			'time'          => $publishedTime,
			'tz'            => $publishedTz,
			'updatedDate'   => $updatedDate,
			'updatedTime'   => $updatedTime,
			'summary'       => $wgParser->insertStripItem( $summary ),
			'hasMore'       => $hasMore ? '*' : '',
			'comments'      => $comments
		);

		$frame = $wgParser->getPreprocessor()->newCustomFrame( $vars );
		$text = $frame->expand( $this->mTemplate );

		return $this->parse( $text );
	}
	/**
	 * ArticleSave hook handler function.
	 * Add article signature if user selected "sign and publish" option in
	 * EditPage.
	 */
	static function ArticleSave( &$article, &$user, &$text, &$summary,
			$minor, $watch, $sectionanchor, &$flags )
	{
		# $article->mExtWikilog piggybacked from WikilogHooks::EditPageAttemptSave().
		if ( isset( $article->mExtWikilog ) && $article->mExtWikilog['signpub'] ) {
			$t = WikilogUtils::getPublishParameters();
			$txtDate = $t['date'];
			$txtUser = $t['user'];
			$text = rtrim( $text ) . "\n{{wl-publish: {$txtDate} | {$txtUser} }}\n";
		}
		return true;
	}
	/**
	 * Wikilog action handler.
	 */
	public function wikilog() {
		global $wgUser, $wgOut, $wgRequest;

		if ( $this->mTitle->exists() && $wgRequest->getBool( 'wlActionNewItem' ) )
			return $this->actionNewItem();

		$wgOut->setPageTitle( wfMsg( 'wikilog-tab-title' ) );
		$wgOut->setRobotpolicy( 'noindex,nofollow' );

		if ( $this->mTitle->exists() ) {
			$skin = $wgUser->getSkin();
			$wgOut->addHTML( $this->formatWikilogDescription( $skin ) );
			$wgOut->addHTML( $this->formatWikilogInformation( $skin ) );
			if ( $this->mTitle->quickUserCan( 'edit' ) ) {
				$wgOut->addHTML( $this->formNewItem() );
			}
		} elseif ( $this->mTitle->userCan( 'create' ) ) {
			$text = wfMsgExt( 'wikilog-missing-wikilog', 'parse' );
			$text = WikilogUtils::wrapDiv( 'noarticletext', $text );
			$wgOut->addHTML( $text );
		} else {
			$this->showMissingArticle();
		}
	}
	public function formatRow( $row ) {
		# Retrieve comment data.
		$item = $this->mSingleItem ? $this->mSingleItem : WikilogItem::newFromRow( $row );
		$comment = WikilogComment::newFromRow( $item, $row );
		$comment->loadText();

		$doReply = $this->mReplyTrigger && $comment->mID == $this->mReplyTrigger;

		$html = $this->mFormatter->startCommentThread( $comment );
		$html .= $this->mFormatter->formatComment( $comment, $doReply );

		if ( $doReply && is_callable( $this->mReplyCallback ) ) {
			if ( ( $res = call_user_func( $this->mReplyCallback, $comment ) ) ) {
				$html .= WikilogUtils::wrapDiv( 'wl-indent', $res );
			}
		}
		return $html;
	}