Never been to TextSnippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world (or not, you can keep them private!)

About this user

James Bennett

« Newer Snippets
Older Snippets »
16 total  XML / RSS feed 

Implement automatic comment moderation queue in Django

Assuming you're using latest trunk (i.e., post-magic-removal merge), you need a few things:

1. A method on your content object which returns whether a comment should go to moderation or not.
2. A method on the FreeComment model which checks this and sets the 'approved' field appropriately.
3. A list filter for the FreeComment admin to show comments in need of moderation.

For the first bit, we borrow from my method for automatically closing comments (http://textsnippets.com/posts/show/266):

def auto_moderate(self):
    return datetime.datetime.today() - datetime.timedelta(30) >= self.pub_date


This returns False if the object is less than 30 days old, True if it's 30 days old or older.

For the second bit, you'll need to hack on the FreeComment model; it's located in django/contrib/comments/models.py. Add this:

def save(self):
    if self.get_content_object().auto_moderate:
        self.approved = False
    super(FreeComment, self).save()


And for the third bit, just add 'approved' to the list of properties the admin can filter on, and you'll be able to click and get a list of all comments which have not yet been approved.

In your templates, you can then manually check whether each comment is approved or not with {% if comment.approved %} or, if you're feeling adventurous, you can add a custom manager to the FreeComment model which only returns comments which have been approved.

Test whether an SGML attribute value requires quoting

SGML attribute values don't always need to be quoted; here's a quick bit of Python which returns whether an attribute value needs to be quoted or not:

import re
if re.compile('^[a-zA-Z0-9_\-\.:]+$').search(value):
   ...does not need quoting..
else:
   ...does need quoting...

For pkit

entries.get_list(pub_date__year=2005, pub_date__month=11)

lighttpd.conf update for use with Django

If you grabbed my sample lighttpd.conf before I made this post in the forum, then open up your lighttpd.conf, scroll down to the url.rewrite section, and change this:

"^(/[^media]/.*)$" => "/main.fcgi$1"


To this:

"^(/[^media].*)$" => "/main.fcgi$1"

Close comments after a set time in Django apps

If you're using Django's bundled comments application, you might want to have comments for objects closed after a set period of time; to do this, just add a method to the model of the object which will be getting the comments (e.g., the 'Entry' class if you have a weblog), like so (this assumes a DateTimeField called 'pub_date' which represents the object's date of publication):

def allow_comments(self):
    return datetime.datetime.today() - datetime.timedelta(30) >= self.pub_date


Change the timedelta value to however many days you'd like to leave comments open after publication, and now you'll be able to selectively display the comment form only when comments are open, by adding this to your template (assuming the object is being referenced by the name 'entry'):

{% if entry.allow_comments %}
... display the comment form here ...
{% endif %}

www to no-www, and vice-versa

To redirect requests for www.example.com to example.com (without the www) put this in your .htaccess:

RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]


And to do the reverse (redirect non-www to www), try this:

RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ http://www.example.com/$1 [R=301,L]

Implement Error 447

There's a humorous pseudo-RFC which establishes HTTP Error 447 as "dropped in Pacific Ocean". Here's some PHP to implement it, if you'd like. Sends the right header, and even looks like a standard Apache 2 error page.

<?php

header("HTTP/1.1 447 Dropped by Accident in the Pacific Ocean");
print("\"1.0\" encoding=\"ISO-8859-1\"?>");
?>DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Dropped in Ocean!title>
/html; charset=iso-8859-1" />
"made" href="mailto:webmaster@example.com" />




Dropped in Ocean!

The requested item was accidentally dropped in the Pacific Ocean while being transmitted to your computer, and cannot be displayed.
If you think this is a server error, please contact the "mailto:webmaster@example.com">webmaster.

Error 447

"/">example.com
"D d M Y h:i:s A T"); print("$date"); ?>
Apache/2.0.40 (Red Hat Linux)

Easy links to/from glossary terms with JavaScript

Quick unobtrusive JavaScript that provides links to and from terms defined in a glossary in your page. Assumes you mark up terms to define with dfn, and put the glossary in a definition list. Currently needs terms in the definition list to appear in the same order as they do in the main text.

Also somewhat old and probably not nearly as efficient as it could be, so ideas for improvement would be welcome.

function createLinkedFootnotes() {
     defs = document.getElementsByTagName('dfn');
     for(i = 0; i < defs.length; i++) {
          num = i + 1;
          footnote = document.createElement('sup');
          linkToDef = document.createElement('a');
          anchor = '#def' + num;
          termID = 'term' + num;
          linkToDef.setAttribute('href', anchor);
          linkToDef.setAttribute('id', termID);
          linkToDef.appendChild(document.createTextNode(num));
          footnote.appendChild(linkToDef);
          defs[i].appendChild(footnote);
     }
     terms = document.getElementsByTagName('dt');
     for(i = 0; i < terms.length; i++) {
          num = i + 1;
          footnote = document.createElement('sup');
          linkBack = document.createElement('a');
          anchor = '#term' + num;
          defID = 'def' + num;
          linkBack.setAttribute('href', anchor);
          linkBack.setAttribute('id', defID);
          linkBack.appendChild(document.createTextNode(num));
          footnote.appendChild(linkBack);
          terms[i].insertBefore(footnote, terms[i].firstChild);
     }
}

Extensible search highlighting in PHP

Based on Dean's original Google Hilite, but refactored a bit to make it easy to add support for more search engines (currently supports some 20-odd major searches).

<?php

function search_highlight($text)  {
  $referer = $_SERVER['HTTP_REFERER'];

  //Did they get here from a search?
  if((preg_match('/www\.google.*/i',$referer) && !preg_match('/^http:\/\/www\.google\.com\//i', $referer))
     || preg_match('/search\.atomz.*/i',$referer)
     || preg_match('/search\.msn.*/i',$referer)
     || preg_match('/search\.yahoo.*/i',$referer)
     || preg_match('/msxml\.excite\.com/i', $referer)
     || preg_match('/search\.lycos\.com/i', $referer)
     || preg_match('/www\.alltheweb\.com/i', $referer)
     || preg_match('/search\.aol\.com/i', $referer)
     || preg_match('/search\.iwon\.com/i', $referer)
     || preg_match('/ask\.com/i', $referer)
     || preg_match('/search\.cometsystems\.com/i', $referer)
     || preg_match('/www\.hotbot\.com/i', $referer)
     || preg_match('/www\.overture\.com/i', $referer)
     || preg_match('/www\.metacrawler\.com/i', $referer)
     || preg_match('/search\.netscape\.com/i', $referer)
     || preg_match('/www\.looksmart\.com/i', $referer)
     || preg_match('/go\.google\.com/i', $referer)
     || preg_match('/dpxml\.webcrawler\.com/i', $referer)
     || preg_match('/search\.earthlink\.net/i', $referer)
     || preg_match('/search\.viewpoint\.com/i', $referer)
     || preg_match('/www\.mamma\.com/i', $referer)
     || preg_match('/home\.bellsouth\.net\/s\/s\.dll/i', $referer)
     || preg_match('/www\.ask\.co\.uk/i', $referer)) {

    //Figure out which search and get the part of its URL which contains the search terms.
    if(preg_match('/(www\.google.*)|(search\.msn.*)|(www\.alltheweb\.com)|(ask\.com)|(go\.google\.com)|(search\.earthlink\.net)/i',$referer))
      $delimiter = "q";
    elseif(preg_match('/www\.ask\.co\.uk/i', $referer))
      $delimiter = "ask";
    elseif(preg_match('/search\.atomz.*/i',$referer))
      $delimiter = "sp-q";
    elseif(preg_match('/search\.yahoo.*/i',$referer))
      $delimiter = "p";
    elseif(preg_match('/(msxml\.excite\.com)|(www\.metacrawler\.com)|(dpxml\.webcrawler\.com)/i', $referer))
      $delimiter = "qkw";
    elseif(preg_match('/(search\.lycos\.com)|(search\.aol\.com)|(www\.hotbot\.com)|(search\.netscape\.com)|(search\.mamma\.com)/i', $referer))
      $delimiter = "query";
    elseif(preg_match('/search\.iwon\.com/i', $referer))
      $delimiter = "searchfor";
    elseif(preg_match('/search\.cometsystems\.com/i', $referer))
      $delimiter = "qry";
    elseif(preg_match('/www\.overture\.com/i', $referer))
      $delimiter = "Keywords";
    elseif(preg_match('/www\.looksmart\.com/i', $referer))
      $delimiter = "key";
    elseif(preg_match('/search\.viewpoint\.com/i', $referer))
      $delimiter = "k";
    elseif(preg_match('/home\.bellsouth\.net\/s\/s\.dll/i', $referer))
      $delimiter = "string";

    $pattern = "/^.*" . $delimiter . "=([^&]+)&?.*\$/i";
    $query = preg_replace($pattern, '$1', $referer);

    //Remove quotation marks.
    $query = preg_replace('/\'|"/','',$query);

    //List of words to exclude from matching.
    $excludes = array('a', 'an', 'the', 'is', 'in', 'are', 'was', 'and', 'by', 'for', 'from', 'of', 'on', 'with', 'this', 'that', 'shtuff', 'or', ' ', '');
    $query_array = preg_split ("/[\s,\+\.]+/",$query);
    //Iterate over search terms and do the highlighting.
    foreach($query_array as $term) {
      //Don't match the excluded terms.
      $term = strtolower($term);
      if(in_array($term, $excludes)) {
        continue;
      }
      if(preg_match('/(?<=>)([^<]+)?(\b'.$term.'\b)/i', $text)) {
        $matched = "Spoon!";
      } else {
        $mismatched = "Whoops";
      }
      if (!preg_match('/<.+>/',$text)) {
        $text = preg_replace('/(\b'.$term.'\b)/i','<span class="searchterm">$1span>',$text);  
      } else {
        $text = preg_replace('/(?<=>)([^<]+)?(\b'.$term.'\b)/i','$1<span class="searchterm">$2span>',$text);
      }
    }
    $query_terms = implode(" ", $query_array);
    $query_terms = htmlspecialchars(urldecode($query_terms));
    //If all terms matched, just tell them you did the highlighting.
    if($matched) {
      //Change this message if you like.
      $message = "

It seems you arrived at this page from a search engine. To help you find " . "what you were looking for, your search terms (\"$query_terms\") should " . "be highlighted with yellow backgrounds, like \"searchterm\">this.

"; $text = $message . $text; } elseif($mismatched) { //If only some or no terms matched, offer to repeat the search locally. $query = implode("+", $query_array); //Also change this message if you like. $message = "

It seems you arrived at this page from a search engine, but that some " . "or all of the terms you searched for (\"$query_terms\") aren’t in this page. Would you like to " . "\"http://search.atomz.com/search/?sp-q=" //Insert a proper URL for your site's search function here, up to BUT NOT INCLUDING the part where the search terms go. . $query //Begin the next line with any parts of the search URL which have to go AFTER the search terms. . "&sp-a=sp10028bf7&sp-p=all&sp-f=iso-8859-1" . "\">try your search again using this site’s built-in search? It might be more accurate.

"; if($matched) { $message .= "

Any of your search terms which do appear in this page " . "should be highlighted with yellow backgrounds, like \"searchterm\">this.

"; } $text = $message . $text; } } return $text; } ?>

Quickly put a bunch of emails into one file

A little bit of Python I used to run through a couple hundred emails in an Evolution folder and put their contents (minus headers) into one file for analysis.

import os, re
output_file = open('/path/to/output/file', 'a')
email_pat = re.compile('^\d+\.$')
[output_file.write(open(email).read().split('From: ')[1]) for email in os.listdir(os.getcwd()) if email_pat.match(email)]

For Ray

In response to his question here, assuming your content is in a string called $content:

$num_paras = substr_count($content, '

'); switch ($num_paras) { case 1: $image = 'one.jpg'; break; case 2: $image = 'two.jpg'; break; case 3: $image = 'three.jpg'; break; default: $image = ''; break; }

mod_rewrite rules to serve application/xhtml+xml

The following mod_rewrite rules will serve HTML files as application/xhtml+xml to supporting browsers.

RewriteEngine on
RewriteBase /
RewriteCond %{HTTP_ACCEPT} application/xhtml\+xml
RewriteCond %{HTTP_ACCEPT} !application/xhtml\+xml\s*;\s*q=0
RewriteCond %{REQUEST_URI} \.html$
RewriteCond %{THE_REQUEST} HTTP/1\.1
RewriteRule .* - [T=application/xhtml+xml]


Source: Mark Pilgrim, The Road to XHTML 2.0: MIME Types.

Textpattern form for Paypal shopping-cart integration

A site I've been working on has a retail area which uses Paypal's shopping cart; since the whole site is Textpattern powered, the necessary HTML for each item is generated by a form. This code is based on 1.0rc1 and using the rei_show_custom plugin. I know that in rc3 there's probably something built in to pull out arbitrary custom fields, so just substitute that.

To use, add each product as an article; in the first custom field put the name you want passed to the cart, and in the second custom field put the price of the item. Then make sure this is called by the article form:

<fieldset>
<legend>Buy this product:legend>

Our fine /> is available for the low, low price of $<txp:rei_show_custom customid="2" />.p>

/> <input type="hidden" name="business" value="yourbiz@yourdomain" /> <input type="hidden" name="item_name" value="" /> <input type="hidden" name="amount" value=""2" />" /> <input type="image" id="add_to_cart" src="/path/to/your_cart_image" name="submit" alt="Add this item to your cart" /> <input type="hidden" name="add" value="1" /> p> </form> <form action="https://www.paypal.com/cgi-bin/webscr" method="post"> <p> <input type="hidden" name="cmd" value="_cart" /> <input type="hidden" name="business" value="yourbiz@yourdomain" /> <input type="image" id="view_cart" src="/path/to/your_view_cart_image.png" name="submit" alt="View all items currently in your cart" /> <input type="hidden" name="display" value="1" /> p> </form> fieldset>



That'll display both an "add to cart" and a "view cart" button.

You'll want to fill in your own Paypal account address and product options, of course.

Improved admin_tools for Scoop

If you've ever built a site with Scoop, you know that all that messy hard-coded table-based HTML can be a pain. In the course of building several Scoop sites I've been reworking some of the common boxes to use cleaner, easily-styled, semantic markup, and this seems as good a place as any to post the code.

Here's a reworked admin_tools which outputs as an unordered list (and puts the "edit user" form in a definition list):

my $content;
my @tools = sort { $a->{pos} <=> $b->{pos} } values %{ $S->{ADMIN_TOOLS} };
foreach my $t (@tools) {
        if ( $S->have_perm($t->{perm}) ) {
                $content .= qq\|
        <LI><A href="|rootdir|/admin/$t->{tool}">$t->{menuname}A></LI>\|;
        }
}
if ($S->have_perm('edit_user')) {
 $content .= qq{<LI>
 <FORM NAME="uedit" METHOD="GET" ACTION="|rootdir|/">
 <DL>
 <DT>Edit User:DT>
 
</DD> DL> </FORM> LI>}; } return '' unless $content; return{content=>qq{
    $content</UL>}};

Improved user_box for Scoop

If you've ever built a site with Scoop, you know that all that messy hard-coded table-based HTML can be a pain. In the course of building several Scoop sites I've been reworking some of the common boxes to use cleaner, easily-styled, semantic markup, and this seems as good a place as any to post the code.

Here's a reworked user_box which outputs the tools menu as an unordered list:

my $content;
if ($S->{UID} > 0) {
  if ($S->have_perm('moderate')) {
    my ($nstories, $tstories) = $S->_count_new_sub();
    my $color = '';
    my $c_end = '';
    if ($nstories) {
       $color = '';
       $c_end = '';
    }
    $content = qq\|<ul>
    <li><a href="|rootdir|/modsub">Vote on Submissionsa> ($tstories/$color$nstories$c_end new)li>\|;
  }

  if ($S->{TRUSTLEV} == 2 \|\| $S->have_perm('super_mojo')) {
    $content .= qq{
  • /search?type=comment;hidden_comments=show">Review Hidden Comments
  • }; } my $urlnick = $S->urlify($S->{NICK}); my $diary_link = $S->{UI}->{VARS}->{use_diaries} ? qq{
  • "|rootdir|/user/$urlnick/diary">Your Diary
  • "|rootdir|/submitstory/Diary">New Diary Entry
  • } : ''; my $upload_link = ($S->{UI}->{VARS}->{allow_uploads} && ($S->have_perm('upload_user') \|\| $S->have_perm('upload_admin'))) ? qq{
  • "|rootdir|/user/$urlnick/files">Your Files
  • } : ''; my $ad_link = $S->{UI}->{VARS}->{use_ads} ? qq{
  • "light" href="|rootdir|/user/$urlnick/ads">Your Advertisements
  • "|rootdir|/submitad">Submit Ad</a>li> } : ''; $content .= qq\|
  • /user/$urlnick">User Info
  • "|rootdir|/user/$urlnick/comments">Your Comments
  • "|rootdir|/user/$urlnick/stories">Your Stories
  • $diary_link $ad_link $upload_link
  • "|rootdir|/user/$urlnick/prefs">User Preferences
  • "|rootdir|/my/prefs/Interface">Display Preferences
  • "|rootdir|/my/prefs/Comments">Comment Preferences
  • "|rootdir|/logout">Logout $S->{NICK}</a>li></ul>\|; $title = $S->{NICK}; } else { $content = $S->{UI}->{BLOCKS}->{login_box}; $content =~ s/|LOGIN_ERROR|/$S->{LOGIN_ERROR}/; } return {content => $content, title => $title };
  • Feed different CSS rules to IE5.0, 5.5 and 6.0

    Sometimes you just have to resort to the hacks; this bit of code will let you feed three separate values for the same property to each of the three common flavors of IE on Windows:

    * html whatevertherestofyourselectorwouldbe {
    property/**/: value;
    property: /**/ value;
    p\roperty: value;
    }


    The first value will be applied by IE5.5, the second by IE5.0 and the third by IE6.0. The order is important; mix them up and it won't work. The "* html" on the selector ensures that only IE on Windows will see any of the values (since it mistakenly thinks there's a higher root element above html).

    This was constructed from the table of CSS hacks at dithered.
    « Newer Snippets
    Older Snippets »
    16 total  XML / RSS feed