grommunio /
grommunio-web
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * Manager for including JS and CSS files into the desired order. |
||
| 5 | */ |
||
| 6 | class FileLoader { |
||
| 7 | |||
| 8 | private $source; |
||
| 9 | |||
| 10 | public function __construct() |
||
| 11 | { |
||
| 12 | // Unique cache file per grommunio Web location. |
||
| 13 | $basePath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . '.' . md5(realpath(__FILE__)); |
||
| 14 | $this->cacheFile = "$basePath-loadcache"; |
||
| 15 | $this->cacheSum = "$basePath-loadsum"; |
||
| 16 | $this->source = DEBUG_LOADER === LOAD_SOURCE; |
||
| 17 | } |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Obtain the list of Extjs & UX files |
||
| 21 | * |
||
| 22 | * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag |
||
| 23 | * to indicate which files should be loaded. |
||
| 24 | * @return array The array of Javascript files |
||
| 25 | */ |
||
| 26 | public function getExtjsJavascriptFiles($load) |
||
| 27 | { |
||
| 28 | $jsLoadingSequence = array(); |
||
| 29 | |||
| 30 | if ($load == LOAD_RELEASE) { |
||
| 31 | $jsLoadingSequence[] = "client/extjs/ext-base-all.js"; |
||
| 32 | $jsLoadingSequence[] = "client/extjs/ux/ux-all.js"; |
||
| 33 | $jsLoadingSequence[] = "client/extjs-mod/extjs-mod.js"; |
||
| 34 | $jsLoadingSequence[] = "client/tinymce/tinymce.min.js"; |
||
| 35 | $jsLoadingSequence[] = "client/third-party/ux-thirdparty.js"; |
||
| 36 | $jsLoadingSequence[] = "client/dompurify/purify.js"; |
||
| 37 | } else if ($load == LOAD_DEBUG) { |
||
| 38 | $jsLoadingSequence[] = "client/extjs/ext-base-debug.js"; |
||
| 39 | $jsLoadingSequence[] = "client/extjs/ext-all-debug.js"; |
||
| 40 | $jsLoadingSequence[] = "client/extjs/ux/ux-all-debug.js"; |
||
| 41 | $jsLoadingSequence[] = "client/extjs-mod/extjs-mod-debug.js"; |
||
| 42 | $jsLoadingSequence[] = "client/tinymce/tinymce.js"; |
||
| 43 | $jsLoadingSequence[] = "client/third-party/ux-thirdparty-debug.js"; |
||
| 44 | $jsLoadingSequence[] = "client/dompurify/purify.js"; |
||
| 45 | } else { |
||
| 46 | $jsLoadingSequence[] = "client/extjs/ext-base-debug.js"; |
||
| 47 | $jsLoadingSequence[] = "client/extjs/ext-all-debug.js"; |
||
| 48 | $jsLoadingSequence[] = "client/extjs/ux/ux-all-debug.js"; |
||
| 49 | $jsLoadingSequence = array_merge( |
||
| 50 | $jsLoadingSequence, |
||
| 51 | $this->buildJSLoadingSequence( |
||
| 52 | $this->getListOfFiles('js', 'client/extjs-mod') |
||
| 53 | ) |
||
| 54 | ); |
||
| 55 | $jsLoadingSequence[] = "client/tinymce/tinymce.js"; |
||
| 56 | $jsLoadingSequence[] = "client/dompurify/purify.js"; |
||
| 57 | $jsLoadingSequence = array_merge( |
||
| 58 | $jsLoadingSequence, |
||
| 59 | $this->buildJSLoadingSequence( |
||
| 60 | $this->getListOfFiles('js', 'client/third-party') |
||
| 61 | ) |
||
| 62 | ); |
||
| 63 | } |
||
| 64 | |||
| 65 | return $jsLoadingSequence; |
||
| 66 | } |
||
| 67 | |||
| 68 | /** |
||
| 69 | * Obtain the list of Extjs & UX files |
||
| 70 | * |
||
| 71 | * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag |
||
| 72 | * to indicate which files should be loaded. |
||
| 73 | * @return array The array of CSS files |
||
| 74 | */ |
||
| 75 | public function getExtjsCSSFiles($load) |
||
| 76 | { |
||
| 77 | return array("client/extjs/resources/css/ext-all-ux.css"); |
||
| 78 | } |
||
| 79 | |||
| 80 | /** |
||
| 81 | * Obtain the list of grommunio Web files |
||
| 82 | * |
||
| 83 | * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag |
||
| 84 | * to indicate which files should be loaded. |
||
| 85 | * @param array $libFiles (optional) library files when $load = LOAD_SOURCE |
||
| 86 | * @return array The array of Javascript files |
||
| 87 | */ |
||
| 88 | public function getZarafaJavascriptFiles($load, $libFiles = Array()) |
||
| 89 | { |
||
| 90 | $jsLoadingSequence = array(); |
||
| 91 | |||
| 92 | if ($load == LOAD_RELEASE) { |
||
| 93 | $jsLoadingSequence[] = "client/grommunio.js"; |
||
| 94 | } else if ($load == LOAD_DEBUG) { |
||
| 95 | $jsLoadingSequence[] = "client/grommunio-debug.js"; |
||
| 96 | } else { |
||
| 97 | $jsLoadingSequence = array_merge( |
||
| 98 | $jsLoadingSequence, |
||
| 99 | $this->buildJSLoadingSequence( |
||
| 100 | $this->getListOfFiles('js', 'client/zarafa'), |
||
| 101 | Array('client/zarafa/core'), |
||
| 102 | $libFiles |
||
| 103 | ) |
||
| 104 | ); |
||
| 105 | } |
||
| 106 | |||
| 107 | return $jsLoadingSequence; |
||
| 108 | } |
||
| 109 | |||
| 110 | /** |
||
| 111 | * Obtain the list of all Javascript files as registered by the plugins. |
||
| 112 | * |
||
| 113 | * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag |
||
| 114 | * to indicate which files should be loaded. |
||
| 115 | * @param array $libFiles (optional) library files when $load = LOAD_SOURCE |
||
| 116 | * @return array The array of Javascript files |
||
| 117 | */ |
||
| 118 | public function getPluginJavascriptFiles($load, $libFiles = Array()) |
||
| 119 | { |
||
| 120 | if ($load === LOAD_SOURCE) { |
||
| 121 | return $this->buildJSLoadingSequence( |
||
| 122 | $GLOBALS['PluginManager']->getClientFiles($load), |
||
| 123 | Array(), |
||
| 124 | $libFiles |
||
| 125 | ); |
||
| 126 | } else { |
||
| 127 | return $GLOBALS['PluginManager']->getClientFiles($load); |
||
| 128 | } |
||
| 129 | } |
||
| 130 | |||
| 131 | /** |
||
| 132 | * Obtain the list of all CSS files as registered by the plugins. |
||
| 133 | * |
||
| 134 | * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag |
||
| 135 | * to indicate which files should be loaded. |
||
| 136 | * @return array The array of CSS files |
||
| 137 | */ |
||
| 138 | public function getPluginCSSFiles($load) |
||
| 139 | { |
||
| 140 | return $GLOBALS['PluginManager']->getResourceFiles($load); |
||
| 141 | } |
||
| 142 | |||
| 143 | /** |
||
| 144 | * Obtain the list of all Javascript files as provided by plugins using PluginManager#triggerHook |
||
| 145 | * for the hook 'server.main.include.jsfiles'. |
||
| 146 | * |
||
| 147 | * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag |
||
| 148 | * to indicate which files should be loaded. |
||
| 149 | * @return array The array of Javascript files |
||
| 150 | */ |
||
| 151 | public function getRemoteJavascriptFiles($load) |
||
| 152 | { |
||
| 153 | $files = Array(); |
||
| 154 | $GLOBALS['PluginManager']->triggerHook('server.main.include.jsfiles', Array('load' => $load, 'files'=> & $files)); |
||
| 155 | return $files; |
||
| 156 | } |
||
| 157 | |||
| 158 | /** |
||
| 159 | * Obtain the list of all CSS files as provided by plugins using PluginManager#triggerHook |
||
| 160 | * for the hook 'server.main.include.cssfiles'. |
||
| 161 | * |
||
| 162 | * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag |
||
| 163 | * to indicate which files should be loaded. |
||
| 164 | * @return array The array of CSS files |
||
| 165 | */ |
||
| 166 | public function getRemoteCSSFiles($load) |
||
| 167 | { |
||
| 168 | $files = Array(); |
||
| 169 | $GLOBALS['PluginManager']->triggerHook('server.main.include.cssfiles', Array('load' => $load, 'files'=> & $files)); |
||
| 170 | return $files; |
||
| 171 | } |
||
| 172 | |||
| 173 | /** |
||
| 174 | * Print each file on a new line using the given $template |
||
| 175 | * @param Array $files The files to print |
||
| 176 | * @param String $template The template used to print each file, the string {file} will |
||
| 177 | * be replaced with the filename |
||
| 178 | * @param Boolean $base True if only the basename of the file must be printed |
||
| 179 | * @param Boolean $concatVersion True if concatenate unique webapp version |
||
| 180 | * with file name to avoid the caching issue. |
||
| 181 | */ |
||
| 182 | public function printFiles($files, $template = '{file}', $base = false, $concatVersion = true) |
||
| 183 | { |
||
| 184 | foreach($files as $file) { |
||
| 185 | $file = $base === true ? basename($file) : $file; |
||
| 186 | if($concatVersion) { |
||
| 187 | $file = $file."?version=".$this->getVersion(); |
||
| 188 | } |
||
| 189 | echo str_replace('{file}', $file, $template) . PHP_EOL; |
||
| 190 | } |
||
| 191 | } |
||
| 192 | |||
| 193 | /** |
||
| 194 | * Return grommunio Web version. |
||
| 195 | * @return String returns grommunio Web version |
||
| 196 | */ |
||
| 197 | public function getVersion() |
||
| 198 | { |
||
| 199 | return trim(file_get_contents('version')); |
||
| 200 | } |
||
| 201 | |||
| 202 | /** |
||
| 203 | * getJavascriptFiles |
||
| 204 | * |
||
| 205 | * Scanning files and subdirectories that can be found within the supplied |
||
| 206 | * path and add all located Javascript files to a list. |
||
| 207 | * @param $path String Path of the directory to scan |
||
| 208 | * @param $recursive Boolean If set to true scans subdirectories as well |
||
| 209 | * @param $excludeFiles Array Optional Paths of files or directories that |
||
| 210 | * are excluded from the search. |
||
| 211 | * @return Array List of arrays containing the paths to files that have to be included. |
||
| 212 | */ |
||
| 213 | public function getJavascriptFiles($path, $recursive = true, $excludeFiles = Array()) { |
||
| 214 | return $this->getListOfFiles('js', $path, $recursive, $excludeFiles); |
||
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * getCSSFiles |
||
| 219 | * |
||
| 220 | * Scanning files and subdirectories that can be found within the supplied |
||
| 221 | * path and add all located CSS files to a list. |
||
| 222 | * @param $path String Path of the directory to scan |
||
| 223 | * @param $recursive Boolean If set to true scans subdirectories as well |
||
| 224 | * @param $excludeFiles Array Optional Paths of files or directories that |
||
| 225 | * are excluded from the search. |
||
| 226 | * @return Array List of arrays containing the paths to files that have to be included. |
||
| 227 | */ |
||
| 228 | public function getCSSFiles($path, $recursive = true, $excludeFiles = Array()) { |
||
| 229 | return $this->getListOfFiles('css', $path, $recursive, $excludeFiles); |
||
| 230 | } |
||
| 231 | |||
| 232 | /** |
||
| 233 | * getListOfFiles |
||
| 234 | * |
||
| 235 | * Scanning files and subdirectories that can be found within the supplied |
||
| 236 | * path and add the files to a list. |
||
| 237 | * @param $ext The extension of files that are included ("js" or "css") |
||
| 238 | * @param $path String Path of the directory to scan |
||
| 239 | * @param $recursive Boolean If set to true scans subdirectories as well |
||
| 240 | * @param $excludeFiles Array Optional Paths of files or directories that |
||
| 241 | * are excluded from the search. |
||
| 242 | * @return Array List of arrays containing the paths to files that have to be included. |
||
| 243 | */ |
||
| 244 | private function getListOfFiles($ext, $path, $recursive = true, $excludeFiles = Array()) { |
||
| 245 | /* |
||
| 246 | * We are using two lists of files to make sure the files from the |
||
| 247 | * subdirectories are added after the current directory files. |
||
| 248 | */ |
||
| 249 | $files = Array(); |
||
| 250 | $subDirFiles = Array(); |
||
| 251 | |||
| 252 | $dir = opendir($path); |
||
| 253 | if (!is_resource($dir)) { |
||
| 254 | return $files; |
||
| 255 | } |
||
| 256 | |||
| 257 | while(($file = readdir($dir)) !== false) |
||
| 258 | { |
||
| 259 | $filepath = $path.'/'.$file; |
||
| 260 | // Skip entries like ".", ".." and ".svn" |
||
| 261 | if (substr($file,0,1)!="." && !in_array($filepath, $excludeFiles)){ |
||
| 262 | // Make sure we have files to include |
||
| 263 | $info = pathinfo($filepath, PATHINFO_EXTENSION); |
||
| 264 | |||
| 265 | if(is_file($filepath) && $info == $ext){ |
||
| 266 | $files[] = $filepath; |
||
| 267 | // Subdirectories will be scanned as well |
||
| 268 | } elseif($recursive && is_dir($filepath)){ |
||
| 269 | $subDirFiles = array_merge($subDirFiles, $this->getListOfFiles($ext, $filepath, $recursive, $excludeFiles)); |
||
| 270 | } |
||
| 271 | } |
||
| 272 | } |
||
| 273 | |||
| 274 | /* |
||
| 275 | * Make the lists alphabetically sorted, doing this separate makes sure |
||
| 276 | * the subdirectories are added after the files in the directory above. |
||
| 277 | */ |
||
| 278 | sort($files); |
||
| 279 | sort($subDirFiles); |
||
| 280 | $files = array_merge($files, $subDirFiles); |
||
| 281 | |||
| 282 | return $files; |
||
| 283 | } |
||
| 284 | |||
| 285 | /** |
||
| 286 | * buildJSLoadingSequence |
||
| 287 | * |
||
| 288 | * Will build the correct loading sequence for the JS files in application based on the class, |
||
| 289 | * extends and depends statements in the files itself. It will first extract the class |
||
| 290 | * definitions and dependencies. It will put that information in a list that holds the |
||
| 291 | * dependencies for each file. With that list the proper sequence of loading can be constructed. |
||
| 292 | * Files that originate from any of the specified coreFiles folders will be marked as core files. |
||
| 293 | * @param $files Array List of files that have to be included. |
||
| 294 | * @param $coreFiles Array (Optional) List of folders that contain core files. |
||
| 295 | * @param $libFiles Array (Optional) List of files that is used as library (and can contain |
||
| 296 | * classed which are depended upon by the given files). |
||
| 297 | * @return Array List of files that are sorted in the correct sequence |
||
| 298 | */ |
||
| 299 | private function buildJSLoadingSequence($files, $coreFiles = Array(), $libFiles = Array()){ |
||
| 300 | // Create a lookup table to easily get the classes which are defined in the library files |
||
| 301 | $libFileLookup = Array(); |
||
| 302 | // Create a lookup table to easily get the name of the file the class is defined in |
||
| 303 | $classFileLookup = Array(); |
||
| 304 | |||
| 305 | $fileDataLookup = Array(); |
||
| 306 | $fileDependencies = Array(); |
||
| 307 | |||
| 308 | // Read all library files to determine the classes which are defined |
||
| 309 | for ($i = 0, $len = count($libFiles); $i < $len; $i++) { |
||
| 310 | $filename = $libFiles[$i]; |
||
| 311 | $content = $this->getFileContents($filename); |
||
| 312 | |||
| 313 | $class = Array(); |
||
| 314 | preg_match_all('(@class\W([^\n\r]*))', $content, $class); |
||
| 315 | |||
| 316 | $libFileLookup[ $filename ] = Array( |
||
| 317 | 'class' => $class[1] |
||
| 318 | ); |
||
| 319 | |||
| 320 | for ($j = 0, $lenJ = count($class[1]); $j < $lenJ; $j++){ |
||
| 321 | $libFileLookup[ $class[1][$j] ] = true; |
||
| 322 | } |
||
| 323 | } |
||
| 324 | |||
| 325 | for ($i = 0, $len = count($files); $i < $len; $i++) { |
||
| 326 | $content = $this->getFileContents($files[$i]); |
||
| 327 | $filename = $files[$i]; |
||
| 328 | |||
| 329 | $extends = Array(); |
||
| 330 | $dependsFile = Array(); |
||
| 331 | $class = Array(); |
||
| 332 | |||
| 333 | preg_match_all('(@extends\W([^\n\r]*))', $content, $extends); |
||
| 334 | preg_match_all('(@class\W([^\n\r]*))', $content, $class); |
||
| 335 | preg_match_all('(#dependsFile\W([^\n\r\*]+))', $content, $dependsFile); |
||
| 336 | $core = (strpos($content, '#core') !== false)? true : false; |
||
| 337 | |||
| 338 | for ($j = 0, $lenJ = count($coreFiles); $j < $lenJ; $j++){ |
||
| 339 | if(strpos($filename, $coreFiles[$j]) === 0){ |
||
| 340 | $core = true; |
||
| 341 | break; |
||
| 342 | } |
||
| 343 | } |
||
| 344 | |||
| 345 | $fileDataLookup[ $filename ] = Array( |
||
| 346 | 'class' => $class[1], |
||
| 347 | 'extends' => $extends[1], |
||
| 348 | 'dependsFile' => $dependsFile[1] |
||
| 349 | ); |
||
| 350 | $fileDependencies[ $filename ] = Array( |
||
| 351 | 'depends' => Array(), |
||
| 352 | 'core' => $core // Based on tag or on class or on file path? |
||
| 353 | ); |
||
| 354 | |||
| 355 | for ($j = 0, $lenJ = count($class[1]); $j < $lenJ; $j++){ |
||
| 356 | $classFileLookup[ $class[1][$j] ] = $filename; |
||
| 357 | } |
||
| 358 | } |
||
| 359 | |||
| 360 | // Convert dependencies found by searching for @extends to a filename. |
||
| 361 | foreach ($fileDataLookup as $filename => &$fileData) { |
||
| 362 | |||
| 363 | // First get the extended class dependencies. We also have to convert them into files names using the $classFileLookup. |
||
| 364 | for ($i = 0, $len = count($fileData['extends']); $i < $len; $i++) { |
||
| 365 | // The check if it extends the Zarafa namespace is needed because we do not index other namespaces. |
||
| 366 | if(substr($fileData['extends'][$i], 0, strlen('Zarafa')) == 'Zarafa'){ |
||
| 367 | if (isset($libFileLookup[ $fileData['extends'][$i] ])) { |
||
| 368 | // The @extends is found in the library file. |
||
| 369 | // No need to update the dependencies |
||
| 370 | } else if(isset($classFileLookup[ $fileData['extends'][$i] ])){ |
||
| 371 | // The @extends is found as @class in another file |
||
| 372 | // Convert the class dependency into a filename |
||
| 373 | $dependencyFilename = $classFileLookup[ $fileData['extends'][$i] ]; |
||
| 374 | // Make sure the file does not depend on itself |
||
| 375 | if($dependencyFilename != $filename){ |
||
| 376 | $fileDependencies[ $filename ]['depends'][] = $dependencyFilename; |
||
| 377 | } |
||
| 378 | } else { |
||
| 379 | trigger_error('Unable to find @extends dependency "'.$fileData['extends'][$i].'" for file "'.$filename.'"'); |
||
| 380 | } |
||
| 381 | } |
||
| 382 | } |
||
| 383 | |||
| 384 | // Add the file dependencies that have beed added by using #dependsFile in the file. |
||
| 385 | for ($i = 0, $len = count($fileData['dependsFile']); $i < $len; $i++){ |
||
| 386 | $dependencyFilename = $fileData['dependsFile'][$i]; |
||
| 387 | // Check if the file exists to prevent non-existent dependencies |
||
| 388 | if(isset($fileDataLookup[ $dependencyFilename ])){ |
||
| 389 | // Make sure the file does not depend on itself |
||
| 390 | if($dependencyFilename != $filename){ |
||
| 391 | $fileDependencies[ $filename ]['depends'][] = $dependencyFilename; |
||
| 392 | } |
||
| 393 | } else { |
||
| 394 | trigger_error('Unable to find file #dependsFile dependency "'.$fileData['dependsFile'][$i].'" for file "'.$filename.'"'); |
||
| 395 | } |
||
| 396 | } |
||
| 397 | } |
||
| 398 | unset($fileData); |
||
| 399 | |||
| 400 | $fileSequence = $this->generateDependencyBasedFileSeq($fileDependencies); |
||
| 401 | |||
| 402 | return $fileSequence; |
||
| 403 | } |
||
| 404 | |||
| 405 | /** |
||
| 406 | * generateDependencyBasedFileSeq |
||
| 407 | * |
||
| 408 | * This function will generate a loading sequence for the supplied list of files and their |
||
| 409 | * dependencies. This function calculates the depth of each file in the dependencytree. Based on |
||
| 410 | * that depth it calculates a weight for each file and that will determine the order in which |
||
| 411 | * the files will be included. |
||
| 412 | * The weight consists of two times the depth of the node and a penalty for files that have not |
||
| 413 | * been marked as a core file. This way core files get included prior to other files at the same |
||
| 414 | * depth. Files with the same weight are added in the order they are in the list and that should |
||
| 415 | * be alphabetically. |
||
| 416 | * |
||
| 417 | * @param $fileData Array List of files with dependency data in the format of |
||
| 418 | * $fileData[ FILENAME ] = Array( |
||
| 419 | * 'depends' => Array(FILENAME1, FILENAME2), |
||
| 420 | * 'core' => true|false |
||
| 421 | * ); |
||
| 422 | * @return Array List of filenames in the calculated loading sequence |
||
| 423 | */ |
||
| 424 | private function generateDependencyBasedFileSeq($fileData){ |
||
| 425 | $fileDepths = Array(); |
||
| 426 | |||
| 427 | $changed = true; |
||
| 428 | while($changed && (count($fileDepths) < count($fileData)) ){ |
||
| 429 | $changed = false; |
||
| 430 | |||
| 431 | // Loop through all the files and see if for each file we can get a depth assigned based on their parents depth. |
||
| 432 | foreach($fileData as $file => $dependencyData){ |
||
| 433 | $dependencies = $dependencyData['depends']; |
||
| 434 | |||
| 435 | if(!isset($fileDepths[ $file ])){ |
||
| 436 | if(count($dependencies) > 0){ |
||
| 437 | $parentsDepthAssigned = true; |
||
| 438 | $highestParentDepth = 0; |
||
| 439 | // See if all the parents already have a depth assigned and if so take the highest one. |
||
| 440 | for($i=0;$i<count($dependencies);$i++){ |
||
| 441 | // Not all parents depths have been assigned yet, wait another turn |
||
| 442 | if(!isset($fileDepths[ $dependencies[$i] ])){ |
||
| 443 | $parentsDepthAssigned = false; |
||
| 444 | break; |
||
| 445 | } else { |
||
| 446 | // We should only take the highest depth |
||
| 447 | $highestParentDepth = max($highestParentDepth, $fileDepths[ $dependencies[$i] ]); |
||
| 448 | } |
||
| 449 | } |
||
| 450 | // All parents have a depth assigned, we can calculate the one for this node. |
||
| 451 | if($parentsDepthAssigned){ |
||
| 452 | $fileDepths[ $file ] = $highestParentDepth + 1; |
||
| 453 | $changed = true; |
||
| 454 | } |
||
| 455 | // The node does not have any dependencies so its a root node. |
||
| 456 | } else { |
||
| 457 | $fileDepths[ $file ] = 0; |
||
| 458 | $changed = true; |
||
| 459 | } |
||
| 460 | } |
||
| 461 | } |
||
| 462 | } |
||
| 463 | |||
| 464 | // If not all the files have been assigned a depth, but nothing changed the last round there |
||
| 465 | // must be something wrong with the dependencies of the skipped files. So lets tell someone. |
||
| 466 | if(count($fileDepths) < count($fileData)){ |
||
| 467 | $errorMsg = '[LOADER] Could not compute all dependencies. The following files cannot be resolved properly: '; |
||
| 468 | $errorMsg .= implode(', ', array_diff(array_keys($fileData), array_keys($fileDepths))); |
||
| 469 | trigger_error($errorMsg); |
||
| 470 | } |
||
| 471 | |||
| 472 | $fileWeights = Array(); |
||
| 473 | // Now lets determine each file's weight |
||
| 474 | foreach($fileData as $file => $dependencyData){ |
||
| 475 | if($fileDepths[ $file ] !== null){ |
||
| 476 | $weight = $fileDepths[ $file ] * 2; |
||
| 477 | // Add a penalty of 1 to non-core files to up the core-files in the sequence. |
||
| 478 | if(!$dependencyData['core']){ |
||
| 479 | $weight++; |
||
| 480 | } |
||
| 481 | } else { |
||
| 482 | // Make up a weight to put it at the end |
||
| 483 | $weight = count($fileData); |
||
| 484 | } |
||
| 485 | if(!isset($fileWeights[ $weight])) |
||
| 486 | $fileWeights[ $weight ] = Array(); |
||
| 487 | $fileWeights[ $weight ][] = $file; |
||
| 488 | } |
||
| 489 | |||
| 490 | // The weights have not been added in the correct order, so sort it first on the keys. |
||
| 491 | ksort($fileWeights); |
||
| 492 | |||
| 493 | // Now put it all in the correct order. Files with the same weight are added in the order |
||
| 494 | // they are in the list. This order should still be alphabetically. |
||
| 495 | $fileSequence = Array(); |
||
| 496 | foreach($fileWeights as $weight => $fileList){ |
||
| 497 | for($i=0;$i<count($fileList);$i++){ |
||
| 498 | $fileSequence[] = $fileList[$i]; |
||
| 499 | } |
||
| 500 | } |
||
| 501 | |||
| 502 | return $fileSequence; |
||
| 503 | } |
||
| 504 | |||
| 505 | /** |
||
| 506 | * getFileContents |
||
| 507 | * |
||
| 508 | * Returns the content of the supplied file name. |
||
| 509 | * @param $fn String File name |
||
| 510 | * @return String Content of the file |
||
| 511 | */ |
||
| 512 | private function getFileContents($fn){ |
||
| 513 | $fn = strtok($fn, '?'); |
||
| 514 | |||
| 515 | $fc=""; |
||
| 516 | $fh = fopen($fn, "r"); |
||
| 517 | if($fh) { |
||
| 518 | while(!feof($fh)){ |
||
| 519 | $fc .= fgets($fh, 4096); |
||
| 520 | } |
||
| 521 | fclose($fh); |
||
| 522 | } |
||
| 523 | return $fc; |
||
| 524 | } |
||
| 525 | |||
| 526 | /** |
||
| 527 | * The JavaScript load order for grommunio Web. The loader order is cached when grommunio Web |
||
| 528 | * is LOAD_SOURCE mode, since calculating the loader is quite expensive. |
||
| 529 | */ |
||
| 530 | public function jsOrder() |
||
| 531 | { |
||
| 532 | if ($this->source) { |
||
| 533 | if ($this->cacheExists()) { |
||
| 534 | echo file_get_contents($this->cacheFile); |
||
| 535 | return; |
||
| 536 | } |
||
| 537 | ob_start(); |
||
| 538 | } |
||
| 539 | |||
| 540 | list($extjsFiles, $webappFiles, $pluginFiles, $remoteFiles) = $this->getJsFiles(); |
||
| 541 | |||
| 542 | $jsTemplate = "\t\t<script type=\"text/javascript\" src=\"{file}\"></script>"; |
||
| 543 | $this->printFiles($extjsFiles, $jsTemplate); |
||
| 544 | $this->printFiles($webappFiles, $jsTemplate); |
||
| 545 | $this->printFiles($pluginFiles, $jsTemplate); |
||
| 546 | $this->printFiles($remoteFiles, $jsTemplate); |
||
| 547 | |||
| 548 | if ($this->source) { |
||
| 549 | $contents = ob_get_contents(); |
||
| 550 | ob_end_clean(); |
||
| 551 | echo $contents; |
||
| 552 | file_put_contents($this->cacheFile, $contents); |
||
| 553 | } |
||
| 554 | } |
||
| 555 | |||
| 556 | /** |
||
| 557 | * Returns an array with all javascript files. The array has four entries, for the ExtJS files, |
||
| 558 | * the Zarafa files, the plugin files and the remote files respectively. |
||
| 559 | * This function will make sure that the directories are read only once. |
||
| 560 | * |
||
| 561 | * @return array An array that contains the names of all the javascript files that should be loaded |
||
| 562 | */ |
||
| 563 | private function getJsFiles() { |
||
| 564 | if ( !isset($this->extjsfiles) ){ |
||
| 565 | $this->extjsFiles = $this->getExtjsJavascriptFiles(DEBUG_LOADER); |
||
| 566 | $this->webappFiles = $this->getZarafaJavascriptFiles(DEBUG_LOADER, $this->extjsFiles); |
||
| 567 | $this->pluginFiles = $this->getPluginJavascriptFiles(DEBUG_LOADER, array_merge($this->extjsFiles, $this->webappFiles)); |
||
| 568 | $this->remoteFiles = $this->getRemoteJavascriptFiles(DEBUG_LOADER); |
||
| 569 | } |
||
| 570 | |||
| 571 | return array($this->extjsFiles, $this->webappFiles, $this->pluginFiles, $this->remoteFiles); |
||
| 572 | } |
||
| 573 | |||
| 574 | /** |
||
| 575 | * The CSS load order for grommunio Web |
||
| 576 | */ |
||
| 577 | public function cssOrder() |
||
| 578 | { |
||
| 579 | $cssTemplate = "\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"{file}\">"; |
||
| 580 | $extjsFiles = $this->getExtjsCSSFiles(DEBUG_LOADER); |
||
| 581 | $this->printFiles($extjsFiles, $cssTemplate); |
||
| 582 | |||
| 583 | // Since we only have one css file, we can add that directly |
||
| 584 | $this->printFiles(array("client/resources/css/grommunio.css"), $cssTemplate); |
||
| 585 | |||
| 586 | $pluginFiles = $this->getPluginCSSFiles(DEBUG_LOADER); |
||
| 587 | $this->printFiles($pluginFiles, $cssTemplate); |
||
| 588 | |||
| 589 | $remoteFiles = $this->getRemoteCSSFiles(DEBUG_LOADER); |
||
| 590 | $this->printFiles($remoteFiles, $cssTemplate); |
||
| 591 | } |
||
| 592 | |||
| 593 | /** |
||
| 594 | * Checks if the JavaScript or CSS files on disk have been changed |
||
| 595 | * and writes a new md5 of the files to the disk. |
||
| 596 | * |
||
| 597 | * return boolean False if cache is outdated |
||
| 598 | */ |
||
| 599 | private function cacheExists() |
||
| 600 | { |
||
| 601 | list($extjsFiles, $webappFiles, $pluginFiles, $remoteFiles) = $this->getJsFiles(); |
||
| 602 | $files = [$extjsFiles, $webappFiles, $pluginFiles, $remoteFiles]; |
||
| 603 | $md5 = md5(json_encode($files)); |
||
| 604 | |||
| 605 | if (!file_exists($this->cacheSum) || file_get_contents($this->cacheSum) !== $md5) { |
||
| 606 | file_put_contents($this->cacheSum, $md5); |
||
| 607 | return false; |
||
| 608 | } |
||
| 609 | |||
| 610 | return true; |
||
| 611 | } |
||
| 612 | } |
||
| 613 | ?> |
||
|
0 ignored issues
–
show
|
|||
| 614 |
Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.
A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.