1/* 2 Copyright (C) 2005-2006 SKYRIX Software AG 3 Copyright (C) 2006 Helge Hess 4 5 This file is part of SOPE. 6 7 SOPE is free software; you can redistribute it and/or modify it under 8 the terms of the GNU Lesser General Public License as published by the 9 Free Software Foundation; either version 2, or (at your option) any 10 later version. 11 12 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY 13 WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with SOPE; see the file COPYING. If not, write to the 19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 20 02111-1307, USA. 21*/ 22 23#include "WEResourceManager.h" 24#include "WEStringTableManager.h" 25#include "WEResourceKey.h" 26#include "common.h" 27 28@implementation WEResourceManager 29 30static BOOL debugOn = NO; 31static BOOL debugComponents = NO; 32static NSArray *wsPathes = nil; 33static NSArray *templatePathes = nil; 34static NSString *suffix = nil; 35static NSString *prefix = nil; 36static NSFileManager *fm = nil; 37static NSNull *null = nil; 38static NSString *themesDirName = @"Themes"; 39 40+ (NSString *)shareSubpath { 41 static NSString *shareSubPath = nil; 42 NSString *p; 43 44 if (shareSubPath != nil) 45 return shareSubPath; 46 47 p = [[WOApplication application] shareDirectoryName]; 48 p = [@"share/" stringByAppendingString:p]; 49 p = [p stringByAppendingString:@"/"]; 50 shareSubPath = [p copy]; 51 return shareSubPath; 52} 53 54+ (NSString *)gsTemplatesSubpath { 55 NSString *p; 56 p = [[WOApplication application] gsTemplatesDirectoryName]; 57#if ! GNUSTEP_BASE_LIBRARY 58 // for GNUSTEP_BASE_LIBRARY this is already there in rootPathesInGNUstep 59 p = [@"Library/" stringByAppendingString:p]; 60#endif 61 return p; 62} 63+ (NSString *)gsWebSubpath { 64 NSString *p; 65 66 p = [[WOApplication application] gsWebDirectoryName]; 67#if ! GNUSTEP_BASE_LIBRARY 68 // for GNUSTEP_BASE_LIBRARY this is already there in rootPathesInGNUstep 69 p = [@"Library/" stringByAppendingString:p]; 70#endif 71 return p; 72} 73 74/* locate resource directories */ 75 76+ (NSArray *)rootPathesInGNUstep { 77 id tmp; 78#if GNUSTEP_BASE_LIBRARY 79 NSEnumerator *libraryPaths; 80 NSString *directory; 81 82 tmp = [NSMutableArray array]; 83 libraryPaths = [NSStandardLibraryPaths() objectEnumerator]; 84 while ((directory = [libraryPaths nextObject])) 85 [tmp addObject: directory]; 86 return tmp; 87#else 88 NSDictionary *env; 89 env = [[NSProcessInfo processInfo] environment]; 90 if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil) 91 tmp = [env objectForKey:@"GNUSTEP_PATHLIST"]; 92#endif 93 94 return [tmp componentsSeparatedByString:@":"]; 95} 96+ (NSArray *)rootPathesInFHS { 97 return [NSArray arrayWithObjects: 98#ifdef FHS_INSTALL_ROOT 99 FHS_INSTALL_ROOT, 100#endif 101 @"/usr/local/", @"/usr/", nil]; 102} 103 104+ (NSArray *)findResourceDirectoryPathesWithName:(NSString *)_name 105 fhsName:(NSString *)_fhs 106{ 107 /* find directories which might contain resources */ 108 NSEnumerator *e; 109 NSFileManager *fm; 110 NSMutableArray *ma; 111 BOOL isDir; 112 id tmp; 113 fm = [NSFileManager defaultManager]; 114 ma = [NSMutableArray arrayWithCapacity:8]; 115 116#ifdef GNUSTEP_BASE_LIBRARY 117 NSEnumerator *libraryPaths; 118 NSString *directory; 119 120 libraryPaths = [NSStandardLibraryPaths() objectEnumerator]; 121 while ((directory = [libraryPaths nextObject])) 122 [ma addObject: [directory stringByAppendingPathComponent: _name]]; 123#else 124 125 e = [[self rootPathesInGNUstep] objectEnumerator]; 126 while ((tmp = [e nextObject]) != nil) { 127 if (![tmp hasSuffix:@"/"]) 128 tmp = [tmp stringByAppendingString:@"/"]; 129 130 tmp = [tmp stringByAppendingString:_name]; 131 if ([ma containsObject:tmp]) continue; 132 133 if (debugOn) [self logWithFormat:@"CHECK: %@", tmp]; 134 if (![fm fileExistsAtPath:tmp isDirectory:&isDir]) 135 continue; 136 137 if (!isDir) continue; 138 139 [ma addObject:tmp]; 140 } 141#endif 142 143 /* hack in FHS pathes */ 144 145 e = [[self rootPathesInFHS] objectEnumerator]; 146 while ((tmp = [e nextObject]) != nil) { 147 tmp = [tmp stringByAppendingString:[[self class] shareSubpath]]; 148 tmp = [tmp stringByAppendingString:_fhs]; 149 if ([ma containsObject:tmp]) continue; 150 if (debugOn) [self logWithFormat:@"CHECK: %@", tmp]; 151 152 if (![fm fileExistsAtPath:tmp isDirectory:&isDir]) 153 continue; 154 if (!isDir) { 155 [self logWithFormat:@"path is not a directory: %@", tmp]; 156 continue; 157 } 158 159 [ma addObject:tmp]; 160 } 161 162 return ma; 163} 164 165+ (void)initialize { 166 static BOOL isInitialized = NO; 167 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; 168 if (isInitialized) return; 169 isInitialized = YES; 170 171 null = [[NSNull null] retain]; 172 173 if ((debugOn = [ud boolForKey:@"WEResourceManagerDebugEnabled"])) 174 NSLog(@"Note: WEResourceManager debugging is enabled."); 175 debugComponents = [ud boolForKey:@"WEResourceManagerComponentDebugEnabled"]; 176 if (debugComponents) 177 NSLog(@"Note: WEResourceManager component debugging is enabled."); 178 179 fm = [[NSFileManager defaultManager] retain]; 180 181 suffix = [[ud stringForKey:@"WOApplicationSuffix"] copy]; 182 prefix = [[ud stringForKey:@"WOResourcePrefix"] copy]; 183 184 wsPathes = [[self findResourceDirectoryPathesWithName: 185 [self gsWebSubpath] fhsName:@"www/"] copy]; 186 if (debugOn) 187 NSLog(@"WebServerResources pathes: %@", wsPathes); 188 189 // TODO: use appname to enable different setups, maybe some var too 190 templatePathes = [[self findResourceDirectoryPathesWithName: 191 [[self class] gsTemplatesSubpath] 192 fhsName:@"templates/"] copy]; 193 if (debugOn) 194 NSLog(@"template pathes: %@", templatePathes); 195 if (![templatePathes isNotEmpty]) { 196 NSLog(@"Note: found no directories containing flat templates (subpath=%@)", 197 [[self class] gsTemplatesSubpath]); 198 } 199} 200 201+ (NSArray *)availableThemes { 202 static NSArray *lthemes = nil; 203 NSMutableSet *themes; 204 NSEnumerator *e; 205 NSFileManager *fm; 206 NSString *path; 207 208 if (lthemes != nil) 209 return lthemes; 210 211 themes = [NSMutableSet setWithCapacity:16]; 212 fm = [NSFileManager defaultManager]; 213 214 e = [templatePathes objectEnumerator]; 215 while ((path = [e nextObject]) != nil) { 216 NSArray *dl; 217 218 path = [path stringByAppendingPathComponent:themesDirName]; 219 dl = [fm directoryContentsAtPath:path]; 220 221 [themes addObjectsFromArray:dl]; 222 } 223 224 /* remove directories to be ignored */ 225 [themes removeObject:@".svn"]; 226 [themes removeObject:@"CVS"]; 227 228 lthemes = [[[themes allObjects] 229 sortedArrayUsingSelector:@selector(compare:)] copy]; 230 if ([lthemes isNotEmpty]) { 231 NSLog(@"Note: located themes: %@", 232 [lthemes componentsJoinedByString:@", "]); 233 } 234 else 235 NSLog(@"Note: located no additional themes."); 236 return lthemes; 237} 238 239- (id)initWithPath:(NSString *)_path { 240 if ((self = [super initWithPath:_path]) != nil) { 241 if ([WOApplication isCachingEnabled]) { 242 self->keyToComponentPath = 243 [[NSMutableDictionary alloc] initWithCapacity:128]; 244 245 self->keyToPath = [[NSMutableDictionary alloc] initWithCapacity:1024]; 246 self->keyToURL = [[NSMutableDictionary alloc] initWithCapacity:1024]; 247 } 248 else 249 [self logWithFormat:@"Note: component path caching is disabled!"]; 250 251 self->labelManager = [[WEStringTableManager alloc] init]; 252 self->cachedKey = [[WEResourceKey alloc] initCachedKey]; 253 } 254 return self; 255} 256- (id)init { 257 // TODO: maybe search and set some 'share/ogo' path? 258 return [self initWithPath:nil]; 259} 260 261- (void)dealloc { 262 [self->cachedKey release]; 263 [self->labelManager release]; 264 [self->keyToURL release]; 265 [self->keyToPath release]; 266 [self->keyToComponentPath release]; 267 [super dealloc]; 268} 269 270/* accessors */ 271 272- (NGBundleManager *)bundleManager { 273 static NGBundleManager *bm = nil; // THREAD 274 if (bm == nil) bm = [[NGBundleManager defaultBundleManager] retain]; 275 return bm; 276} 277 278/* resource cache */ 279 280static id 281checkCache(NSDictionary *_cache, WEResourceKey *_key, 282 NSString *_n, NSString *_fw, NSString *_l) 283{ 284 if (_cache == nil) { 285 if (debugOn) NSLog(@"cache disabled."); 286 return nil; /* caching disabled */ 287 } 288 289 /* setup cache key (THREAD) */ 290 _key->hashValue = 0; /* reset, calculate on next access */ 291 _key->name = _n; 292 _key->frameworkName = _fw; 293 _key->language = _l; 294 295 return [_cache objectForKey:_key]; 296} 297 298- (void)cacheValue:(id)_value inCache:(NSMutableDictionary *)_cache { 299 WEResourceKey *k; 300 301 if (_cache == nil) return; /* caching disabled */ 302 303 /* we need to dup, because the cachedKey does not retain! */ 304 k = [self->cachedKey duplicate]; 305 306 if (debugOn) { 307 [self debugWithFormat:@"cache key %@(#%d): %@", k, [self->keyToPath count], 308 _value]; 309 } 310 311 [_cache setObject:(_value ? _value : (id)null) forKey:k]; 312 [k release]; k = nil; 313} 314 315/* locate Resources */ 316 317- (NSString *)_weCheckPath:(NSString *)_p forResourceNamed:(NSString *)_name 318 inFramework:(NSString *)_frameworkName 319 language:(NSString *)_language 320{ 321 NSString *path; 322 323 path = [_frameworkName isNotEmpty] 324 ? [_p stringByAppendingPathComponent:_frameworkName] 325 : _p; 326 327 /* check language */ 328 if (_language != nil) { 329 path = [path stringByAppendingPathComponent:_language]; 330 path = [path stringByAppendingPathExtension:@"lproj"]; 331 } 332 333 path = [path stringByAppendingPathComponent:_name]; 334 if (debugOn) [self debugWithFormat:@" check path: '%@'", path]; 335 336 if (![fm fileExistsAtPath:path]) 337 return nil; 338 339 return path; 340} 341 342- (NSString *)_weCheckPathes:(NSArray *)_p forResourceNamed:(NSString *)_name 343 inFramework:(NSString *)_frameworkName 344 language:(NSString *)_language 345{ 346 NSEnumerator *e; 347 NSString *path; 348 349 e = [_p objectEnumerator]; 350 while ((path = [e nextObject]) != nil) { 351 path = [self _weCheckPath:path forResourceNamed:_name 352 inFramework:_frameworkName language:_language]; 353 if (path != nil) { 354 if (debugOn) [self debugWithFormat:@"FOUND: '%@'", path]; 355 return path; 356 } 357 } 358 return nil; 359} 360 361- (NSString *)_wePathForResourceNamed:(NSString *)_name 362 inFramework:(NSString *)_fwName 363 language:(NSString *)_lang 364 searchPathes:(NSArray *)_pathes 365{ 366 // TODO: a lot of DUP code with _urlForResourceNamed, needs some refacturing 367 NSString *path; 368 369 if (debugOn) [self debugWithFormat:@"lookup resource '%@'", _name]; 370 371 /* check cache */ 372 373 path = checkCache(self->keyToPath, self->cachedKey, _name, _fwName, _lang); 374 if (path != nil) { 375 if (debugOn) [self debugWithFormat:@" found in cache: %@", path]; 376 return [path isNotNull] ? path : (NSString *)nil; 377 } 378 379 /* check for framework resources (webserver resources + framework) */ 380 381 if (debugOn) 382 [self debugWithFormat:@"check framework resources ..."]; 383 path = [self _weCheckPathes:_pathes forResourceNamed:_name 384 inFramework:_fwName language:_lang]; 385 if (path != nil) { 386 [self cacheValue:path inCache:self->keyToPath]; 387 return path; 388 } 389 390 /* check in basepath of webserver resources */ 391 392 // TODO: where is the difference, same call like above? 393 if (debugOn) [self debugWithFormat:@"check global resources ..."]; 394 path = [self _weCheckPathes:_pathes forResourceNamed:_name 395 inFramework:_fwName language:_lang]; 396 if (path != nil) { 397 [self cacheValue:path inCache:self->keyToPath]; 398 return path; 399 } 400 401 /* finished processing */ 402 if (debugOn) 403 [self debugWithFormat:@"NOT FOUND: %@ (%@)", _name, self->cachedKey]; 404 return nil; 405} 406 407- (BOOL)shouldLookupResourceInWebServerResources:(NSString *)_name { 408 if ([_name hasSuffix:@".wox"]) return NO; 409 if ([_name hasSuffix:@".wo"]) return NO; 410 return YES; 411} 412 413- (NSString *)_wePathForResourceNamed:(NSString *)_name 414 inFramework:(NSString *)_fwName 415 language:(NSString *)_lang 416{ 417 NSString *p; 418 419 /* check in webserver resources */ 420 421 if ([self shouldLookupResourceInWebServerResources:_name]) { 422 p = [self _wePathForResourceNamed:_name inFramework:_fwName 423 language:_lang searchPathes:wsPathes]; 424 if (p != nil) return p; 425 } 426 427 return nil; 428} 429 430- (BOOL)isTemplateResourceName:(NSString *)_name { 431 // TODO: non-extensible 432 return [_name hasSuffix:@".wox"]; 433} 434 435- (NSString *)pathForResourceNamed:(NSString *)_name 436 inFramework:(NSString *)_fwName 437 languages:(NSArray *)_langs 438{ 439 /* 440 Note: this is also called by the superclass method 441 -pathToComponentNamed:inFramework: for each registered component 442 extension. 443 */ 444 NSEnumerator *e; 445 NSString *language; 446 NSString *rpath; 447 448 if (![_name isNotEmpty]) { 449 [self debugWithFormat:@"got no name for resource lookup?!"]; 450 return nil; 451 } 452 453 if (debugOn) { 454 [self debugWithFormat:@"pathForResourceNamed: %@/%@ (languages: %@)", 455 _name, _fwName, [_langs componentsJoinedByString:@","]]; 456 } 457 458 if ([self isTemplateResourceName:_name]) { 459 if (debugOn) [self debugWithFormat:@" is template resource .."]; 460 return [self pathToComponentNamed:[_name stringByDeletingPathExtension] 461 inFramework:_fwName 462 languages:_langs]; 463 } 464 465 /* check languages */ 466 467 e = [_langs objectEnumerator]; 468 while ((language = [e nextObject]) != nil) { 469 NSString *rpath; 470 471 if (debugOn) 472 [self logWithFormat:@" check language (%@): '%@'", _name, language]; 473 rpath = [self _wePathForResourceNamed:_name inFramework:_fwName 474 language:language]; 475 if (rpath != nil) { 476 if (debugOn) [self debugWithFormat:@" FOUND: %@", rpath]; 477 return rpath; 478 } 479 } 480 481 /* check without language */ 482 483 rpath = [self _wePathForResourceNamed:_name inFramework:_fwName 484 language:nil]; 485 if (rpath != nil) 486 return rpath; 487 488 if (debugOn) { 489 [self debugWithFormat: 490 @"did not find resource, try super lookup: '%@'", _name]; 491 } 492 493 /* look using WOResourceManager */ 494 495 rpath = [super pathForResourceNamed:_name inFramework:_fwName 496 languages:_langs]; 497 return rpath; 498} 499 500/* locate WebServerResources */ 501 502- (NSString *)_urlForResourceNamed:(NSString *)_name 503 inFramework:(NSString *)_fwName 504 language:(NSString *)_lang 505 applicationName:(NSString *)_appName 506{ 507 NSString *url; 508 NSEnumerator *e; 509 NSString *path; 510 511 if (debugOn) { 512 [self logWithFormat:@"lookup URL of resource: '%@'/%@/%@", 513 _name, _fwName, _lang]; 514 } 515 516 /* check cache */ 517 518 url = checkCache(self->keyToURL, self->cachedKey, _name, _fwName, _lang); 519 if (url != nil) { 520 if (debugOn) { 521 [self debugWithFormat:@" found in cache: %@ (#%d)", url, 522 [self->keyToURL count]]; 523 } 524 return [url isNotNull] ? url : (NSString *)nil; 525 } 526 527 if (debugOn) { 528 [self debugWithFormat:@" not found in cache: %@ (%@,#%d)", 529 url, self->cachedKey, [self->keyToURL count]]; 530 } 531 532 /* check for framework resources */ 533 534 if ([_fwName isNotEmpty]) { 535 if (debugOn) 536 [self debugWithFormat:@"check framework: '%@'", _fwName]; 537 e = [wsPathes objectEnumerator]; 538 while ((path = [e nextObject])) { 539 NSMutableString *ms; 540 541 path = [path stringByAppendingPathComponent:_fwName]; 542 543 /* check language */ 544 if (_lang) { 545 path = [path stringByAppendingPathComponent:_lang]; 546 path = [path stringByAppendingPathExtension:@"lproj"]; 547 } 548 549 path = [path stringByAppendingPathComponent:_name]; 550 if (debugOn) [self debugWithFormat:@" check path: '%@'", path]; 551 552 if (![fm fileExistsAtPath:path]) 553 continue; 554 555 ms = [[NSMutableString alloc] initWithCapacity:256]; 556 557 if (prefix) [ms appendString:prefix]; 558 if (![ms hasSuffix:@"/"]) [ms appendString:@"/"]; 559 [ms appendString:_appName]; 560 if (suffix) [ms appendString:suffix]; 561 [ms appendString:[ms hasSuffix:@"/"] 562 ? @"WebServerResources/" : @"/WebServerResources/"]; 563 [ms appendString:_fwName]; 564 [ms appendString:@"/"]; 565 if (_lang) { 566 [ms appendString:_lang]; 567 [ms appendString:@".lproj/"]; 568 } 569 [ms appendString:_name]; 570 571 url = ms; 572 if (debugOn) [self debugWithFormat:@"FOUND: '%@'", url]; 573 goto done; 574 } 575 } 576 577 /* check for global resources */ 578 579 if (debugOn) [self debugWithFormat:@"check global WebServerResources ..."]; 580 e = [wsPathes objectEnumerator]; 581 while ((path = [e nextObject])) { 582 NSMutableString *ms; 583 NSString *fpath, *basepath; 584 NSDate *lastModified; 585 586 /* check language */ 587 if (_lang) { 588 basepath = [path stringByAppendingPathComponent:_lang]; 589 basepath = [basepath stringByAppendingPathExtension:@"lproj"]; 590 } 591 else 592 basepath = path; 593 594 fpath = [basepath stringByAppendingPathComponent:_name]; 595 if (debugOn) { 596 [self debugWithFormat: 597 @" check path: '%@'\n base: %@\n name: %@\n " 598 @" path: %@\n lang: %@", 599 fpath, basepath, _name, path, _lang]; 600 } 601 602 if (![fm fileExistsAtPath:fpath]) 603 continue; 604 605 lastModified = [[fm fileAttributesAtPath: fpath 606 traverseLink: YES] 607 fileModificationDate]; 608 609 ms = [[NSMutableString alloc] initWithCapacity:256]; 610 611 if (prefix) [ms appendString:prefix]; 612 if (![ms hasSuffix:@"/"]) [ms appendString:@"/"]; 613 [ms appendString:_appName]; 614 if (suffix) [ms appendString:suffix]; 615 [ms appendString:[ms hasSuffix:@"/"] 616 ? @"WebServerResources/" : @"/WebServerResources/"]; 617 if (_lang) { 618 [ms appendString:_lang]; 619 [ms appendString:@".lproj/"]; 620 } 621 [ms appendString:_name]; 622 [ms appendFormat: @"?lm=%u", 623 (unsigned) [lastModified timeIntervalSince1970]]; 624 625 url = ms; 626 if (debugOn) [self debugWithFormat:@"FOUND: '%@'", url]; 627 goto done; 628 } 629 630 /* finished processing */ 631 if (debugOn) { 632 [self debugWithFormat:@"NOT FOUND: %@ (%@,#%d)", _name, self->cachedKey, 633 [self->keyToURL count]]; 634 } 635 636 done: 637 [self cacheValue:url inCache:self->keyToURL]; 638 [url autorelease]; 639 640 return url; 641} 642 643- (NSString *)urlForResourceNamed:(NSString *)_name 644 inFramework:(NSString *)_fwName 645 languages:(NSArray *)_langs 646 request:(WORequest *)_request 647{ 648 NSEnumerator *e; 649 NSString *language; 650 NSString *url; 651 NSString *appName; 652 653 if (![_name isNotEmpty]) { 654 if (debugOn) [self logWithFormat:@"got no name for resource URL lookup?!"]; 655 return nil; 656 } 657 658 if (debugOn) [self debugWithFormat:@"urlForResourceNamed: %@", _name]; 659 660 if (_langs == nil) { 661 _langs = [_request browserLanguages]; 662 if (debugOn) { 663 [self debugWithFormat:@"using browser languages: %@", 664 [_langs componentsJoinedByString:@", "]]; 665 } 666 } 667 else if (debugOn) { 668 [self debugWithFormat:@"using given languages: %@", 669 [_langs componentsJoinedByString:@","]]; 670 } 671 672 appName = [_request applicationName]; 673 if (appName == nil) 674 appName = [(WOApplication *)[WOApplication application] name]; 675 676 /* check languages */ 677 678 e = [_langs objectEnumerator]; 679 while ((language = [e nextObject])) { 680 NSString *url; 681 682 if (debugOn) [self logWithFormat:@" check language: '%@'", language]; 683 url = [self _urlForResourceNamed:_name 684 inFramework:_fwName 685 language:language 686 applicationName:appName]; 687 if (url != nil) { 688 if (debugOn) [self logWithFormat:@" FOUND: %@", url]; 689 return url; 690 } 691 } 692 693 /* check without language */ 694 695 url = [self _urlForResourceNamed:_name 696 inFramework:_fwName 697 language:nil 698 applicationName:appName]; 699 if (url != nil) 700 return url; 701 702 if (debugOn) { 703 [self debugWithFormat: 704 @"did not find resource in try super lookup: '%@'", _name]; 705 } 706 707 url = [super urlForResourceNamed:_name 708 inFramework:_fwName 709 languages:_langs 710 request:_request]; 711 712 return url; 713} 714 715/* locate components */ 716 717- (NSString *)lookupComponentInStandardPathes:(NSString *)_name 718 inFramework:(NSString *)_framework theme:(NSString *)_theme 719{ 720 // TODO: what about languages/themes?! 721 NSEnumerator *e; 722 NSString *path; 723 724 if (debugComponents) { 725 [self logWithFormat:@"lookup component in std pathes: %@|%@|%@", 726 _name, _framework, _theme]; 727 } 728 729 if ([_theme isNotNull] && [_theme length] == 0) 730 _theme = nil; 731 if ([_framework isNotNull] && [_framework length] == 0) 732 _framework = nil; 733 734 e = [templatePathes objectEnumerator]; 735 while ((path = [e nextObject]) != nil) { 736 NSString *pe; 737 738 if (_theme != nil) { 739 // TODO: should be lower case for FHS? or use a different path? 740 path = [path stringByAppendingPathComponent:themesDirName]; 741 path = [path stringByAppendingPathComponent:_theme]; 742 } 743 744 if (_framework != nil) { 745 NSString *pureName; 746 747 pureName = [_framework lastPathComponent]; 748 pureName = [pureName stringByDeletingPathExtension]; 749 path = [path stringByAppendingPathComponent:pureName]; 750 } 751 752 path = [path stringByAppendingPathComponent:_name]; 753 754 pe = [path stringByAppendingPathExtension:@"wox"]; 755 if (debugComponents) [self logWithFormat:@"CHECK %@", pe]; 756 757 if ([fm fileExistsAtPath:pe]) 758 return pe; 759 760 pe = [path stringByAppendingPathExtension:@"html"]; 761 if ([fm fileExistsAtPath:pe]) { 762 /* 763 Note: we are passing in the path of the HTML template, this is some 764 kind of hack to make the wrapper template builder look for the 765 $name.html/$name.wod in there. 766 */ 767 return pe; 768 } 769 } 770 771 return nil; 772} 773- (NSString *)lookupComponentInStandardPathes:(NSString *)_name 774 inFramework:(NSString *)_framework languages:(NSArray *)_langs 775{ 776 NSString *path; 777 NSString *theme; 778 779 if (debugComponents) { 780 [self logWithFormat:@"lookup component in std pathes(langs): %@|%@", 781 _name, _framework]; 782 } 783 784 /* extract theme from language array (we do not support nested themes ATM) */ 785 786 theme = nil; 787 if ([_langs count] > 1) { 788 NSRange r; 789 790 theme = [_langs objectAtIndex:0]; 791 r = [theme rangeOfString:@"_"]; 792 theme = (r.length > 0) 793 ? [theme substringFromIndex:(r.location + r.length)] 794 : (NSString *)nil; 795 } 796 else 797 theme = nil; 798 799 /* check theme dirs */ 800 801 if (theme != nil) { 802 path = [self lookupComponentInStandardPathes:_name inFramework:_framework 803 theme:theme]; 804 if (path != nil) 805 return path; 806 } 807 808 /* check base dirs */ 809 810 path = [self lookupComponentInStandardPathes:_name inFramework:_framework 811 theme:nil]; 812 if (path != nil) 813 return path; 814 815 /* check without framework subdir */ 816 817 if ([_framework isNotEmpty]) { 818 path = [self lookupComponentInStandardPathes:_name inFramework:nil 819 theme:nil]; 820 if (path != nil) 821 return path; 822 } 823 824 return nil; 825} 826 827- (NSString *)lookupComponentPathUsingBundleManager:(NSString *)_name { 828 // TODO: is this ever invoked? 829 NSFileManager *fm = nil; 830 NSString *wrapper = nil; 831 NSBundle *bundle = nil; 832 NSString *path; 833 834 if (debugComponents) 835 [self logWithFormat:@"lookup component using bundle manager: %@", _name]; 836 837 bundle = [[self bundleManager] 838 bundleProvidingResource:_name 839 ofType:@"WOComponents"]; 840 if (bundle == nil) { 841 [self debugWithFormat:@"did not find a bundle providing component: %@", 842 _name]; 843 return nil; 844 } 845 846 if (debugOn) { 847 [self debugWithFormat:@"bundle %@ for component %@", 848 [[bundle bundlePath] lastPathComponent], _name]; 849 } 850 851 [bundle load]; 852 853 fm = [NSFileManager defaultManager]; 854 wrapper = [_name stringByAppendingPathExtension:@"wo"]; 855 856 path = [[bundle bundlePath] stringByAppendingPathComponent:@"Resources"]; 857 path = [path stringByAppendingPathComponent:wrapper]; 858 if ([fm fileExistsAtPath:path]) 859 return path; 860 861 path = [[bundle bundlePath] stringByAppendingPathComponent:wrapper]; 862 if ([fm fileExistsAtPath:path]) 863 return path; 864 865 return nil; 866} 867 868- (NSString *)pathToComponentNamed:(NSString *)_name 869 inFramework:(NSString *)_fw languages:(NSArray *)_langs 870{ 871 // TODO: what about languages in lookup? 872 NSString *path; 873 874 if (debugComponents) { 875 [self logWithFormat:@"%s: lookup component: %@|%@", 876 __PRETTY_FUNCTION__, _name, _fw]; 877 } 878 879 /* first check cache */ 880 881 path = checkCache(self->keyToComponentPath, self->cachedKey, _name, _fw, 882 [_langs isNotEmpty] 883 ? [_langs objectAtIndex:0] : (id)@"English"); 884 if (path != nil) { 885 if (debugComponents) 886 [self logWithFormat:@" use cached location: %@", path]; 887 return [path isNotNull] ? path : (NSString *)nil; 888 } 889 890 /* look in FHS locations */ 891 892 path = [self lookupComponentInStandardPathes:_name inFramework:_fw 893 languages:_langs]; 894 if (path != nil) { 895 if (debugComponents) 896 [self logWithFormat:@" found in standard pathes: %@", path]; 897 goto done; 898 } 899 900 /* try to find component by standard NGObjWeb method */ 901 902 if (debugComponents) 903 [self logWithFormat:@"lookup component using WOResourceManager ..."]; 904 path = [super pathToComponentNamed:_name inFramework:_fw languages:_langs]; 905 if (path != nil) { 906 if (debugComponents) 907 [self logWithFormat:@" found using WOResourceManager: %@", path]; 908 goto done; 909 } 910 911 /* find component using NGBundleManager */ 912 913 if ((path = [self lookupComponentPathUsingBundleManager:_name]) != nil) { 914 if (debugComponents) 915 [self logWithFormat:@" found using bundle manager: %@", path]; 916 goto done; 917 } 918 919 /* did not find component */ 920 done: 921 [self cacheValue:path inCache:self->keyToComponentPath]; 922 return path; 923} 924 925/* string tables */ 926 927- (NSString *)labelForKey:(NSString *)_key component:(WOComponent *)_component{ 928 return [self->labelManager labelForKey:_key component:_component]; 929} 930 931- (id)stringTableWithName:(NSString *)_tableName 932 inFramework:(NSString *)_framework 933 languages:(NSArray *)_languages 934{ 935 id table; 936 937 table = [self->labelManager 938 stringTableWithName:_tableName inFramework:_framework 939 languages:_languages]; 940 if (table != nil) return table; 941 942 return [super stringTableWithName:_tableName inFramework:_framework 943 languages:_languages]; 944} 945 946- (NSString *)stringForKey:(NSString *)_key 947 inTableNamed:(NSString *)_tableName 948 withDefaultValue:(NSString *)_defaultValue 949 inFramework:(NSString *)_framework 950 languages:(NSArray *)_languages 951{ 952 NSString *s; 953 954 s = [self->labelManager 955 stringForKey:_key inTableNamed:_tableName 956 withDefaultValue:_defaultValue languages:_languages]; 957 if (s != nil) return s; 958 959 s = [super stringForKey:_key inTableNamed:_tableName 960 withDefaultValue:_defaultValue 961 inFramework:_framework 962 languages:_languages]; 963 if (s != nil) return s; 964 965 return s != nil ? s : _defaultValue; 966} 967 968/* debugging */ 969 970- (BOOL)isDebuggingEnabled { 971 return debugOn; 972} 973- (NSString *)loggingPrefix { 974 return @"[we-rm]"; 975} 976 977@end /* WEResourceManager */ 978 979@implementation WOApplication(WEResourceManager) 980 981- (NSString *)shareDirectoryName { 982 return [[self name] lowercaseString]; 983} 984- (NSString *)gsTemplatesDirectoryName { 985 return [[self name] stringByAppendingString:@"/Templates/"]; 986} 987- (NSString *)gsWebDirectoryName { 988 return [[self name] stringByAppendingString:@"/WebServerResources/"]; 989} 990 991@end /* WOApplication(WEResourceManager) */ 992