2010-01-15 20:55:30 +00:00
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# ScribeEngine - Open Source Blog Software #
# --------------------------------------------------------------------------- #
# Copyright (c) 2010 Raoul Snyman #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import re
import hashlib
import hmac
import string
from random import choice
from datetime import datetime
2010-01-30 21:39:44 +00:00
from pylons import config , c
2010-01-15 20:55:30 +00:00
from turbomail import Message
2010-01-31 13:45:20 +00:00
from scribeengine . lib . base import render , h
2010-01-15 20:55:30 +00:00
2010-02-18 07:02:46 +00:00
class KeywordProcessor ( object ) :
""" Process user-supplied keywords, tags, or search terms.
This tries to be as flexible as possible while being efficient .
The vast majority of the work is done in the regular expression . """
def __init__ ( self , separators = ' \t ' , quotes = [ ' " ' , " ' " ] , groups = [ ] , group = False , normalize = None , sort = False , result = list ) :
""" Configure the processor.
separators : A list of acceptable separator characters . The first will be used for joins .
quotes : Pass a list or tuple of allowable quotes . E . g . [ " \" " , " ' " ] or None to disable .
groups : Pass a string , list , or tuple of allowable prefixes . E . g . ' +- ' or None to disable .
group : Pass in the type you want to group by , e . g . list , tuple , or dict .
normalize : Pass a function which will normalize the results . E . g . lambda s : s . lower ( ) . strip ( ' \" ' )
sort : Sort the resulting list ( or lists ) alphabeticlly .
result : The return type . One of set , tuple , list .
If groups are defined , and group is not , the result will be a list / tuple / set of tuples , e . g . [ ( ' + ' , " foo " ) , . . . ]
"""
separators = list ( separators )
self . pattern = ' ' . join ( (
( ' [ \ s %s ]* ' % ( ' ' . join ( separators ) , ) ) , # Trap possible leading space or separators.
' ( ' ,
( ' [ %s ] %s ' % ( ' ' . join ( [ i for i in list ( groups ) if i is not None ] ) , ' ? ' if None in groups else ' ' ) ) if groups else ' ' , # Pass groups=('+','-') to handle optional leading + or -.
' ' . join ( [ ( r ' %s [^ %s ]+ %s | ' % ( i , i , i ) ) for i in quotes ] ) if quotes else ' ' , # Match any amount of text (that isn't a quote) inside quotes.
( ' [^ %s ]+ ' % ( ' ' . join ( separators ) , ) ) , # Match any amount of text that isn't whitespace.
' ) ' ,
( ' [ %s ]* ' % ( ' ' . join ( separators ) , ) ) , # Match possible separator character.
) )
self . regex = re . compile ( self . pattern )
self . groups = list ( groups )
self . group = dict if group is True else group
self . normalize = normalize
self . sort = sort
self . result = result
def split ( self , value ) :
if not isinstance ( value , basestring ) : raise TypeError ( " Invalid type for argument ' value ' . " )
matches = self . regex . findall ( value )
if callable ( self . normalize ) : matches = [ self . normalize ( i ) for i in matches ]
if self . sort : matches . sort ( )
if not self . groups : return self . result ( matches )
groups = dict ( [ ( i , list ( ) ) for i in self . groups ] )
if None not in groups . iterkeys ( ) : groups [ None ] = list ( ) # To prevent errors.
for i in matches :
if i [ 0 ] in self . groups :
groups [ i [ 0 ] ] . append ( i [ 1 : ] )
else :
groups [ None ] . append ( i )
if self . group is dict : return groups
if self . group is False or self . group is None :
results = [ ]
for group in self . groups :
results . extend ( [ ( group , match ) for match in groups [ group ] ] )
return self . result ( results )
return self . group ( [ [ match for match in groups [ group ] ] for group in self . groups ] )
2010-01-15 20:55:30 +00:00
def send_mail ( template , mail_to , mail_from , subject , variables = { } , attachments = [ ] ) :
"""
Sends an e - mail using the template ` ` template ` ` .
` ` template ` `
The template to use .
` ` mail_to ` `
One or more addresses to send the e - mail to .
` ` mail_from ` `
The address to send e - mail from .
` ` subject ` `
The subject of the e - mail .
` ` variables ` `
Variables to be used in the template .
` ` attachments ` `
If you want to attach files to the e - mail , use this list .
"""
for name , value in variables . iteritems ( ) :
setattr ( c , name , value )
message = Message ( mail_from , mail_to , subject )
message . plain = render ( template )
message . send ( )
def generate_url ( title ) :
"""
Generate a friendly URL from a blog post title .
` ` title ` `
The title of the blog post .
"""
2010-01-22 21:59:17 +00:00
return re . sub ( r ' [^a-zA-Z0-9]+ ' , u ' - ' , title . lower ( ) ) . strip ( ' - ' )
2010-01-15 20:55:30 +00:00
def hash_password ( password ) :
"""
Return an HMAC SHA256 hash of a password .
` ` password ` `
The password to hash .
"""
return unicode ( hmac . new ( config [ u ' security.salt ' ] , password ,
hashlib . sha256 ) . hexdigest ( ) , ' utf-8 ' )
def generate_key ( length ) :
"""
Generate a random set of letters and numbers of length ` ` length ` ` . Usually
used to generate activation keys .
` ` length ` `
The length of the key .
"""
return ' ' . join ( [ choice ( string . letters + string . digits ) for i in range ( length ) ] )
def month_first_day ( datetime ) :
"""
Returns a modified datetime with the day being midnight of the first day of
the month , given a datetime object .
"""
return datetime . replace ( day = 1 , hour = 0 , minute = 0 , second = 0 , microsecond = 0 )
def month_last_day ( datetime ) :
"""
Returns a modified datetime with the day being the last day of the month ,
given a datetime object .
"""
if datetime . month in [ 1 , 3 , 5 , 7 , 8 , 10 , 12 ] :
day = 31
elif datetime . month in [ 4 , 6 , 9 , 11 ] :
day = 30
else :
if datetime . year % 4 == 0 and datetime . year % 100 != 0 or datetime . year % 400 == 0 :
day = 29
else :
day = 28
return datetime . replace ( day = day , hour = 23 , minute = 59 , second = 59 , microsecond = 99999 )
2010-01-31 13:45:20 +00:00
def paginate ( query , page_size , current_page , base_url = None ) :
query_count = query . count ( )
page_count = query_count / page_size
if query_count % page_size > 0 :
page_count + = 1
offset = ( current_page - 1 ) * page_size
records = query . offset ( offset ) . limit ( page_size ) . all ( )
prev_page = current_page - 1 if current_page - 1 > = 1 else 1
next_page = current_page + 1 if current_page + 1 < = page_count else page_count
if base_url :
return {
u ' page ' : h . url_for ( base_url , page = current_page ) ,
u ' first ' : h . url_for ( base_url , page = 1 ) ,
u ' prev ' : h . url_for ( base_url , page = prev_page ) ,
u ' next ' : h . url_for ( base_url , page = next_page ) ,
u ' last ' : h . url_for ( base_url , page = page_count ) ,
u ' start ' : offset + 1 ,
u ' end ' : offset + len ( records ) ,
u ' total ' : query_count ,
u ' records ' : records
}
else :
return {
u ' page ' : current_page ,
u ' first ' : 1 ,
u ' prev ' : prev_page ,
u ' next ' : next_page ,
u ' last ' : page_count ,
u ' start ' : offset + 1 ,
u ' end ' : offset + len ( records ) ,
u ' total ' : query_count ,
u ' records ' : records
}