| 746 | We're still missing an important bit of functionality: people should be able to comment on submitted links. Three things are needed to implement that: |
| 747 | * a detail view of a link, showing all comments made so far, |
| 748 | * a way to get to that page from the list of links, and, |
| 749 | * a form to add new comments. |
| 750 | |
| 751 | Note that on the model side we're covered, there's already a `Comment` class in `geddit.model`, and we even have two comments in our database already. |
| 752 | |
| 753 | So let's start by extending the `index.html` template to show how many comments there are so far, and make that a link to the detail page. Change your `geddit/templates/index.html` file to match the following: |
| 754 | |
| 755 | {{{ |
| 756 | #!genshi |
| 757 | <!DOCTYPE html> |
| 758 | <html xmlns="http://www.w3.org/1999/xhtml" |
| 759 | xmlns:xi="http://www.w3.org/2001/XInclude" |
| 760 | xmlns:py="http://genshi.edgewall.org/"> |
| 761 | <xi:include href="layout.html" /> |
| 762 | <head> |
| 763 | <title>News</title> |
| 764 | </head> |
| 765 | <body> |
| 766 | <h1>News</h1> |
| 767 | <p><a href="${url('/submit/')}">Submit new link</a></p> |
| 768 | |
| 769 | <ol py:if="links" class="links"> |
| 770 | <li py:for="link in links"> |
| 771 | <a href="${link.url}">${link.title}</a> |
| 772 | posted by ${link.username} at ${link.time.strftime('%m/%d/%Y %H:%M')} |
| 773 | <div class="info"> |
| 774 | <a href="${url('/info/%s/' % link.id)}"> |
| 775 | ${len(link.comments)} comments |
| 776 | </a> |
| 777 | </div> |
| 778 | </li> |
| 779 | </ol> |
| 780 | </body> |
| 781 | </html> |
| 782 | }}} |
| 783 | |
| 784 | We've added a `<div class="info">` for every link in the list, each containing the number of comments, and linking to the detail page. |
| 785 | |
| 786 | Of couse, if you click on those links, you'll get an error page: we haven't implemented the `info()` view yet! |
| 787 | |
| 788 | Let's do that now. Add the following method to the `Root` class: |
| 789 | |
| 790 | {{{ |
| 791 | #!python |
| 792 | @cherrypy.expose |
| 793 | @template.output('info.html') |
| 794 | def info(self, code): |
| 795 | link = self.data.get(code) |
| 796 | if not link: |
| 797 | raise cherrypy.NotFound() |
| 798 | return template.render(link=link) |
| 799 | }}} |
| 800 | |
| 801 | And then add the needed temlate `geddit/templates/info.html` with the following content: |
| 802 | |
| 803 | {{{ |
| 804 | #!genshi |
| 805 | <!DOCTYPE html> |
| 806 | <html xmlns="http://www.w3.org/1999/xhtml" |
| 807 | xmlns:xi="http://www.w3.org/2001/XInclude" |
| 808 | xmlns:py="http://genshi.edgewall.org/"> |
| 809 | <xi:include href="layout.html" /> |
| 810 | <head> |
| 811 | <title>${link.title}</title> |
| 812 | </head> |
| 813 | <body> |
| 814 | <h1>${link.title}</h1> |
| 815 | <a href="${link.url}">${link.url}</a><br /> |
| 816 | posted by ${link.username} at ${link.time.strftime('%m/%d/%Y %H:%M')}<br /> |
| 817 | <a href="${url('/comment/%s/' % link.id)}">comment</a> |
| 818 | <ul py:if="link.comments" class="comments"> |
| 819 | <li py:for="comment in link.comments"> |
| 820 | <strong>${comment.username}</strong> |
| 821 | at ${comment.time.strftime('%m/%d/%Y %H:%M')} |
| 822 | <blockquote>${comment.content}</blockquote> |
| 823 | </li> |
| 824 | </ul> |
| 825 | </body> |
| 826 | </html> |
| 827 | }}} |
| 828 | |
| 829 | At this point you should be able to see the number of comments on the start page, click on that link to get to the details page, where you should see all comments listed for the corresponding link submission. That page also contains a link for submitting additional comments, and that's what we'll need to set up next. |
| 830 | |
| 831 | First, we need to add a form for submitting comments to `geddit/form.py`. At the bottom of that file, add the following class: |
| 832 | |
| 833 | {{{ |
| 834 | #!python |
| 835 | class CommentForm(Schema): |
| 836 | username = validators.UnicodeString(not_empty=True) |
| 837 | content = validators.UnicodeString(not_empty=True) |
| 838 | }}} |
| 839 | |
| 840 | And add a corresponding import to `geddit/controller.py`, which should then have a line like this somewhere at the top: |
| 841 | |
| 842 | {{{ |
| 843 | #!python |
| 844 | from geddit.form import LinkForm, CommentForm |
| 845 | }}} |
| 846 | |
| 847 | Now we need to add the method for handling comment submissions to our `Root` object. It should look like this: |
| 848 | |
| 849 | {{{ |
| 850 | #!python |
| 851 | @cherrypy.expose |
| 852 | @template.output('comment.html') |
| 853 | def comment(self, code, cancel=False, **data): |
| 854 | link = self.data.get(code) |
| 855 | if not link: |
| 856 | raise cherrypy.NotFound() |
| 857 | if cherrypy.request.method == 'POST': |
| 858 | if cancel: |
| 859 | raise cherrypy.HTTPRedirect('/info/%s' % link.id) |
| 860 | form = CommentForm() |
| 861 | try: |
| 862 | data = form.to_python(data) |
| 863 | comment = link.add_comment(**data) |
| 864 | raise cherrypy.HTTPRedirect('/info/%s' % link.id) |
| 865 | except Invalid, e: |
| 866 | errors = e.unpack_errors() |
| 867 | else: |
| 868 | errors = {} |
| 869 | |
| 870 | return template.render(link=link, comment=None, |
| 871 | errors=errors) | HTMLFormFiller(data=data) |
| 872 | |
| 873 | }}} |
| 874 | |
| 875 | Last but not least, we need the template that renders the comment submission form. Inside `geddit/templates`, add a file named `comment.html`, and insert the following content: |
| 876 | |
| 877 | {{{ |
| 878 | #!genshi |
| 879 | <!DOCTYPE html> |
| 880 | <html xmlns="http://www.w3.org/1999/xhtml" |
| 881 | xmlns:xi="http://www.w3.org/2001/XInclude" |
| 882 | xmlns:py="http://genshi.edgewall.org/"> |
| 883 | <xi:include href="layout.html" /> |
| 884 | <head> |
| 885 | <title>Comment on “${link.title}”</title> |
| 886 | </head> |
| 887 | <body> |
| 888 | <h1>Comment on “${link.title}”</h1> |
| 889 | <p py:if="comment"> |
| 890 | In reply to <strong>${comment.username}</strong> |
| 891 | at ${comment.time.strftime('%M/%d/%Y %H:%m')}: |
| 892 | <blockquote>${comment.content}</blockquote> |
| 893 | </p> |
| 894 | <form action="" method="post"> |
| 895 | <table summary=""><tbody><tr> |
| 896 | <th><label for="username">Your name:</label></th> |
| 897 | <td> |
| 898 | <input type="text" id="username" name="username" /> |
| 899 | <span py:if="'username' in errors" class="error">${errors.username}</span> |
| 900 | </td> |
| 901 | </tr><tr> |
| 902 | <th><label for="comment">Comment:</label></th> |
| 903 | <td> |
| 904 | <textarea id="comment" name="content" rows="6" cols="50"></textarea> |
| 905 | <span py:if="'content' in errors" class="error"><br />${errors.content}</span> |
| 906 | </td> |
| 907 | </tr></tbody></table> |
| 908 | <div> |
| 909 | <input type="submit" value="Submit" /> |
| 910 | <input type="submit" name="cancel" value="Cancel" /> |
| 911 | </div> |
| 912 | </form> |
| 913 | </body> |
| 914 | </html> |
| 915 | }}} |
| 916 | |
| 917 | Phew! We should be done with the commenting now. Play around with the application a bit to get a feel for what we've achieved so far. The next section will look into various things that can be done to further improve the application. |
| 918 | |
| 919 | == Advanced Topics == |
| 920 | |
| 921 | === Allowing Markup in Comments === |
| 922 | |