diff --git a/lib/Cake/Test/Case/View/MediaViewTest.php b/lib/Cake/Test/Case/View/MediaViewTest.php index 3b723c0bf..4f9c1348e 100644 --- a/lib/Cake/Test/Case/View/MediaViewTest.php +++ b/lib/Cake/Test/Case/View/MediaViewTest.php @@ -35,8 +35,15 @@ class MediaViewTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $this->MediaView = $this->getMock('MediaView', array('_isActive', '_clearBuffer', '_flushBuffer')); - $this->MediaView->response = $this->getMock('CakeResponse'); + $this->MediaView = new MediaView(); + $this->MediaView->response = $this->getMock('CakeResponse', array( + '_isActive', + '_clearBuffer', + '_flushBuffer', + 'type', + 'header', + 'download' + )); } /** @@ -71,10 +78,10 @@ class MediaViewTest extends CakeTestCase { public function testRender() { $this->MediaView->viewVars = array( 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS, - 'id' => 'test_asset.css', - 'extension' => 'css', + 'id' => 'test_asset.css' ); - $this->MediaView->expects($this->exactly(2)) + + $this->MediaView->response->expects($this->exactly(1)) ->method('_isActive') ->will($this->returnValue(true)); @@ -83,23 +90,23 @@ class MediaViewTest extends CakeTestCase { ->with('css') ->will($this->returnArgument(0)); - $this->MediaView->response->expects($this->at(1)) + $this->MediaView->response->expects($this->at(0)) ->method('header') ->with(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' + 'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT', + 'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', + 'Last-Modified' => gmdate('D, d M Y H:i:s', time()) . ' GMT' )); $this->MediaView->response->expects($this->at(2)) ->method('header') - ->with(array( - 'Content-Length' => 31 - )); - $this->MediaView->response->expects($this->once())->method('send'); - $this->MediaView->expects($this->once())->method('_clearBuffer'); - $this->MediaView->expects($this->once())->method('_flushBuffer'); + ->with('Content-Length', 31); + + $this->MediaView->response->expects($this->once())->method('_clearBuffer'); + $this->MediaView->response->expects($this->exactly(1)) + ->method('_isActive') + ->will($this->returnValue(true)); + $this->MediaView->response->expects($this->once())->method('_flushBuffer'); ob_start(); $result = $this->MediaView->render(); @@ -118,25 +125,20 @@ class MediaViewTest extends CakeTestCase { $_SERVER['HTTP_USER_AGENT'] = 'Some generic browser'; $this->MediaView->viewVars = array( 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS, - 'id' => 'no_section.ini', - 'extension' => 'ini', + 'id' => 'no_section.ini' ); - $this->MediaView->expects($this->exactly(2)) - ->method('_isActive') - ->will($this->returnValue(true)); $this->MediaView->response->expects($this->exactly(1)) ->method('type') ->with('ini') ->will($this->returnValue(false)); - $this->MediaView->response->expects($this->at(1)) + $this->MediaView->response->expects($this->at(0)) ->method('header') ->with(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' + 'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT', + 'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', + 'Last-Modified' => gmdate('D, d M Y H:i:s', time()) . ' GMT' )); $this->MediaView->response->expects($this->once()) @@ -145,17 +147,17 @@ class MediaViewTest extends CakeTestCase { $this->MediaView->response->expects($this->at(3)) ->method('header') - ->with(array( - 'Accept-Ranges' => 'bytes' - )); + ->with('Accept-Ranges', 'bytes'); $this->MediaView->response->expects($this->at(4)) ->method('header') ->with('Content-Length', 35); - $this->MediaView->response->expects($this->once())->method('send'); - $this->MediaView->expects($this->once())->method('_clearBuffer'); - $this->MediaView->expects($this->once())->method('_flushBuffer'); + $this->MediaView->response->expects($this->once())->method('_clearBuffer'); + $this->MediaView->response->expects($this->exactly(1)) + ->method('_isActive') + ->will($this->returnValue(true)); + $this->MediaView->response->expects($this->once())->method('_flushBuffer'); ob_start(); $result = $this->MediaView->render(); @@ -178,48 +180,43 @@ class MediaViewTest extends CakeTestCase { $this->MediaView->viewVars = array( 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS, 'id' => 'no_section.ini', - 'extension' => 'ini', ); - $this->MediaView->expects($this->exactly(2)) - ->method('_isActive') - ->will($this->returnValue(true)); $this->MediaView->response->expects($this->at(0)) + ->method('header') + ->with(array( + 'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT', + 'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', + 'Last-Modified' => gmdate('D, d M Y H:i:s', time()) . ' GMT' + )); + + $this->MediaView->response->expects($this->at(1)) ->method('type') ->with('ini') ->will($this->returnValue(false)); - $this->MediaView->response->expects($this->at(1)) - ->method('header') - ->with(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' - )); - $this->MediaView->response->expects($this->at(2)) ->method('type') ->with('application/octetstream') ->will($this->returnValue(false)); - $this->MediaView->response->expects($this->once()) + $this->MediaView->response->expects($this->at(3)) ->method('download') ->with('no_section.ini'); $this->MediaView->response->expects($this->at(4)) ->method('header') - ->with(array( - 'Accept-Ranges' => 'bytes' - )); + ->with('Accept-Ranges', 'bytes'); $this->MediaView->response->expects($this->at(5)) ->method('header') ->with('Content-Length', 35); - $this->MediaView->response->expects($this->once())->method('send'); - $this->MediaView->expects($this->once())->method('_clearBuffer'); - $this->MediaView->expects($this->once())->method('_flushBuffer'); + $this->MediaView->response->expects($this->once())->method('_clearBuffer'); + $this->MediaView->response->expects($this->exactly(1)) + ->method('_isActive') + ->will($this->returnValue(true)); + $this->MediaView->response->expects($this->once())->method('_flushBuffer'); ob_start(); $result = $this->MediaView->render(); @@ -242,49 +239,44 @@ class MediaViewTest extends CakeTestCase { $this->MediaView->viewVars = array( 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS, 'id' => 'no_section.ini', - 'extension' => 'ini', 'name' => 'config' ); - $this->MediaView->expects($this->exactly(2)) - ->method('_isActive') - ->will($this->returnValue(true)); $this->MediaView->response->expects($this->at(0)) + ->method('header') + ->with(array( + 'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT', + 'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', + 'Last-Modified' => gmdate('D, d M Y H:i:s', time()) . ' GMT' + )); + + $this->MediaView->response->expects($this->at(1)) ->method('type') ->with('ini') ->will($this->returnValue(false)); - $this->MediaView->response->expects($this->at(1)) - ->method('header') - ->with(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' - )); - $this->MediaView->response->expects($this->at(2)) ->method('type') ->with('application/force-download') ->will($this->returnValue(false)); - $this->MediaView->response->expects($this->once()) + $this->MediaView->response->expects($this->at(3)) ->method('download') ->with('config.ini'); $this->MediaView->response->expects($this->at(4)) ->method('header') - ->with(array( - 'Accept-Ranges' => 'bytes' - )); + ->with('Accept-Ranges', 'bytes'); $this->MediaView->response->expects($this->at(5)) ->method('header') ->with('Content-Length', 35); - $this->MediaView->response->expects($this->once())->method('send'); - $this->MediaView->expects($this->once())->method('_clearBuffer'); - $this->MediaView->expects($this->once())->method('_flushBuffer'); + $this->MediaView->response->expects($this->once())->method('_clearBuffer'); + $this->MediaView->response->expects($this->exactly(1)) + ->method('_isActive') + ->will($this->returnValue(true)); + $this->MediaView->response->expects($this->once())->method('_flushBuffer'); ob_start(); $result = $this->MediaView->render(); @@ -296,29 +288,6 @@ class MediaViewTest extends CakeTestCase { } } -/** - * testConnectionAborted method - * - * @return void - */ - public function testConnectionAborted() { - $this->MediaView->viewVars = array( - 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS, - 'id' => 'test_asset.css', - 'extension' => 'css', - ); - - $this->MediaView->expects($this->once()) - ->method('_isActive') - ->will($this->returnValue(false)); - - $this->MediaView->response->expects($this->never()) - ->method('type'); - - $result = $this->MediaView->render(); - $this->assertFalse($result); - } - /** * testConnectionAbortedOnBuffering method * @@ -327,29 +296,22 @@ class MediaViewTest extends CakeTestCase { public function testConnectionAbortedOnBuffering() { $this->MediaView->viewVars = array( 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS, - 'id' => 'test_asset.css', - 'extension' => 'css', + 'id' => 'test_asset.css' ); - $this->MediaView->expects($this->at(0)) - ->method('_isActive') - ->will($this->returnValue(true)); - $this->MediaView->response->expects($this->any()) ->method('type') ->with('css') ->will($this->returnArgument(0)); - $this->MediaView->expects($this->at(1)) + $this->MediaView->response->expects($this->at(1)) ->method('_isActive') ->will($this->returnValue(false)); - $this->MediaView->response->expects($this->once())->method('send'); - $this->MediaView->expects($this->once())->method('_clearBuffer'); - $this->MediaView->expects($this->never())->method('_flushBuffer'); + $this->MediaView->response->expects($this->once())->method('_clearBuffer'); + $this->MediaView->response->expects($this->never())->method('_flushBuffer'); - $result = $this->MediaView->render(); - $this->assertFalse($result); + $this->MediaView->render(); } /** @@ -360,8 +322,7 @@ class MediaViewTest extends CakeTestCase { public function testRenderUpperExtension() { $this->MediaView->viewVars = array( 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'img' . DS, - 'id' => 'test_2.JPG', - 'extension' => 'JPG', + 'id' => 'test_2.JPG' ); $this->MediaView->response->expects($this->any()) @@ -369,30 +330,7 @@ class MediaViewTest extends CakeTestCase { ->with('jpg') ->will($this->returnArgument(0)); - $this->MediaView->expects($this->at(0)) - ->method('_isActive') - ->will($this->returnValue(true)); - - $this->MediaView->render(); - } - -/** - * Test downloading files with extension not explicitly set. - * - * @return void - */ - public function testRenderExtensionNotSet() { - $this->MediaView->viewVars = array( - 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'img' . DS, - 'id' => 'test_2.JPG', - ); - - $this->MediaView->response->expects($this->any()) - ->method('type') - ->with('jpg') - ->will($this->returnArgument(0)); - - $this->MediaView->expects($this->at(0)) + $this->MediaView->response->expects($this->at(0)) ->method('_isActive') ->will($this->returnValue(true)); diff --git a/lib/Cake/View/MediaView.php b/lib/Cake/View/MediaView.php index 49a3140a8..e4e6ea7e9 100644 --- a/lib/Cake/View/MediaView.php +++ b/lib/Cake/View/MediaView.php @@ -15,6 +15,7 @@ * @package Cake.View * @since CakePHP(tm) v 1.2.0.5714 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * @deprecated Deprecated since version 2.3, use CakeResponse::file() instead */ App::uses('View', 'View'); @@ -31,8 +32,6 @@ App::uses('CakeRequest', 'Network'); * - `id` The filename on the server's filesystem, including extension. * - `name` The filename that will be sent to the user, specified without the extension. * - `download` Set to true to set a `Content-Disposition` header. This is ideal for file downloads. - * - `extension` The extension of the file being served. This is used to set the mimetype. - * If not provided its extracted from filename provided as `id`. * - `path` The absolute path, including the trailing / on the server's filesystem to `id`. * - `mimeType` The mime type of the file if CakeResponse doesn't know about it. * Must be an associative array with extension as key and mime type as value eg. array('ini' => 'text/plain') @@ -59,23 +58,15 @@ App::uses('CakeRequest', 'Network'); */ class MediaView extends View { -/** - * Indicates whether response gzip compression was enabled for this class - * - * @var boolean - */ - protected $_compressionEnabled = false; - /** * Display or download the given file * * @param string $view Not used * @param string $layout Not used - * @return mixed - * @throws NotFoundException + * @return boolean */ public function render($view = null, $layout = null) { - $name = $download = $extension = $id = $modified = $path = $cache = $mimeType = $compress = null; + $name = $download = $id = $modified = $path = $cache = $mimeType = $compress = null; extract($this->viewVars, EXTR_OVERWRITE); if (is_dir($path)) { @@ -84,155 +75,31 @@ class MediaView extends View { $path = APP . $path . $id; } - if (!is_file($path)) { - if (Configure::read('debug')) { - throw new NotFoundException(sprintf('The requested file %s was not found', $path)); - } - throw new NotFoundException('The requested file was not found'); - } - if (is_array($mimeType)) { $this->response->type($mimeType); } - if (!isset($extension)) { - $extension = pathinfo($id, PATHINFO_EXTENSION); - } - - if ($this->_isActive()) { - $extension = strtolower($extension); - $chunkSize = 8192; - $buffer = ''; - $fileSize = @filesize($path); - $handle = fopen($path, 'rb'); - - if ($handle === false) { - return false; - } + if ($cache) { if (!empty($modified) && !is_numeric($modified)) { $modified = strtotime($modified, time()); } else { $modified = time(); } - if (!$extension || $this->response->type($extension) === false) { - $download = true; - } - - if ($cache) { - $this->response->cache($modified, $cache); - } else { - $this->response->header(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' - )); - } - - if ($download) { - $agent = env('HTTP_USER_AGENT'); - - if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) { - $contentType = 'application/octetstream'; - } elseif (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) { - $contentType = 'application/force-download'; - } - - if (!empty($contentType)) { - $this->response->type($contentType); - } - if (is_null($name)) { - $name = $id; - } elseif ($extension) { - $name .= '.' . $extension; - } - $this->response->download($name); - $this->response->header(array('Accept-Ranges' => 'bytes')); - - $httpRange = env('HTTP_RANGE'); - if (isset($httpRange)) { - list($toss, $range) = explode('=', $httpRange); - - $size = $fileSize - 1; - $length = $fileSize - $range; - - $this->response->header(array( - 'Content-Length' => $length, - 'Content-Range' => 'bytes ' . $range . $size . '/' . $fileSize - )); - - $this->response->statusCode(206); - fseek($handle, $range); - } else { - $this->response->header('Content-Length', $fileSize); - } - } else { - $this->response->header(array( - 'Content-Length' => $fileSize - )); - } - $this->_clearBuffer(); - if ($compress) { - $this->_compressionEnabled = $this->response->compress(); - } - - $this->response->send(); - return $this->_sendFile($handle); + $this->response->cache($modified, $cache); + } else { + $this->response->disableCache(); } - return false; - } - -/** - * Reads out a file handle, and echos the content to the client. - * - * @param resource $handle A file handle or stream - * @return void - */ - protected function _sendFile($handle) { - $chunkSize = 8192; - $buffer = ''; - while (!feof($handle)) { - if (!$this->_isActive()) { - fclose($handle); - return false; - } - set_time_limit(0); - $buffer = fread($handle, $chunkSize); - echo $buffer; - if (!$this->_compressionEnabled) { - $this->_flushBuffer(); - } + if ($name !== null) { + $name .= '.' . pathinfo($id, PATHINFO_EXTENSION); } - fclose($handle); - } + $this->response->file($path, compact('name', 'download')); -/** - * Returns true if connection is still active - * - * @return boolean - */ - protected function _isActive() { - return connection_status() == 0 && !connection_aborted(); - } - -/** - * Clears the contents of the topmost output buffer and discards them - * - * @return boolean - */ - protected function _clearBuffer() { - return @ob_end_clean(); - } - -/** - * Flushes the contents of the output buffer - * - * @return void - */ - protected function _flushBuffer() { - @flush(); - @ob_flush(); + if ($compress) { + $this->response->compress(); + } + $this->response->send(); + return true; } }