2 Copyright (C) 2003-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
24 Note: Its very important that the URLs generated properly match the
25 appname! Otherwise you will get security exceptions in JavaScript
26 on the client side (probably depends on the browser).
27 This also implies, that WOResourcePrefix cannot be used in conjunction
31 #include <NGObjWeb/WODynamicElement.h>
33 @interface WEEpozEditor : WODynamicElement
35 // WODynamicElement: extraAttributes
36 // WODynamicElement: otherTagString
40 WOAssociation *disabled;
43 WOAssociation *epozCharset; /* def: iso-8859-1 */
44 WOAssociation *epozButtonStyle;
45 WOAssociation *epozStyle;
50 #include <NGObjWeb/WEClientCapabilities.h>
51 #include <NGExtensions/NSString+Ext.h>
54 @implementation WEEpozEditor
57 return [super version] + 0 /* v2 */;
60 NSAssert2([super version] == 2,
61 @"invalid superclass (%@) version %i !",
62 NSStringFromClass([self superclass]), [super version]);
65 - (id)initWithName:(NSString *)_name
66 associations:(NSDictionary *)_config
67 template:(WOElement *)_root {
69 if ((self = [super initWithName:_name associations:_config template:_root])) {
70 self->containsForm = YES;
71 self->name = OWGetProperty(_config, @"name");
72 self->value = OWGetProperty(_config, @"value");
73 self->disabled = OWGetProperty(_config, @"disabled");
74 self->rows = OWGetProperty(_config, @"rows");
75 self->cols = OWGetProperty(_config, @"cols");
77 self->epozCharset = OWGetProperty(_config, @"charset");
78 self->epozStyle = OWGetProperty(_config, @"style");
79 self->epozButtonStyle = OWGetProperty(_config, @"buttonstyle");
85 [self->epozCharset release];
86 [self->epozButtonStyle release];
87 [self->epozStyle release];
91 [self->value release];
92 [self->disabled release];
98 static NSString *OWFormElementName(WEEpozEditor *self, WOContext *_ctx) {
101 if (self->name == nil)
102 return [_ctx elementID];
104 if ((name = [self->name stringValueInComponent:[_ctx component]]))
109 @"WARNING: in element %@, 'name' attribute configured (%@),"
110 @"but no name assigned (using elementID as name) !",
112 return [_ctx elementID];
115 // ******************** responder ********************
117 - (id)parseFormValue:(id)_value inContext:(WOContext *)_ctx {
121 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
125 if ([self->disabled boolValueInComponent:[_ctx component]])
128 formName = OWFormElementName(self, _ctx);
130 if ((formValue = [_req formValueForKey:formName])) {
132 NSLog(@"%s(%@): form=%@ ctx=%@ value=%@ ..", __PRETTY_FUNCTION__,
133 [_ctx elementID], formName, [_ctx contextID], formValue);
136 if ([self->value isValueSettable]) {
137 formValue = [self parseFormValue:formValue inContext:_ctx];
138 [self->value setStringValue:formValue inComponent:[_ctx component]];
142 NSLog(@"%s: form value is not settable: %@", __PRETTY_FUNCTION__,
149 - (BOOL)isDebuggingEnabled {
153 - (BOOL)isEpozBrowserInContext:(WOContext *)_ctx {
154 WEClientCapabilities *cc;
156 if ((cc = [[_ctx request] clientCapabilities]) == nil) {
157 [self debugWithFormat:@"WARNING: missing client capabilities object!"];
161 if ([cc isInternetExplorer]) {
162 if ([cc majorVersion] <= 4) {
163 [self debugWithFormat:@"disable Epoz with IE <5"];
166 if ([cc majorVersion] == 5 && [cc minorVersion] <= 5) {
167 [self debugWithFormat:@"disable Epoz with IE <5.5"];
170 [self debugWithFormat:@"enable Epoz with IE >=5.5"];
174 if ([cc isMozilla] || [cc isNetscape]) {
175 [self debugWithFormat:@"enable Epoz with Mozilla: %@", cc];
179 [self debugWithFormat:@"does not use Epoz with this browser: %@", cc];
183 - (NSString *)stringValueInContext:(WOContext *)_ctx {
188 v = [[self->value valueInComponent:[_ctx component]] stringValue];
192 ua = [[_ctx request] headerForKey:@"user-agent"];
193 if ([ua rangeOfString:@"Opera"].length > 0)
197 v = [v stringByReplacingString:@"\r" withString:@""];
202 - (void)appendTextAreaToResponse:(WOResponse *)_response
203 inContext:(WOContext *)_ctx
205 WOComponent *sComponent;
209 sComponent = [_ctx component];
210 v = [self stringValueInContext:_ctx];
211 r = [self->rows stringValueInComponent:sComponent];
212 c = [self->cols stringValueInComponent:sComponent];
214 [_response appendContentString:@"<textarea name=\""];
215 [_response appendContentHTMLAttributeValue:OWFormElementName(self, _ctx)];
216 [_response appendContentString:@"\""];
218 [_response appendContentString:@" rows=\""];
219 [_response appendContentString:r];
220 [_response appendContentString:@"\""];
223 [_response appendContentString:@" cols=\""];
224 [_response appendContentString:c];
225 [_response appendContentString:@"\""];
228 [self appendExtraAttributesToResponse:_response inContext:_ctx];
229 if (self->otherTagString) {
232 str = [self->otherTagString stringValueInComponent:sComponent];
233 [_response appendContentString:str];
235 [_response appendContentString:@">"];
238 [_response appendContentHTMLString:v];
240 [_response appendContentString:@"</textarea>"];
243 - (WOResourceManager *)resourceManagerInContext:(WOContext *)_ctx {
244 WOResourceManager *rm;
246 if ((rm = [[_ctx component] resourceManager]) == nil)
247 rm = [[_ctx application] resourceManager];
250 - (NSString *)urlForEpozResourceNamed:(NSString *)_name
251 inContext:(WOContext *)_ctx
253 WOResourceManager *rm;
256 rm = [self resourceManagerInContext:_ctx];
257 languages = [_ctx hasSession] ? [[_ctx session] languages] : nil;
259 return [rm urlForResourceNamed:_name inFramework:nil
260 languages:languages request:[_ctx request]];
263 - (void)appendEpozScript:(NSString *)_scriptName
264 toResponse:(WOResponse *)_response
265 inContext:(WOContext *)_ctx
269 src = [self urlForEpozResourceNamed:_scriptName inContext:_ctx];
270 [_response appendContentString:@"<script language=\"JavaScript\""];
271 [_response appendContentString:@" type=\"text/javascript\""];
272 [_response appendContentString:@" src=\""];
273 [_response appendContentHTMLAttributeValue:src];
274 [_response appendContentString:@"\"></script>\n"];
277 - (NSString *)epozImageURLInContext:(WOContext *)_ctx {
279 Note: Epoz takes the directory where the resources are located, not
280 individual pointers. This also means that you need to have all
281 Epoz resources in the directory which contains the
282 "epoz_button_bold.gif" (which is used as the "reference" point
283 to the active buttons).
288 src = [self urlForEpozResourceNamed:@"epoz_button_bold.gif" inContext:_ctx];
289 r = [src rangeOfString:@"/" options:(NSBackwardsSearch | NSLiteralSearch)];
291 src = [src substringToIndex:(r.location + r.length)];
295 - (NSString *)epozToolboxURLInContext:(WOContext *)_ctx {
297 // return @"/epoz/WebServerResources/toolbox";
298 return [self urlForEpozResourceNamed:@"epoz_toolbox.html" inContext:_ctx];
300 - (NSString *)epozCSSURLInContext:(WOContext *)_ctx {
301 return [self urlForEpozResourceNamed:@"epoz.css" inContext:_ctx];
304 - (void)appendEpozToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
305 WOComponent *sComponent;
308 sComponent = [_ctx component];
309 v = [self stringValueInContext:_ctx];
311 /* first, the background iframe */
312 [_r appendContentString:@"<iframe id=\"EpozIFrame\""];
313 [_r appendContentString:@" style=\""];
314 [_r appendContentString:@"position: absolute;"];
315 [_r appendContentString:@" visibility: hidden;"];
316 [_r appendContentString:@" width: 0px; height: 0px;"];
317 [_r appendContentString:@"\"></iframe>\n"];
319 /* the language mappings */
320 [self appendEpozScript:@"epoz_lang_en.js"
321 toResponse:_r inContext:_ctx];
323 /* the processing JavaScripts */
324 [self appendEpozScript:@"epoz_script_widget.js"
325 toResponse:_r inContext:_ctx];
326 [self appendEpozScript:@"epoz_script_detect.js"
327 toResponse:_r inContext:_ctx];
328 [self appendEpozScript:@"epoz_script_main.js"
329 toResponse:_r inContext:_ctx];
333 """ Create an Epoz-Wysiwyg-Editor.
335 name : the name of the form-element which submits the data
336 data : the data to edit
337 toolbox : a link to a HTML-Page which delivers additional tools
338 lang: a code for the language-file (en,de,...)
339 path : path to Epoz-Javascript. Needed mainly for Plone (portal_url).
340 widget: You can specify a path to an alternative JavaScript for
341 epoz_script_widget.js
342 style : style-definition for the editor-area
343 button : style-definiton for buttons
345 If Epoz can't create a Rich-Text-Editor, a simple textarea is created.
349 [_r appendContentString:
350 @"<script language=\"JavaScript\" type=\"text/javascript\"><!--\n"];
351 [_r appendContentString:@"InitEpoz("];
354 [_r appendContentString:@"'"];
355 [_r appendContentString:OWFormElementName(self, _ctx)];
356 [_r appendContentString:@"',"];
359 [_r appendContentString:@"'"];
361 /* TODO: escape value, is that enough? */
365 jsv = [jsv stringByReplacingString:@"'" withString:@"\\'"];
366 jsv = [jsv stringByReplacingString:@"\n" withString:@"\\n"];
367 jsv = [jsv stringByReplacingString:@"\r" withString:@""];
368 [_r appendContentString:jsv];
370 [_r appendContentString:@"',"];
373 [_r appendContentString:@"'"];
374 [_r appendContentString:[self epozImageURLInContext:_ctx]];
375 [_r appendContentString:@"',"];
378 [_r appendContentString:@"'"];
379 [_r appendContentString:[self epozToolboxURLInContext:_ctx]];
380 [_r appendContentString:@"',"];
382 if ((tmp = [self->epozStyle stringValueInComponent:sComponent]) == nil)
383 tmp = @"width: 590px; height: 250px; border: 1px solid #000000;";
384 [_r appendContentString:@"'"];
385 [_r appendContentString:tmp];
386 [_r appendContentString:@"',"];
388 if ((tmp=[self->epozButtonStyle stringValueInComponent:sComponent])==nil) {
390 @"background-color: #EFEFEF; border: 1px solid #A0A0A0; "
391 @"cursor: pointer; margin-right: 1px; margin-bottom: 1px;";
393 [_r appendContentString:@"'"];
394 [_r appendContentString:tmp];
395 [_r appendContentString:@"',"];
398 [_r appendContentString:@"'"];
399 [_r appendContentString:[self epozCSSURLInContext:_ctx]];
400 [_r appendContentString:@"',"];
403 if ((tmp = [self->epozCharset stringValueInComponent:sComponent]) == nil)
405 [_r appendContentString:@"'"];
406 [_r appendContentString:tmp];
407 [_r appendContentString:@"'"];
409 [_r appendContentString:@");\n"];
410 [_r appendContentString:@"//-->\n</script>"];
412 /* fallback if scripting is disabled */
413 [_r appendContentString:@"<noscript>"];
414 [self appendTextAreaToResponse:_r inContext:_ctx];
415 [_r appendContentString:@"</noscript>"];
418 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
419 if ([self isEpozBrowserInContext:_ctx])
420 [self appendEpozToResponse:_response inContext:_ctx];
422 [self appendTextAreaToResponse:_response inContext:_ctx];
427 - (NSString *)associationDescription {
428 NSMutableString *str = nil;
430 str = [NSMutableString stringWithCapacity:64];
432 if (self->value) [str appendFormat:@" value=%@", self->value];
433 if (self->name) [str appendFormat:@" name=%@", self->name];
434 if (self->disabled) [str appendFormat:@" disabled=%@", self->disabled];
436 if (self->rows) [str appendFormat:@" rows=%@", self->rows];
437 if (self->cols) [str appendFormat:@" cols=%@", self->cols];
442 @end /* WEEpozEditor */