Hi all, I'm hoping a Zope guru on the list can help me with this one. I'm writing a python product that does some fancy processing of HTML. The problem is that the DTML tag I add to the output of getHTML doesn't get rendered in my DTML Document when I call the object. I'm probably overlooking the obvious, but I'm totally stuck :-S The code is like: def getHTML(self): "Returns HTML for page" html = self._htmltxt # do lots of stuff to html here, but just to keep it simple: html += "<p><dtml-var id></p>" return html def index_html (self, REQUEST, RESPONSE, preview=0, original=0): """ The default view of the contents of an Article Page. """ return self.view_normal() view_normal=DTMLFile('dtml/viewArticlePage', globals()) viewArticlePage.dtml does a <dtml-var getHTML> to publish the object. Thanks, Michiel Toneman
I'm hoping a Zope guru on the list can help me with this one. I'm writing a python product that does some fancy processing of HTML. The problem is that the DTML tag I add to the output of getHTML doesn't get rendered in my DTML Document when I call the object. I'm probably overlooking the obvious, but I'm totally stuck :-S
viewArticlePage.dtml does a <dtml-var getHTML> to publish the object.
DTML renders the code once and once only processing getHTML. What you really want to do is html += "<p>%s</p>" % self.getId() Mixing HTML and Python is bad, you should be putting any HTML in your DTML or ZPT and making calls to your python object to get bits and peices or do things... -- Andy McKay
At 07:38 AM 1/22/2003, Michiel Toneman wrote:
The problem is that the DTML tag I add to the output of getHTML doesn't get rendered in my DTML Document when I call the object.
I hate when people on a programming list answer a simple question by telling the poster not to do something the way they're doing it, but here goes: Don't do it that way. DTML and ZPT are available for rendering HTML. Don't do it in your product code. Just don't. Seriously, don't. In a product, it's a far better strategy to define your user interface by using ZPT or DTML in stand-alone files in the product folder. It would appear you already know how to set up and call such pages, but allow me to suggest a couple things. First, use: index_html = DTMLFile('dtml/viewArticlePage', globals()) There's no good reason to call the extra function. If viewArticlePage needs additional logic, call something in your product. Second, use DTML how it's designed, namely for inserting dynamic information into a pre-defined layout. Re-factor until *all* of your HTML is done in a templating language and your product only contains non-layout data. Seriously... not one extra in your data! Having done that, your user interfaces will stay isolated while you focus on using Python for the truly important/difficult stuff like getting your security assertions right, managing your data and performing your logic correctly. I doubt that was the answer you were looking for, but I suspect it's the one you will find most helpful in the long run. I made the mistake of doing all my HTML rendering in code in my first real product and I ended up regretting it deeply. Dylan
Hi Dylan, hi Andy Great answers, very true and also useful, thanks. "Don't do it that way!" has in the past been some of the best advice given to me. (Un?)Fortunately, I just put the "<p><dtml-var id></p>" in there as an example to simplify the explanation of my problem, and (I think) I have a valid reason for wanting to do this. (I'd love some feedback on this. If you decide I'm nuts anyway, I'll probably have agree with you ;-) )
Second, use DTML how it's designed, namely for inserting dynamic information into a pre-defined layout.
I wouldn't dream of putting HTML in that spot in the "real" application, but I'm a bit of a HTML purist, and I can't bear to see content loose on a page without a proper HTML "p" wrapper ;-) What I'm actually doing is building a Content Management system for which the layouts are not pre-defined. The content manager edits content using structured text. The getHTML function takes the structured text, translates this into a HTML snippet. This part is easily achieved in DTML only. Now it gets tricky, though. I need to insert small "info boxes" into the html. These are "floated" just after a paragraph tag. Not all paragraphs have such a floating info-box, this is selectable by the content manager. The HTML might look like this: ---------------------------------------------------------- <h2>Lorem ipsum dolor sit elitr</h2> <p> <div style="float:right; width: 150px;"> <img src="images/voorbeeld4.jpg" alt="voorbeeld" /><br /> Lorem ipsum dolor </div> Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. </p> ---------------------------------------------------------- where the "div" is opional. This is the part that DTML can't handle. To do this, I feed the HTML Snippet to the DOM parser, locate all the paragraphs, and insert a "dtml-var" tag just after the "p" tag where ever a "floating box" should be present. I have thus preprocessed the structured-text into resultant DTML, for which I'd like to let the DTML renderer do the heavy lifting. I was hoping that simply rendering getHTML in a DTML document would do the trick, but the return value is output "raw" into the DTML document. My "real" code therefore looks like: --------------------------------------------------------------- from StructuredText import DocumentClass from StructuredText import HTMLClass from xml.dom.minidom import parse, parseString ..snip... def getHTML(self): "Returns HTML for page" #return self._htmltxt Doc = DocumentClass.DocumentClass() HTML = HTMLClass.HTMLClass() text = Doc(ST.StructuredText(self._htmltxt)) html = "<div>" + HTML(text, level=2, header=0) + "</div>" dom = parseString(html) pars = dom.getElementsByTagName("p") parcount = 0 for i in pars: parcount += 1 c = dom.createComment("#var par%d" %parcount) ref = i.firstChild i.insertBefore(c, ref) html = dom.toxml() ----------------------------------------------------------- I'd be grateful for any feedback. Either an alternative approach, or a way to render getHTML as DTML in a DTMLDocument. Sorry for the long posting. Greetings, Michiel
At 07:02 AM 1/23/2003, Michiel Toneman wrote:
Hi Dylan, hi Andy
Great answers, very true and also useful, thanks. "Don't do it that way!" has in the past been some of the best advice given to me.
Me too. Let's see if it bears out in this case...
(Un?)Fortunately, I just put the "<p><dtml-var id></p>" in there as an example to simplify the explanation of my problem, and (I think) I have a valid reason for wanting to do this. (I'd love some feedback on this. If you decide I'm nuts anyway, I'll probably have agree with you ;-) )
Sure. One of the most frustrating things about these lists is when you put up a trivial example of a complex problem and then you get responses to the example, not the issue itself.
What I'm actually doing is building a Content Management system for which the layouts are not pre-defined.
OK....
The content manager edits content using structured text. The getHTML function takes the structured text, translates this into a HTML snippet. This part is easily achieved in DTML only.
Yep.
Now it gets tricky, though. I need to insert small "info boxes" into the html. These are "floated" just after a paragraph tag. Not all paragraphs have such a floating info-box, this is selectable by the content manager.
The HTML might look like this:
---------------------------------------------------------- <h2>Lorem ipsum dolor sit elitr</h2> <p> <div style="float:right; width: 150px;"> <img src="images/voorbeeld4.jpg" alt="voorbeeld" /><br /> Lorem ipsum dolor </div> Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. </p> ----------------------------------------------------------
where the "div" is opional. This is the part that DTML can't handle.
<div<dtml-if is_floater> style="float:right; width: 150px;"</dtml-if>> stuff </div>
To do this, I feed the HTML Snippet to the DOM parser, locate all the paragraphs, and insert a "dtml-var" tag just after the "p" tag where ever a "floating box" should be present.
Or you could use CSS. Label all the sections and use DTML to dynamically implement the appropriate behavior in the style sheet.
I have thus preprocessed the structured-text into resultant DTML, for which I'd like to let the DTML renderer do the heavy lifting.
Wouldn't the easiest thing be to have the DTML document retrieve the structured text it needs and render it? Ex: <dtml-var some_text fmt="structured-text">
I was hoping that simply rendering getHTML in a DTML document would do the trick, but the return value is output "raw" into the DTML document.
Yes, that's because rendering getHTML into text is all that's done when the DTML is processed. It won't recurse into the results to look for further instructions. That's a *good* thing.
My "real" code therefore looks like:
<snip>
html = "<div>" + HTML(text, level=2, header=0) + "</div>"
Why not: (in Python) def get_text(self): return self._htmltxt (in DTML) <div> <dtml-var "get_text()" fmt="structured-text"> </div> If you want the behavior of your div to be controlled directly by the product, how about: (in Python) _layouts = { 'layout1': 'float:right; width: 150px;', 'layout2': 'width: 150px;' } def div_layout(self, some_condition): if some_condition == some_other_thing: return self._layouts['layout1] else: return self._layouts['layout2'] (in DTML) <div <dtml-var "div_layout(some_info)">> <dtml-var "get_text()" fmt="structured-text"> </div> Obviously, I'm inferring additional logic and data.... but does that help at all? Dylan
(Un?)Fortunately, I just put the "<p><dtml-var id></p>" in there as an example to simplify the explanation of my problem, and (I think) I have a valid reason for wanting to do this. (I'd love some feedback on this. If you decide I'm nuts anyway, I'll probably have agree with you ;-) )
Sure. One of the most frustrating things about these lists is when you put up a trivial example of a complex problem and then you get responses to the example, not the issue itself.
LOL :)
My "real" code therefore looks like:
<snip>
html = "<div>" + HTML(text, level=2, header=0) + "</div>"
*) sidenote: the <div> is in there to make sure that the XML DOM parser doesn't puke because it doesn't have a root/enclosing tag ;-)
Why not:
(in Python) def get_text(self): return self._htmltxt
(in DTML) <div> <dtml-var "get_text()" fmt="structured-text"> </div>
Ah I recognise this. That is exactly what I had before I painted myself into a corner. ;-) It unfortunately doesn't solve the problem. I want the content manager to never see the complexity of the rendered HTML, but to work in structured text. Thus the content manager would write: ---------------------------------------------- First header para 1. blablabla para 2. blablabla Second header para 3. blablabla ---------------------------------------------- This renders to the following HTML (invisible to the content manager) ---------------------------------------------- <h2>First header</h2> <p> para 1. blablabla </p> <p> para 2. blablabla </p> <h2>Second header</h2> <p> para 3. blablabla </p> ---------------------------------------------- The content manager now adds ((through a click-and-drool interface) a floating box to paragraph 2 (created by Zope id="box263") and a floating box to paragraph 3 (id="box544") Now I'd like my DTML to look like: ---------------------------------------------- <h2>First header</h2> <p> para 1. blablabla </p> <p><dtml-var box263> para 2. blablabla </p> <h2>Second header</h2> <p><dtml-var box554> para 3. blablabla </p> ---------------------------------------------- The problem is finding the paragraphs, for which I don't think there is a elegant solution when doing fmt="structured-text" (at least, not easily). JavaScript + DOM would work, but I don't want to make the site dependent on client-side JavaScript (that still wouldn't do me any good, because the DTML wouldn't work client side either). Using DOM and python, this is simple, though.... I think the right answer to the question is: "Don't try to put floating boxes in structured-text content!", which boils down to "Don't do it that way!" :-) This answer may, however, upset my web-designer to the point of physical violence :-/ So I'm still looking for a way to render the resulting DTML the "hard" way. If I succeed, I'll write it up in a tutorial, I promise 8-| Hope you're not too annoyed with me yet ;-) Greetings, Michiel
At 08:37 AM 1/23/2003, Michiel Toneman wrote:
The content manager now adds ((through a click-and-drool interface) a floating box to paragraph 2 (created by Zope id="box263") and a floating box to paragraph 3 (id="box544")
So the interface allows the user to associate a floating box ("box263", say) with a particular insertion point (paragraph 2). Assuming that this information is captured and stored something like the following: self._boxes[box_id] = {'box_content': 'blah blah', 'insert_point':'P3', 'box_format':'size=blah;'} Now what you need is one of two things: 1. A way of naming <P> tags and making calls from DTML based on the names. 2. A "finishing" method that inserts the correct markup *after* DTML has done its job. Looks like you already thought through idea #1 and concluded that it would be very hard. I'm inclined to agree. But what about idea #2? How tough would it be? (This is top-of-the-head stuff, BTW... untested, no warranty, etc.) DTML Doc first_pass.dtml: ---------------------------------------- <dtml-var "get_text()" fmt="structured-text"> DTML Doc second_pass.dtml: ---------------------------------------- <dtml-call "REQUEST.set('my_doc', first_pass)"> <dtml-var "add_boxes(my_doc)"> Your product code: ---------------------------------------- from re import search from StringIO import StringIO # gives file methods to string objects. def has_box(self, graph_id): for box in self._boxes.keys(): if self.boxes[box]['insert_point'] == graph_id: return box return None def add_boxes(self, doc_text): # count up <P> tags and insert any defined boxes graph_count = 0 doc_lines = StringIO(doc_text).readlines() index = 0 for line in doc_lines: if search('<P.*?>', line): graph_count +=1 # assuming you number graphs from 1 box_id = self.has_box('P%s' % graph_count) if box_id: my_box = self.boxes[box] doc_lines[index].append('<div %s>%s</div>' % (my_box.get('box_format'), my_box.get('box_content')) # or use re.sub() for better precision... index += 1 return '\n'.join(doc_lines) Getting warmer?
On Thu, 2003-01-23 at 18:47, Dylan Reinhardt wrote:
At 08:37 AM 1/23/2003, Michiel Toneman wrote:
The content manager now adds ((through a click-and-drool interface) a floating box to paragraph 2 (created by Zope id="box263") and a floating box to paragraph 3 (id="box544")
So the interface allows the user to associate a floating box ("box263", say) with a particular insertion point (paragraph 2).
Assuming that this information is captured and stored something like the following:
self._boxes[box_id] = {'box_content': 'blah blah', 'insert_point':'P3', 'box_format':'size=blah;'}
Now what you need is one of two things: 1. A way of naming <P> tags and making calls from DTML based on the names. 2. A "finishing" method that inserts the correct markup *after* DTML has done its job.
Looks like you already thought through idea #1 and concluded that it would be very hard. I'm inclined to agree.
But what about idea #2? How tough would it be?
...snip...
Getting warmer?
Great stuff! This should work, although more complex than the DOM traversal. There is something odd going on in my product though. I tried a different approach to the problem and managed to write the finished DTML source into a standard PropertySheet text-property. Now I rendered that variable in a seperate and standard DTML Document like: <dtml-with "MyCMFolder.MyProductInstance"> <dtml-var rendered_html> </dtml-with> And the dtml in "rendered_html" doesn't expand/render either. There must be something I'm doing drastically wrong for this to fail :-S. Is there a sort of object mime-type or environment/namespace setting that is telling the DTML Document not to expand the DTML in the "rendered_html" property? ( "rendered_html" contains only <dtml-var expr="5*6"> ) Thanks for all the help so far, I'm very grateful. Greetings, Michiel
At 10:39 AM 1/23/2003, Michiel Toneman wrote:
There is something odd going on in my product though. I tried a different approach to the problem and managed to write the finished DTML source into a standard PropertySheet text-property. Now I rendered that variable in a seperate and standard DTML Document like:
<dtml-with "MyCMFolder.MyProductInstance"> <dtml-var rendered_html> </dtml-with>
And the dtml in "rendered_html" doesn't expand/render either.
By that, do you mean you get back the DTML code from rendered_html? If so, try: <dtml-var "rendered_html"> If that's not it, you might want to re-post this issue with a new subject and some additional detail about where the relevant objects are in your ZMI. HTH, Dylan
there is a recipe on ZopeLabs that I wrote, that takes a piece of text and renders is as if it where DTML: http://www.zopelabs.com/cookbook/1011691351 "This is a simple way of rendering strings as if they where DTML from PythonScripts. Sometimes you might need to generate DTML code or, as is my case, you store all your content in a external datastore (like a RDBMS) and oly have access to your code in textual format. " Should be trivial to adapt to standar Python for use in a Product. Hope this helps, /dario ----- Original Message ----- From: "Dylan Reinhardt" <zope@dylanreinhardt.com> To: "Michiel Toneman" <michiel@ylnd.com> Cc: <zope@zope.org> Sent: Thursday, January 23, 2003 11:08 PM Subject: Re: [Zope] DTML rendering from python?
At 10:39 AM 1/23/2003, Michiel Toneman wrote:
There is something odd going on in my product though. I tried a different approach to the problem and managed to write the finished DTML source into a standard PropertySheet text-property. Now I rendered that variable in a seperate and standard DTML Document like:
<dtml-with "MyCMFolder.MyProductInstance"> <dtml-var rendered_html> </dtml-with>
And the dtml in "rendered_html" doesn't expand/render either.
By that, do you mean you get back the DTML code from rendered_html?
If so, try: <dtml-var "rendered_html">
If that's not it, you might want to re-post this issue with a new subject and some additional detail about where the relevant objects are in your ZMI.
HTH,
Dylan
Solved!!!! :) Thank you all for the great help. The Zope community really rocks! This is what I ended up doing: -------------------------------------------- from OFS.DTMLMethod import DTMLMethod .....snip..... def getHTML(self): "Returns HTML for page" .....snip..... html = dom.toxml() return self.renderstring(html.encode('utf-8')) def renderstring(self, str, ns={}, REQUEST=None): m = DTMLMethod(str, REQUEST) return m(self, ns) -------------------------------------------- Works like a charm :-) I can now let a content manager edit the pages using structured-text, control the rendering to HTML, do arbitrary DOM transforms on the HTML, (which in this case is incorporating floating boxes into the HTML flow at specified points) and finally render the whole thing as a DTML document. I'm soooooooo happy :-D Dieter: I'll have a look at the method you suggested as well. It seems quite similar in approach. Again thank you all for the great help and support. Greetings, Michiel
Michiel Toneman wrote at 2003-1-22 16:38 +0100:
I'm writing a python product that does some fancy processing of HTML. The problem is that the DTML tag I add to the output of getHTML doesn't get rendered in my DTML Document when I call the object. I'm probably overlooking the obvious, but I'm totally stuck :-S
The code is like:
def getHTML(self): "Returns HTML for page" html = self._htmltxt # do lots of stuff to html here, but just to keep it simple: html += "<p><dtml-var id></p>" return html You must create a "DocumentTemplate.DT_HTML.HTML" instance and then render (i.e. call) it.
Read the section "Calling DTML objects" in <http://www.dieter.handshake.de/pyprojects/zope/book/chap3.html> to call your "HTML" instance correctly. Dieter
participants (6)
-
Andy McKay -
Dario Lopez-Kästen -
Dieter Maurer -
Dylan Reinhardt -
Dylan Reinhardt -
Michiel Toneman