2 Copyright (C) 2003-2004 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 Note: Its very important that the URLs generated properly match the
24 appname! Otherwise you will get security exceptions in JavaScript
25 on the client side (probably depends on the browser).
26 This also implies, that WOResourcePrefix cannot be used in conjunction
30 #include <NGObjWeb/WODynamicElement.h>
32 @interface WEEpozEditor : WODynamicElement
34 // WODynamicElement: extraAttributes
35 // WODynamicElement: otherTagString
39 WOAssociation *disabled;
42 WOAssociation *epozCharset; /* def: iso-8859-1 */
43 WOAssociation *epozButtonStyle;
44 WOAssociation *epozStyle;
49 #include <NGObjWeb/WEClientCapabilities.h>
50 #include <NGExtensions/NSString+Ext.h>
53 @implementation WEEpozEditor
56 return [super version] + 0 /* v2 */;
59 NSAssert2([super version] == 2,
60 @"invalid superclass (%@) version %i !",
61 NSStringFromClass([self superclass]), [super version]);
64 - (id)initWithName:(NSString *)_name
65 associations:(NSDictionary *)_config
66 template:(WOElement *)_root {
68 if ((self = [super initWithName:_name associations:_config template:_root])) {
69 self->containsForm = YES;
70 self->name = OWGetProperty(_config, @"name");
71 self->value = OWGetProperty(_config, @"value");
72 self->disabled = OWGetProperty(_config, @"disabled");
73 self->rows = OWGetProperty(_config, @"rows");
74 self->cols = OWGetProperty(_config, @"cols");
76 self->epozCharset = OWGetProperty(_config, @"charset");
77 self->epozStyle = OWGetProperty(_config, @"style");
78 self->epozButtonStyle = OWGetProperty(_config, @"buttonstyle");
84 [self->epozCharset release];
85 [self->epozButtonStyle release];
86 [self->epozStyle release];
90 [self->value release];
91 [self->disabled release];
97 static NSString *OWFormElementName(WEEpozEditor *self, WOContext *_ctx) {
100 if (self->name == nil)
101 return [_ctx elementID];
103 if ((name = [self->name stringValueInComponent:[_ctx component]]))
108 @"WARNING: in element %@, 'name' attribute configured (%@),"
109 @"but no name assigned (using elementID as name) !",
111 return [_ctx elementID];
114 // ******************** responder ********************
116 - (id)parseFormValue:(id)_value inContext:(WOContext *)_ctx {
120 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
124 if ([self->disabled boolValueInComponent:[_ctx component]])
127 formName = OWFormElementName(self, _ctx);
129 if ((formValue = [_req formValueForKey:formName])) {
131 NSLog(@"%s(%@): form=%@ ctx=%@ value=%@ ..", __PRETTY_FUNCTION__,
132 [_ctx elementID], formName, [_ctx contextID], formValue);
135 if ([self->value isValueSettable]) {
136 formValue = [self parseFormValue:formValue inContext:_ctx];
137 [self->value setStringValue:formValue inComponent:[_ctx component]];
141 NSLog(@"%s: form value is not settable: %@", __PRETTY_FUNCTION__,
148 - (BOOL)isDebuggingEnabled {
152 - (BOOL)isEpozBrowserInContext:(WOContext *)_ctx {
153 WEClientCapabilities *cc;
155 if ((cc = [[_ctx request] clientCapabilities]) == nil) {
156 [self debugWithFormat:@"WARNING: missing client capabilities object!"];
160 if ([cc isInternetExplorer]) {
161 if ([cc majorVersion] <= 4) {
162 [self debugWithFormat:@"disable Epoz with IE <5"];
165 if ([cc majorVersion] == 5 && [cc minorVersion] <= 5) {
166 [self debugWithFormat:@"disable Epoz with IE <5.5"];
169 [self debugWithFormat:@"enable Epoz with IE >=5.5"];
173 if ([cc isMozilla] || [cc isNetscape]) {
174 [self debugWithFormat:@"enable Epoz with Mozilla: %@", cc];
178 [self debugWithFormat:@"does not use Epoz with this browser: %@", cc];
182 - (NSString *)stringValueInContext:(WOContext *)_ctx {
187 v = [[self->value valueInComponent:[_ctx component]] stringValue];
191 ua = [[_ctx request] headerForKey:@"user-agent"];
192 if ([ua rangeOfString:@"Opera"].length > 0)
196 v = [v stringByReplacingString:@"\r" withString:@""];
201 - (void)appendTextAreaToResponse:(WOResponse *)_response
202 inContext:(WOContext *)_ctx
204 WOComponent *sComponent;
208 sComponent = [_ctx component];
209 v = [self stringValueInContext:_ctx];
210 r = [self->rows stringValueInComponent:sComponent];
211 c = [self->cols stringValueInComponent:sComponent];
213 [_response appendContentString:@"<textarea name=\""];
214 [_response appendContentHTMLAttributeValue:OWFormElementName(self, _ctx)];
215 [_response appendContentString:@"\""];
217 [_response appendContentString:@" rows=\""];
218 [_response appendContentString:r];
219 [_response appendContentString:@"\""];
222 [_response appendContentString:@" cols=\""];
223 [_response appendContentString:c];
224 [_response appendContentString:@"\""];
227 [self appendExtraAttributesToResponse:_response inContext:_ctx];
228 if (self->otherTagString) {
229 [_response appendContentCharacter:' '];
230 [_response appendContentString:
231 [self->otherTagString stringValueInComponent:sComponent]];
233 [_response appendContentString:@">"];
236 [_response appendContentHTMLString:v];
238 [_response appendContentString:@"</textarea>"];
241 - (WOResourceManager *)resourceManagerInContext:(WOContext *)_ctx {
242 WOResourceManager *rm;
244 if ((rm = [[_ctx component] resourceManager]) == nil)
245 rm = [[_ctx application] resourceManager];
248 - (NSString *)urlForEpozResourceNamed:(NSString *)_name
249 inContext:(WOContext *)_ctx
251 WOResourceManager *rm;
254 rm = [self resourceManagerInContext:_ctx];
255 languages = [_ctx hasSession] ? [[_ctx session] languages] : nil;
257 return [rm urlForResourceNamed:_name inFramework:nil
258 languages:languages request:[_ctx request]];
261 - (void)appendEpozScript:(NSString *)_scriptName
262 toResponse:(WOResponse *)_response
263 inContext:(WOContext *)_ctx
267 src = [self urlForEpozResourceNamed:_scriptName inContext:_ctx];
268 [_response appendContentString:@"<script language=\"JavaScript\""];
269 [_response appendContentString:@" type=\"text/javascript\""];
270 [_response appendContentString:@" src=\""];
271 [_response appendContentHTMLAttributeValue:src];
272 [_response appendContentString:@"\"></script>\n"];
275 - (NSString *)epozImageURLInContext:(WOContext *)_ctx {
277 Note: Epoz takes the directory where the resources are located, not
278 individual pointers. This also means that you need to have all
279 Epoz resources in the directory which contains the
280 "epoz_button_bold.gif" (which is used as the "reference" point
281 to the active buttons).
286 src = [self urlForEpozResourceNamed:@"epoz_button_bold.gif" inContext:_ctx];
287 r = [src rangeOfString:@"/" options:(NSBackwardsSearch | NSLiteralSearch)];
289 src = [src substringToIndex:(r.location + r.length)];
293 - (NSString *)epozToolboxURLInContext:(WOContext *)_ctx {
295 // return @"/epoz/WebServerResources/toolbox";
296 return [self urlForEpozResourceNamed:@"epoz_toolbox.html" inContext:_ctx];
298 - (NSString *)epozCSSURLInContext:(WOContext *)_ctx {
299 return [self urlForEpozResourceNamed:@"epoz.css" inContext:_ctx];
302 - (void)appendEpozToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
303 WOComponent *sComponent;
306 sComponent = [_ctx component];
307 v = [self stringValueInContext:_ctx];
309 /* first, the background iframe */
310 [_r appendContentString:@"<iframe id=\"EpozIFrame\""];
311 [_r appendContentString:@" style=\""];
312 [_r appendContentString:@"position: absolute;"];
313 [_r appendContentString:@" visibility: hidden;"];
314 [_r appendContentString:@" width: 0px; height: 0px;"];
315 [_r appendContentString:@"\"></iframe>\n"];
317 /* the language mappings */
318 [self appendEpozScript:@"epoz_lang_en.js"
319 toResponse:_r inContext:_ctx];
321 /* the processing JavaScripts */
322 [self appendEpozScript:@"epoz_script_widget.js"
323 toResponse:_r inContext:_ctx];
324 [self appendEpozScript:@"epoz_script_detect.js"
325 toResponse:_r inContext:_ctx];
326 [self appendEpozScript:@"epoz_script_main.js"
327 toResponse:_r inContext:_ctx];
331 """ Create an Epoz-Wysiwyg-Editor.
333 name : the name of the form-element which submits the data
334 data : the data to edit
335 toolbox : a link to a HTML-Page which delivers additional tools
336 lang: a code for the language-file (en,de,...)
337 path : path to Epoz-Javascript. Needed mainly for Plone (portal_url).
338 widget: You can specify a path to an alternative JavaScript for
339 epoz_script_widget.js
340 style : style-definition for the editor-area
341 button : style-definiton for buttons
343 If Epoz can't create a Rich-Text-Editor, a simple textarea is created.
347 [_r appendContentString:
348 @"<script language=\"JavaScript\" type=\"text/javascript\"><!--\n"];
349 [_r appendContentString:@"InitEpoz("];
352 [_r appendContentString:@"'"];
353 [_r appendContentString:OWFormElementName(self, _ctx)];
354 [_r appendContentString:@"',"];
357 [_r appendContentString:@"'"];
359 /* TODO: escape value, is that enough? */
363 jsv = [jsv stringByReplacingString:@"'" withString:@"\\'"];
364 jsv = [jsv stringByReplacingString:@"\n" withString:@"\\n"];
365 jsv = [jsv stringByReplacingString:@"\r" withString:@""];
366 [_r appendContentString:jsv];
368 [_r appendContentString:@"',"];
371 [_r appendContentString:@"'"];
372 [_r appendContentString:[self epozImageURLInContext:_ctx]];
373 [_r appendContentString:@"',"];
376 [_r appendContentString:@"'"];
377 [_r appendContentString:[self epozToolboxURLInContext:_ctx]];
378 [_r appendContentString:@"',"];
380 if ((tmp = [self->epozStyle stringValueInComponent:sComponent]) == nil)
381 tmp = @"width: 590px; height: 250px; border: 1px solid #000000;";
382 [_r appendContentString:@"'"];
383 [_r appendContentString:tmp];
384 [_r appendContentString:@"',"];
386 if ((tmp=[self->epozButtonStyle stringValueInComponent:sComponent])==nil) {
388 @"background-color: #EFEFEF; border: 1px solid #A0A0A0; "
389 @"cursor: pointer; margin-right: 1px; margin-bottom: 1px;";
391 [_r appendContentString:@"'"];
392 [_r appendContentString:tmp];
393 [_r appendContentString:@"',"];
396 [_r appendContentString:@"'"];
397 [_r appendContentString:[self epozCSSURLInContext:_ctx]];
398 [_r appendContentString:@"',"];
401 if ((tmp = [self->epozCharset stringValueInComponent:sComponent]) == nil)
403 [_r appendContentString:@"'"];
404 [_r appendContentString:tmp];
405 [_r appendContentString:@"'"];
407 [_r appendContentString:@");\n"];
408 [_r appendContentString:@"//-->\n</script>"];
410 /* fallback if scripting is disabled */
411 [_r appendContentString:@"<noscript>"];
412 [self appendTextAreaToResponse:_r inContext:_ctx];
413 [_r appendContentString:@"</noscript>"];
416 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
417 if ([self isEpozBrowserInContext:_ctx])
418 [self appendEpozToResponse:_response inContext:_ctx];
420 [self appendTextAreaToResponse:_response inContext:_ctx];
425 - (NSString *)associationDescription {
426 NSMutableString *str = nil;
428 str = [NSMutableString stringWithCapacity:64];
430 if (self->value) [str appendFormat:@" value=%@", self->value];
431 if (self->name) [str appendFormat:@" name=%@", self->name];
432 if (self->disabled) [str appendFormat:@" disabled=%@", self->disabled];
434 if (self->rows) [str appendFormat:@" rows=%@", self->rows];
435 if (self->cols) [str appendFormat:@" cols=%@", self->cols];
440 @end /* WEEpozEditor */