* Renamed $handle => $op to avoid line breaks in a few spots.
Change-Id: I4598e685cc27552425a47f9d97eaeddaaf1a64a1
* All "storage paths" are of the format "mwstore://<backend>/<container>/<path>".
* The "<path>" portion is a relative path that uses UNIX file system (FS)
* notation, though any particular backend may not actually be using a local
- * filesystem.
- * Therefore, the relative paths are only virtual.
+ * filesystem. Therefore, the relative paths are only virtual.
*
* Backend contents are stored under wiki-specific container names by default.
* For legacy reasons, this has no effect for the FS backend class, and per-wiki
* 'dst' => <storage path>,
* 'content' => <string of new file contents>,
* 'overwrite' => <boolean>,
- * 'overwriteSame' => <boolean>
+ * 'overwriteSame' => <boolean>,
+ * 'disposition' => <Content-Disposition header value>
* );
* @endcode
*
* 'src' => <file system path>,
* 'dst' => <storage path>,
* 'overwrite' => <boolean>,
- * 'overwriteSame' => <boolean>
+ * 'overwriteSame' => <boolean>,
+ * 'disposition' => <Content-Disposition header value>
* )
* @endcode
*
* 'src' => <storage path>,
* 'dst' => <storage path>,
* 'overwrite' => <boolean>,
- * 'overwriteSame' => <boolean>
+ * 'overwriteSame' => <boolean>,
+ * 'disposition' => <Content-Disposition header value>
* )
* @endcode
*
* 'src' => <storage path>,
* 'dst' => <storage path>,
* 'overwrite' => <boolean>,
- * 'overwriteSame' => <boolean>
+ * 'overwriteSame' => <boolean>,
+ * 'disposition' => <Content-Disposition header value>
* )
* @endcode
*
* - overwriteSame : An error will not be given if a file already
* exists at the destination that has the same
* contents as the new contents to be written there.
+ * - disposition : When supplied, the backend will add a Content-Disposition
+ * header when GETs/HEADs of the destination file are made.
+ * Backends that don't support file metadata will ignore this.
+ * See http://tools.ietf.org/html/rfc6266 (since 1.20).
*
* $opts is an associative of boolean flags, including:
* - force : Operation precondition errors no longer trigger an abort.
* array(
* 'op' => 'create',
* 'dst' => <storage path>,
- * 'content' => <string of new file contents>
+ * 'content' => <string of new file contents>,
+ * 'disposition' => <Content-Disposition header value>
* )
* @endcode
* b) Copy a file system file into storage
* array(
* 'op' => 'store',
* 'src' => <file system path>,
- * 'dst' => <storage path>
+ * 'dst' => <storage path>,
+ * 'disposition' => <Content-Disposition header value>
* )
* @endcode
* c) Copy a file within storage
* array(
* 'op' => 'copy',
* 'src' => <storage path>,
- * 'dst' => <storage path>
+ * 'dst' => <storage path>,
+ * 'disposition' => <Content-Disposition header value>
* )
* @endcode
* d) Move a file within storage
* array(
* 'op' => 'move',
* 'src' => <storage path>,
- * 'dst' => <storage path>
+ * 'dst' => <storage path>,
+ * 'disposition' => <Content-Disposition header value>
* )
* @endcode
* e) Delete a file within storage
* @par Boolean flags for operations (operation-specific):
* - ignoreMissingSource : The operation will simply succeed and do
* nothing if the source file does not exist.
+ * - disposition : When supplied, the backend will add a Content-Disposition
+ * header when GETs/HEADs of the destination file are made.
+ * Backends that don't support file metadata will ignore this.
+ * See http://tools.ietf.org/html/rfc6266 (since 1.20).
*
* $opts is an associative of boolean flags, including:
* - bypassReadOnly : Allow writes in read-only mode (since 1.20)
return ( self::normalizeContainerPath( $path ) !== null );
}
+ /**
+ * Build a Content-Disposition header value per RFC 6266
+ *
+ * @param $type string One of (attachment, inline)
+ * @param $filename string Suggested file name (should not contain slashes)
+ * @return string
+ */
+ final public static function makeContentDisposition( $type, $filename ) {
+ $type = strtolower( $type );
+ $type = in_array( $type, array( 'inline', 'attachment' ) ) ? $type : 'inline';
+ return "$type; filename*=UTF-8''" . rawurlencode( basename( $filename ) );
+ }
+
/**
* Validate and normalize a relative storage path.
* Null is returned if the path involves directory traversal.
* - content : the raw file contents
* - dst : destination storage path
* - overwrite : overwrite any file that exists at the destination
+ * - disposition : Content-Disposition header value for the destination
* - async : Status will be returned immediately if supported.
* If the status is OK, then its value field will be
* set to a FileBackendStoreOpHandle object.
* - src : source path on disk
* - dst : destination storage path
* - overwrite : overwrite any file that exists at the destination
+ * - disposition : Content-Disposition header value for the destination
* - async : Status will be returned immediately if supported.
* If the status is OK, then its value field will be
* set to a FileBackendStoreOpHandle object.
* - src : source storage path
* - dst : destination storage path
* - overwrite : overwrite any file that exists at the destination
+ * - disposition : Content-Disposition header value for the destination
* - async : Status will be returned immediately if supported.
* If the status is OK, then its value field will be
* set to a FileBackendStoreOpHandle object.
* - src : source storage path
* - dst : destination storage path
* - overwrite : overwrite any file that exists at the destination
+ * - disposition : Content-Disposition header value for the destination
* - async : Status will be returned immediately if supported.
* If the status is OK, then its value field will be
* set to a FileBackendStoreOpHandle object.
/**
* Store a file into the backend from a file on the file system.
- * Parameters similar to FileBackendStore::storeInternal(), which include:
- * - src : source path on file system
- * - dst : destination storage path
- * - overwrite : do nothing and pass if an identical file exists at destination
- * - overwriteSame : override any existing file at destination
+ * Parameters for this operation are outlined in FileBackend::doOperations().
*/
class StoreFileOp extends FileOp {
/**
* @return array
*/
protected function allowedParams() {
- return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+ return array( array( 'src', 'dst' ),
+ array( 'overwrite', 'overwriteSame', 'disposition' ) );
}
/**
/**
* Create a file in the backend with the given content.
- * Parameters similar to FileBackendStore::createInternal(), which include:
- * - content : the raw file contents
- * - dst : destination storage path
- * - overwrite : do nothing and pass if an identical file exists at destination
- * - overwriteSame : override any existing file at destination
+ * Parameters for this operation are outlined in FileBackend::doOperations().
*/
class CreateFileOp extends FileOp {
protected function allowedParams() {
- return array( array( 'content', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+ return array( array( 'content', 'dst' ),
+ array( 'overwrite', 'overwriteSame', 'disposition' ) );
}
protected function doPrecheck( array &$predicates ) {
/**
* Copy a file from one storage path to another in the backend.
- * Parameters similar to FileBackendStore::copyInternal(), which include:
- * - src : source storage path
- * - dst : destination storage path
- * - overwrite : do nothing and pass if an identical file exists at destination
- * - overwriteSame : override any existing file at destination
+ * Parameters for this operation are outlined in FileBackend::doOperations().
*/
class CopyFileOp extends FileOp {
/**
* @return array
*/
protected function allowedParams() {
- return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+ return array( array( 'src', 'dst' ),
+ array( 'overwrite', 'overwriteSame', 'disposition' ) );
}
/**
/**
* Move a file from one storage path to another in the backend.
- * Parameters similar to FileBackendStore::moveInternal(), which include:
- * - src : source storage path
- * - dst : destination storage path
- * - overwrite : do nothing and pass if an identical file exists at destination
- * - overwriteSame : override any existing file at destination
+ * Parameters for this operation are outlined in FileBackend::doOperations().
*/
class MoveFileOp extends FileOp {
/**
* @return array
*/
protected function allowedParams() {
- return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+ return array( array( 'src', 'dst' ),
+ array( 'overwrite', 'overwriteSame', 'disposition' ) );
}
/**
/**
* Delete a file at the given storage path from the backend.
- * Parameters similar to FileBackendStore::deleteInternal(), which include:
- * - src : source storage path
- * - ignoreMissingSource : don't return an error if the file does not exist
+ * Parameters for this operation are outlined in FileBackend::doOperations().
*/
class DeleteFileOp extends FileOp {
/**
if ( !strlen( $obj->content_type ) ) { // special case
$obj->content_type = 'unknown/unknown';
}
+ // Set the Content-Disposition header if requested
+ if ( isset( $params['disposition'] ) ) {
+ $obj->headers['Content-Disposition'] = $params['disposition'];
+ }
if ( !empty( $params['async'] ) ) { // deferred
- $handle = $obj->write_async( $params['content'] );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Create', $handle );
+ $op = $obj->write_async( $params['content'] );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Create', $op );
if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
$status->value->affectedObjects[] = $obj;
}
if ( !strlen( $obj->content_type ) ) { // special case
$obj->content_type = 'unknown/unknown';
}
+ // Set the Content-Disposition header if requested
+ if ( isset( $params['disposition'] ) ) {
+ $obj->headers['Content-Disposition'] = $params['disposition'];
+ }
if ( !empty( $params['async'] ) ) { // deferred
wfSuppressWarnings();
$fp = fopen( $params['src'], 'rb' );
if ( !$fp ) {
$status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
} else {
- $handle = $obj->write_async( $fp, filesize( $params['src'] ), true );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Store', $handle );
+ $op = $obj->write_async( $fp, filesize( $params['src'] ), true );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Store', $op );
$status->value->resourcesToClose[] = $fp;
if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
$status->value->affectedObjects[] = $obj;
// (b) Actually copy the file to the destination
try {
$dstObj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
+ $hdrs = array(); // source file headers to override with new values
+ if ( isset( $params['disposition'] ) ) {
+ $hdrs['Content-Disposition'] = $params['disposition'];
+ }
if ( !empty( $params['async'] ) ) { // deferred
- $handle = $sContObj->copy_object_to_async( $srcRel, $dContObj, $dstRel );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Copy', $handle );
+ $op = $sContObj->copy_object_to_async( $srcRel, $dContObj, $dstRel, null, $hdrs );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Copy', $op );
if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
$status->value->affectedObjects[] = $dstObj;
}
} else { // actually write the object in Swift
- $sContObj->copy_object_to( $srcRel, $dContObj, $dstRel );
+ $sContObj->copy_object_to( $srcRel, $dContObj, $dstRel, null, $hdrs );
if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
$this->purgeCDNCache( array( $dstObj ) );
}
try {
$srcObj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
$dstObj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
+ $hdrs = array(); // source file headers to override with new values
+ if ( isset( $params['disposition'] ) ) {
+ $hdrs['Content-Disposition'] = $params['disposition'];
+ }
if ( !empty( $params['async'] ) ) { // deferred
- $handle = $sContObj->move_object_to_async( $srcRel, $dContObj, $dstRel );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Move', $handle );
+ $op = $sContObj->move_object_to_async( $srcRel, $dContObj, $dstRel, null, $hdrs );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Move', $op );
$status->value->affectedObjects[] = $srcObj;
if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
$status->value->affectedObjects[] = $dstObj;
}
} else { // actually write the object in Swift
- $sContObj->move_object_to( $srcRel, $dContObj, $dstRel );
+ $sContObj->move_object_to( $srcRel, $dContObj, $dstRel, null, $hdrs );
$this->purgeCDNCache( array( $srcObj ) );
if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
$this->purgeCDNCache( array( $dstObj ) );
$sContObj = $this->getContainer( $srcCont );
$srcObj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
if ( !empty( $params['async'] ) ) { // deferred
- $handle = $sContObj->delete_object_async( $srcRel );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Delete', $handle );
+ $op = $sContObj->delete_object_async( $srcRel );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Delete', $op );
$status->value->affectedObjects[] = $srcObj;
} else { // actually write the object in Swift
$sContObj->delete_object( $srcRel );