r108426 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r108425‎ | r108426 | r108427 >
Date:16:18, 9 January 2012
Author:mglaser
Status:reverted
Tags:
Comment:
Enables MediaWiki to use Windows Azure as a file backend.
Initial commit
Modified paths:
  • /trunk/phase3/extensions/WindowsAzureStorage (added) (history)
  • /trunk/phase3/extensions/WindowsAzureStorage/README.txt (added) (history)
  • /trunk/phase3/extensions/WindowsAzureStorage/WindowsAzureStorage.i18n.php (added) (history)
  • /trunk/phase3/extensions/WindowsAzureStorage/WindowsAzureStorage.php (added) (history)
  • /trunk/phase3/extensions/WindowsAzureStorage/includes (added) (history)
  • /trunk/phase3/extensions/WindowsAzureStorage/includes/filerepo (added) (history)
  • /trunk/phase3/extensions/WindowsAzureStorage/includes/filerepo/backend (added) (history)
  • /trunk/phase3/extensions/WindowsAzureStorage/includes/filerepo/backend/WindowsAzureFileBackend.php (added) (history)

Diff [purge]

Index: trunk/phase3/extensions/WindowsAzureStorage/includes/filerepo/backend/WindowsAzureFileBackend.php
@@ -0,0 +1,341 @@
 2+<?php
 3+/**
 4+ * @file
 5+ * @ingroup FileBackend
 6+ * @author Markus Glaser
 7+ * @author Robert Vogel
 8+ * @author Hallo Welt! - Medienwerkstatt GmbH for Microsoft Corp.
 9+ */
 10+
 11+/**
 12+ * Copied and modified from Swift FileBackend:
 13+ *
 14+ * Class for a Windows Azure Blob Storage based file backend.
 15+ * Status messages should avoid mentioning the Azure account name
 16+ * Likewise, error suppression should be used to avoid path disclosure.
 17+ *
 18+ * This requires the PHPAzure library to be present,
 19+ * which is available at http://phpazure.codeplex.com/.
 20+ * All of the library classes must be registed in $wgAutoloadClasses.
 21+ * You may use the WindowsAzureSDK MediaWiki extension to fulfill this
 22+ * requirement.
 23+ *
 24+ * @ingroup FileBackend
 25+ */
 26+class WindowsAzureFileBackend extends FileBackend {
 27+
 28+ function doStore( array $p ) {
 29+ return $this->doStoreInternal( $p );
 30+ }
 31+
 32+ function doCopy( array $p ) {
 33+ return $this->doCopyInternal( $p );
 34+ }
 35+
 36+ function doDelete( array $p ) {
 37+ return $this->doDeleteInternal( $p );
 38+ }
 39+
 40+ function doConcatenate( array $p ) {
 41+ return $this->dodoConcatenateInternal( $p );
 42+ }
 43+
 44+ function doCreate( array $p ) {
 45+ return $this->doCreateInternal( $p );
 46+ }
 47+
 48+ /**
 49+ * @see FileBackend::move()
 50+ */
 51+ protected function doMove( array $params ) {
 52+ // Copy source to dest
 53+ // TODO: Remove backend. I assume, this function does not need to be overridden.
 54+ $status = $this->backend->copy( $params );
 55+ if ( !$status->isOK() ) {
 56+ return $status;
 57+ }
 58+ // Delete source (only fails due to races or medium going down)
 59+ // TODO: Remoce backend
 60+ $status->merge( $this->backend->delete( array( 'src' => $params['src'] ) ) );
 61+ $status->setResult( true, $status->value ); // ignore delete() errors
 62+ return $status;
 63+ }
 64+
 65+ /** @var Microsoft_WindowsAzure_Storage_Blob */
 66+ protected $storageClient = null;
 67+
 68+ /** @var Array Map of container names to Azure container names */
 69+ protected $containerPaths = array();
 70+
 71+ /**
 72+ * @see FileBackend::__construct()
 73+ * Additional $config params include:
 74+ * azureHost : Windows Azure server URL
 75+ * azureAccount : Windows Azure user used by MediaWiki
 76+ * azureKey : Authentication key for the above user (used to get sessions)
 77+ * //azureContainer : Identifier of the container. (Optional. If not provided wikiId will be used as container name)
 78+ * containerPaths : Map of container names to Azure container names
 79+ */
 80+ public function __construct( array $config ) {
 81+ parent::__construct( $config );
 82+ $this->storageClient = new Microsoft_WindowsAzure_Storage_Blob(
 83+ $config['azureHost'],
 84+ $config['azureAccount'],
 85+ $config['azureKey']
 86+ );
 87+
 88+ $this->containerPaths = (array)$config['containerPaths'];
 89+ }
 90+
 91+ /**
 92+ * @see FileBackend::resolveContainerPath()
 93+ */
 94+ protected function resolveContainerPath( $container, $relStoragePath ) {
 95+ //Azure container naming conventions; http://msdn.microsoft.com/en-us/library/dd135715.aspx
 96+
 97+ if ( strlen( urlencode( $relStoragePath ) ) > 1024 ) {
 98+ return null;
 99+ }
 100+ // TODO: Should storagepath not be urlencoded?
 101+ return $relStoragePath;
 102+ }
 103+
 104+ /**
 105+ * @see FileBackend::doStoreInternal()
 106+ */
 107+ function doStoreInternal( array $params ) {
 108+ $status = Status::newGood();
 109+ // TODO: Use more telling names
 110+ list( $c, $dir ) = $this->resolveStoragePath( $params['dst'] );
 111+ try {
 112+ $result = $this->storageClient->putBlob( $c, $dir, $params['src']);
 113+ }
 114+ catch ( Exception $e ) {
 115+ // TODO: Read exception. Are there different ones?
 116+ $status->fatal( 'backend-fail-put' );
 117+ }
 118+ //error_log( __METHOD__.'::putBlob - result: '.print_r( $result, true ) );
 119+ return $status;
 120+ }
 121+
 122+ /**
 123+ * @see FileBackend::doCopyInternal()
 124+ */
 125+ function doCopyInternal( array $params ) {
 126+ $status = Status::newGood();
 127+ list( $srcContainer, $srcDir ) = $this->resolveStoragePath( $params['src'] );
 128+ list( $dstContainer, $dstDir ) = $this->resolveStoragePath( $params['dst'] );
 129+ // TODO: check for null
 130+ try {
 131+ $result = $this->storageClient->copyBlob( $srcContainer, $srcDir, $dstContainer, $dstDir);
 132+ }
 133+ catch ( Exception $e ) {
 134+ $status->fatal( 'backend-fail-copy', $e->getMessage() );
 135+ }
 136+ //error_log( __METHOD__.'::copyBlob - result: '.print_r( $result, true ) );
 137+ return $status;
 138+ }
 139+
 140+ /**
 141+ * @see FileBackend::doDeleteInternal()
 142+ */
 143+ function doDeleteInternal( array $params ) {
 144+ $status = Status::newGood();
 145+
 146+ list( $srcCont, $srcRel ) = $this->resolveStoragePath( $params['src'] );
 147+ if ( $srcRel === null ) {
 148+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
 149+ return $status;
 150+ }
 151+
 152+ // (a) Check the source container
 153+ try { //TODO: Unnecessary --> remove
 154+ $container = $this->storageClient->getContainer( $srcCont );
 155+ }
 156+ catch ( Exception $e ) {
 157+ // TODO: remove error_log
 158+ error_log( __METHOD__.':'.__LINE__.' '.$e->getMessage() );
 159+ $status->fatal( 'backend-fail-internal' );
 160+ return $status;
 161+ }
 162+
 163+ // (b) Actually delete the object
 164+ try {
 165+ $this->storageClient->deleteBlob( $srcCont, $srcRel );
 166+ }
 167+ catch ( Exception $e ) {
 168+ error_log( __METHOD__.':'.__LINE__.' '.$e->getMessage() );
 169+ $status->fatal( 'backend-fail-internal' );
 170+ }
 171+
 172+ return $status;
 173+ }
 174+
 175+ /**
 176+ * @see FileBackend::doCreateInternal()
 177+ */
 178+ function doCreateInternal( array $params ) {
 179+ $status = Status::newGood();
 180+
 181+ list( $dstCont, $dstRel ) = $this->resolveStoragePath( $params['dst'] );
 182+ if ( $dstRel === null ) {
 183+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
 184+ return $status;
 185+ }
 186+
 187+ // (a) Check if the destination object already exists
 188+ $blobExists = $this->storageClient->blobExists( $dstCont, $dstRel );
 189+ if ( $blobExists && empty( $params['overwriteDest'] ) ) { //Blob exists _and_ should not be overridden
 190+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
 191+ return $status;
 192+ }
 193+
 194+ // (b) Actually create the object
 195+ try {
 196+ // TODO: how do I know the container exists? Should we call prepare?
 197+ $this->storageClient->putBlobData( $dstCont, $dstRel, $params['content'] );
 198+ }
 199+ catch ( Exception $e ) {
 200+ error_log( __METHOD__.':'.__LINE__.' '.$e->getMessage() );
 201+ $status->fatal( 'backend-fail-internal' );
 202+ }
 203+
 204+ return $status;
 205+ }
 206+
 207+ /**
 208+ * @see FileBackend::prepare()
 209+ */
 210+ function prepare( array $params ) {
 211+ $status = Status::newGood();
 212+
 213+ list( $c, $dir ) = $this->resolveStoragePath( $params['dir'] );
 214+ if ( $dir === null ) {
 215+ $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
 216+ return $status; // invalid storage path
 217+ }
 218+ try {
 219+ $this->storageClient->createContainerIfNotExists( $c );
 220+ $this->storageClient->setContainerAcl( $c, Microsoft_WindowsAzure_Storage_Blob::ACL_PUBLIC );//TODO: Really set public?
 221+
 222+ //TODO: check if readable and writeable
 223+ //$container = $this->storageClient->getContainer( $c );
 224+ //$status->fatal( 'directoryreadonlyerror', $params['dir'] );
 225+ //$status->fatal( 'directorynotreadableerror', $params['dir'] );
 226+ }
 227+ catch (Exception $e ) {
 228+ $status->fatal( 'directorycreateerror', $params['dir'] );
 229+ return $status;
 230+ }
 231+ return $status;
 232+ }
 233+
 234+ /**
 235+ * @see FileBackend::resolveContainerName()
 236+ */
 237+ protected function resolveContainerName( $container ) {
 238+ //Azure container naming conventions; http://msdn.microsoft.com/en-us/library/dd135715.aspx
 239+ $container = strtolower($container);
 240+ $container = preg_replace( '#[^a-z0-9\-]#', '', $container );
 241+ // TODO: -test und test- geht auch nicht
 242+ $container = preg_replace( '#-{2,}#', '-', $container );
 243+
 244+ return $container;
 245+ }
 246+
 247+ /**
 248+ * @see FileBackend::secure()
 249+ */
 250+ function secure( array $params ) {
 251+ $status = Status::newGood();
 252+ // @TODO: restrict container from $this->swiftProxyUser
 253+ return $status; // badgers? We don't need no steenking badgers!
 254+ }
 255+
 256+ /**
 257+ * @see FileBackend::fileExists()
 258+ */
 259+ function fileExists( array $params ) {
 260+ list( $c, $dir ) = $this->resolveStoragePath( $params['src'] );
 261+ // TODO: null? Telling names
 262+ $exists = $this->storageClient->blobExists( $c, $dir );
 263+ //error_log( __METHOD__.'::blobExists - result: '.$exists );
 264+ return $exists;
 265+ }
 266+
 267+ /**
 268+ * @see FileBackend::getFileTimestamp()
 269+ */
 270+ function getFileTimestamp( array $params ) {
 271+ list( $srcCont, $srcRel ) = $this->resolveStoragePath( $params['src'] );
 272+ if ( $srcRel === null ) {
 273+ return false; // invalid storage path
 274+ }
 275+
 276+ $timestamp= false;
 277+ try {
 278+ //TODO Maybe use getBlobData()?
 279+ $blob = $this->storageClient->getBlobInstance( $srcCont, $srcRel );
 280+ $timestamp = wfTimestamp( TS_MW, $blob->lastmodified ); //TODO: Timezone?
 281+ } catch ( Exception $e ) { // some other exception?
 282+ error_log( __METHOD__.':'.__LINE__.' '.$e->getMessage() );
 283+ }
 284+ return $timestamp;
 285+ }
 286+
 287+ /**
 288+ * @see FileBackend::getFileList()
 289+ */
 290+ function getFileList( array $params ) {
 291+ $files = array();
 292+ list( $c, $dir ) = $this->resolveStoragePath( $params['dir'] );
 293+ try {
 294+ if ( $dir === null ) {
 295+ $blobs = $this->storageClient->listBlobs($c);
 296+ }
 297+ else {
 298+ $blobs = $this->storageClient->listBlobs( $c, $dir );//TODO:Check if $dir really is a startsequence of the blob name
 299+ }
 300+ foreach( $blobs as $blob ) {
 301+ $files[] = $blob->name;
 302+ }
 303+ }
 304+ catch( Exception $e ) {
 305+ error_log( __METHOD__.':'.__LINE__.' '.$e->getMessage() );
 306+ return null;
 307+ }
 308+
 309+ // if there are no files matching the prefix, return empty array
 310+ return $files;
 311+ }
 312+
 313+ /**
 314+ * @see FileBackend::getLocalCopy()
 315+ */
 316+ function getLocalCopy( array $params ) {
 317+ list( $srcCont, $srcRel ) = $this->resolveStoragePath( $params['src'] );
 318+ if ( $srcRel === null ) {
 319+ return null;
 320+ }
 321+
 322+ // Get source file extension
 323+ $ext = FileBackend::extensionFromPath( $srcRel );
 324+ // Create a new temporary file...
 325+ // TODO: Caution: tempfile should not write a local file.
 326+ $tmpFile = TempFSFile::factory( wfBaseName( $srcRel ) . '_', $ext );
 327+ if ( !$tmpFile ) {
 328+ return null;
 329+ }
 330+ $tmpPath = $tmpFile->getPath();
 331+
 332+ try {
 333+ $this->storageClient->getBlob( $srcCont, $srcRel, $tmpPath );
 334+ }
 335+ catch ( Exception $e ) {
 336+ error_log( __METHOD__.':'.__LINE__.' '.$e->getMessage() );
 337+ $tmpFile = null;
 338+ }
 339+
 340+ return $tmpFile;
 341+ }
 342+}
Property changes on: trunk/phase3/extensions/WindowsAzureStorage/includes/filerepo/backend/WindowsAzureFileBackend.php
___________________________________________________________________
Added: svn:eol-style
1343 + native
Index: trunk/phase3/extensions/WindowsAzureStorage/WindowsAzureStorage.i18n.php
@@ -0,0 +1,14 @@
 2+<?php
 3+$messages = array();
 4+
 5+$messages['en'] = array(
 6+ 'windowsazurestorage-desc' => 'This extension enables MediaWiki to store files in the Windows Azure cloud, using the Windows Azure Storage Service. Provided by [http://www.hallowelt.biz Hallo Welt! Medienwerkstatt GmbH].'
 7+);
 8+
 9+$messages['de-formal'] = array(
 10+ 'windowsazurestorage-desc' => 'Diese Erweiterung erm�glicht es MediaWiki Datien in der Windows Azure cloud �ber den Windows Azure Storage Service zu speichern. Bereitgestellt von [http://www.hallowelt.biz Hallo Welt! Medienwerkstatt GmbH].'
 11+);
 12+
 13+$messages['de'] = array(
 14+ 'windowsazurestorage-desc' => 'Diese Erweiterung erm�glicht es MediaWiki Datien in der Windows Azure cloud �ber den Windows Azure Storage Service zu speichern. Bereitgestellt von [http://www.hallowelt.biz Hallo Welt! Medienwerkstatt GmbH].'
 15+);
\ No newline at end of file
Property changes on: trunk/phase3/extensions/WindowsAzureStorage/WindowsAzureStorage.i18n.php
___________________________________________________________________
Added: svn:eol-style
116 + native
Index: trunk/phase3/extensions/WindowsAzureStorage/README.txt
@@ -0,0 +1 @@
 2+This extension contains the Microsoft "Windows Azure SDK for PHP v4.1.0" (http://phpazure.codeplex.com/) by REALDOLMEN.
\ No newline at end of file
Property changes on: trunk/phase3/extensions/WindowsAzureStorage/README.txt
___________________________________________________________________
Added: svn:eol-style
13 + native
Index: trunk/phase3/extensions/WindowsAzureStorage/WindowsAzureStorage.php
@@ -0,0 +1,59 @@
 2+<?php
 3+/*
 4+ (c) Hallo Welt! Medienwerkstatt GmbH, 2011 GPL
 5+
 6+ This program is free software; you can redistribute it and/or modify
 7+ it under the terms of the GNU General Public License as published by
 8+ the Free Software Foundation; either version 2 of the License, or
 9+ (at your option) any later version.
 10+
 11+ This program is distributed in the hope that it will be useful,
 12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
 13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14+ GNU General Public License for more details.
 15+
 16+ You should have received a copy of the GNU General Public License along
 17+ with this program; if not, write to the Free Software Foundation, Inc.,
 18+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 19+ http://www.gnu.org/copyleft/gpl.html
 20+*/
 21+
 22+if ( !defined( 'MEDIAWIKI' ) ) {
 23+ echo 'To install WindowsAzureStorage, put the following line in LocalSettings.php: include_once( "$IP/extensions/WindowsAzureStorage/WindowsAzureStorage.php" );'."\n";
 24+ exit( 1 );
 25+}
 26+
 27+$wgExtensionCredits['other'][] = array(
 28+ 'path' => __FILE__,
 29+ 'name' => 'WindowsAzureStorage',
 30+ 'author' => array( 'Hallo Welt! Medienwerkstatt GmbH' ),
 31+ 'url' => 'http://www.hallowelt.biz',
 32+ 'version' => '1.0.0',
 33+ 'descriptionmsg' => 'windowsazurestorage-desc',
 34+);
 35+
 36+$dir = dirname(__FILE__) . '/';
 37+$wgExtensionMessagesFiles['WindowsAzureStorage'] = $dir . 'WindowsAzureStorage.i18n.php';
 38+
 39+$wgAutoloadClasses['WindowsAzureFileBackend'] = $dir . 'includes/filerepo/backend/WindowsAzureFileBackend.php';
 40+
 41+/* Those are just development values. You may override them or specify your own backend definition in LocalSettings.php */
 42+$wgFileBackends[] = array(
 43+ 'name' => 'azure-backend',
 44+ 'class' => 'WindowsAzureFileBackend',
 45+ //'wikiId' => 'some_unique_ID',
 46+ 'lockManager' => 'nullLockManager',
 47+ 'azureHost' => 'http://127.0.0.1:10000',
 48+ 'azureAccount' => 'devstoreaccount1',
 49+ 'azureKey' => 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==',
 50+ //'azureContainer' => 'developcontainer',
 51+
 52+ //IMPORTANT: Mind the container naming conventions! http://msdn.microsoft.com/en-us/library/dd135715.aspx
 53+ 'containerPaths' => array(
 54+ 'media-public' => 'media-public',
 55+ 'media-thumb' => 'media-thumb',
 56+ 'media-deleted' => 'media-deleted',
 57+ 'media-temp' => 'media-temp',
 58+
 59+ )
 60+);
\ No newline at end of file
Property changes on: trunk/phase3/extensions/WindowsAzureStorage/WindowsAzureStorage.php
___________________________________________________________________
Added: svn:eol-style
161 + native

Follow-up revisions

RevisionCommit summaryAuthorDate
r108450Followup r108425, r108426...reedy18:41, 9 January 2012
r108732r108426/r108450: Consistency tweaks in preparation for adding extension to tr...raymond17:29, 12 January 2012
r108733r108426/r108450: Register extension for translatewiki.net.raymond17:30, 12 January 2012
r108752Fix extension credits URL: Link to MediaWiki.org (todo: please create extensi...raymond20:05, 12 January 2012

Status & tagging log