r37161 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r37160‎ | r37161 | r37162 >
Date:12:04, 6 July 2008
Author:shinjiman
Status:old
Tags:
Comment:
Renaming WebDav.php to WebDavServer.php.
As the filename "WebDav.php" and "webdav.php" are treated as a same file on the case-insensistive file system used on their operating systems, such as Windows.
This will causes the problem while these two files are being checked out or updated.
Thanks StasFomin for pointing out this problem.
Modified paths:
  • /trunk/extensions/WebDAV/WebDavServer.php (added) (history)
  • /trunk/extensions/WebDAV/WebDavServer.php (added) (history)
  • /trunk/extensions/WebDAV/deltav.php (modified) (history)
  • /trunk/extensions/WebDAV/webdav.php (modified) (history)

Diff [purge]

Index: trunk/extensions/WebDAV/WebDavServer.php
@@ -0,0 +1,779 @@
 2+<?php
 3+
 4+define( 'MW_SEARCH_LIMIT', 50 );
 5+
 6+require_once( './lib/HTTP/WebDAV/Server.php' );
 7+
 8+class WebDavServer extends HTTP_WebDAV_Server {
 9+
 10+ function init() {
 11+ parent::init();
 12+
 13+ # Prepend script path component to path components
 14+ array_unshift( $this->pathComponents, array_pop( $this->baseUrlComponents['pathComponents'] ) );
 15+ }
 16+
 17+ function getAllowedMethods() {
 18+ return array( 'OPTIONS', 'PROPFIND', 'GET', 'HEAD', 'DELETE', 'PUT', 'REPORT', 'SEARCH' );
 19+ }
 20+
 21+ function options( &$serverOptions ) {
 22+ parent::options( &$serverOptions );
 23+
 24+ if ( $serverOptions['xpath']->evaluate( 'boolean(/D:options/D:activity-collection-set)' ) ) {
 25+ $this->setResponseHeader( 'Content-Type: text/xml; charset="utf-8"' );
 26+
 27+ echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
 28+ echo "<D:options-response xmlns:D=\"DAV:\">\n";
 29+ echo ' <D:activity-collection-set><D:href>' . $this->getUrl( array( 'path' => 'deltav.php/act' ) ) . "</D:href></D:activity-collection-set>\n";
 30+ echo "</D:options-response>\n";
 31+ }
 32+
 33+ return true;
 34+ }
 35+
 36+ function propfind( &$serverOptions ) {
 37+ if ( empty( $this->pathComponents ) ) {
 38+ return;
 39+ }
 40+ $pathComponent = array_shift( $this->pathComponents );
 41+ if ( $pathComponent != 'deltav.php' && $pathComponent != 'webdav.php' ) {
 42+ return;
 43+ }
 44+
 45+ if ( $pathComponent == 'deltav.php' ) {
 46+ if ( empty( $this->pathComponents ) ) {
 47+ return;
 48+ }
 49+ $pathComponent = array_shift( $this->pathComponents );
 50+ if ( $pathComponent != 'bc' && $pathComponent != 'bln' && $pathComponent != 'vcc' && $pathComponent != 'ver' ) {
 51+ return;
 52+ }
 53+
 54+ if ( $pathComponent == 'vcc' ) {
 55+ if ( empty( $this->pathComponents ) ) {
 56+ return;
 57+ }
 58+ $pathComponent = array_shift( $this->pathComponents );
 59+ if ( $pathComponent != 'default' ) {
 60+ return;
 61+ }
 62+ if ( !empty( $this->pathComponents ) ) {
 63+ return;
 64+ }
 65+
 66+ if ( isset( $serverOptions['label'] ) ) {
 67+ return $this->propfindBln( $serverOptions, $serverOptions['label'] );
 68+ }
 69+
 70+ return $this->propfindVcc( $serverOptions );
 71+ }
 72+
 73+ if ( empty( $this->pathComponents ) ) {
 74+ return;
 75+ }
 76+ $revisionId = array_shift( $this->pathComponents );
 77+
 78+ if ( $pathComponent == 'bc' ) {
 79+ return $this->propfindBc( $serverOptions, $revisionId, $this->pathComponents );
 80+ }
 81+
 82+ if ( !empty( $this->pathComponents ) ) {
 83+ return;
 84+ }
 85+
 86+ if ( $pathComponent == 'bln' ) {
 87+ return $this->propfindBln( $serverOptions, $revisionId );
 88+ }
 89+
 90+ return $this->propfindVer( $serverOptions, $revisionId );
 91+ }
 92+
 93+ if ( isset( $serverOptions['label'] ) ) {
 94+ # TODO: Verify revision belongs to this resource, or should we care?
 95+ return $this->propfindVer( $serverOptions, $serverOptions['label'] );
 96+ }
 97+
 98+ $serverOptions['namespaces']['http://subversion.tigris.org/xmlns/dav/'] = 'V';
 99+
 100+ $status = array();
 101+
 102+ # Handle root collection
 103+ if ( empty( $this->pathComponents ) ) {
 104+ global $wgSitename;
 105+
 106+ $response = array();
 107+ $response['path'] = 'webdav.php/';
 108+
 109+ # TODO: Use Main_Page revision?
 110+ $response['props'][] = WebDavServer::mkprop( 'checked-in', $this->getUrl( array( 'path' => 'deltav.php/ver' ) ) );
 111+
 112+ $response['props'][] = WebDavServer::mkprop( 'displayname', $wgSitename );
 113+ $response['props'][] = WebDavServer::mkprop( 'getcontentlength', 0 );
 114+ $response['props'][] = WebDavServer::mkprop( 'getcontenttype', 'httpd/unix-directory' );
 115+ $response['props'][] = WebDavServer::mkprop( 'resourcetype', 'collection' );
 116+ $response['props'][] = WebDavServer::mkprop( 'version-controlled-configuration', $this->getUrl( array( 'path' => 'deltav.php/vcc/default' ) ) );
 117+
 118+ $response['props'][] = WebDavServer::mkprop( 'http://subversion.tigris.org/xmlns/dav/', 'baseline-relative-path', null );
 119+
 120+ # TODO: Don't hardcode this
 121+ $response['props'][] = WebDavServer::mkprop( 'http://subversion.tigris.org/xmlns/dav/', 'repository-uuid', '87a9137c-6795-46f8-83b9-2ee953e66e08' );
 122+
 123+ $status[] = $response;
 124+
 125+ # Don't descend if depth is zero
 126+ if ( empty( $serverOptions['depth'] ) ) {
 127+ return $status;
 128+ }
 129+ }
 130+
 131+ # TODO: Use $wgMemc
 132+ $dbr =& wfGetDB( DB_SLAVE );
 133+
 134+ # TODO: Think harder about pages' hierarchical structure. The trouble is most filesystems don't support directories which themselves have file content, which is a problem for making pages descendents of other pages.
 135+ $where = array();
 136+ if ( !empty( $this->pathComponents ) ) {
 137+ $where[] = 'page_title = ' . $dbr->addQuotes( implode( '/', $this->pathComponents ) );
 138+ }
 139+
 140+ $whereClause = null;
 141+ if ( !empty( $where ) ) {
 142+ $whereClause = ' WHERE ' . implode( ' AND ', $where );
 143+ }
 144+ $results = $dbr->query( '
 145+ SELECT page_title, page_latest, page_len, page_touched
 146+ FROM page' . $whereClause );
 147+
 148+ while ( ( $result = $dbr->fetchRow( $results ) ) !== false ) {
 149+ # TODO: Should maybe not be using page_title as URL component, but it's currently what we do elsewhere
 150+ $title = Title::newFromUrl( $result[0] );
 151+
 152+ $response = array();
 153+ $response['path'] = 'webdav.php/' . $result[0];
 154+ $response['props'][] = WebDavServer::mkprop( 'checked-in', $this->getUrl( array( 'path' => 'deltav.php/ver/' . $result[1] ) ) );
 155+ $response['props'][] = WebDavServer::mkprop( 'displayname', $title->getText() );
 156+ $response['props'][] = WebDavServer::mkprop( 'getcontentlength', $result[2] );
 157+ $response['props'][] = WebDavServer::mkprop( 'getcontenttype', 'text/x-wiki' );
 158+ $response['props'][] = WebDavServer::mkprop( 'getlastmodified', wfTimestamp( TS_UNIX, $result[3] ) );
 159+ $response['props'][] = WebDavServer::mkprop( 'resourcetype', null );
 160+ $response['props'][] = WebDavServer::mkprop( 'version-controlled-configuration', $this->getUrl( array( 'path' => 'deltav.php/vcc/default' ) ) );
 161+
 162+ $response['props'][] = WebDavServer::mkprop( 'http://subversion.tigris.org/xmlns/dav/', 'baseline-relative-path', $result[0] );
 163+
 164+ $status[] = $response;
 165+ }
 166+
 167+ return $status;
 168+ }
 169+
 170+ function propfindBc( &$serverOptions, $revisionId, $pathComponents ) {
 171+ $serverOptions['namespaces']['http://subversion.tigris.org/xmlns/dav/'] = 'V';
 172+
 173+ $status = array();
 174+
 175+ # TODO: Verify $revisionId is valid
 176+ # Handle root collection
 177+ if ( empty( $pathComponents ) ) {
 178+ global $wgSitename;
 179+
 180+ $response = array();
 181+ $response['path'] = 'deltav.php/bc/' . $revisionId . '/';
 182+ # TODO: Use Main_Page revision?
 183+ $response['props'][] = WebDavServer::mkprop( 'checked-in', $this->getUrl( array( 'path' => 'deltav.php/ver' ) ) );
 184+
 185+ $response['props'][] = WebDavServer::mkprop( 'displayname', $wgSitename );
 186+ $response['props'][] = WebDavServer::mkprop( 'getcontentlength', 0 );
 187+ $response['props'][] = WebDavServer::mkprop( 'getcontenttype', 'httpd/unix-directory' );
 188+ $response['props'][] = WebDavServer::mkprop( 'resourcetype', 'collection' );
 189+ $response['props'][] = WebDavServer::mkprop( 'version-controlled-configuration', $this->getUrl( array( 'path' => 'deltav.php/vcc/default' ) ) );
 190+ $response['props'][] = WebDavServer::mkprop( 'version-name', null );
 191+
 192+ $response['props'][] = WebDavServer::mkprop( 'http://subversion.tigris.org/xmlns/dav/', 'baseline-relative-path', null );
 193+
 194+ # TODO: Don't hardcode this
 195+ $response['props'][] = WebDavServer::mkprop( 'http://subversion.tigris.org/xmlns/dav/', 'repository-uuid', '87a9137c-6795-46f8-83b9-2ee953e66e08' );
 196+
 197+ $status[] = $response;
 198+
 199+ # Don't descend if depth is zero
 200+ if ( empty( $serverOptions['depth'] ) ) {
 201+ return $status;
 202+ }
 203+ }
 204+
 205+ # TODO: Use $wgMemc
 206+ $dbr =& wfGetDB( DB_SLAVE );
 207+
 208+ # TODO: Think harder about pages' hierarchical structure. The trouble is most filesystems don't support directories which themselves have file content, which is a problem for making pages descendents of other pages.
 209+ $where = array();
 210+ $where[] = 'rev_page = page_id';
 211+ $where[] = 'rev_id <= ' . $dbr->addQuotes( $revisionId );
 212+ if ( !empty( $pathComponents ) ) {
 213+ $where[] = 'page_title = ' . $dbr->addQuotes( implode( '/', $pathComponents ) );
 214+ }
 215+
 216+ $whereClause = null;
 217+ if ( !empty( $where ) ) {
 218+ $whereClause = ' WHERE ' . implode( ' AND ', $where );
 219+ }
 220+ $results = $dbr->query( '
 221+ SELECT page_title, MAX(rev_id), page_len, page_touched
 222+ FROM page, revision' . $whereClause . '
 223+ GROUP BY page_id' );
 224+
 225+ while ( ( $result = $dbr->fetchRow( $results ) ) !== false ) {
 226+ # TODO: Should maybe not be using page_title as URL component, but it's currently what we do elsewhere
 227+ $title = Title::newFromUrl( $result[0] );
 228+
 229+ $response = array();
 230+ $response['path'] = 'deltav.php/bc/' . $revisionId . '/' . $result[0];
 231+ $response['props'][] = WebDavServer::mkprop( 'checked-in', $this->getUrl( array( 'path' => 'deltav.php/ver/' . $result[1] ) ) );
 232+ $response['props'][] = WebDavServer::mkprop( 'displayname', $title->getText() );
 233+ $response['props'][] = WebDavServer::mkprop( 'getcontentlength', $result[2] );
 234+ $response['props'][] = WebDavServer::mkprop( 'getcontenttype', 'text/x-wiki' );
 235+ $response['props'][] = WebDavServer::mkprop( 'getlastmodified', wfTimestamp( TS_UNIX, $result[3] ) );
 236+ $response['props'][] = WebDavServer::mkprop( 'resourcetype', null );
 237+ $response['props'][] = WebDavServer::mkprop( 'version-controlled-configuration', $this->getUrl( array( 'path' => 'deltav.php/vcc/default' ) ) );
 238+ $response['props'][] = WebDavServer::mkprop( 'version-name', $result[1] );
 239+
 240+ $response['props'][] = WebDavServer::mkprop( 'http://subversion.tigris.org/xmlns/dav/', 'baseline-relative-path', $result[0] );
 241+
 242+ # TODO: Don't hardcode this
 243+ $response['props'][] = WebDavServer::mkprop( 'http://subversion.tigris.org/xmlns/dav/', 'repository-uuid', '87a9137c-6795-46f8-83b9-2ee953e66e08' );
 244+
 245+ $status[] = $response;
 246+ }
 247+
 248+ return $status;
 249+ }
 250+
 251+ function propfindBln( &$serverOptions, $revisionId ) {
 252+ $response = array();
 253+ $response['path'] = 'bln/' . $revisionId;
 254+ $response['props'][] = WebDavServer::mkprop( 'baseline-collection', $this->getUrl( array( 'path' => 'deltav.php/bc/' . $revisionId . '/' ) ) );
 255+ $response['props'][] = WebDavServer::mkprop( 'version-name', $revisionId );
 256+
 257+ return array( $response );
 258+ }
 259+
 260+ function propfindVcc( &$serverOptions ) {
 261+ # TODO: Use $wgMemc
 262+ $dbr =& wfGetDB( DB_SLAVE );
 263+
 264+ $results = $dbr->query( '
 265+ SELECT MAX(rev_id)
 266+ FROM revision' );
 267+
 268+ if ( ( $result = $dbr->fetchRow( $results ) ) === false ) {
 269+ return;
 270+ }
 271+
 272+ $response = array();
 273+ $response['path'] = 'deltav.php/vcc/default';
 274+ $response['props'][] = WebDavServer::mkprop( 'checked-in', $this->getUrl( array( 'path' => 'deltav.php/bln/' . $result[0] ) ) );
 275+
 276+ return array( $response );
 277+ }
 278+
 279+ function propfindVer( &$serverOptions, $revisionId ) {
 280+ $response = array();
 281+ $response['path'] = 'deltav.php/ver/' . $revisionId;
 282+ $response['props'][] = WebDavServer::mkprop( 'resourcetype', null );
 283+ #$response['props'][] = WebDavServer::mkprop( 'version-controlled-configuration', $this->getUrl( array( 'path' => 'deltav.php/vcc/default' ) ) );
 284+
 285+ #$response['props'][] = WebDavServer::mkprop( 'http://subversion.tigris.org/xmlns/dav/', 'baseline-relative-path', null );
 286+
 287+ return array( $response );
 288+ }
 289+
 290+ function getRawPage() {
 291+ if ( empty( $this->pathComponents ) ) {
 292+ return;
 293+ }
 294+ $pathComponent = array_shift( $this->pathComponents );
 295+ if ( $pathComponent != 'deltav.php' && $pathComponent != 'webdav.php' ) {
 296+ return;
 297+ }
 298+
 299+ if ( $pathComponent == 'deltav.php' ) {
 300+ if ( empty( $this->pathComponents ) ) {
 301+ return;
 302+ }
 303+ $pathComponent = array_shift( $this->pathComponents );
 304+ if ( $pathComponent != 'bc' && $pathComponent != 'ver' ) {
 305+ return;
 306+ }
 307+
 308+ if ( empty( $this->pathComponents ) ) {
 309+ return;
 310+ }
 311+ $revisionId = array_shift( $this->pathComponents );
 312+
 313+ if ( $pathComponent == 'bc' ) {
 314+ $title = Title::newFromUrl( implode( '/', $this->pathComponents ) );
 315+ if (!isset( $title )) {
 316+ $title = Title::newMainPage();
 317+ }
 318+ } else {
 319+ if ( !empty( $this->pathComponents ) ) {
 320+ return;
 321+ }
 322+
 323+ $revision = Revision::newFromId( $revisionId );
 324+ $title = $revision->getTitle();
 325+ }
 326+ } else {
 327+ if ( isset( $serverOptions['label'] ) ) {
 328+ # TODO: Verify revision belongs to this resource, or should we care?
 329+ $revisionId = $serverOptions['label'];
 330+ }
 331+
 332+ $title = Title::newFromUrl( implode( '/', $this->pathComponents ) );
 333+ if (!isset( $title )) {
 334+ $title = Title::newMainPage();
 335+ }
 336+ }
 337+
 338+ $mediaWiki = new MediaWiki();
 339+ $article = $mediaWiki->articleFromTitle( $title );
 340+
 341+ $rawPage = new RawPage( $article );
 342+
 343+ if ( isset( $revisionId ) ) {
 344+ $rawPage->mOldId = $revisionId;
 345+ }
 346+
 347+ return $rawPage;
 348+ }
 349+
 350+ # RawPage::view handles Content-Type, Cache-Control, etc. and we don't want get_response_helper to overwrite, but MediaWiki doesn't let us get response headers. It could work if we kept setResponseHeader updated with headers_list on PHP 5.
 351+ function get_wrapper() {
 352+ $rawPage = $this->getRawPage();
 353+ if ( !isset( $rawPage ) ) {
 354+ $this->setResponseStatus( false, false );
 355+ return;
 356+ }
 357+
 358+ $rawPage->view();
 359+ }
 360+
 361+ function head_wrapper() {
 362+ $rawPage = $this->getRawPage();
 363+ if ( !isset( $rawPage ) ) {
 364+ $this->setResponseStatus( false, false );
 365+ return;
 366+ }
 367+
 368+ # TODO: Does MediaWiki handle HEAD requests specially?
 369+ ob_start();
 370+ $rawPage->view();
 371+ ob_end_clean();
 372+ }
 373+
 374+ function delete( $serverOptions ) {
 375+ global $wgUser;
 376+
 377+ if ( !$wgUser->isAllowed( 'delete' ) ) {
 378+ $this->setResponseStatus( '401 Unauthorized' );
 379+ return;
 380+ }
 381+
 382+ if ( wfReadOnly() ) {
 383+ $this->setResponseStatus( '403 Forbidden' );
 384+ return;
 385+ }
 386+
 387+ if ( empty( $this->pathComponents ) ) {
 388+ return;
 389+ }
 390+ $pathComponent = array_shift( $this->pathComponents );
 391+ if ( $pathComponent != 'webdav.php' ) {
 392+ return;
 393+ }
 394+
 395+ $title = Title::newFromUrl( implode( '/', $this->pathComponents ) );
 396+ if (!isset( $title )) {
 397+ $title = Title::newMainPage();
 398+ }
 399+
 400+ $mediaWiki = new MediaWiki();
 401+ $article = $mediaWiki->articleFromTitle( $title );
 402+
 403+ # Must check if article exists to avoid 500 Internal Server Error
 404+
 405+ # No way to get reason for deletion. Can't use null: MySQL returned error "<tt>1048: Column 'log_comment' cannot be null (localhost)</tt>".
 406+ $article->doDelete( null );
 407+ }
 408+
 409+ function put( $serverOptions ) {
 410+ global $wgUser;
 411+
 412+ if ( !$wgUser->isAllowed( 'edit' ) ) {
 413+ $this->setResponseStatus( '401 Unauthorized' );
 414+ return;
 415+ }
 416+
 417+ if ( wfReadOnly() ) {
 418+ $this->setResponseStatus( '403 Forbidden' );
 419+ return;
 420+ }
 421+
 422+ if ( empty( $this->pathComponents ) ) {
 423+ return;
 424+ }
 425+ $pathComponent = array_shift( $this->pathComponents );
 426+ if ( $pathComponent != 'webdav.php' ) {
 427+ return;
 428+ }
 429+
 430+ $title = Title::newFromUrl( implode( '/', $this->pathComponents ) );
 431+ if (!isset( $title )) {
 432+ $title = Title::newMainPage();
 433+ }
 434+
 435+ if ( !$title->exists() && !$title->userCan( 'create' ) ) {
 436+ $this->setResponseStatus( '401 Unauthorized' );
 437+ return;
 438+ }
 439+
 440+ $mediaWiki = new MediaWiki();
 441+ $article = $mediaWiki->articleFromTitle( $title );
 442+
 443+ if ( ( $handle = $this->openRequestBody() ) === false ) {
 444+ return;
 445+ }
 446+
 447+ $text = null;
 448+ while ( !feof( $handle ) ) {
 449+ if ( ( $buffer = fread( $handle, 4096 ) ) === false ) {
 450+ return;
 451+ }
 452+
 453+ $text .= $buffer;
 454+ }
 455+
 456+ $article->doEdit( $text, null );
 457+
 458+ return true;
 459+ }
 460+
 461+ function versionTreeReport( &$serverOptions ) {
 462+ if ( empty( $this->pathComponents ) ) {
 463+ return;
 464+ }
 465+ $pathComponent = array_shift( $this->pathComponents );
 466+ if ( $pathComponent != 'deltav.php' && $pathComponent != 'webdav.php' ) {
 467+ return;
 468+ }
 469+
 470+ $serverOptions['props'] = array();
 471+ foreach ( $serverOptions['xpath']->query( '/D:version-tree/D:prop/*' ) as $node) {
 472+ $serverOptions['props'][] = $this->mkprop( $node->namespaceURI, $node->localName, null );
 473+
 474+ # Namespace handling
 475+ if ( empty( $node->namespaceURI ) || empty( $node->prefix ) ) {
 476+ continue;
 477+ }
 478+
 479+ # http://bugs.php.net/bug.php?id=42082
 480+ #$serverOptions['namespaces'][$node->namespaceURI] = $node->prefix;
 481+ }
 482+
 483+ if (empty($serverOptions['props'])) {
 484+ $serverOptions['props'] = $serverOptions['xpath']->evaluate( 'local-name(/D:version-tree/*)' );
 485+ }
 486+
 487+ $status = array();
 488+
 489+ # Handle root collection
 490+ if ( empty( $this->pathComponents ) ) {
 491+ $response = array();
 492+ $response['props'][] = WebDavServer::mkprop( 'getcontentlength', 0 );
 493+ $response['props'][] = WebDavServer::mkprop( 'getcontenttype', 'httpd/unix-directory' );
 494+ $response['props'][] = WebDavServer::mkprop( 'resourcetype', 'collection' );
 495+
 496+ $status[] = $response;
 497+
 498+ # Don't descend if depth is zero
 499+ if ( empty( $serverOptions['depth'] ) ) {
 500+ return $status;
 501+ }
 502+ }
 503+
 504+ # TODO: Use $wgMemc
 505+ $dbr =& wfGetDB( DB_SLAVE );
 506+
 507+ # TODO: Think harder about pages' hierarchical structure. The trouble is most filesystems don't support directories which themselves have file content, which is a problem for making pages descendents of other pages.
 508+ $where = array();
 509+ $where[] = 'rev_page = page_id';
 510+ if ( !empty( $this->pathComponents ) ) {
 511+ $where[] = 'page_title = ' . $dbr->addQuotes( implode( '/', $this->pathComponents ) );
 512+ }
 513+
 514+ $whereClause = null;
 515+ if ( !empty( $where ) ) {
 516+ $whereClause = ' WHERE ' . implode( ' AND ', $where );
 517+ }
 518+ $results = $dbr->query( '
 519+ SELECT page_title, rev_id, rev_comment, rev_user_text, rev_len, rev_timestamp, rev_parent_id
 520+ FROM page, revision' . $whereClause );
 521+
 522+ $successors = array();
 523+ while ( ( $result = $dbr->fetchRow( $results ) ) !== false ) {
 524+ $response = array();
 525+ $response['path'] = 'deltav.php/ver/' . $result[1];
 526+ $response['props'][] = WebDavServer::mkprop( 'comment', $result[2] );
 527+ $response['props'][] = WebDavServer::mkprop( 'creator-displayname', $result[3] );
 528+ $response['props'][] = WebDavServer::mkprop( 'getcontentlength', $result[4] );
 529+ $response['props'][] = WebDavServer::mkprop( 'getcontenttype', 'text/x-wiki' );
 530+ $response['props'][] = WebDavServer::mkprop( 'getlastmodified', wfTimestamp( TS_UNIX, $result[5] ) );
 531+ $response['props'][] = WebDavServer::mkprop( 'predecessor-set', array( $result[6] ) );
 532+ $response['props'][] = WebDavServer::mkprop( 'resourcetype', null );
 533+ $response['props'][] = WebDavServer::mkprop( 'successor-set', array() );
 534+ $response['props'][] = WebDavServer::mkprop( 'version-name', $result[1] );
 535+
 536+ $status[$result[1]] = $response;
 537+
 538+ # Build successor-set
 539+ $successors[$result[6]][] = $result[1];
 540+ }
 541+
 542+ return $status;
 543+ }
 544+
 545+ function updateReport( &$serverOptions ) {
 546+ if ( empty( $this->pathComponents ) ) {
 547+ return;
 548+ }
 549+ $pathComponent = array_shift( $this->pathComponents );
 550+ if ( $pathComponent != 'deltav.php' ) {
 551+ return;
 552+ }
 553+
 554+ if ( empty( $this->pathComponents ) ) {
 555+ return;
 556+ }
 557+ $pathComponent = array_shift( $this->pathComponents );
 558+ if ( $pathComponent != 'vcc' ) {
 559+ return;
 560+ }
 561+
 562+ if ( empty( $this->pathComponents ) ) {
 563+ return;
 564+ }
 565+ $pathComponent = array_shift( $this->pathComponents );
 566+ if ( $pathComponent != 'default' ) {
 567+ return;
 568+ }
 569+ if ( !empty( $this->pathComponents ) ) {
 570+ return;
 571+ }
 572+
 573+ # TODO: Can we ignore this?
 574+ if ( isset( $serverOptions['label'] ) ) {
 575+ return;
 576+ }
 577+
 578+ $serverOptions['xpath']->registerNamespace( 'S', 'svn:' );
 579+
 580+ # TODO: Error checking?
 581+ $targetRevision = $serverOptions['xpath']->evaluate( 'string(/S:update-report/S:target-revision)' );
 582+
 583+ # src-path is a misnomer, it's a URL
 584+ $srcPath = $serverOptions['xpath']->evaluate( 'string(/S:update-report/S:src-path)' );
 585+ $srcComponents = $this->parseUrl( $srcPath );
 586+ $srcComponents['pathComponents'] = array_slice( $srcComponents['pathComponents'], count( $this->baseUrlComponents['pathComponents'] ) + 1 );
 587+
 588+ # TODO: Use $wgMemc
 589+ $dbr =& wfGetDB( DB_SLAVE );
 590+
 591+ $entryConditions = array();
 592+ foreach ( $serverOptions['xpath']->query( '/S:update-report/S:entry' ) as $node ) {
 593+ $entryConditions[$node->textContent] = null;
 594+ if ( !$node->hasAttribute( 'start-empty' ) ) {
 595+
 596+ # TODO: Error checking?
 597+ $entryConditions[$node->textContent] = 'new.rev_id > ' . $dbr->addQuotes( $node->getAttribute( 'rev' ) );
 598+ }
 599+ }
 600+
 601+ function cmp( $a, $b ) {
 602+ return strlen( $a ) - strlen( $b );
 603+ }
 604+ uksort( $entryConditions, 'cmp' );
 605+
 606+ $entryCondition = null;
 607+ foreach ( $entryConditions as $path => $revisionCondition ) {
 608+ if ( !empty( $path ) ) {
 609+ $pathCondition = '(page_title = ' . $dbr->addQuotes( $path ) . ' OR page_title LIKE \'' . $dbr->escapeLike( $path ) . '/%\')';
 610+
 611+ if ( !empty( $revisionCondition ) ) {
 612+ $revisionCondition = ' AND ' . $revisionCondition;
 613+ }
 614+ $revisionCondition = $pathCondition . $revisionCondition;
 615+
 616+ if ( !empty( $entryCondition ) ) {
 617+ $entryCondition = ' AND ' . $entryCondition;
 618+ }
 619+ $entryCondition = 'NOT ' . $pathCondition . $entryCondition;
 620+ }
 621+
 622+ if ( !empty( $revisionCondition ) ) {
 623+ if ( !empty( $entryCondition ) ) {
 624+ $revisionCondition = '(' . $revisionCondition;
 625+ $entryCondition = ' OR ' . $entryCondition . ')';
 626+ }
 627+ $entryCondition = $revisionCondition . $entryCondition;
 628+ }
 629+ }
 630+ if ( !empty( $entryCondition ) ) {
 631+ $entryCondition = ' AND ' . $entryCondition;
 632+ }
 633+
 634+ $where = array();
 635+ if ( !empty( $targetRevision ) ) {
 636+ $where[] = 'old.rev_id <= ' . $dbr->addQuotes( $targetRevision );
 637+ }
 638+ if ( !empty( $srcComponents['pathComponents'] ) ) {
 639+ $where[] = 'page_title = ' . $dbr->addQuotes( implode( '/', $srcComponents['pathComponents'] ) );
 640+ }
 641+
 642+ if ( empty( $targetRevision ) ) {
 643+ $results = $dbr->query( '
 644+ SELECT MAX(rev_id)
 645+ FROM revision' );
 646+
 647+ if ( ( $result = $dbr->fetchRow( $results ) ) === false ) {
 648+ return;
 649+ }
 650+
 651+ $targetRevision = $result[0];
 652+ }
 653+
 654+ $this->setResponseHeader( 'Content-Type: text/xml; charset="utf-8"', false );
 655+
 656+ echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
 657+ echo "<S:update-report xmlns:D=\"DAV:\" xmlns:S=\"svn:\" xmlns:V=\"http://subversion.tigris.org/xmlns/dav\" send-all=\"true\">\n";
 658+
 659+ # TODO: Get the revision from the report request
 660+ echo " <S:target-revision rev=\"$targetRevision\"/>\n";
 661+
 662+ # TODO: Use Main_Page revision?
 663+ echo " <S:open-directory rev=\"$targetRevision\">\n";
 664+ echo ' <D:checked-in><D:href>' . $this->getUrl( array( 'path' => 'deltav.php/ver' ) ) . "</D:href></D:checked-in>\n";
 665+
 666+ $whereClause = null;
 667+ if ( !empty( $where ) ) {
 668+ $whereClause = ' WHERE ' . implode( ' AND ', $where );
 669+ }
 670+
 671+ # SUM(new.rev_id IS NULL) is the number of revisions which didn't match the entry condition
 672+ # TODO: Invert entry condition to make getting the base revision cleaner
 673+ $results = $dbr->query( '
 674+ SELECT page_title, SUM(new.rev_id IS NULL), MAX(CASE WHEN new.rev_id IS NULL THEN old.rev_id ELSE NULL END), MAX(new.rev_id)
 675+ FROM page
 676+ JOIN revision AS old
 677+ ON page_id = old.rev_page
 678+ LEFT JOIN revision AS new
 679+ ON old.rev_id = new.rev_id' . $entryCondition . $whereClause . '
 680+ GROUP BY page_id
 681+ HAVING COUNT(new.rev_id)' );
 682+
 683+ while ( ( $result = $dbr->fetchRow( $results ) ) !== false ) {
 684+ $addOrOpen = 'add';
 685+ $baseRev = null;
 686+
 687+ $newText = Revision::newFromId( $result[3] )->revText();
 688+ $oldText = null;
 689+
 690+ if ( $result[1] > 0 ) {
 691+ $addOrOpen = 'open';
 692+ $baseRev = ' rev="' . $result[2] . '"';
 693+
 694+ $oldText = Revision::newFromId( $result[2] )->revText();
 695+ }
 696+
 697+ # TODO: Use only last path component
 698+ echo " <S:$addOrOpen-file name=\"$result[0]\"$baseRev>\n";
 699+
 700+ echo ' <D:checked-in><D:href>' . $this->getUrl( array( 'path' => 'deltav.php/ver/' . $result[3] ) ) . "</D:href></D:checked-in>\n";
 701+ echo ' <S:txdelta>' . base64_encode( $this->getSvnDiff( $oldText, $newText ) ) . "\n</S:txdelta>\n";
 702+ echo ' <S:prop><V:md5-checksum>' . md5( $newText ) . "</V:md5-checksum></S:prop>\n";
 703+ echo " </S:$addOrOpen-file>\n";
 704+ }
 705+
 706+ echo " </S:open-directory>\n";
 707+ echo "</S:update-report>\n";
 708+
 709+ return true;
 710+ }
 711+
 712+ function getSvnDiff( $oldText, $newText ) {
 713+ $instructions = chr( 0x80 | strlen( $newText ) );
 714+ if ( strlen( $newText ) > 0x37 ) {
 715+ $instructions = "\x80" . $this->encodeInt( strlen( $newText ) );
 716+ }
 717+
 718+ return "SVN\x00\x00"
 719+ . $this->encodeInt( strlen( $oldText ) )
 720+ . $this->encodeInt( strlen( $newText ) )
 721+ . $this->encodeInt( strlen( $instructions ) )
 722+ . $this->encodeInt( strlen( $newText ) )
 723+ . $instructions
 724+ . $newText;
 725+ }
 726+
 727+ function encodeInt( $int ) {
 728+ # Least seven bits
 729+ $bytes = chr( $int & 0x7f );
 730+
 731+ # Shift by seven bits until nothing remains
 732+ while ( 0 < $int >>= 7 ) {
 733+ # Prepend seven bits with the eighth bit, the continuation bit, set, to the string of bytes
 734+ $bytes = chr( $int & 0x7f | 0x80 ) . $bytes;
 735+ }
 736+
 737+ return $bytes;
 738+ }
 739+
 740+ function search( &$serverOptions ) {
 741+ $serverOptions['namespaces']['http://subversion.tigris.org/xmlns/dav/'] = 'V';
 742+
 743+ $status = array();
 744+
 745+ $search = SearchEngine::create();
 746+
 747+ # TODO: Use (int)$wgUser->getOption( 'searchlimit' );
 748+ $search->setLimitOffset( MW_SEARCH_LIMIT );
 749+
 750+ $results = $search->searchText( $serverOptions['xpath']->evaluate( 'string(/D:searchrequest/D:basicsearch/D:where/D:contains)' ) );
 751+
 752+ while ( ( $result = $results->next() ) !== false ) {
 753+ $title = $result->getTitle();
 754+ $revision = Revision::newFromTitle( $title );
 755+
 756+ $response = array();
 757+ $response['path'] = 'webdav.php/' . $title->getPrefixedUrl();
 758+ $response['props'][] = WebDavServer::mkprop( 'checked-in', $this->getUrl( array( 'path' => 'deltav.php/ver/' . $revision->getId() ) ) );
 759+ $response['props'][] = WebDavServer::mkprop( 'displayname', $title->getText() );
 760+ $response['props'][] = WebDavServer::mkprop( 'getcontentlength', $revision->getSize() );
 761+ $response['props'][] = WebDavServer::mkprop( 'getcontenttype', 'text/x-wiki' );
 762+ $response['props'][] = WebDavServer::mkprop( 'getlastmodified', wfTimestamp( TS_UNIX, $revision->mTimestamp ) );
 763+ $response['props'][] = WebDavServer::mkprop( 'resourcetype', null );
 764+ $response['props'][] = WebDavServer::mkprop( 'version-controlled-configuration', $this->getUrl( array( 'path' => 'deltav.php/vcc/default' ) ) );
 765+
 766+ $response['props'][] = WebDavServer::mkprop( 'http://subversion.tigris.org/xmlns/dav/', 'baseline-relative-path', $title->getFullUrl() );
 767+ $response['score'] = $result->getScore();
 768+
 769+ $status[] = $response;
 770+ }
 771+
 772+ # TODO: Check if we exceed our limit
 773+ #$response = array();
 774+ #$response['status'] = '507 Insufficient Storage';
 775+
 776+ #$status[] = $response;
 777+
 778+ return $status;
 779+ }
 780+}
Property changes on: trunk/extensions/WebDAV/WebDavServer.php
___________________________________________________________________
Added: svn:eol-style
1781 + native
Added: svn:keywords
2782 + Author Id Revision
Index: trunk/extensions/WebDAV/deltav.php
@@ -3,7 +3,7 @@
44 # Initialise common code
55 require_once( './includes/WebStart.php' );
66
7 -require_once( './WebDav.php' );
 7+require_once( './WebDavServer.php' );
88
99 $server = new WebDavServer();
1010 $server->handleRequest();
Index: trunk/extensions/WebDAV/webdav.php
@@ -3,7 +3,7 @@
44 # Initialise common code
55 require_once( './includes/WebStart.php' );
66
7 -require_once( './WebDav.php' );
 7+require_once( './WebDavServer.php' );
88
99 $server = new WebDavServer;
1010 $server->handleRequest();

Follow-up revisions

RevisionCommit summaryAuthorDate
r37162some leftout on the previous commit r37161shinjiman12:06, 6 July 2008

Status & tagging log