[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Implements Special:LinkSearch 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 * @ingroup SpecialPage 22 * @author Brion Vibber 23 */ 24 25 /** 26 * Special:LinkSearch to search the external-links table. 27 * @ingroup SpecialPage 28 */ 29 class LinkSearchPage extends QueryPage { 30 31 /** 32 * @var PageLinkRenderer 33 */ 34 protected $linkRenderer = null; 35 36 function setParams( $params ) { 37 $this->mQuery = $params['query']; 38 $this->mNs = $params['namespace']; 39 $this->mProt = $params['protocol']; 40 } 41 42 function __construct( $name = 'LinkSearch' ) { 43 parent::__construct( $name ); 44 45 // Since we don't control the constructor parameters, we can't inject services that way. 46 // Instead, we initialize services in the execute() method, and allow them to be overridden 47 // using the setServices() method. 48 } 49 50 /** 51 * Initialize or override the PageLinkRenderer LinkSearchPage collaborates with. 52 * Useful mainly for testing. 53 * 54 * @todo query logic and rendering logic should be split and also injected 55 * 56 * @param PageLinkRenderer $linkRenderer 57 */ 58 public function setPageLinkRenderer( 59 PageLinkRenderer $linkRenderer 60 ) { 61 $this->linkRenderer = $linkRenderer; 62 } 63 64 /** 65 * Initialize any services we'll need (unless it has already been provided via a setter). 66 * This allows for dependency injection even though we don't control object creation. 67 */ 68 private function initServices() { 69 if ( !$this->linkRenderer ) { 70 $lang = $this->getContext()->getLanguage(); 71 $titleFormatter = new MediaWikiTitleCodec( $lang, GenderCache::singleton() ); 72 $this->linkRenderer = new MediaWikiPageLinkRenderer( $titleFormatter ); 73 } 74 } 75 76 function isCacheable() { 77 return false; 78 } 79 80 function execute( $par ) { 81 $this->initServices(); 82 83 $this->setHeaders(); 84 $this->outputHeader(); 85 86 $out = $this->getOutput(); 87 $out->allowClickjacking(); 88 89 $request = $this->getRequest(); 90 $target = $request->getVal( 'target', $par ); 91 $namespace = $request->getIntorNull( 'namespace', null ); 92 93 $protocols_list = array(); 94 foreach ( $this->getConfig()->get( 'UrlProtocols' ) as $prot ) { 95 if ( $prot !== '//' ) { 96 $protocols_list[] = $prot; 97 } 98 } 99 100 $target2 = $target; 101 // Get protocol, default is http:// 102 $protocol = 'http://'; 103 $bits = wfParseUrl( $target ); 104 if ( isset( $bits['scheme'] ) && isset( $bits['delimiter'] ) ) { 105 $protocol = $bits['scheme'] . $bits['delimiter']; 106 // Make sure wfParseUrl() didn't make some well-intended correction in the 107 // protocol 108 if ( strcasecmp( $protocol, substr( $target, 0, strlen( $protocol ) ) ) === 0 ) { 109 $target2 = substr( $target, strlen( $protocol ) ); 110 } else { 111 // If it did, let LinkFilter::makeLikeArray() handle this 112 $protocol = ''; 113 } 114 } 115 116 $out->addWikiMsg( 117 'linksearch-text', 118 '<nowiki>' . $this->getLanguage()->commaList( $protocols_list ) . '</nowiki>', 119 count( $protocols_list ) 120 ); 121 $s = Html::openElement( 122 'form', 123 array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => wfScript() ) 124 ) . "\n" . 125 Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) . "\n" . 126 Html::openElement( 'fieldset' ) . "\n" . 127 Html::element( 'legend', array(), $this->msg( 'linksearch' )->text() ) . "\n" . 128 Xml::inputLabel( 129 $this->msg( 'linksearch-pat' )->text(), 130 'target', 131 'target', 132 50, 133 $target, 134 array( 135 // URLs are always ltr 136 'dir' => 'ltr', 137 ) 138 ) . "\n"; 139 140 if ( !$this->getConfig()->get( 'MiserMode' ) ) { 141 $s .= Html::namespaceSelector( 142 array( 143 'selected' => $namespace, 144 'all' => '', 145 'label' => $this->msg( 'linksearch-ns' )->text() 146 ), array( 147 'name' => 'namespace', 148 'id' => 'namespace', 149 'class' => 'namespaceselector', 150 ) 151 ); 152 } 153 154 $s .= Xml::submitButton( $this->msg( 'linksearch-ok' )->text() ) . "\n" . 155 Html::closeElement( 'fieldset' ) . "\n" . 156 Html::closeElement( 'form' ) . "\n"; 157 $out->addHTML( $s ); 158 159 if ( $target != '' ) { 160 $this->setParams( array( 161 'query' => $target2, 162 'namespace' => $namespace, 163 'protocol' => $protocol ) ); 164 parent::execute( $par ); 165 if ( $this->mMungedQuery === false ) { 166 $out->addWikiMsg( 'linksearch-error' ); 167 } 168 } 169 } 170 171 /** 172 * Disable RSS/Atom feeds 173 * @return bool 174 */ 175 function isSyndicated() { 176 return false; 177 } 178 179 /** 180 * Return an appropriately formatted LIKE query and the clause 181 * 182 * @param string $query Search pattern to search for 183 * @param string $prot Protocol, e.g. 'http://' 184 * 185 * @return array 186 */ 187 static function mungeQuery( $query, $prot ) { 188 $field = 'el_index'; 189 $dbr = wfGetDB( DB_SLAVE ); 190 191 if ( $query === '*' && $prot !== '' ) { 192 // Allow queries like 'ftp://*' to find all ftp links 193 $rv = array( $prot, $dbr->anyString() ); 194 } else { 195 $rv = LinkFilter::makeLikeArray( $query, $prot ); 196 } 197 198 if ( $rv === false ) { 199 // LinkFilter doesn't handle wildcard in IP, so we'll have to munge here. 200 $pattern = '/^(:?[0-9]{1,3}\.)+\*\s*$|^(:?[0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]*\*\s*$/'; 201 if ( preg_match( $pattern, $query ) ) { 202 $rv = array( $prot . rtrim( $query, " \t*" ), $dbr->anyString() ); 203 $field = 'el_to'; 204 } 205 } 206 207 return array( $rv, $field ); 208 } 209 210 function linkParameters() { 211 $params = array(); 212 $params['target'] = $this->mProt . $this->mQuery; 213 if ( $this->mNs !== null && !$this->getConfig()->get( 'MiserMode' ) ) { 214 $params['namespace'] = $this->mNs; 215 } 216 217 return $params; 218 } 219 220 function getQueryInfo() { 221 $dbr = wfGetDB( DB_SLAVE ); 222 // strip everything past first wildcard, so that 223 // index-based-only lookup would be done 224 list( $this->mMungedQuery, $clause ) = self::mungeQuery( $this->mQuery, $this->mProt ); 225 if ( $this->mMungedQuery === false ) { 226 // Invalid query; return no results 227 return array( 'tables' => 'page', 'fields' => 'page_id', 'conds' => '0=1' ); 228 } 229 230 $stripped = LinkFilter::keepOneWildcard( $this->mMungedQuery ); 231 $like = $dbr->buildLike( $stripped ); 232 $retval = array( 233 'tables' => array( 'page', 'externallinks' ), 234 'fields' => array( 235 'namespace' => 'page_namespace', 236 'title' => 'page_title', 237 'value' => 'el_index', 238 'url' => 'el_to' 239 ), 240 'conds' => array( 241 'page_id = el_from', 242 "$clause $like" 243 ), 244 'options' => array( 'USE INDEX' => $clause ) 245 ); 246 247 if ( $this->mNs !== null && !$this->getConfig()->get( 'MiserMode' ) ) { 248 $retval['conds']['page_namespace'] = $this->mNs; 249 } 250 251 return $retval; 252 } 253 254 /** 255 * @param Skin $skin 256 * @param object $result Result row 257 * @return string 258 */ 259 function formatResult( $skin, $result ) { 260 $title = new TitleValue( (int)$result->namespace, $result->title ); 261 $pageLink = $this->linkRenderer->renderHtmlLink( $title ); 262 263 $url = $result->url; 264 $urlLink = Linker::makeExternalLink( $url, $url ); 265 266 return $this->msg( 'linksearch-line' )->rawParams( $urlLink, $pageLink )->escaped(); 267 } 268 269 /** 270 * Override to check query validity. 271 * 272 * @param mixed $offset Numerical offset or false for no offset 273 * @param mixed $limit Numerical limit or false for no limit 274 */ 275 function doQuery( $offset = false, $limit = false ) { 276 list( $this->mMungedQuery, ) = LinkSearchPage::mungeQuery( $this->mQuery, $this->mProt ); 277 if ( $this->mMungedQuery === false ) { 278 $this->getOutput()->addWikiMsg( 'linksearch-error' ); 279 } else { 280 // For debugging 281 // Generates invalid xhtml with patterns that contain -- 282 //$this->getOutput()->addHTML( "\n<!-- " . htmlspecialchars( $this->mMungedQuery ) . " -->\n" ); 283 parent::doQuery( $offset, $limit ); 284 } 285 } 286 287 /** 288 * Override to squash the ORDER BY. 289 * We do a truncated index search, so the optimizer won't trust 290 * it as good enough for optimizing sort. The implicit ordering 291 * from the scan will usually do well enough for our needs. 292 * @return array 293 */ 294 function getOrderFields() { 295 return array(); 296 } 297 298 protected function getGroupName() { 299 return 'redirects'; 300 } 301 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |