2012-06-08 00:08:47 +08:00
from __future__ import unicode_literals
2012-10-01 02:44:27 +08:00
from calendar import timegm
2010-01-28 21:46:18 +08:00
from django . conf import settings
2014-01-26 04:54:25 +08:00
from django . contrib . sites . shortcuts import get_current_site
2010-01-28 21:46:18 +08:00
from django . core . exceptions import ImproperlyConfigured , ObjectDoesNotExist
2006-05-02 09:31:56 +08:00
from django . http import HttpResponse , Http404
2011-02-13 03:12:14 +08:00
from django . template import loader , TemplateDoesNotExist , RequestContext
2013-09-08 16:43:33 +08:00
from django . utils import feedgenerator
2012-07-21 16:00:10 +08:00
from django . utils . encoding import force_text , iri_to_uri , smart_text
2010-01-28 21:46:18 +08:00
from django . utils . html import escape
2012-10-01 02:44:27 +08:00
from django . utils . http import http_date
2013-03-17 17:19:30 +08:00
from django . utils import six
2013-09-08 16:43:33 +08:00
from django . utils . timezone import get_default_timezone , is_naive , make_aware
2010-01-28 21:46:18 +08:00
2012-07-07 17:34:04 +08:00
2010-10-08 22:14:05 +08:00
def add_domain ( domain , url , secure = False ) :
2011-11-19 06:54:24 +08:00
protocol = ' https ' if secure else ' http '
if url . startswith ( ' // ' ) :
# Support network-path reference (see #16753) - RSS requires a protocol
url = ' %s : %s ' % ( protocol , url )
elif not ( url . startswith ( ' http:// ' )
2010-01-28 21:46:18 +08:00
or url . startswith ( ' https:// ' )
or url . startswith ( ' mailto: ' ) ) :
2012-06-08 00:08:47 +08:00
url = iri_to_uri ( ' %s :// %s %s ' % ( protocol , domain , url ) )
2010-01-28 21:46:18 +08:00
return url
2012-10-01 02:44:27 +08:00
2010-01-28 21:46:18 +08:00
class FeedDoesNotExist ( ObjectDoesNotExist ) :
pass
class Feed ( object ) :
feed_type = feedgenerator . DefaultFeed
title_template = None
description_template = None
def __call__ ( self , request , * args , * * kwargs ) :
try :
obj = self . get_object ( request , * args , * * kwargs )
except ObjectDoesNotExist :
raise Http404 ( ' Feed object does not exist. ' )
feedgen = self . get_feed ( obj , request )
2011-12-17 11:53:25 +08:00
response = HttpResponse ( content_type = feedgen . mime_type )
2013-07-18 03:20:20 +08:00
if hasattr ( self , ' item_pubdate ' ) or hasattr ( self , ' item_updateddate ' ) :
# if item_pubdate or item_updateddate is defined for the feed, set
# header so as ConditionalGetMiddleware is able to send 304 NOT MODIFIED
2012-10-01 02:44:27 +08:00
response [ ' Last-Modified ' ] = http_date (
timegm ( feedgen . latest_post_date ( ) . utctimetuple ( ) ) )
2010-01-28 21:46:18 +08:00
feedgen . write ( response , ' utf-8 ' )
return response
def item_title ( self , item ) :
# Titles should be double escaped by default (see #6533)
2012-07-21 16:00:10 +08:00
return escape ( force_text ( item ) )
2010-01-28 21:46:18 +08:00
def item_description ( self , item ) :
2012-07-21 16:00:10 +08:00
return force_text ( item )
2010-01-28 21:46:18 +08:00
def item_link ( self , item ) :
try :
return item . get_absolute_url ( )
except AttributeError :
raise ImproperlyConfigured ( ' Give your %s class a get_absolute_url() method, or define an item_link() method in your Feed class. ' % item . __class__ . __name__ )
def __get_dynamic_attr ( self , attname , obj , default = None ) :
try :
attr = getattr ( self , attname )
except AttributeError :
return default
if callable ( attr ) :
2013-03-17 17:19:30 +08:00
# Check co_argcount rather than try/excepting the function and
# catching the TypeError, because something inside the function
# may raise the TypeError. This technique is more accurate.
try :
code = six . get_function_code ( attr )
except AttributeError :
code = six . get_function_code ( attr . __call__ )
if code . co_argcount == 2 : # one argument is 'self'
2010-01-28 21:46:18 +08:00
return attr ( obj )
else :
return attr ( )
return attr
def feed_extra_kwargs ( self , obj ) :
"""
Returns an extra keyword arguments dictionary that is used when
initializing the feed generator .
"""
return { }
def item_extra_kwargs ( self , item ) :
"""
Returns an extra keyword arguments dictionary that is used with
the ` add_item ` call of the feed generator .
"""
return { }
def get_object ( self , request , * args , * * kwargs ) :
return None
2013-02-24 22:00:34 +08:00
def get_context_data ( self , * * kwargs ) :
"""
Returns a dictionary to use as extra context if either
` ` self . description_template ` ` or ` ` self . item_template ` ` are used .
Default implementation preserves the old behavior
of using { ' obj ' : item , ' site ' : current_site } as the context .
"""
return { ' obj ' : kwargs . get ( ' item ' ) , ' site ' : kwargs . get ( ' site ' ) }
2010-01-28 21:46:18 +08:00
def get_feed ( self , obj , request ) :
"""
Returns a feedgenerator . DefaultFeed object , fully populated , for
this feed . Raises FeedDoesNotExist for invalid parameters .
"""
Fixed #14386, #8960, #10235, #10909, #10608, #13845, #14377 - standardize Site/RequestSite usage in various places.
Many thanks to gabrielhurley for putting most of this together. Also to
bmihelac, arthurk, qingfeng, hvendelbo, petr.pulc@s-cape.cz, Hraban for
reports and some initial patches.
The patch also contains some whitespace/PEP8 fixes.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@13980 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2010-10-04 22:20:47 +08:00
current_site = get_current_site ( request )
2010-01-28 21:46:18 +08:00
link = self . __get_dynamic_attr ( ' link ' , obj )
2010-10-08 22:14:05 +08:00
link = add_domain ( current_site . domain , link , request . is_secure ( ) )
2010-01-28 21:46:18 +08:00
feed = self . feed_type (
2013-11-03 17:22:11 +08:00
title = self . __get_dynamic_attr ( ' title ' , obj ) ,
subtitle = self . __get_dynamic_attr ( ' subtitle ' , obj ) ,
link = link ,
description = self . __get_dynamic_attr ( ' description ' , obj ) ,
language = settings . LANGUAGE_CODE ,
feed_url = add_domain (
2010-10-08 22:14:05 +08:00
current_site . domain ,
self . __get_dynamic_attr ( ' feed_url ' , obj ) or request . path ,
request . is_secure ( ) ,
) ,
2013-11-03 17:22:11 +08:00
author_name = self . __get_dynamic_attr ( ' author_name ' , obj ) ,
author_link = self . __get_dynamic_attr ( ' author_link ' , obj ) ,
author_email = self . __get_dynamic_attr ( ' author_email ' , obj ) ,
categories = self . __get_dynamic_attr ( ' categories ' , obj ) ,
feed_copyright = self . __get_dynamic_attr ( ' feed_copyright ' , obj ) ,
feed_guid = self . __get_dynamic_attr ( ' feed_guid ' , obj ) ,
ttl = self . __get_dynamic_attr ( ' ttl ' , obj ) ,
2010-01-28 21:46:18 +08:00
* * self . feed_extra_kwargs ( obj )
)
title_tmp = None
if self . title_template is not None :
try :
title_tmp = loader . get_template ( self . title_template )
except TemplateDoesNotExist :
pass
description_tmp = None
if self . description_template is not None :
try :
description_tmp = loader . get_template ( self . description_template )
except TemplateDoesNotExist :
pass
for item in self . __get_dynamic_attr ( ' items ' , obj ) :
2013-02-24 22:00:34 +08:00
context = self . get_context_data ( item = item , site = current_site ,
obj = obj , request = request )
2010-01-28 21:46:18 +08:00
if title_tmp is not None :
2013-02-24 22:00:34 +08:00
title = title_tmp . render ( RequestContext ( request , context ) )
2010-01-28 21:46:18 +08:00
else :
title = self . __get_dynamic_attr ( ' item_title ' , item )
if description_tmp is not None :
2013-02-24 22:00:34 +08:00
description = description_tmp . render ( RequestContext ( request , context ) )
2010-01-28 21:46:18 +08:00
else :
description = self . __get_dynamic_attr ( ' item_description ' , item )
2010-10-08 22:14:05 +08:00
link = add_domain (
current_site . domain ,
self . __get_dynamic_attr ( ' item_link ' , item ) ,
request . is_secure ( ) ,
)
2010-01-28 21:46:18 +08:00
enc = None
enc_url = self . __get_dynamic_attr ( ' item_enclosure_url ' , item )
if enc_url :
enc = feedgenerator . Enclosure (
2013-11-03 17:22:11 +08:00
url = smart_text ( enc_url ) ,
length = smart_text ( self . __get_dynamic_attr ( ' item_enclosure_length ' , item ) ) ,
mime_type = smart_text ( self . __get_dynamic_attr ( ' item_enclosure_mime_type ' , item ) )
2010-01-28 21:46:18 +08:00
)
author_name = self . __get_dynamic_attr ( ' item_author_name ' , item )
if author_name is not None :
author_email = self . __get_dynamic_attr ( ' item_author_email ' , item )
author_link = self . __get_dynamic_attr ( ' item_author_link ' , item )
else :
author_email = author_link = None
2013-09-08 16:43:33 +08:00
tz = get_default_timezone ( )
2010-01-28 21:46:18 +08:00
pubdate = self . __get_dynamic_attr ( ' item_pubdate ' , item )
2011-11-18 21:01:06 +08:00
if pubdate and is_naive ( pubdate ) :
2013-09-08 16:43:33 +08:00
pubdate = make_aware ( pubdate , tz )
2010-01-28 21:46:18 +08:00
2013-07-18 03:20:20 +08:00
updateddate = self . __get_dynamic_attr ( ' item_updateddate ' , item )
if updateddate and is_naive ( updateddate ) :
2013-09-08 16:43:33 +08:00
updateddate = make_aware ( updateddate , tz )
2013-07-18 03:20:20 +08:00
2010-01-28 21:46:18 +08:00
feed . add_item (
2013-11-03 17:22:11 +08:00
title = title ,
link = link ,
description = description ,
unique_id = self . __get_dynamic_attr ( ' item_guid ' , item , link ) ,
unique_id_is_permalink = self . __get_dynamic_attr (
2013-02-06 18:25:35 +08:00
' item_guid_is_permalink ' , item ) ,
2013-11-03 17:22:11 +08:00
enclosure = enc ,
pubdate = pubdate ,
updateddate = updateddate ,
author_name = author_name ,
author_email = author_email ,
author_link = author_link ,
categories = self . __get_dynamic_attr ( ' item_categories ' , item ) ,
item_copyright = self . __get_dynamic_attr ( ' item_copyright ' , item ) ,
2010-01-28 21:46:18 +08:00
* * self . item_extra_kwargs ( item )
)
return feed