2 Copyright (C) 2002-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "SoProductResourceManager.h"
23 #include "SoProduct.h"
25 #include "SoClassSecurityInfo.h"
26 #include <NGObjWeb/WOApplication.h>
27 #include <NGObjWeb/WOContext.h>
28 #include <NGObjWeb/WOResponse.h>
29 #include <NGObjWeb/WOSession.h>
30 #include <NGObjWeb/WORequest.h>
31 #include <NGExtensions/NSString+Ext.h>
32 #include <NGExtensions/NSBundle+misc.h>
35 @interface WOResourceManager(UsedPrivates)
36 - (NSString *)webServerResourcesPath;
37 - (NSString *)resourcesPath;
40 @implementation SoProductResourceManager
42 static NGBundleManager *bm = nil;
43 static BOOL debugOn = NO;
46 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
48 bm = [[NGBundleManager defaultBundleManager] retain];
49 debugOn = [ud boolForKey:@"SoProductResourceManagerDebugEnabled"];
52 - (id)initWithProduct:(SoProduct *)_product {
53 if ((self = [super initWithPath:[[_product bundle] bundlePath]])) {
54 self->product = _product;
61 - (void)detachFromContainer {
67 - (NSString *)nameInContainer {
71 /* lookup resources */
73 - (NSString *)pathForResourceNamed:(NSString *)_name
74 inFramework:(NSString *)_frameworkName
75 languages:(NSArray *)_languages
77 // TODO: should we do acquisition? (hm, don't think so!, done in lookup)
78 // but maybe we should not fall back to WOApplication resources
82 if (debugOn) [self debugWithFormat:@"lookup resource: '%@'", _name];
84 /* determine product bundle or explicitly requested framework/bundle */
86 if ([_frameworkName length] > 0) {
87 if ([_frameworkName hasPrefix:@"/"]) {
88 bundle = [bm bundleWithPath:_frameworkName];
91 bundle = [bm bundleWithName:
92 [_frameworkName stringByDeletingPathExtension]
93 type:[_frameworkName pathExtension]];
96 [self warnWithFormat:@"missing bundle for framework: '%@'",
102 if ((bundle = [self->product bundle]) == nil) {
103 [self warnWithFormat:@"missing bundle for product: %@", self->product];
108 if (debugOn) [self debugWithFormat:@" bundle: %@", bundle];
110 /* lookup resource in bundle */
112 path = [bundle pathForResource:[_name stringByDeletingPathExtension]
113 ofType:[_name pathExtension]
115 forLocalizations:_languages];
117 if (debugOn) [self debugWithFormat:@" => found: %@", path];
120 if (debugOn) [self debugWithFormat:@" resource not found in bundle ..."];
122 /* fall back to WOResourceManager lookup */
124 return [super pathForResourceNamed:_name inFramework:_frameworkName
125 languages:_languages];
128 /* generate URL for resources (eg filename binding in WOImage) */
130 - (NSString *)webServerResourcesPath {
131 /* to avoid warning that WebServerResources path does not exist ... */
132 return [[[WOApplication application] resourceManager]
133 webServerResourcesPath];
136 - (NSString *)urlForResourceNamed:(NSString *)_name
137 inFramework:(NSString *)_frameworkName
138 languages:(NSArray *)_languages
139 request:(WORequest *)_request
141 NSString *resource = nil, *tmp;
142 NSString *path = nil, *sbase;
146 if (debugOn) [self debugWithFormat:@"lookup url: '%@'", _name];
148 if (_languages == nil) _languages = [_request browserLanguages];
150 resource = [self pathForResourceNamed:_name
151 inFramework:_frameworkName
152 languages:_languages];
153 #if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY
154 if ([resource rangeOfString:@"/Contents/"].length > 0) {
155 resource = [resource stringByReplacingString:@"/Contents"
160 tmp = [resource stringByStandardizingPath];
161 if (tmp) resource = tmp;
164 if (resource == nil) {
166 [self debugWithFormat:@" => not found '%@' (fw=%@,langs=%@)",
167 _name, _frameworkName, [_languages componentsJoinedByString:@","]];
173 tmp = [sbase commonPrefixWithString:resource options:0];
176 path = [sbase substringFromIndex:len];
177 tmp = [resource substringFromIndex:len];
178 if (([path length] > 0) && ![tmp hasPrefix:@"/"] && ![tmp hasPrefix:@"\\"])
179 path = [path stringByAppendingString:@"/"];
180 path = [path stringByAppendingString:tmp];
185 cs = [path componentsSeparatedByString:@"\\"];
186 path = [cs componentsJoinedByString:@"/"];
192 if ([path hasPrefix:@"/Resources/"])
193 path = [path substringFromIndex:11];
194 else if ([path hasPrefix:@"Resources/"])
195 path = [path substringFromIndex:10];
197 /* Note: cannot use -stringByAppendingPathComponent: on OSX! */
198 url = [self baseURLInContext:[[WOApplication application] context]];
199 if (debugOn) [self debugWithFormat:@" base: '%@'", url];
201 if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
202 url = [url stringByAppendingString:path];
204 if (debugOn) [self debugWithFormat:@" => '%@'", url];
208 - (WOElement *)templateWithName:(NSString *)_name
209 languages:(NSArray *)_languages
211 [self logWithFormat:@"lookup template with name '%@' (languages=%@)",
212 _name, [_languages componentsJoinedByString:@","]];
213 return [super templateWithName:_name languages:_languages];
216 /* resource manager as a SoObject */
218 - (NSString *)mimeTypeForExtension:(NSString *)_ext {
219 // TODO: HACK, move to some object
220 NSString *ctype = nil;
222 if ([_ext isEqualToString:@"css"]) ctype = @"text/css";
223 else if ([_ext isEqualToString:@"gif"]) ctype = @"image/gif";
224 else if ([_ext isEqualToString:@"jpg"]) ctype = @"image/jpeg";
225 else if ([_ext isEqualToString:@"png"]) ctype = @"image/png";
226 else if ([_ext isEqualToString:@"html"]) ctype = @"text/html";
227 else if ([_ext isEqualToString:@"xml"]) ctype = @"text/xml";
228 else if ([_ext isEqualToString:@"txt"]) ctype = @"text/plain";
229 else if ([_ext isEqualToString:@"js"]) ctype = @"application/x-javascript";
230 else if ([_ext isEqualToString:@"xhtml"]) ctype = @"application/xhtml+xml";
234 - (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag {
237 NSString *p, *pe, *ctype;
239 NSArray *languages = nil;
241 /* TODO: add support for languages (eg English.lproj/ok.gif) ! */
243 /* check whether the resource is made public */
245 if (![self->product isPublicResource:_key]) {
246 [self debugWithFormat:@"key '%@' is not declared a public resource.",_key];
250 if ((b = [self->product bundle]) == nil) {
251 [self debugWithFormat:@"product has no bundle for lookup of %@", _key];
255 pe = [_key pathExtension];
257 /* ask resource-manager (self) for path */
259 languages = [_ctx hasSession]
260 ? [(WOSession *)[_ctx session] languages]
261 : [[(id <WOPageGenerationContext>)_ctx request] browserLanguages];
263 p = [self pathForResourceNamed:_key
264 inFramework:[b bundlePath]
265 languages:languages];
267 [self errorWithFormat:@"did not find product resource: %@", _key];
273 if ((data = [NSData dataWithContentsOfMappedFile:p]) == nil) {
274 [self errorWithFormat:@"failed to load product resource: %@", _key];
278 /* and deliver as a complete response */
280 r = [(id<WOPageGenerationContext>)_ctx response];
282 [r setStatus:200 /* OK */];
285 if ((ctype = [self mimeTypeForExtension:pe]) == nil) {
286 [self warnWithFormat:@"did not recognize extension '%@', "
287 @"delivering as application/octet-stream.", pe];
288 ctype = @"application/octet-stream";
292 NSDate *expDate = nil;
295 expDate = [[NSDate alloc] initWithTimeInterval:(60 * 60 * 1) /* 1 hour */
296 sinceDate:[NSDate date]];
297 str = [expDate descriptionWithCalendarFormat:
298 @"%a, %d %b %Y %H:%M:%S GMT"
299 timeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]
301 [r setHeader:str forKey:@"expires"];
305 [r setHeader:ctype forKey:@"content-type"];
311 - (BOOL)isDebuggingEnabled {
314 - (NSString *)loggingPrefix {
315 return [NSString stringWithFormat:@"[RM:%@]", [self->product productName]];
320 - (NSString *)description {
321 NSMutableString *str;
323 str = [NSMutableString stringWithCapacity:64];
324 [str appendFormat:@"<%@[0x%08X]:", NSStringFromClass([self class]), self];
325 [str appendFormat:@" product='%@'", [self->product productName]];
326 [str appendString:@">"];
330 @end /* SoProductResourceManager */