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