Changeset 1691

Show
Ignore:
Timestamp:
07/18/08 12:58:51 (20 months ago)
Author:
JensDiemer
Message:

many updates for the blog plugin

Location:
trunk/pylucid
Files:
8 modified

Legend:

Unmodified
Added
Removed
  • trunk/pylucid/media/PyLucid/internal_page/blog/display_blog.css

    r1688 r1691  
    112112  margin: 0px; 
    113113} 
     114.comment .admin_info_line { 
     115  color:red; 
     116  line-height: 0px; 
     117} 
    114118/* -------------------------------------------------------------------------- */ 
    115119#input_fields { 
  • trunk/pylucid/media/PyLucid/internal_page/blog/display_blog.html

    r1688 r1691  
    22{% if create_url %}<p><a href="{{ create_url }}" class="action_link">create a new blog entry</a></p>{% endif %} 
    33 
     4{% if not entries %}no blog entry exists{% endif %} 
    45{% for entry in entries %} 
    56    <fieldset class="entry"><legend class="headline"><a href="{{ entry.detail_url }}">{{ entry.headline }}</a></legend> 
     
    4546                        {{ comment.person_name }}{% if comment.homepage %}</a>{% endif %}: 
    4647                      </legend> 
     48                      {% if not comment.is_public %} 
     49                          <p class="admin_info_line">not public</p> 
     50                      {% endif %} 
    4751 
    4852                      <div class="admin_links"> 
    4953                        {% if comment.edit_url %}<a href="{{ comment.edit_url }}">edit</a>{% endif %} 
     54                        {% if comment.delete_url %}<a href="{{ comment.delete_url }}" onClick="return confirm('Realy want to delete the comment entry?');">delete</a>{% endif %} 
    5055                      </div> 
    5156 
  • trunk/pylucid/media/PyLucid/internal_page/blog/mod_comments.html

    r1688 r1691  
    33{% for comment in comments %} 
    44  <li> 
    5     <a href="{{ comment.edit_url }}">{{ comment }}</a> 
     5    <a href="{{ comment.edit_url }}">edit</a> 
     6    {{ comment.content }} 
     7    {{ comment.blog_entry.headline }} 
    68  </li> 
    79{% endfor %} 
  • trunk/pylucid/media/PyLucid/internal_page/blog/notify_mailtext.html

    r1688 r1691  
    99  IP Address...: {{ comment_entry.ip_address }} 
    1010  submit time..: {{ comment_entry.createtime }} 
    11 {% if is_spam %} 
    12 This comment is detected as SPAM. It is not saved into the database! 
    13 {% else %} 
    14 Edit this new comment: {{ edit_url }} 
    15 {% endif %} 
     11 
     12--[submit messages]------------------------------------------------------------ 
     13{% for line in submit_msg %} 
     14  {{ line }} 
     15{% endfor %} 
     16--[submit messages end]-------------------------------------------------------- 
     17 
     18{% if edit_url %}Edit this new comment: {{ edit_url }}{% else %}This comment was not saved into the database.{% endif %} 
     19 
    1620--[comment content]------------------------------------------------------------ 
    1721{{ comment_entry.content }} 
  • trunk/pylucid/PyLucid/plugins_internal/blog/blog.py

    r1688 r1691  
    3232from PyLucid.system.BasePlugin import PyLucidBasePlugin 
    3333from PyLucid.tools.content_processors import apply_markup 
     34from PyLucid.tools.newforms_utils import StripedCharField 
    3435from PyLucid.tools.utils import escape_django_tags 
     36from PyLucid.system.page_msg import PageMessages 
    3537from PyLucid.models.Page import MARKUPS 
     38 
     39from PyLucid.plugins_internal.blog.blog_cfg import DONT_CHECK, REJECT_SPAM, \ 
     40                                                                    MODERATED 
    3641 
    3742# Don't send mails, display them only. 
     
    5055    ip_address = models.IPAddressField(_('ip address'),) 
    5156    person_name = models.CharField( 
    52         _("person's name"), max_length=50 
     57        _("person's name"), max_length=50, 
     58        help_text=_("Your full name (will be published) (required)"), 
    5359    ) 
    5460    email = models.EmailField( 
    5561        _('e-mail address'), 
    56         help_text=_("Only for internal use."), 
     62        help_text=_("Only for internal use. (will not be published) (required)"), 
    5763    ) 
    5864    homepage = models.URLField( 
     
    9096 
    9197 
     98 
    9299class BlogCommentForm(forms.ModelForm): 
    93100    """ 
    94     Add a comment. 
     101    Add a new comment. 
    95102    """ 
    96103    person_name = forms.CharField( 
     
    98105        help_text=_("Your name."), 
    99106    ) 
    100     content = forms.CharField( 
     107    content = StripedCharField( 
    101108        label = _('content'), min_length=5, max_length=3000, 
    102109        help_text=_("Your comment to this blog entry."), 
     
    107114        model = BlogComment 
    108115        # Using a subset of fields on the form 
    109         fields = ('person_name', 'email', "homepage", "comment") 
    110  
     116        fields = ('person_name', 'email', "homepage") 
     117 
     118 
     119class AdminCommentForm(BlogCommentForm): 
     120    """ 
     121    Form for editing a existing comment. Only for Admins 
     122    """ 
     123    class Meta: 
     124        model = BlogComment 
     125        fields = ( 
     126            'ip_address', 'person_name', 'email', "homepage", 
     127            "content", "is_public", 
     128            "createtime", "lastupdatetime", "createby", "lastupdateby" 
     129        ) 
    111130 
    112131#______________________________________________________________________________ 
     
    244263 
    245264 
     265 
    246266class blog(PyLucidBasePlugin): 
    247267 
     
    251271        super(blog, self).__init__(*args, **kwargs) 
    252272 
     273        # Log info about handling blog comment submissions 
     274        self.submit_msg = PageMessages( 
     275            self.request, use_django_msg=False, html=False 
     276        ) 
     277 
    253278        # Get the default preference entry. 
    254279        self.preferences = self.get_preferences() 
     
    258283 
    259284 
    260     def _add_comment_edit_url(self, comments): 
     285    def _add_comment_admin_urls(self, comments): 
    261286        for comment in comments: 
    262287            comment.edit_url = self.URLs.methodLink( 
    263288                "edit_comment", comment.id 
     289            ) 
     290            comment.delete_url = self.URLs.methodLink( 
     291                "delete_comment", comment.id 
    264292            ) 
    265293 
     
    287315                if self.request.user.is_staff: 
    288316                    comments = comments.all() 
    289                     self._add_comment_edit_url(comments) 
     317                    self._add_comment_admin_urls(comments) 
    290318                else: 
    291319                    comments = comments.filter(is_public = True).all() 
     
    347375 
    348376        self.current_page.title += " - " + blog_entry.headline 
    349  
    350377 
    351378        if blog_entry.is_public != True: 
     
    363390            #self.page_msg(self.request.POST) 
    364391            if form.is_valid(): 
    365                 ok = self._save_new_comment(blog_entry, form) 
     392                ok = self._save_new_comment( 
     393                    blog_entry, clean_data = form.cleaned_data 
     394                ) 
    366395                if ok: 
    367396                    return self._list_entries([blog_entry], full_comments=True) 
     
    437466                    blog_obj.save() 
    438467                    self.page_msg.green("New blog entry created.") 
     468                    tags_string = form.cleaned_data["tags"] 
    439469                else: 
    440470                    # Update a existing blog entry 
     471                    tags_string = form.cleaned_data.pop("tags") 
     472                    self.page_msg.green("Update existing blog entry.") 
    441473                    blog_obj.lastupdateby = self.request.user 
    442474                    for k,v in form.cleaned_data.iteritems(): 
    443475                        setattr(blog_obj, k, v) 
    444                     self.page_msg.green("Update existing blog entry.") 
    445  
    446                 tags_string = form.cleaned_data["tags"] 
     476 
    447477                tag_objects, new_tags = BlogTag.objects.get_or_creates( 
    448478                    tags_string 
     
    525555    # COMMENTS 
    526556 
    527     def _send_notify(self, mail_title, blog_entry, is_spam, comment_entry): 
     557    def _send_notify(self, mail_title, blog_entry, comment_entry): 
    528558        """ 
    529559        Send a email noitify for a submited blog comment. 
     
    534564                self.URLs.methodLink("edit", blog_entry.id) 
    535565            ), 
    536             "is_spam": is_spam, 
    537566            "comment_entry": comment_entry, 
     567            "submit_msg": self.submit_msg, 
    538568        } 
    539569 
    540         if not is_spam: 
     570        if hasattr(comment_entry, "id"): 
    541571            # Add edit link into the mail 
    542572            email_context["edit_url"] = self.URLs.make_absolute_url( 
     
    554584 
    555585        send_mail_kwargs = { 
     586            "from_email": settings.DEFAULT_FROM_EMAIL, 
    556587            "subject": "%s %s" % (settings.EMAIL_SUBJECT_PREFIX, mail_title), 
    557588#                from_email = sender, 
     
    569600            return 
    570601        else: 
    571             send_mail(message = emailtext,**send_mail_kwargs) 
    572  
    573     def _check_spam(self, blog_entry, form_cleaned_data, content_lower): 
    574         """ 
    575         Check if the submitted comment is spam. 
    576         Display error messages and handle email notify. 
    577         """ 
    578         contains_spam = self._check_wordlist( 
    579             content_lower, pref_key = "spam_keywords" 
    580         ) 
    581         if not contains_spam: 
    582             # The submitted content contains no spam keyword 
    583             return False 
    584  
    585         self.page_msg.red("Sorry, your comment identify as spam.") 
    586  
     602            send_mail(message = emailtext, **send_mail_kwargs) 
     603 
     604    def _reject_spam_comment(self, blog_entry, clean_data): 
     605        """ 
     606        Reject a submited comment as spam: 
     607        1. Display page_msg 
     608        2. Handle email notify. 
     609        """ 
    587610        if not self.preferences["spam_notify"]: 
    588611            # Don't send spam notify email 
    589             return True 
     612            return 
    590613 
    591614        # Add ID Adress for notify mail text 
    592         form_cleaned_data["ip_address"] = self.request.META.get('REMOTE_ADDR') 
    593         form_cleaned_data["createtime"] = datetime.datetime.now() 
     615        clean_data["ip_address"] = self.request.META.get('REMOTE_ADDR') 
     616        clean_data["createtime"] = datetime.datetime.now() 
    594617 
    595618        self._send_notify( 
    596619            mail_title = _("blog comment as spam detected."), 
    597             blog_entry = blog_entry, is_spam = True, 
    598             comment_entry = form_cleaned_data 
    599         ) 
    600         return True 
    601  
    602     def _save_new_comment(self, blog_entry, form): 
     620            blog_entry = blog_entry, comment_entry = clean_data 
     621        ) 
     622 
     623    def _check_comment_submit(self, blog_entry, content): 
     624        """ 
     625        Check the submit of a new blog comment 
     626        """ 
     627        if self.request.user.is_staff: 
     628            # Don't check comments from staff users 
     629            self.submit_msg("comment submit by page member.") 
     630            return _("new blog comment from page member published.") 
     631 
     632        # Check the http referer, exception would be raised if something wrong 
     633        self._check_referer(blog_entry) 
     634 
     635        content_lower = content.lower() 
     636 
     637        # check SPAM keywords 
     638        spam_keyword = self._check_wordlist( 
     639            content_lower, pref_key = "spam_keywords" 
     640        ) 
     641        if spam_keyword: 
     642            raise RejectSpam( 
     643                "Comment contains SPAM keyword: '%s'" % spam_keyword 
     644            ) 
     645 
     646        # check mod_keywords 
     647        mod_keyword = self._check_wordlist( 
     648            content_lower, pref_key = "mod_keywords" 
     649        ) 
     650        if mod_keyword: 
     651            raise ModerateSubmit( 
     652                "Comment contains mod_keyword: '%s'" % mod_keyword 
     653            ) 
     654 
     655 
     656 
     657    def _save_new_comment(self, blog_entry, clean_data): 
    603658        """ 
    604659        Save a valid submited comment form into the database. 
     
    609664        Send notify emails. 
    610665        """ 
    611         content = form.cleaned_data["content"] 
    612         content = content.strip() 
    613         content_lower = content.lower() 
    614  
    615         if self.request.user.is_staff: 
    616             # Don't check comments from staff users 
     666        content = clean_data["content"] 
     667 
     668        try: 
     669            mail_title = self._check_comment_submit(blog_entry, content) 
     670        except RejectSpam, msg: 
     671            self.page_msg.red("Sorry, your comment identify as spam.") 
     672            self.submit_msg(msg) 
     673            # Display page_msg and handle email notify: 
     674            self._reject_spam_comment(blog_entry, clean_data) 
     675            return False 
     676        except ModerateSubmit, msg: 
     677            self.page_msg(_("Your comment must wait for authorization.")) 
     678            mail_title = _("Blog comment moderation needed.") 
     679            self.submit_msg(msg) 
     680            is_public = False 
     681        else: 
     682            self.submit_msg("Blog comment published.") 
     683            mail_title = _("Blog comment published.") 
    617684            is_public = True 
    618             mail_title = _("new blog comment from page member published.") 
    619         else: 
    620             is_spam = self._check_spam( 
    621                 blog_entry, form.cleaned_data, content_lower 
    622             ) 
    623             if is_spam != False: 
    624                 # Is spam: page_msg and notify was handled by _ckeck_spam() 
    625                 return False 
    626  
    627             should_moderated = self._check_wordlist( 
    628                 content_lower, pref_key = "mod_keywords" 
    629             ) 
    630             if should_moderated: 
    631                 self.page_msg(_("Your comment must wait for authorization.")) 
    632                 mail_title = _("new blog comment waits for moderation.") 
    633                 is_public = False 
    634             else: 
    635                 mail_title = _("blog comment published.") 
    636                 is_public = True 
    637685 
    638686        content = escape_django_tags(content) 
     
    641689            blog_entry = blog_entry, 
    642690            ip_address = self.request.META.get('REMOTE_ADDR'), 
    643             person_name = form.cleaned_data["person_name"], 
    644             email = form.cleaned_data["email"], 
    645             homepage = form.cleaned_data["homepage"], 
     691            person_name = clean_data["person_name"], 
     692            email = clean_data["email"], 
     693            homepage = clean_data["homepage"], 
    646694            content = content, 
    647695            is_public = is_public, 
     
    653701        # Send a notify email 
    654702        self._send_notify( 
    655             mail_title, blog_entry, is_spam=False, comment_entry=new_comment 
    656         ) 
    657  
    658         self.page_msg.green("comment saved.") 
     703            mail_title, blog_entry, comment_entry=new_comment 
     704        ) 
     705 
     706        self.page_msg.green("Your comment saved.") 
    659707        return True 
    660708 
     
    674722    def _check_wordlist(self, content, pref_key): 
    675723        """ 
    676         Simple check, if the content contains one keyword. 
     724        Simple check, if the content contains one of the keywords. 
     725        If a keyword found, returns it else returns None 
    677726        """ 
    678727        keywords = self._get_wordlist(pref_key) 
    679728        for keyword in keywords: 
    680729            if keyword in content: 
    681                 return True 
    682         return False 
    683  
    684     def edit_comment(self, urlargs): 
    685         """ 
    686         Edit a comment (only for admins) 
     730                return keyword 
     731 
     732    def _check_referer(self, blog_entry): 
     733        """ 
     734        Check if the referer is ok. 
     735        raise RejectSpam() or ModerateSubmit() if referer is wrong. 
     736        """ 
     737        check_referer = self.preferences["check_referer"] 
     738        if check_referer == DONT_CHECK: 
     739            # We should not check the referer 
     740            return 
     741 
     742        referer = self.request.META["HTTP_REFERER"] 
     743        should_be = self.URLs.make_absolute_url( 
     744            self.URLs.methodLink("detail", blog_entry.id) 
     745        ) 
     746        self.submit_msg("http referer: '%s' - '%s'" % (referer, should_be)) 
     747 
     748        if referer == should_be: 
     749            # Referer is ok 
     750            return 
     751 
     752        msg = "Wrong http referer" 
     753 
     754        # Something wrong with the referer 
     755        if check_referer == REJECT_SPAM: 
     756            # We should it rejected as spam 
     757            raise RejectSpam(msg) 
     758        elif check_referer == MODERATED: 
     759            # We should moderate the comment 
     760            raise ModerateSubmit(msg) 
     761        else: 
     762            # Should never appear 
     763            raise AttributeError("Wrong check_referer value?!?") 
     764 
     765    def _delete_comment(self, comment_entry): 
     766        """ 
     767        Delete one comment entry. Display page_msg. 
     768        Used in delete_comment() and edit_comment(). 
     769        """ 
     770        old_id = comment_entry.id 
     771        comment_entry.delete() 
     772        self.page_msg.green(_("Comment entry %s deleted." % old_id)) 
     773 
     774    def delete_comment(self, urlargs): 
     775        """ 
     776        Delete a comment (only for admins) 
    687777        """ 
    688778        comment_entry = self._get_entry_from_url(urlargs, model=BlogComment) 
     
    691781            return 
    692782 
    693         CommentForm = forms.form_for_instance(comment_entry) 
    694  
    695783        blog_entry = comment_entry.blog_entry # ForeignKey("BlogEntry") 
    696784 
     785        self._delete_comment(comment_entry) 
     786 
     787        return self._list_entries( 
     788            [blog_entry], context={}, full_comments=True 
     789        ) 
     790 
     791 
     792    def edit_comment(self, urlargs): 
     793        """ 
     794        Edit a comment (only for admins) 
     795        """ 
     796        comment_entry = self._get_entry_from_url(urlargs, model=BlogComment) 
     797        if not comment_entry: 
     798            # Wrong url, page_msg was send to the user 
     799            return 
     800 
     801#        CommentForm = AdminCommentForm 
     802# 
     803# 
     804#        CommentForm = forms.form_for_instance( 
     805#            instance=comment_entry#, form=BlogCommentForm 
     806#        ) 
     807 
     808        blog_entry = comment_entry.blog_entry # ForeignKey("BlogEntry") 
     809 
    697810        if self.request.method == 'POST': 
    698             form = CommentForm(self.request.POST) 
     811#            form = CommentForm(self.request.POST) 
     812            form = AdminCommentForm(self.request.POST, instance=comment_entry) 
    699813            #self.page_msg(self.request.POST) 
    700814            if form.is_valid(): 
    701815                if "delete" in self.request.POST: 
    702                     comment_entry.delete() 
    703                     self.page_msg.green("Comment deleted.") 
     816                    self._delete_comment(comment_entry) 
    704817                else: 
    705818                    form.save() 
    706819                    self.page_msg.green("Saved.") 
    707                 return self._list_entries([blog_entry], context={}, full_comments=True) 
     820                return self._list_entries( 
     821                    [blog_entry], context={}, full_comments=True 
     822                ) 
    708823        else: 
    709             form = CommentForm() 
     824#            form = CommentForm() 
     825            form = AdminCommentForm(instance=comment_entry) 
    710826 
    711827        context = { 
     
    725841 
    726842        comments = BlogComment.objects.filter(is_public=False) 
    727         self._add_comment_edit_url(comments) 
    728         self.page_msg(comments) 
     843        self._add_comment_admin_urls(comments) 
    729844 
    730845        context = { 
     
    733848 
    734849        self._render_template("mod_comments", context)#, debug=2) 
     850 
     851 
     852 
     853class WrongReferer(Exception): 
     854    """ 
     855    A comment submit was made with a wrong http referer information 
     856    """ 
     857    pass 
     858 
     859class RejectSpam(Exception): 
     860    """ 
     861    A submission was identify as SPAM 
     862    """ 
     863    pass 
     864 
     865class ModerateSubmit(Exception): 
     866    """ 
     867    A submitted comment should be moderated 
     868    """ 
     869    pass 
  • trunk/pylucid/PyLucid/plugins_internal/blog/blog_cfg.py

    r1688 r1691  
    1818 
    1919from PyLucid.models.Page import MARKUPS 
     20 
     21DONT_CHECK = 0 
     22REJECT_SPAM = 1 
     23MODERATED = 2 
     24 
     25ACTIONS = ( 
     26    (DONT_CHECK,    _("don't check")), 
     27    (REJECT_SPAM,   _("reject as spam")), 
     28    (MODERATED,     _("hide, for later moderation")), 
     29) 
    2030 
    2131class PreferencesForm(forms.Form): 
     
    93103    ) 
    94104 
     105    check_referer = forms.ChoiceField( 
     106        choices = ACTIONS, 
     107        initial = MODERATED, 
     108        help_text = _( 
     109            "What to do, if http referer contains not your domain?" 
     110        ), 
     111    ) 
    95112 
    96113# Optional, this Plugin can't have multiple preferences 
     
    139156        "must_admin": True, 
    140157    }, 
     158    "delete_comment": { 
     159        "must_login": True, 
     160        "must_admin": True, 
     161    }, 
    141162    "mod_comments": { 
    142163        "must_login": True, 
  • trunk/pylucid/PyLucid/system/page_msg.py

    r1634 r1691  
    22 
    33""" 
    4 A small Wrapper aound djangos user messages system: 
    5 http://www.djangoproject.com/documentation/authentication/#messages 
     4    The PyLucid page message system 
     5    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    66 
    7 enhanced features: 
    8   - easy callable to print a messages 
    9   - simple color output: self.page_msg.green() 
    10   - use pprint for dicts and lists 
    11   - special debug mode: Inserts informationen, where from the message has come. 
     7    A small Wrapper aound djangos user messages system. 
    128 
    13 In PyLucid modules/plugins, you can use the old system, but it use the django 
    14 messages to store the data: 
    15   self.page_msg("I am a new django user message in the old PyLucid style ;)") 
    16   self.page_msg.red("Alert!") 
     9    enhanced features: 
     10      - easy callable to print a messages 
     11      - simple color output: self.page_msg.green() 
     12      - use pprint for dicts and lists 
     13      - special debug mode: 
     14          Inserts informationen, where from the message has come. 
     15 
     16    Can also used in Plugin for storing internal messages. 
     17 
     18    Links 
     19    ~~~~~ 
     20    http://www.pylucid.org/_goto/134/plugin-output/ 
     21    http://www.djangoproject.com/documentation/authentication/#messages 
    1722 
    1823 
    19 Last commit info: 
    20 ---------------------------------- 
    21 $LastChangedDate$ 
    22 $Rev$ 
    23 $Author$ 
     24    Last commit info: 
     25    ~~~~~~~~~~~~~~~~~ 
     26    $LastChangedDate$ 
     27    $Rev$ 
     28    $Author$ 
    2429 
    25 Created by Jens Diemer 
    26  
    27 license: 
    28     GNU General Public License v2 or above 
    29     http://www.opensource.org/licenses/gpl-license.php 
     30    :copyleft: 2008 by the PyLucid team, see AUTHORS for more details. 
     31    :license: GNU GPL v3 or above, see LICENSE for more details. 
    3032""" 
    3133 
     
    4345class PageMessages(object): 
    4446    """ 
    45     http://www.djangoproject.com/documentation/authentication/#messages 
     47    The page message container. 
    4648    """ 
    47     def __init__(self, request): 
    48         try: 
    49             self.messages = request.user.get_and_delete_messages() 
    50         except AttributeError: 
    51             # In the _install section we have no user 
    52             self.messages = [] 
     49    def __init__(self, request, use_django_msg=True, html=True): 
     50        self.html = html # Should we generate colored html output? 
     51 
     52        self.messages = [] 
     53        if use_django_msg: 
     54            try: 
     55                self.messages = request.user.get_and_delete_messages() 
     56            except AttributeError: 
     57                # In the _install section we have no user 
     58                pass 
    5359 
    5460        self.debug_mode = getattr(request, "debug", False) 
     
    8086 
    8187    def append_color_data(self, color, *msg): 
    82         msg = '<span style="color:%s;">%s</span>' % ( 
    83             color, self.prepare(*msg) 
    84         ) 
     88        if self.html: 
     89            msg = '<span style="color:%s;">%s</span>' % ( 
     90                color, self.prepare(*msg) 
     91            ) 
     92        else: 
     93            msg = self.prepare(*msg) 
     94 
    8595        #~ self.request.user.message_set.create(message=msg) 
    8696        msg = mark_safe(msg) # turn djngo auto-escaping off 
     
    130140                for line in item: 
    131141                    line = self.encode_and_prepare(line) 
    132                     result.append("%s<br />\n" % line) 
     142                    if self.html: 
     143                        result.append("%s<br />\n" % line) 
     144                    else: 
     145                        result.append("%s\n" % line) 
    133146            else: 
    134147                item = self.encode_and_prepare(item) 
     
    150163 
    151164        return escape(txt) 
    152  
    153165 
    154166    #________________________________________________________________ 
  • trunk/pylucid/PyLucid/tools/newforms_utils.py

    r1675 r1691  
    8484 
    8585 
     86class StripedCharField(forms.CharField): 
     87    """ 
     88    Same as forms.CharField but stripes the output. 
     89     
     90    >>> f = StripedCharField() 
     91    >>> f.clean('\\n\\n[\\nTEST\\n]\\n\\n') 
     92    u'[\\nTEST\\n]' 
     93    """ 
     94    def clean(self, value): 
     95        value = super(StripedCharField, self).clean(value) 
     96        return value.strip() 
     97 
     98 
    8699if __name__ == "__main__": 
    87100    import doctest