summaryrefslogtreecommitdiff
path: root/includes/EditPage.php
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2014-12-27 15:41:37 +0100
committerPierre Schmitz <pierre@archlinux.de>2014-12-31 11:43:28 +0100
commitc1f9b1f7b1b77776192048005dcc66dcf3df2bfb (patch)
tree2b38796e738dd74cb42ecd9bfd151803108386bc /includes/EditPage.php
parentb88ab0086858470dd1f644e64cb4e4f62bb2be9b (diff)
Update to MediaWiki 1.24.1
Diffstat (limited to 'includes/EditPage.php')
-rw-r--r--includes/EditPage.php1422
1 files changed, 790 insertions, 632 deletions
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 4dd83845..128244a8 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -1,6 +1,6 @@
<?php
/**
- * Page edition user interface.
+ * User interface for page editing.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,7 +36,6 @@
* headaches, which may be fatal.
*/
class EditPage {
-
/**
* Status: Article successfully updated
*/
@@ -68,11 +67,6 @@ class EditPage {
const AS_CONTENT_TOO_BIG = 216;
/**
- * Status: User cannot edit? (not used)
- */
- const AS_USER_CANNOT_EDIT = 217;
-
- /**
* Status: this anonymous user is not allowed to edit this page
*/
const AS_READ_ONLY_PAGE_ANON = 218;
@@ -105,7 +99,7 @@ class EditPage {
const AS_NO_CREATE_PERMISSION = 223;
/**
- * Status: user tried to create a blank page
+ * Status: user tried to create a blank page and wpIgnoreBlankArticle == false
*/
const AS_BLANK_ARTICLE = 224;
@@ -131,11 +125,6 @@ class EditPage {
const AS_MAX_ARTICLE_SIZE_EXCEEDED = 229;
/**
- * not used
- */
- const AS_OK = 230;
-
- /**
* Status: WikiPage::doEdit() was unsuccessful
*/
const AS_END = 231;
@@ -182,8 +171,9 @@ class EditPage {
* The cookie will be removed instantly if the JavaScript runs.
*
* Otherwise, though, we don't want the cookies to accumulate.
- * RFC 2109 ( https://www.ietf.org/rfc/rfc2109.txt ) specifies a possible limit of only 20 cookies per domain.
- * This still applies at least to some versions of IE without full updates:
+ * RFC 2109 ( https://www.ietf.org/rfc/rfc2109.txt ) specifies a possible
+ * limit of only 20 cookies per domain. This still applies at least to some
+ * versions of IE without full updates:
* https://blogs.msdn.com/b/ieinternals/archive/2009/08/20/wininet-ie-cookie-internals-faq.aspx
*
* A value of 20 minutes should be enough to take into account slow loads and minor
@@ -191,65 +181,166 @@ class EditPage {
*/
const POST_EDIT_COOKIE_DURATION = 1200;
- /**
- * @var Article
- */
- var $mArticle;
+ /** @var Article */
+ public $mArticle;
- /**
- * @var Title
- */
- var $mTitle;
+ /** @var Title */
+ public $mTitle;
+
+ /** @var null|Title */
private $mContextTitle = null;
- var $action = 'submit';
- var $isConflict = false;
- var $isCssJsSubpage = false;
- var $isCssSubpage = false;
- var $isJsSubpage = false;
- var $isWrongCaseCssJsPage = false;
- var $isNew = false; // new page or new section
- var $deletedSinceEdit;
- var $formtype;
- var $firsttime;
- var $lastDelete;
- var $mTokenOk = false;
- var $mTokenOkExceptSuffix = false;
- var $mTriedSave = false;
- var $incompleteForm = false;
- var $tooBig = false;
- var $kblength = false;
- var $missingComment = false;
- var $missingSummary = false;
- var $allowBlankSummary = false;
- var $autoSumm = '';
- var $hookError = '';
- #var $mPreviewTemplates;
-
- /**
- * @var ParserOutput
- */
- var $mParserOutput;
-
- /**
- * Has a summary been preset using GET parameter &summary= ?
- * @var Bool
- */
- var $hasPresetSummary = false;
-
- var $mBaseRevision = false;
- var $mShowSummaryField = true;
+
+ /** @var string */
+ public $action = 'submit';
+
+ /** @var bool */
+ public $isConflict = false;
+
+ /** @var bool */
+ public $isCssJsSubpage = false;
+
+ /** @var bool */
+ public $isCssSubpage = false;
+
+ /** @var bool */
+ public $isJsSubpage = false;
+
+ /** @var bool */
+ public $isWrongCaseCssJsPage = false;
+
+ /** @var bool New page or new section */
+ public $isNew = false;
+
+ /** @var bool */
+ public $deletedSinceEdit;
+
+ /** @var string */
+ public $formtype;
+
+ /** @var bool */
+ public $firsttime;
+
+ /** @var bool|stdClass */
+ public $lastDelete;
+
+ /** @var bool */
+ public $mTokenOk = false;
+
+ /** @var bool */
+ public $mTokenOkExceptSuffix = false;
+
+ /** @var bool */
+ public $mTriedSave = false;
+
+ /** @var bool */
+ public $incompleteForm = false;
+
+ /** @var bool */
+ public $tooBig = false;
+
+ /** @var bool */
+ public $kblength = false;
+
+ /** @var bool */
+ public $missingComment = false;
+
+ /** @var bool */
+ public $missingSummary = false;
+
+ /** @var bool */
+ public $allowBlankSummary = false;
+
+ /** @var bool */
+ protected $blankArticle = false;
+
+ /** @var bool */
+ protected $allowBlankArticle = false;
+
+ /** @var string */
+ public $autoSumm = '';
+
+ /** @var string */
+ public $hookError = '';
+
+ /** @var ParserOutput */
+ public $mParserOutput;
+
+ /** @var bool Has a summary been preset using GET parameter &summary= ? */
+ public $hasPresetSummary = false;
+
+ /** @var bool */
+ public $mBaseRevision = false;
+
+ /** @var bool */
+ public $mShowSummaryField = true;
# Form values
- var $save = false, $preview = false, $diff = false;
- var $minoredit = false, $watchthis = false, $recreate = false;
- var $textbox1 = '', $textbox2 = '', $summary = '', $nosummary = false;
- var $edittime = '', $section = '', $sectiontitle = '', $starttime = '';
- var $oldid = 0, $editintro = '', $scrolltop = null, $bot = true;
- var $contentModel = null, $contentFormat = null;
+
+ /** @var bool */
+ public $save = false;
+
+ /** @var bool */
+ public $preview = false;
+
+ /** @var bool */
+ public $diff = false;
+
+ /** @var bool */
+ public $minoredit = false;
+
+ /** @var bool */
+ public $watchthis = false;
+
+ /** @var bool */
+ public $recreate = false;
+
+ /** @var string */
+ public $textbox1 = '';
+
+ /** @var string */
+ public $textbox2 = '';
+
+ /** @var string */
+ public $summary = '';
+
+ /** @var bool */
+ public $nosummary = false;
+
+ /** @var string */
+ public $edittime = '';
+
+ /** @var string */
+ public $section = '';
+
+ /** @var string */
+ public $sectiontitle = '';
+
+ /** @var string */
+ public $starttime = '';
+
+ /** @var int */
+ public $oldid = 0;
+
+ /** @var string */
+ public $editintro = '';
+
+ /** @var null */
+ public $scrolltop = null;
+
+ /** @var bool */
+ public $bot = true;
+
+ /** @var null|string */
+ public $contentModel = null;
+
+ /** @var null|string */
+ public $contentFormat = null;
# Placeholders for text injection by hooks (must be HTML)
# extensions should take care to _append_ to the present value
- public $editFormPageTop = ''; // Before even the preview
+
+ /** @var string Before even the preview */
+ public $editFormPageTop = '';
public $editFormTextTop = '';
public $editFormTextBeforeContent = '';
public $editFormTextAfterWarn = '';
@@ -265,15 +356,17 @@ class EditPage {
public $suppressIntro = false;
- /**
- * Set to true to allow editing of non-text content types.
- *
- * @var bool
- */
+ /** @var bool Set to true to allow editing of non-text content types. */
public $allowNonTextContent = false;
+ /** @var bool */
+ protected $edit;
+
+ /** @var bool */
+ public $live;
+
/**
- * @param $article Article
+ * @param Article $article
*/
public function __construct( Article $article ) {
$this->mArticle = $article;
@@ -303,7 +396,7 @@ class EditPage {
/**
* Set the context Title object
*
- * @param $title Title object or null
+ * @param Title|null $title Title object or null
*/
public function setContextTitle( $title ) {
$this->mContextTitle = $title;
@@ -314,7 +407,7 @@ class EditPage {
* If not set, $wgTitle will be returned. This behavior might change in
* the future to return $this->mTitle instead.
*
- * @return Title object
+ * @return Title
*/
public function getContextTitle() {
if ( is_null( $this->mContextTitle ) ) {
@@ -325,6 +418,18 @@ class EditPage {
}
}
+ /**
+ * Returns if the given content model is editable.
+ *
+ * @param string $modelId The ID of the content model to test. Use CONTENT_MODEL_XXX constants.
+ * @return bool
+ * @throws MWException If $modelId has no known handler
+ */
+ public function isSupportedContentModel( $modelId ) {
+ return $this->allowNonTextContent ||
+ ContentHandler::getForModelID( $modelId ) instanceof TextContentHandler;
+ }
+
function submit() {
$this->edit();
}
@@ -406,6 +511,7 @@ class EditPage {
$this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
$this->isCssSubpage = $this->mTitle->isCssSubpage();
$this->isJsSubpage = $this->mTitle->isJsSubpage();
+ // @todo FIXME: Silly assignment.
$this->isWrongCaseCssJsPage = $this->isWrongCaseCssJsPage();
# Show applicable editing introductions
@@ -463,9 +569,9 @@ class EditPage {
# Ignore some permissions errors when a user is just previewing/viewing diffs
$remove = array();
foreach ( $permErrors as $error ) {
- if ( ( $this->preview || $this->diff ) &&
- ( $error[0] == 'blockedtext' || $error[0] == 'autoblockedtext' ) )
- {
+ if ( ( $this->preview || $this->diff )
+ && ( $error[0] == 'blockedtext' || $error[0] == 'autoblockedtext' )
+ ) {
$remove[] = $error;
}
}
@@ -482,8 +588,8 @@ class EditPage {
* "View source for ..." page displaying the source code after the error message.
*
* @since 1.19
- * @param array $permErrors of permissions errors, as returned by
- * Title::getUserPermissionsErrors().
+ * @param array $permErrors Array of permissions errors, as returned by
+ * Title::getUserPermissionsErrors().
* @throws PermissionsError
*/
protected function displayPermissionsError( array $permErrors ) {
@@ -506,8 +612,13 @@ class EditPage {
throw new PermissionsError( $action, $permErrors );
}
+ wfRunHooks( 'EditPage::showReadOnlyForm:initial', array( $this, &$wgOut ) );
+
$wgOut->setRobotPolicy( 'noindex,nofollow' );
- $wgOut->setPageTitle( wfMessage( 'viewsource-title', $this->getContextTitle()->getPrefixedText() ) );
+ $wgOut->setPageTitle( wfMessage(
+ 'viewsource-title',
+ $this->getContextTitle()->getPrefixedText()
+ ) );
$wgOut->addBacklinkSubtitle( $this->getContextTitle() );
$wgOut->addWikiText( $wgOut->formatPermissionsErrorMessage( $permErrors, 'edit' ) );
$wgOut->addHTML( "<hr />\n" );
@@ -535,26 +646,6 @@ class EditPage {
}
/**
- * Show a read-only error
- * Parameters are the same as OutputPage:readOnlyPage()
- * Redirect to the article page if redlink=1
- * @deprecated in 1.19; use displayPermissionsError() instead
- */
- function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) {
- wfDeprecated( __METHOD__, '1.19' );
-
- global $wgRequest, $wgOut;
- if ( $wgRequest->getBool( 'redlink' ) ) {
- // The edit page was reached via a red link.
- // Redirect to the article page and let them click the edit tab if
- // they really want a permission error.
- $wgOut->redirect( $this->mTitle->getFullURL() );
- } else {
- $wgOut->readOnlyPage( $source, $protected, $reasons, $action );
- }
- }
-
- /**
* Should we show a preview when the edit form is first shown?
*
* @return bool
@@ -570,13 +661,15 @@ class EditPage {
} elseif ( $this->section == 'new' ) {
// Nothing *to* preview for new sections
return false;
- } elseif ( ( $wgRequest->getVal( 'preload' ) !== null || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
+ } elseif ( ( $wgRequest->getVal( 'preload' ) !== null || $this->mTitle->exists() )
+ && $wgUser->getOption( 'previewonfirst' )
+ ) {
// Standard preference behavior
return true;
- } elseif ( !$this->mTitle->exists() &&
- isset( $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] ) &&
- $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] )
- {
+ } elseif ( !$this->mTitle->exists()
+ && isset( $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] )
+ && $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()]
+ ) {
// Categories are special
return true;
} else {
@@ -609,7 +702,7 @@ class EditPage {
* Subclasses may override this to replace the default behavior, which is
* to check ContentHandler::supportsSections.
*
- * @return bool true if this edit page supports sections, false otherwise.
+ * @return bool True if this edit page supports sections, false otherwise.
*/
protected function isSectionEditSupported() {
$contentHandler = ContentHandler::getForTitle( $this->mTitle );
@@ -618,7 +711,8 @@ class EditPage {
/**
* This function collects the form data and uses it to populate various member variables.
- * @param $request WebRequest
+ * @param WebRequest $request
+ * @throws ErrorPageError
*/
function importFormData( &$request ) {
global $wgContLang, $wgUser;
@@ -685,9 +779,13 @@ class EditPage {
// suhosin.request.max_value_length (d'oh)
$this->incompleteForm = true;
} else {
- // edittime should be one of our last fields; if it's missing,
- // the submission probably broke somewhere in the middle.
- $this->incompleteForm = is_null( $this->edittime );
+ // If we receive the last parameter of the request, we can fairly
+ // claim the POST request has not been truncated.
+
+ // TODO: softened the check for cutover. Once we determine
+ // that it is safe, we should complete the transition by
+ // removing the "edittime" clause.
+ $this->incompleteForm = ( !$request->getVal( 'wpUltimateParam' ) && is_null( $this->edittime ) );
}
if ( $this->incompleteForm ) {
# If the form is incomplete, force to preview.
@@ -734,15 +832,18 @@ class EditPage {
$this->watchthis = $request->getCheck( 'wpWatchthis' );
# Don't force edit summaries when a user is editing their own user or talk page
- if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK ) &&
- $this->mTitle->getText() == $wgUser->getName() )
- {
+ if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK )
+ && $this->mTitle->getText() == $wgUser->getName()
+ ) {
$this->allowBlankSummary = true;
} else {
- $this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' ) || !$wgUser->getOption( 'forceeditsummary' );
+ $this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' )
+ || !$wgUser->getOption( 'forceeditsummary' );
}
$this->autoSumm = $request->getText( 'wpAutoSummary' );
+
+ $this->allowBlankArticle = $request->getBool( 'wpIgnoreBlankArticle' );
} else {
# Not a posted form? Start with nothing.
wfDebug( __METHOD__ . ": Not a posted form.\n" );
@@ -756,7 +857,8 @@ class EditPage {
$this->save = false;
$this->diff = false;
$this->minoredit = false;
- $this->watchthis = $request->getBool( 'watchthis', false ); // Watch may be overridden by request parameters
+ // Watch may be overridden by request parameters
+ $this->watchthis = $request->getBool( 'watchthis', false );
$this->recreate = false;
// When creating a new section, we can preload a section title by passing it as the
@@ -765,8 +867,7 @@ class EditPage {
$this->sectiontitle = $request->getVal( 'preloadtitle' );
// Once wpSummary isn't being use for setting section titles, we should delete this.
$this->summary = $request->getVal( 'preloadtitle' );
- }
- elseif ( $this->section != 'new' && $request->getVal( 'summary' ) ) {
+ } elseif ( $this->section != 'new' && $request->getVal( 'summary' ) ) {
$this->summary = $request->getText( 'summary' );
if ( $this->summary !== '' ) {
$this->hasPresetSummary = true;
@@ -783,12 +884,26 @@ class EditPage {
$this->bot = $request->getBool( 'bot', true );
$this->nosummary = $request->getBool( 'nosummary' );
- $content_handler = ContentHandler::getForTitle( $this->mTitle );
- $this->contentModel = $request->getText( 'model', $content_handler->getModelID() ); #may be overridden by revision
- $this->contentFormat = $request->getText( 'format', $content_handler->getDefaultFormat() ); #may be overridden by revision
+ // May be overridden by revision.
+ $this->contentModel = $request->getText( 'model', $this->contentModel );
+ // May be overridden by revision.
+ $this->contentFormat = $request->getText( 'format', $this->contentFormat );
- #TODO: check if the desired model is allowed in this namespace, and if a transition from the page's current model to the new model is allowed
- #TODO: check if the desired content model supports the given content format!
+ if ( !ContentHandler::getForModelID( $this->contentModel )
+ ->isSupportedFormat( $this->contentFormat )
+ ) {
+ throw new ErrorPageError(
+ 'editpage-notsupportedcontentformat-title',
+ 'editpage-notsupportedcontentformat-text',
+ array( $this->contentFormat, ContentHandler::getLocalizedName( $this->contentModel ) )
+ );
+ }
+
+ /**
+ * @todo Check if the desired model is allowed in this namespace, and if
+ * a transition from the page's current model to the new model is
+ * allowed.
+ */
$this->live = $request->getCheck( 'live' );
$this->editintro = $request->getText( 'editintro',
@@ -807,7 +922,7 @@ class EditPage {
* this method should be overridden and return the page text that will be used
* for saving, preview parsing and so on...
*
- * @param $request WebRequest
+ * @param WebRequest $request
*/
protected function importContentFormData( &$request ) {
return; // Don't do anything, EditPage already extracted wpTextbox1
@@ -816,7 +931,7 @@ class EditPage {
/**
* Initialise form fields in the object
* Called on the first invocation, e.g. when a user clicks an edit link
- * @return bool -- if the requested section is valid
+ * @return bool If the requested section is valid
*/
function initialiseForm() {
global $wgUser;
@@ -850,37 +965,14 @@ class EditPage {
}
/**
- * Fetch initial editing page content.
- *
- * @param $def_text string|bool
- * @return mixed string on success, $def_text for invalid sections
- * @private
- * @deprecated since 1.21, get WikiPage::getContent() instead.
- */
- function getContent( $def_text = false ) {
- ContentHandler::deprecated( __METHOD__, '1.21' );
-
- if ( $def_text !== null && $def_text !== false && $def_text !== '' ) {
- $def_content = $this->toEditContent( $def_text );
- } else {
- $def_content = false;
- }
-
- $content = $this->getContentObject( $def_content );
-
- // Note: EditPage should only be used with text based content anyway.
- return $this->toEditText( $content );
- }
-
- /**
* @param Content|null $def_content The default value to return
*
- * @return mixed Content on success, $def_content for invalid sections
+ * @return Content|null Content on success, $def_content for invalid sections
*
* @since 1.21
*/
protected function getContentObject( $def_content = null ) {
- global $wgOut, $wgRequest;
+ global $wgOut, $wgRequest, $wgUser, $wgContLang;
wfProfileIn( __METHOD__ );
@@ -900,14 +992,15 @@ class EditPage {
$preload = $wgRequest->getVal( 'preload',
// Custom preload text for new sections
$this->section === 'new' ? 'MediaWiki:addsection-preload' : '' );
+ $params = $wgRequest->getArray( 'preloadparams', array() );
- $content = $this->getPreloadedContent( $preload );
+ $content = $this->getPreloadedContent( $preload, $params );
}
// For existing pages, get text based on "undo" or section parameters.
} else {
if ( $this->section != '' ) {
// Get section edit text (returns $def_text for invalid sections)
- $orig = $this->getOriginalContent();
+ $orig = $this->getOriginalContent( $wgUser );
$content = $orig ? $orig->getSection( $this->section ) : null;
if ( !$content ) {
@@ -918,10 +1011,6 @@ class EditPage {
$undo = $wgRequest->getInt( 'undo' );
if ( $undo > 0 && $undoafter > 0 ) {
- if ( $undo < $undoafter ) {
- # If they got undoafter and undo round the wrong way, switch them
- list( $undo, $undoafter ) = array( $undoafter, $undo );
- }
$undorev = Revision::newFromId( $undo );
$oldrev = Revision::newFromId( $undoafter );
@@ -930,8 +1019,6 @@ class EditPage {
# the revisions exist and they were not deleted.
# Otherwise, $content will be left as-is.
if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
- $undorev->getPage() == $oldrev->getPage() &&
- $undorev->getPage() == $this->mTitle->getArticleID() &&
!$undorev->isDeleted( Revision::DELETED_TEXT ) &&
!$oldrev->isDeleted( Revision::DELETED_TEXT ) ) {
@@ -941,34 +1028,45 @@ class EditPage {
# Warn the user that something went wrong
$undoMsg = 'failure';
} else {
- # Inform the user of our success and set an automatic edit summary
- $undoMsg = 'success';
-
- # If we just undid one rev, use an autosummary
- $firstrev = $oldrev->getNext();
- if ( $firstrev && $firstrev->getId() == $undo ) {
- $userText = $undorev->getUserText();
- if ( $userText === '' ) {
- $undoSummary = wfMessage(
- 'undo-summary-username-hidden',
- $undo
- )->inContentLanguage()->text();
- } else {
- $undoSummary = wfMessage(
- 'undo-summary',
- $undo,
- $userText
- )->inContentLanguage()->text();
- }
- if ( $this->summary === '' ) {
- $this->summary = $undoSummary;
- } else {
- $this->summary = $undoSummary . wfMessage( 'colon-separator' )
- ->inContentLanguage()->text() . $this->summary;
+ $oldContent = $this->mArticle->getPage()->getContent( Revision::RAW );
+ $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
+ $newContent = $content->preSaveTransform( $this->mTitle, $wgUser, $popts );
+
+ if ( $newContent->equals( $oldContent ) ) {
+ # Tell the user that the undo results in no change,
+ # i.e. the revisions were already undone.
+ $undoMsg = 'nochange';
+ $content = false;
+ } else {
+ # Inform the user of our success and set an automatic edit summary
+ $undoMsg = 'success';
+
+ # If we just undid one rev, use an autosummary
+ $firstrev = $oldrev->getNext();
+ if ( $firstrev && $firstrev->getId() == $undo ) {
+ $userText = $undorev->getUserText();
+ if ( $userText === '' ) {
+ $undoSummary = wfMessage(
+ 'undo-summary-username-hidden',
+ $undo
+ )->inContentLanguage()->text();
+ } else {
+ $undoSummary = wfMessage(
+ 'undo-summary',
+ $undo,
+ $userText
+ )->inContentLanguage()->text();
+ }
+ if ( $this->summary === '' ) {
+ $this->summary = $undoSummary;
+ } else {
+ $this->summary = $undoSummary . wfMessage( 'colon-separator' )
+ ->inContentLanguage()->text() . $this->summary;
+ }
+ $this->undidRev = $undo;
}
- $this->undidRev = $undo;
+ $this->formtype = 'diff';
}
- $this->formtype = 'diff';
}
} else {
// Failed basic sanity checks.
@@ -977,14 +1075,14 @@ class EditPage {
$undoMsg = 'norev';
}
- // Messages: undo-success, undo-failure, undo-norev
+ // Messages: undo-success, undo-failure, undo-norev, undo-nochange
$class = ( $undoMsg == 'success' ? '' : 'error ' ) . "mw-undo-{$undoMsg}";
$this->editFormPageTop .= $wgOut->parse( "<div class=\"{$class}\">" .
wfMessage( 'undo-' . $undoMsg )->plain() . '</div>', true, /* interface */true );
}
if ( $content === false ) {
- $content = $this->getOriginalContent();
+ $content = $this->getOriginalContent( $wgUser );
}
}
}
@@ -1005,9 +1103,10 @@ class EditPage {
* 'missing-revision' message.
*
* @since 1.19
+ * @param User $user The user to get the revision for
* @return Content|null
*/
- private function getOriginalContent() {
+ private function getOriginalContent( User $user ) {
if ( $this->section == 'new' ) {
return $this->getCurrentContent();
}
@@ -1020,7 +1119,7 @@ class EditPage {
return $handler->makeEmptyContent();
}
- $content = $revision->getContent();
+ $content = $revision->getContent( Revision::FOR_THIS_USER, $user );
return $content;
}
@@ -1053,23 +1152,9 @@ class EditPage {
}
/**
- * Use this method before edit() to preload some text into the edit box
- *
- * @param $text string
- * @deprecated since 1.21, use setPreloadedContent() instead.
- */
- public function setPreloadedText( $text ) {
- ContentHandler::deprecated( __METHOD__, "1.21" );
-
- $content = $this->toEditContent( $text );
-
- $this->setPreloadedContent( $content );
- }
-
- /**
* Use this method before edit() to preload some content into the edit box
*
- * @param $content Content
+ * @param Content $content
*
* @since 1.21
*/
@@ -1081,32 +1166,14 @@ class EditPage {
* Get the contents to be preloaded into the box, either set by
* an earlier setPreloadText() or by loading the given page.
*
- * @param string $preload representing the title to preload from.
- *
- * @return String
- *
- * @deprecated since 1.21, use getPreloadedContent() instead
- */
- protected function getPreloadedText( $preload ) {
- ContentHandler::deprecated( __METHOD__, "1.21" );
-
- $content = $this->getPreloadedContent( $preload );
- $text = $this->toEditText( $content );
-
- return $text;
- }
-
- /**
- * Get the contents to be preloaded into the box, either set by
- * an earlier setPreloadText() or by loading the given page.
- *
- * @param string $preload representing the title to preload from.
+ * @param string $preload Representing the title to preload from.
+ * @param array $params Parameters to use (interface-message style) in the preloaded text
*
* @return Content
*
* @since 1.21
*/
- protected function getPreloadedContent( $preload ) {
+ protected function getPreloadedContent( $preload, $params = array() ) {
global $wgUser;
if ( !empty( $this->mPreloadContent ) ) {
@@ -1160,13 +1227,13 @@ class EditPage {
$content = $converted;
}
- return $content->preloadTransform( $title, $parserOptions );
+ return $content->preloadTransform( $title, $parserOptions, $params );
}
/**
* Make sure the form isn't faking a user's credentials.
*
- * @param $request WebRequest
+ * @param WebRequest $request
* @return bool
* @private
*/
@@ -1189,18 +1256,24 @@ class EditPage {
* marked HttpOnly. The JavaScript code converts the cookie to a wgPostEdit config
* variable.
*
- * We use a path of '/' since wgCookiePath is not exposed to JS
- *
* If the variable were set on the server, it would be cached, which is unwanted
* since the post-edit state should only apply to the load right after the save.
+ *
+ * @param int $statusValue The status value (to check for new article status)
*/
- protected function setPostEditCookie() {
+ protected function setPostEditCookie( $statusValue ) {
$revisionId = $this->mArticle->getLatest();
$postEditKey = self::POST_EDIT_COOKIE_KEY_PREFIX . $revisionId;
+ $val = 'saved';
+ if ( $statusValue == self::AS_SUCCESS_NEW_ARTICLE ) {
+ $val = 'created';
+ } elseif ( $this->oldid ) {
+ $val = 'restored';
+ }
+
$response = RequestContext::getMain()->getRequest()->response();
- $response->setcookie( $postEditKey, '1', time() + self::POST_EDIT_COOKIE_DURATION, array(
- 'path' => '/',
+ $response->setcookie( $postEditKey, $val, time() + self::POST_EDIT_COOKIE_DURATION, array(
'httpOnly' => false,
) );
}
@@ -1208,20 +1281,41 @@ class EditPage {
/**
* Attempt submission
* @throws UserBlockedError|ReadOnlyError|ThrottledError|PermissionsError
- * @return bool false if output is done, true if the rest of the form should be displayed
+ * @return bool False if output is done, true if the rest of the form should be displayed
*/
- function attemptSave() {
- global $wgUser, $wgOut;
+ public function attemptSave() {
+ global $wgUser;
$resultDetails = false;
# Allow bots to exempt some edits from bot flagging
$bot = $wgUser->isAllowed( 'bot' ) && $this->bot;
$status = $this->internalAttemptSave( $resultDetails, $bot );
- // FIXME: once the interface for internalAttemptSave() is made nicer, this should use the message in $status
- if ( $status->value == self::AS_SUCCESS_UPDATE || $status->value == self::AS_SUCCESS_NEW_ARTICLE ) {
+
+ return $this->handleStatus( $status, $resultDetails );
+ }
+
+ /**
+ * Handle status, such as after attempt save
+ *
+ * @param Status $status
+ * @param array|bool $resultDetails
+ *
+ * @throws ErrorPageError
+ * @return bool False, if output is done, true if rest of the form should be displayed
+ */
+ private function handleStatus( Status $status, $resultDetails ) {
+ global $wgUser, $wgOut;
+
+ /**
+ * @todo FIXME: once the interface for internalAttemptSave() is made
+ * nicer, this should use the message in $status
+ */
+ if ( $status->value == self::AS_SUCCESS_UPDATE
+ || $status->value == self::AS_SUCCESS_NEW_ARTICLE
+ ) {
$this->didSave = true;
if ( !$resultDetails['nullEdit'] ) {
- $this->setPostEditCookie();
+ $this->setPostEditCookie( $status->value );
}
}
@@ -1234,6 +1328,7 @@ class EditPage {
case self::AS_TEXTBOX_EMPTY:
case self::AS_MAX_ARTICLE_SIZE_EXCEEDED:
case self::AS_END:
+ case self::AS_BLANK_ARTICLE:
return true;
case self::AS_HOOK_ERROR:
@@ -1254,7 +1349,10 @@ class EditPage {
$sectionanchor = $resultDetails['sectionanchor'];
// Give extensions a chance to modify URL query on update
- wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this->mArticle, &$sectionanchor, &$extraQuery ) );
+ wfRunHooks(
+ 'ArticleUpdateBeforeRedirect',
+ array( $this->mArticle, &$sectionanchor, &$extraQuery )
+ );
if ( $resultDetails['redirect'] ) {
if ( $extraQuery == '' ) {
@@ -1266,10 +1364,6 @@ class EditPage {
$wgOut->redirect( $this->mTitle->getFullURL( $extraQuery ) . $sectionanchor );
return false;
- case self::AS_BLANK_ARTICLE:
- $wgOut->redirect( $this->getContextTitle()->getFullURL() );
- return false;
-
case self::AS_SPAM_ERROR:
$this->spamPageWithContent( $resultDetails['spam'] );
return false;
@@ -1312,9 +1406,9 @@ class EditPage {
/**
* Run hooks that can filter edits just before they get saved.
*
- * @param Content $content the Content to filter.
- * @param Status $status for reporting the outcome to the caller
- * @param User $user the user performing the edit
+ * @param Content $content The Content to filter.
+ * @param Status $status For reporting the outcome to the caller
+ * @param User $user The user performing the edit
*
* @return bool
*/
@@ -1358,20 +1452,58 @@ class EditPage {
}
/**
+ * Return the summary to be used for a new section.
+ *
+ * @param string $sectionanchor Set to the section anchor text
+ * @return string
+ */
+ private function newSectionSummary( &$sectionanchor = null ) {
+ global $wgParser;
+
+ if ( $this->sectiontitle !== '' ) {
+ $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
+ // If no edit summary was specified, create one automatically from the section
+ // title and have it link to the new section. Otherwise, respect the summary as
+ // passed.
+ if ( $this->summary === '' ) {
+ $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle );
+ return wfMessage( 'newsectionsummary' )
+ ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
+ }
+ } elseif ( $this->summary !== '' ) {
+ $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->summary );
+ # This is a new section, so create a link to the new section
+ # in the revision summary.
+ $cleanSummary = $wgParser->stripSectionName( $this->summary );
+ return wfMessage( 'newsectionsummary' )
+ ->rawParams( $cleanSummary )->inContentLanguage()->text();
+ }
+ return $this->summary;
+ }
+
+ /**
* Attempt submission (no UI)
*
- * @param array $result array to add statuses to, currently with the possible keys:
- * spam - string - Spam string from content if any spam is detected by matchSpamRegex
- * sectionanchor - string - Section anchor for a section save
- * nullEdit - boolean - Set if doEditContent is OK. True if null edit, false otherwise.
- * redirect - boolean - Set if doEditContent is OK. True if resulting revision is a redirect
+ * @param array $result Array to add statuses to, currently with the
+ * possible keys:
+ * - spam (string): Spam string from content if any spam is detected by
+ * matchSpamRegex.
+ * - sectionanchor (string): Section anchor for a section save.
+ * - nullEdit (boolean): Set if doEditContent is OK. True if null edit,
+ * false otherwise.
+ * - redirect (bool): Set if doEditContent is OK. True if resulting
+ * revision is a redirect.
* @param bool $bot True if edit is being made under the bot right.
*
- * @return Status object, possibly with a message, but always with one of the AS_* constants in $status->value,
+ * @return Status Status object, possibly with a message, but always with
+ * one of the AS_* constants in $status->value,
*
- * FIXME: This interface is TERRIBLE, but hard to get rid of due to various error display idiosyncrasies. There are
- * also lots of cases where error metadata is set in the object and retrieved later instead of being returned, e.g.
- * AS_CONTENT_TOO_BIG and AS_BLOCKED_PAGE_FOR_USER. All that stuff needs to be cleaned up some time.
+ * @todo FIXME: This interface is TERRIBLE, but hard to get rid of due to
+ * various error display idiosyncrasies. There are also lots of cases
+ * where error metadata is set in the object and retrieved later instead
+ * of being returned, e.g. AS_CONTENT_TOO_BIG and
+ * AS_BLOCKED_PAGE_FOR_USER. All that stuff needs to be cleaned up some
+ * time.
*/
function internalAttemptSave( &$result, $bot = false ) {
global $wgUser, $wgRequest, $wgParser, $wgMaxArticleSize;
@@ -1412,7 +1544,12 @@ class EditPage {
# Construct Content object
$textbox_content = $this->toEditContent( $this->textbox1 );
} catch ( MWContentSerializationException $ex ) {
- $status->fatal( 'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() );
+ $status->fatal(
+ 'content-failed-to-parse',
+ $this->contentModel,
+ $this->contentFormat,
+ $ex->getMessage()
+ );
$status->value = self::AS_PARSE_ERROR;
wfProfileOut( __METHOD__ . '-checks' );
wfProfileOut( __METHOD__ );
@@ -1422,7 +1559,8 @@ class EditPage {
# Check image redirect
if ( $this->mTitle->getNamespace() == NS_FILE &&
$textbox_content->isRedirect() &&
- !$wgUser->isAllowed( 'upload' ) ) {
+ !$wgUser->isAllowed( 'upload' )
+ ) {
$code = $wgUser->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED;
$status->setResult( false, $code );
@@ -1461,7 +1599,10 @@ class EditPage {
wfProfileOut( __METHOD__ );
return $status;
}
- if ( !wfRunHooks( 'EditFilter', array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) ) ) {
+ if ( !wfRunHooks(
+ 'EditFilter',
+ array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) )
+ ) {
# Error messages etc. could be handled within the hook...
$status->fatal( 'hookaborted' );
$status->value = self::AS_HOOK_ERROR;
@@ -1572,7 +1713,9 @@ class EditPage {
$defaultText = '';
}
- if ( $this->textbox1 === $defaultText ) {
+ if ( !$this->allowBlankArticle && $this->textbox1 === $defaultText ) {
+ $this->blankArticle = true;
+ $status->fatal( 'blankarticle' );
$status->setResult( false, self::AS_BLANK_ARTICLE );
wfProfileOut( __METHOD__ );
return $status;
@@ -1590,30 +1733,11 @@ class EditPage {
if ( $this->sectiontitle !== '' ) {
// Insert the section title above the content.
$content = $content->addSectionHeader( $this->sectiontitle );
-
- // Jump to the new section
- $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
-
- // If no edit summary was specified, create one automatically from the section
- // title and have it link to the new section. Otherwise, respect the summary as
- // passed.
- if ( $this->summary === '' ) {
- $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle );
- $this->summary = wfMessage( 'newsectionsummary' )
- ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
- }
} elseif ( $this->summary !== '' ) {
// Insert the section title above the content.
$content = $content->addSectionHeader( $this->summary );
-
- // Jump to the new section
- $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->summary );
-
- // Create a link to the new section from the edit summary.
- $cleanSummary = $wgParser->stripSectionName( $this->summary );
- $this->summary = wfMessage( 'newsectionsummary' )
- ->rawParams( $cleanSummary )->inContentLanguage()->text();
}
+ $this->summary = $this->newSectionSummary( $result['sectionanchor'] );
}
$status->value = self::AS_SUCCESS_NEW_ARTICLE;
@@ -1631,18 +1755,24 @@ class EditPage {
$this->isConflict = true;
if ( $this->section == 'new' ) {
if ( $this->mArticle->getUserText() == $wgUser->getName() &&
- $this->mArticle->getComment() == $this->summary ) {
+ $this->mArticle->getComment() == $this->newSectionSummary()
+ ) {
// Probably a duplicate submission of a new comment.
// This can happen when squid resends a request after
// a timeout but the first one actually went through.
- wfDebug( __METHOD__ . ": duplicate new section submission; trigger edit conflict!\n" );
+ wfDebug( __METHOD__
+ . ": duplicate new section submission; trigger edit conflict!\n" );
} else {
// New comment; suppress conflict.
$this->isConflict = false;
wfDebug( __METHOD__ . ": conflict suppressed; new section\n" );
}
- } elseif ( $this->section == '' && Revision::userWasLastToEdit( DB_MASTER, $this->mTitle->getArticleID(),
- $wgUser->getId(), $this->edittime ) ) {
+ } elseif ( $this->section == ''
+ && Revision::userWasLastToEdit(
+ DB_MASTER, $this->mTitle->getArticleID(),
+ $wgUser->getId(), $this->edittime
+ )
+ ) {
# Suppress edit conflict with self, except for section edits where merging is required.
wfDebug( __METHOD__ . ": Suppressing edit conflict, same user.\n" );
$this->isConflict = false;
@@ -1659,13 +1789,23 @@ class EditPage {
$content = null;
if ( $this->isConflict ) {
- wfDebug( __METHOD__ . ": conflict! getting section '{$this->section}' for time '{$this->edittime}'"
- . " (article time '{$timestamp}')\n" );
-
- $content = $this->mArticle->replaceSectionContent( $this->section, $textbox_content, $sectionTitle, $this->edittime );
+ wfDebug( __METHOD__
+ . ": conflict! getting section '{$this->section}' for time '{$this->edittime}'"
+ . " (article time '{$timestamp}')\n" );
+
+ $content = $this->mArticle->replaceSectionContent(
+ $this->section,
+ $textbox_content,
+ $sectionTitle,
+ $this->edittime
+ );
} else {
wfDebug( __METHOD__ . ": getting section '{$this->section}'\n" );
- $content = $this->mArticle->replaceSectionContent( $this->section, $textbox_content, $sectionTitle );
+ $content = $this->mArticle->replaceSectionContent(
+ $this->section,
+ $textbox_content,
+ $sectionTitle
+ );
}
if ( is_null( $content ) ) {
@@ -1715,7 +1855,7 @@ class EditPage {
return $status;
}
} elseif ( !$this->allowBlankSummary
- && !$content->equals( $this->getOriginalContent() )
+ && !$content->equals( $this->getOriginalContent( $wgUser ) )
&& !$content->isRedirect()
&& md5( $this->summary ) == $this->autoSumm
) {
@@ -1730,31 +1870,15 @@ class EditPage {
wfProfileIn( __METHOD__ . '-sectionanchor' );
$sectionanchor = '';
if ( $this->section == 'new' ) {
- if ( $this->sectiontitle !== '' ) {
- $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
- // If no edit summary was specified, create one automatically from the section
- // title and have it link to the new section. Otherwise, respect the summary as
- // passed.
- if ( $this->summary === '' ) {
- $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle );
- $this->summary = wfMessage( 'newsectionsummary' )
- ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
- }
- } elseif ( $this->summary !== '' ) {
- $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->summary );
- # This is a new section, so create a link to the new section
- # in the revision summary.
- $cleanSummary = $wgParser->stripSectionName( $this->summary );
- $this->summary = wfMessage( 'newsectionsummary' )
- ->rawParams( $cleanSummary )->inContentLanguage()->text();
- }
+ $this->summary = $this->newSectionSummary( $sectionanchor );
} elseif ( $this->section != '' ) {
- # Try to get a section anchor from the section source, redirect to edited section if header found
- # XXX: might be better to integrate this into Article::replaceSection
- # for duplicate heading checking and maybe parsing
+ # Try to get a section anchor from the section source, redirect
+ # to edited section if header found.
+ # XXX: Might be better to integrate this into Article::replaceSection
+ # for duplicate heading checking and maybe parsing.
$hasmatch = preg_match( "/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1, $matches );
- # we can't deal with anchors, includes, html etc in the header for now,
- # headline would need to be parsed to improve this
+ # We can't deal with anchors, includes, html etc in the header for now,
+ # headline would need to be parsed to improve this.
if ( $hasmatch && strlen( $matches[2] ) > 0 ) {
$sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $matches[2] );
}
@@ -1773,7 +1897,7 @@ class EditPage {
}
// Check for length errors again now that the section is merged in
- $this->kblength = (int)( strlen( $this->toEditText( $content ) ) / 1024 );
+ $this->kblength = (int)( strlen( $this->toEditText( $content ) ) / 1024 );
if ( $this->kblength > $wgMaxArticleSize ) {
$this->tooBig = true;
$status->setResult( false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
@@ -1786,17 +1910,23 @@ class EditPage {
( ( $this->minoredit && !$this->isNew ) ? EDIT_MINOR : 0 ) |
( $bot ? EDIT_FORCE_BOT : 0 );
- $doEditStatus = $this->mArticle->doEditContent( $content, $this->summary, $flags,
- false, null, $this->contentFormat );
+ $doEditStatus = $this->mArticle->doEditContent(
+ $content,
+ $this->summary,
+ $flags,
+ false,
+ null,
+ $content->getDefaultFormat()
+ );
if ( !$doEditStatus->isOK() ) {
// Failure from doEdit()
// Show the edit conflict page for certain recognized errors from doEdit(),
// but don't show it for errors from extension hooks
$errors = $doEditStatus->getErrorsArray();
- if ( in_array( $errors[0][0], array( 'edit-gone-missing', 'edit-conflict',
- 'edit-already-exists' ) ) )
- {
+ if ( in_array( $errors[0][0],
+ array( 'edit-gone-missing', 'edit-conflict', 'edit-already-exists' ) )
+ ) {
$this->isConflict = true;
// Destroys data doEdit() put in $status->value but who cares
$doEditStatus->value = self::AS_END;
@@ -1831,44 +1961,20 @@ class EditPage {
// Do this in its own transaction to reduce contention...
$dbw = wfGetDB( DB_MASTER );
- $dbw->onTransactionIdle( function() use ( $dbw, $title, $watch, $wgUser, $fname ) {
- $dbw->begin( $fname );
+ $dbw->onTransactionIdle( function () use ( $dbw, $title, $watch, $wgUser, $fname ) {
WatchAction::doWatchOrUnwatch( $watch, $title, $wgUser );
- $dbw->commit( $fname );
} );
}
}
/**
- * Attempts to merge text content with base and current revisions
- *
- * @param $editText string
- *
- * @return bool
- * @deprecated since 1.21, use mergeChangesIntoContent() instead
- */
- function mergeChangesInto( &$editText ) {
- ContentHandler::deprecated( __METHOD__, "1.21" );
-
- $editContent = $this->toEditContent( $editText );
-
- $ok = $this->mergeChangesIntoContent( $editContent );
-
- if ( $ok ) {
- $editText = $this->toEditText( $editContent );
- return true;
- }
- return false;
- }
-
- /**
* Attempts to do 3-way merge of edit content with a base revision
* and current content, in case of edit conflict, in whichever way appropriate
* for the content type.
*
* @since 1.21
*
- * @param $editContent
+ * @param Content $editContent
*
* @return bool
*/
@@ -1915,20 +2021,18 @@ class EditPage {
function getBaseRevision() {
if ( !$this->mBaseRevision ) {
$db = wfGetDB( DB_MASTER );
- $baseRevision = Revision::loadFromTimestamp(
+ $this->mBaseRevision = Revision::loadFromTimestamp(
$db, $this->mTitle, $this->edittime );
- return $this->mBaseRevision = $baseRevision;
- } else {
- return $this->mBaseRevision;
}
+ return $this->mBaseRevision;
}
/**
* Check given input text against $wgSpamRegex, and return the text of the first match.
*
- * @param $text string
+ * @param string $text
*
- * @return string|bool matching string or false
+ * @return string|bool Matching string or false
*/
public static function matchSpamRegex( $text ) {
global $wgSpamRegex;
@@ -1940,9 +2044,9 @@ class EditPage {
/**
* Check given input text against $wgSummarySpamRegex, and return the text of the first match.
*
- * @param $text string
+ * @param string $text
*
- * @return string|bool matching string or false
+ * @return string|bool Matching string or false
*/
public static function matchSummarySpamRegex( $text ) {
global $wgSummarySpamRegex;
@@ -1951,8 +2055,8 @@ class EditPage {
}
/**
- * @param $text string
- * @param $regexes array
+ * @param string $text
+ * @param array $regexes
* @return bool|string
*/
protected static function matchSpamRegexInternal( $text, $regexes ) {
@@ -1979,9 +2083,6 @@ class EditPage {
$wgOut->addModules( 'mediawiki.action.edit.editWarning' );
}
- // Bug #19334: textarea jumps when editing articles in IE8
- $wgOut->addStyle( 'common/IE80Fixes.css', 'screen', 'IE 8' );
-
$wgOut->setRobotPolicy( 'noindex,nofollow' );
# Enabled article-related sidebar, toplinks, etc.
@@ -1993,9 +2094,14 @@ class EditPage {
} elseif ( $contextTitle->exists() && $this->section != '' ) {
$msg = $this->section == 'new' ? 'editingcomment' : 'editingsection';
} else {
- $msg = $contextTitle->exists() || ( $contextTitle->getNamespace() == NS_MEDIAWIKI && $contextTitle->getDefaultMessageText() !== false ) ?
- 'editing' : 'creating';
+ $msg = $contextTitle->exists()
+ || ( $contextTitle->getNamespace() == NS_MEDIAWIKI
+ && $contextTitle->getDefaultMessageText() !== false
+ )
+ ? 'editing'
+ : 'creating';
}
+
# Use the title defined by DISPLAYTITLE magic word when present
$displayTitle = isset( $this->mParserOutput ) ? $this->mParserOutput->getDisplayTitle() : false;
if ( $displayTitle === false ) {
@@ -2045,14 +2151,15 @@ class EditPage {
$username = $parts[0];
$user = User::newFromName( $username, false /* allow IP users*/ );
$ip = User::isIP( $username );
+ $block = Block::newFromTarget( $user, $user );
if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
$wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
array( 'userpage-userdoesnotexist', wfEscapeWikiText( $username ) ) );
- } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
+ } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) { # Show log extract if the user is currently blocked
LogEventsList::showLogExtract(
$wgOut,
'block',
- $user->getUserPage(),
+ MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
'',
array(
'lim' => 1,
@@ -2115,7 +2222,7 @@ class EditPage {
if ( $title instanceof Title && $title->exists() && $title->userCan( 'read' ) ) {
global $wgOut;
// Added using template syntax, to take <noinclude>'s into account.
- $wgOut->addWikiTextTitleTidy( '{{:' . $title->getFullText() . '}}', $this->mTitle );
+ $wgOut->addWikiTextTitleTidy( '<div class="mw-editintro">{{:' . $title->getFullText() . '}}</div>', $this->mTitle );
return true;
}
}
@@ -2129,14 +2236,16 @@ class EditPage {
*
* If $content is null or false or a string, $content is returned unchanged.
*
- * If the given Content object is not of a type that can be edited using the text base EditPage,
- * an exception will be raised. Set $this->allowNonTextContent to true to allow editing of non-textual
+ * If the given Content object is not of a type that can be edited using
+ * the text base EditPage, an exception will be raised. Set
+ * $this->allowNonTextContent to true to allow editing of non-textual
* content.
*
* @param Content|null|bool|string $content
- * @return String the editable text form of the content.
+ * @return string The editable text form of the content.
*
- * @throws MWException if $content is not an instance of TextContent and $this->allowNonTextContent is not true.
+ * @throws MWException If $content is not an instance of TextContent and
+ * $this->allowNonTextContent is not true.
*/
protected function toEditText( $content ) {
if ( $content === null || $content === false ) {
@@ -2147,9 +2256,9 @@ class EditPage {
return $content;
}
- if ( !$this->allowNonTextContent && !( $content instanceof TextContent ) ) {
- throw new MWException( "This content model can not be edited as text: "
- . ContentHandler::getLocalizedName( $content->getModel() ) );
+ if ( !$this->isSupportedContentModel( $content->getModel() ) ) {
+ throw new MWException( 'This content model is not supported: '
+ . ContentHandler::getLocalizedName( $content->getModel() ) );
}
return $content->serialize( $this->contentFormat );
@@ -2158,16 +2267,18 @@ class EditPage {
/**
* Turns the given text into a Content object by unserializing it.
*
- * If the resulting Content object is not of a type that can be edited using the text base EditPage,
- * an exception will be raised. Set $this->allowNonTextContent to true to allow editing of non-textual
+ * If the resulting Content object is not of a type that can be edited using
+ * the text base EditPage, an exception will be raised. Set
+ * $this->allowNonTextContent to true to allow editing of non-textual
* content.
*
* @param string|null|bool $text Text to unserialize
- * @return Content The content object created from $text. If $text was false or null, false resp. null will be
- * returned instead.
+ * @return Content The content object created from $text. If $text was false
+ * or null, false resp. null will be returned instead.
*
- * @throws MWException if unserializing the text results in a Content object that is not an instance of TextContent
- * and $this->allowNonTextContent is not true.
+ * @throws MWException If unserializing the text results in a Content
+ * object that is not an instance of TextContent and
+ * $this->allowNonTextContent is not true.
*/
protected function toEditContent( $text ) {
if ( $text === false || $text === null ) {
@@ -2177,8 +2288,8 @@ class EditPage {
$content = ContentHandler::makeContent( $text, $this->getTitle(),
$this->contentModel, $this->contentFormat );
- if ( !$this->allowNonTextContent && !( $content instanceof TextContent ) ) {
- throw new MWException( "This content model can not be edited as text: "
+ if ( !$this->isSupportedContentModel( $content->getModel() ) ) {
+ throw new MWException( 'This content model is not supported: '
. ContentHandler::getLocalizedName( $content->getModel() ) );
}
@@ -2187,7 +2298,7 @@ class EditPage {
/**
* Send the edit form and related headers to $wgOut
- * @param $formCallback Callback|null that takes an OutputPage parameter; will be called
+ * @param callable|null $formCallback That takes an OutputPage parameter; will be called
* during form output near the top, for captchas and the like.
*/
function showEditForm( $formCallback = null ) {
@@ -2235,9 +2346,16 @@ class EditPage {
// @todo add EditForm plugin interface and use it here!
// search for textarea1 and textares2, and allow EditForm to override all uses.
- $wgOut->addHTML( Html::openElement( 'form', array( 'id' => self::EDITFORM_ID, 'name' => self::EDITFORM_ID,
- 'method' => 'post', 'action' => $this->getActionURL( $this->getContextTitle() ),
- 'enctype' => 'multipart/form-data' ) ) );
+ $wgOut->addHTML( Html::openElement(
+ 'form',
+ array(
+ 'id' => self::EDITFORM_ID,
+ 'name' => self::EDITFORM_ID,
+ 'method' => 'post',
+ 'action' => $this->getActionURL( $this->getContextTitle() ),
+ 'enctype' => 'multipart/form-data'
+ )
+ ) );
if ( is_callable( $formCallback ) ) {
call_user_func_array( $formCallback, array( &$wgOut ) );
@@ -2246,8 +2364,20 @@ class EditPage {
// Add an empty field to trip up spambots
$wgOut->addHTML(
Xml::openElement( 'div', array( 'id' => 'antispam-container', 'style' => 'display: none;' ) )
- . Html::rawElement( 'label', array( 'for' => 'wpAntiSpam' ), wfMessage( 'simpleantispam-label' )->parse() )
- . Xml::element( 'input', array( 'type' => 'text', 'name' => 'wpAntispam', 'id' => 'wpAntispam', 'value' => '' ) )
+ . Html::rawElement(
+ 'label',
+ array( 'for' => 'wpAntiSpam' ),
+ wfMessage( 'simpleantispam-label' )->parse()
+ )
+ . Xml::element(
+ 'input',
+ array(
+ 'type' => 'text',
+ 'name' => 'wpAntispam',
+ 'id' => 'wpAntispam',
+ 'value' => ''
+ )
+ )
. Xml::closeElement( 'div' )
);
@@ -2321,6 +2451,10 @@ class EditPage {
$wgOut->addHTML( EditPage::getEditToolbar() );
}
+ if ( $this->blankArticle ) {
+ $wgOut->addHTML( Html::hidden( 'wpIgnoreBlankArticle', true ) );
+ }
+
if ( $this->isConflict ) {
// In an edit conflict bypass the overridable content form method
// and fallback to the raw wpTextbox1 since editconflicts can't be
@@ -2364,11 +2498,19 @@ class EditPage {
$this->showConflict();
} catch ( MWContentSerializationException $ex ) {
// this can't really happen, but be nice if it does.
- $msg = wfMessage( 'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() );
+ $msg = wfMessage(
+ 'content-failed-to-parse',
+ $this->contentModel,
+ $this->contentFormat,
+ $ex->getMessage()
+ );
$wgOut->addWikiText( '<div class="error">' . $msg->text() . '</div>' );
}
}
+ // Marker for detecting truncated form data. This must be the last
+ // parameter sent in order to be of use, so do not move me.
+ $wgOut->addHTML( Html::hidden( 'wpUltimateParam', true ) );
$wgOut->addHTML( $this->editFormTextBottom . "\n</form>\n" );
if ( !$wgUser->getOption( 'previewontop' ) ) {
@@ -2382,7 +2524,7 @@ class EditPage {
* Extract the section title from current section text, if any.
*
* @param string $text
- * @return Mixed|string or false
+ * @return string|bool String or false
*/
public static function extractSectionTitle( $text ) {
preg_match( "/^(=+)(.+)\\1\\s*(\n|$)/i", $text, $matches );
@@ -2394,8 +2536,12 @@ class EditPage {
}
}
+ /**
+ * @return bool
+ */
protected function showHeader() {
global $wgOut, $wgUser, $wgMaxArticleSize, $wgLang;
+ global $wgAllowUserCss, $wgAllowUserJs;
if ( $this->mTitle->isTalkPage() ) {
$wgOut->addWikiMsg( 'talkpagetext' );
@@ -2437,6 +2583,10 @@ class EditPage {
$wgOut->wrapWikiMsg( "<div id='mw-missingcommentheader'>\n$1\n</div>", 'missingcommentheader' );
}
+ if ( $this->blankArticle ) {
+ $wgOut->wrapWikiMsg( "<div id='mw-blankarticle'>\n$1\n</div>", 'blankarticle' );
+ }
+
if ( $this->hookError !== '' ) {
$wgOut->addWikiText( $this->hookError );
}
@@ -2451,9 +2601,15 @@ class EditPage {
// Let sysop know that this will make private content public if saved
if ( !$revision->userCan( Revision::DELETED_TEXT, $wgUser ) ) {
- $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", 'rev-deleted-text-permission' );
+ $wgOut->wrapWikiMsg(
+ "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
+ 'rev-deleted-text-permission'
+ );
} elseif ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
- $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", 'rev-deleted-text-view' );
+ $wgOut->wrapWikiMsg(
+ "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
+ 'rev-deleted-text-view'
+ );
}
if ( !$revision->isCurrent() ) {
@@ -2470,32 +2626,55 @@ class EditPage {
}
if ( wfReadOnly() ) {
- $wgOut->wrapWikiMsg( "<div id=\"mw-read-only-warning\">\n$1\n</div>", array( 'readonlywarning', wfReadOnlyReason() ) );
+ $wgOut->wrapWikiMsg(
+ "<div id=\"mw-read-only-warning\">\n$1\n</div>",
+ array( 'readonlywarning', wfReadOnlyReason() )
+ );
} elseif ( $wgUser->isAnon() ) {
if ( $this->formtype != 'preview' ) {
- $wgOut->wrapWikiMsg( "<div id=\"mw-anon-edit-warning\">\n$1</div>", 'anoneditwarning' );
+ $wgOut->wrapWikiMsg(
+ "<div id='mw-anon-edit-warning'>\n$1\n</div>",
+ array( 'anoneditwarning',
+ // Log-in link
+ '{{fullurl:Special:UserLogin|returnto={{FULLPAGENAMEE}}}}',
+ // Sign-up link
+ '{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}' )
+ );
} else {
- $wgOut->wrapWikiMsg( "<div id=\"mw-anon-preview-warning\">\n$1</div>", 'anonpreviewwarning' );
+ $wgOut->wrapWikiMsg( "<div id=\"mw-anon-preview-warning\">\n$1</div>",
+ 'anonpreviewwarning'
+ );
}
} else {
if ( $this->isCssJsSubpage ) {
# Check the skin exists
if ( $this->isWrongCaseCssJsPage ) {
- $wgOut->wrapWikiMsg( "<div class='error' id='mw-userinvalidcssjstitle'>\n$1\n</div>", array( 'userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage() ) );
+ $wgOut->wrapWikiMsg(
+ "<div class='error' id='mw-userinvalidcssjstitle'>\n$1\n</div>",
+ array( 'userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage() )
+ );
}
if ( $this->formtype !== 'preview' ) {
- if ( $this->isCssSubpage ) {
- $wgOut->wrapWikiMsg( "<div id='mw-usercssyoucanpreview'>\n$1\n</div>", array( 'usercssyoucanpreview' ) );
+ if ( $this->isCssSubpage && $wgAllowUserCss ) {
+ $wgOut->wrapWikiMsg(
+ "<div id='mw-usercssyoucanpreview'>\n$1\n</div>",
+ array( 'usercssyoucanpreview' )
+ );
}
- if ( $this->isJsSubpage ) {
- $wgOut->wrapWikiMsg( "<div id='mw-userjsyoucanpreview'>\n$1\n</div>", array( 'userjsyoucanpreview' ) );
+ if ( $this->isJsSubpage && $wgAllowUserJs ) {
+ $wgOut->wrapWikiMsg(
+ "<div id='mw-userjsyoucanpreview'>\n$1\n</div>",
+ array( 'userjsyoucanpreview' )
+ );
}
}
}
}
- if ( $this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected( 'edit' ) ) {
+ if ( $this->mTitle->isProtected( 'edit' ) &&
+ MWNamespace::getRestrictionLevels( $this->mTitle->getNamespace() ) !== array( '' )
+ ) {
# Is the title semi-protected?
if ( $this->mTitle->isSemiProtected() ) {
$noticeMsg = 'semiprotectedpagewarning';
@@ -2534,16 +2713,27 @@ class EditPage {
if ( $this->tooBig || $this->kblength > $wgMaxArticleSize ) {
$wgOut->wrapWikiMsg( "<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>",
- array( 'longpageerror', $wgLang->formatNum( $this->kblength ), $wgLang->formatNum( $wgMaxArticleSize ) ) );
+ array(
+ 'longpageerror',
+ $wgLang->formatNum( $this->kblength ),
+ $wgLang->formatNum( $wgMaxArticleSize )
+ )
+ );
} else {
if ( !wfMessage( 'longpage-hint' )->isDisabled() ) {
$wgOut->wrapWikiMsg( "<div id='mw-edit-longpage-hint'>\n$1\n</div>",
- array( 'longpage-hint', $wgLang->formatSize( strlen( $this->textbox1 ) ), strlen( $this->textbox1 ) )
+ array(
+ 'longpage-hint',
+ $wgLang->formatSize( strlen( $this->textbox1 ) ),
+ strlen( $this->textbox1 )
+ )
);
}
}
# Add header copyright warning
$this->showHeaderCopyrightWarning();
+
+ return true;
}
/**
@@ -2555,12 +2745,14 @@ class EditPage {
*
* @param string $summary The value of the summary input
* @param string $labelText The html to place inside the label
- * @param array $inputAttrs of attrs to use on the input
- * @param array $spanLabelAttrs of attrs to use on the span inside the label
+ * @param array $inputAttrs Array of attrs to use on the input
+ * @param array $spanLabelAttrs Array of attrs to use on the span inside the label
*
* @return array An array in the format array( $label, $input )
*/
- function getSummaryInput( $summary = "", $labelText = null, $inputAttrs = null, $spanLabelAttrs = null ) {
+ function getSummaryInput( $summary = "", $labelText = null,
+ $inputAttrs = null, $spanLabelAttrs = null
+ ) {
// Note: the maxlength is overridden in JS to 255 and to make it use UTF-8 bytes, not characters.
$inputAttrs = ( is_array( $inputAttrs ) ? $inputAttrs : array() ) + array(
'id' => 'wpSummary',
@@ -2577,7 +2769,11 @@ class EditPage {
$label = null;
if ( $labelText ) {
- $label = Xml::tags( 'label', $inputAttrs['id'] ? array( 'for' => $inputAttrs['id'] ) : null, $labelText );
+ $label = Xml::tags(
+ 'label',
+ $inputAttrs['id'] ? array( 'for' => $inputAttrs['id'] ) : null,
+ $labelText
+ );
$label = Xml::tags( 'span', $spanLabelAttrs, $label );
}
@@ -2587,11 +2783,10 @@ class EditPage {
}
/**
- * @param $isSubjectPreview Boolean: true if this is the section subject/title
- * up top, or false if this is the comment summary
- * down below the textarea
+ * @param bool $isSubjectPreview True if this is the section subject/title
+ * up top, or false if this is the comment summary
+ * down below the textarea
* @param string $summary The text of the summary to display
- * @return String
*/
protected function showSummaryInput( $isSubjectPreview, $summary = "" ) {
global $wgOut, $wgContLang;
@@ -2608,16 +2803,21 @@ class EditPage {
}
$summary = $wgContLang->recodeForEdit( $summary );
$labelText = wfMessage( $isSubjectPreview ? 'subject' : 'summary' )->parse();
- list( $label, $input ) = $this->getSummaryInput( $summary, $labelText, array( 'class' => $summaryClass ), array() );
+ list( $label, $input ) = $this->getSummaryInput(
+ $summary,
+ $labelText,
+ array( 'class' => $summaryClass ),
+ array()
+ );
$wgOut->addHTML( "{$label} {$input}" );
}
/**
- * @param $isSubjectPreview Boolean: true if this is the section subject/title
- * up top, or false if this is the comment summary
- * down below the textarea
- * @param string $summary the text of the summary to display
- * @return String
+ * @param bool $isSubjectPreview True if this is the section subject/title
+ * up top, or false if this is the comment summary
+ * down below the textarea
+ * @param string $summary The text of the summary to display
+ * @return string
*/
protected function getSummaryPreview( $isSubjectPreview, $summary = "" ) {
// avoid spaces in preview, gets always trimmed on save
@@ -2635,7 +2835,8 @@ class EditPage {
$message = $isSubjectPreview ? 'subject-preview' : 'summary-preview';
- $summary = wfMessage( $message )->parse() . Linker::commentBlock( $summary, $this->mTitle, $isSubjectPreview );
+ $summary = wfMessage( $message )->parse()
+ . Linker::commentBlock( $summary, $this->mTitle, $isSubjectPreview );
return Xml::tags( 'div', array( 'class' => 'mw-summary-preview' ), $summary );
}
@@ -2689,15 +2890,17 @@ HTML
* The $textoverride method can be used by subclasses overriding showContentForm
* to pass back to this method.
*
- * @param array $customAttribs of html attributes to use in the textarea
- * @param string $textoverride optional text to override $this->textarea1 with
+ * @param array $customAttribs Array of html attributes to use in the textarea
+ * @param string $textoverride Optional text to override $this->textarea1 with
*/
protected function showTextbox1( $customAttribs = null, $textoverride = null ) {
if ( $this->wasDeletedSinceLastEdit() && $this->formtype == 'save' ) {
$attribs = array( 'style' => 'display:none;' );
} else {
$classes = array(); // Textarea CSS
- if ( $this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected( 'edit' ) ) {
+ if ( $this->mTitle->isProtected( 'edit' ) &&
+ MWNamespace::getRestrictionLevels( $this->mTitle->getNamespace() ) !== array( '' )
+ ) {
# Is the title semi-protected?
if ( $this->mTitle->isSemiProtected() ) {
$classes[] = 'mw-textarea-sprotected';
@@ -2725,7 +2928,11 @@ HTML
}
}
- $this->showTextbox( $textoverride !== null ? $textoverride : $this->textbox1, 'wpTextbox1', $attribs );
+ $this->showTextbox(
+ $textoverride !== null ? $textoverride : $this->textbox1,
+ 'wpTextbox1',
+ $attribs
+ );
}
protected function showTextbox2() {
@@ -2749,7 +2956,9 @@ HTML
'id' => $name,
'cols' => $wgUser->getIntOption( 'cols' ),
'rows' => $wgUser->getIntOption( 'rows' ),
- 'style' => '' // avoid php notices when appending preferences (appending allows customAttribs['style'] to still work
+ // Avoid PHP notices when appending preferences
+ // (appending allows customAttribs['style'] to still work).
+ 'style' => ''
);
$pageLang = $this->mTitle->getPageLanguage();
@@ -2784,7 +2993,12 @@ HTML
try {
$this->showDiff();
} catch ( MWContentSerializationException $ex ) {
- $msg = wfMessage( 'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() );
+ $msg = wfMessage(
+ 'content-failed-to-parse',
+ $this->contentModel,
+ $this->contentFormat,
+ $ex->getMessage()
+ );
$wgOut->addWikiText( '<div class="error">' . $msg->text() . '</div>' );
}
}
@@ -2794,7 +3008,7 @@ HTML
* Append preview output to $wgOut.
* Includes category rendering if this is a category page.
*
- * @param string $text the HTML to be output for the preview.
+ * @param string $text The HTML to be output for the preview.
*/
protected function showPreview( $text ) {
global $wgOut;
@@ -2914,6 +3128,7 @@ HTML
* Get the copyright warning
*
* Renamed to getCopyrightWarning(), old name kept around for backwards compatibility
+ * @return string
*/
protected function getCopywarn() {
return self::getCopyrightWarning( $this->mTitle );
@@ -2923,8 +3138,7 @@ HTML
* Get the copyright warning, by default returns wikitext
*
* @param Title $title
- * @param string $format output format, valid values are any function of
- * a Message object
+ * @param string $format Output format, valid values are any function of a Message object
* @return string
*/
public static function getCopyrightWarning( $title, $format = 'plain' ) {
@@ -2972,7 +3186,7 @@ HTML
foreach ( $output->getLimitReportData() as $key => $value ) {
if ( wfRunHooks( 'ParserLimitReportFormat',
- array( $key, $value, &$limitReport, true, true )
+ array( $key, &$value, &$limitReport, true, true )
) ) {
$keyMsg = wfMessage( $key );
$valueMsg = wfMessage( array( "$key-value-html", "$key-value" ) );
@@ -2998,7 +3212,7 @@ HTML
}
protected function showStandardInputs( &$tabindex = 2 ) {
- global $wgOut;
+ global $wgOut, $wgUseMediaWikiUIEverywhere;
$wgOut->addHTML( "<div class='editOptions'>\n" );
if ( $this->section != 'new' ) {
@@ -3023,14 +3237,26 @@ HTML
array( 'class' => 'mw-editButtons-pipe-separator' ),
wfMessage( 'pipe-separator' )->text() );
}
- $edithelpurl = Skin::makeInternalOrExternalUrl( wfMessage( 'edithelppage' )->inContentLanguage()->text() );
- $edithelp = '<a target="helpwindow" href="' . $edithelpurl . '">' .
- wfMessage( 'edithelp' )->escaped() . '</a> ' .
+
+ $message = wfMessage( 'edithelppage' )->inContentLanguage()->text();
+ $edithelpurl = Skin::makeInternalOrExternalUrl( $message );
+ $attrs = array(
+ 'target' => 'helpwindow',
+ 'href' => $edithelpurl,
+ );
+ if ( $wgUseMediaWikiUIEverywhere ) {
+ $attrs['class'] = 'mw-ui-button mw-ui-quiet';
+ }
+ $edithelp = Html::element( 'a', $attrs, wfMessage( 'edithelp' )->text() ) .
+ wfMessage( 'word-separator' )->escaped() .
wfMessage( 'newwindow' )->parse();
+
$wgOut->addHTML( " <span class='cancelLink'>{$cancel}</span>\n" );
$wgOut->addHTML( " <span class='editHelp'>{$edithelp}</span>\n" );
$wgOut->addHTML( "</div><!-- editButtons -->\n" );
+
wfRunHooks( 'EditPage::showStandardInputs:options', array( $this, $wgOut, &$tabindex ) );
+
$wgOut->addHTML( "</div><!-- editOptions -->\n" );
}
@@ -3064,15 +3290,20 @@ HTML
* @return string
*/
public function getCancelLink() {
+ global $wgUseMediaWikiUIEverywhere;
$cancelParams = array();
if ( !$this->isConflict && $this->oldid > 0 ) {
$cancelParams['oldid'] = $this->oldid;
}
+ $attrs = array( 'id' => 'mw-editform-cancel' );
+ if ( $wgUseMediaWikiUIEverywhere ) {
+ $attrs['class'] = 'mw-ui-button mw-ui-quiet';
+ }
return Linker::linkKnown(
$this->getContextTitle(),
wfMessage( 'cancel' )->parse(),
- array( 'id' => 'mw-editform-cancel' ),
+ $attrs,
$cancelParams
);
}
@@ -3083,7 +3314,7 @@ HTML
* variable in the constructor is not enough. This can be used when the
* EditPage lives inside of a Special page rather than a custom page action.
*
- * @param $title Title object for which is being edited (where we go to for &action= links)
+ * @param Title $title Title object for which is being edited (where we go to for &action= links)
* @return string
*/
protected function getActionURL( Title $title ) {
@@ -3095,6 +3326,7 @@ HTML
* Note that we rely on the logging table, which hasn't been always there,
* but that doesn't matter, because this only applies to brand new
* deletes.
+ * @return bool
*/
protected function wasDeletedSinceLastEdit() {
if ( $this->deletedSinceEdit !== null ) {
@@ -3116,6 +3348,9 @@ HTML
return $this->deletedSinceEdit;
}
+ /**
+ * @return bool|stdClass
+ */
protected function getLastDelete() {
$dbr = wfGetDB( DB_SLAVE );
$data = $dbr->selectRow(
@@ -3151,6 +3386,7 @@ HTML
$data->log_comment = wfMessage( 'rev-deleted-comment' )->escaped();
}
}
+
return $data;
}
@@ -3161,6 +3397,7 @@ HTML
*/
function getPreviewText() {
global $wgOut, $wgUser, $wgRawHtml, $wgLang;
+ global $wgAllowUserCss, $wgAllowUserJs;
wfProfileIn( __METHOD__ );
@@ -3185,23 +3422,28 @@ HTML
$content = $this->toEditContent( $this->textbox1 );
$previewHTML = '';
- if ( !wfRunHooks( 'AlternateEditPreview', array( $this, &$content, &$previewHTML, &$this->mParserOutput ) ) ) {
+ if ( !wfRunHooks(
+ 'AlternateEditPreview',
+ array( $this, &$content, &$previewHTML, &$this->mParserOutput ) )
+ ) {
wfProfileOut( __METHOD__ );
return $previewHTML;
}
+ # provide a anchor link to the editform
+ $continueEditing = '<span class="mw-continue-editing">' .
+ '[[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' .
+ wfMessage( 'continue-editing' )->text() . ']]</span>';
if ( $this->mTriedSave && !$this->mTokenOk ) {
if ( $this->mTokenOkExceptSuffix ) {
$note = wfMessage( 'token_suffix_mismatch' )->plain();
-
} else {
$note = wfMessage( 'session_fail_preview' )->plain();
}
} elseif ( $this->incompleteForm ) {
$note = wfMessage( 'edit_form_incomplete' )->plain();
} else {
- $note = wfMessage( 'previewnote' )->plain() .
- ' [[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' . wfMessage( 'continue-editing' )->text() . ']]';
+ $note = wfMessage( 'previewnote' )->plain() . ' ' . $continueEditing;
}
$parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() );
@@ -3221,8 +3463,14 @@ HTML
if ( $content->getModel() == CONTENT_MODEL_CSS ) {
$format = 'css';
+ if ( $level === 'user' && !$wgAllowUserCss ) {
+ $format = false;
+ }
} elseif ( $content->getModel() == CONTENT_MODEL_JAVASCRIPT ) {
$format = 'js';
+ if ( $level === 'user' && !$wgAllowUserJs ) {
+ $format = false;
+ }
} else {
$format = false;
}
@@ -3230,49 +3478,55 @@ HTML
# Used messages to make sure grep find them:
# Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
if ( $level && $format ) {
- $note = "<div id='mw-{$level}{$format}preview'>" . wfMessage( "{$level}{$format}preview" )->text() . "</div>";
+ $note = "<div id='mw-{$level}{$format}preview'>" .
+ wfMessage( "{$level}{$format}preview" )->text() .
+ ' ' . $continueEditing . "</div>";
}
}
- $rt = $content->getRedirectChain();
- if ( $rt ) {
- $previewHTML = $this->mArticle->viewRedirect( $rt, false );
- } else {
-
- # If we're adding a comment, we need to show the
- # summary as the headline
- if ( $this->section === "new" && $this->summary !== "" ) {
- $content = $content->addSectionHeader( $this->summary );
- }
+ # If we're adding a comment, we need to show the
+ # summary as the headline
+ if ( $this->section === "new" && $this->summary !== "" ) {
+ $content = $content->addSectionHeader( $this->summary );
+ }
- $hook_args = array( $this, &$content );
- ContentHandler::runLegacyHooks( 'EditPageGetPreviewText', $hook_args );
- wfRunHooks( 'EditPageGetPreviewContent', $hook_args );
+ $hook_args = array( $this, &$content );
+ ContentHandler::runLegacyHooks( 'EditPageGetPreviewText', $hook_args );
+ wfRunHooks( 'EditPageGetPreviewContent', $hook_args );
- $parserOptions->enableLimitReport();
+ $parserOptions->enableLimitReport();
- # For CSS/JS pages, we should have called the ShowRawCssJs hook here.
- # But it's now deprecated, so never mind
+ # For CSS/JS pages, we should have called the ShowRawCssJs hook here.
+ # But it's now deprecated, so never mind
- $content = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
- $parserOutput = $content->getParserOutput( $this->getArticle()->getTitle(), null, $parserOptions );
+ $content = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
+ $parserOutput = $content->getParserOutput(
+ $this->getArticle()->getTitle(),
+ null,
+ $parserOptions
+ );
- $previewHTML = $parserOutput->getText();
- $this->mParserOutput = $parserOutput;
- $wgOut->addParserOutputNoText( $parserOutput );
+ $previewHTML = $parserOutput->getText();
+ $this->mParserOutput = $parserOutput;
+ $wgOut->addParserOutputMetadata( $parserOutput );
- if ( count( $parserOutput->getWarnings() ) ) {
- $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
- }
+ if ( count( $parserOutput->getWarnings() ) ) {
+ $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
}
} catch ( MWContentSerializationException $ex ) {
- $m = wfMessage( 'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() );
+ $m = wfMessage(
+ 'content-failed-to-parse',
+ $this->contentModel,
+ $this->contentFormat,
+ $ex->getMessage()
+ );
$note .= "\n\n" . $m->parse();
$previewHTML = '';
}
if ( $this->isConflict ) {
- $conflict = '<h2 id="mw-previewconflict">' . wfMessage( 'previewconflict' )->escaped() . "</h2>\n";
+ $conflict = '<h2 id="mw-previewconflict">'
+ . wfMessage( 'previewconflict' )->escaped() . "</h2>\n";
} else {
$conflict = '<hr />';
}
@@ -3291,7 +3545,7 @@ HTML
}
/**
- * @return Array
+ * @return array
*/
function getTemplates() {
if ( $this->preview || $this->section != '' ) {
@@ -3313,128 +3567,94 @@ HTML
/**
* Shows a bulletin board style toolbar for common editing functions.
* It can be disabled in the user preferences.
- * The necessary JavaScript code can be found in skins/common/edit.js.
*
* @return string
*/
static function getEditToolbar() {
- global $wgStylePath, $wgContLang, $wgLang, $wgOut;
- global $wgUseTeX, $wgEnableUploads, $wgForeignFileRepos;
+ global $wgContLang, $wgOut;
+ global $wgEnableUploads, $wgForeignFileRepos;
$imagesAvailable = $wgEnableUploads || count( $wgForeignFileRepos );
/**
* $toolarray is an array of arrays each of which includes the
- * filename of the button image (without path), the opening
- * tag, the closing tag, optionally a sample text that is
+ * opening tag, the closing tag, optionally a sample text that is
* inserted between the two when no selection is highlighted
* and. The tip text is shown when the user moves the mouse
* over the button.
*
- * Also here: accesskeys (key), which are not used yet until
- * someone can figure out a way to make them work in
- * IE. However, we should make sure these keys are not defined
- * on the edit page.
+ * Images are defined in ResourceLoaderEditToolbarModule.
*/
$toolarray = array(
array(
- 'image' => $wgLang->getImageFile( 'button-bold' ),
'id' => 'mw-editbutton-bold',
'open' => '\'\'\'',
'close' => '\'\'\'',
'sample' => wfMessage( 'bold_sample' )->text(),
'tip' => wfMessage( 'bold_tip' )->text(),
- 'key' => 'B'
),
array(
- 'image' => $wgLang->getImageFile( 'button-italic' ),
'id' => 'mw-editbutton-italic',
'open' => '\'\'',
'close' => '\'\'',
'sample' => wfMessage( 'italic_sample' )->text(),
'tip' => wfMessage( 'italic_tip' )->text(),
- 'key' => 'I'
),
array(
- 'image' => $wgLang->getImageFile( 'button-link' ),
'id' => 'mw-editbutton-link',
'open' => '[[',
'close' => ']]',
'sample' => wfMessage( 'link_sample' )->text(),
'tip' => wfMessage( 'link_tip' )->text(),
- 'key' => 'L'
),
array(
- 'image' => $wgLang->getImageFile( 'button-extlink' ),
'id' => 'mw-editbutton-extlink',
'open' => '[',
'close' => ']',
'sample' => wfMessage( 'extlink_sample' )->text(),
'tip' => wfMessage( 'extlink_tip' )->text(),
- 'key' => 'X'
),
array(
- 'image' => $wgLang->getImageFile( 'button-headline' ),
'id' => 'mw-editbutton-headline',
'open' => "\n== ",
'close' => " ==\n",
'sample' => wfMessage( 'headline_sample' )->text(),
'tip' => wfMessage( 'headline_tip' )->text(),
- 'key' => 'H'
),
$imagesAvailable ? array(
- 'image' => $wgLang->getImageFile( 'button-image' ),
'id' => 'mw-editbutton-image',
'open' => '[[' . $wgContLang->getNsText( NS_FILE ) . ':',
'close' => ']]',
'sample' => wfMessage( 'image_sample' )->text(),
'tip' => wfMessage( 'image_tip' )->text(),
- 'key' => 'D',
) : false,
$imagesAvailable ? array(
- 'image' => $wgLang->getImageFile( 'button-media' ),
'id' => 'mw-editbutton-media',
'open' => '[[' . $wgContLang->getNsText( NS_MEDIA ) . ':',
'close' => ']]',
'sample' => wfMessage( 'media_sample' )->text(),
'tip' => wfMessage( 'media_tip' )->text(),
- 'key' => 'M'
- ) : false,
- $wgUseTeX ? array(
- 'image' => $wgLang->getImageFile( 'button-math' ),
- 'id' => 'mw-editbutton-math',
- 'open' => "<math>",
- 'close' => "</math>",
- 'sample' => wfMessage( 'math_sample' )->text(),
- 'tip' => wfMessage( 'math_tip' )->text(),
- 'key' => 'C'
) : false,
array(
- 'image' => $wgLang->getImageFile( 'button-nowiki' ),
'id' => 'mw-editbutton-nowiki',
'open' => "<nowiki>",
'close' => "</nowiki>",
'sample' => wfMessage( 'nowiki_sample' )->text(),
'tip' => wfMessage( 'nowiki_tip' )->text(),
- 'key' => 'N'
),
array(
- 'image' => $wgLang->getImageFile( 'button-sig' ),
'id' => 'mw-editbutton-signature',
'open' => '--~~~~',
'close' => '',
'sample' => '',
'tip' => wfMessage( 'sig_tip' )->text(),
- 'key' => 'Y'
),
array(
- 'image' => $wgLang->getImageFile( 'button-hr' ),
'id' => 'mw-editbutton-hr',
'open' => "\n----\n",
'close' => '',
'sample' => '',
'tip' => wfMessage( 'hr_tip' )->text(),
- 'key' => 'R'
)
);
@@ -3445,16 +3665,17 @@ HTML
}
$params = array(
- $image = $wgStylePath . '/common/images/' . $tool['image'],
+ // Images are defined in ResourceLoaderEditToolbarModule
+ false,
// Note that we use the tip both for the ALT tag and the TITLE tag of the image.
// Older browsers show a "speedtip" type message only for ALT.
// Ideally these should be different, realistically they
// probably don't need to be.
- $tip = $tool['tip'],
- $open = $tool['open'],
- $close = $tool['close'],
- $sample = $tool['sample'],
- $cssId = $tool['id'],
+ $tool['tip'],
+ $tool['open'],
+ $tool['close'],
+ $tool['sample'],
+ $tool['id'],
);
$script .= Xml::encodeJsCall( 'mw.toolbar.addButton', $params );
@@ -3481,13 +3702,13 @@ HTML
* minor and watch
*
* @param int $tabindex Current tabindex
- * @param array $checked of checkbox => bool, where bool indicates the checked
+ * @param array $checked Array of checkbox => bool, where bool indicates the checked
* status of the checkbox
*
* @return array
*/
public function getCheckboxes( &$tabindex, $checked ) {
- global $wgUser;
+ global $wgUser, $wgUseMediaWikiUIEverywhere;
$checkboxes = array();
@@ -3501,11 +3722,19 @@ HTML
'accesskey' => wfMessage( 'accesskey-minoredit' )->text(),
'id' => 'wpMinoredit',
);
- $checkboxes['minor'] =
+ $minorEditHtml =
Xml::check( 'wpMinoredit', $checked['minor'], $attribs ) .
"&#160;<label for='wpMinoredit' id='mw-editpage-minoredit'" .
Xml::expandAttributes( array( 'title' => Linker::titleAttrib( 'minoredit', 'withaccess' ) ) ) .
">{$minorLabel}</label>";
+
+ if ( $wgUseMediaWikiUIEverywhere ) {
+ $checkboxes['minor'] = Html::openElement( 'div', array( 'class' => 'mw-ui-checkbox' ) ) .
+ $minorEditHtml .
+ Html::closeElement( 'div' );
+ } else {
+ $checkboxes['minor'] = $minorEditHtml;
+ }
}
}
@@ -3517,11 +3746,18 @@ HTML
'accesskey' => wfMessage( 'accesskey-watch' )->text(),
'id' => 'wpWatchthis',
);
- $checkboxes['watch'] =
+ $watchThisHtml =
Xml::check( 'wpWatchthis', $checked['watch'], $attribs ) .
"&#160;<label for='wpWatchthis' id='mw-editpage-watch'" .
Xml::expandAttributes( array( 'title' => Linker::titleAttrib( 'watch', 'withaccess' ) ) ) .
">{$watchLabel}</label>";
+ if ( $wgUseMediaWikiUIEverywhere ) {
+ $checkboxes['watch'] = Html::openElement( 'div', array( 'class' => 'mw-ui-checkbox' ) ) .
+ $watchThisHtml .
+ Html::closeElement( 'div' );
+ } else {
+ $checkboxes['watch'] = $watchThisHtml;
+ }
}
wfRunHooks( 'EditPageBeforeEditChecks', array( &$this, &$checkboxes, &$tabindex ) );
return $checkboxes;
@@ -3536,42 +3772,47 @@ HTML
* @return array
*/
public function getEditButtons( &$tabindex ) {
+ global $wgUseMediaWikiUIEverywhere;
+
$buttons = array();
- $temp = array(
+ $attribs = array(
'id' => 'wpSave',
'name' => 'wpSave',
'type' => 'submit',
'tabindex' => ++$tabindex,
'value' => wfMessage( 'savearticle' )->text(),
- 'accesskey' => wfMessage( 'accesskey-save' )->text(),
- 'title' => wfMessage( 'tooltip-save' )->text() . ' [' . wfMessage( 'accesskey-save' )->text() . ']',
- );
- $buttons['save'] = Xml::element( 'input', $temp, '' );
+ ) + Linker::tooltipAndAccesskeyAttribs( 'save' );
+ if ( $wgUseMediaWikiUIEverywhere ) {
+ $attribs['class'] = 'mw-ui-button mw-ui-constructive';
+ }
+ $buttons['save'] = Xml::element( 'input', $attribs, '' );
++$tabindex; // use the same for preview and live preview
- $temp = array(
+ $attribs = array(
'id' => 'wpPreview',
'name' => 'wpPreview',
'type' => 'submit',
'tabindex' => $tabindex,
'value' => wfMessage( 'showpreview' )->text(),
- 'accesskey' => wfMessage( 'accesskey-preview' )->text(),
- 'title' => wfMessage( 'tooltip-preview' )->text() . ' [' . wfMessage( 'accesskey-preview' )->text() . ']',
- );
- $buttons['preview'] = Xml::element( 'input', $temp, '' );
+ ) + Linker::tooltipAndAccesskeyAttribs( 'preview' );
+ if ( $wgUseMediaWikiUIEverywhere ) {
+ $attribs['class'] = 'mw-ui-button mw-ui-progressive';
+ }
+ $buttons['preview'] = Xml::element( 'input', $attribs, '' );
$buttons['live'] = '';
- $temp = array(
+ $attribs = array(
'id' => 'wpDiff',
'name' => 'wpDiff',
'type' => 'submit',
'tabindex' => ++$tabindex,
'value' => wfMessage( 'showdiff' )->text(),
- 'accesskey' => wfMessage( 'accesskey-diff' )->text(),
- 'title' => wfMessage( 'tooltip-diff' )->text() . ' [' . wfMessage( 'accesskey-diff' )->text() . ']',
- );
- $buttons['diff'] = Xml::element( 'input', $temp, '' );
+ ) + Linker::tooltipAndAccesskeyAttribs( 'diff' );
+ if ( $wgUseMediaWikiUIEverywhere ) {
+ $attribs['class'] = 'mw-ui-button mw-ui-progressive';
+ }
+ $buttons['diff'] = Xml::element( 'input', $attribs, '' );
wfRunHooks( 'EditPageBeforeEditButtons', array( &$this, &$buttons, &$tabindex ) );
return $buttons;
@@ -3599,49 +3840,15 @@ HTML
#$categories = $skin->getCategoryLinks();
$s =
- '<?xml version="1.0" encoding="UTF-8" ?>' . "\n" .
- Xml::tags( 'livepreview', null,
- Xml::element( 'preview', null, $previewText )
- #. Xml::element( 'category', null, $categories )
- );
+ '<?xml version="1.0" encoding="UTF-8" ?>' . "\n" .
+ Xml::tags( 'livepreview', null,
+ Xml::element( 'preview', null, $previewText )
+ #. Xml::element( 'category', null, $categories )
+ );
echo $s;
}
/**
- * Call the stock "user is blocked" page
- *
- * @deprecated in 1.19; throw an exception directly instead
- */
- function blockedPage() {
- wfDeprecated( __METHOD__, '1.19' );
- global $wgUser;
-
- throw new UserBlockedError( $wgUser->getBlock() );
- }
-
- /**
- * Produce the stock "please login to edit pages" page
- *
- * @deprecated in 1.19; throw an exception directly instead
- */
- function userNotLoggedInPage() {
- wfDeprecated( __METHOD__, '1.19' );
- throw new PermissionsError( 'edit' );
- }
-
- /**
- * Show an error page saying to the user that he has insufficient permissions
- * to create a new page
- *
- * @deprecated in 1.19; throw an exception directly instead
- */
- function noCreatePermission() {
- wfDeprecated( __METHOD__, '1.19' );
- $permission = $this->mTitle->isTalkPage() ? 'createtalk' : 'createpage';
- throw new PermissionsError( $permission );
- }
-
- /**
* Creates a basic error page which informs the user that
* they have attempted to edit a nonexistent section.
*/
@@ -3658,32 +3865,9 @@ HTML
}
/**
- * Produce the stock "your edit contains spam" page
- *
- * @param string|bool $match Text which triggered one or more filters
- * @deprecated since 1.17 Use method spamPageWithContent() instead
- */
- static function spamPage( $match = false ) {
- wfDeprecated( __METHOD__, '1.17' );
-
- global $wgOut, $wgTitle;
-
- $wgOut->prepareErrorPage( wfMessage( 'spamprotectiontitle' ) );
-
- $wgOut->addHTML( '<div id="spamprotected">' );
- $wgOut->addWikiMsg( 'spamprotectiontext' );
- if ( $match ) {
- $wgOut->addWikiMsg( 'spamprotectionmatch', wfEscapeWikiText( $match ) );
- }
- $wgOut->addHTML( '</div>' );
-
- $wgOut->returnToMain( false, $wgTitle );
- }
-
- /**
* Show "your edit contains spam" page with your diff and text
*
- * @param $match string|Array|bool Text (or array of texts) which triggered one or more filters
+ * @param string|array|bool $match Text (or array of texts) which triggered one or more filters
*/
public function spamPageWithContent( $match = false ) {
global $wgOut, $wgLang;
@@ -3711,24 +3895,12 @@ HTML
}
/**
- * Format an anchor fragment as it would appear for a given section name
- * @param $text String
- * @return String
- * @private
- */
- function sectionAnchor( $text ) {
- global $wgParser;
- return $wgParser->guessSectionNameFromWikiText( $text );
- }
-
- /**
* Check if the browser is on a blacklist of user-agents known to
* mangle UTF-8 data on form submission. Returns true if Unicode
* should make it through, false if it's known to be a problem.
* @return bool
- * @private
*/
- function checkUnicodeCompliantBrowser() {
+ private function checkUnicodeCompliantBrowser() {
global $wgBrowserBlackList, $wgRequest;
$currentbrowser = $wgRequest->getHeader( 'User-Agent' );
@@ -3749,27 +3921,14 @@ HTML
* Filter an input field through a Unicode de-armoring process if it
* came from an old browser with known broken Unicode editing issues.
*
- * @param $request WebRequest
- * @param $field String
- * @return String
- * @private
- */
- function safeUnicodeInput( $request, $field ) {
- $text = rtrim( $request->getText( $field ) );
- return $request->getBool( 'safemode' )
- ? $this->unmakesafe( $text )
- : $text;
- }
-
- /**
- * @param $request WebRequest
- * @param $text string
+ * @param WebRequest $request
+ * @param string $field
* @return string
*/
- function safeUnicodeText( $request, $text ) {
- $text = rtrim( $text );
+ protected function safeUnicodeInput( $request, $field ) {
+ $text = rtrim( $request->getText( $field ) );
return $request->getBool( 'safemode' )
- ? $this->unmakesafe( $text )
+ ? $this->unmakeSafe( $text )
: $text;
}
@@ -3777,16 +3936,15 @@ HTML
* Filter an output field through a Unicode armoring process if it is
* going to an old browser with known broken Unicode editing issues.
*
- * @param $text String
- * @return String
- * @private
+ * @param string $text
+ * @return string
*/
- function safeUnicodeOutput( $text ) {
+ protected function safeUnicodeOutput( $text ) {
global $wgContLang;
$codedText = $wgContLang->recodeForEdit( $text );
return $this->checkUnicodeCompliantBrowser()
? $codedText
- : $this->makesafe( $codedText );
+ : $this->makeSafe( $codedText );
}
/**
@@ -3798,18 +3956,18 @@ HTML
* Preexisting such character references will have a 0 added to them
* to ensure that round-trips do not alter the original data.
*
- * @param $invalue String
- * @return String
- * @private
+ * @param string $invalue
+ * @return string
*/
- function makesafe( $invalue ) {
+ private function makeSafe( $invalue ) {
// Armor existing references for reversibility.
$invalue = strtr( $invalue, array( "&#x" => "&#x0" ) );
$bytesleft = 0;
$result = "";
$working = 0;
- for ( $i = 0; $i < strlen( $invalue ); $i++ ) {
+ $valueLength = strlen( $invalue );
+ for ( $i = 0; $i < $valueLength; $i++ ) {
$bytevalue = ord( $invalue[$i] );
if ( $bytevalue <= 0x7F ) { // 0xxx xxxx
$result .= chr( $bytevalue );
@@ -3840,13 +3998,13 @@ HTML
* back to UTF-8. Used to protect data from corruption by broken web browsers
* as listed in $wgBrowserBlackList.
*
- * @param $invalue String
- * @return String
- * @private
+ * @param string $invalue
+ * @return string
*/
- function unmakesafe( $invalue ) {
+ private function unmakeSafe( $invalue ) {
$result = "";
- for ( $i = 0; $i < strlen( $invalue ); $i++ ) {
+ $valueLength = strlen( $invalue );
+ for ( $i = 0; $i < $valueLength; $i++ ) {
if ( ( substr( $invalue, $i, 3 ) == "&#x" ) && ( $invalue[$i + 3] != '0' ) ) {
$i += 3;
$hexstring = "";