changeset 625:dba522b4c31d

GenshiTutorial: implemented AJAX commenting.
author cmlenz
date Fri, 31 Aug 2007 17:01:00 +0000
parents 1d23dddd6c2d
children 3110105824e8
files examples/tutorial/geddit/controller.py examples/tutorial/geddit/lib/ajax.py examples/tutorial/geddit/lib/template.py examples/tutorial/geddit/model.py examples/tutorial/geddit/templates/_comment.html examples/tutorial/geddit/templates/_form.html examples/tutorial/geddit/templates/comment.html examples/tutorial/geddit/templates/index.html examples/tutorial/geddit/templates/info.html examples/tutorial/geddit/templates/submit.html
diffstat 10 files changed, 109 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/examples/tutorial/geddit/controller.py
+++ b/examples/tutorial/geddit/controller.py
@@ -10,7 +10,7 @@
 from genshi.filters import HTMLFormFiller
 
 from geddit.form import LinkForm, CommentForm
-from geddit.lib import template
+from geddit.lib import ajax, template
 from geddit.model import Link, Comment
 
 
@@ -77,14 +77,20 @@
             try:
                 data = form.to_python(data)
                 comment = link.add_comment(**data)
-                raise cherrypy.HTTPRedirect('/info/%s' % link.id)
+                if not ajax.is_xhr():
+                    raise cherrypy.HTTPRedirect('/info/%s' % link.id)
+                return template.render('_comment.html', comment=comment,
+                                       num=len(link.comments))
             except Invalid, e:
                 errors = e.unpack_errors()
         else:
             errors = {}
 
-        return template.render(link=link, comment=None,
-                               errors=errors) | HTMLFormFiller(data=data)
+        if ajax.is_xhr():
+            stream = template.render('_form.html', link=link, errors=errors)
+        else:
+            stream = template.render(link=link, comment=None, errors=errors)
+        return stream | HTMLFormFiller(data=data)
 
 
 def main(filename):
new file mode 100644
--- /dev/null
+++ b/examples/tutorial/geddit/lib/ajax.py
@@ -0,0 +1,5 @@
+import cherrypy
+
+def is_xhr():
+    requested_with = cherrypy.request.headers.get('X-Requested-With')
+    return requested_with and requested_with.lower() == 'xmlhttprequest'
--- a/examples/tutorial/geddit/lib/template.py
+++ b/examples/tutorial/geddit/lib/template.py
@@ -5,6 +5,8 @@
 from genshi.output import encode, get_serializer
 from genshi.template import Context, TemplateLoader
 
+from geddit.lib import ajax
+
 loader = TemplateLoader(
     os.path.join(os.path.dirname(__file__), '..', 'templates'),
     auto_reload=True
@@ -18,9 +20,10 @@
     def decorate(func):
         def wrapper(*args, **kwargs):
             cherrypy.thread_data.template = loader.load(filename)
-            if method == 'html':
-                options.setdefault('doctype', 'html')
-            serializer = get_serializer(method, **options)
+            opt = options.copy()
+            if not ajax.is_xhr() and method == 'html':
+                opt.setdefault('doctype', 'html')
+            serializer = get_serializer(method, **opt)
             stream = func(*args, **kwargs)
             if not isinstance(stream, Stream):
                 return stream
--- a/examples/tutorial/geddit/model.py
+++ b/examples/tutorial/geddit/model.py
@@ -15,7 +15,9 @@
         return '<%s %r>' % (type(self).__name__, self.title)
 
     def add_comment(self, username, content):
-        self.comments.append(Comment(username, content))
+        comment = Comment(username, content)
+        self.comments.append(comment)
+        return comment
 
 
 class Comment(object):
@@ -26,4 +28,4 @@
         self.time = datetime.utcnow()
 
     def __repr__(self):
-        return '<%s>' % (type(self).__name__)
+        return '<%s by %r>' % (type(self).__name__, self.username)
new file mode 100644
--- /dev/null
+++ b/examples/tutorial/geddit/templates/_comment.html
@@ -0,0 +1,4 @@
+<li id="comment$num">
+  <strong>${comment.username}</strong> at ${comment.time.strftime('%x %X')}
+  <blockquote>${comment.content}</blockquote>
+</li>
new file mode 100644
--- /dev/null
+++ b/examples/tutorial/geddit/templates/_form.html
@@ -0,0 +1,23 @@
+<form xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      class="comment" action="${url('/comment/%s/' % link.id)}" method="post">
+  <table summary=""><tbody><tr>
+    <th><label for="username">Your name:</label></th>
+    <td>
+      <input type="text" id="username" name="username" />
+      <span py:if="'username' in errors" class="error">${errors.username}</span>
+    </td>
+  </tr><tr>
+    <th><label for="comment">Comment:</label></th>
+    <td>
+      <textarea id="comment" name="content" rows="6" cols="50"></textarea>
+      <span py:if="'content' in errors" class="error"><br />${errors.content}</span>
+    </td>
+  </tr><tr>
+    <td></td>
+    <td>
+      <input type="submit" value="Submit" />
+      <input type="submit" name="cancel" value="Cancel" />
+    </td>
+  </tr></tbody></table>
+</form>
--- a/examples/tutorial/geddit/templates/comment.html
+++ b/examples/tutorial/geddit/templates/comment.html
@@ -13,24 +13,6 @@
       at ${comment.time.strftime('%x %X')}:
       <blockquote>${comment.content}</blockquote>
     </p>
-    <form action="" method="post">
-      <table summary=""><tbody><tr>
-        <th><label for="username">Your name:</label></th>
-        <td>
-          <input type="text" id="username" name="username" />
-          <span py:if="'username' in errors" class="error">${errors.username}</span>
-        </td>
-      </tr><tr>
-        <th><label for="comment">Comment:</label></th>
-        <td>
-          <textarea id="comment" name="content" rows="6" cols="50"></textarea>
-          <span py:if="'content' in errors" class="error"><br />${errors.content}</span>
-        </td>
-      </tr></tbody></table>
-      <div>
-        <input type="submit" value="Submit" />
-        <input type="submit" name="cancel" value="Cancel" />
-      </div>
-    </form>
+    <xi:include href="_form.html" />
   </body>
 </html>
--- a/examples/tutorial/geddit/templates/index.html
+++ b/examples/tutorial/geddit/templates/index.html
@@ -10,7 +10,6 @@
   </head>
   <body>
     <h1>News</h1>
-    <p><a class="action" href="${url('/submit/')}">Submit new link</a></p>
 
     <ol py:if="links" class="links">
       <li py:for="link in links">
@@ -23,5 +22,7 @@
         </div>
       </li>
     </ol>
+
+    <p><a class="action" href="${url('/submit/')}">Submit new link</a></p>
   </body>
 </html>
--- a/examples/tutorial/geddit/templates/info.html
+++ b/examples/tutorial/geddit/templates/info.html
@@ -5,22 +5,60 @@
   <xi:include href="layout.html" />
   <head>
     <title>${link.title}</title>
-    <link rel="alternate" type="application/atom+xml" title="Geddit: ${link.title}"
-          href="${url('/feed/%s/' % link.id)}" />
+    <link rel="alternate" title="Geddit: ${link.title}"
+          type="application/atom+xml" href="${url('/feed/%s/' % link.id)}" />
+    <script type="text/javascript">
+      function loadCommentForm(a) {
+        $.get("${url('/comment/%s/' % link.id)}", {}, function(html) {
+          var form = a.hide().parent().after(html).next();
+          function closeForm() {
+            form.slideUp("fast", function() { a.fadeIn(); form.remove() });
+            return false;
+          }
+          function initForm() {
+            form.find("input[@name='cancel']").click(closeForm);
+            form.submit(function() {
+              var data = form.find("input[@type='text'], textarea").serialize();
+              $.post("${url('/comment/%s/' % link.id)}", data, function(html) {
+                var elem = $(html).get(0);
+                if (/form/i.test(elem.tagName)) {
+                  form.after(elem).remove();
+                  form = $(elem);
+                  initForm();
+                } else {
+                  if ($("ul.comments").length == 0) {
+                    a.parent().before('<ul class="comments"></ul>');
+                  }
+                  $("ul.comments")
+                    .find("li.hilite").removeClass("hilite").end()
+                    .append($(elem).addClass("hilite")).slideDown();
+                  closeForm();
+                }
+              });
+              return false;
+            });
+          }
+          initForm();
+        });
+      }
+      $(document).ready(function() {
+        $("a.action").click(function() {
+          loadCommentForm($(this));
+          return false;
+        });
+      });
+    </script>
   </head>
   <body>
     <h1>${link.title}</h1>
     <a href="${link.url}">${link.url}</a><br />
     posted by ${link.username} at ${link.time.strftime('%x %X')}<br />
 
-    <p><a class="action" href="${url('/comment/%s/' % link.id)}">comment</a></p>
-
     <ul py:if="link.comments" class="comments">
-      <li py:for="idx, comment in enumerate(link.comments)" id="comment$idx">
-        <strong>${comment.username}</strong>
-        at ${comment.time.strftime('%x %X')}
-        <blockquote>${comment.content}</blockquote>
-      </li>
+      <xi:include href="_comment.html"
+          py:for="num, comment in enumerate(link.comments)" />
     </ul>
+
+    <p><a class="action" href="${url('/comment/%s/' % link.id)}">comment</a></p>
   </body>
 </html>
--- a/examples/tutorial/geddit/templates/submit.html
+++ b/examples/tutorial/geddit/templates/submit.html
@@ -21,18 +21,19 @@
           <input type="text" id="url" name="url" />
           <span py:if="'url' in errors" class="error">${errors.url}</span>
         </td>
-      </tr>
-      <tr>
+      </tr><tr>
         <th><label for="title">Title:</label></th>
         <td>
           <input type="text" name="title" />
           <span py:if="'title' in errors" class="error">${errors.title}</span>
         </td>
+      </tr><tr>
+        <td></td>
+        <td>
+          <input type="submit" value="Submit" />
+          <input type="submit" name="cancel" value="Cancel" />
+        </td>
       </tr></tbody></table>
-      <div>
-        <input type="submit" value="Submit" />
-        <input type="submit" name="cancel" value="Cancel" />
-      </div>
     </form>
   </body>
 </html>
Copyright (C) 2012-2017 Edgewall Software