*/
class CommentStore {
+ /** Maximum length of a comment. Longer comments will be truncated. */
+ const MAX_COMMENT_LENGTH = 65535;
+
+ /** Maximum length of serialized data. Longer data will result in an exception. */
+ const MAX_DATA_LENGTH = 65535;
+
/**
* Define fields that use temporary tables for transitional purposes
* @var array Keys are '$key', values are arrays with four fields:
/** @var array|null Cache for `self::getJoin()` */
protected $joinCache = null;
+ /** @var Language Language to use for comment truncation */
+ protected $lang;
+
/**
* @param string $key A key such as "rev_comment" identifying the comment
* field being fetched.
+ * @param Language $lang Language to use for comment truncation. Defaults
+ * to $wgContLang.
*/
- public function __construct( $key ) {
- global $wgCommentTableSchemaMigrationStage;
+ public function __construct( $key, Language $lang = null ) {
+ global $wgCommentTableSchemaMigrationStage, $wgContLang;
$this->key = $key;
$this->stage = $wgCommentTableSchemaMigrationStage;
+ $this->lang = $lang ?: $wgContLang;
}
/**
}
}
+ # Truncate comment in a Unicode-sensitive manner
+ $comment->text = $this->lang->truncate( $comment->text, self::MAX_COMMENT_LENGTH );
+
if ( $this->stage > MIGRATION_OLD && !$comment->id ) {
$dbData = $comment->data;
if ( !$comment->message instanceof RawMessage ) {
}
if ( $dbData !== null ) {
$dbData = FormatJson::encode( (object)$dbData, false, FormatJson::ALL_OK );
+ $len = strlen( $dbData );
+ if ( $len > self::MAX_DATA_LENGTH ) {
+ $max = self::MAX_DATA_LENGTH;
+ throw new OverflowException( "Comment data is too long ($len bytes, maximum is $max)" );
+ }
}
$hash = self::hash( $comment->text, $dbData );
$comment = $this->createComment( $dbw, $comment, $data );
if ( $this->stage <= MIGRATION_WRITE_BOTH ) {
- $fields[$this->key] = $comment->text;
+ $fields[$this->key] = $this->lang->truncate( $comment->text, 255 );
}
if ( $this->stage >= MIGRATION_WRITE_BOTH ) {
* @throws ErrorPageError
*/
public function importFormData( &$request ) {
- global $wgContLang, $wgUser;
+ global $wgUser;
# Section edit can come from either the form or a link
$this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
}
}
- # Truncate for whole multibyte characters
- $this->summary = $wgContLang->truncate( $request->getText( 'wpSummary' ), 255 );
+ $this->summary = $request->getText( 'wpSummary' );
# If the summary consists of a heading, e.g. '==Foobar==', extract the title from the
# header syntax, e.g. 'Foobar'. This is mainly an issue when we are using wpSummary for
# currently doing double duty as both edit summary and section title. Right now this
# is just to allow API edits to work around this limitation, but this should be
# incorporated into the actual edit form when EditPage is rewritten (Bugs 18654, 26312).
- $this->sectiontitle = $wgContLang->truncate( $request->getText( 'wpSectionTitle' ), 255 );
+ $this->sectiontitle = $request->getText( 'wpSectionTitle' );
$this->sectiontitle = preg_replace( '/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->sectiontitle );
$this->edittime = $request->getVal( 'wpEdittime' );
private function moveToInternal( User $user, &$nt, $reason = '', $createRedirect = true,
array $changeTags = []
) {
- global $wgContLang;
if ( $nt->exists() ) {
$moveOverRedirect = true;
$logType = 'move_redir';
if ( $reason ) {
$comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
}
- # Truncate for whole multibyte characters.
- $comment = $wgContLang->truncate( $comment, 255 );
$dbw = wfGetDB( DB_MASTER );
* @return Revision|null Revision or null on error
*/
public static function newNullRevision( $dbw, $pageId, $summary, $minor, $user = null ) {
- global $wgContentHandlerUseDB, $wgContLang;
+ global $wgContentHandlerUseDB;
$fields = [ 'page_latest', 'page_namespace', 'page_title',
'rev_text_id', 'rev_len', 'rev_sha1' ];
$user = $wgUser;
}
- // Truncate for whole multibyte characters
- $summary = $wgContLang->truncate( $summary, 255 );
-
$row = [
'page' => $pageId,
'user_text' => $user->getName(),
* @param bool $noudp
*/
public function save( $noudp = false ) {
- global $wgPutIPinRC, $wgUseEnotif, $wgShowUpdatedMarker, $wgContLang;
+ global $wgPutIPinRC, $wgUseEnotif, $wgShowUpdatedMarker;
$dbw = wfGetDB( DB_MASTER );
if ( !is_array( $this->mExtra ) ) {
# Trim spaces on user supplied text
$this->mAttribs['rc_comment'] = trim( $this->mAttribs['rc_comment'] );
- # Make sure summary is truncated (whole multibyte characters)
- $this->mAttribs['rc_comment'] = $wgContLang->truncate( $this->mAttribs['rc_comment'], 255 );
-
# Fixup database timestamps
$this->mAttribs['rc_timestamp'] = $dbw->timestamp( $this->mAttribs['rc_timestamp'] );
$this->mAttribs['rc_id'] = $dbw->nextSequenceValue( 'recentchanges_rc_id_seq' );
function upload( $src, $comment, $pageText, $flags = 0, $props = false,
$timestamp = false, $user = null, $tags = []
) {
- global $wgContLang;
-
if ( $this->getRepo()->getReadOnlyReason() !== false ) {
return $this->readOnlyFatalStatus();
}
// Trim spaces on user supplied text
$comment = trim( $comment );
- // Truncate nicely or the DB will do it for us
- // non-nicely (dangling multi-byte chars, non-truncated version in cache).
- $comment = $wgContLang->truncate( $comment, 255 );
$this->lock(); // begin
$status = $this->publish( $src, $flags, $options );
* @throws MWException
*/
public function insert( IDatabase $dbw = null ) {
- global $wgContLang;
-
$dbw = $dbw ?: wfGetDB( DB_MASTER );
$id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
// Trim spaces on user supplied text
$comment = trim( $this->getComment() );
- // Truncate for whole multibyte characters.
- $comment = $wgContLang->truncate( $comment, 255 );
-
$params = $this->getParameters();
$relations = $this->relations;
* @return int The log_id of the inserted log entry
*/
public function addEntry( $action, $target, $comment, $params = [], $doer = null ) {
- global $wgContLang;
-
if ( !is_array( $params ) ) {
$params = [ $params ];
}
# Trim spaces on user supplied text
$comment = trim( $comment );
- # Truncate for whole multibyte characters.
- $comment = $wgContLang->truncate( $comment, 255 );
-
$this->action = $action;
$this->target = $target;
$this->comment = $comment;
public function doUpdateRestrictions( array $limit, array $expiry,
&$cascade, $reason, User $user, $tags = null
) {
- global $wgCascadingRestrictionLevels, $wgContLang;
+ global $wgCascadingRestrictionLevels;
if ( wfReadOnly() ) {
return Status::newFatal( wfMessage( 'readonlytext', wfReadOnlyReason() ) );
$logAction = 'protect';
}
- // Truncate for whole multibyte characters
- $reason = $wgContLang->truncate( $reason, 255 );
-
$logRelationsValues = [];
$logRelationsField = null;
$logParamsDetails = [];
// Trim spaces on user supplied text
$summary = trim( $summary );
- // Truncate for whole multibyte characters.
- $summary = $wgContLang->truncate( $summary, 255 );
-
// Save
$flags = EDIT_UPDATE | EDIT_INTERNAL;
* @return bool|string
*/
public static function processForm( array $data, IContextSource $context ) {
- global $wgBlockAllowsUTEdit, $wgHideUserContribLimit, $wgContLang;
+ global $wgBlockAllowsUTEdit, $wgHideUserContribLimit;
$performer = $context->getUser();
$block = new Block();
$block->setTarget( $target );
$block->setBlocker( $performer );
- # Truncate reason for whole multibyte characters
- $block->mReason = $wgContLang->truncate( $data['Reason'][0], 255 );
+ $block->mReason = $data['Reason'][0];
$block->mExpiry = $expiryTime;
$block->prevents( 'createaccount', $data['CreateAccount'] );
$block->prevents( 'editownusertalk', ( !$wgBlockAllowsUTEdit || $data['DisableUTEdit'] ) );
}
public function onSubmit( array $data ) {
- global $wgContLang;
-
if ( $data['pagetitle'] === '' ) {
// Initial form view of special page, pass
return false;
if ( $data['reason'] !== '' ) {
$reason .= $this->msg( 'colon-separator' )->inContentLanguage()->text() . $data['reason'];
}
- # Truncate for whole multibyte characters.
- $reason = $wgContLang->truncate( $reason, 255 );
// Run edit filters
$derivativeContext = new DerivativeContext( $this->getContext() );
$this->assertTrue( is_callable( $callback ) );
}
+ public function testInsertTruncation() {
+ $comment = str_repeat( '💣', 16400 );
+ $truncated1 = str_repeat( '💣', 63 ) . '...';
+ $truncated2 = str_repeat( '💣', 16383 ) . '...';
+
+ $store = $this->makeStore( MIGRATION_WRITE_BOTH, 'ipb_reason' );
+ $fields = $store->insert( $this->db, $comment );
+ $this->assertSame( $truncated1, $fields['ipb_reason'] );
+ $stored = $this->db->selectField(
+ 'comment', 'comment_text', [ 'comment_id' => $fields['ipb_reason_id'] ], __METHOD__
+ );
+ $this->assertSame( $truncated2, $stored );
+ }
+
+ /**
+ * @expectedException OverflowException
+ * @expectedExceptionMessage Comment data is too long (65611 bytes, maximum is 65535)
+ */
+ public function testInsertTooMuchData() {
+ $store = $this->makeStore( MIGRATION_WRITE_BOTH, 'ipb_reason' );
+ $store->insert( $this->db, 'foo', [
+ 'long' => str_repeat( '💣', 16400 )
+ ] );
+ }
+
public function testConstructor() {
$this->assertInstanceOf( CommentStore::class, CommentStore::newKey( 'dummy' ) );
}