Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F102062
code
Grondin
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Authored By
Grondin
Mar 20 2015, 7:02 PM
2015-03-20 19:02:06 (UTC+0)
Size
16 KB
Referenced Files
None
Subscribers
None
code
View Options
<?php
/**
* Implements Special:MultiUpload
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup SpecialPage
* @ingroup Upload
*/
if
(
!
defined
(
'MEDIAWIKI'
)
)
{
die
();
}
/* use the local, patched version of SpecialUpload.php for now */
global
$wgVersion
;
if
(
version_compare
(
$wgVersion
,
'1.22'
,
'<'
)
)
{
$wgAutoloadLocalClasses
[
'SpecialUpload'
]
=
$wgAutoloadLocalClasses
[
'UploadForm'
]
=
$wgAutoloadLocalClasses
[
'UploadSourceField'
]
=
__DIR__
.
'/SpecialUpload.1.21.3.php'
;
}
else
{
$wgAutoloadLocalClasses
[
'SpecialUpload'
]
=
$wgAutoloadLocalClasses
[
'UploadForm'
]
=
$wgAutoloadLocalClasses
[
'UploadSourceField'
]
=
__DIR__
.
'/SpecialUpload.php'
;
}
/**
* Special page for uploading multiple files in one submission.
*/
class
SpecialMultiUpload
extends
SpecialUpload
{
function
__construct
(
$request
=
null
)
{
SpecialPage
::
__construct
(
'MultiUpload'
,
'upload'
);
}
public
$mFrom
;
// which numbered rows to display, process
public
$mTo
;
public
$mRows
;
/**
* Under which header this special page is listed in Special:SpecialPages
*
* @return string
*/
protected
function
getGroupName
()
{
return
'media'
;
}
protected
function
handleRequestData
()
{
$request
=
$this
->
getRequest
();
$this
->
checkToken
(
$request
);
$this
->
mFrom
=
1
;
# $request->getVal( 'from', 1 );
global
$wgMultiUploadInitialNumberOfImportRows
;
$this
->
mTo
=
$request
->
getVal
(
'wpLastRowIndex'
,
$wgMultiUploadInitialNumberOfImportRows
);
# stick an invisible template row in front
$this
->
mRows
=
array
(
$this
->
createRow
(
'template'
),
);
$i
=
$this
->
mFrom
;
while
(
$i
<=
$this
->
mTo
)
{
$this
->
mRows
[]
=
$row
=
$this
->
createRow
(
$i
);
$row
->
handleRequestData
();
$i
++;
}
$this
->
showUploadForm
(
$this
->
getUploadForm
()
);
}
protected
function
createRow
(
$i
)
{
$row
=
new
UploadRow
(
$this
,
$i
);
$row
->
setContext
(
$this
->
getContext
()
);
return
$row
;
}
/**
* Get an UploadForm instance with title and text properly set.
*
* @param $message String: HTML string to add to the form
* @param $sessionKey String: session key in case this is a stashed upload
* @param $hideIgnoreWarning true if warning's already been dealt with
* @return UploadForm
*/
protected
function
getUploadForm
(
$message
=
''
,
$sessionKey
=
''
,
$hideIgnoreWarning
=
false
)
{
# Initialize form
$form
=
new
MultiUploadForm
(
$this
,
$this
->
mRows
,
$this
->
mTo
,
$this
->
getContext
()
);
$form
->
setTitle
(
$this
->
getTitle
()
);
# Check the edit token.
# Unlike Special:Upload, no fine distinctions about
# whether they're uploading vs. cancelling, etc.
if
(
!
$this
->
mTokenOk
&&
$this
->
getRequest
()->
wasPosted
()
)
{
$form
->
addPreText
(
$this
->
msg
(
'session_fail_preview'
)->
parse
()
);
}
# Add the page-top text.
$form
->
addPreText
(
$this
->
msg
(
'multiupload-text'
)->
parse
()
);
// @todo FIXME: add footer
return
$form
;
}
public
function
getGlobalFormDescriptors
()
{
return
array
();
}
}
/**
* Subclass of HTMLForm that provides the form section of Special:MultiUpload
*/
class
MultiUploadForm
extends
UploadForm
{
protected
$mPage
;
protected
$mRows
;
protected
$mLastIndex
;
public
function
__construct
(
$page
,
$rows
,
$lastIndex
,
IContextSource
$context
=
null
)
{
$this
->
mPage
=
$page
;
$this
->
mRows
=
$rows
;
$this
->
mLastIndex
=
$lastIndex
;
parent
::
__construct
(
array
(),
$context
);
$this
->
mSourceIds
=
array
();
$this
->
mMessagePrefix
=
'multiupload'
;
$this
->
setSubmitText
(
wfMessage
(
'multiupload-submit'
)->
parse
()
);
}
protected
function
constructData
(
array
$options
=
array
(),
IContextSource
$context
=
null
)
{
}
protected
function
constructForm
(
IContextSource
$context
)
{
$descriptor
=
$this
->
getGlobalFormDescriptors
()
+
$this
->
mPage
->
getGlobalFormDescriptors
();
foreach
(
$this
->
mRows
as
$row
)
{
$rowdesc
=
$row
->
getFormDescriptors
();
$descriptor
=
$descriptor
+
$rowdesc
;
}
HTMLForm
::
__construct
(
$descriptor
,
$context
,
'upload'
);
}
protected
function
getGlobalFormDescriptors
()
{
return
array
(
'LastRowIndex'
=>
array
(
'type'
=>
'hidden'
,
'id'
=>
'wpLastRowIndex'
,
'default'
=>
$this
->
mLastIndex
,
),
);
}
public
function
getLegend
(
$key
)
{
$parts
=
explode
(
'-'
,
$key
);
$msg
=
array_shift
(
$parts
);
return
wfMessage
(
"{$this->mMessagePrefix}-$msg"
,
$parts
)->
parse
();
}
protected
function
addJsConfigVars
(
$out
)
{
parent
::
addJsConfigVars
(
$out
);
$jsConfig
=
array
(
'wpFirstRowIndex'
=>
$this
->
mPage
->
mFrom
,
'wpLastRowIndex'
=>
$this
->
mPage
->
mTo
,
'wgMultiUploadMaxPhpUploadSize'
=>
min
(
wfShorthandToInteger
(
ini_get
(
'upload_max_filesize'
)
),
wfShorthandToInteger
(
ini_get
(
'post_max_size'
)
)
),
);
foreach
(
$this
->
mRows
as
$row
)
{
$jsConfig
=
$jsConfig
+
$row
->
jsConfigVars
();
}
$out
->
addJsConfigVars
(
$jsConfig
);
}
protected
function
addRLModules
(
$out
)
{
$out
->
addModules
(
array
(
'ext.multiupload.top'
,
'ext.multiupload'
,
)
);
}
}
/**
* Hoping this gets merged into core, won't have to do it here
*/
if
(
!
class_exists
(
'FauxWebRequestUpload'
)
)
{
/**
* A WebRequestUpload that can be faked.
*/
class
FauxWebRequestUpload
extends
WebRequestUpload
{
/**
* Constructor. Should only be called by FauxRequest.
*
* @param $request WebRequest The associated request
* @param array $data Data in the same format that would be found
* in the $_FILES array. If provided, will be used
* instead of $_FILES[$key].
*/
public
function
__construct
(
$request
,
$data
)
{
$this
->
request
=
$request
;
$this
->
fileInfo
=
$data
;
$this
->
doesExist
=
true
;
}
}
/**
* allow DerivativeRequest to include fake uploaded files
*/
class
DerivativeRequestWithFiles
extends
DerivativeRequest
{
/**
* @param string $key
* @return FauxWebRequestUpload|WebRequestUpload
*/
public
function
getUpload
(
$key
)
{
if
(
array_key_exists
(
$key
,
$this
->
data
)
)
{
return
new
FauxWebRequestUpload
(
$this
,
$this
->
data
[
$key
]
);
}
else
{
return
new
WebRequestUpload
(
$this
,
$key
);
}
}
}
}
else
{
/**
* If the feature is in MW core, just use it
*/
class
DerivativeRequestWithFiles
extends
DerivativeRequest
{
}
}
class
UploadRow
extends
SpecialUpload
{
public
$mPage
;
public
$mRowNumber
;
public
$mRequest
;
public
$mFormMessage
;
public
$mSessionKey
;
public
$mHideIgnoreWarning
;
public
$mExtraButtons
;
/**
* Different constructor, let it know which row it is and
* the upload object it belongs to
*/
public
function
__construct
(
$page
,
$number
)
{
$this
->
mPage
=
$page
;
$this
->
setContext
(
$page
->
getContext
()
);
$this
->
mRowNumber
=
$number
;
$this
->
mRequest
=
null
;
$this
->
mFormMessage
=
''
;
$this
->
mSessionKey
=
''
;
$this
->
mHideIgnoreWarning
=
''
;
$this
->
mExtraButtons
=
array
();
}
/**
* UploadBase and various parent class methods expect certain
* form field names that don't have a row number appended.
* Here we create a fake request object that responds to those field names.
*
* @return DerivativeRequestWithFiles
*/
public
function
getRequest
()
{
if
(
!
$this
->
mRequest
)
{
$webRequest
=
$this
->
mPage
->
getRequest
();
$i
=
$this
->
mRowNumber
;
$valuesKept
=
$valuesAltered
=
array
();
foreach
(
$webRequest
->
getValues
()
+
$_FILES
as
$key
=>
$value
)
{
$matches
=
null
;
$prefixMatch
=
preg_match
(
'/^(.*?)(
\d
+)$/'
,
$key
,
$matches
);
if
(
$prefixMatch
===
false
)
{
// ERROR
}
elseif
(
$prefixMatch
==
0
)
{
// key has no row number
$valuesKept
[
$key
]
=
$value
;
}
elseif
(
$matches
[
2
]
==
$this
->
mRowNumber
)
{
// key has my row number
$valuesAltered
[
$matches
[
1
]]
=
$value
;
}
// else it has some other row number
}
$this
->
mRequest
=
new
DerivativeRequestWithFiles
(
$webRequest
,
$valuesKept
+
$valuesAltered
,
$webRequest
->
wasPosted
()
);
}
return
$this
->
mRequest
;
}
protected
function
handleRequestData
()
{
$request
=
$this
->
getRequest
();
$this
->
mUploadSuccessful
=
$request
->
getCheck
(
'wpUploadSuccessful'
);
parent
::
handleRequestData
();
}
/**
* We don't do our own form output - we give all output to the
* page object to aggregate into a single form
*/
protected
function
showUploadForm
(
$form
)
{
// it gets called
// wfDebug(" *** SHOWUPLOADFORM SHOULD NOT BE CALLED! *** \n");
}
/**
* Unlike the superclass, don't actually create a form object when
* this is called, wait and do it when the page object is ready to
* assemble its full output form.
*
* @param $message String: HTML string to add to the form
* @param $sessionKey String: session key in case this is a stashed upload
* @param $hideIgnoreWarning Boolean: whether to hide "ignore warning" check box
* @return UploadForm
* todo lots of redundancy here
*/
public
function
getUploadForm
(
$message
=
''
,
$sessionKey
=
''
,
$hideIgnoreWarning
=
false
)
{
}
public
function
showUploadError
(
$message
)
{
$this
->
mFormMessage
.=
$this
->
getUploadError
(
$message
);
}
protected
function
showRecoverableUploadError
(
$message
)
{
$this
->
mSessionKey
=
$this
->
mUpload
->
stashSession
();
$this
->
mFormMessage
.=
$this
->
getRecoverableUploadError
(
$message
);
}
protected
function
showUploadWarning
(
$warnings
)
{
$warningHtml
=
$this
->
getUploadWarning
(
$warnings
);
if
(
$warningHtml
===
false
)
{
return
false
;
}
$this
->
mSessionKey
=
$this
->
mUpload
->
stashSession
();
$this
->
mFormMessage
.=
$warningHtml
;
$this
->
mHideIgnoreWarning
=
true
;
# Special:Upload changes the 'Upload' button to
# 'Submit modified file description', and adds two
# additional submit buttons. We add the additional
# two as check boxes, and just leave the
# 'Upload' button below all rows.
$this
->
mExtraButtons
=
array
(
'UploadIgnoreWarning'
=>
'ignorewarning'
,
'CancelUpload'
=>
'reuploaddesc'
,
);
return
true
;
}
/**
* This is apparently a pretty bad one.
* Special:Upload replaces the whole page with an error page
* when this happens. I'll just do it as an error message added
* to the form. But if it happens, you should probably start
* over clean.
*/
protected
function
showFileDeleteError
()
{
$this
->
mFormMessage
.=
'<div class="error">'
.
$this
->
getOutput
()->
msg
(
'filenotfound'
,
$this
->
mUpload
->
getTempPath
()
)
.
'</div>'
;
}
/**
* Suppress error message that file is empty, because this
* happens normally when you don't fill all the rows of the form.
*/
protected
function
processVerificationError
(
$details
)
{
if
(
$details
[
'status'
]
===
UploadBase
::
EMPTY_FILE
&&
$this
->
mDesiredDestName
===
''
)
{
return
;
}
parent
::
processVerificationError
(
$details
);
}
protected
function
createFormRow
()
{
return
new
UploadFormRow
(
$this
,
$this
->
getFormOptions
(
$this
->
mSessionKey
,
$this
->
mHideIgnoreWarning
),
$this
->
getContext
()
);
}
public
function
getFormDescriptors
()
{
# Initialize form
$form
=
$this
->
createFormRow
();
$preText
=
''
;
if
(
$this
->
mDesiredDestName
)
{
$preText
.=
$this
->
getViewDeletedLinks
();
}
# Give a notice if the user is uploading a file that has been deleted or moved
# Note that this is independent from the message 'filewasdeleted' that requires JS
$desiredTitleObj
=
Title
::
makeTitleSafe
(
NS_FILE
,
$this
->
mDesiredDestName
);
$delNotice
=
''
;
// empty by default
if
(
$desiredTitleObj
instanceof
Title
&&
!
$desiredTitleObj
->
exists
()
)
{
LogEventsList
::
showLogExtract
(
$delNotice
,
array
(
'delete'
,
'move'
),
$desiredTitleObj
,
''
,
array
(
'lim'
=>
10
,
'conds'
=>
array
(
"log_action != 'revision'"
),
'showIfEmpty'
=>
false
,
'msgKey'
=>
array
(
'upload-recreate-warning'
)
)
);
}
$preText
.=
$delNotice
;
$preText
.=
$this
->
mFormMessage
;
return
$form
->
descriptor
(
$preText
,
$this
->
mExtraButtons
,
$this
->
mUploadSuccessful
);
}
protected
function
shouldProcessUpload
()
{
return
(
!
$this
->
mUploadSuccessful
&&
$this
->
mPage
->
mTokenOk
&&
!
$this
->
mCancelUpload
&&
(
$this
->
getRequest
()->
getVal
(
'wpDestFile'
)
&&
$this
->
mUploadClicked
)
);
}
protected
function
uploadSucceeded
()
{
$this
->
mDesiredDestName
=
$this
->
mLocalFile
->
getTitle
()->
getDBkey
();
}
/**
* @return array
*/
public
function
jsConfigVars
()
{
return
array
(
'wgMultiUploadAutoFill'
.
$this
->
mRowNumber
=>
(
!
$this
->
mForReUpload
&&
// if mDestFile was provided in the request,
// don't overwrite it by autofilling
$this
->
mDesiredDestName
===
''
),
);
}
}
class
UploadFormRow
extends
UploadForm
{
public
$mRow
;
function
__construct
(
$row
,
array
$options
=
array
(),
IContextSource
$context
=
null
)
{
$this
->
mRow
=
$row
;
$this
->
constructData
(
$options
,
$context
);
# HTMLForm::__construct( array(), $context, 'upload' );
# $this->mSourceIds = array();
}
protected
function
twoColumnDescriptor
(
$text
,
$section
)
{
return
array
(
'type'
=>
'info'
,
'raw'
=>
true
,
'rawrow'
=>
true
,
'default'
=>
'<tr><td colspan="2">'
.
$text
.
'</td></tr>'
,
'section'
=>
$section
,
);
}
protected
function
uploadedMessage
()
{
$destTitle
=
Title
::
newFromText
(
$this
->
mDestFile
,
NS_FILE
);
return
'<div class="multiupload-success-message">'
.
wfMessage
(
'multiupload-uploadedto'
,
Linker
::
linkKnown
(
$destTitle
,
$destTitle
->
getText
()
)
)->
text
()
.
'</div>'
;
}
protected
function
uploadSucceededDescriptor
(
$i
,
$sectionLabel
)
{
return
array
(
'UploadedMessage'
.
$i
=>
$this
->
twoColumnDescriptor
(
$this
->
uploadedMessage
(),
$sectionLabel
),
'DestFile'
.
$i
=>
array
(
'type'
=>
'hidden'
,
'default'
=>
$this
->
mDestFile
,
'section'
=>
$sectionLabel
),
'UploadSuccessful'
.
$i
=>
array
(
'type'
=>
'hidden'
,
'default'
=>
true
,
'section'
=>
$sectionLabel
),
);
}
public
function
descriptor
(
$preText
=
''
,
$extraButtons
=
array
(),
$uploadSuccessful
=
false
)
{
$descriptor
=
array
();
$i
=
$this
->
mRow
->
mRowNumber
;
$sectionLabel
=
'row-'
.
$i
;
if
(
$uploadSuccessful
)
{
return
$this
->
uploadSucceededDescriptor
(
$i
,
$sectionLabel
);
}
$sectionDescriptors
=
$this
->
getSourceSection
()
+
$this
->
getDescriptionSection
()
+
$this
->
getOptionsSection
();
$header
=
''
;
foreach
(
array
(
$preText
,
$this
->
mHeader
)
+
$this
->
mSectionHeaders
as
$head
)
{
if
(
$head
!=
''
)
{
if
(
$header
!=
''
)
{
$header
.=
"<br/>
\n
"
;
}
$header
.=
$head
;
}
}
$preTextSection
=
array
();
if
(
$header
!=
''
)
{
$preTextSection
[
'Message'
]
=
$this
->
twoColumnDescriptor
(
$header
,
$sectionLabel
);
}
# a couple markers for the JavaScript animations
if
(
isset
(
$sectionDescriptors
[
'DestFile'
]
)
)
{
$sectionDescriptors
[
'DestFile'
][
'cssclass'
]
=
'multiupload-first-to-collapse multiupload-width-exemplar'
;
}
foreach
(
$preTextSection
+
$sectionDescriptors
as
$name
=>
$field
)
{
if
(
isset
(
$field
[
'id'
]
)
)
{
# put the IDs that Special:Upload uses into
# the class attributes, without numbers appended,
# so that JavaScript routines can find them that
# way
if
(
isset
(
$field
[
'cssclass'
]
)
)
{
$field
[
'cssclass'
]
.=
' '
.
$field
[
'id'
];
}
else
{
$field
[
'cssclass'
]
=
$field
[
'id'
];
}
# add the row number to the actual ID, for use
# as distinct form fields.
$field
[
'id'
]
=
$field
[
'id'
]
.
$i
;
}
if
(
isset
(
$field
[
'radio-name'
]
)
)
{
$field
[
'radio-name'
]
=
$field
[
'radio-name'
]
.
$i
;
}
if
(
isset
(
$field
[
'section'
]
)
)
{
if
(
isset
(
$field
[
'cssclass'
]
)
)
{
$field
[
'cssclass'
]
.=
' '
;
}
else
{
$field
[
'cssclass'
]
=
''
;
}
$field
[
'cssclass'
]
.=
'mw-htmlform-section-'
.
str_replace
(
'/'
,
'-'
,
$field
[
'section'
]
);
}
$field
[
'section'
]
=
$sectionLabel
;
$descriptor
[
"$name$i"
]
=
$field
;
}
foreach
(
$extraButtons
as
$key
=>
$msg
)
{
$descriptor
[
$key
]
=
array
(
'type'
=>
'check'
,
'id'
=>
$key
,
'label-message'
=>
$msg
,
'section'
=>
$sectionLabel
,
);
}
return
$descriptor
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
99033
Default Alt Text
code (16 KB)
Attached To
Mode
T93236: Cannot redeclare class SpecialMultiUpload
Attached
Detach File
Event Timeline
Log In to Comment