Index: trunk/extensions/MultiUpload/MultiUpload.body.php |
— | — | @@ -1,445 +1,498 @@ |
2 | 2 | <?php |
| 3 | +/** |
| 4 | + * @file |
| 5 | + * @ingroup SpecialPage |
| 6 | + * @ingroup Upload |
| 7 | + * |
| 8 | + * Form for handling multiple uploads and special page. |
| 9 | + * |
| 10 | + */ |
3 | 11 | |
4 | | -class MultipleUpload extends SpecialPage { |
| 12 | +class MultipleUpload extends SpecialUpload { |
5 | 13 | /** |
6 | | - * Constructor |
| 14 | + * Constructor : initialise object |
| 15 | + * Get data POSTed through the form and assign them to the object |
| 16 | + * @param WebRequest $request Data posted. |
7 | 17 | */ |
8 | | - public function __construct() { |
9 | | - parent::__construct( 'MultipleUpload'/*class*/, 'upload'/*restriction*/ ); |
| 18 | + |
| 19 | + public $mDesiredDestNames; |
| 20 | + public $mUploads; |
| 21 | + public $mUploadHasBeenShown; |
| 22 | + public $mSessionKeys; |
| 23 | + |
| 24 | + // status messagse for multiple files |
| 25 | + public $mWarnings; |
| 26 | + public $mSuccesses; |
| 27 | + public $mErrors; |
| 28 | + |
| 29 | + public function __construct( $request = null ) { |
| 30 | + global $wgRequest; |
| 31 | + |
| 32 | + parent::SpecialPage( 'MultipleUpload', 'upload' ); |
| 33 | + |
| 34 | + $this->loadRequest( is_null( $request ) ? $wgRequest : $request ); |
| 35 | + $this->mUploadHasBeenShown = false; |
| 36 | + $this->mSessionKeys = array(); |
| 37 | + $this->mWarnings = array(); |
| 38 | + $this->mSuccesses = array(); |
| 39 | + $this->mErrors = array(); |
10 | 40 | } |
11 | 41 | |
12 | 42 | /** |
13 | | - * Show the special page |
| 43 | + * Initialize instance variables from request and create an Upload handler |
14 | 44 | * |
15 | | - * @param $par Mixed: parameter passed to the page or null |
| 45 | + * @param WebRequest $request The request to extract variables from |
16 | 46 | */ |
17 | | - public function execute( $par ) { |
18 | | - global $wgRequest, $wgOut, $wgUser; |
19 | | - $this->setHeaders(); |
| 47 | + protected function loadRequest( $request ) { |
| 48 | + global $wgUser, $wgMaxUploadFiles; |
20 | 49 | |
21 | | - # Check permissions |
22 | | - if( !$wgUser->isAllowed( 'upload' ) ) { |
23 | | - $wgOut->permissionRequired( 'upload' ); |
24 | | - return; |
| 50 | + // let's make the parent happy |
| 51 | + $_FILES['wpUploadFile'] = $_FILES['wpUploadFile0']; |
| 52 | + // Guess the desired name from the filename if not provided |
| 53 | + $this->mDesiredDestNames = array(); |
| 54 | + $this->mUploads = array(); |
| 55 | + |
| 56 | + // deal with session keys, if we have some pick the first one, for now |
| 57 | + $vals = $request->getValues(); |
| 58 | + $fromsession = false; |
| 59 | + foreach ($vals as $k=>$v) { |
| 60 | + if (preg_match("@^wpSessionKey@", $k)) { |
| 61 | + $request->setVal('wpSessionKey', $v); |
| 62 | + $fromsession = true; |
| 63 | + $filenum = preg_replace("@wpSessionKey@", "", $k); |
| 64 | + $request->setVal('wpDestFile', $request->getVal('wpDestFile' . $filenum)); |
| 65 | + $up = UploadBase::createFromRequest( $request ); |
| 66 | + $this->mUploads[] = $up; |
| 67 | + $this->mDesiredDestNames[] = $request->getVal('wpDestFile' . $filenum); |
| 68 | + } |
25 | 69 | } |
| 70 | + |
| 71 | + parent::loadRequest($request); |
26 | 72 | |
27 | | - # Show a message if the database is in read-only mode |
28 | | - if ( wfReadOnly() ) { |
29 | | - $wgOut->readOnlyPage(); |
30 | | - return; |
| 73 | + $this->mUploadClicked = $request->wasPosted() |
| 74 | + && ( $request->getCheck( 'wpUpload' ) |
| 75 | + || $request->getCheck( 'wpUploadIgnoreWarning' ) ); |
| 76 | + |
| 77 | + if (!$fromsession) { |
| 78 | + for ($i = 0; $i < $wgMaxUploadFiles; $i++) { |
| 79 | + $this->mDesiredDestNames[$i] = $request->getText( 'wpDestFile'. $i); |
| 80 | + if( !$this->mDesiredDestNames[$i] && $request->getFileName( 'wpUploadFile' . $i) !== null ) |
| 81 | + $this->mDesiredDestNames[$i] = $request->getFileName( 'wpUploadFile' . $i); |
| 82 | + $request->setVal('wpUploadFile', $_FILES['wpUploadFile' . $i]); |
| 83 | + $request->setVal('wpDestFile', $request->getVal('wpDestFile' . $i)); |
| 84 | + move_uploaded_file('wpUploadFile' . $i, 'wpUploadFile'); |
| 85 | + $_FILES['wpUploadFile'] = $_FILES['wpUploadFile' . $i]; |
| 86 | + $up = UploadBase::createFromRequest( $request ); |
| 87 | + $this->mUploads[$i] = $up; |
| 88 | + } |
31 | 89 | } |
| 90 | + // |
| 91 | + $this->mDesiredDestName = $this->mDesiredDestNames[0]; |
| 92 | + $this->mUpload= $this->mUploads[0]; |
| 93 | + } |
32 | 94 | |
33 | | - # If user is blocked, s/he doesn't need to access this page |
34 | | - if ( $wgUser->isBlocked() ) { |
35 | | - $wgOut->blockedPage(); |
| 95 | + function showUploadForm($form) { |
| 96 | + if ($this->mUploadHasBeenShown) |
36 | 97 | return; |
| 98 | + parent::showUploadForm($form); |
| 99 | + $this->mUploadHasBeenShown = true; |
| 100 | + } |
| 101 | + /** |
| 102 | + * Get an UploadForm instance with title and text properly set. |
| 103 | + * |
| 104 | + * @param string $message HTML string to add to the form |
| 105 | + * @param string $sessionKey Session key in case this is a stashed upload |
| 106 | + * @return UploadForm |
| 107 | + */ |
| 108 | + protected function getUploadForm( $message = '', $sessionKeys = array(), $hideIgnoreWarning = false ) { |
| 109 | + global $wgOut, $wgMaxUploadFiles; |
| 110 | + |
| 111 | + # Initialize form |
| 112 | + $options = array( |
| 113 | + 'watch' => $this->getWatchCheck(), |
| 114 | + 'forreupload' => $this->mForReUpload, |
| 115 | + 'hideignorewarning' => $hideIgnoreWarning, |
| 116 | + 'destwarningack' => (bool)$this->mDestWarningAck, |
| 117 | + 'texttop' => $this->uploadFormTextTop, |
| 118 | + 'textaftersummary' => $this->uploadFormTextAfterSummary, |
| 119 | + ); |
| 120 | + foreach ($this->mSessionKeys as $f=>$key) |
| 121 | + $options['sessionkey'. $f] = $key; |
| 122 | + for ($i = 0; $i < $wgMaxUploadFiles; $i++) |
| 123 | + $options['destfile' . $i] = $this->mDesiredDestNames[$i]; |
| 124 | + $form = new MultiUploadForm($options); |
| 125 | + $form->setTitle( $this->getTitle() ); |
| 126 | + |
| 127 | + # Check the token, but only if necessary |
| 128 | + if( !$this->mTokenOk && !$this->mCancelUpload |
| 129 | + && ( $this->mUploads[0] && $this->mUploadClicked ) ) { |
| 130 | + $form->addPreText( wfMsgExt( 'session_fail_preview', 'parseinline' ) ); |
37 | 131 | } |
38 | 132 | |
39 | | - $form = new MultipleUploadForm( $wgRequest ); |
40 | | - $form->execute(); |
| 133 | + # Add text to form |
| 134 | + $form->addPreText( '<div id="uploadtext">' . |
| 135 | + wfMsgExt( 'uploadtext', 'parse', array( $this->mDesiredDestName ) ) . |
| 136 | + '</div>' ); |
| 137 | + # Add upload error message |
| 138 | + $form->addPreText( $message ); |
| 139 | + |
| 140 | + # Add footer to form |
| 141 | + $uploadFooter = wfMsgNoTrans( 'uploadfooter' ); |
| 142 | + if ( $uploadFooter != '-' && !wfEmptyMsg( 'uploadfooter', $uploadFooter ) ) { |
| 143 | + $form->addPostText( '<div id="mw-upload-footer-message">' |
| 144 | + . $wgOut->parse( $uploadFooter ) . "</div>\n" ); |
| 145 | + } |
| 146 | + |
| 147 | + return $form; |
41 | 148 | |
42 | | - $form->cleanupTempFile(); |
43 | 149 | } |
44 | | -} |
45 | 150 | |
46 | | -/** |
47 | | - * Main class |
48 | | - * @ingroup SpecialPage |
49 | | - */ |
50 | | -class MultipleUploadForm extends UploadForm { |
51 | | - |
52 | | - // extra goodies |
53 | | - // access private |
54 | | - var $mUploadTempNameArray, $mUploadSizeArray, $mOnameArray, $mUploadError, $mDestFileArray, $mFilePropsArray; |
55 | | - var $mUploadDescriptionArray; |
56 | | - var $mShowUploadForm, $mHasWarning, $mFileIndex; |
57 | | - var $mWarnings = 0; |
58 | | - var $mSessionKey = null; |
59 | | - |
60 | 151 | /** |
61 | | - * Constructor : initialise object |
62 | | - * Get data POSTed through the form and assign them to the object |
63 | | - * @param $request Data posted. |
| 152 | + * Shows the "view X deleted revivions link"" |
64 | 153 | */ |
65 | | - function __construct( &$request ) { |
| 154 | + protected function showViewDeletedLinks() { |
66 | 155 | global $wgMaxUploadFiles; |
67 | | - // call the parent constructor |
68 | | - parent::UploadForm($request); |
| 156 | + for ($i = 0; $i < $wgMaxUploadFiles; $i++) |
| 157 | + $this->showViewDeletedLinksInner($this->mDesiredDestNames[$i]); |
| 158 | + } |
69 | 159 | |
70 | | - // initialize |
71 | | - $this->mUploadTempNameArray = $this->mUploadSizeArray = $this->mOnameArray = $this->mUploadError = $this->mDestFileArray = $this->mUploadDescriptionArray = array(); |
72 | | - $this->mmFilePropsArray = array(); |
73 | | - $this->mShowUploadForm = true; |
74 | | - $this->mFileIndex = 0; |
| 160 | + protected function showViewDeletedLinksInner($name) { |
| 161 | + global $wgOut, $wgUser; |
75 | 162 | |
76 | | - for( $x = 0; $x < $wgMaxUploadFiles; $x++ ) $this->mDestFileArray[$x] = $request->getText( "wpDestFile_$x" ); |
| 163 | + $title = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); |
| 164 | + // Show a subtitle link to deleted revisions (to sysops et al only) |
| 165 | + if( $title instanceof Title ) { |
| 166 | + $count = $title->isDeleted(); |
| 167 | + if ( $count > 0 && $wgUser->isAllowed( 'deletedhistory' ) ) { |
| 168 | + $link = wfMsgExt( |
| 169 | + $wgUser->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted', |
| 170 | + array( 'parse', 'replaceafter' ), |
| 171 | + $wgUser->getSkin()->linkKnown( |
| 172 | + SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ), |
| 173 | + wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $count ) |
| 174 | + ) |
| 175 | + ); |
| 176 | + $wgOut->addHTML( "<div id=\"contentSub2\">{$link}</div>" ); |
| 177 | + } |
| 178 | + } |
77 | 179 | |
78 | | - if( !$request->wasPosted() ) { |
79 | | - # GET requests just give the main form; no data except wpDestfile. |
80 | | - return; |
| 180 | + // Show the relevant lines from deletion log (for still deleted files only) |
| 181 | + if( $title instanceof Title && $title->isDeletedQuick() && !$title->exists() ) { |
| 182 | + $this->showDeletionLog( $wgOut, $title->getPrefixedText() ); |
81 | 183 | } |
| 184 | + } |
82 | 185 | |
83 | | - for( $x = 0; $x < $wgMaxUploadFiles; $x++ ) { |
84 | | - $this->mDestFile[$x] = $request->getText( "wpDestFile_$x" ); |
85 | | - $this->mUploadDescriptionArray[$x] = $request->getText( "wpUploadDescription_$x" ); |
| 186 | + /** |
| 187 | + * Stashes the upload, shows the main form, but adds an "continue anyway button". |
| 188 | + * Also checks whether there are actually warnings to display. |
| 189 | + * |
| 190 | + * @param array $warnings |
| 191 | + * @return boolean true if warnings were displayed, false if there are no |
| 192 | + * warnings and the should continue processing like there was no warning |
| 193 | + */ |
| 194 | + protected function showUploadWarning( $warnings ) { |
| 195 | + global $wgUser; |
| 196 | + |
| 197 | + # If there are no warnings, or warnings we can ignore, return early. |
| 198 | + # mDestWarningAck is set when some javascript has shown the warning |
| 199 | + # to the user. mForReUpload is set when the user clicks the "upload a |
| 200 | + # new version" link. |
| 201 | + if ( !$warnings || ( count( $warnings ) == 1 && |
| 202 | + isset( $warnings['exists'] ) && |
| 203 | + ( $this->mDestWarningAck || $this->mForReUpload ) ) ) |
| 204 | + { |
| 205 | + return false; |
86 | 206 | } |
87 | | - $this->mSessionKey = $request->getInt( 'wpSessionKey' ); |
88 | 207 | |
89 | | - if( !empty( $this->mSessionKey ) ) { |
90 | | - for( $x = 0; $x < $wgMaxUploadFiles; $x++ ) { |
91 | | - if( !isset( $_SESSION["wsUploadData_$x"][$this->mSessionKey] ) ) continue; |
92 | | - $data = $_SESSION["wsUploadData_$x"][$this->mSessionKey]; |
93 | | - $this->mUploadTempNameArray[$x] = $data['mTempPath']; |
94 | | - $this->mUploadSizeArray[$x] = $data['mFileSize']; |
95 | | - $this->mOnameArray[$x] = $data['mSrcName']; |
96 | | - $this->mFileProps[$x] = $data['mFileProps']; |
97 | | - $this->mCurlError = 0 /*UPLOAD_ERR_OK*/; |
98 | | - $this->mStashed = true; |
99 | | - $this->mRemoveTempFile = false; |
100 | | - } |
101 | | - } else { |
102 | | - /** |
103 | | - *Check for a newly uploaded file. |
104 | | - */ |
105 | | - for( $x = 0; $x < $wgMaxUploadFiles; $x++ ) { |
106 | | - $this->mUploadTempNameArray[$x] = $request->getFileTempName( "wpUploadFile_$x" ); |
107 | | - $this->mUploadSizeArray [$x] = $request->getFileSize( "wpUploadFile_$x" ); |
108 | | - $this->mOnameArray[$x] = $request->getFileName( "wpUploadFile_$x" ); |
109 | | - $this->mUploadErrorArray[$x] = $request->getUploadError( "wpUploadFile_$x" ); |
110 | | - $this->mUploadDescriptionArray[$x] = $request->getVal( "wpUploadDescription_$x" ); |
111 | | - } |
| 208 | + |
| 209 | + $sessionKey = $this->mUpload->stashSession(); |
| 210 | + |
| 211 | + $sk = $wgUser->getSkin(); |
| 212 | + |
| 213 | + $warningHtml = '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" |
| 214 | + . '<ul class="warning">'; |
| 215 | + foreach( $warnings as $warning => $args ) { |
| 216 | + $msg = ''; |
| 217 | + if( $warning == 'exists' ) { |
| 218 | + $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n"; |
| 219 | + } elseif( $warning == 'duplicate' ) { |
| 220 | + $msg = self::getDupeWarning( $args, $this->mLocalFile->getTitle() ); |
| 221 | + } elseif( $warning == 'duplicate-archive' ) { |
| 222 | + $msg = "\t<li>" . wfMsgExt( 'file-deleted-duplicate', 'parseinline', |
| 223 | + array( Title::makeTitle( NS_FILE, $args )->getPrefixedText() ) ) |
| 224 | + . "</li>\n"; |
| 225 | + } else { |
| 226 | + if ( $args === true ) |
| 227 | + $args = array(); |
| 228 | + elseif ( !is_array( $args ) ) |
| 229 | + $args = array( $args ); |
| 230 | + $msg = "\t<li>" . wfMsgExt( $warning, 'parseinline', $args ) . "</li>\n"; |
| 231 | + } |
| 232 | + $warningHtml .= $msg; |
112 | 233 | } |
| 234 | + $warningHtml .= "</ul>\n"; |
| 235 | + $warningHtml .= wfMsgExt( 'uploadwarning-text', 'parse' ); |
113 | 236 | |
| 237 | + // store it in an array to show later |
| 238 | + $this->mWarnings[] = $warningHtml; |
| 239 | + $this->mSessionKeys[$this->mUpload->getLocalFile()->getName()] = $sessionKey; |
| 240 | + |
| 241 | + # Indicate that we showed a form |
| 242 | + return true; |
114 | 243 | } |
115 | 244 | |
| 245 | + /** |
| 246 | + * Show the upload form with error message, but do not stash the file. |
| 247 | + * |
| 248 | + * @param string $message |
| 249 | + */ |
| 250 | + protected function showUploadError( $message ) { |
| 251 | + $this->mErrors[] = '<ul><li>' . $this->mLocalFile->getTitle()->getFullText() . ':' . $message . "</li></ul>\n"; |
| 252 | + } |
| 253 | + |
| 254 | + |
116 | 255 | /** |
117 | | - * Really do the upload |
| 256 | + * Do the upload. |
118 | 257 | * Checks are made in SpecialUpload::execute() |
119 | | - * @access private |
120 | 258 | */ |
121 | | - function processUpload() { |
122 | | - global $wgMaxUploadFiles, $wgOut, $wgRequest; |
| 259 | + protected function processUpload() { |
| 260 | + global $wgMaxUploadFiles, $wgOut; |
123 | 261 | |
124 | | - $images = 0; |
125 | | - $wgOut->addHTML( '<table>' ); |
126 | | - $this->mShowUploadForm = false; |
127 | | - |
128 | | - $titleObj = SpecialPage::getTitleFor( 'MultipleUpload' ); |
129 | | - $wgOut->addHTML( |
130 | | - Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ), |
131 | | - 'enctype' => 'multipart/form-data', 'id' => 'uploadwarning' ) ) . "\n" |
132 | | - ); |
133 | | - |
134 | | - $this->mLicense = $wgRequest->getText( 'wpLicense' ); |
135 | | - |
136 | | - for( $x = 0; $x < $wgMaxUploadFiles; $x++ ) { |
137 | | - $this->mFileIndex = $x; |
138 | | - if( !isset( $this->mUploadTempNameArray[$x] ) || $this->mUploadTempNameArray[$x] == null ) { |
139 | | - continue; |
| 262 | + for ($i = 0; $i < $wgMaxUploadFiles; $i++) { |
| 263 | + if (isset($this->mUploads[$i])) { |
| 264 | + $this->mUpload = $this->mUploads[$i]; |
| 265 | + $this->mUploadSuccessful = false; // reset |
| 266 | + parent::processUpload(); |
| 267 | + if ( $this->mUploadSuccessful) { |
| 268 | + $this->mSuccesses[] = "<ul><li><a href='" . $this->mLocalFile->getTitle()->getFullURL() . "' target='new'>{$this->mLocalFile->getTitle()->getFullText()}: " . wfMsg('multiupload-fileuploaded') . "</a></li></ul>"; |
| 269 | + } |
140 | 270 | } |
| 271 | + } |
| 272 | + // clear out the redirects |
| 273 | + $wgOut->redirect(''); |
141 | 274 | |
142 | | - $images++; |
143 | | - $this->mTempPath = $this->mUploadTempNameArray[$x]; |
144 | | - $this->mFileSize = $this->mUploadSizeArray[$x]; |
145 | | - $this->mSrcName = $this->mOnameArray[$x]; // for mw > 1.9 |
146 | | - $this->mRemoveTempFile = true; |
147 | | - $this->mIgnoreWarning = $wgRequest->getCheck( 'wpIgnoreWarning' ); |
| 275 | + // tell the good news first |
| 276 | + if (sizeof($this->mSuccesses) > 0) { |
| 277 | + $wgOut->addHTML('<h2>' . wfMsgHtml( 'successfulupload' ) . "</h2>\n"); |
| 278 | + $wgOut->addHTML(implode($this->mSuccesses)); |
| 279 | + } |
148 | 280 | |
149 | | - $this->mUploadError = $this->mUploadErrorArray[$x]; |
150 | | - $this->mDesiredDestName = $this->mDestFileArray[$x]; |
151 | | - $this->mComment = $this->mUploadDescriptionArray[$x]; |
152 | | - $this->mFileProps = $this->mFileProps[$x]; |
153 | | - |
154 | | - $wgOut->addHTML( '<tr><td>' ); |
155 | | - parent::processUpload(); |
156 | | - $wgOut->addHTML( '</td></tr>' ); |
| 281 | + // the bad news |
| 282 | + if (sizeof($this->mErrors) > 0) { |
| 283 | + $wgOut->addHTML('<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n"); |
| 284 | + $wgOut->addHTML(implode($this->mErrors)); |
157 | 285 | } |
158 | 286 | |
159 | | - $wgOut->addHTML( '</table>' ); |
160 | | - // Display a form again with a warning if we gave no files, instead of a blank screen |
161 | | - if( 0 == $images ) { |
162 | | - $this->mShowUploadForm = true; |
163 | | - $this->mUploadSaveName = wfMsg( 'multiupload-blank' ); |
164 | | - $this->mainUploadForm( wfMsg( 'multiupload-no-files' ) ); |
165 | | - } else { |
166 | | - $this->mShowUploadForm = false; |
167 | | - $wgOut->redirect( '' ); // clear the redirect, we want to show a nice page of images |
168 | | - $this->mShowUploadForm = true; |
169 | | - if( $this->mHasWarning ) { |
170 | | - $this->showWarningOptions(); |
171 | | - } |
| 287 | + // the hopefully recoverable news |
| 288 | + if (sizeof($this->mWarnings) > 0 || sizeof($this->mErrors) > 0 ) { |
| 289 | + $wgOut->addHTML("<br/><br/><hr/>"); // visually separate the form from the errors/successes |
| 290 | + $form = $this->getUploadForm( implode($this->mWarnings), $this->mSessionKeys, /* $hideIgnoreWarning */ true ); |
| 291 | + $form->setSubmitText( wfMsg( 'upload-tryagain' ) ); |
| 292 | + $form->addButton( 'wpUploadIgnoreWarning', wfMsg( 'ignorewarning' ) ); |
| 293 | + $form->addButton( 'wpCancelUpload', wfMsg( 'reuploaddesc' ) ); |
| 294 | + $this->showUploadForm( $form ); |
172 | 295 | } |
173 | | - $wgOut->addHTML( Xml::closeElement( 'form' ) . "\n" ); |
174 | 296 | } |
175 | 297 | |
176 | | - /** |
177 | | - * Show some text and linkage on successful upload. |
178 | | - * @access private |
179 | | - */ |
180 | | - function showSuccess() { |
181 | | - global $wgUser, $wgOut; |
182 | | - $t = $this->mLocalFile->getTitle(); |
183 | | - $wgOut->addHTML( '<h2>' . wfMsg( 'multiupload-fileuploaded' ) . '</h2>' ); |
184 | | - $wgOut->addHTML( $wgUser->getSkin()->makeThumbLink2( $t, $this->mLocalFile, array( 'caption' => $t->getText() ) ) ); |
185 | | - } |
186 | 298 | |
187 | 299 | /** |
188 | | - * @param string $error as HTML |
| 300 | + * Remove a temporarily kept file stashed by saveTempUploadedFile(). |
189 | 301 | * @access private |
| 302 | + * @return success |
190 | 303 | */ |
191 | | - function uploadError( $error ) { |
192 | | - global $wgOut; |
193 | | - $wgOut->addHTML( "<b>{$this->mUploadSaveName}</b>\n" ); |
194 | | - $wgOut->addHTML( "<span class='error'>{$error}</span>\n" ); |
| 304 | + protected function unsaveUploadedFile() { |
| 305 | + global $wgMaxUploadFiles; |
| 306 | + $ret = true; |
| 307 | + for ($i = 0; $i < $wgMaxUploadFiles; $i++) { |
| 308 | + if (isset($this->mUploads[$i])) { |
| 309 | + $this->mUpload = $this->mUploads[$i]; |
| 310 | + // return false if even one of them failed |
| 311 | + $ret = $ret && parent::unsaveUploadedFile(); |
| 312 | + } |
| 313 | + } |
| 314 | + return $ret; |
195 | 315 | } |
196 | 316 | |
197 | | - function showWarningOptions() { |
198 | | - global $wgOut, $wgMaxUploadFiles, $wgUseCopyrightUpload; |
| 317 | + /** |
| 318 | + * Construct a warning and a gallery from an array of duplicate files. |
| 319 | + * Override because the original doesn't say which file is a dupe |
| 320 | + */ |
| 321 | + public static function getDupeWarning( $dupes, $dupetitle = null ) { |
| 322 | + $result = parent::getDupeWarning($dupes); |
| 323 | + return preg_replace("@<li>@", "<li>{$dupetitle->getText()}", $result); |
| 324 | + } |
199 | 325 | |
200 | | - $save = wfMsgHtml( 'multipleupload-saveallfiles' ); |
201 | | - $reupload = wfMsgHtml( 'reupload' ); |
202 | | - $iw = wfMsgWikiHtml( 'multipleupload-ignoreallwarnings' ); |
203 | | - $reup = wfMsgWikiHtml( 'reuploaddesc' ); |
| 326 | +} |
204 | 327 | |
205 | | - if ( $wgUseCopyrightUpload ) { |
206 | | - $copyright = ' |
207 | | - <input type="hidden" name="wpUploadCopyStatus" value="' . htmlspecialchars( $this->mUploadCopyStatus ) . '" /> |
208 | | - <input type="hidden" name="wpUploadSource" value="' . htmlspecialchars( $this->mUploadSource ) . '" />'; |
209 | | - } else { |
210 | | - $copyright = ''; |
211 | | - } |
| 328 | +/** |
| 329 | + * Sub class of HTMLForm that provides the form section of SpecialUpload |
| 330 | + */ |
| 331 | +class MultiUploadForm extends UploadForm { |
212 | 332 | |
213 | | - $wgOut->addHTML( '<input type="hidden" name="wpIgnoreWarning" value="1" /> |
214 | | - <input type="hidden" name="wpSessionKey" value="' . htmlspecialchars( $this->mSessionKey ) . '" /> |
215 | | - <input type="hidden" name="wpLicense" value="' . htmlspecialchars( $this->mLicense ) . '" />'); |
| 333 | + protected $mDestFiles; |
| 334 | + protected $mSessionKeys; |
216 | 335 | |
217 | | - for( $x = 0; $x < $wgMaxUploadFiles; $x++ ) { |
218 | | - $wgOut->addHTML("<input type='hidden' name='wpDestFile_$x' value=\"" . htmlspecialchars( $this->mDestFileArray[$x] ) . "\" />"); |
| 336 | + public function __construct( $options = array() ) { |
| 337 | + |
| 338 | +// basically we want to map filenames to session keys here somehow |
| 339 | + global $wgMaxUploadFiles; |
| 340 | + $this->mDestFiles = array(); |
| 341 | + for($i = 0; $i < $wgMaxUploadFiles; $i++) { |
| 342 | + $this->mDestFiles[$i] = $options['destfile'. $i]; |
| 343 | + } |
| 344 | + $this->mSessionKeys = array(); |
| 345 | + foreach ($options as $k=>$v) { |
| 346 | + if (preg_match("@^sessionkey@", $k)) |
| 347 | + $this->mSessionKeys[$k] = $v; |
219 | 348 | } |
| 349 | + parent::__construct( $options) ; |
| 350 | + } |
220 | 351 | |
221 | | - $wgOut->addHTML("<input type='hidden' name='wpWatchthis' value=\"" . htmlspecialchars( intval( $this->mWatchthis ) ) . "\" /> |
222 | | - {$copyright} |
223 | | - <table border='0'> |
224 | | - <tr> |
225 | | - <tr> |
226 | | - <td align='right'> |
227 | | - <input tabindex='2' type='submit' name='wpUpload' value='$save' /> |
228 | | - </td> |
229 | | - <td align='left'>$iw</td> |
230 | | - </tr> |
231 | | - <tr> |
232 | | - <td align='right'> |
233 | | - <input tabindex='2' type='submit' name='wpReUpload' value='{$reupload}' /> |
234 | | - </td> |
235 | | - <td align='left'>$reup</td> |
236 | | - </tr> |
237 | | - </tr> |
238 | | - </table></form>\n" ); |
239 | 352 | |
| 353 | + protected function getDescriptionSection() { |
| 354 | + // get the usual one and clear out the DestFile |
| 355 | + $descriptor = parent::getDescriptionSection(); |
| 356 | + unset($descriptor['DestFile']); |
| 357 | + return $descriptor; |
240 | 358 | } |
241 | | - |
242 | 359 | /** |
243 | | - * Displays the main upload form, optionally with a highlighted |
244 | | - * error message up at the top. |
245 | | - * |
246 | | - * @param string $msg as HTML |
247 | | - * @access private |
| 360 | + * Get the descriptor of the fieldset that contains the file source |
| 361 | + * selection. The section is 'source' |
| 362 | + * |
| 363 | + * @return array Descriptor array |
248 | 364 | */ |
249 | | - function mainUploadForm( $msg = '' ) { |
250 | | - global $wgOut, $wgUser, $wgScriptPath, $wgUseCopyrightUpload, $wgMaxUploadFiles; |
| 365 | + protected function getSourceSection() { |
| 366 | + global $wgLang, $wgUser, $wgRequest, $wgMaxUploadFiles; |
251 | 367 | |
252 | | - if( $msg == '' && !$this->mShowUploadForm ) return; |
253 | | - $cols = intval( $wgUser->getOption( 'cols' ) ); |
254 | | - $ew = $wgUser->getOption( 'editwidth' ); |
255 | | - if ( $ew ) $ew = " style=\"width:100%\""; |
256 | | - else $ew = ''; |
257 | | - |
258 | | - if ( '' != $msg ) { |
259 | | - $wgOut->addHTML( "<b>{$this->mUploadSaveName}</b>\n<br />" ); |
260 | | - $sub = wfMsgHtml( 'multipleupload-addresswarnings' ); |
261 | | - $wgOut->addHTML( "<b>{$sub}</b><br /><span class='error'>{$msg}</span>\n" ); |
| 368 | + if ( sizeof($this->mSessionKeys) > 0) { |
| 369 | + $data = array( |
| 370 | + 'wpSourceType' => array( |
| 371 | + 'type' => 'hidden', |
| 372 | + 'default' => 'Stash', |
| 373 | + ), |
| 374 | + ); |
| 375 | + $index = 0; |
| 376 | + foreach ($this->mDestFiles as $k=>$v) { |
| 377 | + if ($v == "") continue; |
| 378 | + $data['wpDestFile' . $index] = array( |
| 379 | + 'type' => 'hidden', |
| 380 | + 'default' => $v, |
| 381 | + ); |
| 382 | + $data['wpSessionKey' . $index] = array( |
| 383 | + 'type' => 'hidden', |
| 384 | + 'default' => $this->mSessionKeys['sessionkey' . $v], |
| 385 | + ); |
| 386 | + $index++; |
| 387 | + } |
| 388 | + return $data; |
262 | 389 | } |
263 | | - $wgOut->addHTML( '<div id="uploadtext">' ); |
264 | | - $wgOut->addWikiMsg('multipleupload-text', $wgMaxUploadFiles); |
265 | | - $wgOut->addHTML( '</div>' ); |
266 | | - $sk = $wgUser->getSkin(); |
267 | 390 | |
268 | | - $sourcefilename = wfMsgHtml( 'sourcefilename' ); |
269 | | - $destfilename = wfMsgHtml( 'destfilename' ); |
270 | | - $summary = wfMsg( 'fileuploadsummary' ); |
271 | | - $licenses = new Licenses(); |
272 | | - $license = wfMsgHtml( 'license' ); |
273 | | - $nolicense = wfMsgHtml( 'nolicense' ); |
274 | | - $licenseshtml = $licenses->getHtml(); |
275 | | - $ulb = wfMsgHtml( 'uploadbtn' ); |
| 391 | + $canUploadByUrl = UploadFromUrl::isEnabled() && $wgUser->isAllowed( 'upload_by_url' ); |
| 392 | + $radio = $canUploadByUrl; |
| 393 | + $selectedSourceType = strtolower( $wgRequest->getText( 'wpSourceType', 'File' ) ); |
276 | 394 | |
277 | | - $titleObj = SpecialPage::getTitleFor( 'MultipleUpload' ); |
278 | | - $action = $titleObj->escapeLocalURL(); |
279 | | - |
280 | | - $watchChecked = $wgUser->getOption( 'watchdefault' ) |
281 | | - ? 'checked="checked"' |
282 | | - : ''; |
283 | | - |
284 | | - $wgOut->addScriptFile( $wgScriptPath . '/extensions/MultiUpload/multiupload.js' ); |
285 | | - $wgOut->addHTML( " |
286 | | - <form id='upload' method='post' enctype='multipart/form-data' action=\"$action\"> |
287 | | - <table border='0' width='90%'> |
288 | | - <tr> |
289 | | - <td align='left'><label for='wpUploadFile'><b>{$sourcefilename}</b></label></td> |
290 | | - <td align='left'><label for='wpDestFile'><b>{$destfilename}</b></label></td> |
291 | | - <td align='left' valign='middle'><b>{$summary}</b></td> |
292 | | - </tr>"); |
293 | | - for( $i = 0; $i < $wgMaxUploadFiles; $i++ ) { |
294 | | - $encDestFile = htmlspecialchars( $this->mDestFileArray[$i] ); |
295 | | - $wgOut->addHTML(" |
296 | | - <tr> |
297 | | - <td align='left'> |
298 | | - <input tabindex='1' type='file' name='wpUploadFile_$i' id='wpUploadFile_$i' " . ( $this->mDestName ? "" : "onchange='fillDestFilenameMulti($i)' " ) . "size='25' /> |
299 | | - </td> |
300 | | - <td align='left'> |
301 | | - <input tabindex='2' type='text' name='wpDestFile_$i' id='wpDestFile_$i' size='25' value=\"$encDestFile\" /> |
302 | | - </td> |
303 | | - <td align='left'> |
304 | | - <input tabindex='3' name='wpUploadDescription_$i' id='wpUploadDescription' value=\"". htmlspecialchars( $this->mComment ) . "\" size=25> |
305 | | - </td> |
306 | | - </tr> |
307 | | - <tr>" ); |
308 | | - } |
309 | | - |
310 | | - if ( $licenseshtml != '' ) { |
311 | | - global $wgAjaxLicensePreview; |
312 | | - $wgOut->addScriptFile( 'upload.js' ); |
313 | | - // This is one nasty hack...but necessary to make upload.js not bitch if the user actually touches the "Licensing" dropdown menu instead of just admiring it from a distance. |
314 | | - $wgOut->addInlineScript( 'var wgAjaxLicensePreview = "'.$wgAjaxLicensePreview.'";' ); |
315 | | - $wgOut->addHTML( " |
316 | | - <td align='left' colspan='3'> |
317 | | - <label for='wpLicense'>$license</label> |
318 | | - <select name='wpLicense' id='wpLicense' tabindex='4' style='font-size: xx-small;' |
319 | | - onchange='licenseSelectorCheck()'> |
320 | | - <option value=''>$nolicense</option> |
321 | | - $licenseshtml |
322 | | - </select> |
323 | | - </td> |
324 | | - </tr> |
325 | | - <tr>"); |
326 | | - // So that the license previews will show up on the page |
327 | | - $wgOut->addHTML( ' |
328 | | - <td id="mw-license-preview" colspan="3"></td> |
329 | | - </tr> |
330 | | - <tr>' ); |
| 395 | + $descriptor = array(); |
| 396 | + if ( $this->mTextTop ) { |
| 397 | + $descriptor['UploadFormTextTop'] = array( |
| 398 | + 'type' => 'info', |
| 399 | + 'section' => 'source', |
| 400 | + 'default' => $this->mTextTop, |
| 401 | + 'raw' => true, |
| 402 | + ); |
331 | 403 | } |
332 | | - |
333 | | - if ( $wgUseCopyrightUpload ) { |
334 | | - global $wgRequest; |
335 | | - $filestatus = wfMsgHtml( 'filestatus' ); |
336 | | - $copystatus = htmlspecialchars( $this->mUploadCopyStatus ); |
337 | | - $filesource = wfMsgHtml( 'filesource' ); |
338 | | - $uploadsource = htmlspecialchars( $this->mUploadSource ); |
339 | | - |
340 | | - $wgOut->addHTML( " |
341 | | - <td align='right' nowrap='nowrap'><label for='wpUploadCopyStatus'>$filestatus</label></td> |
342 | | - <td><input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus' value=\"$copystatus\" size='40' /></td> |
343 | | - </tr> |
344 | | - <tr> |
345 | | - <td align='right'><label for='wpUploadCopyStatus'>$filesource</label></td> |
346 | | - <td><input tabindex='6' type='text' name='wpUploadSource' id='wpUploadCopyStatus' value=\"$uploadsource\" size='40' /></td> |
347 | | - </tr> |
348 | | - <tr>" ); |
349 | | - // For license selector |
350 | | - $wgOut->addHTML( Xml::hidden( 'wpLicense', $wgRequest->getText( 'wpLicense' ) ) . "\n" ); |
| 404 | + |
| 405 | + for ($i = 0; $i < $wgMaxUploadFiles; $i++) { |
| 406 | + $descriptor['UploadFile' . $i] = array( |
| 407 | + 'class' => 'UploadSourceField', |
| 408 | + 'section' => 'source', |
| 409 | + 'type' => 'file', |
| 410 | + 'id' => 'wpUploadFile' . $i, |
| 411 | + 'label-message' => 'sourcefilename', |
| 412 | + 'upload-type' => 'File', |
| 413 | + 'radio' => &$radio, |
| 414 | + 'checked' => $selectedSourceType == 'file', |
| 415 | + ); |
| 416 | + $descriptor['DestFile' . $i] = array( |
| 417 | + 'type' => 'text', |
| 418 | + 'section' => 'source', |
| 419 | + 'id' => 'wpDestFile' . $i, |
| 420 | + 'label-message' => 'destfilename', |
| 421 | + 'size' => 60, |
| 422 | + 'default' => $this->mDestFiles[$i], |
| 423 | + # FIXME: hack to work around poor handling of the 'default' option in HTMLForm |
| 424 | + 'nodata' => strval( $this->mDestFile ) !== '', |
| 425 | + ); |
| 426 | + |
| 427 | + if ( $canUploadByUrl ) { |
| 428 | + global $wgMaxUploadSize; |
| 429 | + $descriptor['UploadFileURL'] = array( |
| 430 | + 'class' => 'UploadSourceField', |
| 431 | + 'section' => 'source', |
| 432 | + 'id' => 'wpUploadFileURL', |
| 433 | + 'label-message' => 'sourceurl', |
| 434 | + 'upload-type' => 'url', |
| 435 | + 'radio' => &$radio, |
| 436 | + 'help' => wfMsgExt( 'upload-maxfilesize', |
| 437 | + array( 'parseinline', 'escapenoentities' ), |
| 438 | + $wgLang->formatSize( $wgMaxUploadSize ) |
| 439 | + ) . ' ' . wfMsgHtml( 'upload_source_url' ), |
| 440 | + 'checked' => $selectedSourceType == 'url', |
| 441 | + ); |
| 442 | + } |
351 | 443 | } |
| 444 | + wfRunHooks( 'UploadFormSourceDescriptors', array( &$descriptor, &$radio, $selectedSourceType ) ); |
352 | 445 | |
353 | | - $wgOut->addHTML( " |
354 | | - <td> |
355 | | - <input tabindex='7' type='checkbox' name='wpWatchthis' id='wpWatchthis' $watchChecked value='true' /> |
356 | | - <label for='wpWatchthis'>" . wfMsgHtml( 'watchthis' ) . "</label> |
357 | | - <input tabindex='8' type='checkbox' name='wpIgnoreWarning' id='wpIgnoreWarning' value='true' /> |
358 | | - <label for='wpIgnoreWarning'>" . wfMsgHtml( 'ignorewarnings' ) . "</label> |
359 | | - </td> |
360 | | - </tr> |
361 | | - <tr> |
| 446 | + $descriptor['Extensions'] = array( |
| 447 | + 'type' => 'info', |
| 448 | + 'section' => 'source', |
| 449 | + 'default' => $this->getExtensionsMessage(), |
| 450 | + 'raw' => true, |
| 451 | + 'help' => wfMsgExt( 'upload-maxfilesize', |
| 452 | + array( 'parseinline', 'escapenoentities' ), |
| 453 | + $wgLang->formatSize( |
| 454 | + wfShorthandToInteger( ini_get( 'upload_max_filesize' ) ) |
| 455 | + ) |
| 456 | + ) . ' ' . wfMsgHtml( 'upload_source_file' ), |
| 457 | + ); |
| 458 | + return $descriptor; |
| 459 | + } |
362 | 460 | |
363 | | - </tr> |
364 | | - <tr> |
365 | | - <td align='left'><input tabindex='9' type='submit' name='wpUpload' value=\"{$ulb}\" /></td> |
366 | | - </tr> |
367 | 461 | |
368 | | - </table> |
369 | | - </form>" ); |
370 | | - } |
371 | | - |
372 | 462 | /** |
373 | | - * Override uploadWarning so we can put multiple warnings on the same page |
374 | | - * @param $warning String: warning as HTML |
375 | | - * @access private |
| 463 | + * Add upload JS to $wgOut |
| 464 | + * |
| 465 | + * @param bool $autofill Whether or not to autofill the destination |
| 466 | + * filename text box |
376 | 467 | */ |
377 | | - function uploadWarning( $warning ) { |
378 | | - global $wgOut, $wgUseCopyrightUpload; |
| 468 | + protected function addUploadJS( ) { |
| 469 | + global $wgScriptPath, $wgMaxUploadFiles, $wgFileExtensions; |
379 | 470 | |
380 | | - $this->mHasWarning = true; |
381 | | - $this->mSessionKey = $this->stashSession(); |
382 | | - if( !$this->mSessionKey ) { |
383 | | - # Couldn't save file; an error has been displayed so let's go. |
384 | | - return; |
385 | | - } |
| 471 | + global $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview, $wgEnableAPI; |
| 472 | + global $wgOut; |
386 | 473 | |
387 | | - $wgOut->addHTML( '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" ); |
388 | | - $wgOut->addHTML( '<ul class="warning">' . $warning . "</ul>\n" ); |
| 474 | + $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck; |
| 475 | + $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview && $wgEnableAPI; |
389 | 476 | |
390 | | - $titleObj = SpecialPage::getTitleFor( 'Upload' ); |
391 | | - |
392 | | - if ( $wgUseCopyrightUpload ) { |
393 | | - $copyright = Xml::hidden( 'wpUploadCopyStatus', $this->mCopyrightStatus ) . "\n" . |
394 | | - Xml::hidden( 'wpUploadSource', $this->mCopyrightSource ) . "\n"; |
395 | | - } else { |
396 | | - $copyright = ''; |
397 | | - } |
398 | | - |
399 | | - $wgOut->addHTML( |
400 | | - Xml::hidden( 'wpIgnoreWarning', '1' ) . "\n" . |
401 | | - Xml::hidden( 'wpUploadDescription_' . $this->mFileIndex, $this->mComment ) . "\n" . |
402 | | - Xml::hidden( 'wpLicense', $this->mLicense ) . "\n" . |
403 | | - Xml::hidden( 'wpDestFile_' . $this->mFileIndex, $this->mDesiredDestName ) . "\n" . |
404 | | - Xml::hidden( 'wpWatchthis', $this->mWatchthis ) . "\n" . |
405 | | - "{$copyright}<br />" . |
406 | | - Xml::check( 'wpUpload_' . $this->mSessionKey, array( 'name' => 'wpUpload', 'id' => 'wpUpload', 'checked' => 'checked' ) ) . ' ' . |
407 | | - Xml::label( wfMsg( 'ignorewarning' ), 'wpUpload_' . $this->mSessionKey ) . '<br />' . |
408 | | - Xml::check( 'wpReUpload_' . $this->mSessionKey ) . |
409 | | - Xml::label( wfMsg( 'reuploaddesc' ), 'wpUpload_' . $this->mSessionKey ) |
| 477 | + $scriptVars = array( |
| 478 | + 'wgAjaxUploadDestCheck' => $useAjaxDestCheck, |
| 479 | + 'wgAjaxLicensePreview' => $useAjaxLicensePreview, |
| 480 | + 'wgUploadAutoFill' => !$this->mForReUpload && |
| 481 | + // If we received mDestFile from the request, don't autofill |
| 482 | + // the wpDestFile textbox |
| 483 | + $this->mDestFile === '', |
| 484 | + 'wgUploadSourceIds' => $this->mSourceIds, |
410 | 485 | ); |
411 | | - } |
412 | 486 | |
413 | | - function stashSession() { |
414 | | - $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); |
| 487 | + $wgOut->addScript( Skin::makeVariablesScript( $scriptVars ) ); |
| 488 | + |
| 489 | + // For <charinsert> support |
| 490 | + $wgOut->addScriptFile( 'edit.js' ); |
415 | 491 | |
416 | | - if( !$stash ) { |
417 | | - # Couldn't save the file. |
418 | | - return false; |
419 | | - } |
420 | | - |
421 | | - if( $this->mSessionKey == null ) { |
422 | | - $this->mSessionKey = mt_rand( 0, 0x7fffffff ); |
423 | | - } |
424 | | - $_SESSION['wsUploadData_' . $this->mFileIndex][$this->mSessionKey] = array( |
425 | | - 'mTempPath' => $stash, |
426 | | - 'mFileSize' => $this->mFileSize, |
427 | | - 'mSrcName' => $this->mSrcName, |
428 | | - 'mFileProps' => $this->mFileProps, |
429 | | - 'version' => self::SESSION_VERSION, |
430 | | - ); |
431 | | - return $this->mSessionKey; |
| 492 | + // changed |
| 493 | + $wgOut->addScriptFile( "$wgScriptPath/extensions/MultiUpload/multiupload.js" ); |
| 494 | + $newscriptVars = array('wgMaxUploadFiles' => $wgMaxUploadFiles, 'wgFileExtensions' => $wgFileExtensions); |
| 495 | + $wgOut->addScript( Skin::makeVariablesScript( $newscriptVars ) ); |
432 | 496 | } |
433 | 497 | |
434 | | - /** |
435 | | - * If we've modified the upload file we need to manually remove it |
436 | | - * on exit to clean up. |
437 | | - * @access private |
438 | | - */ |
439 | | - function cleanupTempFile() { |
440 | | - global $wgMaxUploadFiles; |
441 | | - for( $x = 0; $x < $wgMaxUploadFiles; $x++ ) { |
442 | | - $this->mTempPath = $this->mTempPathArray[$x]; |
443 | | - parent::cleanupTempFile(); |
444 | | - } |
445 | | - } |
446 | 498 | } |
| 499 | + |