DJAGEN project migrates to LKD's svn.
126
DJAGEN/branches/mustafa_branch/00_default_vhost.conf
Executable file
|
@ -0,0 +1,126 @@
|
||||||
|
### Section 3: Virtual Hosts
|
||||||
|
#
|
||||||
|
# VirtualHost: If you want to maintain multiple domains/hostnames on your
|
||||||
|
# machine you can setup VirtualHost containers for them. Most configurations
|
||||||
|
# use only name-based virtual hosts so the server doesn't need to worry about
|
||||||
|
# IP addresses. This is indicated by the asterisks in the directives below.
|
||||||
|
#
|
||||||
|
# Please see the documentation at
|
||||||
|
# <URL:http://httpd.apache.org/docs-2.0/vhosts/>
|
||||||
|
# for further details before you try to setup virtual hosts.
|
||||||
|
#
|
||||||
|
# You may use the command line option '-S' to verify your virtual host
|
||||||
|
# configuration.
|
||||||
|
Listen 80
|
||||||
|
#
|
||||||
|
# Use name-based virtual hosting.
|
||||||
|
#
|
||||||
|
NameVirtualHost *:80
|
||||||
|
|
||||||
|
#
|
||||||
|
# VirtualHost example:
|
||||||
|
# Almost any Apache directive may go into a VirtualHost container.
|
||||||
|
# The first VirtualHost section is used for requests without a known
|
||||||
|
# server name.
|
||||||
|
#
|
||||||
|
#<VirtualHost *:80>
|
||||||
|
# ServerAdmin webmaster@dummy-host.example.com
|
||||||
|
# DocumentRoot /www/docs/dummy-host.example.com
|
||||||
|
# ServerName dummy-host.example.com
|
||||||
|
# ErrorLog @rel_logfiledir@/dummy-host.example.com-error_log
|
||||||
|
# CustomLog @rel_logfiledir@/dummy-host.example.com-access_log common
|
||||||
|
#</VirtualHost>
|
||||||
|
|
||||||
|
#
|
||||||
|
# The First Virtual Host is also your DEFAULT Virtual Host.
|
||||||
|
# This means any requests that do not match any other vhosts will
|
||||||
|
# goto this virtual host.
|
||||||
|
#
|
||||||
|
|
||||||
|
<IfDefine DEFAULT_VHOST>
|
||||||
|
<VirtualHost *:80>
|
||||||
|
#
|
||||||
|
# DocumentRoot: The directory out of which you will serve your
|
||||||
|
# documents. By default, all requests are taken from this directory, but
|
||||||
|
# symbolic links and aliases may be used to point to other locations.
|
||||||
|
#
|
||||||
|
DocumentRoot "/var/www/localhost/htdocs"
|
||||||
|
|
||||||
|
#
|
||||||
|
# This should be changed to whatever you set DocumentRoot to.
|
||||||
|
#
|
||||||
|
<Directory "/var/www/localhost/htdocs">
|
||||||
|
|
||||||
|
#
|
||||||
|
# Possible values for the Options directive are "None", "All",
|
||||||
|
# or any combination of:
|
||||||
|
# Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
|
||||||
|
#
|
||||||
|
# Note that "MultiViews" must be named *explicitly* --- "Options All"
|
||||||
|
# doesn't give it to you.
|
||||||
|
#
|
||||||
|
# The Options directive is both complicated and important. Please see
|
||||||
|
# http://httpd.apache.org/docs-2.0/mod/core.html#options
|
||||||
|
# for more information.
|
||||||
|
#
|
||||||
|
Options Indexes FollowSymLinks
|
||||||
|
|
||||||
|
#
|
||||||
|
# AllowOverride controls what directives may be placed in .htaccess files.
|
||||||
|
# It can be "All", "None", or any combination of the keywords:
|
||||||
|
# Options FileInfo AuthConfig Limit
|
||||||
|
#
|
||||||
|
AllowOverride None
|
||||||
|
|
||||||
|
#
|
||||||
|
# Controls who can get stuff from this server.
|
||||||
|
#
|
||||||
|
Order allow,deny
|
||||||
|
Allow from all
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<IfModule peruser.c>
|
||||||
|
# this must match a Processor
|
||||||
|
ServerEnvironment apache apache
|
||||||
|
|
||||||
|
# these are optional - defaults to the values specified in httpd.conf
|
||||||
|
MinSpareProcessors 4
|
||||||
|
MaxProcessors 20
|
||||||
|
</IfModule>
|
||||||
|
</VirtualHost>
|
||||||
|
</IfDefine>
|
||||||
|
|
||||||
|
<VirtualHost 127.0.0.2:80>
|
||||||
|
ServerName /
|
||||||
|
ServerAlias */
|
||||||
|
Alias /phpmyadmin/ /var/www/localhost/htdocs/phpmyadmin/
|
||||||
|
<Directory /var/www/localhost/htdocs/phpmyadmin/>
|
||||||
|
order deny,allow
|
||||||
|
Allow from all
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
Alias /djagenmedia/ /var/www/localhost/htdocs/djagen/
|
||||||
|
<Directory /var/www/localhost/htdocs/djagen/>
|
||||||
|
order deny,allow
|
||||||
|
Allow from all
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
Alias /admin_media/ /usr/lib/python2.5/site-packages/django/contrib/admin/media
|
||||||
|
<Directory /usr/lib/python2.5/site-packages/django/contrib/admin/media>
|
||||||
|
order deny,allow
|
||||||
|
Allow from all
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
WSGIScriptAlias / /home/cad/Workspace/djagen_ws/gezegen/branches/mustafa_branch/djagen/wsgi_handler.py
|
||||||
|
WSGIDaemonProcess djagen user=cad group=root processes=1 threads=10
|
||||||
|
WSGIProcessGroup djagen
|
||||||
|
|
||||||
|
|
||||||
|
<Directory /home/cad/Workspace/djagen_ws/gezegen/branches/mustafa_branch/djagen>
|
||||||
|
Order deny,allow
|
||||||
|
Allow from all
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
|
||||||
|
</VirtualHost>
|
||||||
|
|
0
DJAGEN/branches/mustafa_branch/djagen/__init__.py
Executable file
12
DJAGEN/branches/mustafa_branch/djagen/captcha/__init__.py
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
VERSION = (0, 1, 7)
|
||||||
|
|
||||||
|
def get_version(svn=False):
|
||||||
|
"Returns the version as a human-format string."
|
||||||
|
v = '.'.join([str(i) for i in VERSION])
|
||||||
|
if svn:
|
||||||
|
from django.utils.version import get_svn_revision
|
||||||
|
import os
|
||||||
|
svn_rev = get_svn_revision(os.path.dirname(__file__))
|
||||||
|
if svn_rev:
|
||||||
|
v = '%s-%s' % (v, svn_rev)
|
||||||
|
return v
|
0
DJAGEN/branches/mustafa_branch/djagen/captcha/conf/__init__.py
Executable file
49
DJAGEN/branches/mustafa_branch/djagen/captcha/conf/settings.py
Executable file
|
@ -0,0 +1,49 @@
|
||||||
|
import os
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
CAPTCHA_FONT_PATH = getattr(settings,'CAPTCHA_FONT_PATH', os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'fonts/Vera.ttf')))
|
||||||
|
CAPTCHA_FONT_SIZE = getattr(settings,'CAPTCHA_FONT_SIZE', 22)
|
||||||
|
CAPTCHA_LETTER_ROTATION = getattr(settings, 'CAPTCHA_LETTER_ROTATION', (-35,35))
|
||||||
|
CAPTCHA_BACKGROUND_COLOR = getattr(settings,'CAPTCHA_BACKGROUND_COLOR', '#ffffff')
|
||||||
|
CAPTCHA_FOREGROUND_COLOR= getattr(settings,'CAPTCHA_FOREGROUND_COLOR', '#001100')
|
||||||
|
CAPTCHA_CHALLENGE_FUNCT = getattr(settings,'CAPTCHA_CHALLENGE_FUNCT','captcha.helpers.random_char_challenge')
|
||||||
|
CAPTCHA_NOISE_FUNCTIONS = getattr(settings,'CAPTCHA_NOISE_FUNCTIONS', ('captcha.helpers.noise_arcs','captcha.helpers.noise_dots',))
|
||||||
|
CAPTCHA_FILTER_FUNCTIONS = getattr(settings,'CAPTCHA_FILTER_FUNCTIONS',('captcha.helpers.post_smooth',))
|
||||||
|
CAPTCHA_WORDS_DICTIONARY = getattr(settings,'CAPTCHA_WORDS_DICTIONARY', '/usr/share/dict/words')
|
||||||
|
CAPTCHA_FLITE_PATH = getattr(settings,'CAPTCHA_FLITE_PATH',None)
|
||||||
|
CAPTCHA_TIMEOUT = getattr(settings, 'CAPTCHA_TIMEOUT', 5) # Minutes
|
||||||
|
CAPTCHA_LENGTH = int(getattr(settings, 'CAPTCHA_LENGTH', 4)) # Chars
|
||||||
|
CAPTCHA_IMAGE_BEFORE_FIELD = getattr(settings,'CAPTCHA_IMAGE_BEFORE_FIELD', True)
|
||||||
|
CAPTCHA_DICTIONARY_MIN_LENGTH = getattr(settings,'CAPTCHA_DICTIONARY_MIN_LENGTH', 0)
|
||||||
|
CAPTCHA_DICTIONARY_MAX_LENGTH = getattr(settings,'CAPTCHA_DICTIONARY_MAX_LENGTH', 99)
|
||||||
|
if CAPTCHA_IMAGE_BEFORE_FIELD:
|
||||||
|
CAPTCHA_OUTPUT_FORMAT = getattr(settings,'CAPTCHA_OUTPUT_FORMAT', u'%(image)s %(hidden_field)s %(text_field)s')
|
||||||
|
else:
|
||||||
|
CAPTCHA_OUTPUT_FORMAT = getattr(settings,'CAPTCHA_OUTPUT_FORMAT', u'%(hidden_field)s %(text_field)s %(image)s')
|
||||||
|
|
||||||
|
|
||||||
|
# Failsafe
|
||||||
|
if CAPTCHA_DICTIONARY_MIN_LENGTH > CAPTCHA_DICTIONARY_MAX_LENGTH:
|
||||||
|
CAPTCHA_DICTIONARY_MIN_LENGTH, CAPTCHA_DICTIONARY_MAX_LENGTH = CAPTCHA_DICTIONARY_MAX_LENGTH, CAPTCHA_DICTIONARY_MIN_LENGTH
|
||||||
|
|
||||||
|
|
||||||
|
def _callable_from_string(string_or_callable):
|
||||||
|
if callable(string_or_callable):
|
||||||
|
return string_or_callable
|
||||||
|
else:
|
||||||
|
return getattr(__import__( '.'.join(string_or_callable.split('.')[:-1]), {}, {}, ['']), string_or_callable.split('.')[-1])
|
||||||
|
|
||||||
|
def get_challenge():
|
||||||
|
return _callable_from_string(CAPTCHA_CHALLENGE_FUNCT)
|
||||||
|
|
||||||
|
|
||||||
|
def noise_functions():
|
||||||
|
if CAPTCHA_NOISE_FUNCTIONS:
|
||||||
|
return map(_callable_from_string, CAPTCHA_NOISE_FUNCTIONS)
|
||||||
|
return list()
|
||||||
|
|
||||||
|
def filter_functions():
|
||||||
|
if CAPTCHA_FILTER_FUNCTIONS:
|
||||||
|
return map(_callable_from_string, CAPTCHA_FILTER_FUNCTIONS)
|
||||||
|
return list()
|
||||||
|
|
81
DJAGEN/branches/mustafa_branch/djagen/captcha/fields.py
Executable file
|
@ -0,0 +1,81 @@
|
||||||
|
from django.forms.fields import CharField, MultiValueField
|
||||||
|
from django.forms import ValidationError
|
||||||
|
from django.forms.widgets import TextInput, MultiWidget, HiddenInput
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from captcha.models import CaptchaStore
|
||||||
|
from captcha.conf import settings
|
||||||
|
from captcha.helpers import *
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
class CaptchaTextInput(MultiWidget):
|
||||||
|
def __init__(self,attrs=None):
|
||||||
|
widgets = (
|
||||||
|
HiddenInput(attrs),
|
||||||
|
TextInput(attrs),
|
||||||
|
)
|
||||||
|
|
||||||
|
for key in ('image','hidden_field','text_field'):
|
||||||
|
if '%%(%s)s'%key not in settings.CAPTCHA_OUTPUT_FORMAT:
|
||||||
|
raise KeyError('All of %s must be present in your CAPTCHA_OUTPUT_FORMAT setting. Could not find %s' %(
|
||||||
|
', '.join(['%%(%s)s'%k for k in ('image','hidden_field','text_field')]),
|
||||||
|
'%%(%s)s'%key
|
||||||
|
))
|
||||||
|
|
||||||
|
super(CaptchaTextInput,self).__init__(widgets,attrs)
|
||||||
|
|
||||||
|
def decompress(self,value):
|
||||||
|
if value:
|
||||||
|
return value.split(',')
|
||||||
|
return [None,None]
|
||||||
|
|
||||||
|
def format_output(self, rendered_widgets):
|
||||||
|
hidden_field, text_field = rendered_widgets
|
||||||
|
return settings.CAPTCHA_OUTPUT_FORMAT %dict(image=self.image_and_audio, hidden_field=hidden_field, text_field=text_field)
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None):
|
||||||
|
challenge,response= settings.get_challenge()()
|
||||||
|
|
||||||
|
store = CaptchaStore.objects.create(challenge=challenge,response=response)
|
||||||
|
key = store.hashkey
|
||||||
|
value = [key, u'']
|
||||||
|
|
||||||
|
self.image_and_audio = '<img src="%s" alt="captcha" class="captcha" />' %reverse('captcha-image',kwargs=dict(key=key))
|
||||||
|
if settings.CAPTCHA_FLITE_PATH:
|
||||||
|
self.image_and_audio = '<a href="%s" title="%s">%s</a>' %( reverse('captcha-audio', kwargs=dict(key=key)), unicode(_('Play captcha as audio file')), self.image_and_audio)
|
||||||
|
#fields = super(CaptchaTextInput, self).render(name, value, attrs=attrs)
|
||||||
|
|
||||||
|
return super(CaptchaTextInput, self).render(name, value, attrs=attrs)
|
||||||
|
|
||||||
|
class CaptchaField(MultiValueField):
|
||||||
|
widget=CaptchaTextInput
|
||||||
|
|
||||||
|
def __init__(self, *args,**kwargs):
|
||||||
|
fields = (
|
||||||
|
CharField(show_hidden_initial=True),
|
||||||
|
CharField(),
|
||||||
|
)
|
||||||
|
if 'error_messages' not in kwargs or 'invalid' not in kwargs.get('error_messages'):
|
||||||
|
if 'error_messages' not in kwargs:
|
||||||
|
kwargs['error_messages'] = dict()
|
||||||
|
kwargs['error_messages'].update(dict(invalid=_('Invalid CAPTCHA')))
|
||||||
|
|
||||||
|
|
||||||
|
super(CaptchaField,self).__init__(fields=fields, *args, **kwargs)
|
||||||
|
|
||||||
|
def compress(self,data_list):
|
||||||
|
if data_list:
|
||||||
|
return ','.join(data_list)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
super(CaptchaField, self).clean(value)
|
||||||
|
response, value[1] = value[1].strip().lower(), ''
|
||||||
|
CaptchaStore.remove_expired()
|
||||||
|
try:
|
||||||
|
store = CaptchaStore.objects.get(response=response, hashkey=value[0], expiration__gt=datetime.datetime.now())
|
||||||
|
store.delete()
|
||||||
|
except Exception:
|
||||||
|
raise ValidationError(getattr(self,'error_messages',dict()).get('invalid', _('Invalid CAPTCHA')))
|
||||||
|
return value
|
124
DJAGEN/branches/mustafa_branch/djagen/captcha/fonts/COPYRIGHT.TXT
Executable file
|
@ -0,0 +1,124 @@
|
||||||
|
Bitstream Vera Fonts Copyright
|
||||||
|
|
||||||
|
The fonts have a generous copyright, allowing derivative works (as
|
||||||
|
long as "Bitstream" or "Vera" are not in the names), and full
|
||||||
|
redistribution (so long as they are not *sold* by themselves). They
|
||||||
|
can be be bundled, redistributed and sold with any software.
|
||||||
|
|
||||||
|
The fonts are distributed under the following copyright:
|
||||||
|
|
||||||
|
Copyright
|
||||||
|
=========
|
||||||
|
|
||||||
|
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
|
||||||
|
Vera is a trademark of Bitstream, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the fonts accompanying this license ("Fonts") and associated
|
||||||
|
documentation files (the "Font Software"), to reproduce and distribute
|
||||||
|
the Font Software, including without limitation the rights to use,
|
||||||
|
copy, merge, publish, distribute, and/or sell copies of the Font
|
||||||
|
Software, and to permit persons to whom the Font Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice
|
||||||
|
shall be included in all copies of one or more of the Font Software
|
||||||
|
typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in
|
||||||
|
particular the designs of glyphs or characters in the Fonts may be
|
||||||
|
modified and additional glyphs or characters may be added to the
|
||||||
|
Fonts, only if the fonts are renamed to names not containing either
|
||||||
|
the words "Bitstream" or the word "Vera".
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts
|
||||||
|
or Font Software that has been modified and is distributed under the
|
||||||
|
"Bitstream Vera" names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but
|
||||||
|
no copy of one or more of the Font Software typefaces may be sold by
|
||||||
|
itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||||
|
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
||||||
|
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
|
||||||
|
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the names of Gnome, the Gnome
|
||||||
|
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||||
|
otherwise to promote the sale, use or other dealings in this Font
|
||||||
|
Software without prior written authorization from the Gnome Foundation
|
||||||
|
or Bitstream Inc., respectively. For further information, contact:
|
||||||
|
fonts at gnome dot org.
|
||||||
|
|
||||||
|
Copyright FAQ
|
||||||
|
=============
|
||||||
|
|
||||||
|
1. I don't understand the resale restriction... What gives?
|
||||||
|
|
||||||
|
Bitstream is giving away these fonts, but wishes to ensure its
|
||||||
|
competitors can't just drop the fonts as is into a font sale system
|
||||||
|
and sell them as is. It seems fair that if Bitstream can't make money
|
||||||
|
from the Bitstream Vera fonts, their competitors should not be able to
|
||||||
|
do so either. You can sell the fonts as part of any software package,
|
||||||
|
however.
|
||||||
|
|
||||||
|
2. I want to package these fonts separately for distribution and
|
||||||
|
sale as part of a larger software package or system. Can I do so?
|
||||||
|
|
||||||
|
Yes. A RPM or Debian package is a "larger software package" to begin
|
||||||
|
with, and you aren't selling them independently by themselves.
|
||||||
|
See 1. above.
|
||||||
|
|
||||||
|
3. Are derivative works allowed?
|
||||||
|
Yes!
|
||||||
|
|
||||||
|
4. Can I change or add to the font(s)?
|
||||||
|
Yes, but you must change the name(s) of the font(s).
|
||||||
|
|
||||||
|
5. Under what terms are derivative works allowed?
|
||||||
|
|
||||||
|
You must change the name(s) of the fonts. This is to ensure the
|
||||||
|
quality of the fonts, both to protect Bitstream and Gnome. We want to
|
||||||
|
ensure that if an application has opened a font specifically of these
|
||||||
|
names, it gets what it expects (though of course, using fontconfig,
|
||||||
|
substitutions could still could have occurred during font
|
||||||
|
opening). You must include the Bitstream copyright. Additional
|
||||||
|
copyrights can be added, as per copyright law. Happy Font Hacking!
|
||||||
|
|
||||||
|
6. If I have improvements for Bitstream Vera, is it possible they might get
|
||||||
|
adopted in future versions?
|
||||||
|
|
||||||
|
Yes. The contract between the Gnome Foundation and Bitstream has
|
||||||
|
provisions for working with Bitstream to ensure quality additions to
|
||||||
|
the Bitstream Vera font family. Please contact us if you have such
|
||||||
|
additions. Note, that in general, we will want such additions for the
|
||||||
|
entire family, not just a single font, and that you'll have to keep
|
||||||
|
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
|
||||||
|
glyphs to the font, they must be stylistically in keeping with Vera's
|
||||||
|
design. Vera cannot become a "ransom note" font. Jim Lyles will be
|
||||||
|
providing a document describing the design elements used in Vera, as a
|
||||||
|
guide and aid for people interested in contributing to Vera.
|
||||||
|
|
||||||
|
7. I want to sell a software package that uses these fonts: Can I do so?
|
||||||
|
|
||||||
|
Sure. Bundle the fonts with your software and sell your software
|
||||||
|
with the fonts. That is the intent of the copyright.
|
||||||
|
|
||||||
|
8. If applications have built the names "Bitstream Vera" into them,
|
||||||
|
can I override this somehow to use fonts of my choosing?
|
||||||
|
|
||||||
|
This depends on exact details of the software. Most open source
|
||||||
|
systems and software (e.g., Gnome, KDE, etc.) are now converting to
|
||||||
|
use fontconfig (see www.fontconfig.org) to handle font configuration,
|
||||||
|
selection and substitution; it has provisions for overriding font
|
||||||
|
names and subsituting alternatives. An example is provided by the
|
||||||
|
supplied local.conf file, which chooses the family Bitstream Vera for
|
||||||
|
"sans", "serif" and "monospace". Other software (e.g., the XFree86
|
||||||
|
core server) has other mechanisms for font substitution.
|
||||||
|
|
11
DJAGEN/branches/mustafa_branch/djagen/captcha/fonts/README.TXT
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
Contained herin is the Bitstream Vera font family.
|
||||||
|
|
||||||
|
The Copyright information is found in the COPYRIGHT.TXT file (along
|
||||||
|
with being incoporated into the fonts themselves).
|
||||||
|
|
||||||
|
The releases notes are found in the file "RELEASENOTES.TXT".
|
||||||
|
|
||||||
|
We hope you enjoy Vera!
|
||||||
|
|
||||||
|
Bitstream, Inc.
|
||||||
|
The Gnome Project
|
BIN
DJAGEN/branches/mustafa_branch/djagen/captcha/fonts/Vera.ttf
Executable file
51
DJAGEN/branches/mustafa_branch/djagen/captcha/helpers.py
Executable file
|
@ -0,0 +1,51 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import random
|
||||||
|
from captcha.conf import settings
|
||||||
|
|
||||||
|
def math_challenge():
|
||||||
|
operators = ('+','*','-',)
|
||||||
|
operands = (random.randint(1,10),random.randint(1,10))
|
||||||
|
operator = random.choice(operators)
|
||||||
|
if operands[0] < operands[1] and '-' == operator:
|
||||||
|
operands = (operands[1],operands[0])
|
||||||
|
challenge = '%d%s%d' %(operands[0],operator,operands[1])
|
||||||
|
return u'%s=' %(challenge), unicode(eval(challenge))
|
||||||
|
|
||||||
|
def random_char_challenge():
|
||||||
|
chars,ret = u'abcdefghijklmnopqrstuvwxyz', u''
|
||||||
|
for i in range(settings.CAPTCHA_LENGTH):
|
||||||
|
ret += random.choice(chars)
|
||||||
|
return ret.upper(),ret
|
||||||
|
|
||||||
|
def unicode_challenge():
|
||||||
|
chars,ret = u'äàáëéèïíîöóòüúù', u''
|
||||||
|
for i in range(settings.CAPTCHA_LENGTH):
|
||||||
|
ret += random.choice(chars)
|
||||||
|
return ret.upper(), ret
|
||||||
|
|
||||||
|
def word_challenge():
|
||||||
|
fd = file(settings.CAPTCHA_WORDS_DICTIONARY,'rb')
|
||||||
|
l = fd.readlines()
|
||||||
|
fd.close()
|
||||||
|
while True:
|
||||||
|
word = random.choice(l).strip()
|
||||||
|
if len(word) >= settings.CAPTCHA_DICTIONARY_MIN_LENGTH and len(word) <= settings.CAPTCHA_DICTIONARY_MAX_LENGTH:
|
||||||
|
break
|
||||||
|
return word.upper(), word.lower()
|
||||||
|
|
||||||
|
def noise_arcs(draw,image):
|
||||||
|
size = image.size
|
||||||
|
draw.arc([-20,-20, size[0],20], 0, 295, fill=settings.CAPTCHA_FOREGROUND_COLOR)
|
||||||
|
draw.line([-20,20, size[0]+20,size[1]-20], fill=settings.CAPTCHA_FOREGROUND_COLOR)
|
||||||
|
draw.line([-20,0, size[0]+20,size[1]], fill=settings.CAPTCHA_FOREGROUND_COLOR)
|
||||||
|
return draw
|
||||||
|
|
||||||
|
def noise_dots(draw,image):
|
||||||
|
size = image.size
|
||||||
|
for p in range(int(size[0]*size[1]*0.1)):
|
||||||
|
draw.point((random.randint(0, size[0]),random.randint(0, size[1])), fill=settings.CAPTCHA_FOREGROUND_COLOR )
|
||||||
|
return draw
|
||||||
|
|
||||||
|
def post_smooth(image):
|
||||||
|
import ImageFilter
|
||||||
|
return image.filter(ImageFilter.SMOOTH)
|
|
@ -0,0 +1,28 @@
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from optparse import make_option
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Clean up expired captcha hashkeys."
|
||||||
|
|
||||||
|
def handle(self, **options):
|
||||||
|
from captcha.models import CaptchaStore
|
||||||
|
import datetime
|
||||||
|
verbose = int(options.get('verbosity'))
|
||||||
|
expired_keys = CaptchaStore.objects.filter(expiration__lte=datetime.datetime.now()).count()
|
||||||
|
if verbose >= 1:
|
||||||
|
print "Currently %s expired hashkeys" % expired_keys
|
||||||
|
try:
|
||||||
|
CaptchaStore.remove_expired()
|
||||||
|
except:
|
||||||
|
if verbose >= 1 :
|
||||||
|
print "Unable to delete expired hashkeys."
|
||||||
|
sys.exit(1)
|
||||||
|
if verbose >= 1:
|
||||||
|
if expired_keys > 0:
|
||||||
|
print "Expired hashkeys removed."
|
||||||
|
else:
|
||||||
|
print "No keys to remove."
|
||||||
|
|
||||||
|
|
46
DJAGEN/branches/mustafa_branch/djagen/captcha/models.py
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
from django.db import models
|
||||||
|
from captcha.conf import settings as captcha_settings
|
||||||
|
import datetime, unicodedata, random, time
|
||||||
|
|
||||||
|
# Heavily based on session key generation in Django
|
||||||
|
# Use the system (hardware-based) random number generator if it exists.
|
||||||
|
if hasattr(random, 'SystemRandom'):
|
||||||
|
randrange = random.SystemRandom().randrange
|
||||||
|
else:
|
||||||
|
randrange = random.randrange
|
||||||
|
MAX_RANDOM_KEY = 18446744073709551616L # 2 << 63
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import hashlib # sha for Python 2.5+
|
||||||
|
except ImportError:
|
||||||
|
import sha # sha for Python 2.4 (deprecated in Python 2.6)
|
||||||
|
hashlib = False
|
||||||
|
|
||||||
|
class CaptchaStore(models.Model):
|
||||||
|
challenge = models.CharField(blank=False, max_length=32)
|
||||||
|
response = models.CharField(blank=False, max_length=32)
|
||||||
|
hashkey = models.CharField(blank=False, max_length=40, unique=True)
|
||||||
|
expiration = models.DateTimeField(blank=False)
|
||||||
|
|
||||||
|
def save(self,*args,**kwargs):
|
||||||
|
self.response = self.response.lower()
|
||||||
|
if not self.expiration:
|
||||||
|
self.expiration = datetime.datetime.now() + datetime.timedelta(minutes= int(captcha_settings.CAPTCHA_TIMEOUT))
|
||||||
|
if not self.hashkey:
|
||||||
|
key_ = unicodedata.normalize('NFKD', str(randrange(0,MAX_RANDOM_KEY)) + str(time.time()) + unicode(self.challenge)).encode('ascii', 'ignore') + unicodedata.normalize('NFKD', unicode(self.response)).encode('ascii', 'ignore')
|
||||||
|
if hashlib:
|
||||||
|
self.hashkey = hashlib.new('sha', key_).hexdigest()
|
||||||
|
else:
|
||||||
|
self.hashkey = sha.new(key_).hexdigest()
|
||||||
|
del(key_)
|
||||||
|
super(CaptchaStore,self).save(*args,**kwargs)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.challenge
|
||||||
|
|
||||||
|
|
||||||
|
def remove_expired(cls):
|
||||||
|
cls.objects.filter(expiration__lte=datetime.datetime.now()).delete()
|
||||||
|
remove_expired = classmethod(remove_expired)
|
||||||
|
|
153
DJAGEN/branches/mustafa_branch/djagen/captcha/tests/__init__.py
Executable file
|
@ -0,0 +1,153 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from captcha.conf import settings
|
||||||
|
from captcha.models import CaptchaStore
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class CaptchaCase(TestCase):
|
||||||
|
urls = 'captcha.tests.urls'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.default_challenge = settings.get_challenge()()
|
||||||
|
self.math_challenge = settings._callable_from_string('captcha.helpers.math_challenge')()
|
||||||
|
self.chars_challenge = settings._callable_from_string('captcha.helpers.random_char_challenge')()
|
||||||
|
self.unicode_challenge = settings._callable_from_string('captcha.helpers.unicode_challenge')()
|
||||||
|
|
||||||
|
self.default_store, created = CaptchaStore.objects.get_or_create(challenge=self.default_challenge[0],response=self.default_challenge[1])
|
||||||
|
self.math_store, created = CaptchaStore.objects.get_or_create(challenge=self.math_challenge[0],response=self.math_challenge[1])
|
||||||
|
self.chars_store, created = CaptchaStore.objects.get_or_create(challenge=self.chars_challenge[0],response=self.chars_challenge[1])
|
||||||
|
self.unicode_store, created = CaptchaStore.objects.get_or_create(challenge=self.unicode_challenge[0],response=self.unicode_challenge[1])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def testImages(self):
|
||||||
|
for key in (self.math_store.hashkey, self.chars_store.hashkey, self.default_store.hashkey, self.unicode_store.hashkey):
|
||||||
|
response = self.client.get(reverse('captcha-image',kwargs=dict(key=key)))
|
||||||
|
self.failUnlessEqual(response.status_code, 200)
|
||||||
|
self.assertTrue(response.has_header('content-type'))
|
||||||
|
self.assertEquals(response._headers.get('content-type'), ('Content-Type', 'image/png'))
|
||||||
|
|
||||||
|
def testAudio(self):
|
||||||
|
if not settings.CAPTCHA_FLITE_PATH:
|
||||||
|
return
|
||||||
|
for key in (self.math_store.hashkey, self.chars_store.hashkey, self.default_store.hashkey, self.unicode_store.hashkey):
|
||||||
|
response = self.client.get(reverse('captcha-audio',kwargs=dict(key=key)))
|
||||||
|
self.failUnlessEqual(response.status_code, 200)
|
||||||
|
self.assertTrue(len(response.content) > 1024)
|
||||||
|
self.assertTrue(response.has_header('content-type'))
|
||||||
|
self.assertEquals(response._headers.get('content-type'), ('Content-Type', 'audio/x-wav'))
|
||||||
|
|
||||||
|
def testFormSubmit(self):
|
||||||
|
r = self.client.get(reverse('captcha-test'))
|
||||||
|
self.failUnlessEqual(r.status_code, 200)
|
||||||
|
hash_ = r.content[r.content.find('value="')+7:r.content.find('value="')+47]
|
||||||
|
try:
|
||||||
|
response = CaptchaStore.objects.get(hashkey=hash_).response
|
||||||
|
except:
|
||||||
|
self.fail()
|
||||||
|
|
||||||
|
r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_,captcha_1=response, subject='xxx', sender='asasd@asdasd.com'))
|
||||||
|
self.failUnlessEqual(r.status_code, 200)
|
||||||
|
self.assertTrue(r.content.find('Form validated') > 0)
|
||||||
|
|
||||||
|
r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_,captcha_1=response, subject='xxx', sender='asasd@asdasd.com'))
|
||||||
|
self.failUnlessEqual(r.status_code, 200)
|
||||||
|
self.assertFalse(r.content.find('Form validated') > 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def testWrongSubmit(self):
|
||||||
|
r = self.client.get(reverse('captcha-test'))
|
||||||
|
self.failUnlessEqual(r.status_code, 200)
|
||||||
|
r = self.client.post(reverse('captcha-test'), dict(captcha_0='abc',captcha_1='wrong response', subject='xxx', sender='asasd@asdasd.com'))
|
||||||
|
self.assertFormError(r,'form','captcha',_('Invalid CAPTCHA'))
|
||||||
|
|
||||||
|
def testDeleteExpired(self):
|
||||||
|
self.default_store.expiration = datetime.datetime.now() - datetime.timedelta(minutes=5)
|
||||||
|
self.default_store.save()
|
||||||
|
hash_ = self.default_store.hashkey
|
||||||
|
r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_,captcha_1=self.default_store.response, subject='xxx', sender='asasd@asdasd.com'))
|
||||||
|
|
||||||
|
self.failUnlessEqual(r.status_code, 200)
|
||||||
|
self.assertFalse(r.content.find('Form validated') > 0)
|
||||||
|
|
||||||
|
# expired -> deleted
|
||||||
|
try:
|
||||||
|
CaptchaStore.objects.get(hashkey=hash_)
|
||||||
|
self.fail()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def testCustomErrorMessage(self):
|
||||||
|
r = self.client.get(reverse('captcha-test-custom-error-message'))
|
||||||
|
self.failUnlessEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
# Wrong answer
|
||||||
|
r = self.client.post(reverse('captcha-test-custom-error-message'), dict(captcha_0='abc',captcha_1='wrong response'))
|
||||||
|
self.assertFormError(r,'form','captcha','TEST CUSTOM ERROR MESSAGE')
|
||||||
|
# empty answer
|
||||||
|
r = self.client.post(reverse('captcha-test-custom-error-message'), dict(captcha_0='abc',captcha_1=''))
|
||||||
|
self.assertFormError(r,'form','captcha',_('This field is required.'))
|
||||||
|
|
||||||
|
def testRepeatedChallenge(self):
|
||||||
|
store = CaptchaStore.objects.create(challenge='xxx',response='xxx')
|
||||||
|
try:
|
||||||
|
store2 = CaptchaStore.objects.create(challenge='xxx',response='xxx')
|
||||||
|
except Exception:
|
||||||
|
self.fail()
|
||||||
|
|
||||||
|
|
||||||
|
def testRepeatedChallengeFormSubmit(self):
|
||||||
|
settings.CAPTCHA_CHALLENGE_FUNCT = 'captcha.tests.trivial_challenge'
|
||||||
|
|
||||||
|
r1 = self.client.get(reverse('captcha-test'))
|
||||||
|
r2 = self.client.get(reverse('captcha-test'))
|
||||||
|
self.failUnlessEqual(r1.status_code, 200)
|
||||||
|
self.failUnlessEqual(r2.status_code, 200)
|
||||||
|
hash_1 = r1.content[r1.content.find('value="')+7:r1.content.find('value="')+47]
|
||||||
|
hash_2 = r2.content[r2.content.find('value="')+7:r2.content.find('value="')+47]
|
||||||
|
try:
|
||||||
|
store_1 = CaptchaStore.objects.get(hashkey=hash_1)
|
||||||
|
store_2 = CaptchaStore.objects.get(hashkey=hash_2)
|
||||||
|
except:
|
||||||
|
self.fail()
|
||||||
|
|
||||||
|
self.assertTrue(store_1.pk != store_2.pk)
|
||||||
|
self.assertTrue(store_1.response == store_2.response)
|
||||||
|
self.assertTrue(hash_1 != hash_2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
r1 = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_1,captcha_1=store_1.response, subject='xxx', sender='asasd@asdasd.com'))
|
||||||
|
self.failUnlessEqual(r1.status_code, 200)
|
||||||
|
self.assertTrue(r1.content.find('Form validated') > 0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
store_2 = CaptchaStore.objects.get(hashkey=hash_2)
|
||||||
|
except:
|
||||||
|
self.fail()
|
||||||
|
|
||||||
|
r2 = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_2,captcha_1=store_2.response, subject='xxx', sender='asasd@asdasd.com'))
|
||||||
|
self.failUnlessEqual(r2.status_code, 200)
|
||||||
|
self.assertTrue(r2.content.find('Form validated') > 0)
|
||||||
|
|
||||||
|
def testOutputFormat(self):
|
||||||
|
settings.CAPTCHA_OUTPUT_FORMAT = u'%(image)s<p>Hello, captcha world</p>%(hidden_field)s%(text_field)s'
|
||||||
|
r = self.client.get(reverse('captcha-test'))
|
||||||
|
self.failUnlessEqual(r.status_code, 200)
|
||||||
|
self.assertTrue('<p>Hello, captcha world</p>' in r.content)
|
||||||
|
|
||||||
|
def testInvalidOutputFormat(self):
|
||||||
|
settings.CAPTCHA_OUTPUT_FORMAT = u'%(image)s'
|
||||||
|
try:
|
||||||
|
r = self.client.get(reverse('captcha-test'))
|
||||||
|
self.fail()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def trivial_challenge():
|
||||||
|
return 'trivial','trivial'
|
6
DJAGEN/branches/mustafa_branch/djagen/captcha/tests/urls.py
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
url(r'test/$','captcha.tests.views.test',name='captcha-test'),
|
||||||
|
url(r'test2/$','captcha.tests.views.test_custom_error_message',name='captcha-test-custom-error-message'),
|
||||||
|
url(r'',include('captcha.urls')),
|
||||||
|
)
|
58
DJAGEN/branches/mustafa_branch/djagen/captcha/tests/views.py
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
from django import forms
|
||||||
|
from captcha.fields import CaptchaField
|
||||||
|
from django.template import Context, RequestContext, loader
|
||||||
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
TEST_TEMPLATE = r'''
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||||
|
<title>captcha test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% if passed %}
|
||||||
|
<p style="color:green">Form validated</p>
|
||||||
|
{% endif %}
|
||||||
|
<form action="{% url captcha-test %}" method="post">
|
||||||
|
{{form.as_p}}
|
||||||
|
<p><input type="submit" value="Continue →"></p>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
def test(request):
|
||||||
|
|
||||||
|
class CaptchaTestForm(forms.Form):
|
||||||
|
subject = forms.CharField(max_length=100)
|
||||||
|
sender = forms.EmailField()
|
||||||
|
captcha = CaptchaField(help_text='asdasd')
|
||||||
|
|
||||||
|
if request.POST:
|
||||||
|
form = CaptchaTestForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
passed = True
|
||||||
|
else:
|
||||||
|
form = CaptchaTestForm()
|
||||||
|
|
||||||
|
t = loader.get_template_from_string(TEST_TEMPLATE)
|
||||||
|
return HttpResponse(t.render(RequestContext(request, locals())))
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_error_message(request):
|
||||||
|
|
||||||
|
class CaptchaTestForm(forms.Form):
|
||||||
|
captcha = CaptchaField(help_text='asdasd', error_messages=dict(invalid='TEST CUSTOM ERROR MESSAGE'))
|
||||||
|
|
||||||
|
if request.POST:
|
||||||
|
form = CaptchaTestForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
passed = True
|
||||||
|
else:
|
||||||
|
form = CaptchaTestForm()
|
||||||
|
|
||||||
|
t = loader.get_template_from_string(TEST_TEMPLATE)
|
||||||
|
return HttpResponse(t.render(RequestContext(request, locals())))
|
6
DJAGEN/branches/mustafa_branch/djagen/captcha/urls.py
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
urlpatterns = patterns('captcha.views',
|
||||||
|
url(r'image/(?P<key>\w+)/$','captcha_image',name='captcha-image'),
|
||||||
|
url(r'audio/(?P<key>\w+)/$','captcha_audio',name='captcha-audio'),
|
||||||
|
)
|
92
DJAGEN/branches/mustafa_branch/djagen/captcha/views.py
Executable file
|
@ -0,0 +1,92 @@
|
||||||
|
from cStringIO import StringIO
|
||||||
|
from captcha.models import CaptchaStore
|
||||||
|
from django.http import HttpResponse, Http404
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
import Image,ImageDraw,ImageFont,ImageFilter,random
|
||||||
|
from captcha.conf import settings
|
||||||
|
import re
|
||||||
|
|
||||||
|
NON_DIGITS_RX = re.compile('[^\d]')
|
||||||
|
|
||||||
|
def captcha_image(request,key):
|
||||||
|
store = get_object_or_404(CaptchaStore,hashkey=key)
|
||||||
|
text=store.challenge
|
||||||
|
|
||||||
|
if settings.CAPTCHA_FONT_PATH.lower().strip().endswith('ttf'):
|
||||||
|
font = ImageFont.truetype(settings.CAPTCHA_FONT_PATH,settings.CAPTCHA_FONT_SIZE)
|
||||||
|
else:
|
||||||
|
font = ImageFont.load(settings.CAPTCHA_FONT_PATH)
|
||||||
|
|
||||||
|
size = font.getsize(text)
|
||||||
|
size = (size[0]*2,size[1])
|
||||||
|
image = Image.new('RGB', size , settings.CAPTCHA_BACKGROUND_COLOR)
|
||||||
|
|
||||||
|
try:
|
||||||
|
PIL_VERSION = int(NON_DIGITS_RX.sub('',Image.VERSION))
|
||||||
|
except:
|
||||||
|
PIL_VERSION = 116
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
xpos = 2
|
||||||
|
for char in text:
|
||||||
|
fgimage = Image.new('RGB', size, settings.CAPTCHA_FOREGROUND_COLOR)
|
||||||
|
charimage = Image.new('L', font.getsize(' %s '%char), '#000000')
|
||||||
|
chardraw = ImageDraw.Draw(charimage)
|
||||||
|
chardraw.text((0,0), ' %s '%char, font=font, fill='#ffffff')
|
||||||
|
if settings.CAPTCHA_LETTER_ROTATION:
|
||||||
|
if PIL_VERSION >= 116:
|
||||||
|
charimage = charimage.rotate(random.randrange( *settings.CAPTCHA_LETTER_ROTATION ), expand=0, resample=Image.BICUBIC)
|
||||||
|
else:
|
||||||
|
charimage = charimage.rotate(random.randrange( *settings.CAPTCHA_LETTER_ROTATION ), resample=Image.BICUBIC)
|
||||||
|
charimage = charimage.crop(charimage.getbbox())
|
||||||
|
maskimage = Image.new('L', size)
|
||||||
|
|
||||||
|
maskimage.paste(charimage, (xpos, 4, xpos+charimage.size[0], 4+charimage.size[1] ))
|
||||||
|
size = maskimage.size
|
||||||
|
image = Image.composite(fgimage, image, maskimage)
|
||||||
|
xpos = xpos + 2 + charimage.size[0]
|
||||||
|
|
||||||
|
image = image.crop((0,0,xpos+1,size[1]))
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
|
||||||
|
for f in settings.noise_functions():
|
||||||
|
draw = f(draw,image)
|
||||||
|
for f in settings.filter_functions():
|
||||||
|
image = f(image)
|
||||||
|
|
||||||
|
out = StringIO()
|
||||||
|
image.save(out,"PNG")
|
||||||
|
out.seek(0)
|
||||||
|
|
||||||
|
response = HttpResponse()
|
||||||
|
response['Content-Type'] = 'image/png'
|
||||||
|
response.write(out.read())
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def captcha_audio(request,key):
|
||||||
|
if settings.CAPTCHA_FLITE_PATH:
|
||||||
|
store = get_object_or_404(CaptchaStore,hashkey=key)
|
||||||
|
text=store.challenge
|
||||||
|
if 'captcha.helpers.math_challenge' == settings.CAPTCHA_CHALLENGE_FUNCT:
|
||||||
|
text = text.replace('*','times').replace('-','minus')
|
||||||
|
else:
|
||||||
|
text = ', '.join(list(text))
|
||||||
|
|
||||||
|
import tempfile, os
|
||||||
|
|
||||||
|
path = str(os.path.join(tempfile.gettempdir(),'%s.wav' %key))
|
||||||
|
cline = '%s -t "%s" -o "%s"' %(settings.CAPTCHA_FLITE_PATH, text, path)
|
||||||
|
|
||||||
|
os.popen(cline).read()
|
||||||
|
if os.path.isfile(path):
|
||||||
|
response = HttpResponse()
|
||||||
|
f = open(path,'rb')
|
||||||
|
response['Content-Type'] = 'audio/x-wav'
|
||||||
|
response.write(f.read())
|
||||||
|
f.close()
|
||||||
|
os.unlink(path)
|
||||||
|
return response
|
||||||
|
|
||||||
|
raise Http404
|
0
DJAGEN/branches/mustafa_branch/djagen/collector/__init__.py
Executable file
74
DJAGEN/branches/mustafa_branch/djagen/collector/admin.py
Executable file
|
@ -0,0 +1,74 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from djagen.collector.models import *
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from djagen.collector.configini import *
|
||||||
|
|
||||||
|
class AuthorsAdmin (admin.ModelAdmin):
|
||||||
|
|
||||||
|
list_display = ('author_id', 'author_name', 'author_email', 'author_face', 'current_status', 'is_approved', 'label_personal', 'label_lkd', 'label_community', 'label_eng')
|
||||||
|
list_select_related = True
|
||||||
|
|
||||||
|
search_fields = ['author_name', 'author_surname', 'author_email']
|
||||||
|
|
||||||
|
def save_model(self, request, obj, form, change):
|
||||||
|
|
||||||
|
#get the values for saving
|
||||||
|
author_name = obj.author_name
|
||||||
|
author_surname = obj.author_surname
|
||||||
|
author_face = obj.author_face
|
||||||
|
channel_url = obj.channel_url
|
||||||
|
|
||||||
|
current_status = obj.current_status
|
||||||
|
is_approved = obj.is_approved
|
||||||
|
|
||||||
|
#creating the history
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
action_type = current_status
|
||||||
|
|
||||||
|
author_id = obj.author_id
|
||||||
|
if author_id:
|
||||||
|
#then this is an update
|
||||||
|
author = Authors.objects.get(author_id = author_id)
|
||||||
|
pre_status = author.is_approved
|
||||||
|
current_status = obj.is_approved
|
||||||
|
obj.save()
|
||||||
|
else:
|
||||||
|
obj.save()
|
||||||
|
author = Authors.objects.get(author_name=author_name, author_surname=author_surname, channel_url=channel_url)
|
||||||
|
pre_status = None
|
||||||
|
current_status = author.is_approved
|
||||||
|
|
||||||
|
author.history_set.create(action_type=action_type, action_date=now, action_owner=request.user.username)
|
||||||
|
|
||||||
|
|
||||||
|
#create tmp_config.ini here
|
||||||
|
handler = Handler(author.author_id)
|
||||||
|
handler.create_tmp_entries()
|
||||||
|
|
||||||
|
if pre_status != current_status:
|
||||||
|
a_face = author.author_face
|
||||||
|
|
||||||
|
images_path = os.path.join(settings.MAIN_PATH, 'www', 'images')
|
||||||
|
heads_path = os.path.join(images_path, 'heads')
|
||||||
|
face_path = os.path.join(heads_path, a_face)
|
||||||
|
|
||||||
|
tmp_image_path = os.path.join(settings.MAIN_PATH, 'temp_ini', a_face)
|
||||||
|
|
||||||
|
if os.path.exists(tmp_image_path):
|
||||||
|
shutil.move(tmp_image_path, face_path)
|
||||||
|
|
||||||
|
class HistoryAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('action_type', 'action_date', 'action_author', 'action_owner')
|
||||||
|
|
||||||
|
admin.site.register(History, HistoryAdmin)
|
||||||
|
admin.site.register(Authors, AuthorsAdmin)
|
||||||
|
|
93
DJAGEN/branches/mustafa_branch/djagen/collector/configini.py
Executable file
|
@ -0,0 +1,93 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
from django.conf import settings
|
||||||
|
from djagen.collector.models import *
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
class Handler:
|
||||||
|
|
||||||
|
def __init__(self, id):
|
||||||
|
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self.tmp_entries_ini = os.path.join(settings.MAIN_PATH, 'tmp_ini', 'tmp_entries.ini')
|
||||||
|
|
||||||
|
self.config_entries_ini = os.path.join(settings.MAIN_PATH, 'gezegen', 'config_entries.ini')
|
||||||
|
|
||||||
|
def __set_values(self):
|
||||||
|
|
||||||
|
author = Authors.objects.get(author_id = self.id)
|
||||||
|
|
||||||
|
if not author.is_approved:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.name = author.author_name + ' ' + author.author_surname
|
||||||
|
self.face = author.author_face
|
||||||
|
self.url = author.channel_url
|
||||||
|
|
||||||
|
labels = {author.label_personal:'Personal', author.label_lkd: 'LKD', author.label_community: 'Community', author.label_eng: 'Eng'}
|
||||||
|
|
||||||
|
label_li = [k for k,v in labels.iteritems() if v==1]
|
||||||
|
self.author_labels = " ".join(label_li)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def create_tmp_entries(self):
|
||||||
|
|
||||||
|
if not self.__set_values(): return
|
||||||
|
|
||||||
|
config_entries = open(self.config_entries_ini)
|
||||||
|
tmp_entries = open(self.tmp_entries_ini, 'w')
|
||||||
|
|
||||||
|
Config = ConfigParser.ConfigParser()
|
||||||
|
Config.read(self.config_entries_ini)
|
||||||
|
sections = Config.sections()
|
||||||
|
|
||||||
|
for section in sections:
|
||||||
|
|
||||||
|
config_name = Config.get(section, 'name')
|
||||||
|
config_label = Config.get(section, 'label')
|
||||||
|
config_id = Config.get(section, 'id')
|
||||||
|
config_url = section
|
||||||
|
|
||||||
|
try:
|
||||||
|
config_face = Config.get(section, 'face')
|
||||||
|
except:
|
||||||
|
config_face = None
|
||||||
|
|
||||||
|
if config_id == self.id:
|
||||||
|
|
||||||
|
url = self.url
|
||||||
|
face = self.face
|
||||||
|
name = self.name
|
||||||
|
label = self.author_labels
|
||||||
|
id = self.id
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
url = config_url
|
||||||
|
face = config_face
|
||||||
|
name = config_name
|
||||||
|
label = config_label
|
||||||
|
id = config_id
|
||||||
|
|
||||||
|
s = url + '\n'
|
||||||
|
s += 'name = ' + name + '\n'
|
||||||
|
s += 'label = ' + label + '\n'
|
||||||
|
if face:
|
||||||
|
s += 'face = ' + face + '\n'
|
||||||
|
s += 'id = ' + id + '\n' + '\n'
|
||||||
|
|
||||||
|
tmp_entries.write(s)
|
||||||
|
|
||||||
|
tmp_entries.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
121
DJAGEN/branches/mustafa_branch/djagen/collector/configxml.py
Executable file
|
@ -0,0 +1,121 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
from xml.dom import minidom
|
||||||
|
|
||||||
|
class Handler:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.main_url = "/home/oguz/django-projects/djagen/gezegen"
|
||||||
|
self.gezegen_url = os.path.join(self.main_url,"gezegen")
|
||||||
|
self.entries_xml = os.path.join(self.gezegen_url, "config_entries.xml")
|
||||||
|
self.header_xml = os.path.join(self.gezegen_url, 'config_header.xml')
|
||||||
|
self.tmp_ini_dir_path = os.path.join(self.main_url, "tmp_ini")
|
||||||
|
|
||||||
|
def get_doc(self, type="entries"):
|
||||||
|
|
||||||
|
if type == "entries":
|
||||||
|
self.doc = minidom.parse(self.entries_xml)
|
||||||
|
else:
|
||||||
|
self.doc = minidom.parse(self.header_xml)
|
||||||
|
return self.doc
|
||||||
|
|
||||||
|
def get_tag_entries(self,tag):
|
||||||
|
|
||||||
|
self.entries = self.doc.getElementsByTagName(tag)
|
||||||
|
return self.entries
|
||||||
|
|
||||||
|
def set_ini_variables(self, id, name, feed, nick, face, label):
|
||||||
|
|
||||||
|
self.tmp_ini = {'id': id, 'name': name, 'feed': feed, 'nick': nick, 'face': face, 'label': label}
|
||||||
|
|
||||||
|
def open_file(self):
|
||||||
|
path = os.path.join(self.tmp_ini_dir_path, 'tmp.ini')
|
||||||
|
self.f = open(path, "w")
|
||||||
|
|
||||||
|
def create_header(self):
|
||||||
|
|
||||||
|
for header in self.entries:
|
||||||
|
|
||||||
|
children = header.childNodes
|
||||||
|
for child in children:
|
||||||
|
if child.nodeType == child.TEXT_NODE: continue
|
||||||
|
else:
|
||||||
|
node_name = child.nodeName
|
||||||
|
f_child = child.firstChild
|
||||||
|
node_value = f_child.nodeValue
|
||||||
|
|
||||||
|
s = []
|
||||||
|
if node_name != "header_name":
|
||||||
|
s.append(node_name)
|
||||||
|
s.append("=")
|
||||||
|
s.append(node_value)
|
||||||
|
s.append("\n")
|
||||||
|
ss = " ".join(s)
|
||||||
|
self.f.write(ss)
|
||||||
|
|
||||||
|
def traverse(self):
|
||||||
|
|
||||||
|
for entry in self.entries:
|
||||||
|
|
||||||
|
nodes = entry.childNodes
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
|
||||||
|
child = node.firstChild
|
||||||
|
self.face = None
|
||||||
|
|
||||||
|
if node.nodeType == node.TEXT_NODE: continue
|
||||||
|
|
||||||
|
if node.nodeName == "feed":
|
||||||
|
self.feed = child.toxml()
|
||||||
|
|
||||||
|
if node.nodeName == "name":
|
||||||
|
self.name = child.toxml()
|
||||||
|
|
||||||
|
if node.nodeName == "nick":
|
||||||
|
self.nick = child.toxml()
|
||||||
|
|
||||||
|
if node.nodeName == "label":
|
||||||
|
self.label = child.toxml()
|
||||||
|
|
||||||
|
if node.nodeName == "face":
|
||||||
|
self.face = child.toxml()
|
||||||
|
|
||||||
|
if node.nodeName == "id":
|
||||||
|
self.id = child.toxml()
|
||||||
|
|
||||||
|
if int(self.tmp_ini['id']) == int(self.id):
|
||||||
|
|
||||||
|
self.write_to_file(self.tmp_ini)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
config = {'id': self.id, 'name': self.name, 'feed': self.feed, 'nick': self.nick, 'label': self.label, 'face': self.face}
|
||||||
|
self.write_to_file(config)
|
||||||
|
|
||||||
|
|
||||||
|
def write_to_file(self, dic):
|
||||||
|
|
||||||
|
feed = "feed = " + dic['feed'] + "\n"
|
||||||
|
name = "name = " + dic['name'] + "\n"
|
||||||
|
nick = "nick = " + dic['nick'] + "\n"
|
||||||
|
label = "label = " + dic['label'] + "\n"
|
||||||
|
id = "id = " + dic['id'] + "\n"
|
||||||
|
|
||||||
|
self.f.write("\n")
|
||||||
|
self.f.write(feed)
|
||||||
|
self.f.write(name)
|
||||||
|
self.f.write(nick)
|
||||||
|
if dic['face']:
|
||||||
|
face = "face = " + dic['face'] + "\n"
|
||||||
|
self.f.write(face)
|
||||||
|
self.f.write(label)
|
||||||
|
self.f.write(id)
|
||||||
|
|
||||||
|
def close_file(self):
|
||||||
|
self.f.close()
|
||||||
|
|
||||||
|
|
21
DJAGEN/branches/mustafa_branch/djagen/collector/forms.py
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from captcha.fields import CaptchaField
|
||||||
|
|
||||||
|
class ContactForm(forms.Form):
|
||||||
|
|
||||||
|
name = forms.CharField(max_length=25, required=True, error_messages={'required': 'Lütfen adınızı giriniz'}, label='Adınız')
|
||||||
|
surname = forms.CharField(max_length=25, required=True, error_messages={'required': 'Lütfen soyadınızı giriniz'}, label='Soyadınız')
|
||||||
|
email = forms.EmailField(required=True, error_messages={'required': 'Size ulaşabileceğimiz eposta adresinizi giriniz'}, label='Eposta Adresiniz')
|
||||||
|
hackergotchi = forms.FileField(required=False, label='Hacketgotchiniz', help_text='Max 80*80 pixellik Gezegende görünmesini istediğiniz fotoğrafınız')
|
||||||
|
feed = forms.URLField(required=True, label='Besleme adresiniz', help_text='Günlüğünüzün XML kaynağının adresi')
|
||||||
|
message = forms.CharField(required=False, label='İletişim Mesajınız', widget=forms.widgets.Textarea())
|
||||||
|
#field for captcha
|
||||||
|
captcha = CaptchaField(label="Captcha Alanı", help_text='Gördüğünü karakterleri aynen yazınız', error_messages={'required': 'Hatalı yazdınız!'})
|
||||||
|
|
||||||
|
class QueryForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=25, required = False, label = 'Adı')
|
||||||
|
surname = forms.CharField(max_length=25, required = False, label = 'Soyadı')
|
||||||
|
text = forms.CharField(required = False, label = 'Aradığınız metin', widget = forms.widgets.Textarea() )
|
111
DJAGEN/branches/mustafa_branch/djagen/collector/models.py
Executable file
|
@ -0,0 +1,111 @@
|
||||||
|
from django.db import models
|
||||||
|
import datetime, unicodedata, random, time
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
ACTION_CHOICES = (
|
||||||
|
(1, u'Removed'),
|
||||||
|
(2, u'Approved'),
|
||||||
|
(3, u'Paused'),
|
||||||
|
(4, u'Readded'),
|
||||||
|
(5, u'Applied'),
|
||||||
|
(6, u'Editted')
|
||||||
|
)
|
||||||
|
|
||||||
|
class Authors (models.Model):
|
||||||
|
author_id = models.AutoField(primary_key=True, help_text="Author ID")
|
||||||
|
author_name = models.CharField(max_length=50, help_text="Author Name")
|
||||||
|
author_surname = models.CharField(max_length=50, help_text="Author Name")
|
||||||
|
#we dont keep emails at the config.ini files, this part should be entered at the admin page
|
||||||
|
author_email = models.EmailField(null=True, blank=True, help_text="Author Email Address")
|
||||||
|
#the png file name of the author
|
||||||
|
author_face = models.CharField(max_length=30, null=True, blank=True, help_text="Author Face Name")
|
||||||
|
channel_subtitle = models.TextField(null=True, blank=True, help_text="Channel Subtitle")
|
||||||
|
channel_title = models.TextField(null=True, blank=True, help_text="Channel Title")
|
||||||
|
#URL of the feed.
|
||||||
|
channel_url = models.URLField(help_text="Channel URL")
|
||||||
|
#Link to the original format feed
|
||||||
|
channel_link = models.URLField(null=True, blank=True, help_text="Channel Link")
|
||||||
|
channel_urlstatus = models.IntegerField(null=True, blank=True, help_text="Channel URL Status")
|
||||||
|
|
||||||
|
#use this field to check whether the author is shown on the planet or not, like banned situations
|
||||||
|
current_status = models.SmallIntegerField(default=2, choices=ACTION_CHOICES, help_text="Current Status of the Author")
|
||||||
|
#whether the application to the planet is approved, the approved ones will be shown at the planet
|
||||||
|
is_approved = models.BooleanField(default=1, help_text="Approve Status of the Author")
|
||||||
|
|
||||||
|
#planets that the channel belongs to
|
||||||
|
#at the config.ini the entries should be obe of the belows:
|
||||||
|
#label = Personal
|
||||||
|
#label = LKD
|
||||||
|
#label = Eng
|
||||||
|
#label = Community
|
||||||
|
label_personal = models.BooleanField(default=1, help_text="Channnels at the Personal Blog Page")
|
||||||
|
label_lkd = models.BooleanField(default=0, help_text="Channels that are belong to LKD Blogs")
|
||||||
|
label_community = models.BooleanField(default=0, help_text="Channels that are belong to some community blogs")
|
||||||
|
label_eng = models.BooleanField(default=0, help_text="Channels that have English entries")
|
||||||
|
#at the main page, lets just show personal and lkd for now, for communities lets ask them a special rss
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u'%s %s' % (self.author_name, self.author_surname)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
#order according to the author_name, ascending
|
||||||
|
ordering = ['author_name']
|
||||||
|
|
||||||
|
# keep the history for the action that are done on the member urls
|
||||||
|
class History (models.Model):
|
||||||
|
action_type = models.SmallIntegerField(choices=ACTION_CHOICES)
|
||||||
|
action_date = models.DateTimeField()
|
||||||
|
action_explanation = models.TextField(help_text="Reason of Action", blank=True, null=True)
|
||||||
|
action_author = models.ForeignKey('Authors')
|
||||||
|
action_owner = models.CharField(max_length=20, help_text="The user who did the action")
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return str(self.action_type)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
#order descending, show the last actions at top
|
||||||
|
ordering = ['-action_date']
|
||||||
|
|
||||||
|
class Entries (models.Model):
|
||||||
|
id_hash = models.CharField(max_length=50, help_text="Hash of the ID", primary_key=True)
|
||||||
|
title = models.CharField(max_length=150, help_text="Entry Title")
|
||||||
|
content_html = models.TextField(help_text="Entry Orginal Content")
|
||||||
|
content_text = models.TextField(help_text="Entry Pure Text Content")
|
||||||
|
summary = models.TextField(help_text="Entry Summary", null=True, blank=True)
|
||||||
|
link = models.URLField(help_text="Link to Entry")
|
||||||
|
date = models.DateTimeField(help_text="Date of the entry")
|
||||||
|
entry_id = models.ForeignKey('Authors')
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
|
||||||
|
ordering = ['-date']
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize(self, data):
|
||||||
|
p = re.compile(r'<[^<]*?/?>')
|
||||||
|
return p.sub('', data)
|
||||||
|
|
||||||
|
class RunTime (models.Model):
|
||||||
|
run_time = models.DateTimeField(help_text="Run time of the planet script", auto_now=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
|
||||||
|
return self.run_time
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
|
||||||
|
ordering = ['-run_time']
|
||||||
|
|
||||||
|
def get_run_time(self):
|
||||||
|
|
||||||
|
dt = ".".join(map(lambda x: str(x), [self.run_time.day, self.run_time.month, self.run_time.year]))
|
||||||
|
hm = ":".join(map(lambda x: str(x), [self.run_time.hour, self.run_time.minute]))
|
||||||
|
|
||||||
|
rslt = " ".join([dt, hm])
|
||||||
|
return rslt
|
||||||
|
|
232
DJAGEN/branches/mustafa_branch/djagen/collector/views.py
Executable file
|
@ -0,0 +1,232 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# View definitions are created here.
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
from django.http import HttpResponse,HttpResponseRedirect
|
||||||
|
from djagen.collector.models import *
|
||||||
|
from djagen.collector.forms import ContactForm, QueryForm
|
||||||
|
from djagen.collector.wrappers import render_response
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils.datastructures import MultiValueDictKeyError
|
||||||
|
import magic
|
||||||
|
import os
|
||||||
|
import datetime, time
|
||||||
|
from django.core.paginator import Paginator, EmptyPage, InvalidPage
|
||||||
|
|
||||||
|
import string
|
||||||
|
|
||||||
|
BASE_URL = settings.BASE_URL
|
||||||
|
|
||||||
|
def main(request):
|
||||||
|
selected_entries = Entries.objects.select_related()
|
||||||
|
entries_list1 = selected_entries.filter(entry_id__label_personal = 1)
|
||||||
|
entries_list2 = selected_entries.filter(entry_id__label_lkd = 1)
|
||||||
|
entries_list3 = selected_entries.filter(entry_id__label_community = 1)
|
||||||
|
entries_list = entries_list1 | entries_list2 | entries_list3
|
||||||
|
|
||||||
|
# This setting gets the content truncated which contains more than <truncate_words> words.
|
||||||
|
truncate_words = 250
|
||||||
|
items_per_page = 25
|
||||||
|
|
||||||
|
#get the last run time
|
||||||
|
run_time = RunTime.objects.all()[0]
|
||||||
|
|
||||||
|
#get the last entries' date
|
||||||
|
last_entry_date = Entries.objects.all()[0].date
|
||||||
|
day = datetime.timedelta(days=1)
|
||||||
|
last_date_li = []
|
||||||
|
for x in xrange(6):
|
||||||
|
last_entry_date -= day
|
||||||
|
last_date_li.append(last_entry_date)
|
||||||
|
|
||||||
|
return render_to_response('main/main.html' ,{
|
||||||
|
'entries_list':entries_list,
|
||||||
|
'truncate_words':truncate_words,
|
||||||
|
'items_per_page':repr(items_per_page),
|
||||||
|
'run_time':run_time,
|
||||||
|
#'pag_entries_list':pag_entries_list,
|
||||||
|
'BASE_URL': BASE_URL,
|
||||||
|
'last_date_li': last_date_li,
|
||||||
|
})
|
||||||
|
def member_subscribe(request):
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = ContactForm(request.POST, request.FILES)
|
||||||
|
#return HttpResponse(str(request.FILES))
|
||||||
|
if form.is_valid():
|
||||||
|
human = True
|
||||||
|
try:
|
||||||
|
check = handle_uploaded_file(request.FILES['hackergotchi'])
|
||||||
|
except MultiValueDictKeyError:
|
||||||
|
check = (False, '')
|
||||||
|
|
||||||
|
#save the author information
|
||||||
|
if check[0]:
|
||||||
|
f = request.FILES['hackergotchi']
|
||||||
|
|
||||||
|
#change the name of the file with the unique name created
|
||||||
|
f.name = check[1]
|
||||||
|
|
||||||
|
author = Authors(author_name=request.POST['name'], author_surname=request.POST['surname'], author_email=request.POST['email'], channel_url=request.POST['feed'], author_face=f.name, is_approved=0, current_status=5)
|
||||||
|
else:
|
||||||
|
author = Authors(author_name=request.POST['name'], author_surname=request.POST['surname'], author_email=request.POST['email'], channel_url=request.POST['feed'], is_approved=0, current_status=5)
|
||||||
|
author.save()
|
||||||
|
|
||||||
|
#save the history with explanation
|
||||||
|
author.history_set.create(action_type=5, action_date=datetime.datetime.now(), action_explanation=request.POST['message'])
|
||||||
|
#send mail part
|
||||||
|
#fill it here
|
||||||
|
return render_response(request, 'main/subscribe.html/',{'submit': 'done', 'BASE_URL': BASE_URL})
|
||||||
|
else:
|
||||||
|
form = ContactForm()
|
||||||
|
return render_response(request, 'main/subscribe.html', {'form': form, 'BASE_URL': BASE_URL})
|
||||||
|
|
||||||
|
def handle_uploaded_file(f):
|
||||||
|
|
||||||
|
if not f.name: return False
|
||||||
|
#lets create a unique name for the image
|
||||||
|
t = str(time.time()).split(".")
|
||||||
|
img_name = t[0] + t[1].f.name.split(".")[1]
|
||||||
|
f.name = img_name
|
||||||
|
path = os.path.join(settings.FILE_UPLOAD_TEMP_DIR, f.name)
|
||||||
|
|
||||||
|
destination = open(path, 'wb+')
|
||||||
|
for chunk in f.chunks():
|
||||||
|
destination.write(chunk)
|
||||||
|
destination.close()
|
||||||
|
|
||||||
|
m = magic.open(magic.MAGIC_MIME)
|
||||||
|
m.load()
|
||||||
|
t = m.file(path)
|
||||||
|
if t.split('/')[0] == 'image':
|
||||||
|
return (True, f.name)
|
||||||
|
else:
|
||||||
|
os.unlink(path)
|
||||||
|
return (False, '')
|
||||||
|
|
||||||
|
def list_members(request):
|
||||||
|
|
||||||
|
authors = Authors.objects.all()
|
||||||
|
|
||||||
|
return render_response(request, 'main/members.html', {'members': authors, 'BASE_URL': BASE_URL})
|
||||||
|
|
||||||
|
def query(request):
|
||||||
|
|
||||||
|
return render_response(request,'main/query.html',{'BASE_URL' : BASE_URL})
|
||||||
|
|
||||||
|
def archive(request,archive_year='',archive_month='',archive_day=''):
|
||||||
|
|
||||||
|
# This setting gets the content truncated which contains more than <truncate_words> words.
|
||||||
|
truncate_words = 250
|
||||||
|
items_per_page = 25
|
||||||
|
|
||||||
|
#get the last run time
|
||||||
|
run_time = RunTime.objects.all()[0]
|
||||||
|
|
||||||
|
|
||||||
|
### Determine if the request includes any query or not. ###
|
||||||
|
try:
|
||||||
|
does_getPage_exists = request.GET['page']
|
||||||
|
except:
|
||||||
|
does_getPage_exists = None
|
||||||
|
|
||||||
|
if ( (request.GET) and ( not( does_getPage_exists) )):
|
||||||
|
# Switch to 'return the result of query' mode.
|
||||||
|
|
||||||
|
#Querying
|
||||||
|
#TODO: We should improve the querying method here.
|
||||||
|
if ( ('q_author_name' in request.GET) and (request.GET['q_author_name'] )):
|
||||||
|
for item in Authors.objects.filter(author_name__icontains = request.GET['q_author_name']):
|
||||||
|
try:
|
||||||
|
entries_list |= item.entries_set.all()
|
||||||
|
except:
|
||||||
|
entries_list = item.entries_set.all()
|
||||||
|
|
||||||
|
if (('q_author_surname' in request.GET) and (request.GET['q_author_surname'])):
|
||||||
|
for item in Authors.objects.filter(author_name__icontains = request.GET['q_author_surname']):
|
||||||
|
try:
|
||||||
|
entries_list |= item.entries_set.all()
|
||||||
|
except:
|
||||||
|
entries_list = item.entries_set.all()
|
||||||
|
|
||||||
|
if( ('q_text' in request.GET)and(request.GET['q_text'])):
|
||||||
|
try:
|
||||||
|
entries_list |= Entries.objects.filter(content_text__icontains = request.GET['q_text'])
|
||||||
|
except:
|
||||||
|
entries_list = Entries.objects.filter(content_text__icontains = request.GET['q_text'])
|
||||||
|
try:
|
||||||
|
if(not(entries_list)):
|
||||||
|
return HttpResponseRedirect(BASE_URL+"/query")
|
||||||
|
except:
|
||||||
|
return HttpResponseRedirect(BASE_URL+ "/query")
|
||||||
|
#here is gonna be edited [X]
|
||||||
|
return render_to_response('main/main.html' ,{
|
||||||
|
'entries_list':entries_list,
|
||||||
|
#'p_entries_list':p_entries_list,
|
||||||
|
'truncate_words':truncate_words,
|
||||||
|
'items_per_page':repr(items_per_page),
|
||||||
|
'run_time':run_time,
|
||||||
|
#'archive_year':archive_year,
|
||||||
|
#'archive_month':archive_month,
|
||||||
|
#'error':error,
|
||||||
|
'BASE_URL':BASE_URL,
|
||||||
|
})
|
||||||
|
### If not ###
|
||||||
|
else:
|
||||||
|
#Switch to return the result of arguments provided mode.
|
||||||
|
|
||||||
|
selected_entries = Entries.objects.select_related()
|
||||||
|
|
||||||
|
# For entry categories
|
||||||
|
entries_list1 = selected_entries.filter(entry_id__label_personal = 1)
|
||||||
|
entries_list2 = selected_entries.filter(entry_id__label_lkd = 1)
|
||||||
|
entries_list3 = selected_entries.filter(entry_id__label_community = 1)
|
||||||
|
entries_list = entries_list1 | entries_list2 | entries_list3
|
||||||
|
|
||||||
|
## Validating arguments provided by urls.py.
|
||||||
|
# Check if archive_year is not empty and numeric.
|
||||||
|
if((archive_year != '' ) and (str(archive_year).isalnum()) and (not(str(archive_year).isalpha()))):
|
||||||
|
entries_list = entries_list.filter(date__year=archive_year)
|
||||||
|
else:
|
||||||
|
# Fall back to main view.
|
||||||
|
return HttpResponseRedirect(BASE_URL+"/main")
|
||||||
|
|
||||||
|
# Check if archive_month is not empty and numeric.
|
||||||
|
if(archive_month != ''and (str(archive_month).isalnum()) and not(str(archive_month).isalpha())):
|
||||||
|
entries_list = entries_list.filter(date__month=archive_month)
|
||||||
|
|
||||||
|
# Check if archive_day is not empty and numeric.
|
||||||
|
if(archive_day != ''and (str(archive_day).isalnum()) and not(str(archive_day).isalpha())):
|
||||||
|
entries_list = entries_list.filter(date__day=archive_day)
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
# Pagination
|
||||||
|
elements_in_a_page = 25 # This determines, how many elements will be displayed in a paginator page.
|
||||||
|
paginator = Paginator(entries_list,elements_in_a_page)
|
||||||
|
|
||||||
|
# Validation for page number if it is not int return first page.
|
||||||
|
try:
|
||||||
|
page = int(request.GET.get('page', '1'))
|
||||||
|
except ValueError:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
# If page request is out of range, return last page .
|
||||||
|
try:
|
||||||
|
p_entries_list = paginator.page(page)
|
||||||
|
except (EmptyPage, InvalidPage):
|
||||||
|
p_entries_list = paginator.page(paginator.num_pages)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return render_to_response('main/archive.html' ,{
|
||||||
|
'entries_list':entries_list,
|
||||||
|
'p_entries_list':p_entries_list,
|
||||||
|
'truncate_words':truncate_words,
|
||||||
|
'items_per_page':repr(items_per_page),
|
||||||
|
'run_time':run_time,
|
||||||
|
'archive_year':archive_year,
|
||||||
|
'archive_month':archive_month,
|
||||||
|
#'error':error,
|
||||||
|
'BASE_URL':BASE_URL,
|
||||||
|
})
|
13
DJAGEN/branches/mustafa_branch/djagen/collector/wrappers.py
Executable file
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
from django.template import RequestContext
|
||||||
|
|
||||||
|
def render_response(req, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Wrapper function that automatically adds "context_instance" to render_to_response
|
||||||
|
"""
|
||||||
|
|
||||||
|
kwargs['context_instance'] = RequestContext(req)
|
||||||
|
return render_to_response(*args, **kwargs)
|
0
DJAGEN/branches/mustafa_branch/djagen/gezegen/__init__.py
Executable file
0
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/__init__.py
Executable file
61
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/atom.xml.tmpl
Executable file
|
@ -0,0 +1,61 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||||
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
|
||||||
|
<title><TMPL_VAR name></title>
|
||||||
|
<link rel="self" href="<TMPL_VAR feed ESCAPE="HTML">"/>
|
||||||
|
<link href="<TMPL_VAR link ESCAPE="HTML">"/>
|
||||||
|
<id><TMPL_VAR feed ESCAPE="HTML"></id>
|
||||||
|
<updated><TMPL_VAR date_iso></updated>
|
||||||
|
<generator uri="http://www.planetplanet.org/"><TMPL_VAR generator ESCAPE="HTML"></generator>
|
||||||
|
|
||||||
|
<TMPL_LOOP Items>
|
||||||
|
<entry<TMPL_IF channel_language> xml:lang="<TMPL_VAR channel_language>"</TMPL_IF>>
|
||||||
|
<title type="html"<TMPL_IF title_language> xml:lang="<TMPL_VAR title_language>"</TMPL_IF>><TMPL_VAR title ESCAPE="HTML"></title>
|
||||||
|
<link href="<TMPL_VAR link ESCAPE="HTML">"/>
|
||||||
|
<id><TMPL_VAR id ESCAPE="HTML"></id>
|
||||||
|
<updated><TMPL_VAR date_iso></updated>
|
||||||
|
<content type="html"<TMPL_IF content_language> xml:lang="<TMPL_VAR content_language>"</TMPL_IF>><TMPL_VAR content ESCAPE="HTML"></content>
|
||||||
|
<author>
|
||||||
|
<TMPL_IF author_name>
|
||||||
|
<name><TMPL_VAR author_name ESCAPE="HTML"></name>
|
||||||
|
<TMPL_IF author_email>
|
||||||
|
<email><TMPL_VAR author_email ESCAPE="HTML"></email>
|
||||||
|
</TMPL_IF author_email>
|
||||||
|
<TMPL_ELSE>
|
||||||
|
<TMPL_IF channel_author_name>
|
||||||
|
<name><TMPL_VAR channel_author_name ESCAPE="HTML"></name>
|
||||||
|
<TMPL_IF channel_author_email>
|
||||||
|
<email><TMPL_VAR channel_author_email ESCAPE="HTML"></email>
|
||||||
|
</TMPL_IF channel_author_email>
|
||||||
|
<TMPL_ELSE>
|
||||||
|
<name><TMPL_VAR channel_name ESCAPE="HTML"></name>
|
||||||
|
</TMPL_IF>
|
||||||
|
</TMPL_IF>
|
||||||
|
<uri><TMPL_VAR channel_link ESCAPE="HTML"></uri>
|
||||||
|
</author>
|
||||||
|
<source>
|
||||||
|
<TMPL_IF channel_title>
|
||||||
|
<title type="html"><TMPL_VAR channel_title ESCAPE="HTML"></title>
|
||||||
|
<TMPL_ELSE>
|
||||||
|
<title type="html"><TMPL_VAR channel_name ESCAPE="HTML"></title>
|
||||||
|
</TMPL_IF>
|
||||||
|
<TMPL_IF channel_subtitle>
|
||||||
|
<subtitle type="html"><TMPL_VAR channel_subtitle ESCAPE="HTML"></subtitle>
|
||||||
|
</TMPL_IF>
|
||||||
|
<link rel="self" href="<TMPL_VAR channel_url ESCAPE="HTML">"/>
|
||||||
|
<TMPL_IF channel_id>
|
||||||
|
<id><TMPL_VAR channel_id ESCAPE="HTML"></id>
|
||||||
|
<TMPL_ELSE>
|
||||||
|
<id><TMPL_VAR channel_url ESCAPE="HTML"></id>
|
||||||
|
</TMPL_IF>
|
||||||
|
<TMPL_IF channel_updated_iso>
|
||||||
|
<updated><TMPL_VAR channel_updated_iso></updated>
|
||||||
|
</TMPL_IF>
|
||||||
|
<TMPL_IF channel_rights>
|
||||||
|
<rights type="html"><TMPL_VAR channel_rights ESCAPE="HTML"></rights>
|
||||||
|
</TMPL_IF>
|
||||||
|
</source>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
</TMPL_LOOP>
|
||||||
|
</feed>
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/atom.xml.tmplc
Executable file
42
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/config.ini
Executable file
|
@ -0,0 +1,42 @@
|
||||||
|
[Planet]
|
||||||
|
name = Linux Gezegeni
|
||||||
|
link = http://gezegen.linux.org.tr
|
||||||
|
owner_name = Gezegen Ekibi
|
||||||
|
owner_email = gezegen@linux.org.tr
|
||||||
|
cache_directory = cache
|
||||||
|
new_feed_items = 1
|
||||||
|
log_level = DEBUG
|
||||||
|
template_files = gezegen/index.html.tmpl gezegen/rss20.xml.tmpl gezegen/rss10.xml.tmpl gezegen/opml.xml.tmpl gezegen/foafroll.xml.tmpl gezegen/sidebar.html.tmpl gezegen/simple.html.tmpl gezegen/feeds.html.tmpl gezegen/atom.xml.tmpl
|
||||||
|
output_dir = www/
|
||||||
|
# items_per_page = 15
|
||||||
|
items_per_page = 25
|
||||||
|
#days_per_page = 0
|
||||||
|
feed_timeout = 20
|
||||||
|
|
||||||
|
# future_dates = ignore_date
|
||||||
|
# ignore_in_feed = updated
|
||||||
|
|
||||||
|
encoding = utf-8
|
||||||
|
locale = tr_TR.UTF-8
|
||||||
|
|
||||||
|
date_format = %d %b %Y @ %I:%M %p
|
||||||
|
#date_format = %B %d, %Y %I:%M %p
|
||||||
|
new_date_format = %d %B %Y
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
facewidth = 64
|
||||||
|
faceheight = 64
|
||||||
|
|
||||||
|
|
||||||
|
[http://www.hakanuygun.com/blog/?feed=atom&cat=13]
|
||||||
|
name = Hakan Uygun
|
||||||
|
nick = huygun
|
||||||
|
label = Personal
|
||||||
|
id = 1
|
||||||
|
|
||||||
|
[http://feeds.feedburner.com/oguzy-gezegen]
|
||||||
|
name = Oğuz Yarımtepe
|
||||||
|
face = oguzyarimtepe.png
|
||||||
|
nick = oyarimtepe
|
||||||
|
label = Personal
|
||||||
|
id = 2
|
44
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/config_entries.ini
Executable file
|
@ -0,0 +1,44 @@
|
||||||
|
[Planet]
|
||||||
|
name = Linux Gezegeni
|
||||||
|
link = http://gezegen.linux.org.tr
|
||||||
|
label = Personal
|
||||||
|
id =
|
||||||
|
owner_name = Gezegen Ekibi
|
||||||
|
owner_email = gezegen@linux.org.tr
|
||||||
|
cache_directory = cache
|
||||||
|
new_feed_items = 1
|
||||||
|
log_level = DEBUG
|
||||||
|
template_files = gezegen/index.html.tmpl gezegen/rss20.xml.tmpl gezegen/rss10.xml.tmpl gezegen/opml.xml.tmpl gezegen/foafroll.xml.tmpl gezegen/sidebar.html.tmpl gezegen/simple.html.tmpl gezegen/feeds.html.tmpl gezegen/atom.xml.tmpl
|
||||||
|
output_dir = www/
|
||||||
|
# items_per_page = 15
|
||||||
|
items_per_page = 25
|
||||||
|
#days_per_page = 0
|
||||||
|
feed_timeout = 20
|
||||||
|
|
||||||
|
# future_dates = ignore_date
|
||||||
|
# ignore_in_feed = updated
|
||||||
|
|
||||||
|
encoding = utf-8
|
||||||
|
locale = tr_TR.UTF-8
|
||||||
|
|
||||||
|
date_format = %d %b %Y @ %I:%M %p
|
||||||
|
#date_format = %B %d, %Y %I:%M %p
|
||||||
|
new_date_format = %d %B %Y
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
facewidth = 64
|
||||||
|
faceheight = 64
|
||||||
|
|
||||||
|
|
||||||
|
[http://www.hakanuygun.com/blog/?feed=atom&cat=13]
|
||||||
|
name = Hakan Uygun
|
||||||
|
nick = huygun
|
||||||
|
label = Personal
|
||||||
|
id = 1
|
||||||
|
|
||||||
|
[http://feeds.feedburner.com/oguzy-gezegen]
|
||||||
|
name = Oğuz Yarımtepe
|
||||||
|
face = oguzyarimtepe.png
|
||||||
|
nick = oyarimtepe
|
||||||
|
label = Personal
|
||||||
|
id = 2
|
17
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/config_entries.xml
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
<planet>
|
||||||
|
<entry>
|
||||||
|
<feed>[http://www.bugunlinux.com/?feed=rss2]</feed>
|
||||||
|
<name>Ahmet Yıldız</name>
|
||||||
|
<nick>ayildiz</nick>
|
||||||
|
<label>Personal</label>
|
||||||
|
<id>1</id>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<feed>[http://www.bugunlinux.com/?feed=rss3]</feed>
|
||||||
|
<name>Ahmet Yıldızz</name>
|
||||||
|
<nick>ayildizz</nick>
|
||||||
|
<label>LKD</label>
|
||||||
|
<id>2</id>
|
||||||
|
</entry>
|
||||||
|
</planet>
|
28
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/config_header.xml
Executable file
|
@ -0,0 +1,28 @@
|
||||||
|
<planet>
|
||||||
|
<header>
|
||||||
|
<header_name>[Planet]</header_name>
|
||||||
|
<name>Linux Gezegeni</name>
|
||||||
|
<link>http://gezegen.linux.org.tr</link>
|
||||||
|
<owner_name>Gezegen Ekibi</owner_name>
|
||||||
|
<owner_email>gezegen@linux.org.tr</owner_email>
|
||||||
|
<cache_directory>cache</cache_directory>
|
||||||
|
<new_feed_items>1</new_feed_items>
|
||||||
|
<log_level>DEBUG</log_level>
|
||||||
|
<template_files>gezegen/index.html.tmpl gezegen/rss20.xml.tmpl gezegen/rss10.xml.tmpl gezegen/opml.xml.tmpl gezegen/foafroll.xml.tmpl gezegen/sidebar.html.tmpl gezegen/simple.html.tmpl gezegen/feeds.html.tmpl gezegen/atom.xml.tmpl</template_files>
|
||||||
|
<output_dir>www/</output_dir>
|
||||||
|
<items_per_page>25</items_per_page>
|
||||||
|
<feed_timeout>20</feed_timeout>
|
||||||
|
|
||||||
|
<encoding>utf-8</encoding>
|
||||||
|
<locale>tr_TR.UTF-8</locale>
|
||||||
|
|
||||||
|
<date_format>%d %b %Y @ %I:%M %p</date_format>
|
||||||
|
<new_date_format>new_date_format = %d %B %Y</new_date_format>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<header_name>[DEFAULT]</header_name>
|
||||||
|
<facewidth>64</facewidth>
|
||||||
|
<faceheight>64</faceheight>
|
||||||
|
</header>
|
||||||
|
</planet>
|
22
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/feeds.html.tmpl
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
<div class="bloggers section" id="bloggers">
|
||||||
|
<ul>
|
||||||
|
<TMPL_LOOP Channels>
|
||||||
|
<li<TMPL_IF nick> class="<TMPL_VAR nick>"</TMPL_IF>>
|
||||||
|
<div>
|
||||||
|
<TMPL_IF face>
|
||||||
|
<img class="head" src="images/heads/<TMPL_VAR face ESCAPE="HTML">" title="<TMPL_VAR face>" />
|
||||||
|
<TMPL_ELSE>
|
||||||
|
<img class="head" src="images/heads/nobody.png" title="<TMPL_VAR channel_name>" />
|
||||||
|
</TMPL_IF>
|
||||||
|
<div class="ircnick"> </div>
|
||||||
|
</div>
|
||||||
|
<a href="<TMPL_VAR url ESCAPE="HTML">" title="subscribe">
|
||||||
|
<img src="images/feed-icon-10x10.png" alt="(feed)">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<TMPL_IF link><a href="<TMPL_VAR link ESCAPE="HTML">" <TMPL_IF message>class="message" title="<TMPL_VAR message ESCAPE="HTML">"</TMPL_IF><TMPL_UNLESS message>title="<TMPL_VAR title_plain ESCAPE="HTML">"</TMPL_UNLESS>><TMPL_VAR name></a><TMPL_ELSE><span <TMPL_IF message>class="message" title="<TMPL_VAR message ESCAPE="HTML">"</TMPL_IF><TMPL_UNLESS message>title="<TMPL_VAR title_plain ESCAPE="HTML">"</TMPL_UNLESS>><TMPL_VAR name></span></TMPL_IF>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</TMPL_LOOP>
|
||||||
|
</ul>
|
||||||
|
</div>
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/feeds.html.tmplc
Executable file
31
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/foafroll.xml.tmpl
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<rdf:RDF
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||||
|
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||||
|
xmlns:rss="http://purl.org/rss/1.0/"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
>
|
||||||
|
<foaf:Group>
|
||||||
|
<foaf:name><TMPL_VAR name></foaf:name>
|
||||||
|
<foaf:homepage><TMPL_VAR link ESCAPE="HTML"></foaf:homepage>
|
||||||
|
<rdfs:seeAlso rdf:resource="<TMPL_VAR uri ESCAPE="HTML">" />
|
||||||
|
|
||||||
|
<TMPL_LOOP Channels>
|
||||||
|
<foaf:member>
|
||||||
|
<foaf:Agent>
|
||||||
|
<foaf:name><TMPL_VAR name></foaf:name>
|
||||||
|
<foaf:weblog>
|
||||||
|
<foaf:Document rdf:about="<TMPL_VAR link ESCAPE="HTML">">
|
||||||
|
<dc:title><TMPL_VAR title></dc:title>
|
||||||
|
<rdfs:seeAlso>
|
||||||
|
<rss:channel rdf:about="<TMPL_VAR uri ESCAPE="HTML">" />
|
||||||
|
</rdfs:seeAlso>
|
||||||
|
</foaf:Document>
|
||||||
|
</foaf:weblog>
|
||||||
|
</foaf:Agent>
|
||||||
|
</foaf:member>
|
||||||
|
</TMPL_LOOP>
|
||||||
|
|
||||||
|
</foaf:Group>
|
||||||
|
</rdf:RDF>
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/foafroll.xml.tmplc
Executable file
356
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/index.html.tmpl
Executable file
|
@ -0,0 +1,356 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml/transitional.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><TMPL_VAR name></title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<link rel="stylesheet" href="generic.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="layout.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="planet.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="bloggers.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="nlayout.css" type="text/css" />
|
||||||
|
<link rel="icon" type="image/png" href="images/planet.png" />
|
||||||
|
<link rel="alternate" type="application/rss+xml" title="<TMPL_VAR name>" href="rss20.xml" />
|
||||||
|
|
||||||
|
<script type="text/javascript" src="jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="jquery.cookie.min.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function toggleFeeds() {
|
||||||
|
$("#feedlist").slideToggle(function() {
|
||||||
|
$("#feeds h3").toggleClass("open");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$(document).ready(function() {
|
||||||
|
//$.cookie("hidden", null, {expires: -1});
|
||||||
|
var hidden = $.cookie("hidden") ? $.cookie("hidden").split(",") : new Array();
|
||||||
|
// hide posts by disabled folks
|
||||||
|
if (hidden.length > 0) for (i in hidden) {
|
||||||
|
$("div." + hidden[i]).hide();
|
||||||
|
}
|
||||||
|
$("#feeds h3 a").click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if ($("#feedlist ul").length == 0) {
|
||||||
|
$("#feedlist").load('feeds.html', '', function(r,t,x) {
|
||||||
|
$('<input type="checkbox" checked>')
|
||||||
|
.change(function() {
|
||||||
|
var nick = $(this).parent().attr("class");
|
||||||
|
var i = $.inArray(nick, hidden);
|
||||||
|
if ($(this).attr("checked")) {
|
||||||
|
if (hidden[i]) hidden.splice(i, 1);
|
||||||
|
$("div." + nick).show();
|
||||||
|
} else {
|
||||||
|
if (!hidden[i]) hidden[hidden.length] = nick;
|
||||||
|
$("div." + nick).hide();
|
||||||
|
}
|
||||||
|
$.cookie("hidden", hidden.join(","),
|
||||||
|
{expires: 365});
|
||||||
|
})
|
||||||
|
.prependTo("#feedlist ul li");
|
||||||
|
// remove checks from disabled folks
|
||||||
|
for (i in hidden) {
|
||||||
|
$("#feedlist ul li." + hidden[i] + " input")
|
||||||
|
.removeAttr("checked");
|
||||||
|
}
|
||||||
|
toggleFeeds();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toggleFeeds();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="gezegen-sites">
|
||||||
|
<ul>
|
||||||
|
<li class="home"><a href="http://gezegen.linux.org.tr/" title="Gezegen">Gezegen</a><a href="http://haber.linux.org.tr" title="Linux Haber">Linux Haber</a></li>
|
||||||
|
<li><a href="http://gezegen.linux.org.tr/kisisel/" title="Kişisel Günlükler">Kişisel Günlükler</a></li>
|
||||||
|
<li><a href="http://gezegen.linux.org.tr/lkd/" title="LKD Günlükleri">LKD Günlükleri</a></li>
|
||||||
|
<li><a href="http://gezegen.linux.org.tr/topluluk/" title="Topluluk Günlükleri">Topluluk Günlükleri</a></li>
|
||||||
|
<li><a href="http://planet.linux.org.tr/" title="İngilizce Günlükler">İngilizce Günlükler</a></li>
|
||||||
|
</div> <!-- /gezegen-sites -->
|
||||||
|
|
||||||
|
<div id="hdr">
|
||||||
|
<div id="banner">
|
||||||
|
<img src="<TMPL_VAR link>/images/spacer.png" />
|
||||||
|
</div>
|
||||||
|
<div id="logo">
|
||||||
|
<a href="<TMPL_VAR link>/">
|
||||||
|
<img src="<TMPL_VAR link>/images/spacer.png" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="body">
|
||||||
|
<TMPL_LOOP Items>
|
||||||
|
<TMPL_IF new_date>
|
||||||
|
<h2 class="date"><TMPL_VAR new_date></h2>
|
||||||
|
</TMPL_IF>
|
||||||
|
|
||||||
|
<div class="entry <TMPL_IF channel_nick><TMPL_VAR channel_nick></TMPL_IF>">
|
||||||
|
<div class="person-info">
|
||||||
|
<a href="<TMPL_VAR channel_link ESCAPE="HTML">" title="<TMPL_VAR channel_title ESCAPE="HTML">">
|
||||||
|
<TMPL_IF channel_face>
|
||||||
|
<img class="face" src="images/heads/<TMPL_VAR channel_face ESCAPE="HTML">" title="<TMPL_VAR channel_name>" />
|
||||||
|
<br />
|
||||||
|
<TMPL_ELSE>
|
||||||
|
<img class="face" src="images/heads/nobody.png" title="<TMPL_VAR channel_name>" />
|
||||||
|
<br />
|
||||||
|
</TMPL_IF>
|
||||||
|
<TMPL_VAR channel_name>
|
||||||
|
<TMPL_IF channel_nick>
|
||||||
|
<br />
|
||||||
|
<!-- (<TMPL_VAR channel_nick>) -->
|
||||||
|
</TMPL_IF>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="post">
|
||||||
|
<div class="post2">
|
||||||
|
<div class="post-header">
|
||||||
|
<TMPL_IF title>
|
||||||
|
<h4 class="post-title">
|
||||||
|
<a href="<TMPL_VAR link ESCAPE="HTML">">
|
||||||
|
<TMPL_VAR title>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
<TMPL_ELSE>
|
||||||
|
<div class="post-title">
|
||||||
|
<span> </span>
|
||||||
|
</div>
|
||||||
|
</TMPL_IF>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="post-contents">
|
||||||
|
<TMPL_VAR content>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<div id="post-links" style="text-align: center;">
|
||||||
|
<TMPL_IF comments>
|
||||||
|
<a href="<TMPL_VAR comments ESCAPE="HTML">">
|
||||||
|
<img src="images/yorum.png" border="0" title="Yorumlar" />
|
||||||
|
</a>
|
||||||
|
</TMPL_IF>
|
||||||
|
<a href="http://del.icio.us/post?url=<TMPL_VAR link ESCAPE="HTML">&title=<TMPL_VAR title ESCAPE="HTML">" target="_blank">
|
||||||
|
<img src="images/delicious.png" border="0" title="del.icio.us'a gönder" />
|
||||||
|
</a>
|
||||||
|
<a href="http://technorati.com/search/<TMPL_VAR link ESCAPE="HTML">" target="_blank">
|
||||||
|
<img src="images/technorati.png" border="0" title="technorati'de ara" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="post-footer">
|
||||||
|
<p>
|
||||||
|
<a href="<TMPL_VAR link ESCAPE="HTML">">
|
||||||
|
<TMPL_VAR date>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TMPL_LOOP>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="sidebar">
|
||||||
|
<div class="section">
|
||||||
|
<h3>Gezegen Hakkında</h3>
|
||||||
|
<p>
|
||||||
|
Linux Gezegeni, Türkiye'de Linux ve Özgür Yazılım konusunda çalışmalar yapan arkadaşlarımızın internet üzerindeki günlüklerini bir tek sayfadan okumamızı ve kendi dünyalarına ulaşmamızı sağlayan basit bir web sitesidir.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Gezegen kurallarına ulaşmak için <a href="http://gunluk.lkd.org.tr/gezegen/2008/10/27/gezegene-inis-ve-gezegen-kurallari/" title="Linux Gezegeni kurallarını okumak için tıklayın.." target="_blank">tıklayın</a>..
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Gezegeni <a href="http://www.planetplanet.org/">Planet</a> ile oluşturuyoruz, tasarım <a href="http://www.actsofvolition.com/">Steven Garrity</a>'nin eseri.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
|
||||||
|
<!--/* OpenX Javascript Tag v2.6.3 */-->
|
||||||
|
|
||||||
|
<!--/*
|
||||||
|
* The backup image section of this tag has been generated for use on a
|
||||||
|
* non-SSL page. If this tag is to be placed on an SSL page, change the
|
||||||
|
* 'http://reklam.lkd.org.tr/www/delivery/...'
|
||||||
|
* to
|
||||||
|
* 'https://reklam.lkd.org.tr/www/delivery/...'
|
||||||
|
*
|
||||||
|
* This noscript section of this tag only shows image banners. There
|
||||||
|
* is no width or height in these banners, so if you want these tags to
|
||||||
|
* allocate space for the ad before it shows, you will need to add this
|
||||||
|
* information to the <img> tag.
|
||||||
|
*
|
||||||
|
* If you do not want to deal with the intricities of the noscript
|
||||||
|
* section, delete the tag (from <noscript>... to </noscript>). On
|
||||||
|
* average, the noscript tag is called from less than 1% of internet
|
||||||
|
* users.
|
||||||
|
*/-->
|
||||||
|
|
||||||
|
<script type='text/javascript'><!--//<![CDATA[
|
||||||
|
var m3_u =
|
||||||
|
(location.protocol=='https:'?'https://reklam.lkd.org.tr/www/delivery/ajs.php':'http://reklam.lkd.org.tr/www/delivery/ajs.php');
|
||||||
|
var m3_r = Math.floor(Math.random()*99999999999);
|
||||||
|
if (!document.MAX_used) document.MAX_used = ',';
|
||||||
|
document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
|
||||||
|
document.write ("?zoneid=2");
|
||||||
|
document.write ('&cb=' + m3_r);
|
||||||
|
if (document.MAX_used != ',') document.write ("&exclude=" + document.MAX_used);
|
||||||
|
document.write (document.charset ? '&charset='+document.charset :
|
||||||
|
(document.characterSet ? '&charset='+document.characterSet : ''));
|
||||||
|
document.write ("&loc=" + escape(window.location));
|
||||||
|
if (document.referrer) document.write ("&referer=" + escape(document.referrer));
|
||||||
|
if (document.context) document.write ("&context=" + escape(document.context));
|
||||||
|
if (document.mmm_fo) document.write ("&mmm_fo=1");
|
||||||
|
document.write ("'><\/scr"+"ipt>");
|
||||||
|
//]]>--></script><noscript><a
|
||||||
|
href='http://reklam.lkd.org.tr/www/delivery/ck.php?n=a26e76e1&cb=INSERT_RANDOM_NUMBER_HERE'
|
||||||
|
target='_blank'><img
|
||||||
|
src='http://reklam.lkd.org.tr/www/delivery/avw.php?zoneid=2&cb=INSERT_RANDOM_NUMBER_HERE&n=a26e76e1'
|
||||||
|
border='0' alt='' /></a></noscript>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
|
||||||
|
<!--/* OpenX Javascript Tag v2.6.3 */-->
|
||||||
|
|
||||||
|
<!--/*
|
||||||
|
* The backup image section of this tag has been generated for use on a
|
||||||
|
* non-SSL page. If this tag is to be placed on an SSL page, change the
|
||||||
|
* 'http://reklam.lkd.org.tr/www/delivery/...'
|
||||||
|
* to
|
||||||
|
* 'https://reklam.lkd.org.tr/www/delivery/...'
|
||||||
|
*
|
||||||
|
* This noscript section of this tag only shows image banners. There
|
||||||
|
* is no width or height in these banners, so if you want these tags to
|
||||||
|
* allocate space for the ad before it shows, you will need to add this
|
||||||
|
* information to the <img> tag.
|
||||||
|
*
|
||||||
|
* If you do not want to deal with the intricities of the noscript
|
||||||
|
* section, delete the tag (from <noscript>... to </noscript>). On
|
||||||
|
* average, the noscript tag is called from less than 1% of internet
|
||||||
|
* users.
|
||||||
|
*/-->
|
||||||
|
|
||||||
|
<script type='text/javascript'><!--//<![CDATA[
|
||||||
|
var m3_u =
|
||||||
|
(location.protocol=='https:'?'https://reklam.lkd.org.tr/www/delivery/ajs.php':'http://reklam.lkd.org.tr/www/delivery/ajs.php');
|
||||||
|
var m3_r = Math.floor(Math.random()*99999999999);
|
||||||
|
if (!document.MAX_used) document.MAX_used = ',';
|
||||||
|
document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
|
||||||
|
document.write ("?zoneid=6");
|
||||||
|
document.write ('&cb=' + m3_r);
|
||||||
|
if (document.MAX_used != ',') document.write ("&exclude=" +
|
||||||
|
document.MAX_used);
|
||||||
|
document.write (document.charset ? '&charset='+document.charset :
|
||||||
|
(document.characterSet ? '&charset='+document.characterSet : ''));
|
||||||
|
document.write ("&loc=" + escape(window.location));
|
||||||
|
if (document.referrer) document.write ("&referer=" +
|
||||||
|
escape(document.referrer));
|
||||||
|
if (document.context) document.write ("&context=" +
|
||||||
|
escape(document.context));
|
||||||
|
if (document.mmm_fo) document.write ("&mmm_fo=1");
|
||||||
|
document.write ("'><\/scr"+"ipt>");
|
||||||
|
//]]>--></script><noscript><a
|
||||||
|
href='http://reklam.lkd.org.tr/www/delivery/ck.php?n=ae096af2&cb=INSERT_RANDOM_NUMBER_HERE'
|
||||||
|
target='_blank'><img
|
||||||
|
src='http://reklam.lkd.org.tr/www/delivery/avw.php?zoneid=6&cb=INSERT_RANDOM_NUMBER_HERE&n=ae096af2'
|
||||||
|
border='0' alt='' /></a></noscript>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="feeds">
|
||||||
|
<h3><a href="feeds.html">Gezegen Sakinleri</a></h3>
|
||||||
|
<div id="feedlist"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h3>Takip edin</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="rss20.xml">RSS 2.0</a></li>
|
||||||
|
<li><a href="rss10.xml">RSS 1.0</a></li>
|
||||||
|
<li><a href="atom.xml">ATOM</a></li>
|
||||||
|
<li><a href="foafroll.xml">FOAF</a></li>
|
||||||
|
<li><a href="opml.xml">OPML</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h3>Diğer Gezegenler</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="http://gezegen.pardus.org.tr/">Pardus</a></li>
|
||||||
|
<li><a href="http://www.kernelplanet.org/">Kernel</a></li>
|
||||||
|
<li><a href="http://www.planetkde.org/">KDE</a></li>
|
||||||
|
<li><a href="http://planet.gnome.org">Gnome</a></li>
|
||||||
|
<li><a href="http://www.planetsuse.org/">SuSE</a></li>
|
||||||
|
<li><a href="http://planet.python.org">Python</a></li>
|
||||||
|
<li><a href="http://planet.gentoo.org">Gentoo</a></li>
|
||||||
|
<li><a href="http://www.go-mono.com/monologue/">MONOlogue</a></li>
|
||||||
|
<li><a href="http://planetjava.org">Java</a></li>
|
||||||
|
<li><a href="http://planet.lisp.org">LISP</a></li>
|
||||||
|
<li><a href="http://planet.perl.org">Perl</a></li>
|
||||||
|
<li><a href="http://fedoraproject.org/people/">Fedora</a></li>
|
||||||
|
<li><a href="http://planet.ubuntu.com/">Ubuntu</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h3>Güncelleme</h3>
|
||||||
|
<p>Gezegen her 10 dakikada bir yenilenir.</p>
|
||||||
|
<p>
|
||||||
|
Son güncelleme:
|
||||||
|
<br />
|
||||||
|
<TMPL_VAR date>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h3>İletişim</h3>
|
||||||
|
<p>
|
||||||
|
Linux Gezegeni <a href="mailto:gezegen [at] linux.org.tr">Gezegen Ekibi</a> tarafından yönetilmektedir, Gezegen hakkındaki sorularınızı ve Gezegen'e iniş başvurularınızı e-posta ile iletebilirsiniz.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Gezegene iniş başvurularınızda <a href="http://gunluk.lkd.org.tr/gezegen/2008/10/27/gezegene-inis-ve-gezegen-kurallari/" title="Linux Gezegeni kurallarını okumak için tıklayın.." target="_blank">Gezegen Kuralları</a>'na uyan RSS/Atom beslemenizi ve gezegen içerisinde kullanmak istediğiniz (en fazla 80x80 çözünürlüklü) resminizi (bir başka deyişle hackergotchi); varsa jabber adresinizle birlikte e-posta yoluyla göndermenizi rica ediyoruz.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
|
||||||
|
<div id="copyright">
|
||||||
|
Bu sayfa içerisinde yazılanlar doğru veya yanlış herhangi bir biçimde <a href="http://www.lkd.org.tr/">Linux Kullanıcıları Derneği</a>'ni bağlamaz.
|
||||||
|
<br />
|
||||||
|
LKD yalnızca Linux Gezegeni için teknik olanakları (sunucu, yazılım, bant genişliği) sağlar.
|
||||||
|
<br />
|
||||||
|
Ayrıca Gezegen istatistiklere <a href="<TMPL_VAR link>/stats">buradan</a> ulaşabilirsiniz.
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Start of Google Analytics Code -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||||
|
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var pageTracker = _gat._getTracker("UA-7686552-1");
|
||||||
|
pageTracker._trackPageview();
|
||||||
|
} catch(err) {}</script>
|
||||||
|
<!-- End of Google Analytics Code -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/index.html.tmplc
Executable file
16
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/opml.xml.tmpl
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<opml version="1.1">
|
||||||
|
<head>
|
||||||
|
<title><TMPL_VAR name></title>
|
||||||
|
<dateCreated><TMPL_VAR date_822></dateCreated>
|
||||||
|
<dateModified><TMPL_VAR date_822></dateModified>
|
||||||
|
<ownerName><TMPL_VAR owner_name></ownerName>
|
||||||
|
<ownerEmail><TMPL_VAR owner_email></ownerEmail>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<TMPL_LOOP Channels>
|
||||||
|
<outline text="<TMPL_VAR name ESCAPE="HTML">" xmlUrl="<TMPL_VAR uri ESCAPE="HTML">"/>
|
||||||
|
</TMPL_LOOP>
|
||||||
|
</body>
|
||||||
|
</opml>
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/opml.xml.tmplc
Executable file
37
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/rss10.xml.tmpl
Executable file
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<rdf:RDF
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||||
|
xmlns:content="http://purl.org/rss/1.0/modules/content/"
|
||||||
|
xmlns="http://purl.org/rss/1.0/"
|
||||||
|
>
|
||||||
|
<channel rdf:about="<TMPL_VAR link ESCAPE="HTML">">
|
||||||
|
<title><TMPL_VAR name></title>
|
||||||
|
<link><TMPL_VAR link ESCAPE="HTML"></link>
|
||||||
|
<description><TMPL_VAR name> - <TMPL_VAR link ESCAPE="HTML"></description>
|
||||||
|
|
||||||
|
<items>
|
||||||
|
<rdf:Seq>
|
||||||
|
<TMPL_LOOP Items>
|
||||||
|
<rdf:li rdf:resource="<TMPL_VAR id ESCAPE="HTML">" />
|
||||||
|
</TMPL_LOOP>
|
||||||
|
</rdf:Seq>
|
||||||
|
</items>
|
||||||
|
</channel>
|
||||||
|
|
||||||
|
<TMPL_LOOP Items>
|
||||||
|
<item rdf:about="<TMPL_VAR id ESCAPE="HTML">">
|
||||||
|
<title><TMPL_VAR channel_name><TMPL_IF title>: <TMPL_VAR title></TMPL_IF></title>
|
||||||
|
<link><TMPL_VAR link ESCAPE="HTML"></link>
|
||||||
|
<TMPL_IF content>
|
||||||
|
<content:encoded><TMPL_VAR content ESCAPE="HTML"></content:encoded>
|
||||||
|
</TMPL_IF>
|
||||||
|
<dc:date><TMPL_VAR date_822></dc:date>
|
||||||
|
<TMPL_IF creator>
|
||||||
|
<dc:creator><TMPL_VAR creator></dc:creator>
|
||||||
|
</TMPL_IF>
|
||||||
|
</item>
|
||||||
|
</TMPL_LOOP>
|
||||||
|
|
||||||
|
</rdf:RDF>
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/rss10.xml.tmplc
Executable file
30
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/rss20.xml.tmpl
Executable file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
|
||||||
|
<channel>
|
||||||
|
<title><TMPL_VAR name></title>
|
||||||
|
<link><TMPL_VAR link ESCAPE="HTML"></link>
|
||||||
|
<language>en</language>
|
||||||
|
<description><TMPL_VAR name> - <TMPL_VAR link ESCAPE="HTML"></description>
|
||||||
|
|
||||||
|
<TMPL_LOOP Items>
|
||||||
|
<item>
|
||||||
|
<title><TMPL_VAR channel_name><TMPL_IF title>: <TMPL_VAR title></TMPL_IF></title>
|
||||||
|
<guid><TMPL_VAR id ESCAPE="HTML"></guid>
|
||||||
|
<link><TMPL_VAR link ESCAPE="HTML"></link>
|
||||||
|
<TMPL_IF content>
|
||||||
|
<description>
|
||||||
|
<TMPL_IF channel_face>
|
||||||
|
<![CDATA[<img src="http://gezegen.linux.org.tr/images/heads/<TMPL_VAR channel_face ESCAPE="HTML">" align="right" width="<TMPL_VAR channel_facewidth ESCAPE="HTML">" height="<TMPL_VAR channel_height ESCAPE="HTML">">]]>
|
||||||
|
</TMPL_IF>
|
||||||
|
<TMPL_VAR content ESCAPE="HTML"></description>
|
||||||
|
</TMPL_IF>
|
||||||
|
<pubDate><TMPL_VAR date_822></pubDate>
|
||||||
|
<TMPL_IF creator>
|
||||||
|
<dc:creator><TMPL_VAR creator></dc:creator>
|
||||||
|
</TMPL_IF>
|
||||||
|
</item>
|
||||||
|
</TMPL_LOOP>
|
||||||
|
|
||||||
|
</channel>
|
||||||
|
</rss>
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/rss20.xml.tmplc
Executable file
17
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/sidebar.html.tmpl
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
<ul>
|
||||||
|
<TMPL_LOOP Items>
|
||||||
|
### we can only show items that have titles in this feed
|
||||||
|
<TMPL_IF title>
|
||||||
|
<li>
|
||||||
|
<a href="<TMPL_VAR link ESCAPE="HTML">">
|
||||||
|
<TMPL_IF channel_face>
|
||||||
|
<img src="<TMPL_VAR channel_face ESCAPE="HTML">" height="24" alt="">
|
||||||
|
<TMPL_ELSE>
|
||||||
|
<img src="http://planet.gnome.org/heads/nobody.png" height="24" alt="">
|
||||||
|
</TMPL_IF>
|
||||||
|
<TMPL_VAR title></a>
|
||||||
|
</li>
|
||||||
|
</TMPL_IF>
|
||||||
|
|
||||||
|
</TMPL_LOOP>
|
||||||
|
</ul>
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/sidebar.html.tmplc
Executable file
74
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/simple.html.tmpl
Executable file
|
@ -0,0 +1,74 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
<html>
|
||||||
|
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title><TMPL_VAR name></title>
|
||||||
|
<link rel="stylesheet" href="http://planet.gnome.org/generic.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="http://planet.gnome.org/planet.css" type="text/css">
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" href="http://planet.gnome.org/img/gnome-16.png">
|
||||||
|
<link rel="SHORTCUT ICON" type="image/png" href="http://planet.gnome.org/img/gnome-16.png">
|
||||||
|
<link rel="alternate" type="application/atom+xml" title="Planet GNOME Atom 1.0" href="http://planet.gnome.org/atom.xml">
|
||||||
|
<link rel="alternate" type="application/rss+xml" title="Planet GNOME RSS 2.0" href="http://planet.gnome.org/rss20.xml">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
<TMPL_LOOP Items>
|
||||||
|
<TMPL_IF new_date>
|
||||||
|
<h2 class="date"><TMPL_VAR new_date></h2>
|
||||||
|
</TMPL_IF>
|
||||||
|
|
||||||
|
<div class="entry<TMPL_IF channel_nick> <TMPL_VAR channel_nick></TMPL_IF>">
|
||||||
|
|
||||||
|
<div class="person-info">
|
||||||
|
<a href="<TMPL_VAR channel_link ESCAPE="HTML">" title="<TMPL_VAR channel_title ESCAPE="HTML">">
|
||||||
|
<TMPL_IF channel_face>
|
||||||
|
<img class="face" src="<TMPL_VAR channel_face ESCAPE="HTML">" alt=""><br />
|
||||||
|
<TMPL_ELSE>
|
||||||
|
<img class="face" src="http://planet.gnome.org/heads/nobody.png" alt=""><br />
|
||||||
|
</TMPL_IF>
|
||||||
|
<TMPL_VAR channel_name>
|
||||||
|
<TMPL_IF channel_real><br /><TMPL_VAR channel_real></TMPL_IF>
|
||||||
|
<TMPL_IF channel_nick><br />(<TMPL_VAR channel_nick>)</TMPL_IF>
|
||||||
|
</a>
|
||||||
|
<TMPL_IF channel_gsoc2008>
|
||||||
|
<p><a href="http://www.gnome.org/projects/soc/" title="Google Summer of Code 2008"><img src="http://planet.gnome.org/heads/logos/gsoc2008.png" alt="GSoC 2007" /></a></p>
|
||||||
|
</TMPL_IF>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="post">
|
||||||
|
<div class="post-header">
|
||||||
|
<TMPL_IF title>
|
||||||
|
<h3 class="post-title"><a href="<TMPL_VAR link ESCAPE="HTML">"><TMPL_VAR title></a></h3>
|
||||||
|
</TMPL_IF>
|
||||||
|
</div>
|
||||||
|
<div class="post-contents">
|
||||||
|
<TMPL_VAR content>
|
||||||
|
</div>
|
||||||
|
<div class="post-footer">
|
||||||
|
<p><a href="<TMPL_VAR link ESCAPE="HTML">"><TMPL_VAR date></a></p>
|
||||||
|
</div>
|
||||||
|
<b class="vt lt"></b><b class="vt rt"></b><b class="hz to"></b><b class="hz bo"></b>
|
||||||
|
<b class="cr tl"></b><b class="cr tr"></b><b class="cr bl"></b><b class="cr br"></b>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</TMPL_LOOP>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="copyright">
|
||||||
|
Copyright © 2003-2007, <a href="http://www.gnome.org/">The GNOME Project</a><br />
|
||||||
|
Blog entries aggregated on this page are owned by, and represent the opinion of the author.<br />
|
||||||
|
<a href="http://validator.w3.org/check/referer">Optimised</a> for <a href="http://www.w3.org/">standards</a>. Hosted by <a href="http://redhat.com/">Red Hat</a>.<br />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/simple.html.tmplc
Executable file
6
DJAGEN/branches/mustafa_branch/djagen/gezegen/gezegen/zaman.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
while read x
|
||||||
|
do
|
||||||
|
echo "$(date)::$x"
|
||||||
|
done
|
194
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet-cache.py
Executable file
|
@ -0,0 +1,194 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: UTF-8 -*-
|
||||||
|
"""Planet cache tool.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__authors__ = [ "Scott James Remnant <scott@netsplit.com>",
|
||||||
|
"Jeff Waugh <jdub@perkypants.org>" ]
|
||||||
|
__license__ = "Python"
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import dbhash
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
import planet
|
||||||
|
|
||||||
|
|
||||||
|
def usage():
|
||||||
|
print "Usage: planet-cache [options] CACHEFILE [ITEMID]..."
|
||||||
|
print
|
||||||
|
print "Examine and modify information in the Planet cache."
|
||||||
|
print
|
||||||
|
print "Channel Commands:"
|
||||||
|
print " -C, --channel Display known information on the channel"
|
||||||
|
print " -L, --list List items in the channel"
|
||||||
|
print " -K, --keys List all keys found in channel items"
|
||||||
|
print
|
||||||
|
print "Item Commands (need ITEMID):"
|
||||||
|
print " -I, --item Display known information about the item(s)"
|
||||||
|
print " -H, --hide Mark the item(s) as hidden"
|
||||||
|
print " -U, --unhide Mark the item(s) as not hidden"
|
||||||
|
print
|
||||||
|
print "Other Options:"
|
||||||
|
print " -h, --help Display this help message and exit"
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def usage_error(msg, *args):
|
||||||
|
print >>sys.stderr, msg, " ".join(args)
|
||||||
|
print >>sys.stderr, "Perhaps you need --help ?"
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def print_keys(item, title):
|
||||||
|
keys = item.keys()
|
||||||
|
keys.sort()
|
||||||
|
key_len = max([ len(k) for k in keys ])
|
||||||
|
|
||||||
|
print title + ":"
|
||||||
|
for key in keys:
|
||||||
|
if item.key_type(key) == item.DATE:
|
||||||
|
value = time.strftime(planet.TIMEFMT_ISO, item[key])
|
||||||
|
else:
|
||||||
|
value = str(item[key])
|
||||||
|
print " %-*s %s" % (key_len, key, fit_str(value, 74 - key_len))
|
||||||
|
|
||||||
|
def fit_str(string, length):
|
||||||
|
if len(string) <= length:
|
||||||
|
return string
|
||||||
|
else:
|
||||||
|
return string[:length-4] + " ..."
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cache_file = None
|
||||||
|
want_ids = 0
|
||||||
|
ids = []
|
||||||
|
|
||||||
|
command = None
|
||||||
|
|
||||||
|
for arg in sys.argv[1:]:
|
||||||
|
if arg == "-h" or arg == "--help":
|
||||||
|
usage()
|
||||||
|
elif arg == "-C" or arg == "--channel":
|
||||||
|
if command is not None:
|
||||||
|
usage_error("Only one command option may be supplied")
|
||||||
|
command = "channel"
|
||||||
|
elif arg == "-L" or arg == "--list":
|
||||||
|
if command is not None:
|
||||||
|
usage_error("Only one command option may be supplied")
|
||||||
|
command = "list"
|
||||||
|
elif arg == "-K" or arg == "--keys":
|
||||||
|
if command is not None:
|
||||||
|
usage_error("Only one command option may be supplied")
|
||||||
|
command = "keys"
|
||||||
|
elif arg == "-I" or arg == "--item":
|
||||||
|
if command is not None:
|
||||||
|
usage_error("Only one command option may be supplied")
|
||||||
|
command = "item"
|
||||||
|
want_ids = 1
|
||||||
|
elif arg == "-H" or arg == "--hide":
|
||||||
|
if command is not None:
|
||||||
|
usage_error("Only one command option may be supplied")
|
||||||
|
command = "hide"
|
||||||
|
want_ids = 1
|
||||||
|
elif arg == "-U" or arg == "--unhide":
|
||||||
|
if command is not None:
|
||||||
|
usage_error("Only one command option may be supplied")
|
||||||
|
command = "unhide"
|
||||||
|
want_ids = 1
|
||||||
|
elif arg.startswith("-"):
|
||||||
|
usage_error("Unknown option:", arg)
|
||||||
|
else:
|
||||||
|
if cache_file is None:
|
||||||
|
cache_file = arg
|
||||||
|
elif want_ids:
|
||||||
|
ids.append(arg)
|
||||||
|
else:
|
||||||
|
usage_error("Unexpected extra argument:", arg)
|
||||||
|
|
||||||
|
if cache_file is None:
|
||||||
|
usage_error("Missing expected cache filename")
|
||||||
|
elif want_ids and not len(ids):
|
||||||
|
usage_error("Missing expected entry ids")
|
||||||
|
|
||||||
|
# Open the cache file directly to get the URL it represents
|
||||||
|
try:
|
||||||
|
db = dbhash.open(cache_file)
|
||||||
|
url = db["url"]
|
||||||
|
db.close()
|
||||||
|
except dbhash.bsddb._db.DBError, e:
|
||||||
|
print >>sys.stderr, cache_file + ":", e.args[1]
|
||||||
|
sys.exit(1)
|
||||||
|
except KeyError:
|
||||||
|
print >>sys.stderr, cache_file + ": Probably not a cache file"
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Now do it the right way :-)
|
||||||
|
my_planet = planet.Planet(ConfigParser.ConfigParser())
|
||||||
|
my_planet.cache_directory = os.path.dirname(cache_file)
|
||||||
|
channel = planet.Channel(my_planet, url)
|
||||||
|
|
||||||
|
for item_id in ids:
|
||||||
|
if not channel.has_item(item_id):
|
||||||
|
print >>sys.stderr, item_id + ": Not in channel"
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Do the user's bidding
|
||||||
|
if command == "channel":
|
||||||
|
print_keys(channel, "Channel Keys")
|
||||||
|
|
||||||
|
elif command == "item":
|
||||||
|
for item_id in ids:
|
||||||
|
item = channel.get_item(item_id)
|
||||||
|
print_keys(item, "Item Keys for %s" % item_id)
|
||||||
|
|
||||||
|
elif command == "list":
|
||||||
|
print "Items in Channel:"
|
||||||
|
for item in channel.items(hidden=1, sorted=1):
|
||||||
|
print " " + item.id
|
||||||
|
print " " + time.strftime(planet.TIMEFMT_ISO, item.date)
|
||||||
|
if hasattr(item, "title"):
|
||||||
|
print " " + fit_str(item.title, 70)
|
||||||
|
if hasattr(item, "hidden"):
|
||||||
|
print " (hidden)"
|
||||||
|
|
||||||
|
elif command == "keys":
|
||||||
|
keys = {}
|
||||||
|
for item in channel.items():
|
||||||
|
for key in item.keys():
|
||||||
|
keys[key] = 1
|
||||||
|
|
||||||
|
keys = keys.keys()
|
||||||
|
keys.sort()
|
||||||
|
|
||||||
|
print "Keys used in Channel:"
|
||||||
|
for key in keys:
|
||||||
|
print " " + key
|
||||||
|
print
|
||||||
|
|
||||||
|
print "Use --item to output values of particular items."
|
||||||
|
|
||||||
|
elif command == "hide":
|
||||||
|
for item_id in ids:
|
||||||
|
item = channel.get_item(item_id)
|
||||||
|
if hasattr(item, "hidden"):
|
||||||
|
print item_id + ": Already hidden."
|
||||||
|
else:
|
||||||
|
item.hidden = "yes"
|
||||||
|
|
||||||
|
channel.cache_write()
|
||||||
|
print "Done."
|
||||||
|
|
||||||
|
elif command == "unhide":
|
||||||
|
for item_id in ids:
|
||||||
|
item = channel.get_item(item_id)
|
||||||
|
if hasattr(item, "hidden"):
|
||||||
|
del(item.hidden)
|
||||||
|
else:
|
||||||
|
print item_id + ": Not hidden."
|
||||||
|
|
||||||
|
channel.cache_write()
|
||||||
|
print "Done."
|
280
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet.py
Executable file
|
@ -0,0 +1,280 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""The Planet aggregator.
|
||||||
|
|
||||||
|
A flexible and easy-to-use aggregator for generating websites.
|
||||||
|
|
||||||
|
Visit http://www.planetplanet.org/ for more information and to download
|
||||||
|
the latest version.
|
||||||
|
|
||||||
|
Requires Python 2.1, recommends 2.3.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__authors__ = [ "Scott James Remnant <scott@netsplit.com>",
|
||||||
|
"Jeff Waugh <jdub@perkypants.org>" ]
|
||||||
|
__license__ = "Python"
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import locale
|
||||||
|
import urlparse
|
||||||
|
|
||||||
|
import planet
|
||||||
|
|
||||||
|
from ConfigParser import ConfigParser
|
||||||
|
|
||||||
|
# Default configuration file path
|
||||||
|
CONFIG_FILE = "config.ini"
|
||||||
|
|
||||||
|
# Defaults for the [Planet] config section
|
||||||
|
PLANET_NAME = "Unconfigured Planet"
|
||||||
|
PLANET_LINK = "Unconfigured Planet"
|
||||||
|
PLANET_FEED = None
|
||||||
|
OWNER_NAME = "Anonymous Coward"
|
||||||
|
OWNER_EMAIL = ""
|
||||||
|
LOG_LEVEL = "WARNING"
|
||||||
|
FEED_TIMEOUT = 20 # seconds
|
||||||
|
|
||||||
|
# Default template file list
|
||||||
|
TEMPLATE_FILES = "examples/basic/planet.html.tmpl"
|
||||||
|
|
||||||
|
#part for django api usage
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
# In order to reduce integration issues, this path gets defined automatically.
|
||||||
|
sys.path.append(os.path.abspath('../..'))
|
||||||
|
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'djagen.settings'
|
||||||
|
from djagen.collector.models import *
|
||||||
|
|
||||||
|
def config_get(config, section, option, default=None, raw=0, vars=None):
|
||||||
|
"""Get a value from the configuration, with a default."""
|
||||||
|
if config.has_option(section, option):
|
||||||
|
return config.get(section, option, raw=raw, vars=None)
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def main():
|
||||||
|
config_file = CONFIG_FILE
|
||||||
|
offline = 0
|
||||||
|
verbose = 0
|
||||||
|
|
||||||
|
for arg in sys.argv[1:]:
|
||||||
|
if arg == "-h" or arg == "--help":
|
||||||
|
print "Usage: planet [options] [CONFIGFILE]"
|
||||||
|
print
|
||||||
|
print "Options:"
|
||||||
|
print " -v, --verbose DEBUG level logging during update"
|
||||||
|
print " -o, --offline Update the Planet from the cache only"
|
||||||
|
print " -h, --help Display this help message and exit"
|
||||||
|
print
|
||||||
|
sys.exit(0)
|
||||||
|
elif arg == "-v" or arg == "--verbose":
|
||||||
|
verbose = 1
|
||||||
|
elif arg == "-o" or arg == "--offline":
|
||||||
|
offline = 1
|
||||||
|
elif arg.startswith("-"):
|
||||||
|
print >>sys.stderr, "Unknown option:", arg
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
config_file = arg
|
||||||
|
|
||||||
|
# Read the configuration file
|
||||||
|
config = ConfigParser()
|
||||||
|
config.read(config_file)
|
||||||
|
if not config.has_section("Planet"):
|
||||||
|
print >>sys.stderr, "Configuration missing [Planet] section."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Read the [Planet] config section
|
||||||
|
planet_name = config_get(config, "Planet", "name", PLANET_NAME)
|
||||||
|
planet_link = config_get(config, "Planet", "link", PLANET_LINK)
|
||||||
|
planet_feed = config_get(config, "Planet", "feed", PLANET_FEED)
|
||||||
|
owner_name = config_get(config, "Planet", "owner_name", OWNER_NAME)
|
||||||
|
owner_email = config_get(config, "Planet", "owner_email", OWNER_EMAIL)
|
||||||
|
if verbose:
|
||||||
|
log_level = "DEBUG"
|
||||||
|
else:
|
||||||
|
log_level = config_get(config, "Planet", "log_level", LOG_LEVEL)
|
||||||
|
feed_timeout = config_get(config, "Planet", "feed_timeout", FEED_TIMEOUT)
|
||||||
|
template_files = config_get(config, "Planet", "template_files",
|
||||||
|
TEMPLATE_FILES).split(" ")
|
||||||
|
|
||||||
|
# Default feed to the first feed for which there is a template
|
||||||
|
if not planet_feed:
|
||||||
|
for template_file in template_files:
|
||||||
|
name = os.path.splitext(os.path.basename(template_file))[0]
|
||||||
|
if name.find('atom')>=0 or name.find('rss')>=0:
|
||||||
|
planet_feed = urlparse.urljoin(planet_link, name)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Define locale
|
||||||
|
if config.has_option("Planet", "locale"):
|
||||||
|
# The user can specify more than one locale (separated by ":") as
|
||||||
|
# fallbacks.
|
||||||
|
locale_ok = False
|
||||||
|
for user_locale in config.get("Planet", "locale").split(':'):
|
||||||
|
user_locale = user_locale.strip()
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, user_locale)
|
||||||
|
except locale.Error:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
locale_ok = True
|
||||||
|
break
|
||||||
|
if not locale_ok:
|
||||||
|
print >>sys.stderr, "Unsupported locale setting."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Activate logging
|
||||||
|
planet.logging.basicConfig()
|
||||||
|
planet.logging.getLogger().setLevel(planet.logging.getLevelName(log_level))
|
||||||
|
log = planet.logging.getLogger("planet.runner")
|
||||||
|
try:
|
||||||
|
log.warning
|
||||||
|
except:
|
||||||
|
log.warning = log.warn
|
||||||
|
|
||||||
|
# timeoutsocket allows feedparser to time out rather than hang forever on
|
||||||
|
# ultra-slow servers. Python 2.3 now has this functionality available in
|
||||||
|
# the standard socket library, so under 2.3 you don't need to install
|
||||||
|
# anything. But you probably should anyway, because the socket module is
|
||||||
|
# buggy and timeoutsocket is better.
|
||||||
|
if feed_timeout:
|
||||||
|
try:
|
||||||
|
feed_timeout = float(feed_timeout)
|
||||||
|
except:
|
||||||
|
log.warning("Feed timeout set to invalid value '%s', skipping", feed_timeout)
|
||||||
|
feed_timeout = None
|
||||||
|
|
||||||
|
if feed_timeout and not offline:
|
||||||
|
try:
|
||||||
|
from planet import timeoutsocket
|
||||||
|
timeoutsocket.setDefaultSocketTimeout(feed_timeout)
|
||||||
|
log.debug("Socket timeout set to %d seconds", feed_timeout)
|
||||||
|
except ImportError:
|
||||||
|
import socket
|
||||||
|
if hasattr(socket, 'setdefaulttimeout'):
|
||||||
|
log.debug("timeoutsocket not found, using python function")
|
||||||
|
socket.setdefaulttimeout(feed_timeout)
|
||||||
|
log.debug("Socket timeout set to %d seconds", feed_timeout)
|
||||||
|
else:
|
||||||
|
log.error("Unable to set timeout to %d seconds", feed_timeout)
|
||||||
|
|
||||||
|
# run the planet
|
||||||
|
my_planet = planet.Planet(config)
|
||||||
|
my_planet.run(planet_name, planet_link, template_files, offline)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## This is where archiving is done! ##
|
||||||
|
#add the current channels to the db
|
||||||
|
channels = my_planet.channels()
|
||||||
|
for channel in channels:
|
||||||
|
|
||||||
|
author_name = channel.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
author_face = channel.face
|
||||||
|
except:
|
||||||
|
author_face = None
|
||||||
|
try:
|
||||||
|
channel_subtitle = channel.subtitle
|
||||||
|
except:
|
||||||
|
channel_subtitle = None
|
||||||
|
try:
|
||||||
|
channel_title = channel.title
|
||||||
|
except:
|
||||||
|
channel_title = None
|
||||||
|
|
||||||
|
channel_url = channel.url
|
||||||
|
|
||||||
|
try:
|
||||||
|
channel_link = channel.link
|
||||||
|
except:
|
||||||
|
channel_link = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
channel_urlstatus = channel.url_status
|
||||||
|
except:
|
||||||
|
channel_urlstatus = None
|
||||||
|
|
||||||
|
label = channel.label
|
||||||
|
|
||||||
|
label_personal = 0
|
||||||
|
label_lkd = 0
|
||||||
|
label_community = 0
|
||||||
|
label_eng = 0
|
||||||
|
if label == "Personal":
|
||||||
|
label_personal = 1
|
||||||
|
if label == "LKD":
|
||||||
|
label_lkd = 1
|
||||||
|
if label == "Community":
|
||||||
|
label_community = 1
|
||||||
|
if label == "Eng":
|
||||||
|
label_eng = 1
|
||||||
|
|
||||||
|
id = channel.id
|
||||||
|
|
||||||
|
try:
|
||||||
|
author = Authors.objects.get(author_id=id)
|
||||||
|
|
||||||
|
#update the values with the ones at the config file
|
||||||
|
author.author_name = author_name
|
||||||
|
#print author_name
|
||||||
|
author.author_face = author_face
|
||||||
|
author.channel_subtitle = channel_subtitle
|
||||||
|
author.channel_title = channel_title
|
||||||
|
author.channel_url = channel_url
|
||||||
|
author.channel_link = channel_link
|
||||||
|
author.channel_url_status = channel_urlstatus
|
||||||
|
author.label_personal = label_personal
|
||||||
|
author.label_lkd = label_lkd
|
||||||
|
author.label_community = label_community
|
||||||
|
author.label_eng = label_eng
|
||||||
|
|
||||||
|
except Exception, ex:
|
||||||
|
#print ex
|
||||||
|
author = Authors(author_id=id, author_name=author_name, author_face=author_face, channel_subtitle=channel_subtitle, channel_title=channel_title, channel_url=channel_url, channel_link=channel_link, channel_urlstatus=channel_urlstatus, label_personal=label_personal, label_lkd=label_lkd, label_community=label_community, label_eng=label_eng)
|
||||||
|
|
||||||
|
|
||||||
|
author.save()
|
||||||
|
|
||||||
|
#entry issues
|
||||||
|
items = channel.items()
|
||||||
|
for item in items:
|
||||||
|
id_hash = item.id_hash
|
||||||
|
|
||||||
|
try:
|
||||||
|
entry = author.entries_set.get(id_hash = id_hash)
|
||||||
|
entry.title = item.title
|
||||||
|
entry.content_html = item.content
|
||||||
|
entry.content_text = entry.sanitize(item.content)
|
||||||
|
entry.summary = item.summary
|
||||||
|
entry.link = item.link
|
||||||
|
d = item.date
|
||||||
|
entry.date = datetime.datetime(d[0], d[1], d[2], d[3], d[4], d[5])
|
||||||
|
except:
|
||||||
|
content_html = item.content
|
||||||
|
#content_text = entry.sanitize(content_html)
|
||||||
|
d = item.date
|
||||||
|
if not item.has_key('summary'): summary = None
|
||||||
|
else: summary = item.summary
|
||||||
|
entry = author.entries_set.create(id_hash=id_hash, title=item.title, content_html=item.content, summary=summary, link=item.link, date=datetime.datetime(d[0], d[1], d[2], d[3], d[4], d[5]))
|
||||||
|
entry.content_text = entry.sanitize(content_html)
|
||||||
|
|
||||||
|
entry.save()
|
||||||
|
|
||||||
|
#datetime issue
|
||||||
|
r = RunTime()
|
||||||
|
r.save()
|
||||||
|
|
||||||
|
my_planet.generate_all_files(template_files, planet_name,
|
||||||
|
planet_link, planet_feed, owner_name, owner_email)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
969
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/__init__.py
Executable file
|
@ -0,0 +1,969 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: UTF-8 -*-
|
||||||
|
"""Planet aggregator library.
|
||||||
|
|
||||||
|
This package is a library for developing web sites or software that
|
||||||
|
aggregate RSS, CDF and Atom feeds taken from elsewhere into a single,
|
||||||
|
combined feed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "2.0"
|
||||||
|
__authors__ = [ "Scott James Remnant <scott@netsplit.com>",
|
||||||
|
"Jeff Waugh <jdub@perkypants.org>" ]
|
||||||
|
__license__ = "Python"
|
||||||
|
|
||||||
|
import locale
|
||||||
|
|
||||||
|
# Modules available without separate import
|
||||||
|
import cache
|
||||||
|
import feedparser
|
||||||
|
import sanitize
|
||||||
|
import htmltmpl
|
||||||
|
import sgmllib
|
||||||
|
try:
|
||||||
|
import logging
|
||||||
|
except:
|
||||||
|
import compat_logging as logging
|
||||||
|
|
||||||
|
# Limit the effect of "from planet import *"
|
||||||
|
__all__ = ("cache", "feedparser", "htmltmpl", "logging",
|
||||||
|
"Planet", "Channel", "NewsItem")
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import md5
|
||||||
|
import time
|
||||||
|
import dbhash
|
||||||
|
import re
|
||||||
|
|
||||||
|
try:
|
||||||
|
from xml.sax.saxutils import escape
|
||||||
|
except:
|
||||||
|
def escape(data):
|
||||||
|
return data.replace("&","&").replace(">",">").replace("<","<")
|
||||||
|
|
||||||
|
# Version information (for generator headers)
|
||||||
|
VERSION = ("Planet/%s +http://www.planetplanet.org" % __version__)
|
||||||
|
|
||||||
|
# Default User-Agent header to send when retreiving feeds
|
||||||
|
USER_AGENT = VERSION + " " + feedparser.USER_AGENT
|
||||||
|
|
||||||
|
# Default cache directory
|
||||||
|
CACHE_DIRECTORY = "cache"
|
||||||
|
|
||||||
|
# Default number of items to display from a new feed
|
||||||
|
NEW_FEED_ITEMS = 10
|
||||||
|
|
||||||
|
# Useful common date/time formats
|
||||||
|
TIMEFMT_ISO = "%Y-%m-%dT%H:%M:%S+00:00"
|
||||||
|
TIMEFMT_822 = "%a, %d %b %Y %H:%M:%S +0000"
|
||||||
|
|
||||||
|
|
||||||
|
# Log instance to use here
|
||||||
|
log = logging.getLogger("planet")
|
||||||
|
try:
|
||||||
|
log.warning
|
||||||
|
except:
|
||||||
|
log.warning = log.warn
|
||||||
|
|
||||||
|
# Defaults for the template file config sections
|
||||||
|
ENCODING = "utf-8"
|
||||||
|
ITEMS_PER_PAGE = 60
|
||||||
|
DAYS_PER_PAGE = 0
|
||||||
|
OUTPUT_DIR = "output"
|
||||||
|
DATE_FORMAT = "%B %d, %Y %I:%M %p"
|
||||||
|
NEW_DATE_FORMAT = "%B %d, %Y"
|
||||||
|
ACTIVITY_THRESHOLD = 0
|
||||||
|
|
||||||
|
class stripHtml(sgmllib.SGMLParser):
|
||||||
|
"remove all tags from the data"
|
||||||
|
def __init__(self, data):
|
||||||
|
sgmllib.SGMLParser.__init__(self)
|
||||||
|
self.result=''
|
||||||
|
self.feed(data)
|
||||||
|
self.close()
|
||||||
|
def handle_data(self, data):
|
||||||
|
if data: self.result+=data
|
||||||
|
|
||||||
|
def template_info(item, date_format):
|
||||||
|
"""Produce a dictionary of template information."""
|
||||||
|
info = {}
|
||||||
|
|
||||||
|
#set the locale so that the dates at the feeds will be in english
|
||||||
|
lc=locale.getlocale()
|
||||||
|
if lc[0] == None:
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif lc[0].find("tr") != -1:
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for key in item.keys():
|
||||||
|
if item.key_type(key) == item.DATE:
|
||||||
|
date = item.get_as_date(key)
|
||||||
|
info[key] = time.strftime(date_format, date)
|
||||||
|
info[key + "_iso"] = time.strftime(TIMEFMT_ISO, date)
|
||||||
|
info[key + "_822"] = time.strftime(TIMEFMT_822, date)
|
||||||
|
else:
|
||||||
|
info[key] = item[key]
|
||||||
|
if 'title' in item.keys():
|
||||||
|
info['title_plain'] = stripHtml(info['title']).result
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
class Planet:
|
||||||
|
"""A set of channels.
|
||||||
|
|
||||||
|
This class represents a set of channels for which the items will
|
||||||
|
be aggregated together into one combined feed.
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
user_agent User-Agent header to fetch feeds with.
|
||||||
|
cache_directory Directory to store cached channels in.
|
||||||
|
new_feed_items Number of items to display from a new feed.
|
||||||
|
filter A regular expression that articles must match.
|
||||||
|
exclude A regular expression that articles must not match.
|
||||||
|
"""
|
||||||
|
def __init__(self, config):
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
self._channels = []
|
||||||
|
|
||||||
|
self.user_agent = USER_AGENT
|
||||||
|
self.cache_directory = CACHE_DIRECTORY
|
||||||
|
self.new_feed_items = NEW_FEED_ITEMS
|
||||||
|
self.filter = None
|
||||||
|
self.exclude = None
|
||||||
|
|
||||||
|
def tmpl_config_get(self, template, option, default=None, raw=0, vars=None):
|
||||||
|
"""Get a template value from the configuration, with a default."""
|
||||||
|
if self.config.has_option(template, option):
|
||||||
|
return self.config.get(template, option, raw=raw, vars=None)
|
||||||
|
elif self.config.has_option("Planet", option):
|
||||||
|
return self.config.get("Planet", option, raw=raw, vars=None)
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def gather_channel_info(self, template_file="Planet"):
|
||||||
|
date_format = self.tmpl_config_get(template_file,
|
||||||
|
"date_format", DATE_FORMAT, raw=1)
|
||||||
|
|
||||||
|
activity_threshold = int(self.tmpl_config_get(template_file,
|
||||||
|
"activity_threshold",
|
||||||
|
ACTIVITY_THRESHOLD))
|
||||||
|
|
||||||
|
if activity_threshold:
|
||||||
|
activity_horizon = \
|
||||||
|
time.gmtime(time.time()-86400*activity_threshold)
|
||||||
|
else:
|
||||||
|
activity_horizon = 0
|
||||||
|
|
||||||
|
channels = {}
|
||||||
|
channels_list = []
|
||||||
|
for channel in self.channels(hidden=1):
|
||||||
|
channels[channel] = template_info(channel, date_format)
|
||||||
|
channels_list.append(channels[channel])
|
||||||
|
|
||||||
|
# identify inactive feeds
|
||||||
|
if activity_horizon:
|
||||||
|
latest = channel.items(sorted=1)
|
||||||
|
if len(latest)==0 or latest[0].date < activity_horizon:
|
||||||
|
channels[channel]["message"] = \
|
||||||
|
"no activity in %d days" % activity_threshold
|
||||||
|
|
||||||
|
# report channel level errors
|
||||||
|
if not channel.url_status: continue
|
||||||
|
status = int(channel.url_status)
|
||||||
|
if status == 403:
|
||||||
|
channels[channel]["message"] = "403: forbidden"
|
||||||
|
elif status == 404:
|
||||||
|
channels[channel]["message"] = "404: not found"
|
||||||
|
elif status == 408:
|
||||||
|
channels[channel]["message"] = "408: request timeout"
|
||||||
|
elif status == 410:
|
||||||
|
channels[channel]["message"] = "410: gone"
|
||||||
|
elif status == 500:
|
||||||
|
channels[channel]["message"] = "internal server error"
|
||||||
|
elif status >= 400:
|
||||||
|
channels[channel]["message"] = "http status %s" % status
|
||||||
|
|
||||||
|
return channels, channels_list
|
||||||
|
|
||||||
|
def gather_items_info(self, channels, template_file="Planet", channel_list=None):
|
||||||
|
items_list = []
|
||||||
|
prev_date = []
|
||||||
|
prev_channel = None
|
||||||
|
|
||||||
|
date_format = self.tmpl_config_get(template_file,
|
||||||
|
"date_format", DATE_FORMAT, raw=1)
|
||||||
|
items_per_page = int(self.tmpl_config_get(template_file,
|
||||||
|
"items_per_page", ITEMS_PER_PAGE))
|
||||||
|
days_per_page = int(self.tmpl_config_get(template_file,
|
||||||
|
"days_per_page", DAYS_PER_PAGE))
|
||||||
|
new_date_format = self.tmpl_config_get(template_file,
|
||||||
|
"new_date_format", NEW_DATE_FORMAT, raw=1)
|
||||||
|
|
||||||
|
for newsitem in self.items(max_items=items_per_page,
|
||||||
|
max_days=days_per_page,
|
||||||
|
channels=channel_list):
|
||||||
|
item_info = template_info(newsitem, date_format)
|
||||||
|
chan_info = channels[newsitem._channel]
|
||||||
|
for k, v in chan_info.items():
|
||||||
|
item_info["channel_" + k] = v
|
||||||
|
|
||||||
|
# Check for the start of a new day
|
||||||
|
if prev_date[:3] != newsitem.date[:3]:
|
||||||
|
prev_date = newsitem.date
|
||||||
|
item_info["new_date"] = time.strftime(new_date_format,
|
||||||
|
newsitem.date)
|
||||||
|
|
||||||
|
# Check for the start of a new channel
|
||||||
|
if item_info.has_key("new_date") \
|
||||||
|
or prev_channel != newsitem._channel:
|
||||||
|
prev_channel = newsitem._channel
|
||||||
|
item_info["new_channel"] = newsitem._channel.url
|
||||||
|
|
||||||
|
items_list.append(item_info)
|
||||||
|
|
||||||
|
return items_list
|
||||||
|
|
||||||
|
def run(self, planet_name, planet_link, template_files, offline = False):
|
||||||
|
log = logging.getLogger("planet.runner")
|
||||||
|
|
||||||
|
# Create a planet
|
||||||
|
log.info("Loading cached data")
|
||||||
|
if self.config.has_option("Planet", "cache_directory"):
|
||||||
|
self.cache_directory = self.config.get("Planet", "cache_directory")
|
||||||
|
if self.config.has_option("Planet", "new_feed_items"):
|
||||||
|
self.new_feed_items = int(self.config.get("Planet", "new_feed_items"))
|
||||||
|
self.user_agent = "%s +%s %s" % (planet_name, planet_link,
|
||||||
|
self.user_agent)
|
||||||
|
if self.config.has_option("Planet", "filter"):
|
||||||
|
self.filter = self.config.get("Planet", "filter")
|
||||||
|
|
||||||
|
# The other configuration blocks are channels to subscribe to
|
||||||
|
for feed_url in self.config.sections():
|
||||||
|
if feed_url == "Planet" or feed_url in template_files:
|
||||||
|
continue
|
||||||
|
log.info(feed_url)
|
||||||
|
# Create a channel, configure it and subscribe it
|
||||||
|
channel = Channel(self, feed_url)
|
||||||
|
self.subscribe(channel)
|
||||||
|
|
||||||
|
# Update it
|
||||||
|
try:
|
||||||
|
if not offline and not channel.url_status == '410':
|
||||||
|
channel.update()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
log.exception("Update of <%s> failed", feed_url)
|
||||||
|
|
||||||
|
def generate_all_files(self, template_files, planet_name,
|
||||||
|
planet_link, planet_feed, owner_name, owner_email):
|
||||||
|
|
||||||
|
log = logging.getLogger("planet.runner")
|
||||||
|
# Go-go-gadget-template
|
||||||
|
for template_file in template_files:
|
||||||
|
manager = htmltmpl.TemplateManager()
|
||||||
|
log.info("Processing template %s", template_file)
|
||||||
|
try:
|
||||||
|
template = manager.prepare(template_file)
|
||||||
|
except htmltmpl.TemplateError:
|
||||||
|
template = manager.prepare(os.path.basename(template_file))
|
||||||
|
# Read the configuration
|
||||||
|
output_dir = self.tmpl_config_get(template_file,
|
||||||
|
"output_dir", OUTPUT_DIR)
|
||||||
|
date_format = self.tmpl_config_get(template_file,
|
||||||
|
"date_format", DATE_FORMAT, raw=1)
|
||||||
|
encoding = self.tmpl_config_get(template_file, "encoding", ENCODING)
|
||||||
|
|
||||||
|
# We treat each template individually
|
||||||
|
base = os.path.splitext(os.path.basename(template_file))[0]
|
||||||
|
url = os.path.join(planet_link, base)
|
||||||
|
output_file = os.path.join(output_dir, base)
|
||||||
|
|
||||||
|
# Gather information
|
||||||
|
channels, channels_list = self.gather_channel_info(template_file)
|
||||||
|
items_list = self.gather_items_info(channels, template_file)
|
||||||
|
|
||||||
|
# Gather item information
|
||||||
|
|
||||||
|
# Process the template
|
||||||
|
tp = htmltmpl.TemplateProcessor(html_escape=0)
|
||||||
|
tp.set("Items", items_list)
|
||||||
|
tp.set("Channels", channels_list)
|
||||||
|
|
||||||
|
# Generic information
|
||||||
|
tp.set("generator", VERSION)
|
||||||
|
tp.set("name", planet_name)
|
||||||
|
tp.set("link", planet_link)
|
||||||
|
tp.set("owner_name", owner_name)
|
||||||
|
tp.set("owner_email", owner_email)
|
||||||
|
tp.set("url", url)
|
||||||
|
|
||||||
|
if planet_feed:
|
||||||
|
tp.set("feed", planet_feed)
|
||||||
|
tp.set("feedtype", planet_feed.find('rss')>=0 and 'rss' or 'atom')
|
||||||
|
|
||||||
|
# Update time
|
||||||
|
date = time.localtime()
|
||||||
|
tp.set("date", time.strftime(date_format, date))
|
||||||
|
tp.set("date_iso", time.strftime(TIMEFMT_ISO, date))
|
||||||
|
tp.set("date_822", time.strftime(TIMEFMT_822, date))
|
||||||
|
|
||||||
|
try:
|
||||||
|
log.info("Writing %s", output_file)
|
||||||
|
output_fd = open(output_file, "w")
|
||||||
|
if encoding.lower() in ("utf-8", "utf8"):
|
||||||
|
# UTF-8 output is the default because we use that internally
|
||||||
|
output_fd.write(tp.process(template))
|
||||||
|
elif encoding.lower() in ("xml", "html", "sgml"):
|
||||||
|
# Magic for Python 2.3 users
|
||||||
|
output = tp.process(template).decode("utf-8")
|
||||||
|
output_fd.write(output.encode("ascii", "xmlcharrefreplace"))
|
||||||
|
else:
|
||||||
|
# Must be a "known" encoding
|
||||||
|
output = tp.process(template).decode("utf-8")
|
||||||
|
output_fd.write(output.encode(encoding, "replace"))
|
||||||
|
output_fd.close()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
log.exception("Write of %s failed", output_file)
|
||||||
|
|
||||||
|
def channels(self, hidden=0, sorted=1):
|
||||||
|
"""Return the list of channels."""
|
||||||
|
channels = []
|
||||||
|
for channel in self._channels:
|
||||||
|
if hidden or not channel.has_key("hidden"):
|
||||||
|
channels.append((channel.name, channel))
|
||||||
|
|
||||||
|
if sorted:
|
||||||
|
channels.sort()
|
||||||
|
|
||||||
|
return [ c[-1] for c in channels ]
|
||||||
|
|
||||||
|
def find_by_basename(self, basename):
|
||||||
|
for channel in self._channels:
|
||||||
|
if basename == channel.cache_basename(): return channel
|
||||||
|
|
||||||
|
def subscribe(self, channel):
|
||||||
|
"""Subscribe the planet to the channel."""
|
||||||
|
self._channels.append(channel)
|
||||||
|
|
||||||
|
def unsubscribe(self, channel):
|
||||||
|
"""Unsubscribe the planet from the channel."""
|
||||||
|
self._channels.remove(channel)
|
||||||
|
|
||||||
|
def items(self, hidden=0, sorted=1, max_items=0, max_days=0, channels=None):
|
||||||
|
"""Return an optionally filtered list of items in the channel.
|
||||||
|
|
||||||
|
The filters are applied in the following order:
|
||||||
|
|
||||||
|
If hidden is true then items in hidden channels and hidden items
|
||||||
|
will be returned.
|
||||||
|
|
||||||
|
If sorted is true then the item list will be sorted with the newest
|
||||||
|
first.
|
||||||
|
|
||||||
|
If max_items is non-zero then this number of items, at most, will
|
||||||
|
be returned.
|
||||||
|
|
||||||
|
If max_days is non-zero then any items older than the newest by
|
||||||
|
this number of days won't be returned. Requires sorted=1 to work.
|
||||||
|
|
||||||
|
|
||||||
|
The sharp-eyed will note that this looks a little strange code-wise,
|
||||||
|
it turns out that Python gets *really* slow if we try to sort the
|
||||||
|
actual items themselves. Also we use mktime here, but it's ok
|
||||||
|
because we discard the numbers and just need them to be relatively
|
||||||
|
consistent between each other.
|
||||||
|
"""
|
||||||
|
planet_filter_re = None
|
||||||
|
if self.filter:
|
||||||
|
planet_filter_re = re.compile(self.filter, re.I)
|
||||||
|
planet_exclude_re = None
|
||||||
|
if self.exclude:
|
||||||
|
planet_exclude_re = re.compile(self.exclude, re.I)
|
||||||
|
|
||||||
|
items = []
|
||||||
|
seen_guids = {}
|
||||||
|
if not channels: channels=self.channels(hidden=hidden, sorted=0)
|
||||||
|
for channel in channels:
|
||||||
|
for item in channel._items.values():
|
||||||
|
if hidden or not item.has_key("hidden"):
|
||||||
|
|
||||||
|
channel_filter_re = None
|
||||||
|
if channel.filter:
|
||||||
|
channel_filter_re = re.compile(channel.filter,
|
||||||
|
re.I)
|
||||||
|
channel_exclude_re = None
|
||||||
|
if channel.exclude:
|
||||||
|
channel_exclude_re = re.compile(channel.exclude,
|
||||||
|
re.I)
|
||||||
|
if (planet_filter_re or planet_exclude_re \
|
||||||
|
or channel_filter_re or channel_exclude_re):
|
||||||
|
title = ""
|
||||||
|
if item.has_key("title"):
|
||||||
|
title = item.title
|
||||||
|
content = item.get_content("content")
|
||||||
|
|
||||||
|
if planet_filter_re:
|
||||||
|
if not (planet_filter_re.search(title) \
|
||||||
|
or planet_filter_re.search(content)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if planet_exclude_re:
|
||||||
|
if (planet_exclude_re.search(title) \
|
||||||
|
or planet_exclude_re.search(content)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if channel_filter_re:
|
||||||
|
if not (channel_filter_re.search(title) \
|
||||||
|
or channel_filter_re.search(content)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if channel_exclude_re:
|
||||||
|
if (channel_exclude_re.search(title) \
|
||||||
|
or channel_exclude_re.search(content)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not seen_guids.has_key(item.id):
|
||||||
|
seen_guids[item.id] = 1;
|
||||||
|
items.append((time.mktime(item.date), item.order, item))
|
||||||
|
|
||||||
|
# Sort the list
|
||||||
|
if sorted:
|
||||||
|
items.sort()
|
||||||
|
items.reverse()
|
||||||
|
|
||||||
|
# Apply max_items filter
|
||||||
|
if len(items) and max_items:
|
||||||
|
items = items[:max_items]
|
||||||
|
|
||||||
|
# Apply max_days filter
|
||||||
|
if len(items) and max_days:
|
||||||
|
max_count = 0
|
||||||
|
max_time = items[0][0] - max_days * 84600
|
||||||
|
for item in items:
|
||||||
|
if item[0] > max_time:
|
||||||
|
max_count += 1
|
||||||
|
else:
|
||||||
|
items = items[:max_count]
|
||||||
|
break
|
||||||
|
|
||||||
|
return [ i[-1] for i in items ]
|
||||||
|
|
||||||
|
class Channel(cache.CachedInfo):
|
||||||
|
"""A list of news items.
|
||||||
|
|
||||||
|
This class represents a list of news items taken from the feed of
|
||||||
|
a website or other source.
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
url URL of the feed.
|
||||||
|
url_etag E-Tag of the feed URL.
|
||||||
|
url_modified Last modified time of the feed URL.
|
||||||
|
url_status Last HTTP status of the feed URL.
|
||||||
|
hidden Channel should be hidden (True if exists).
|
||||||
|
name Name of the feed owner, or feed title.
|
||||||
|
next_order Next order number to be assigned to NewsItem
|
||||||
|
|
||||||
|
updated Correct UTC-Normalised update time of the feed.
|
||||||
|
last_updated Correct UTC-Normalised time the feed was last updated.
|
||||||
|
|
||||||
|
id An identifier the feed claims is unique (*).
|
||||||
|
title One-line title (*).
|
||||||
|
link Link to the original format feed (*).
|
||||||
|
tagline Short description of the feed (*).
|
||||||
|
info Longer description of the feed (*).
|
||||||
|
|
||||||
|
modified Date the feed claims to have been modified (*).
|
||||||
|
|
||||||
|
author Name of the author (*).
|
||||||
|
publisher Name of the publisher (*).
|
||||||
|
generator Name of the feed generator (*).
|
||||||
|
category Category name (*).
|
||||||
|
copyright Copyright information for humans to read (*).
|
||||||
|
license Link to the licence for the content (*).
|
||||||
|
docs Link to the specification of the feed format (*).
|
||||||
|
language Primary language (*).
|
||||||
|
errorreportsto E-Mail address to send error reports to (*).
|
||||||
|
|
||||||
|
image_url URL of an associated image (*).
|
||||||
|
image_link Link to go with the associated image (*).
|
||||||
|
image_title Alternative text of the associated image (*).
|
||||||
|
image_width Width of the associated image (*).
|
||||||
|
image_height Height of the associated image (*).
|
||||||
|
|
||||||
|
filter A regular expression that articles must match.
|
||||||
|
exclude A regular expression that articles must not match.
|
||||||
|
|
||||||
|
Properties marked (*) will only be present if the original feed
|
||||||
|
contained them. Note that the optional 'modified' date field is simply
|
||||||
|
a claim made by the item and parsed from the information given, 'updated'
|
||||||
|
(and 'last_updated') are far more reliable sources of information.
|
||||||
|
|
||||||
|
Some feeds may define additional properties to those above.
|
||||||
|
"""
|
||||||
|
IGNORE_KEYS = ("links", "contributors", "textinput", "cloud", "categories",
|
||||||
|
"url", "href", "url_etag", "url_modified", "tags", "itunes_explicit")
|
||||||
|
|
||||||
|
def __init__(self, planet, url):
|
||||||
|
if not os.path.isdir(planet.cache_directory):
|
||||||
|
os.makedirs(planet.cache_directory)
|
||||||
|
cache_filename = cache.filename(planet.cache_directory, url)
|
||||||
|
cache_file = dbhash.open(cache_filename, "c", 0666)
|
||||||
|
|
||||||
|
cache.CachedInfo.__init__(self, cache_file, url, root=1)
|
||||||
|
|
||||||
|
self._items = {}
|
||||||
|
self._planet = planet
|
||||||
|
self._expired = []
|
||||||
|
self.url = url
|
||||||
|
# retain the original URL for error reporting
|
||||||
|
self.configured_url = url
|
||||||
|
self.url_etag = None
|
||||||
|
self.url_status = None
|
||||||
|
self.url_modified = None
|
||||||
|
self.name = None
|
||||||
|
self.updated = None
|
||||||
|
self.last_updated = None
|
||||||
|
self.filter = None
|
||||||
|
self.exclude = None
|
||||||
|
self.next_order = "0"
|
||||||
|
self.cache_read()
|
||||||
|
self.cache_read_entries()
|
||||||
|
|
||||||
|
if planet.config.has_section(url):
|
||||||
|
for option in planet.config.options(url):
|
||||||
|
value = planet.config.get(url, option)
|
||||||
|
self.set_as_string(option, value, cached=0)
|
||||||
|
|
||||||
|
def has_item(self, id_):
|
||||||
|
"""Check whether the item exists in the channel."""
|
||||||
|
return self._items.has_key(id_)
|
||||||
|
|
||||||
|
def get_item(self, id_):
|
||||||
|
"""Return the item from the channel."""
|
||||||
|
return self._items[id_]
|
||||||
|
|
||||||
|
# Special methods
|
||||||
|
__contains__ = has_item
|
||||||
|
|
||||||
|
def items(self, hidden=0, sorted=0):
|
||||||
|
"""Return the item list."""
|
||||||
|
items = []
|
||||||
|
for item in self._items.values():
|
||||||
|
if hidden or not item.has_key("hidden"):
|
||||||
|
items.append((time.mktime(item.date), item.order, item))
|
||||||
|
|
||||||
|
if sorted:
|
||||||
|
items.sort()
|
||||||
|
items.reverse()
|
||||||
|
|
||||||
|
return [ i[-1] for i in items ]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Iterate the sorted item list."""
|
||||||
|
return iter(self.items(sorted=1))
|
||||||
|
|
||||||
|
def cache_read_entries(self):
|
||||||
|
"""Read entry information from the cache."""
|
||||||
|
keys = self._cache.keys()
|
||||||
|
for key in keys:
|
||||||
|
if key.find(" ") != -1: continue
|
||||||
|
if self.has_key(key): continue
|
||||||
|
|
||||||
|
item = NewsItem(self, key)
|
||||||
|
self._items[key] = item
|
||||||
|
|
||||||
|
def cache_basename(self):
|
||||||
|
return cache.filename('',self._id)
|
||||||
|
|
||||||
|
def cache_write(self, sync=1):
|
||||||
|
|
||||||
|
"""Write channel and item information to the cache."""
|
||||||
|
for item in self._items.values():
|
||||||
|
item.cache_write(sync=0)
|
||||||
|
for item in self._expired:
|
||||||
|
item.cache_clear(sync=0)
|
||||||
|
cache.CachedInfo.cache_write(self, sync)
|
||||||
|
|
||||||
|
self._expired = []
|
||||||
|
|
||||||
|
def feed_information(self):
|
||||||
|
"""
|
||||||
|
Returns a description string for the feed embedded in this channel.
|
||||||
|
|
||||||
|
This will usually simply be the feed url embedded in <>, but in the
|
||||||
|
case where the current self.url has changed from the original
|
||||||
|
self.configured_url the string will contain both pieces of information.
|
||||||
|
This is so that the URL in question is easier to find in logging
|
||||||
|
output: getting an error about a URL that doesn't appear in your config
|
||||||
|
file is annoying.
|
||||||
|
"""
|
||||||
|
if self.url == self.configured_url:
|
||||||
|
return "<%s>" % self.url
|
||||||
|
else:
|
||||||
|
return "<%s> (formerly <%s>)" % (self.url, self.configured_url)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Download the feed to refresh the information.
|
||||||
|
|
||||||
|
This does the actual work of pulling down the feed and if it changes
|
||||||
|
updates the cached information about the feed and entries within it.
|
||||||
|
"""
|
||||||
|
info = feedparser.parse(self.url,
|
||||||
|
etag=self.url_etag, modified=self.url_modified,
|
||||||
|
agent=self._planet.user_agent)
|
||||||
|
if info.has_key("status"):
|
||||||
|
self.url_status = str(info.status)
|
||||||
|
elif info.has_key("entries") and len(info.entries)>0:
|
||||||
|
self.url_status = str(200)
|
||||||
|
elif info.bozo and info.bozo_exception.__class__.__name__=='Timeout':
|
||||||
|
self.url_status = str(408)
|
||||||
|
else:
|
||||||
|
self.url_status = str(500)
|
||||||
|
|
||||||
|
if self.url_status == '301' and \
|
||||||
|
(info.has_key("entries") and len(info.entries)>0):
|
||||||
|
log.warning("Feed has moved from <%s> to <%s>", self.url, info.url)
|
||||||
|
try:
|
||||||
|
os.link(cache.filename(self._planet.cache_directory, self.url),
|
||||||
|
cache.filename(self._planet.cache_directory, info.url))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.url = info.url
|
||||||
|
elif self.url_status == '304':
|
||||||
|
log.info("Feed %s unchanged", self.feed_information())
|
||||||
|
return
|
||||||
|
elif self.url_status == '410':
|
||||||
|
log.info("Feed %s gone", self.feed_information())
|
||||||
|
self.cache_write()
|
||||||
|
return
|
||||||
|
elif self.url_status == '408':
|
||||||
|
log.warning("Feed %s timed out", self.feed_information())
|
||||||
|
return
|
||||||
|
elif int(self.url_status) >= 400:
|
||||||
|
log.error("Error %s while updating feed %s",
|
||||||
|
self.url_status, self.feed_information())
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
log.info("Updating feed %s", self.feed_information())
|
||||||
|
|
||||||
|
self.url_etag = info.has_key("etag") and info.etag or None
|
||||||
|
self.url_modified = info.has_key("modified") and info.modified or None
|
||||||
|
if self.url_etag is not None:
|
||||||
|
log.debug("E-Tag: %s", self.url_etag)
|
||||||
|
if self.url_modified is not None:
|
||||||
|
log.debug("Last Modified: %s",
|
||||||
|
time.strftime(TIMEFMT_ISO, self.url_modified))
|
||||||
|
|
||||||
|
self.update_info(info.feed)
|
||||||
|
self.update_entries(info.entries)
|
||||||
|
self.cache_write()
|
||||||
|
|
||||||
|
def update_info(self, feed):
|
||||||
|
"""Update information from the feed.
|
||||||
|
|
||||||
|
This reads the feed information supplied by feedparser and updates
|
||||||
|
the cached information about the feed. These are the various
|
||||||
|
potentially interesting properties that you might care about.
|
||||||
|
"""
|
||||||
|
for key in feed.keys():
|
||||||
|
if key in self.IGNORE_KEYS or key + "_parsed" in self.IGNORE_KEYS:
|
||||||
|
# Ignored fields
|
||||||
|
pass
|
||||||
|
elif feed.has_key(key + "_parsed"):
|
||||||
|
# Ignore unparsed date fields
|
||||||
|
pass
|
||||||
|
elif key.endswith("_detail"):
|
||||||
|
# retain name and email sub-fields
|
||||||
|
if feed[key].has_key('name') and feed[key].name:
|
||||||
|
self.set_as_string(key.replace("_detail","_name"), \
|
||||||
|
feed[key].name)
|
||||||
|
if feed[key].has_key('email') and feed[key].email:
|
||||||
|
self.set_as_string(key.replace("_detail","_email"), \
|
||||||
|
feed[key].email)
|
||||||
|
elif key == "items":
|
||||||
|
# Ignore items field
|
||||||
|
pass
|
||||||
|
elif key.endswith("_parsed"):
|
||||||
|
# Date fields
|
||||||
|
if feed[key] is not None:
|
||||||
|
self.set_as_date(key[:-len("_parsed")], feed[key])
|
||||||
|
elif key == "image":
|
||||||
|
# Image field: save all the information
|
||||||
|
if feed[key].has_key("url"):
|
||||||
|
self.set_as_string(key + "_url", feed[key].url)
|
||||||
|
if feed[key].has_key("link"):
|
||||||
|
self.set_as_string(key + "_link", feed[key].link)
|
||||||
|
if feed[key].has_key("title"):
|
||||||
|
self.set_as_string(key + "_title", feed[key].title)
|
||||||
|
if feed[key].has_key("width"):
|
||||||
|
self.set_as_string(key + "_width", str(feed[key].width))
|
||||||
|
if feed[key].has_key("height"):
|
||||||
|
self.set_as_string(key + "_height", str(feed[key].height))
|
||||||
|
elif isinstance(feed[key], (str, unicode)):
|
||||||
|
# String fields
|
||||||
|
try:
|
||||||
|
detail = key + '_detail'
|
||||||
|
if feed.has_key(detail) and feed[detail].has_key('type'):
|
||||||
|
if feed[detail].type == 'text/html':
|
||||||
|
feed[key] = sanitize.HTML(feed[key])
|
||||||
|
elif feed[detail].type == 'text/plain':
|
||||||
|
feed[key] = escape(feed[key])
|
||||||
|
self.set_as_string(key, feed[key])
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
log.exception("Ignored '%s' of <%s>, unknown format",
|
||||||
|
key, self.url)
|
||||||
|
|
||||||
|
def update_entries(self, entries):
|
||||||
|
"""Update entries from the feed.
|
||||||
|
|
||||||
|
This reads the entries supplied by feedparser and updates the
|
||||||
|
cached information about them. It's at this point we update
|
||||||
|
the 'updated' timestamp and keep the old one in 'last_updated',
|
||||||
|
these provide boundaries for acceptable entry times.
|
||||||
|
|
||||||
|
If this is the first time a feed has been updated then most of the
|
||||||
|
items will be marked as hidden, according to Planet.new_feed_items.
|
||||||
|
|
||||||
|
If the feed does not contain items which, according to the sort order,
|
||||||
|
should be there; those items are assumed to have been expired from
|
||||||
|
the feed or replaced and are removed from the cache.
|
||||||
|
"""
|
||||||
|
if not len(entries):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.last_updated = self.updated
|
||||||
|
self.updated = time.gmtime()
|
||||||
|
|
||||||
|
new_items = []
|
||||||
|
feed_items = []
|
||||||
|
for entry in entries:
|
||||||
|
# Try really hard to find some kind of unique identifier
|
||||||
|
if entry.has_key("id"):
|
||||||
|
entry_id = cache.utf8(entry.id)
|
||||||
|
elif entry.has_key("link"):
|
||||||
|
entry_id = cache.utf8(entry.link)
|
||||||
|
elif entry.has_key("title"):
|
||||||
|
entry_id = (self.url + "/"
|
||||||
|
+ md5.new(cache.utf8(entry.title)).hexdigest())
|
||||||
|
elif entry.has_key("summary"):
|
||||||
|
entry_id = (self.url + "/"
|
||||||
|
+ md5.new(cache.utf8(entry.summary)).hexdigest())
|
||||||
|
else:
|
||||||
|
log.error("Unable to find or generate id, entry ignored")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Create the item if necessary and update
|
||||||
|
if self.has_item(entry_id):
|
||||||
|
item = self._items[entry_id]
|
||||||
|
else:
|
||||||
|
item = NewsItem(self, entry_id)
|
||||||
|
self._items[entry_id] = item
|
||||||
|
new_items.append(item)
|
||||||
|
item.update(entry)
|
||||||
|
feed_items.append(entry_id)
|
||||||
|
|
||||||
|
# Hide excess items the first time through
|
||||||
|
if self.last_updated is None and self._planet.new_feed_items \
|
||||||
|
and len(feed_items) > self._planet.new_feed_items:
|
||||||
|
item.hidden = "yes"
|
||||||
|
log.debug("Marked <%s> as hidden (new feed)", entry_id)
|
||||||
|
|
||||||
|
# Assign order numbers in reverse
|
||||||
|
new_items.reverse()
|
||||||
|
for item in new_items:
|
||||||
|
item.order = self.next_order = str(int(self.next_order) + 1)
|
||||||
|
|
||||||
|
# Check for expired or replaced items
|
||||||
|
feed_count = len(feed_items)
|
||||||
|
log.debug("Items in Feed: %d", feed_count)
|
||||||
|
for item in self.items(sorted=1):
|
||||||
|
if feed_count < 1:
|
||||||
|
break
|
||||||
|
elif item.id in feed_items:
|
||||||
|
feed_count -= 1
|
||||||
|
elif item._channel.url_status != '226':
|
||||||
|
del(self._items[item.id])
|
||||||
|
self._expired.append(item)
|
||||||
|
log.debug("Removed expired or replaced item <%s>", item.id)
|
||||||
|
|
||||||
|
def get_name(self, key):
|
||||||
|
"""Return the key containing the name."""
|
||||||
|
for key in ("name", "title"):
|
||||||
|
if self.has_key(key) and self.key_type(key) != self.NULL:
|
||||||
|
return self.get_as_string(key)
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
class NewsItem(cache.CachedInfo):
|
||||||
|
"""An item of news.
|
||||||
|
|
||||||
|
This class represents a single item of news on a channel. They're
|
||||||
|
created by members of the Channel class and accessible through it.
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
id Channel-unique identifier for this item.
|
||||||
|
id_hash Relatively short, printable cryptographic hash of id
|
||||||
|
date Corrected UTC-Normalised update time, for sorting.
|
||||||
|
order Order in which items on the same date can be sorted.
|
||||||
|
hidden Item should be hidden (True if exists).
|
||||||
|
|
||||||
|
title One-line title (*).
|
||||||
|
link Link to the original format text (*).
|
||||||
|
summary Short first-page summary (*).
|
||||||
|
content Full HTML content.
|
||||||
|
|
||||||
|
modified Date the item claims to have been modified (*).
|
||||||
|
issued Date the item claims to have been issued (*).
|
||||||
|
created Date the item claims to have been created (*).
|
||||||
|
expired Date the item claims to expire (*).
|
||||||
|
|
||||||
|
author Name of the author (*).
|
||||||
|
publisher Name of the publisher (*).
|
||||||
|
category Category name (*).
|
||||||
|
comments Link to a page to enter comments (*).
|
||||||
|
license Link to the licence for the content (*).
|
||||||
|
source_name Name of the original source of this item (*).
|
||||||
|
source_link Link to the original source of this item (*).
|
||||||
|
|
||||||
|
Properties marked (*) will only be present if the original feed
|
||||||
|
contained them. Note that the various optional date fields are
|
||||||
|
simply claims made by the item and parsed from the information
|
||||||
|
given, 'date' is a far more reliable source of information.
|
||||||
|
|
||||||
|
Some feeds may define additional properties to those above.
|
||||||
|
"""
|
||||||
|
IGNORE_KEYS = ("categories", "contributors", "enclosures", "links",
|
||||||
|
"guidislink", "date", "tags")
|
||||||
|
|
||||||
|
def __init__(self, channel, id_):
|
||||||
|
cache.CachedInfo.__init__(self, channel._cache, id_)
|
||||||
|
|
||||||
|
self._channel = channel
|
||||||
|
self.id = id_
|
||||||
|
self.id_hash = md5.new(id_).hexdigest()
|
||||||
|
self.date = None
|
||||||
|
self.order = None
|
||||||
|
self.content = None
|
||||||
|
self.cache_read()
|
||||||
|
|
||||||
|
def update(self, entry):
|
||||||
|
"""Update the item from the feedparser entry given."""
|
||||||
|
for key in entry.keys():
|
||||||
|
if key in self.IGNORE_KEYS or key + "_parsed" in self.IGNORE_KEYS:
|
||||||
|
# Ignored fields
|
||||||
|
pass
|
||||||
|
elif entry.has_key(key + "_parsed"):
|
||||||
|
# Ignore unparsed date fields
|
||||||
|
pass
|
||||||
|
elif key.endswith("_detail"):
|
||||||
|
# retain name, email, and language sub-fields
|
||||||
|
if entry[key].has_key('name') and entry[key].name:
|
||||||
|
self.set_as_string(key.replace("_detail","_name"), \
|
||||||
|
entry[key].name)
|
||||||
|
if entry[key].has_key('email') and entry[key].email:
|
||||||
|
self.set_as_string(key.replace("_detail","_email"), \
|
||||||
|
entry[key].email)
|
||||||
|
if entry[key].has_key('language') and entry[key].language and \
|
||||||
|
(not self._channel.has_key('language') or \
|
||||||
|
entry[key].language != self._channel.language):
|
||||||
|
self.set_as_string(key.replace("_detail","_language"), \
|
||||||
|
entry[key].language)
|
||||||
|
elif key.endswith("_parsed"):
|
||||||
|
# Date fields
|
||||||
|
if entry[key] is not None:
|
||||||
|
self.set_as_date(key[:-len("_parsed")], entry[key])
|
||||||
|
elif key == "source":
|
||||||
|
# Source field: save both url and value
|
||||||
|
if entry[key].has_key("value"):
|
||||||
|
self.set_as_string(key + "_name", entry[key].value)
|
||||||
|
if entry[key].has_key("url"):
|
||||||
|
self.set_as_string(key + "_link", entry[key].url)
|
||||||
|
elif key == "content":
|
||||||
|
# Content field: concatenate the values
|
||||||
|
value = ""
|
||||||
|
for item in entry[key]:
|
||||||
|
if item.type == 'text/html':
|
||||||
|
item.value = sanitize.HTML(item.value)
|
||||||
|
elif item.type == 'text/plain':
|
||||||
|
item.value = escape(item.value)
|
||||||
|
if item.has_key('language') and item.language and \
|
||||||
|
(not self._channel.has_key('language') or
|
||||||
|
item.language != self._channel.language) :
|
||||||
|
self.set_as_string(key + "_language", item.language)
|
||||||
|
value += cache.utf8(item.value)
|
||||||
|
self.set_as_string(key, value)
|
||||||
|
elif isinstance(entry[key], (str, unicode)):
|
||||||
|
# String fields
|
||||||
|
try:
|
||||||
|
detail = key + '_detail'
|
||||||
|
if entry.has_key(detail):
|
||||||
|
if entry[detail].has_key('type'):
|
||||||
|
if entry[detail].type == 'text/html':
|
||||||
|
entry[key] = sanitize.HTML(entry[key])
|
||||||
|
elif entry[detail].type == 'text/plain':
|
||||||
|
entry[key] = escape(entry[key])
|
||||||
|
self.set_as_string(key, entry[key])
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
log.exception("Ignored '%s' of <%s>, unknown format",
|
||||||
|
key, self.id)
|
||||||
|
|
||||||
|
# Generate the date field if we need to
|
||||||
|
self.get_date("date")
|
||||||
|
|
||||||
|
def get_date(self, key):
|
||||||
|
"""Get (or update) the date key.
|
||||||
|
|
||||||
|
We check whether the date the entry claims to have been changed is
|
||||||
|
since we last updated this feed and when we pulled the feed off the
|
||||||
|
site.
|
||||||
|
|
||||||
|
If it is then it's probably not bogus, and we'll sort accordingly.
|
||||||
|
|
||||||
|
If it isn't then we bound it appropriately, this ensures that
|
||||||
|
entries appear in posting sequence but don't overlap entries
|
||||||
|
added in previous updates and don't creep into the next one.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for other_key in ("updated", "modified", "published", "issued", "created"):
|
||||||
|
if self.has_key(other_key):
|
||||||
|
date = self.get_as_date(other_key)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
date = None
|
||||||
|
|
||||||
|
if date is not None:
|
||||||
|
if date > self._channel.updated:
|
||||||
|
date = self._channel.updated
|
||||||
|
# elif date < self._channel.last_updated:
|
||||||
|
# date = self._channel.updated
|
||||||
|
elif self.has_key(key) and self.key_type(key) != self.NULL:
|
||||||
|
return self.get_as_date(key)
|
||||||
|
else:
|
||||||
|
date = self._channel.updated
|
||||||
|
|
||||||
|
self.set_as_date(key, date)
|
||||||
|
return date
|
||||||
|
|
||||||
|
def get_content(self, key):
|
||||||
|
"""Return the key containing the content."""
|
||||||
|
for key in ("content", "tagline", "summary"):
|
||||||
|
if self.has_key(key) and self.key_type(key) != self.NULL:
|
||||||
|
return self.get_as_string(key)
|
||||||
|
|
||||||
|
return ""
|
124
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/atomstyler.py
Executable file
|
@ -0,0 +1,124 @@
|
||||||
|
from xml.dom import minidom, Node
|
||||||
|
from urlparse import urlparse, urlunparse
|
||||||
|
from xml.parsers.expat import ExpatError
|
||||||
|
from htmlentitydefs import name2codepoint
|
||||||
|
import re
|
||||||
|
|
||||||
|
# select and apply an xml:base for this entry
|
||||||
|
class relativize:
|
||||||
|
def __init__(self, parent):
|
||||||
|
self.score = {}
|
||||||
|
self.links = []
|
||||||
|
self.collect_and_tally(parent)
|
||||||
|
self.base = self.select_optimal_base()
|
||||||
|
if self.base:
|
||||||
|
if not parent.hasAttribute('xml:base'):
|
||||||
|
self.rebase(parent)
|
||||||
|
parent.setAttribute('xml:base', self.base)
|
||||||
|
|
||||||
|
# collect and tally cite, href and src attributes
|
||||||
|
def collect_and_tally(self,parent):
|
||||||
|
uri = None
|
||||||
|
if parent.hasAttribute('cite'): uri=parent.getAttribute('cite')
|
||||||
|
if parent.hasAttribute('href'): uri=parent.getAttribute('href')
|
||||||
|
if parent.hasAttribute('src'): uri=parent.getAttribute('src')
|
||||||
|
|
||||||
|
if uri:
|
||||||
|
parts=urlparse(uri)
|
||||||
|
if parts[0].lower() == 'http':
|
||||||
|
parts = (parts[1]+parts[2]).split('/')
|
||||||
|
base = None
|
||||||
|
for i in range(1,len(parts)):
|
||||||
|
base = tuple(parts[0:i])
|
||||||
|
self.score[base] = self.score.get(base,0) + len(base)
|
||||||
|
if base and base not in self.links: self.links.append(base)
|
||||||
|
|
||||||
|
for node in parent.childNodes:
|
||||||
|
if node.nodeType == Node.ELEMENT_NODE:
|
||||||
|
self.collect_and_tally(node)
|
||||||
|
|
||||||
|
# select the xml:base with the highest score
|
||||||
|
def select_optimal_base(self):
|
||||||
|
if not self.score: return None
|
||||||
|
for link in self.links:
|
||||||
|
self.score[link] = 0
|
||||||
|
winner = max(self.score.values())
|
||||||
|
if not winner: return None
|
||||||
|
for key in self.score.keys():
|
||||||
|
if self.score[key] == winner:
|
||||||
|
if winner == len(key): return None
|
||||||
|
return urlunparse(('http', key[0], '/'.join(key[1:]), '', '', '')) + '/'
|
||||||
|
|
||||||
|
# rewrite cite, href and src attributes using this base
|
||||||
|
def rebase(self,parent):
|
||||||
|
uri = None
|
||||||
|
if parent.hasAttribute('cite'): uri=parent.getAttribute('cite')
|
||||||
|
if parent.hasAttribute('href'): uri=parent.getAttribute('href')
|
||||||
|
if parent.hasAttribute('src'): uri=parent.getAttribute('src')
|
||||||
|
if uri and uri.startswith(self.base):
|
||||||
|
uri = uri[len(self.base):] or '.'
|
||||||
|
if parent.hasAttribute('href'): uri=parent.setAttribute('href', uri)
|
||||||
|
if parent.hasAttribute('src'): uri=parent.setAttribute('src', uri)
|
||||||
|
|
||||||
|
for node in parent.childNodes:
|
||||||
|
if node.nodeType == Node.ELEMENT_NODE:
|
||||||
|
self.rebase(node)
|
||||||
|
|
||||||
|
# convert type="html" to type="plain" or type="xhtml" as appropriate
|
||||||
|
def retype(parent):
|
||||||
|
for node in parent.childNodes:
|
||||||
|
if node.nodeType == Node.ELEMENT_NODE:
|
||||||
|
|
||||||
|
if node.hasAttribute('type') and node.getAttribute('type') == 'html':
|
||||||
|
if len(node.childNodes)==0:
|
||||||
|
node.removeAttribute('type')
|
||||||
|
elif len(node.childNodes)==1:
|
||||||
|
|
||||||
|
# replace html entity defs with utf-8
|
||||||
|
chunks=re.split('&(\w+);', node.childNodes[0].nodeValue)
|
||||||
|
for i in range(1,len(chunks),2):
|
||||||
|
if chunks[i] in ['amp', 'lt', 'gt', 'apos', 'quot']:
|
||||||
|
chunks[i] ='&' + chunks[i] +';'
|
||||||
|
elif chunks[i] in name2codepoint:
|
||||||
|
chunks[i]=unichr(name2codepoint[chunks[i]])
|
||||||
|
else:
|
||||||
|
chunks[i]='&' + chunks[i] + ';'
|
||||||
|
text = u"".join(chunks)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# see if the resulting text is a well-formed XML fragment
|
||||||
|
div = '<div xmlns="http://www.w3.org/1999/xhtml">%s</div>'
|
||||||
|
data = minidom.parseString((div % text.encode('utf-8')))
|
||||||
|
|
||||||
|
if text.find('<') < 0:
|
||||||
|
# plain text
|
||||||
|
node.removeAttribute('type')
|
||||||
|
text = data.documentElement.childNodes[0].nodeValue
|
||||||
|
node.childNodes[0].replaceWholeText(text)
|
||||||
|
|
||||||
|
elif len(text) > 80:
|
||||||
|
# xhtml
|
||||||
|
node.setAttribute('type', 'xhtml')
|
||||||
|
node.removeChild(node.childNodes[0])
|
||||||
|
node.appendChild(data.documentElement)
|
||||||
|
|
||||||
|
except ExpatError:
|
||||||
|
# leave as html
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
# recurse
|
||||||
|
retype(node)
|
||||||
|
|
||||||
|
if parent.nodeName == 'entry':
|
||||||
|
relativize(parent)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# run styler on each file mention on the command line
|
||||||
|
import sys
|
||||||
|
for feed in sys.argv[1:]:
|
||||||
|
doc = minidom.parse(feed)
|
||||||
|
doc.normalize()
|
||||||
|
retype(doc.documentElement)
|
||||||
|
open(feed,'w').write(doc.toxml('utf-8'))
|
306
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/cache.py
Executable file
|
@ -0,0 +1,306 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: UTF-8 -*-
|
||||||
|
"""Item cache.
|
||||||
|
|
||||||
|
Between runs of Planet we need somewhere to store the feed information
|
||||||
|
we parsed, this is so we don't lose information when a particular feed
|
||||||
|
goes away or is too short to hold enough items.
|
||||||
|
|
||||||
|
This module provides the code to handle this cache transparently enough
|
||||||
|
that the rest of the code can take the persistance for granted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
# Regular expressions to sanitise cache filenames
|
||||||
|
re_url_scheme = re.compile(r'^[^:]*://')
|
||||||
|
re_slash = re.compile(r'[?/]+')
|
||||||
|
re_initial_cruft = re.compile(r'^[,.]*')
|
||||||
|
re_final_cruft = re.compile(r'[,.]*$')
|
||||||
|
|
||||||
|
|
||||||
|
class CachedInfo:
|
||||||
|
"""Cached information.
|
||||||
|
|
||||||
|
This class is designed to hold information that is stored in a cache
|
||||||
|
between instances. It can act both as a dictionary (c['foo']) and
|
||||||
|
as an object (c.foo) to get and set values and supports both string
|
||||||
|
and date values.
|
||||||
|
|
||||||
|
If you wish to support special fields you can derive a class off this
|
||||||
|
and implement get_FIELD and set_FIELD functions which will be
|
||||||
|
automatically called.
|
||||||
|
"""
|
||||||
|
STRING = "string"
|
||||||
|
DATE = "date"
|
||||||
|
NULL = "null"
|
||||||
|
|
||||||
|
def __init__(self, cache, id_, root=0):
|
||||||
|
self._type = {}
|
||||||
|
self._value = {}
|
||||||
|
self._cached = {}
|
||||||
|
|
||||||
|
self._cache = cache
|
||||||
|
self._id = id_.replace(" ", "%20")
|
||||||
|
self._root = root
|
||||||
|
|
||||||
|
def cache_key(self, key):
|
||||||
|
"""Return the cache key name for the given key."""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
if self._root:
|
||||||
|
return key
|
||||||
|
else:
|
||||||
|
return self._id + " " + key
|
||||||
|
|
||||||
|
def cache_read(self):
|
||||||
|
"""Read information from the cache."""
|
||||||
|
if self._root:
|
||||||
|
keys_key = " keys"
|
||||||
|
else:
|
||||||
|
keys_key = self._id
|
||||||
|
|
||||||
|
if self._cache.has_key(keys_key):
|
||||||
|
keys = self._cache[keys_key].split(" ")
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
for key in keys:
|
||||||
|
cache_key = self.cache_key(key)
|
||||||
|
if not self._cached.has_key(key) or self._cached[key]:
|
||||||
|
# Key either hasn't been loaded, or is one for the cache
|
||||||
|
self._value[key] = self._cache[cache_key]
|
||||||
|
self._type[key] = self._cache[cache_key + " type"]
|
||||||
|
self._cached[key] = 1
|
||||||
|
|
||||||
|
def cache_write(self, sync=1):
|
||||||
|
"""Write information to the cache."""
|
||||||
|
self.cache_clear(sync=0)
|
||||||
|
|
||||||
|
keys = []
|
||||||
|
for key in self.keys():
|
||||||
|
cache_key = self.cache_key(key)
|
||||||
|
if not self._cached[key]:
|
||||||
|
if self._cache.has_key(cache_key):
|
||||||
|
# Non-cached keys need to be cleared
|
||||||
|
del(self._cache[cache_key])
|
||||||
|
del(self._cache[cache_key + " type"])
|
||||||
|
continue
|
||||||
|
|
||||||
|
keys.append(key)
|
||||||
|
self._cache[cache_key] = self._value[key]
|
||||||
|
self._cache[cache_key + " type"] = self._type[key]
|
||||||
|
|
||||||
|
if self._root:
|
||||||
|
keys_key = " keys"
|
||||||
|
else:
|
||||||
|
keys_key = self._id
|
||||||
|
|
||||||
|
self._cache[keys_key] = " ".join(keys)
|
||||||
|
if sync:
|
||||||
|
self._cache.sync()
|
||||||
|
|
||||||
|
def cache_clear(self, sync=1):
|
||||||
|
"""Remove information from the cache."""
|
||||||
|
if self._root:
|
||||||
|
keys_key = " keys"
|
||||||
|
else:
|
||||||
|
keys_key = self._id
|
||||||
|
|
||||||
|
if self._cache.has_key(keys_key):
|
||||||
|
keys = self._cache[keys_key].split(" ")
|
||||||
|
del(self._cache[keys_key])
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
for key in keys:
|
||||||
|
cache_key = self.cache_key(key)
|
||||||
|
del(self._cache[cache_key])
|
||||||
|
del(self._cache[cache_key + " type"])
|
||||||
|
|
||||||
|
if sync:
|
||||||
|
self._cache.sync()
|
||||||
|
|
||||||
|
def has_key(self, key):
|
||||||
|
"""Check whether the key exists."""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
return self._value.has_key(key)
|
||||||
|
|
||||||
|
def key_type(self, key):
|
||||||
|
"""Return the key type."""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
return self._type[key]
|
||||||
|
|
||||||
|
def set(self, key, value, cached=1):
|
||||||
|
"""Set the value of the given key.
|
||||||
|
|
||||||
|
If a set_KEY function exists that is called otherwise the
|
||||||
|
string function is called and the date function if that fails
|
||||||
|
(it nearly always will).
|
||||||
|
"""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
|
||||||
|
try:
|
||||||
|
func = getattr(self, "set_" + key)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return func(key, value)
|
||||||
|
|
||||||
|
if value == None:
|
||||||
|
return self.set_as_null(key, value)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return self.set_as_string(key, value)
|
||||||
|
except TypeError:
|
||||||
|
return self.set_as_date(key, value)
|
||||||
|
|
||||||
|
def get(self, key):
|
||||||
|
"""Return the value of the given key.
|
||||||
|
|
||||||
|
If a get_KEY function exists that is called otherwise the
|
||||||
|
correctly typed function is called if that exists.
|
||||||
|
"""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
|
||||||
|
try:
|
||||||
|
func = getattr(self, "get_" + key)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return func(key)
|
||||||
|
|
||||||
|
try:
|
||||||
|
func = getattr(self, "get_as_" + self._type[key])
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return func(key)
|
||||||
|
|
||||||
|
return self._value[key]
|
||||||
|
|
||||||
|
def set_as_string(self, key, value, cached=1):
|
||||||
|
"""Set the key to the string value.
|
||||||
|
|
||||||
|
The value is converted to UTF-8 if it is a Unicode string, otherwise
|
||||||
|
it's assumed to have failed decoding (feedparser tries pretty hard)
|
||||||
|
so has all non-ASCII characters stripped.
|
||||||
|
"""
|
||||||
|
value = utf8(value)
|
||||||
|
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
self._value[key] = value
|
||||||
|
self._type[key] = self.STRING
|
||||||
|
self._cached[key] = cached
|
||||||
|
|
||||||
|
def get_as_string(self, key):
|
||||||
|
"""Return the key as a string value."""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
if not self.has_key(key):
|
||||||
|
raise KeyError, key
|
||||||
|
|
||||||
|
return self._value[key]
|
||||||
|
|
||||||
|
def set_as_date(self, key, value, cached=1):
|
||||||
|
"""Set the key to the date value.
|
||||||
|
|
||||||
|
The date should be a 9-item tuple as returned by time.gmtime().
|
||||||
|
"""
|
||||||
|
value = " ".join([ str(s) for s in value ])
|
||||||
|
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
self._value[key] = value
|
||||||
|
self._type[key] = self.DATE
|
||||||
|
self._cached[key] = cached
|
||||||
|
|
||||||
|
def get_as_date(self, key):
|
||||||
|
"""Return the key as a date value."""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
if not self.has_key(key):
|
||||||
|
raise KeyError, key
|
||||||
|
|
||||||
|
value = self._value[key]
|
||||||
|
return tuple([ int(i) for i in value.split(" ") ])
|
||||||
|
|
||||||
|
def set_as_null(self, key, value, cached=1):
|
||||||
|
"""Set the key to the null value.
|
||||||
|
|
||||||
|
This only exists to make things less magic.
|
||||||
|
"""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
self._value[key] = ""
|
||||||
|
self._type[key] = self.NULL
|
||||||
|
self._cached[key] = cached
|
||||||
|
|
||||||
|
def get_as_null(self, key):
|
||||||
|
"""Return the key as the null value."""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
if not self.has_key(key):
|
||||||
|
raise KeyError, key
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def del_key(self, key):
|
||||||
|
"""Delete the given key."""
|
||||||
|
key = key.replace(" ", "_")
|
||||||
|
if not self.has_key(key):
|
||||||
|
raise KeyError, key
|
||||||
|
|
||||||
|
del(self._value[key])
|
||||||
|
del(self._type[key])
|
||||||
|
del(self._cached[key])
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
"""Return the list of cached keys."""
|
||||||
|
return self._value.keys()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Iterate the cached keys."""
|
||||||
|
return iter(self._value.keys())
|
||||||
|
|
||||||
|
# Special methods
|
||||||
|
__contains__ = has_key
|
||||||
|
__setitem__ = set_as_string
|
||||||
|
__getitem__ = get
|
||||||
|
__delitem__ = del_key
|
||||||
|
__delattr__ = del_key
|
||||||
|
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
if key.startswith("_"):
|
||||||
|
self.__dict__[key] = value
|
||||||
|
else:
|
||||||
|
self.set(key, value)
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
if self.has_key(key):
|
||||||
|
return self.get(key)
|
||||||
|
else:
|
||||||
|
raise AttributeError, key
|
||||||
|
|
||||||
|
|
||||||
|
def filename(directory, filename):
|
||||||
|
"""Return a filename suitable for the cache.
|
||||||
|
|
||||||
|
Strips dangerous and common characters to create a filename we
|
||||||
|
can use to store the cache in.
|
||||||
|
"""
|
||||||
|
filename = re_url_scheme.sub("", filename)
|
||||||
|
filename = re_slash.sub(",", filename)
|
||||||
|
filename = re_initial_cruft.sub("", filename)
|
||||||
|
filename = re_final_cruft.sub("", filename)
|
||||||
|
|
||||||
|
return os.path.join(directory, filename)
|
||||||
|
|
||||||
|
def utf8(value):
|
||||||
|
"""Return the value as a UTF-8 string."""
|
||||||
|
if type(value) == type(u''):
|
||||||
|
return value.encode("utf-8")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return unicode(value, "utf-8").encode("utf-8")
|
||||||
|
except UnicodeError:
|
||||||
|
try:
|
||||||
|
return unicode(value, "iso-8859-1").encode("utf-8")
|
||||||
|
except UnicodeError:
|
||||||
|
return unicode(value, "ascii", "replace").encode("utf-8")
|
1196
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/compat_logging/__init__.py
Executable file
299
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/compat_logging/config.py
Executable file
|
@ -0,0 +1,299 @@
|
||||||
|
# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
# documentation for any purpose and without fee is hereby granted,
|
||||||
|
# provided that the above copyright notice appear in all copies and that
|
||||||
|
# both that copyright notice and this permission notice appear in
|
||||||
|
# supporting documentation, and that the name of Vinay Sajip
|
||||||
|
# not be used in advertising or publicity pertaining to distribution
|
||||||
|
# of the software without specific, written prior permission.
|
||||||
|
# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
||||||
|
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||||
|
# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
||||||
|
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||||
|
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Logging package for Python. Based on PEP 282 and comments thereto in
|
||||||
|
comp.lang.python, and influenced by Apache's log4j system.
|
||||||
|
|
||||||
|
Should work under Python versions >= 1.5.2, except that source line
|
||||||
|
information is not available unless 'inspect' is.
|
||||||
|
|
||||||
|
Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
|
||||||
|
|
||||||
|
To use, simply 'import logging' and log away!
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys, logging, logging.handlers, string, thread, threading, socket, struct, os
|
||||||
|
|
||||||
|
from SocketServer import ThreadingTCPServer, StreamRequestHandler
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_LOGGING_CONFIG_PORT = 9030
|
||||||
|
if sys.platform == "win32":
|
||||||
|
RESET_ERROR = 10054 #WSAECONNRESET
|
||||||
|
else:
|
||||||
|
RESET_ERROR = 104 #ECONNRESET
|
||||||
|
|
||||||
|
#
|
||||||
|
# The following code implements a socket listener for on-the-fly
|
||||||
|
# reconfiguration of logging.
|
||||||
|
#
|
||||||
|
# _listener holds the server object doing the listening
|
||||||
|
_listener = None
|
||||||
|
|
||||||
|
def fileConfig(fname, defaults=None):
|
||||||
|
"""
|
||||||
|
Read the logging configuration from a ConfigParser-format file.
|
||||||
|
|
||||||
|
This can be called several times from an application, allowing an end user
|
||||||
|
the ability to select from various pre-canned configurations (if the
|
||||||
|
developer provides a mechanism to present the choices and load the chosen
|
||||||
|
configuration).
|
||||||
|
In versions of ConfigParser which have the readfp method [typically
|
||||||
|
shipped in 2.x versions of Python], you can pass in a file-like object
|
||||||
|
rather than a filename, in which case the file-like object will be read
|
||||||
|
using readfp.
|
||||||
|
"""
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
cp = ConfigParser.ConfigParser(defaults)
|
||||||
|
if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
|
||||||
|
cp.readfp(fname)
|
||||||
|
else:
|
||||||
|
cp.read(fname)
|
||||||
|
#first, do the formatters...
|
||||||
|
flist = cp.get("formatters", "keys")
|
||||||
|
if len(flist):
|
||||||
|
flist = string.split(flist, ",")
|
||||||
|
formatters = {}
|
||||||
|
for form in flist:
|
||||||
|
sectname = "formatter_%s" % form
|
||||||
|
opts = cp.options(sectname)
|
||||||
|
if "format" in opts:
|
||||||
|
fs = cp.get(sectname, "format", 1)
|
||||||
|
else:
|
||||||
|
fs = None
|
||||||
|
if "datefmt" in opts:
|
||||||
|
dfs = cp.get(sectname, "datefmt", 1)
|
||||||
|
else:
|
||||||
|
dfs = None
|
||||||
|
f = logging.Formatter(fs, dfs)
|
||||||
|
formatters[form] = f
|
||||||
|
#next, do the handlers...
|
||||||
|
#critical section...
|
||||||
|
logging._acquireLock()
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
#first, lose the existing handlers...
|
||||||
|
logging._handlers.clear()
|
||||||
|
#now set up the new ones...
|
||||||
|
hlist = cp.get("handlers", "keys")
|
||||||
|
if len(hlist):
|
||||||
|
hlist = string.split(hlist, ",")
|
||||||
|
handlers = {}
|
||||||
|
fixups = [] #for inter-handler references
|
||||||
|
for hand in hlist:
|
||||||
|
sectname = "handler_%s" % hand
|
||||||
|
klass = cp.get(sectname, "class")
|
||||||
|
opts = cp.options(sectname)
|
||||||
|
if "formatter" in opts:
|
||||||
|
fmt = cp.get(sectname, "formatter")
|
||||||
|
else:
|
||||||
|
fmt = ""
|
||||||
|
klass = eval(klass, vars(logging))
|
||||||
|
args = cp.get(sectname, "args")
|
||||||
|
args = eval(args, vars(logging))
|
||||||
|
h = apply(klass, args)
|
||||||
|
if "level" in opts:
|
||||||
|
level = cp.get(sectname, "level")
|
||||||
|
h.setLevel(logging._levelNames[level])
|
||||||
|
if len(fmt):
|
||||||
|
h.setFormatter(formatters[fmt])
|
||||||
|
#temporary hack for FileHandler and MemoryHandler.
|
||||||
|
if klass == logging.handlers.MemoryHandler:
|
||||||
|
if "target" in opts:
|
||||||
|
target = cp.get(sectname,"target")
|
||||||
|
else:
|
||||||
|
target = ""
|
||||||
|
if len(target): #the target handler may not be loaded yet, so keep for later...
|
||||||
|
fixups.append((h, target))
|
||||||
|
handlers[hand] = h
|
||||||
|
#now all handlers are loaded, fixup inter-handler references...
|
||||||
|
for fixup in fixups:
|
||||||
|
h = fixup[0]
|
||||||
|
t = fixup[1]
|
||||||
|
h.setTarget(handlers[t])
|
||||||
|
#at last, the loggers...first the root...
|
||||||
|
llist = cp.get("loggers", "keys")
|
||||||
|
llist = string.split(llist, ",")
|
||||||
|
llist.remove("root")
|
||||||
|
sectname = "logger_root"
|
||||||
|
root = logging.root
|
||||||
|
log = root
|
||||||
|
opts = cp.options(sectname)
|
||||||
|
if "level" in opts:
|
||||||
|
level = cp.get(sectname, "level")
|
||||||
|
log.setLevel(logging._levelNames[level])
|
||||||
|
for h in root.handlers[:]:
|
||||||
|
root.removeHandler(h)
|
||||||
|
hlist = cp.get(sectname, "handlers")
|
||||||
|
if len(hlist):
|
||||||
|
hlist = string.split(hlist, ",")
|
||||||
|
for hand in hlist:
|
||||||
|
log.addHandler(handlers[hand])
|
||||||
|
#and now the others...
|
||||||
|
#we don't want to lose the existing loggers,
|
||||||
|
#since other threads may have pointers to them.
|
||||||
|
#existing is set to contain all existing loggers,
|
||||||
|
#and as we go through the new configuration we
|
||||||
|
#remove any which are configured. At the end,
|
||||||
|
#what's left in existing is the set of loggers
|
||||||
|
#which were in the previous configuration but
|
||||||
|
#which are not in the new configuration.
|
||||||
|
existing = root.manager.loggerDict.keys()
|
||||||
|
#now set up the new ones...
|
||||||
|
for log in llist:
|
||||||
|
sectname = "logger_%s" % log
|
||||||
|
qn = cp.get(sectname, "qualname")
|
||||||
|
opts = cp.options(sectname)
|
||||||
|
if "propagate" in opts:
|
||||||
|
propagate = cp.getint(sectname, "propagate")
|
||||||
|
else:
|
||||||
|
propagate = 1
|
||||||
|
logger = logging.getLogger(qn)
|
||||||
|
if qn in existing:
|
||||||
|
existing.remove(qn)
|
||||||
|
if "level" in opts:
|
||||||
|
level = cp.get(sectname, "level")
|
||||||
|
logger.setLevel(logging._levelNames[level])
|
||||||
|
for h in logger.handlers[:]:
|
||||||
|
logger.removeHandler(h)
|
||||||
|
logger.propagate = propagate
|
||||||
|
logger.disabled = 0
|
||||||
|
hlist = cp.get(sectname, "handlers")
|
||||||
|
if len(hlist):
|
||||||
|
hlist = string.split(hlist, ",")
|
||||||
|
for hand in hlist:
|
||||||
|
logger.addHandler(handlers[hand])
|
||||||
|
#Disable any old loggers. There's no point deleting
|
||||||
|
#them as other threads may continue to hold references
|
||||||
|
#and by disabling them, you stop them doing any logging.
|
||||||
|
for log in existing:
|
||||||
|
root.manager.loggerDict[log].disabled = 1
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
ei = sys.exc_info()
|
||||||
|
traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
|
||||||
|
del ei
|
||||||
|
finally:
|
||||||
|
logging._releaseLock()
|
||||||
|
|
||||||
|
def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
|
||||||
|
"""
|
||||||
|
Start up a socket server on the specified port, and listen for new
|
||||||
|
configurations.
|
||||||
|
|
||||||
|
These will be sent as a file suitable for processing by fileConfig().
|
||||||
|
Returns a Thread object on which you can call start() to start the server,
|
||||||
|
and which you can join() when appropriate. To stop the server, call
|
||||||
|
stopListening().
|
||||||
|
"""
|
||||||
|
if not thread:
|
||||||
|
raise NotImplementedError, "listen() needs threading to work"
|
||||||
|
|
||||||
|
class ConfigStreamHandler(StreamRequestHandler):
|
||||||
|
"""
|
||||||
|
Handler for a logging configuration request.
|
||||||
|
|
||||||
|
It expects a completely new logging configuration and uses fileConfig
|
||||||
|
to install it.
|
||||||
|
"""
|
||||||
|
def handle(self):
|
||||||
|
"""
|
||||||
|
Handle a request.
|
||||||
|
|
||||||
|
Each request is expected to be a 4-byte length,
|
||||||
|
followed by the config file. Uses fileConfig() to do the
|
||||||
|
grunt work.
|
||||||
|
"""
|
||||||
|
import tempfile
|
||||||
|
try:
|
||||||
|
conn = self.connection
|
||||||
|
chunk = conn.recv(4)
|
||||||
|
if len(chunk) == 4:
|
||||||
|
slen = struct.unpack(">L", chunk)[0]
|
||||||
|
chunk = self.connection.recv(slen)
|
||||||
|
while len(chunk) < slen:
|
||||||
|
chunk = chunk + conn.recv(slen - len(chunk))
|
||||||
|
#Apply new configuration. We'd like to be able to
|
||||||
|
#create a StringIO and pass that in, but unfortunately
|
||||||
|
#1.5.2 ConfigParser does not support reading file
|
||||||
|
#objects, only actual files. So we create a temporary
|
||||||
|
#file and remove it later.
|
||||||
|
file = tempfile.mktemp(".ini")
|
||||||
|
f = open(file, "w")
|
||||||
|
f.write(chunk)
|
||||||
|
f.close()
|
||||||
|
fileConfig(file)
|
||||||
|
os.remove(file)
|
||||||
|
except socket.error, e:
|
||||||
|
if type(e.args) != types.TupleType:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
errcode = e.args[0]
|
||||||
|
if errcode != RESET_ERROR:
|
||||||
|
raise
|
||||||
|
|
||||||
|
class ConfigSocketReceiver(ThreadingTCPServer):
|
||||||
|
"""
|
||||||
|
A simple TCP socket-based logging config receiver.
|
||||||
|
"""
|
||||||
|
|
||||||
|
allow_reuse_address = 1
|
||||||
|
|
||||||
|
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
|
||||||
|
handler=None):
|
||||||
|
ThreadingTCPServer.__init__(self, (host, port), handler)
|
||||||
|
logging._acquireLock()
|
||||||
|
self.abort = 0
|
||||||
|
logging._releaseLock()
|
||||||
|
self.timeout = 1
|
||||||
|
|
||||||
|
def serve_until_stopped(self):
|
||||||
|
import select
|
||||||
|
abort = 0
|
||||||
|
while not abort:
|
||||||
|
rd, wr, ex = select.select([self.socket.fileno()],
|
||||||
|
[], [],
|
||||||
|
self.timeout)
|
||||||
|
if rd:
|
||||||
|
self.handle_request()
|
||||||
|
logging._acquireLock()
|
||||||
|
abort = self.abort
|
||||||
|
logging._releaseLock()
|
||||||
|
|
||||||
|
def serve(rcvr, hdlr, port):
|
||||||
|
server = rcvr(port=port, handler=hdlr)
|
||||||
|
global _listener
|
||||||
|
logging._acquireLock()
|
||||||
|
_listener = server
|
||||||
|
logging._releaseLock()
|
||||||
|
server.serve_until_stopped()
|
||||||
|
|
||||||
|
return threading.Thread(target=serve,
|
||||||
|
args=(ConfigSocketReceiver,
|
||||||
|
ConfigStreamHandler, port))
|
||||||
|
|
||||||
|
def stopListening():
|
||||||
|
"""
|
||||||
|
Stop the listening server which was created with a call to listen().
|
||||||
|
"""
|
||||||
|
global _listener
|
||||||
|
if _listener:
|
||||||
|
logging._acquireLock()
|
||||||
|
_listener.abort = 1
|
||||||
|
_listener = None
|
||||||
|
logging._releaseLock()
|
728
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/compat_logging/handlers.py
Executable file
|
@ -0,0 +1,728 @@
|
||||||
|
# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
# documentation for any purpose and without fee is hereby granted,
|
||||||
|
# provided that the above copyright notice appear in all copies and that
|
||||||
|
# both that copyright notice and this permission notice appear in
|
||||||
|
# supporting documentation, and that the name of Vinay Sajip
|
||||||
|
# not be used in advertising or publicity pertaining to distribution
|
||||||
|
# of the software without specific, written prior permission.
|
||||||
|
# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
||||||
|
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||||
|
# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
||||||
|
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||||
|
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Logging package for Python. Based on PEP 282 and comments thereto in
|
||||||
|
comp.lang.python, and influenced by Apache's log4j system.
|
||||||
|
|
||||||
|
Should work under Python versions >= 1.5.2, except that source line
|
||||||
|
information is not available unless 'inspect' is.
|
||||||
|
|
||||||
|
Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
|
||||||
|
|
||||||
|
To use, simply 'import logging' and log away!
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys, logging, socket, types, os, string, cPickle, struct, time
|
||||||
|
|
||||||
|
from SocketServer import ThreadingTCPServer, StreamRequestHandler
|
||||||
|
|
||||||
|
#
|
||||||
|
# Some constants...
|
||||||
|
#
|
||||||
|
|
||||||
|
DEFAULT_TCP_LOGGING_PORT = 9020
|
||||||
|
DEFAULT_UDP_LOGGING_PORT = 9021
|
||||||
|
DEFAULT_HTTP_LOGGING_PORT = 9022
|
||||||
|
DEFAULT_SOAP_LOGGING_PORT = 9023
|
||||||
|
SYSLOG_UDP_PORT = 514
|
||||||
|
|
||||||
|
|
||||||
|
class RotatingFileHandler(logging.FileHandler):
|
||||||
|
def __init__(self, filename, mode="a", maxBytes=0, backupCount=0):
|
||||||
|
"""
|
||||||
|
Open the specified file and use it as the stream for logging.
|
||||||
|
|
||||||
|
By default, the file grows indefinitely. You can specify particular
|
||||||
|
values of maxBytes and backupCount to allow the file to rollover at
|
||||||
|
a predetermined size.
|
||||||
|
|
||||||
|
Rollover occurs whenever the current log file is nearly maxBytes in
|
||||||
|
length. If backupCount is >= 1, the system will successively create
|
||||||
|
new files with the same pathname as the base file, but with extensions
|
||||||
|
".1", ".2" etc. appended to it. For example, with a backupCount of 5
|
||||||
|
and a base file name of "app.log", you would get "app.log",
|
||||||
|
"app.log.1", "app.log.2", ... through to "app.log.5". The file being
|
||||||
|
written to is always "app.log" - when it gets filled up, it is closed
|
||||||
|
and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
|
||||||
|
exist, then they are renamed to "app.log.2", "app.log.3" etc.
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
If maxBytes is zero, rollover never occurs.
|
||||||
|
"""
|
||||||
|
logging.FileHandler.__init__(self, filename, mode)
|
||||||
|
self.maxBytes = maxBytes
|
||||||
|
self.backupCount = backupCount
|
||||||
|
if maxBytes > 0:
|
||||||
|
self.mode = "a"
|
||||||
|
|
||||||
|
def doRollover(self):
|
||||||
|
"""
|
||||||
|
Do a rollover, as described in __init__().
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.stream.close()
|
||||||
|
if self.backupCount > 0:
|
||||||
|
for i in range(self.backupCount - 1, 0, -1):
|
||||||
|
sfn = "%s.%d" % (self.baseFilename, i)
|
||||||
|
dfn = "%s.%d" % (self.baseFilename, i + 1)
|
||||||
|
if os.path.exists(sfn):
|
||||||
|
#print "%s -> %s" % (sfn, dfn)
|
||||||
|
if os.path.exists(dfn):
|
||||||
|
os.remove(dfn)
|
||||||
|
os.rename(sfn, dfn)
|
||||||
|
dfn = self.baseFilename + ".1"
|
||||||
|
if os.path.exists(dfn):
|
||||||
|
os.remove(dfn)
|
||||||
|
os.rename(self.baseFilename, dfn)
|
||||||
|
#print "%s -> %s" % (self.baseFilename, dfn)
|
||||||
|
self.stream = open(self.baseFilename, "w")
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""
|
||||||
|
Emit a record.
|
||||||
|
|
||||||
|
Output the record to the file, catering for rollover as described
|
||||||
|
in doRollover().
|
||||||
|
"""
|
||||||
|
if self.maxBytes > 0: # are we rolling over?
|
||||||
|
msg = "%s\n" % self.format(record)
|
||||||
|
self.stream.seek(0, 2) #due to non-posix-compliant Windows feature
|
||||||
|
if self.stream.tell() + len(msg) >= self.maxBytes:
|
||||||
|
self.doRollover()
|
||||||
|
logging.FileHandler.emit(self, record)
|
||||||
|
|
||||||
|
|
||||||
|
class SocketHandler(logging.Handler):
|
||||||
|
"""
|
||||||
|
A handler class which writes logging records, in pickle format, to
|
||||||
|
a streaming socket. The socket is kept open across logging calls.
|
||||||
|
If the peer resets it, an attempt is made to reconnect on the next call.
|
||||||
|
The pickle which is sent is that of the LogRecord's attribute dictionary
|
||||||
|
(__dict__), so that the receiver does not need to have the logging module
|
||||||
|
installed in order to process the logging event.
|
||||||
|
|
||||||
|
To unpickle the record at the receiving end into a LogRecord, use the
|
||||||
|
makeLogRecord function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, host, port):
|
||||||
|
"""
|
||||||
|
Initializes the handler with a specific host address and port.
|
||||||
|
|
||||||
|
The attribute 'closeOnError' is set to 1 - which means that if
|
||||||
|
a socket error occurs, the socket is silently closed and then
|
||||||
|
reopened on the next logging call.
|
||||||
|
"""
|
||||||
|
logging.Handler.__init__(self)
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.sock = None
|
||||||
|
self.closeOnError = 0
|
||||||
|
|
||||||
|
def makeSocket(self):
|
||||||
|
"""
|
||||||
|
A factory method which allows subclasses to define the precise
|
||||||
|
type of socket they want.
|
||||||
|
"""
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.connect((self.host, self.port))
|
||||||
|
return s
|
||||||
|
|
||||||
|
def send(self, s):
|
||||||
|
"""
|
||||||
|
Send a pickled string to the socket.
|
||||||
|
|
||||||
|
This function allows for partial sends which can happen when the
|
||||||
|
network is busy.
|
||||||
|
"""
|
||||||
|
if hasattr(self.sock, "sendall"):
|
||||||
|
self.sock.sendall(s)
|
||||||
|
else:
|
||||||
|
sentsofar = 0
|
||||||
|
left = len(s)
|
||||||
|
while left > 0:
|
||||||
|
sent = self.sock.send(s[sentsofar:])
|
||||||
|
sentsofar = sentsofar + sent
|
||||||
|
left = left - sent
|
||||||
|
|
||||||
|
def makePickle(self, record):
|
||||||
|
"""
|
||||||
|
Pickles the record in binary format with a length prefix, and
|
||||||
|
returns it ready for transmission across the socket.
|
||||||
|
"""
|
||||||
|
s = cPickle.dumps(record.__dict__, 1)
|
||||||
|
#n = len(s)
|
||||||
|
#slen = "%c%c" % ((n >> 8) & 0xFF, n & 0xFF)
|
||||||
|
slen = struct.pack(">L", len(s))
|
||||||
|
return slen + s
|
||||||
|
|
||||||
|
def handleError(self, record):
|
||||||
|
"""
|
||||||
|
Handle an error during logging.
|
||||||
|
|
||||||
|
An error has occurred during logging. Most likely cause -
|
||||||
|
connection lost. Close the socket so that we can retry on the
|
||||||
|
next event.
|
||||||
|
"""
|
||||||
|
if self.closeOnError and self.sock:
|
||||||
|
self.sock.close()
|
||||||
|
self.sock = None #try to reconnect next time
|
||||||
|
else:
|
||||||
|
logging.Handler.handleError(self, record)
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""
|
||||||
|
Emit a record.
|
||||||
|
|
||||||
|
Pickles the record and writes it to the socket in binary format.
|
||||||
|
If there is an error with the socket, silently drop the packet.
|
||||||
|
If there was a problem with the socket, re-establishes the
|
||||||
|
socket.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
s = self.makePickle(record)
|
||||||
|
if not self.sock:
|
||||||
|
self.sock = self.makeSocket()
|
||||||
|
self.send(s)
|
||||||
|
except:
|
||||||
|
self.handleError(record)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""
|
||||||
|
Closes the socket.
|
||||||
|
"""
|
||||||
|
if self.sock:
|
||||||
|
self.sock.close()
|
||||||
|
self.sock = None
|
||||||
|
|
||||||
|
class DatagramHandler(SocketHandler):
|
||||||
|
"""
|
||||||
|
A handler class which writes logging records, in pickle format, to
|
||||||
|
a datagram socket. The pickle which is sent is that of the LogRecord's
|
||||||
|
attribute dictionary (__dict__), so that the receiver does not need to
|
||||||
|
have the logging module installed in order to process the logging event.
|
||||||
|
|
||||||
|
To unpickle the record at the receiving end into a LogRecord, use the
|
||||||
|
makeLogRecord function.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, host, port):
|
||||||
|
"""
|
||||||
|
Initializes the handler with a specific host address and port.
|
||||||
|
"""
|
||||||
|
SocketHandler.__init__(self, host, port)
|
||||||
|
self.closeOnError = 0
|
||||||
|
|
||||||
|
def makeSocket(self):
|
||||||
|
"""
|
||||||
|
The factory method of SocketHandler is here overridden to create
|
||||||
|
a UDP socket (SOCK_DGRAM).
|
||||||
|
"""
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def send(self, s):
|
||||||
|
"""
|
||||||
|
Send a pickled string to a socket.
|
||||||
|
|
||||||
|
This function no longer allows for partial sends which can happen
|
||||||
|
when the network is busy - UDP does not guarantee delivery and
|
||||||
|
can deliver packets out of sequence.
|
||||||
|
"""
|
||||||
|
self.sock.sendto(s, (self.host, self.port))
|
||||||
|
|
||||||
|
class SysLogHandler(logging.Handler):
|
||||||
|
"""
|
||||||
|
A handler class which sends formatted logging records to a syslog
|
||||||
|
server. Based on Sam Rushing's syslog module:
|
||||||
|
http://www.nightmare.com/squirl/python-ext/misc/syslog.py
|
||||||
|
Contributed by Nicolas Untz (after which minor refactoring changes
|
||||||
|
have been made).
|
||||||
|
"""
|
||||||
|
|
||||||
|
# from <linux/sys/syslog.h>:
|
||||||
|
# ======================================================================
|
||||||
|
# priorities/facilities are encoded into a single 32-bit quantity, where
|
||||||
|
# the bottom 3 bits are the priority (0-7) and the top 28 bits are the
|
||||||
|
# facility (0-big number). Both the priorities and the facilities map
|
||||||
|
# roughly one-to-one to strings in the syslogd(8) source code. This
|
||||||
|
# mapping is included in this file.
|
||||||
|
#
|
||||||
|
# priorities (these are ordered)
|
||||||
|
|
||||||
|
LOG_EMERG = 0 # system is unusable
|
||||||
|
LOG_ALERT = 1 # action must be taken immediately
|
||||||
|
LOG_CRIT = 2 # critical conditions
|
||||||
|
LOG_ERR = 3 # error conditions
|
||||||
|
LOG_WARNING = 4 # warning conditions
|
||||||
|
LOG_NOTICE = 5 # normal but significant condition
|
||||||
|
LOG_INFO = 6 # informational
|
||||||
|
LOG_DEBUG = 7 # debug-level messages
|
||||||
|
|
||||||
|
# facility codes
|
||||||
|
LOG_KERN = 0 # kernel messages
|
||||||
|
LOG_USER = 1 # random user-level messages
|
||||||
|
LOG_MAIL = 2 # mail system
|
||||||
|
LOG_DAEMON = 3 # system daemons
|
||||||
|
LOG_AUTH = 4 # security/authorization messages
|
||||||
|
LOG_SYSLOG = 5 # messages generated internally by syslogd
|
||||||
|
LOG_LPR = 6 # line printer subsystem
|
||||||
|
LOG_NEWS = 7 # network news subsystem
|
||||||
|
LOG_UUCP = 8 # UUCP subsystem
|
||||||
|
LOG_CRON = 9 # clock daemon
|
||||||
|
LOG_AUTHPRIV = 10 # security/authorization messages (private)
|
||||||
|
|
||||||
|
# other codes through 15 reserved for system use
|
||||||
|
LOG_LOCAL0 = 16 # reserved for local use
|
||||||
|
LOG_LOCAL1 = 17 # reserved for local use
|
||||||
|
LOG_LOCAL2 = 18 # reserved for local use
|
||||||
|
LOG_LOCAL3 = 19 # reserved for local use
|
||||||
|
LOG_LOCAL4 = 20 # reserved for local use
|
||||||
|
LOG_LOCAL5 = 21 # reserved for local use
|
||||||
|
LOG_LOCAL6 = 22 # reserved for local use
|
||||||
|
LOG_LOCAL7 = 23 # reserved for local use
|
||||||
|
|
||||||
|
priority_names = {
|
||||||
|
"alert": LOG_ALERT,
|
||||||
|
"crit": LOG_CRIT,
|
||||||
|
"critical": LOG_CRIT,
|
||||||
|
"debug": LOG_DEBUG,
|
||||||
|
"emerg": LOG_EMERG,
|
||||||
|
"err": LOG_ERR,
|
||||||
|
"error": LOG_ERR, # DEPRECATED
|
||||||
|
"info": LOG_INFO,
|
||||||
|
"notice": LOG_NOTICE,
|
||||||
|
"panic": LOG_EMERG, # DEPRECATED
|
||||||
|
"warn": LOG_WARNING, # DEPRECATED
|
||||||
|
"warning": LOG_WARNING,
|
||||||
|
}
|
||||||
|
|
||||||
|
facility_names = {
|
||||||
|
"auth": LOG_AUTH,
|
||||||
|
"authpriv": LOG_AUTHPRIV,
|
||||||
|
"cron": LOG_CRON,
|
||||||
|
"daemon": LOG_DAEMON,
|
||||||
|
"kern": LOG_KERN,
|
||||||
|
"lpr": LOG_LPR,
|
||||||
|
"mail": LOG_MAIL,
|
||||||
|
"news": LOG_NEWS,
|
||||||
|
"security": LOG_AUTH, # DEPRECATED
|
||||||
|
"syslog": LOG_SYSLOG,
|
||||||
|
"user": LOG_USER,
|
||||||
|
"uucp": LOG_UUCP,
|
||||||
|
"local0": LOG_LOCAL0,
|
||||||
|
"local1": LOG_LOCAL1,
|
||||||
|
"local2": LOG_LOCAL2,
|
||||||
|
"local3": LOG_LOCAL3,
|
||||||
|
"local4": LOG_LOCAL4,
|
||||||
|
"local5": LOG_LOCAL5,
|
||||||
|
"local6": LOG_LOCAL6,
|
||||||
|
"local7": LOG_LOCAL7,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
|
||||||
|
"""
|
||||||
|
Initialize a handler.
|
||||||
|
|
||||||
|
If address is specified as a string, UNIX socket is used.
|
||||||
|
If facility is not specified, LOG_USER is used.
|
||||||
|
"""
|
||||||
|
logging.Handler.__init__(self)
|
||||||
|
|
||||||
|
self.address = address
|
||||||
|
self.facility = facility
|
||||||
|
if type(address) == types.StringType:
|
||||||
|
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||||
|
# syslog may require either DGRAM or STREAM sockets
|
||||||
|
try:
|
||||||
|
self.socket.connect(address)
|
||||||
|
except socket.error:
|
||||||
|
self.socket.close()
|
||||||
|
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
self.socket.connect(address)
|
||||||
|
self.unixsocket = 1
|
||||||
|
else:
|
||||||
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
self.unixsocket = 0
|
||||||
|
|
||||||
|
self.formatter = None
|
||||||
|
|
||||||
|
# curious: when talking to the unix-domain '/dev/log' socket, a
|
||||||
|
# zero-terminator seems to be required. this string is placed
|
||||||
|
# into a class variable so that it can be overridden if
|
||||||
|
# necessary.
|
||||||
|
log_format_string = '<%d>%s\000'
|
||||||
|
|
||||||
|
def encodePriority (self, facility, priority):
|
||||||
|
"""
|
||||||
|
Encode the facility and priority. You can pass in strings or
|
||||||
|
integers - if strings are passed, the facility_names and
|
||||||
|
priority_names mapping dictionaries are used to convert them to
|
||||||
|
integers.
|
||||||
|
"""
|
||||||
|
if type(facility) == types.StringType:
|
||||||
|
facility = self.facility_names[facility]
|
||||||
|
if type(priority) == types.StringType:
|
||||||
|
priority = self.priority_names[priority]
|
||||||
|
return (facility << 3) | priority
|
||||||
|
|
||||||
|
def close (self):
|
||||||
|
"""
|
||||||
|
Closes the socket.
|
||||||
|
"""
|
||||||
|
if self.unixsocket:
|
||||||
|
self.socket.close()
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""
|
||||||
|
Emit a record.
|
||||||
|
|
||||||
|
The record is formatted, and then sent to the syslog server. If
|
||||||
|
exception information is present, it is NOT sent to the server.
|
||||||
|
"""
|
||||||
|
msg = self.format(record)
|
||||||
|
"""
|
||||||
|
We need to convert record level to lowercase, maybe this will
|
||||||
|
change in the future.
|
||||||
|
"""
|
||||||
|
msg = self.log_format_string % (
|
||||||
|
self.encodePriority(self.facility,
|
||||||
|
string.lower(record.levelname)),
|
||||||
|
msg)
|
||||||
|
try:
|
||||||
|
if self.unixsocket:
|
||||||
|
self.socket.send(msg)
|
||||||
|
else:
|
||||||
|
self.socket.sendto(msg, self.address)
|
||||||
|
except:
|
||||||
|
self.handleError(record)
|
||||||
|
|
||||||
|
class SMTPHandler(logging.Handler):
|
||||||
|
"""
|
||||||
|
A handler class which sends an SMTP email for each logging event.
|
||||||
|
"""
|
||||||
|
def __init__(self, mailhost, fromaddr, toaddrs, subject):
|
||||||
|
"""
|
||||||
|
Initialize the handler.
|
||||||
|
|
||||||
|
Initialize the instance with the from and to addresses and subject
|
||||||
|
line of the email. To specify a non-standard SMTP port, use the
|
||||||
|
(host, port) tuple format for the mailhost argument.
|
||||||
|
"""
|
||||||
|
logging.Handler.__init__(self)
|
||||||
|
if type(mailhost) == types.TupleType:
|
||||||
|
host, port = mailhost
|
||||||
|
self.mailhost = host
|
||||||
|
self.mailport = port
|
||||||
|
else:
|
||||||
|
self.mailhost = mailhost
|
||||||
|
self.mailport = None
|
||||||
|
self.fromaddr = fromaddr
|
||||||
|
if type(toaddrs) == types.StringType:
|
||||||
|
toaddrs = [toaddrs]
|
||||||
|
self.toaddrs = toaddrs
|
||||||
|
self.subject = subject
|
||||||
|
|
||||||
|
def getSubject(self, record):
|
||||||
|
"""
|
||||||
|
Determine the subject for the email.
|
||||||
|
|
||||||
|
If you want to specify a subject line which is record-dependent,
|
||||||
|
override this method.
|
||||||
|
"""
|
||||||
|
return self.subject
|
||||||
|
|
||||||
|
weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||||
|
|
||||||
|
monthname = [None,
|
||||||
|
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||||
|
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||||
|
|
||||||
|
def date_time(self):
|
||||||
|
"""Return the current date and time formatted for a MIME header."""
|
||||||
|
year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time())
|
||||||
|
s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
|
||||||
|
self.weekdayname[wd],
|
||||||
|
day, self.monthname[month], year,
|
||||||
|
hh, mm, ss)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""
|
||||||
|
Emit a record.
|
||||||
|
|
||||||
|
Format the record and send it to the specified addressees.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import smtplib
|
||||||
|
port = self.mailport
|
||||||
|
if not port:
|
||||||
|
port = smtplib.SMTP_PORT
|
||||||
|
smtp = smtplib.SMTP(self.mailhost, port)
|
||||||
|
msg = self.format(record)
|
||||||
|
msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % (
|
||||||
|
self.fromaddr,
|
||||||
|
string.join(self.toaddrs, ","),
|
||||||
|
self.getSubject(record),
|
||||||
|
self.date_time(), msg)
|
||||||
|
smtp.sendmail(self.fromaddr, self.toaddrs, msg)
|
||||||
|
smtp.quit()
|
||||||
|
except:
|
||||||
|
self.handleError(record)
|
||||||
|
|
||||||
|
class NTEventLogHandler(logging.Handler):
|
||||||
|
"""
|
||||||
|
A handler class which sends events to the NT Event Log. Adds a
|
||||||
|
registry entry for the specified application name. If no dllname is
|
||||||
|
provided, win32service.pyd (which contains some basic message
|
||||||
|
placeholders) is used. Note that use of these placeholders will make
|
||||||
|
your event logs big, as the entire message source is held in the log.
|
||||||
|
If you want slimmer logs, you have to pass in the name of your own DLL
|
||||||
|
which contains the message definitions you want to use in the event log.
|
||||||
|
"""
|
||||||
|
def __init__(self, appname, dllname=None, logtype="Application"):
|
||||||
|
logging.Handler.__init__(self)
|
||||||
|
try:
|
||||||
|
import win32evtlogutil, win32evtlog
|
||||||
|
self.appname = appname
|
||||||
|
self._welu = win32evtlogutil
|
||||||
|
if not dllname:
|
||||||
|
dllname = os.path.split(self._welu.__file__)
|
||||||
|
dllname = os.path.split(dllname[0])
|
||||||
|
dllname = os.path.join(dllname[0], r'win32service.pyd')
|
||||||
|
self.dllname = dllname
|
||||||
|
self.logtype = logtype
|
||||||
|
self._welu.AddSourceToRegistry(appname, dllname, logtype)
|
||||||
|
self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
|
||||||
|
self.typemap = {
|
||||||
|
logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
|
||||||
|
logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
|
||||||
|
logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
|
||||||
|
logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
|
||||||
|
logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
|
||||||
|
}
|
||||||
|
except ImportError:
|
||||||
|
print "The Python Win32 extensions for NT (service, event "\
|
||||||
|
"logging) appear not to be available."
|
||||||
|
self._welu = None
|
||||||
|
|
||||||
|
def getMessageID(self, record):
|
||||||
|
"""
|
||||||
|
Return the message ID for the event record. If you are using your
|
||||||
|
own messages, you could do this by having the msg passed to the
|
||||||
|
logger being an ID rather than a formatting string. Then, in here,
|
||||||
|
you could use a dictionary lookup to get the message ID. This
|
||||||
|
version returns 1, which is the base message ID in win32service.pyd.
|
||||||
|
"""
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def getEventCategory(self, record):
|
||||||
|
"""
|
||||||
|
Return the event category for the record.
|
||||||
|
|
||||||
|
Override this if you want to specify your own categories. This version
|
||||||
|
returns 0.
|
||||||
|
"""
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def getEventType(self, record):
|
||||||
|
"""
|
||||||
|
Return the event type for the record.
|
||||||
|
|
||||||
|
Override this if you want to specify your own types. This version does
|
||||||
|
a mapping using the handler's typemap attribute, which is set up in
|
||||||
|
__init__() to a dictionary which contains mappings for DEBUG, INFO,
|
||||||
|
WARNING, ERROR and CRITICAL. If you are using your own levels you will
|
||||||
|
either need to override this method or place a suitable dictionary in
|
||||||
|
the handler's typemap attribute.
|
||||||
|
"""
|
||||||
|
return self.typemap.get(record.levelno, self.deftype)
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""
|
||||||
|
Emit a record.
|
||||||
|
|
||||||
|
Determine the message ID, event category and event type. Then
|
||||||
|
log the message in the NT event log.
|
||||||
|
"""
|
||||||
|
if self._welu:
|
||||||
|
try:
|
||||||
|
id = self.getMessageID(record)
|
||||||
|
cat = self.getEventCategory(record)
|
||||||
|
type = self.getEventType(record)
|
||||||
|
msg = self.format(record)
|
||||||
|
self._welu.ReportEvent(self.appname, id, cat, type, [msg])
|
||||||
|
except:
|
||||||
|
self.handleError(record)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""
|
||||||
|
Clean up this handler.
|
||||||
|
|
||||||
|
You can remove the application name from the registry as a
|
||||||
|
source of event log entries. However, if you do this, you will
|
||||||
|
not be able to see the events as you intended in the Event Log
|
||||||
|
Viewer - it needs to be able to access the registry to get the
|
||||||
|
DLL name.
|
||||||
|
"""
|
||||||
|
#self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HTTPHandler(logging.Handler):
|
||||||
|
"""
|
||||||
|
A class which sends records to a Web server, using either GET or
|
||||||
|
POST semantics.
|
||||||
|
"""
|
||||||
|
def __init__(self, host, url, method="GET"):
|
||||||
|
"""
|
||||||
|
Initialize the instance with the host, the request URL, and the method
|
||||||
|
("GET" or "POST")
|
||||||
|
"""
|
||||||
|
logging.Handler.__init__(self)
|
||||||
|
method = string.upper(method)
|
||||||
|
if method not in ["GET", "POST"]:
|
||||||
|
raise ValueError, "method must be GET or POST"
|
||||||
|
self.host = host
|
||||||
|
self.url = url
|
||||||
|
self.method = method
|
||||||
|
|
||||||
|
def mapLogRecord(self, record):
|
||||||
|
"""
|
||||||
|
Default implementation of mapping the log record into a dict
|
||||||
|
that is send as the CGI data. Overwrite in your class.
|
||||||
|
Contributed by Franz Glasner.
|
||||||
|
"""
|
||||||
|
return record.__dict__
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""
|
||||||
|
Emit a record.
|
||||||
|
|
||||||
|
Send the record to the Web server as an URL-encoded dictionary
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import httplib, urllib
|
||||||
|
h = httplib.HTTP(self.host)
|
||||||
|
url = self.url
|
||||||
|
data = urllib.urlencode(self.mapLogRecord(record))
|
||||||
|
if self.method == "GET":
|
||||||
|
if (string.find(url, '?') >= 0):
|
||||||
|
sep = '&'
|
||||||
|
else:
|
||||||
|
sep = '?'
|
||||||
|
url = url + "%c%s" % (sep, data)
|
||||||
|
h.putrequest(self.method, url)
|
||||||
|
if self.method == "POST":
|
||||||
|
h.putheader("Content-length", str(len(data)))
|
||||||
|
h.endheaders()
|
||||||
|
if self.method == "POST":
|
||||||
|
h.send(data)
|
||||||
|
h.getreply() #can't do anything with the result
|
||||||
|
except:
|
||||||
|
self.handleError(record)
|
||||||
|
|
||||||
|
class BufferingHandler(logging.Handler):
|
||||||
|
"""
|
||||||
|
A handler class which buffers logging records in memory. Whenever each
|
||||||
|
record is added to the buffer, a check is made to see if the buffer should
|
||||||
|
be flushed. If it should, then flush() is expected to do what's needed.
|
||||||
|
"""
|
||||||
|
def __init__(self, capacity):
|
||||||
|
"""
|
||||||
|
Initialize the handler with the buffer size.
|
||||||
|
"""
|
||||||
|
logging.Handler.__init__(self)
|
||||||
|
self.capacity = capacity
|
||||||
|
self.buffer = []
|
||||||
|
|
||||||
|
def shouldFlush(self, record):
|
||||||
|
"""
|
||||||
|
Should the handler flush its buffer?
|
||||||
|
|
||||||
|
Returns true if the buffer is up to capacity. This method can be
|
||||||
|
overridden to implement custom flushing strategies.
|
||||||
|
"""
|
||||||
|
return (len(self.buffer) >= self.capacity)
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""
|
||||||
|
Emit a record.
|
||||||
|
|
||||||
|
Append the record. If shouldFlush() tells us to, call flush() to process
|
||||||
|
the buffer.
|
||||||
|
"""
|
||||||
|
self.buffer.append(record)
|
||||||
|
if self.shouldFlush(record):
|
||||||
|
self.flush()
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
"""
|
||||||
|
Override to implement custom flushing behaviour.
|
||||||
|
|
||||||
|
This version just zaps the buffer to empty.
|
||||||
|
"""
|
||||||
|
self.buffer = []
|
||||||
|
|
||||||
|
class MemoryHandler(BufferingHandler):
|
||||||
|
"""
|
||||||
|
A handler class which buffers logging records in memory, periodically
|
||||||
|
flushing them to a target handler. Flushing occurs whenever the buffer
|
||||||
|
is full, or when an event of a certain severity or greater is seen.
|
||||||
|
"""
|
||||||
|
def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
|
||||||
|
"""
|
||||||
|
Initialize the handler with the buffer size, the level at which
|
||||||
|
flushing should occur and an optional target.
|
||||||
|
|
||||||
|
Note that without a target being set either here or via setTarget(),
|
||||||
|
a MemoryHandler is no use to anyone!
|
||||||
|
"""
|
||||||
|
BufferingHandler.__init__(self, capacity)
|
||||||
|
self.flushLevel = flushLevel
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
def shouldFlush(self, record):
|
||||||
|
"""
|
||||||
|
Check for buffer full or a record at the flushLevel or higher.
|
||||||
|
"""
|
||||||
|
return (len(self.buffer) >= self.capacity) or \
|
||||||
|
(record.levelno >= self.flushLevel)
|
||||||
|
|
||||||
|
def setTarget(self, target):
|
||||||
|
"""
|
||||||
|
Set the target handler for this handler.
|
||||||
|
"""
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
"""
|
||||||
|
For a MemoryHandler, flushing means just sending the buffered
|
||||||
|
records to the target, if there is one. Override if you want
|
||||||
|
different behaviour.
|
||||||
|
"""
|
||||||
|
if self.target:
|
||||||
|
for record in self.buffer:
|
||||||
|
self.target.handle(record)
|
||||||
|
self.buffer = []
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""
|
||||||
|
Flush, set the target to None and lose the buffer.
|
||||||
|
"""
|
||||||
|
self.flush()
|
||||||
|
self.target = None
|
||||||
|
self.buffer = []
|
2931
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/feedparser.py
Executable file
1480
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/htmltmpl.py
Executable file
354
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/sanitize.py
Executable file
|
@ -0,0 +1,354 @@
|
||||||
|
"""
|
||||||
|
sanitize: bringing sanitiy to world of messed-up data
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = ["Mark Pilgrim <http://diveintomark.org/>",
|
||||||
|
"Aaron Swartz <http://www.aaronsw.com/>"]
|
||||||
|
__contributors__ = ["Sam Ruby <http://intertwingly.net/>"]
|
||||||
|
__license__ = "BSD"
|
||||||
|
__version__ = "0.25"
|
||||||
|
|
||||||
|
_debug = 0
|
||||||
|
|
||||||
|
# If you want sanitize to automatically run HTML markup through HTML Tidy, set
|
||||||
|
# this to 1. Requires mxTidy <http://www.egenix.com/files/python/mxTidy.html>
|
||||||
|
# or utidylib <http://utidylib.berlios.de/>.
|
||||||
|
TIDY_MARKUP = 0
|
||||||
|
|
||||||
|
# List of Python interfaces for HTML Tidy, in order of preference. Only useful
|
||||||
|
# if TIDY_MARKUP = 1
|
||||||
|
PREFERRED_TIDY_INTERFACES = ["uTidy", "mxTidy"]
|
||||||
|
|
||||||
|
import sgmllib, re
|
||||||
|
|
||||||
|
# chardet library auto-detects character encodings
|
||||||
|
# Download from http://chardet.feedparser.org/
|
||||||
|
try:
|
||||||
|
import chardet
|
||||||
|
if _debug:
|
||||||
|
import chardet.constants
|
||||||
|
chardet.constants._debug = 1
|
||||||
|
|
||||||
|
_chardet = lambda data: chardet.detect(data)['encoding']
|
||||||
|
except:
|
||||||
|
chardet = None
|
||||||
|
_chardet = lambda data: None
|
||||||
|
|
||||||
|
class _BaseHTMLProcessor(sgmllib.SGMLParser):
|
||||||
|
elements_no_end_tag = ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr',
|
||||||
|
'img', 'input', 'isindex', 'link', 'meta', 'param']
|
||||||
|
|
||||||
|
_r_barebang = re.compile(r'<!((?!DOCTYPE|--|\[))', re.IGNORECASE)
|
||||||
|
_r_bareamp = re.compile("&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)")
|
||||||
|
_r_shorttag = re.compile(r'<([^<\s]+?)\s*/>')
|
||||||
|
|
||||||
|
def __init__(self, encoding):
|
||||||
|
self.encoding = encoding
|
||||||
|
if _debug: sys.stderr.write('entering BaseHTMLProcessor, encoding=%s\n' % self.encoding)
|
||||||
|
sgmllib.SGMLParser.__init__(self)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.pieces = []
|
||||||
|
sgmllib.SGMLParser.reset(self)
|
||||||
|
|
||||||
|
def _shorttag_replace(self, match):
|
||||||
|
tag = match.group(1)
|
||||||
|
if tag in self.elements_no_end_tag:
|
||||||
|
return '<' + tag + ' />'
|
||||||
|
else:
|
||||||
|
return '<' + tag + '></' + tag + '>'
|
||||||
|
|
||||||
|
def feed(self, data):
|
||||||
|
data = self._r_barebang.sub(r'<!\1', data)
|
||||||
|
data = self._r_bareamp.sub("&", data)
|
||||||
|
data = self._r_shorttag.sub(self._shorttag_replace, data)
|
||||||
|
if self.encoding and type(data) == type(u''):
|
||||||
|
data = data.encode(self.encoding)
|
||||||
|
sgmllib.SGMLParser.feed(self, data)
|
||||||
|
|
||||||
|
def normalize_attrs(self, attrs):
|
||||||
|
# utility method to be called by descendants
|
||||||
|
attrs = [(k.lower(), v) for k, v in attrs]
|
||||||
|
attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def unknown_starttag(self, tag, attrs):
|
||||||
|
# called for each start tag
|
||||||
|
# attrs is a list of (attr, value) tuples
|
||||||
|
# e.g. for <pre class='screen'>, tag='pre', attrs=[('class', 'screen')]
|
||||||
|
if _debug: sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
|
||||||
|
uattrs = []
|
||||||
|
# thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
|
||||||
|
for key, value in attrs:
|
||||||
|
if type(value) != type(u''):
|
||||||
|
value = unicode(value, self.encoding)
|
||||||
|
uattrs.append((unicode(key, self.encoding), value))
|
||||||
|
strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs]).encode(self.encoding)
|
||||||
|
if tag in self.elements_no_end_tag:
|
||||||
|
self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
|
||||||
|
else:
|
||||||
|
self.pieces.append('<%(tag)s%(strattrs)s>' % locals())
|
||||||
|
|
||||||
|
def unknown_endtag(self, tag):
|
||||||
|
# called for each end tag, e.g. for </pre>, tag will be 'pre'
|
||||||
|
# Reconstruct the original end tag.
|
||||||
|
if tag not in self.elements_no_end_tag:
|
||||||
|
self.pieces.append("</%(tag)s>" % locals())
|
||||||
|
|
||||||
|
def handle_charref(self, ref):
|
||||||
|
# called for each character reference, e.g. for ' ', ref will be '160'
|
||||||
|
# Reconstruct the original character reference.
|
||||||
|
self.pieces.append('&#%(ref)s;' % locals())
|
||||||
|
|
||||||
|
def handle_entityref(self, ref):
|
||||||
|
# called for each entity reference, e.g. for '©', ref will be 'copy'
|
||||||
|
# Reconstruct the original entity reference.
|
||||||
|
self.pieces.append('&%(ref)s;' % locals())
|
||||||
|
|
||||||
|
def handle_data(self, text):
|
||||||
|
# called for each block of plain text, i.e. outside of any tag and
|
||||||
|
# not containing any character or entity references
|
||||||
|
# Store the original text verbatim.
|
||||||
|
if _debug: sys.stderr.write('_BaseHTMLProcessor, handle_text, text=%s\n' % text)
|
||||||
|
self.pieces.append(text)
|
||||||
|
|
||||||
|
def handle_comment(self, text):
|
||||||
|
# called for each HTML comment, e.g. <!-- insert Javascript code here -->
|
||||||
|
# Reconstruct the original comment.
|
||||||
|
self.pieces.append('<!--%(text)s-->' % locals())
|
||||||
|
|
||||||
|
def handle_pi(self, text):
|
||||||
|
# called for each processing instruction, e.g. <?instruction>
|
||||||
|
# Reconstruct original processing instruction.
|
||||||
|
self.pieces.append('<?%(text)s>' % locals())
|
||||||
|
|
||||||
|
def handle_decl(self, text):
|
||||||
|
# called for the DOCTYPE, if present, e.g.
|
||||||
|
# <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||||
|
# "http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
# Reconstruct original DOCTYPE
|
||||||
|
self.pieces.append('<!%(text)s>' % locals())
|
||||||
|
|
||||||
|
_new_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9:]*\s*').match
|
||||||
|
def _scan_name(self, i, declstartpos):
|
||||||
|
rawdata = self.rawdata
|
||||||
|
n = len(rawdata)
|
||||||
|
if i == n:
|
||||||
|
return None, -1
|
||||||
|
m = self._new_declname_match(rawdata, i)
|
||||||
|
if m:
|
||||||
|
s = m.group()
|
||||||
|
name = s.strip()
|
||||||
|
if (i + len(s)) == n:
|
||||||
|
return None, -1 # end of buffer
|
||||||
|
return name.lower(), m.end()
|
||||||
|
else:
|
||||||
|
self.handle_data(rawdata)
|
||||||
|
# self.updatepos(declstartpos, i)
|
||||||
|
return None, -1
|
||||||
|
|
||||||
|
def output(self):
|
||||||
|
'''Return processed HTML as a single string'''
|
||||||
|
return ''.join([str(p) for p in self.pieces])
|
||||||
|
|
||||||
|
class _HTMLSanitizer(_BaseHTMLProcessor):
|
||||||
|
acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area', 'b', 'big',
|
||||||
|
'blockquote', 'br', 'button', 'caption', 'center', 'cite', 'code', 'col',
|
||||||
|
'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset',
|
||||||
|
'font', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input',
|
||||||
|
'ins', 'kbd', 'label', 'legend', 'li', 'map', 'menu', 'ol', 'optgroup',
|
||||||
|
'option', 'p', 'pre', 'q', 's', 'samp', 'select', 'small', 'span', 'strike',
|
||||||
|
'strong', 'sub', 'sup', 'table', 'textarea', 'tbody', 'td', 'tfoot', 'th',
|
||||||
|
'thead', 'tr', 'tt', 'u', 'ul', 'var']
|
||||||
|
|
||||||
|
acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey',
|
||||||
|
'action', 'align', 'alt', 'axis', 'border', 'cellpadding', 'cellspacing',
|
||||||
|
'char', 'charoff', 'charset', 'checked', 'cite', 'class', 'clear', 'cols',
|
||||||
|
'colspan', 'color', 'compact', 'coords', 'datetime', 'dir', 'disabled',
|
||||||
|
'enctype', 'for', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace',
|
||||||
|
'id', 'ismap', 'label', 'lang', 'longdesc', 'maxlength', 'media', 'method',
|
||||||
|
'multiple', 'name', 'nohref', 'noshade', 'nowrap', 'prompt', 'readonly',
|
||||||
|
'rel', 'rev', 'rows', 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size',
|
||||||
|
'span', 'src', 'start', 'summary', 'tabindex', 'target', 'title', 'type',
|
||||||
|
'usemap', 'valign', 'value', 'vspace', 'width']
|
||||||
|
|
||||||
|
ignorable_elements = ['script', 'applet', 'style']
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
_BaseHTMLProcessor.reset(self)
|
||||||
|
self.tag_stack = []
|
||||||
|
self.ignore_level = 0
|
||||||
|
|
||||||
|
def feed(self, data):
|
||||||
|
_BaseHTMLProcessor.feed(self, data)
|
||||||
|
while self.tag_stack:
|
||||||
|
_BaseHTMLProcessor.unknown_endtag(self, self.tag_stack.pop())
|
||||||
|
|
||||||
|
def unknown_starttag(self, tag, attrs):
|
||||||
|
if tag in self.ignorable_elements:
|
||||||
|
self.ignore_level += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.ignore_level:
|
||||||
|
return
|
||||||
|
|
||||||
|
if tag in self.acceptable_elements:
|
||||||
|
attrs = self.normalize_attrs(attrs)
|
||||||
|
attrs = [(key, value) for key, value in attrs if key in self.acceptable_attributes]
|
||||||
|
if tag not in self.elements_no_end_tag:
|
||||||
|
self.tag_stack.append(tag)
|
||||||
|
_BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
|
||||||
|
|
||||||
|
def unknown_endtag(self, tag):
|
||||||
|
if tag in self.ignorable_elements:
|
||||||
|
self.ignore_level -= 1
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.ignore_level:
|
||||||
|
return
|
||||||
|
|
||||||
|
if tag in self.acceptable_elements and tag not in self.elements_no_end_tag:
|
||||||
|
match = False
|
||||||
|
while self.tag_stack:
|
||||||
|
top = self.tag_stack.pop()
|
||||||
|
if top == tag:
|
||||||
|
match = True
|
||||||
|
break
|
||||||
|
_BaseHTMLProcessor.unknown_endtag(self, top)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
_BaseHTMLProcessor.unknown_endtag(self, tag)
|
||||||
|
|
||||||
|
def handle_pi(self, text):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_decl(self, text):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_data(self, text):
|
||||||
|
if not self.ignore_level:
|
||||||
|
text = text.replace('<', '')
|
||||||
|
_BaseHTMLProcessor.handle_data(self, text)
|
||||||
|
|
||||||
|
def HTML(htmlSource, encoding='utf8'):
|
||||||
|
p = _HTMLSanitizer(encoding)
|
||||||
|
p.feed(htmlSource)
|
||||||
|
data = p.output()
|
||||||
|
if TIDY_MARKUP:
|
||||||
|
# loop through list of preferred Tidy interfaces looking for one that's installed,
|
||||||
|
# then set up a common _tidy function to wrap the interface-specific API.
|
||||||
|
_tidy = None
|
||||||
|
for tidy_interface in PREFERRED_TIDY_INTERFACES:
|
||||||
|
try:
|
||||||
|
if tidy_interface == "uTidy":
|
||||||
|
from tidy import parseString as _utidy
|
||||||
|
def _tidy(data, **kwargs):
|
||||||
|
return str(_utidy(data, **kwargs))
|
||||||
|
break
|
||||||
|
elif tidy_interface == "mxTidy":
|
||||||
|
from mx.Tidy import Tidy as _mxtidy
|
||||||
|
def _tidy(data, **kwargs):
|
||||||
|
nerrors, nwarnings, data, errordata = _mxtidy.tidy(data, **kwargs)
|
||||||
|
return data
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if _tidy:
|
||||||
|
utf8 = type(data) == type(u'')
|
||||||
|
if utf8:
|
||||||
|
data = data.encode('utf-8')
|
||||||
|
data = _tidy(data, output_xhtml=1, numeric_entities=1, wrap=0, char_encoding="utf8")
|
||||||
|
if utf8:
|
||||||
|
data = unicode(data, 'utf-8')
|
||||||
|
if data.count('<body'):
|
||||||
|
data = data.split('<body', 1)[1]
|
||||||
|
if data.count('>'):
|
||||||
|
data = data.split('>', 1)[1]
|
||||||
|
if data.count('</body'):
|
||||||
|
data = data.split('</body', 1)[0]
|
||||||
|
data = data.strip().replace('\r\n', '\n')
|
||||||
|
return data
|
||||||
|
|
||||||
|
unicode_bom_map = {
|
||||||
|
'\x00\x00\xfe\xff': 'utf-32be',
|
||||||
|
'\xff\xfe\x00\x00': 'utf-32le',
|
||||||
|
'\xfe\xff##': 'utf-16be',
|
||||||
|
'\xff\xfe##': 'utf-16le',
|
||||||
|
'\xef\bb\bf': 'utf-8'
|
||||||
|
}
|
||||||
|
xml_bom_map = {
|
||||||
|
'\x00\x00\x00\x3c': 'utf-32be',
|
||||||
|
'\x3c\x00\x00\x00': 'utf-32le',
|
||||||
|
'\x00\x3c\x00\x3f': 'utf-16be',
|
||||||
|
'\x3c\x00\x3f\x00': 'utf-16le',
|
||||||
|
'\x3c\x3f\x78\x6d': 'utf-8', # or equivalent
|
||||||
|
'\x4c\x6f\xa7\x94': 'ebcdic'
|
||||||
|
}
|
||||||
|
|
||||||
|
_ebcdic_to_ascii_map = None
|
||||||
|
def _ebcdic_to_ascii(s):
|
||||||
|
global _ebcdic_to_ascii_map
|
||||||
|
if not _ebcdic_to_ascii_map:
|
||||||
|
emap = (
|
||||||
|
0,1,2,3,156,9,134,127,151,141,142,11,12,13,14,15,
|
||||||
|
16,17,18,19,157,133,8,135,24,25,146,143,28,29,30,31,
|
||||||
|
128,129,130,131,132,10,23,27,136,137,138,139,140,5,6,7,
|
||||||
|
144,145,22,147,148,149,150,4,152,153,154,155,20,21,158,26,
|
||||||
|
32,160,161,162,163,164,165,166,167,168,91,46,60,40,43,33,
|
||||||
|
38,169,170,171,172,173,174,175,176,177,93,36,42,41,59,94,
|
||||||
|
45,47,178,179,180,181,182,183,184,185,124,44,37,95,62,63,
|
||||||
|
186,187,188,189,190,191,192,193,194,96,58,35,64,39,61,34,
|
||||||
|
195,97,98,99,100,101,102,103,104,105,196,197,198,199,200,201,
|
||||||
|
202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208,
|
||||||
|
209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215,
|
||||||
|
216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,
|
||||||
|
123,65,66,67,68,69,70,71,72,73,232,233,234,235,236,237,
|
||||||
|
125,74,75,76,77,78,79,80,81,82,238,239,240,241,242,243,
|
||||||
|
92,159,83,84,85,86,87,88,89,90,244,245,246,247,248,249,
|
||||||
|
48,49,50,51,52,53,54,55,56,57,250,251,252,253,254,255
|
||||||
|
)
|
||||||
|
import string
|
||||||
|
_ebcdic_to_ascii_map = string.maketrans( \
|
||||||
|
''.join(map(chr, range(256))), ''.join(map(chr, emap)))
|
||||||
|
return s.translate(_ebcdic_to_ascii_map)
|
||||||
|
|
||||||
|
def _startswithbom(text, bom):
|
||||||
|
for i, c in enumerate(bom):
|
||||||
|
if c == '#':
|
||||||
|
if text[i] == '\x00':
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if text[i] != c:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _detectbom(text, bom_map=unicode_bom_map):
|
||||||
|
for bom, encoding in bom_map.iteritems():
|
||||||
|
if _startswithbom(text, bom):
|
||||||
|
return encoding
|
||||||
|
return None
|
||||||
|
|
||||||
|
def characters(text, isXML=False, guess=None):
|
||||||
|
"""
|
||||||
|
Takes a string text of unknown encoding and tries to
|
||||||
|
provide a Unicode string for it.
|
||||||
|
"""
|
||||||
|
_triedEncodings = []
|
||||||
|
def tryEncoding(encoding):
|
||||||
|
if encoding and encoding not in _triedEncodings:
|
||||||
|
if encoding == 'ebcdic':
|
||||||
|
return _ebcdic_to_ascii(text)
|
||||||
|
try:
|
||||||
|
return unicode(text, encoding)
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
pass
|
||||||
|
_triedEncodings.append(encoding)
|
||||||
|
|
||||||
|
return (
|
||||||
|
tryEncoding(guess) or
|
||||||
|
tryEncoding(_detectbom(text)) or
|
||||||
|
isXML and tryEncoding(_detectbom(text, xml_bom_map)) or
|
||||||
|
tryEncoding(_chardet(text)) or
|
||||||
|
tryEncoding('utf8') or
|
||||||
|
tryEncoding('windows-1252') or
|
||||||
|
tryEncoding('iso-8859-1'))
|
38
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/tests/test_channel.py
Executable file
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import planet
|
||||||
|
import tempfile
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
class FakePlanet:
|
||||||
|
"""
|
||||||
|
A dummy Planet object that's enough to fool the
|
||||||
|
Channel.__init__ method
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.cache_directory = tempfile.gettempdir()
|
||||||
|
self.config = ConfigParser.ConfigParser()
|
||||||
|
|
||||||
|
class FeedInformationTest(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
Test the Channel.feed_information method
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.url = 'URL'
|
||||||
|
self.changed_url = 'Changed URL'
|
||||||
|
self.channel = planet.Channel(FakePlanet(), self.url)
|
||||||
|
|
||||||
|
def test_unchangedurl(self):
|
||||||
|
self.assertEqual(self.channel.feed_information(), '<%s>' % self.url)
|
||||||
|
|
||||||
|
def test_changedurl(self):
|
||||||
|
# change the URL directly
|
||||||
|
self.channel.url = self.changed_url
|
||||||
|
self.assertEqual(self.channel.feed_information(),
|
||||||
|
"<%s> (formerly <%s>)" % (self.changed_url, self.url))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
71
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/tests/test_main.py
Executable file
|
@ -0,0 +1,71 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import os, sys, shutil, errno, unittest
|
||||||
|
from ConfigParser import ConfigParser
|
||||||
|
from StringIO import StringIO
|
||||||
|
import planet
|
||||||
|
|
||||||
|
class MainTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_minimal(self):
|
||||||
|
configp = ConfigParser()
|
||||||
|
my_planet = planet.Planet(configp)
|
||||||
|
my_planet.run("Planet Name", "http://example.com", [])
|
||||||
|
|
||||||
|
def test_onefeed(self):
|
||||||
|
configp = ConfigParser()
|
||||||
|
configp.readfp(StringIO("""[http://www.example.com/]
|
||||||
|
name = Mary
|
||||||
|
"""))
|
||||||
|
my_planet = planet.Planet(configp)
|
||||||
|
my_planet.run("Planet Name", "http://example.com", [], True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_generateall(self):
|
||||||
|
configp = ConfigParser()
|
||||||
|
configp.readfp(StringIO("""[http://www.example.com/]
|
||||||
|
name = Mary
|
||||||
|
"""))
|
||||||
|
my_planet = planet.Planet(configp)
|
||||||
|
my_planet.run("Planet Name", "http://example.com", [], True)
|
||||||
|
basedir = os.path.join(os.path.dirname(os.path.abspath(sys.modules[__name__].__file__)), 'data')
|
||||||
|
os.mkdir(self.output_dir)
|
||||||
|
t_file_names = ['simple', 'simple2']
|
||||||
|
self._remove_cached_templates(basedir, t_file_names)
|
||||||
|
t_files = [os.path.join(basedir, t_file) + '.tmpl' for t_file in t_file_names]
|
||||||
|
my_planet.generate_all_files(t_files, "Planet Name",
|
||||||
|
'http://example.com/', 'http://example.com/feed/', 'Mary', 'mary@example.com')
|
||||||
|
for file_name in t_file_names:
|
||||||
|
name = os.path.join(self.output_dir, file_name)
|
||||||
|
content = file(name).read()
|
||||||
|
self.assertEqual(content, 'Mary\n')
|
||||||
|
|
||||||
|
def _remove_cached_templates(self, basedir, template_files):
|
||||||
|
"""
|
||||||
|
Remove the .tmplc files and force them to be rebuilt.
|
||||||
|
|
||||||
|
This is required mainly so that the tests don't fail in mysterious ways in
|
||||||
|
directories that have been moved, eg 'branches/my-branch' to
|
||||||
|
'branches/mysterious-branch' -- the .tmplc files seem to remember their full
|
||||||
|
path
|
||||||
|
"""
|
||||||
|
for file in template_files:
|
||||||
|
path = os.path.join(basedir, file + '.tmplc')
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
except OSError, e:
|
||||||
|
# we don't care about the file not being there, we care about
|
||||||
|
# everything else
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(MainTest, self).setUp()
|
||||||
|
self.output_dir = 'output'
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(MainTest, self).tearDown()
|
||||||
|
shutil.rmtree(self.output_dir, ignore_errors = True)
|
||||||
|
shutil.rmtree('cache', ignore_errors = True)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
125
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/tests/test_sanitize.py
Executable file
|
@ -0,0 +1,125 @@
|
||||||
|
# adapted from http://www.iamcal.com/publish/articles/php/processing_html_part_2/
|
||||||
|
# and from http://feedparser.org/tests/wellformed/sanitize/
|
||||||
|
# by Aaron Swartz, 2006, public domain
|
||||||
|
|
||||||
|
import unittest, new
|
||||||
|
from planet import sanitize
|
||||||
|
|
||||||
|
class SanitizeTest(unittest.TestCase): pass
|
||||||
|
|
||||||
|
# each call to HTML adds a test case to SanitizeTest
|
||||||
|
testcases = 0
|
||||||
|
def HTML(a, b):
|
||||||
|
global testcases
|
||||||
|
testcases += 1
|
||||||
|
func = lambda self: self.assertEqual(sanitize.HTML(a), b)
|
||||||
|
method = new.instancemethod(func, None, SanitizeTest)
|
||||||
|
setattr(SanitizeTest, "test_%d" % testcases, method)
|
||||||
|
|
||||||
|
## basics
|
||||||
|
HTML("","")
|
||||||
|
HTML("hello","hello")
|
||||||
|
|
||||||
|
## balancing tags
|
||||||
|
HTML("<b>hello","<b>hello</b>")
|
||||||
|
HTML("hello<b>","hello<b></b>")
|
||||||
|
HTML("hello</b>","hello")
|
||||||
|
HTML("hello<b/>","hello<b></b>")
|
||||||
|
HTML("<b><b><b>hello","<b><b><b>hello</b></b></b>")
|
||||||
|
HTML("</b><b>","<b></b>")
|
||||||
|
|
||||||
|
## trailing slashes
|
||||||
|
HTML('<img>','<img />')
|
||||||
|
HTML('<img/>','<img />')
|
||||||
|
HTML('<b/></b>','<b></b>')
|
||||||
|
|
||||||
|
## balancing angle brakets
|
||||||
|
HTML('<img src="foo"','')
|
||||||
|
HTML('b>','b>')
|
||||||
|
HTML('<img src="foo"/','')
|
||||||
|
HTML('>','>')
|
||||||
|
HTML('foo<b','foo')
|
||||||
|
HTML('b>foo','b>foo')
|
||||||
|
HTML('><b','>')
|
||||||
|
HTML('b><','b>')
|
||||||
|
HTML('><b>','><b></b>')
|
||||||
|
|
||||||
|
## attributes
|
||||||
|
HTML('<img src=foo>','<img src="foo" />')
|
||||||
|
HTML('<img asrc=foo>','<img />')
|
||||||
|
HTML('<img src=test test>','<img src="test" />')
|
||||||
|
|
||||||
|
## dangerous tags (a small sample)
|
||||||
|
sHTML = lambda x: HTML(x, 'safe <b>description</b>')
|
||||||
|
sHTML('safe<applet code="foo.class" codebase="http://example.com/"></applet> <b>description</b>')
|
||||||
|
sHTML('<notinventedyet>safe</notinventedyet> <b>description</b>')
|
||||||
|
sHTML('<blink>safe</blink> <b>description</b>')
|
||||||
|
sHTML('safe<embed src="http://example.com/"> <b>description</b>')
|
||||||
|
sHTML('safe<frameset rows="*"><frame src="http://example.com/"></frameset> <b>description</b>')
|
||||||
|
sHTML('safe<iframe src="http://example.com/"> <b>description</b></iframe>')
|
||||||
|
sHTML('safe<link rel="stylesheet" type="text/css" href="http://example.com/evil.css"> <b>description</b>')
|
||||||
|
sHTML('safe<meta http-equiv="Refresh" content="0; URL=http://example.com/"> <b>description</b>')
|
||||||
|
sHTML('safe<object classid="clsid:C932BA85-4374-101B-A56C-00AA003668DC"> <b>description</b>')
|
||||||
|
sHTML('safe<script type="text/javascript">location.href=\'http:/\'+\'/example.com/\';</script> <b>description</b>')
|
||||||
|
|
||||||
|
for x in ['onabort', 'onblur', 'onchange', 'onclick', 'ondblclick', 'onerror', 'onfocus', 'onkeydown', 'onkeypress', 'onkeyup', 'onload', 'onmousedown', 'onmouseout', 'onmouseover', 'onmouseup', 'onreset', 'resize', 'onsubmit', 'onunload']:
|
||||||
|
HTML('<img src="http://www.ragingplatypus.com/i/cam-full.jpg" %s="location.href=\'http://www.ragingplatypus.com/\';" />' % x,
|
||||||
|
'<img src="http://www.ragingplatypus.com/i/cam-full.jpg" />')
|
||||||
|
|
||||||
|
HTML('<a href="http://www.ragingplatypus.com/" style="display:block; position:absolute; left:0; top:0; width:100%; height:100%; z-index:1; background-color:black; background-image:url(http://www.ragingplatypus.com/i/cam-full.jpg); background-x:center; background-y:center; background-repeat:repeat;">never trust your upstream platypus</a>', '<a href="http://www.ragingplatypus.com/">never trust your upstream platypus</a>')
|
||||||
|
|
||||||
|
## ignorables
|
||||||
|
HTML('foo<style>bar', 'foo')
|
||||||
|
HTML('foo<style>bar</style>', 'foo')
|
||||||
|
|
||||||
|
## non-allowed tags
|
||||||
|
HTML('<script>','')
|
||||||
|
HTML('<script','')
|
||||||
|
HTML('<script/>','')
|
||||||
|
HTML('</script>','')
|
||||||
|
HTML('<script woo=yay>','')
|
||||||
|
HTML('<script woo="yay">','')
|
||||||
|
HTML('<script woo="yay>','')
|
||||||
|
HTML('<script woo="yay<b>','')
|
||||||
|
HTML('<script<script>>','')
|
||||||
|
HTML('<<script>script<script>>','')
|
||||||
|
HTML('<<script><script>>','')
|
||||||
|
HTML('<<script>script>>','')
|
||||||
|
HTML('<<script<script>>','')
|
||||||
|
|
||||||
|
## bad protocols
|
||||||
|
HTML('<a href="http://foo">bar</a>', '<a href="http://foo">bar</a>')
|
||||||
|
HTML('<a href="ftp://foo">bar</a>', '<a href="ftp://foo">bar</a>')
|
||||||
|
HTML('<a href="mailto:foo">bar</a>', '<a href="mailto:foo">bar</a>')
|
||||||
|
|
||||||
|
# not yet supported:
|
||||||
|
#HTML('<a href="javascript:foo">bar</a>', '<a href="#foo">bar</a>')
|
||||||
|
#HTML('<a href="java script:foo">bar</a>', '<a href="#foo">bar</a>')
|
||||||
|
#HTML('<a href="java\tscript:foo">bar</a>', '<a href="#foo">bar</a>')
|
||||||
|
#HTML('<a href="java\nscript:foo">bar</a>', '<a href="#foo">bar</a>')
|
||||||
|
#HTML('<a href="java'+chr(1)+'script:foo">bar</a>', '<a href="#foo">bar</a>')
|
||||||
|
#HTML('<a href="jscript:foo">bar</a>', '<a href="#foo">bar</a>')
|
||||||
|
#HTML('<a href="vbscript:foo">bar</a>', '<a href="#foo">bar</a>')
|
||||||
|
#HTML('<a href="view-source:foo">bar</a>', '<a href="#foo">bar</a>')
|
||||||
|
|
||||||
|
## auto closers
|
||||||
|
HTML('<img src="a">', '<img src="a" />')
|
||||||
|
HTML('<img src="a">foo</img>', '<img src="a" />foo')
|
||||||
|
HTML('</img>', '')
|
||||||
|
|
||||||
|
## crazy: http://alpha-geek.com/example/crazy_html2.html
|
||||||
|
HTML('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r\n\r\n<html xmlns="http://www.w3.org/1999/xhtml">\r\n<head>\r\n<title>Crazy HTML -- Can Your Regex Parse This?</title>\r\n</head>\r\n<body notRealAttribute="value"onload="executeMe();"foo="bar"\r\n\r\n>\r\n<!-- <script> -->\r\n\r\n<!-- \r\n\t<script> \r\n-->\r\n\r\n</script>\r\n\r\n\r\n<script\r\n\r\n\r\n>\r\n\r\nfunction executeMe()\r\n{\r\n\r\n\r\n\r\n\r\n/* <script> \r\nfunction am_i_javascript()\r\n{\r\n\tvar str = "Some innocuously commented out stuff";\r\n}\r\n< /script>\r\n*/\r\n\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\talert("Executed");\r\n}\r\n\r\n </script\r\n\r\n\r\n\r\n>\r\n<h1>Did The Javascript Execute?</h1>\r\n<div notRealAttribute="value\r\n"onmouseover="\r\nexecuteMe();\r\n"foo="bar">\r\nI will execute here, too, if you mouse over me\r\n</div>\r\nThis is to keep you guys honest...<br />\r\nI like ontonology. I like to script ontology. Though, script>style>this.\r\n</body>\r\n</html>', 'Crazy HTML -- Can Your Regex Parse This?\n\n\n<!-- <script> -->\n\n<!-- \n\t<script> \n-->\n\n\n\nfunction executeMe()\n{\n\n\n\n\n/* \n<h1>Did The Javascript Execute?</h1>\n<div>\nI will execute here, too, if you mouse over me\n</div>\nThis is to keep you guys honest...<br />\nI like ontonology. I like to script ontology. Though, script>style>this.')
|
||||||
|
|
||||||
|
# valid entity references
|
||||||
|
HTML(" "," ");
|
||||||
|
HTML(" "," ");
|
||||||
|
HTML(" "," ");
|
||||||
|
HTML(" "," ");
|
||||||
|
|
||||||
|
# unescaped ampersands
|
||||||
|
HTML("AT&T","AT&T");
|
||||||
|
HTML("http://example.org?a=1&b=2","http://example.org?a=1&b=2");
|
||||||
|
|
||||||
|
# quote characters
|
||||||
|
HTML('<a title=""">quote</a>','<a title=""">quote</a>')
|
||||||
|
HTML('<a title="'">quote</a>','<a title="'">quote</a>')
|
424
DJAGEN/branches/mustafa_branch/djagen/gezegen/planet/timeoutsocket.py
Executable file
|
@ -0,0 +1,424 @@
|
||||||
|
|
||||||
|
####
|
||||||
|
# Copyright 2000,2001 by Timothy O'Malley <timo@alum.mit.edu>
|
||||||
|
#
|
||||||
|
# All Rights Reserved
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and distribute this software
|
||||||
|
# and its documentation for any purpose and without fee is hereby
|
||||||
|
# granted, provided that the above copyright notice appear in all
|
||||||
|
# copies and that both that copyright notice and this permission
|
||||||
|
# notice appear in supporting documentation, and that the name of
|
||||||
|
# Timothy O'Malley not be used in advertising or publicity
|
||||||
|
# pertaining to distribution of the software without specific, written
|
||||||
|
# prior permission.
|
||||||
|
#
|
||||||
|
# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
|
||||||
|
# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||||
|
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
####
|
||||||
|
|
||||||
|
"""Timeout Socket
|
||||||
|
|
||||||
|
This module enables a timeout mechanism on all TCP connections. It
|
||||||
|
does this by inserting a shim into the socket module. After this module
|
||||||
|
has been imported, all socket creation goes through this shim. As a
|
||||||
|
result, every TCP connection will support a timeout.
|
||||||
|
|
||||||
|
The beauty of this method is that it immediately and transparently
|
||||||
|
enables the entire python library to support timeouts on TCP sockets.
|
||||||
|
As an example, if you wanted to SMTP connections to have a 20 second
|
||||||
|
timeout:
|
||||||
|
|
||||||
|
import timeoutsocket
|
||||||
|
import smtplib
|
||||||
|
timeoutsocket.setDefaultSocketTimeout(20)
|
||||||
|
|
||||||
|
|
||||||
|
The timeout applies to the socket functions that normally block on
|
||||||
|
execution: read, write, connect, and accept. If any of these
|
||||||
|
operations exceeds the specified timeout, the exception Timeout
|
||||||
|
will be raised.
|
||||||
|
|
||||||
|
The default timeout value is set to None. As a result, importing
|
||||||
|
this module does not change the default behavior of a socket. The
|
||||||
|
timeout mechanism only activates when the timeout has been set to
|
||||||
|
a numeric value. (This behavior mimics the behavior of the
|
||||||
|
select.select() function.)
|
||||||
|
|
||||||
|
This module implements two classes: TimeoutSocket and TimeoutFile.
|
||||||
|
|
||||||
|
The TimeoutSocket class defines a socket-like object that attempts to
|
||||||
|
avoid the condition where a socket may block indefinitely. The
|
||||||
|
TimeoutSocket class raises a Timeout exception whenever the
|
||||||
|
current operation delays too long.
|
||||||
|
|
||||||
|
The TimeoutFile class defines a file-like object that uses the TimeoutSocket
|
||||||
|
class. When the makefile() method of TimeoutSocket is called, it returns
|
||||||
|
an instance of a TimeoutFile.
|
||||||
|
|
||||||
|
Each of these objects adds two methods to manage the timeout value:
|
||||||
|
|
||||||
|
get_timeout() --> returns the timeout of the socket or file
|
||||||
|
set_timeout() --> sets the timeout of the socket or file
|
||||||
|
|
||||||
|
|
||||||
|
As an example, one might use the timeout feature to create httplib
|
||||||
|
connections that will timeout after 30 seconds:
|
||||||
|
|
||||||
|
import timeoutsocket
|
||||||
|
import httplib
|
||||||
|
H = httplib.HTTP("www.python.org")
|
||||||
|
H.sock.set_timeout(30)
|
||||||
|
|
||||||
|
Note: When used in this manner, the connect() routine may still
|
||||||
|
block because it happens before the timeout is set. To avoid
|
||||||
|
this, use the 'timeoutsocket.setDefaultSocketTimeout()' function.
|
||||||
|
|
||||||
|
Good Luck!
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "$Revision: 1.1.1.1 $"
|
||||||
|
__author__ = "Timothy O'Malley <timo@alum.mit.edu>"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Imports
|
||||||
|
#
|
||||||
|
import select, string
|
||||||
|
import socket
|
||||||
|
if not hasattr(socket, "_no_timeoutsocket"):
|
||||||
|
_socket = socket.socket
|
||||||
|
else:
|
||||||
|
_socket = socket._no_timeoutsocket
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Set up constants to test for Connected and Blocking operations.
|
||||||
|
# We delete 'os' and 'errno' to keep our namespace clean(er).
|
||||||
|
# Thanks to Alex Martelli and G. Li for the Windows error codes.
|
||||||
|
#
|
||||||
|
import os
|
||||||
|
if os.name == "nt":
|
||||||
|
_IsConnected = ( 10022, 10056 )
|
||||||
|
_ConnectBusy = ( 10035, )
|
||||||
|
_AcceptBusy = ( 10035, )
|
||||||
|
else:
|
||||||
|
import errno
|
||||||
|
_IsConnected = ( errno.EISCONN, )
|
||||||
|
_ConnectBusy = ( errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK )
|
||||||
|
_AcceptBusy = ( errno.EAGAIN, errno.EWOULDBLOCK )
|
||||||
|
del errno
|
||||||
|
del os
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Default timeout value for ALL TimeoutSockets
|
||||||
|
#
|
||||||
|
_DefaultTimeout = None
|
||||||
|
def setDefaultSocketTimeout(timeout):
|
||||||
|
global _DefaultTimeout
|
||||||
|
_DefaultTimeout = timeout
|
||||||
|
def getDefaultSocketTimeout():
|
||||||
|
return _DefaultTimeout
|
||||||
|
|
||||||
|
#
|
||||||
|
# Exceptions for socket errors and timeouts
|
||||||
|
#
|
||||||
|
Error = socket.error
|
||||||
|
class Timeout(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Factory function
|
||||||
|
#
|
||||||
|
from socket import AF_INET, SOCK_STREAM
|
||||||
|
def timeoutsocket(family=AF_INET, type=SOCK_STREAM, proto=None):
|
||||||
|
if family != AF_INET or type != SOCK_STREAM:
|
||||||
|
if proto:
|
||||||
|
return _socket(family, type, proto)
|
||||||
|
else:
|
||||||
|
return _socket(family, type)
|
||||||
|
return TimeoutSocket( _socket(family, type), _DefaultTimeout )
|
||||||
|
# end timeoutsocket
|
||||||
|
|
||||||
|
#
|
||||||
|
# The TimeoutSocket class definition
|
||||||
|
#
|
||||||
|
class TimeoutSocket:
|
||||||
|
"""TimeoutSocket object
|
||||||
|
Implements a socket-like object that raises Timeout whenever
|
||||||
|
an operation takes too long.
|
||||||
|
The definition of 'too long' can be changed using the
|
||||||
|
set_timeout() method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_copies = 0
|
||||||
|
_blocking = 1
|
||||||
|
|
||||||
|
def __init__(self, sock, timeout):
|
||||||
|
self._sock = sock
|
||||||
|
self._timeout = timeout
|
||||||
|
# end __init__
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
return getattr(self._sock, key)
|
||||||
|
# end __getattr__
|
||||||
|
|
||||||
|
def get_timeout(self):
|
||||||
|
return self._timeout
|
||||||
|
# end set_timeout
|
||||||
|
|
||||||
|
def set_timeout(self, timeout=None):
|
||||||
|
self._timeout = timeout
|
||||||
|
# end set_timeout
|
||||||
|
|
||||||
|
def setblocking(self, blocking):
|
||||||
|
self._blocking = blocking
|
||||||
|
return self._sock.setblocking(blocking)
|
||||||
|
# end set_timeout
|
||||||
|
|
||||||
|
def connect_ex(self, addr):
|
||||||
|
errcode = 0
|
||||||
|
try:
|
||||||
|
self.connect(addr)
|
||||||
|
except Error, why:
|
||||||
|
errcode = why[0]
|
||||||
|
return errcode
|
||||||
|
# end connect_ex
|
||||||
|
|
||||||
|
def connect(self, addr, port=None, dumbhack=None):
|
||||||
|
# In case we were called as connect(host, port)
|
||||||
|
if port != None: addr = (addr, port)
|
||||||
|
|
||||||
|
# Shortcuts
|
||||||
|
sock = self._sock
|
||||||
|
timeout = self._timeout
|
||||||
|
blocking = self._blocking
|
||||||
|
|
||||||
|
# First, make a non-blocking call to connect
|
||||||
|
try:
|
||||||
|
sock.setblocking(0)
|
||||||
|
sock.connect(addr)
|
||||||
|
sock.setblocking(blocking)
|
||||||
|
return
|
||||||
|
except Error, why:
|
||||||
|
# Set the socket's blocking mode back
|
||||||
|
sock.setblocking(blocking)
|
||||||
|
|
||||||
|
# If we are not blocking, re-raise
|
||||||
|
if not blocking:
|
||||||
|
raise
|
||||||
|
|
||||||
|
# If we are already connected, then return success.
|
||||||
|
# If we got a genuine error, re-raise it.
|
||||||
|
errcode = why[0]
|
||||||
|
if dumbhack and errcode in _IsConnected:
|
||||||
|
return
|
||||||
|
elif errcode not in _ConnectBusy:
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Now, wait for the connect to happen
|
||||||
|
# ONLY if dumbhack indicates this is pass number one.
|
||||||
|
# If select raises an error, we pass it on.
|
||||||
|
# Is this the right behavior?
|
||||||
|
if not dumbhack:
|
||||||
|
r,w,e = select.select([], [sock], [], timeout)
|
||||||
|
if w:
|
||||||
|
return self.connect(addr, dumbhack=1)
|
||||||
|
|
||||||
|
# If we get here, then we should raise Timeout
|
||||||
|
raise Timeout("Attempted connect to %s timed out." % str(addr) )
|
||||||
|
# end connect
|
||||||
|
|
||||||
|
def accept(self, dumbhack=None):
|
||||||
|
# Shortcuts
|
||||||
|
sock = self._sock
|
||||||
|
timeout = self._timeout
|
||||||
|
blocking = self._blocking
|
||||||
|
|
||||||
|
# First, make a non-blocking call to accept
|
||||||
|
# If we get a valid result, then convert the
|
||||||
|
# accept'ed socket into a TimeoutSocket.
|
||||||
|
# Be carefult about the blocking mode of ourselves.
|
||||||
|
try:
|
||||||
|
sock.setblocking(0)
|
||||||
|
newsock, addr = sock.accept()
|
||||||
|
sock.setblocking(blocking)
|
||||||
|
timeoutnewsock = self.__class__(newsock, timeout)
|
||||||
|
timeoutnewsock.setblocking(blocking)
|
||||||
|
return (timeoutnewsock, addr)
|
||||||
|
except Error, why:
|
||||||
|
# Set the socket's blocking mode back
|
||||||
|
sock.setblocking(blocking)
|
||||||
|
|
||||||
|
# If we are not supposed to block, then re-raise
|
||||||
|
if not blocking:
|
||||||
|
raise
|
||||||
|
|
||||||
|
# If we got a genuine error, re-raise it.
|
||||||
|
errcode = why[0]
|
||||||
|
if errcode not in _AcceptBusy:
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Now, wait for the accept to happen
|
||||||
|
# ONLY if dumbhack indicates this is pass number one.
|
||||||
|
# If select raises an error, we pass it on.
|
||||||
|
# Is this the right behavior?
|
||||||
|
if not dumbhack:
|
||||||
|
r,w,e = select.select([sock], [], [], timeout)
|
||||||
|
if r:
|
||||||
|
return self.accept(dumbhack=1)
|
||||||
|
|
||||||
|
# If we get here, then we should raise Timeout
|
||||||
|
raise Timeout("Attempted accept timed out.")
|
||||||
|
# end accept
|
||||||
|
|
||||||
|
def send(self, data, flags=0):
|
||||||
|
sock = self._sock
|
||||||
|
if self._blocking:
|
||||||
|
r,w,e = select.select([],[sock],[], self._timeout)
|
||||||
|
if not w:
|
||||||
|
raise Timeout("Send timed out")
|
||||||
|
return sock.send(data, flags)
|
||||||
|
# end send
|
||||||
|
|
||||||
|
def recv(self, bufsize, flags=0):
|
||||||
|
sock = self._sock
|
||||||
|
if self._blocking:
|
||||||
|
r,w,e = select.select([sock], [], [], self._timeout)
|
||||||
|
if not r:
|
||||||
|
raise Timeout("Recv timed out")
|
||||||
|
return sock.recv(bufsize, flags)
|
||||||
|
# end recv
|
||||||
|
|
||||||
|
def makefile(self, flags="r", bufsize=-1):
|
||||||
|
self._copies = self._copies +1
|
||||||
|
return TimeoutFile(self, flags, bufsize)
|
||||||
|
# end makefile
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self._copies <= 0:
|
||||||
|
self._sock.close()
|
||||||
|
else:
|
||||||
|
self._copies = self._copies -1
|
||||||
|
# end close
|
||||||
|
|
||||||
|
# end TimeoutSocket
|
||||||
|
|
||||||
|
|
||||||
|
class TimeoutFile:
|
||||||
|
"""TimeoutFile object
|
||||||
|
Implements a file-like object on top of TimeoutSocket.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, sock, mode="r", bufsize=4096):
|
||||||
|
self._sock = sock
|
||||||
|
self._bufsize = 4096
|
||||||
|
if bufsize > 0: self._bufsize = bufsize
|
||||||
|
if not hasattr(sock, "_inqueue"): self._sock._inqueue = ""
|
||||||
|
|
||||||
|
# end __init__
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
return getattr(self._sock, key)
|
||||||
|
# end __getattr__
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._sock.close()
|
||||||
|
self._sock = None
|
||||||
|
# end close
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
self.send(data)
|
||||||
|
# end write
|
||||||
|
|
||||||
|
def read(self, size=-1):
|
||||||
|
_sock = self._sock
|
||||||
|
_bufsize = self._bufsize
|
||||||
|
while 1:
|
||||||
|
datalen = len(_sock._inqueue)
|
||||||
|
if datalen >= size >= 0:
|
||||||
|
break
|
||||||
|
bufsize = _bufsize
|
||||||
|
if size > 0:
|
||||||
|
bufsize = min(bufsize, size - datalen )
|
||||||
|
buf = self.recv(bufsize)
|
||||||
|
if not buf:
|
||||||
|
break
|
||||||
|
_sock._inqueue = _sock._inqueue + buf
|
||||||
|
data = _sock._inqueue
|
||||||
|
_sock._inqueue = ""
|
||||||
|
if size > 0 and datalen > size:
|
||||||
|
_sock._inqueue = data[size:]
|
||||||
|
data = data[:size]
|
||||||
|
return data
|
||||||
|
# end read
|
||||||
|
|
||||||
|
def readline(self, size=-1):
|
||||||
|
_sock = self._sock
|
||||||
|
_bufsize = self._bufsize
|
||||||
|
while 1:
|
||||||
|
idx = string.find(_sock._inqueue, "\n")
|
||||||
|
if idx >= 0:
|
||||||
|
break
|
||||||
|
datalen = len(_sock._inqueue)
|
||||||
|
if datalen >= size >= 0:
|
||||||
|
break
|
||||||
|
bufsize = _bufsize
|
||||||
|
if size > 0:
|
||||||
|
bufsize = min(bufsize, size - datalen )
|
||||||
|
buf = self.recv(bufsize)
|
||||||
|
if not buf:
|
||||||
|
break
|
||||||
|
_sock._inqueue = _sock._inqueue + buf
|
||||||
|
|
||||||
|
data = _sock._inqueue
|
||||||
|
_sock._inqueue = ""
|
||||||
|
if idx >= 0:
|
||||||
|
idx = idx + 1
|
||||||
|
_sock._inqueue = data[idx:]
|
||||||
|
data = data[:idx]
|
||||||
|
elif size > 0 and datalen > size:
|
||||||
|
_sock._inqueue = data[size:]
|
||||||
|
data = data[:size]
|
||||||
|
return data
|
||||||
|
# end readline
|
||||||
|
|
||||||
|
def readlines(self, sizehint=-1):
|
||||||
|
result = []
|
||||||
|
data = self.read()
|
||||||
|
while data:
|
||||||
|
idx = string.find(data, "\n")
|
||||||
|
if idx >= 0:
|
||||||
|
idx = idx + 1
|
||||||
|
result.append( data[:idx] )
|
||||||
|
data = data[idx:]
|
||||||
|
else:
|
||||||
|
result.append( data )
|
||||||
|
data = ""
|
||||||
|
return result
|
||||||
|
# end readlines
|
||||||
|
|
||||||
|
def flush(self): pass
|
||||||
|
|
||||||
|
# end TimeoutFile
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Silently replace the socket() builtin function with
|
||||||
|
# our timeoutsocket() definition.
|
||||||
|
#
|
||||||
|
if not hasattr(socket, "_no_timeoutsocket"):
|
||||||
|
socket._no_timeoutsocket = socket.socket
|
||||||
|
socket.socket = timeoutsocket
|
||||||
|
del socket
|
||||||
|
socket = timeoutsocket
|
||||||
|
# Finis
|
11
DJAGEN/branches/mustafa_branch/djagen/gezegen/runtests.py
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import glob, trace, unittest
|
||||||
|
|
||||||
|
# find all of the planet test modules
|
||||||
|
modules = map(trace.fullmodname, glob.glob('planet/tests/test_*.py'))
|
||||||
|
|
||||||
|
# load all of the tests into a suite
|
||||||
|
suite = unittest.TestLoader().loadTestsFromNames(modules)
|
||||||
|
|
||||||
|
# run test suite
|
||||||
|
unittest.TextTestRunner().run(suite)
|
22
DJAGEN/branches/mustafa_branch/djagen/gezegen/setup.py
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""The Planet Feed Aggregator"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from distutils.core import setup
|
||||||
|
|
||||||
|
from planet import __version__ as VERSION
|
||||||
|
from planet import __license__ as LICENSE
|
||||||
|
|
||||||
|
if 'PLANET_VERSION' in os.environ.keys():
|
||||||
|
VERSION = os.environ['PLANET_VERSION']
|
||||||
|
|
||||||
|
setup(name="planet",
|
||||||
|
version=VERSION,
|
||||||
|
description="The Planet Feed Aggregator",
|
||||||
|
author="Planet Developers",
|
||||||
|
author_email="devel@lists.planetplanet.org",
|
||||||
|
url="http://www.planetplanet.org/",
|
||||||
|
license=LICENSE,
|
||||||
|
packages=["planet", "planet.compat_logging", "planet.tests"],
|
||||||
|
scripts=["planet.py", "planet-cache.py", "runtests.py"],
|
||||||
|
)
|
103
DJAGEN/branches/mustafa_branch/djagen/gezegen/sync
Executable file
|
@ -0,0 +1,103 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# sync.sh
|
||||||
|
#
|
||||||
|
# this script is written to syncronize the lkd planet..
|
||||||
|
#
|
||||||
|
# author: Alper KANAT <alper.kanat@linux.org.tr>
|
||||||
|
|
||||||
|
PLANET_DIR="${HOME}/public_html/gezegen"
|
||||||
|
LOG_DIR="$PLANET_DIR/logs"
|
||||||
|
LOG_FILE="planet-$(date +"%d.%m.%Y").log"
|
||||||
|
LOCK_FILE="planet-sync.lck"
|
||||||
|
VERBOSE=0
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat << EOF
|
||||||
|
Synchronizes the planet by invoking the necessary commands and logges everything.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help this help
|
||||||
|
-p, --planetdir useful if the planet dir is somewhere else than the
|
||||||
|
default one
|
||||||
|
-v, --verbose print the log instead of writing it to the log file..
|
||||||
|
|
||||||
|
Usage: $(basename $0) [--help] [--planetdir /path/to/planet]
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
--help|-h)
|
||||||
|
usage
|
||||||
|
shift
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
--planetdir|-p)
|
||||||
|
case "$2" in
|
||||||
|
[a-zA-Z0-9\/]*)
|
||||||
|
PLANET_DIR="$2"
|
||||||
|
LOG_DIR="$PLANET_DIR/logs"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
|
||||||
|
--verbose|-v)
|
||||||
|
VERBOSE=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
-*)
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
?*)
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# checking planet dir
|
||||||
|
if [[ ! -d "$PLANET_DIR" ]]; then
|
||||||
|
echo "invalid planet directory.. please specify the correct planet dir with --planetdir /path/to/planet"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# checking if the log file created for the current date
|
||||||
|
if [[ ! -f "$LOG_DIR/$LOG_FILE" ]]; then
|
||||||
|
touch "$LOG_DIR/$LOG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$PLANET_DIR/$LOCK_FILE" ]]; then
|
||||||
|
echo "there's a sync process running behind, please try again later.."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
touch "$PLANET_DIR/$LOCK_FILE"
|
||||||
|
|
||||||
|
cd "$PLANET_DIR"
|
||||||
|
|
||||||
|
if (( $VERBOSE == 0 )); then
|
||||||
|
echo >> "$LOG_DIR/$LOG_FILE" 2>&1
|
||||||
|
echo "starting new sync ($(date +"%d.%m.%Y, %H:%M"))" >> "$LOG_DIR/$LOG_FILE" 2>&1
|
||||||
|
echo "-----------------------------------------------------------" >> "$LOG_DIR/$LOG_FILE" 2>&1
|
||||||
|
"$PLANET_DIR/planet.py" "$PLANET_DIR/gezegen/config.ini" >> "$LOG_DIR/$LOG_FILE" 2>&1
|
||||||
|
echo "-----------------------------------------------------------" >> "$LOG_DIR/$LOG_FILE" 2>&1
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo "starting new sync ($(date +"%d.%m.%Y, %H:%M"))"
|
||||||
|
echo "-----------------------------------------------------------"
|
||||||
|
"$PLANET_DIR/planet.py" "$PLANET_DIR/gezegen/config.ini"
|
||||||
|
echo "-----------------------------------------------------------"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if ended successfully delete the lock file
|
||||||
|
rm "$PLANET_DIR/$LOCK_FILE"
|
16
DJAGEN/branches/mustafa_branch/djagen/gezegen/tmp_ini/tmp_entries.ini
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
Planet
|
||||||
|
name = Linux Gezegeni
|
||||||
|
label = Personal
|
||||||
|
id =
|
||||||
|
|
||||||
|
http://feeds.feedburner.com/oguzy-gezegen
|
||||||
|
name = Oğuz Yarımtepe
|
||||||
|
label = Personal
|
||||||
|
face = oguzyarimtepe.png
|
||||||
|
id = 2
|
||||||
|
|
||||||
|
http://www.hakanuygun.com/blog/?feed=atom&cat=13
|
||||||
|
name = Hakan Uygun
|
||||||
|
label = Personal
|
||||||
|
id = 1
|
||||||
|
|
278
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/BrowserCompatible.js
Executable file
|
@ -0,0 +1,278 @@
|
||||||
|
var BrowserDetect = {
|
||||||
|
init: function(){
|
||||||
|
this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
|
||||||
|
this.version = this.searchVersion(navigator.userAgent) ||
|
||||||
|
this.searchVersion(navigator.appVersion) ||
|
||||||
|
"an unknown version";
|
||||||
|
},
|
||||||
|
searchString: function(data){
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
var dataString = data[i].string;
|
||||||
|
var dataProp = data[i].prop;
|
||||||
|
this.versionSearchString = data[i].versionSearch || data[i].identity;
|
||||||
|
if (dataString) {
|
||||||
|
if (dataString.indexOf(data[i].subString) != -1)
|
||||||
|
return data[i].identity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (dataProp)
|
||||||
|
return data[i].identity;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchVersion: function(dataString){
|
||||||
|
var index = dataString.indexOf(this.versionSearchString);
|
||||||
|
if (index == -1)
|
||||||
|
return;
|
||||||
|
return parseFloat(dataString.substring(index + this.versionSearchString.length + 1));
|
||||||
|
},
|
||||||
|
dataBrowser: [{
|
||||||
|
string: navigator.userAgent,
|
||||||
|
subString: "Chrome",
|
||||||
|
identity: "Chrome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
string: navigator.vendor,
|
||||||
|
subString: "Apple",
|
||||||
|
identity: "Safari"
|
||||||
|
}, {
|
||||||
|
prop: window.opera,
|
||||||
|
identity: "Opera"
|
||||||
|
}, {
|
||||||
|
string: navigator.userAgent,
|
||||||
|
subString: "Flock",
|
||||||
|
identity: "Flock"
|
||||||
|
}, {
|
||||||
|
string: navigator.userAgent,
|
||||||
|
subString: "Firefox",
|
||||||
|
identity: "Firefox"
|
||||||
|
}, {
|
||||||
|
string: navigator.userAgent,
|
||||||
|
subString: "MSIE",
|
||||||
|
identity: "IExplorer",
|
||||||
|
versionSearch: "MSIE"
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
var BrowserCompatible = {
|
||||||
|
check: function(){
|
||||||
|
BrowserDetect.init();
|
||||||
|
if ((this.useBlackList && this.unCompatibleBrowsers[BrowserDetect.browser] && BrowserDetect.version <= this.unCompatibleBrowsers[BrowserDetect.browser]) ||
|
||||||
|
(!this.useBlackList && (BrowserDetect.version < this.compatibleBrowsers[BrowserDetect.browser] || !this.compatibleBrowsers[BrowserDetect.browser]))) {
|
||||||
|
if (!this.readCookie('browsercheck_dontShowAgain'))
|
||||||
|
this.showWarning();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStyle: function(el, styleProp){
|
||||||
|
var x = el;
|
||||||
|
if (x.currentStyle)
|
||||||
|
var y = x.currentStyle[styleProp];
|
||||||
|
else
|
||||||
|
if (window.getComputedStyle)
|
||||||
|
var y = document.defaultView.getComputedStyle(x, null).getPropertyValue(styleProp);
|
||||||
|
return y;
|
||||||
|
},
|
||||||
|
createCookie: function(name, value, days){
|
||||||
|
if (days) {
|
||||||
|
var date = new Date();
|
||||||
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
|
var expires = ";expires=" + date.toGMTString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
var expires = "";
|
||||||
|
document.cookie = name + "=" + value + expires + ";path=/";
|
||||||
|
},
|
||||||
|
|
||||||
|
readCookie: function(name){
|
||||||
|
var nameEQ = name + "=";
|
||||||
|
var ca = document.cookie.split(';');
|
||||||
|
for (var i = 0; i < ca.length; i++) {
|
||||||
|
var c = ca[i];
|
||||||
|
while (c.charAt(0) == ' ')
|
||||||
|
c = c.substring(1, c.length);
|
||||||
|
if (c.indexOf(nameEQ) == 0)
|
||||||
|
return c.substring(nameEQ.length, c.length);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
eraseCookie: function(name){
|
||||||
|
this.createCookie(name, "", -1);
|
||||||
|
},
|
||||||
|
showWarning: function(){
|
||||||
|
if(!this.lang){
|
||||||
|
this.lang=navigator.language || navigator.browserLanguage;
|
||||||
|
if(!this.langTranslations[this.lang]) this.lang="en";
|
||||||
|
}
|
||||||
|
var bg = document.createElement("div");
|
||||||
|
bg.id = "browsercheck_bg";
|
||||||
|
bg.style["background"] = "#fff";
|
||||||
|
bg.style["filter"] = "alpha(opacity=90)";
|
||||||
|
bg.style["-moz-opacity"] = "0.90";
|
||||||
|
bg.style["opacity"] = "0.9";
|
||||||
|
bg.style["position"] = "fixed";
|
||||||
|
if (BrowserDetect.browser == "IExplorer" && BrowserDetect.version < 7)
|
||||||
|
bg.style["position"] = "absolute";
|
||||||
|
bg.style["z-index"] = "9998";
|
||||||
|
bg.style["top"] = "0";
|
||||||
|
bg.style["left"] = "0";
|
||||||
|
bg.style["height"] = (screen.availHeight + 300) + "px";
|
||||||
|
bg.style["width"] = (screen.availWidth + 300) + "px";
|
||||||
|
|
||||||
|
var warning_html = "";
|
||||||
|
if (this.allowCancel)
|
||||||
|
warning_html += '<a href="javascript:BrowserCompatible.cancel()" style="background:url('+this.images['cancel']+') no-repeat; height:15px; width:16px; position:absolute; right:10px; top:7px;" title="' + this.langTranslations[this.lang]['cancel'] + '"></a>';
|
||||||
|
warning_html += '<div id="browsercheck_title" style="font-family:arial; font-size:24px; color:#000; margin:15px;">' + this.langTranslations[this.lang]['title'] + '</div>';
|
||||||
|
warning_html += '<div id="browsercheck_description" style="font-family:arial; font-size:12px; color:#707070; margin:15px;">' + this.langTranslations[this.lang]['description'] + '</div>';
|
||||||
|
warning_html += '<div id="browsercheck_recomendation" style="font-family:arial; font-size:12px; color:#707070; margin:15px;">' + this.langTranslations[this.lang]['recomendation'] + '</div>';
|
||||||
|
for (var i = 0; i < this.offeredBrowsers.length; i++) {
|
||||||
|
warning_html += '<a href="' + this.browsersList[this.offeredBrowsers[i]].link + '" title="' + this.langTranslations[this.lang][this.offeredBrowsers[i]] + '" style="height:60px; width:165px; display:block; float:left; margin:15px; text-decoration:none; background: url(' + this.browsersList[this.offeredBrowsers[i]].image + ') no-repeat;" target="_blank"> </a>';
|
||||||
|
|
||||||
|
}
|
||||||
|
if (this.allowToHide)
|
||||||
|
warning_html += '<div style="clear:both;font-family:arial; font-size:12px; color:#707070; padding:7px 15px;"><label><input type="checkbox" id="browsercheck_dontShowAgain" onclick="BrowserCompatible.dontShowAgain()" />' + this.langTranslations[this.lang]['dontShowAgain'] + '</label></div>';
|
||||||
|
var warning = document.createElement("div");
|
||||||
|
warning.id = "browsercheck_warning";
|
||||||
|
warning.style["background"] = "url("+this.images['background']+") no-repeat";
|
||||||
|
warning.style["padding"] = "2px";
|
||||||
|
warning.style["width"] = "600px";
|
||||||
|
warning.style["height"] = "400px";
|
||||||
|
warning.style["position"] = "fixed";
|
||||||
|
if (BrowserDetect.browser == "IExplorer" && BrowserDetect.version < 7)
|
||||||
|
warning.style["position"] = "absolute";
|
||||||
|
warning.style["z-index"] = "9999";
|
||||||
|
warning.style["top"] = ((window.innerHeight || document.body.parentNode.offsetHeight) - 400) / 2 + "px";
|
||||||
|
warning.style["left"] = ((window.innerWidth || document.body.parentNode.offsetWidth) - 600) / 2 + "px";
|
||||||
|
warning.innerHTML = warning_html;
|
||||||
|
|
||||||
|
this.old_overflow_style = this.getStyle(document.body.parentNode, "overflow") || this.getStyle(document.body, "overflow");
|
||||||
|
if (BrowserDetect.browser == "Opera" && this.old_overflow_style == "visible")
|
||||||
|
this.old_overflow_style = "auto";
|
||||||
|
document.body.parentNode.style["overflow"] = "hidden";
|
||||||
|
document.body.style["overflow"] = "hidden";
|
||||||
|
|
||||||
|
document.body.appendChild(bg);
|
||||||
|
document.body.appendChild(warning);
|
||||||
|
|
||||||
|
if (document.addEventListener) {
|
||||||
|
document.addEventListener('resize', this.warningPosition, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.attachEvent('onresize', this.warningPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
warningPosition: function(){
|
||||||
|
var warning = document.getElementById('browsercheck_warning');
|
||||||
|
warning.style["top"] = ((window.innerHeight || document.body.parentNode.offsetHeight) - 400) / 2 + "px";
|
||||||
|
warning.style["left"] = ((window.innerWidth || document.body.parentNode.offsetWidth) - 600) / 2 + "px";
|
||||||
|
},
|
||||||
|
dontShowAgain: function(){
|
||||||
|
var inpDontShowAgain = document.getElementById('browsercheck_dontShowAgain').checked;
|
||||||
|
var dontShowAgain = this.readCookie('browsercheck_dontShowAgain');
|
||||||
|
if (inpDontShowAgain) {
|
||||||
|
this.createCookie('browsercheck_dontShowAgain', 'on', this.cookiesExpire);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.eraseCookie('browsercheck_dontShowAgain');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel: function(){
|
||||||
|
var bg = document.getElementById('browsercheck_bg');
|
||||||
|
var warning = document.getElementById('browsercheck_warning');
|
||||||
|
bg.parentNode.removeChild(bg);
|
||||||
|
warning.parentNode.removeChild(warning);
|
||||||
|
document.body.parentNode.style["overflow"] = this.old_overflow_style;
|
||||||
|
if (BrowserDetect.browser != "IExplorer")
|
||||||
|
document.body.style["overflow"] = this.old_overflow_style;
|
||||||
|
document.onresize = this.resize_function;
|
||||||
|
},
|
||||||
|
old_overflow_style: "",
|
||||||
|
resize_function: null,
|
||||||
|
allowCancel: false,
|
||||||
|
allowToHide: false,
|
||||||
|
cookiesExpire: 1,
|
||||||
|
images : {
|
||||||
|
'background':"img/bg.gif",
|
||||||
|
'cancel':"img/cancel.gif"
|
||||||
|
},
|
||||||
|
useBlackList: false,
|
||||||
|
compatibleBrowsers: {
|
||||||
|
"Opera": 9.25,
|
||||||
|
"Firefox": 2,
|
||||||
|
"IExplorer": 7,
|
||||||
|
"Safari": 525.17,
|
||||||
|
"Flock": 1.1,
|
||||||
|
"Chrome": 1
|
||||||
|
},
|
||||||
|
unCompatibleBrowsers: {
|
||||||
|
"IExplorer": 6
|
||||||
|
},
|
||||||
|
offeredBrowsers: ["Chrome","Firefox", "Flock", "Safari", "IExplorer", "Opera"],
|
||||||
|
browsersList: {
|
||||||
|
"Chrome": {
|
||||||
|
"image": "http://www.goodbyeie6.org.ua/chrome.gif",
|
||||||
|
"link": "http://www.google.com/chrome/"
|
||||||
|
},
|
||||||
|
"Opera": {
|
||||||
|
"image": "http://www.goodbyeie6.org.ua/opera.gif",
|
||||||
|
"link": "http://www.opera.com/products/desktop/"
|
||||||
|
},
|
||||||
|
"Firefox": {
|
||||||
|
"image": "http://www.goodbyeie6.org.ua/firefox.gif",
|
||||||
|
"link": "http://www.mozilla-europe.org/"
|
||||||
|
},
|
||||||
|
"IExplorer": {
|
||||||
|
"image": "http://www.goodbyeie6.org.ua/iexplorer.gif",
|
||||||
|
"link": "http://www.microsoft.com/windows/internet-explorer/download-ie.aspx"
|
||||||
|
},
|
||||||
|
"Safari": {
|
||||||
|
"image": "http://www.goodbyeie6.org.ua/safari.gif",
|
||||||
|
"link": "http://www.apple.com/safari/"
|
||||||
|
},
|
||||||
|
"Flock": {
|
||||||
|
"image": "http://www.goodbyeie6.org.ua/flock.gif",
|
||||||
|
"link": "http://www.flock.com/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lang: "",
|
||||||
|
langTranslations: {
|
||||||
|
"uk": {
|
||||||
|
"title": "Несумісний браузер",
|
||||||
|
"description": "Ваш браузер вже застарів, тому в ньому немає всіх необхідних функцій для коректної роботи веб-сайтів. Сучасні веб-сайти створюються, щоб бути максимально зручними та максимально ефективними для людини, а разом із удосконаленням сайтів покращуються браузери. Крім цього, з розвитком інтернет-комерції, зростає кількість зловмисників та хакерських атак; використання найновіших версій браузерів - хороший спосіб вберегти свій комп'ютер.",
|
||||||
|
"recomendation": "Ми рекомендуємо використовувати останню версію одного із наступних браузерів:",
|
||||||
|
"cancel": "Закрити попередження",
|
||||||
|
"dontShowAgain": "Не показувати це попередження наступного разу",
|
||||||
|
"Flock": "Браузер Flock спеціалізований для користувачів різноманітних соціальних мереж. \nВін оснований на тому ж двигуні що й Firefox, тому демонструє таку ж стабільність та корекність роботи.",
|
||||||
|
"Firefox": "На сьогоднішній день найпопулярніший браузер у світі. \nЗагальне число користувачів браузера Firefox становить 40%.",
|
||||||
|
"IExplorer": "Браузер Internet Explorer від компанії Microsoft з 7-ї версії вийшов на новий рівень. \nПроте все ж поступається за коректністю роботи іншим браузерам.",
|
||||||
|
"Safari": "Популярний браузер від компанії Apple. \nЗ версії 3.1 демонструє достатню стабільність, за що й потрапив до цього списку.",
|
||||||
|
"Opera": "Браузер Opera користується популярністю в Європі, але великі компанії досі його ігнорують. \nOpera має низку недоліків, проте стабільно удосконалюється.",
|
||||||
|
"Chrome": "Браузер Chrome - молодий браузер створений компанією Google. \nРозробники приділили особливу увагу зручності браузера, і разом з тим він ні скільки не поступається за коректністю роботи."
|
||||||
|
},
|
||||||
|
"ru": {
|
||||||
|
"title": "Несовместимый браузер",
|
||||||
|
"description": "Ваш браузер уже устарел, потому в нем нет всех необходимых функций для корректной работы веб-сайтов. Современные веб-сайты создаются, чтобы быть максимально удобными и максимально эффективными для человека, а вместе с усовершенствованием сайтов улучшаются браузеры. Кроме этого, с развитием интернет-комерции, растет количество злоумышленников и хакерских атак; использование новейших версий браузеров - хороший способ уберечь свой компьютер.",
|
||||||
|
"recomendation": "Мы рекомендуем использовать последнюю версию одного из следующих браузеров:",
|
||||||
|
"cancel": "Закрыть предупреждение",
|
||||||
|
"dontShowAgain": "Не показывать это предупреждение вновь",
|
||||||
|
"Flock": "Браузер Flock специализирован для пользователей разнообразных социальных сетей. \nОн основан на том же движке что и Firefox, потому демонстрирует такую же стабильность и коректность работы.",
|
||||||
|
"Firefox": "На сегодняшний день самый популярный браузер в мире. \nОбщее число пользователей браузера Firefox составляет 40%.",
|
||||||
|
"IExplorer": "Браузер Internet Explorer от компании Microsoft после 7-и версии вышел на новый уровень. \nОднако все же уступает за корректностью работы другим браузерам.",
|
||||||
|
"Safari": "Популярный браузер от компании Apple. \nПосле версии 3.1 демонстрирует достаточную стабильность, за что и попал к этому списку.",
|
||||||
|
"Opera": "Браузер Opera пользуется популярностью в Европе, но большие компании до сих пор его игнорируют. \nOpera имеет ряд недостатков, однако стабильно совершенствуется.",
|
||||||
|
"Chrome": "Браузер Chrome - молодой браузер созданный компанией Google. \nРазработчики уделили особое внимание удобству браузера, и вместе с тем он ни сколько не уступает по коректнистю работы."
|
||||||
|
},
|
||||||
|
"en": {
|
||||||
|
"title": "Desteklenmeyen Tarayıcı!",
|
||||||
|
"description": "Tarayıcınız web sayfamız tarafından artık desteklenmiyor. Bu da demek oluyor ki sayfamızı verimli olarak kullanamayacaksınız. Lütfen aşağıdaki tarayıcılardan birini kurun.",
|
||||||
|
"recomendation": "",
|
||||||
|
"cancel": "Bu uyarıyı kapat",
|
||||||
|
"dontShowAgain": "Bu uyarıyı tekrar gösterme",
|
||||||
|
"Firefox": "",
|
||||||
|
"Flock": "",
|
||||||
|
"IExplorer": "",
|
||||||
|
"Safari": "",
|
||||||
|
"Opera": "",
|
||||||
|
"Chrome" : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
693
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/atom.xml
Executable file
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/favicon.ico
Executable file
After Width: | Height: | Size: 809 B |
1498
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/foafroll.xml
Executable file
66
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/form.css
Executable file
|
@ -0,0 +1,66 @@
|
||||||
|
form
|
||||||
|
{
|
||||||
|
margin:0 auto;
|
||||||
|
width:305px;
|
||||||
|
padding:14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, h1, form {border:0; margin:0; padding:0;}
|
||||||
|
.spacer{clear:both; height:1px;}
|
||||||
|
|
||||||
|
.fieldWrapper
|
||||||
|
{
|
||||||
|
border:solid 2px #b7ddf2;
|
||||||
|
background:#ebf4fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1
|
||||||
|
{
|
||||||
|
font-size:14px;
|
||||||
|
font-weight:bold;
|
||||||
|
margin-bottom:8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldWrapper label
|
||||||
|
{
|
||||||
|
display:block;
|
||||||
|
font-weight:bold;
|
||||||
|
text-align:left;
|
||||||
|
width:140px;
|
||||||
|
float:left;
|
||||||
|
margin:2px 2px 1px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldWrapper input
|
||||||
|
{
|
||||||
|
float:left;
|
||||||
|
font-size:12px;
|
||||||
|
padding:4px 2px;
|
||||||
|
border:solid 1px #aacfe4;
|
||||||
|
width:200px;
|
||||||
|
margin:2px 0px 20px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldWrapper .small
|
||||||
|
{
|
||||||
|
color:#666666;
|
||||||
|
display:block;
|
||||||
|
font-size:11px;
|
||||||
|
font-weight:normal;
|
||||||
|
text-align:left;
|
||||||
|
width:140px;
|
||||||
|
margin: 2px 2px 1px 10px;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldWrapper .error
|
||||||
|
{
|
||||||
|
color:red;
|
||||||
|
display:block;
|
||||||
|
font-size:11px;
|
||||||
|
font-weight:normal;
|
||||||
|
text-align:left;
|
||||||
|
width:140px;
|
||||||
|
margin: 2px 2px 1px 0px;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/feed-icon-10x10.png
Executable file
After Width: | Height: | Size: 469 B |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/hdr-planet-clean.png
Executable file
After Width: | Height: | Size: 15 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/hdr-planet.png
Executable file
After Width: | Height: | Size: 21 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/ahmetaygun.png
Executable file
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 8.3 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/alperkanat.png
Executable file
After Width: | Height: | Size: 5.5 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/alpersomuncu.png
Executable file
After Width: | Height: | Size: 3.0 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/ardacetin.png
Executable file
After Width: | Height: | Size: 8.3 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/armanaksoy.png
Executable file
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 5.9 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/barismetin.png
Executable file
After Width: | Height: | Size: 4.8 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/barisozyurt.png
Executable file
After Width: | Height: | Size: 6.0 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/bayramkaragoz.png
Executable file
After Width: | Height: | Size: 12 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/burakdayioglu.png
Executable file
After Width: | Height: | Size: 8.5 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/caglaronur.png
Executable file
After Width: | Height: | Size: 6.3 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/devrimgunduz.png
Executable file
After Width: | Height: | Size: 1.1 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/dorukfisek.png
Executable file
After Width: | Height: | Size: 3.5 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/efeciftci.png
Executable file
After Width: | Height: | Size: 5.7 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/ekinmeroglu.png
Executable file
After Width: | Height: | Size: 3.8 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/emrecansuster.png
Executable file
After Width: | Height: | Size: 8.5 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/ercineker.png
Executable file
After Width: | Height: | Size: 6.8 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/erkantekman.png
Executable file
After Width: | Height: | Size: 7.2 KiB |
BIN
DJAGEN/branches/mustafa_branch/djagen/gezegen/www/images/heads/faikuygur.png
Executable file
After Width: | Height: | Size: 8.5 KiB |