1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
19:
20: App::uses('DispatcherFilter', 'Routing');
21:
22: 23: 24: 25: 26: 27:
28: class AssetDispatcher extends DispatcherFilter {
29:
30: 31: 32: 33: 34: 35:
36: public $priority = 9;
37:
38: 39: 40: 41: 42: 43:
44: public function beforeDispatch(CakeEvent $event) {
45: $url = urldecode($event->data['request']->url);
46: if (strpos($url, '..') !== false || strpos($url, '.') === false) {
47: return;
48: }
49:
50: if ($result = $this->_filterAsset($event)) {
51: $event->stopPropagation();
52: return $result;
53: }
54:
55: $assetFile = $this->_getAssetFile($url);
56: if ($assetFile === null || !file_exists($assetFile)) {
57: return null;
58: }
59:
60: $response = $event->data['response'];
61: $event->stopPropagation();
62:
63: $response->modified(filemtime($assetFile));
64: if ($response->checkNotModified($event->data['request'])) {
65: return $response;
66: }
67:
68: $pathSegments = explode('.', $url);
69: $ext = array_pop($pathSegments);
70: $this->_deliverAsset($response, $assetFile, $ext);
71: return $response;
72: }
73:
74: 75: 76: 77: 78: 79: 80:
81: protected function _filterAsset(CakeEvent $event) {
82: $url = $event->data['request']->url;
83: $response = $event->data['response'];
84: $filters = Configure::read('Asset.filter');
85: $isCss = (
86: strpos($url, 'ccss/') === 0 ||
87: preg_match('#^(theme/([^/]+)/ccss/)|(([^/]+)(?<!css)/ccss)/#i', $url)
88: );
89: $isJs = (
90: strpos($url, 'cjs/') === 0 ||
91: preg_match('#^/((theme/[^/]+)/cjs/)|(([^/]+)(?<!js)/cjs)/#i', $url)
92: );
93:
94: if (($isCss && empty($filters['css'])) || ($isJs && empty($filters['js']))) {
95: $response->statusCode(404);
96: return $response;
97: }
98:
99: if ($isCss) {
100: include WWW_ROOT . DS . $filters['css'];
101: return $response;
102: }
103:
104: if ($isJs) {
105: include WWW_ROOT . DS . $filters['js'];
106: return $response;
107: }
108: }
109:
110: 111: 112: 113: 114: 115:
116: protected function _getAssetFile($url) {
117: $parts = explode('/', $url);
118: if ($parts[0] === 'theme') {
119: $themeName = $parts[1];
120: unset($parts[0], $parts[1]);
121: $fileFragment = implode(DS, $parts);
122: $path = App::themePath($themeName) . 'webroot' . DS;
123: return $path . $fileFragment;
124: }
125:
126: $plugin = Inflector::camelize($parts[0]);
127: if ($plugin && CakePlugin::loaded($plugin)) {
128: unset($parts[0]);
129: $fileFragment = implode(DS, $parts);
130: $pluginWebroot = CakePlugin::path($plugin) . 'webroot' . DS;
131: return $pluginWebroot . $fileFragment;
132: }
133: }
134:
135: 136: 137: 138: 139: 140: 141: 142:
143: protected function _deliverAsset(CakeResponse $response, $assetFile, $ext) {
144: ob_start();
145: $compressionEnabled = Configure::read('Asset.compress') && $response->compress();
146: if ($response->type($ext) == $ext) {
147: $contentType = 'application/octet-stream';
148: $agent = env('HTTP_USER_AGENT');
149: if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
150: $contentType = 'application/octetstream';
151: }
152: $response->type($contentType);
153: }
154: if (!$compressionEnabled) {
155: $response->header('Content-Length', filesize($assetFile));
156: }
157: $response->cache(filemtime($assetFile));
158: $response->send();
159: ob_clean();
160: if ($ext === 'css' || $ext === 'js') {
161: include $assetFile;
162: } else {
163: readfile($assetFile);
164: }
165:
166: if ($compressionEnabled) {
167: ob_end_flush();
168: }
169: }
170:
171: }
172: