Sync to trunk.

This commit is contained in:
John Schember 2009-08-11 17:15:27 -04:00
commit 98f2f556fa
109 changed files with 207 additions and 5380 deletions

View File

@ -26,9 +26,12 @@ def freeze():
binary_excludes = ['libGLcore*', 'libGL*', 'libnvidia*']
os.system('sudo cp /usr/bin/calibre-mount-helper /tmp/calibre-mount-helper')
os.system('sudo chown kovid:users /tmp/calibre-mount-helper')
binary_includes = [
'/usr/bin/pdftohtml',
'/usr/bin/calibre-mount-helper',
'/tmp/calibre-mount-helper',
'/usr/lib/libunrar.so',
'/usr/lib/libsqlite3.so.0',
'/usr/lib/libsqlite3.so.0',

View File

@ -10,11 +10,18 @@ from distutils.core import Extension as _Extension
from distutils.command.build_ext import build_ext as _build_ext
from distutils.dep_util import newer_group
from distutils import log
from distutils.spawn import find_executable
import sipconfig, os, sys, string, glob, shutil
from PyQt4 import pyqtconfig
iswindows = 'win32' in sys.platform
QMAKE = os.path.expanduser('~/qt/bin/qmake') if 'darwin' in sys.platform else'qmake'
isosx = 'darwin' in sys.platform
QMAKE = '/Volumes/sw/qt/bin/qmake' if isosx else 'qmake'
if find_executable('qmake-qt4'):
QMAKE = find_executable('qmake-qt4')
elif find_executable('qmake'):
QMAKE = find_executable('qmake')
QMAKE = os.environ.get('QMAKE', QMAKE)
WINDOWS_PYTHON = ['C:/Python26/libs']
OSX_SDK = '/Developer/SDKs/MacOSX10.4u.sdk'

View File

@ -65,7 +65,7 @@ def setup_mount_helper():
if __name__ == '__main__':
from setuptools import setup, find_packages
from pyqtdistutils import PyQtExtension, build_ext, Extension
from pyqtdistutils import PyQtExtension, build_ext, Extension, QMAKE
from upload import sdist, pot, build, build_py, manual, \
resources, clean, gui, translations, update, \
tag_release, upload_demo, build_linux, build_windows, \
@ -76,10 +76,8 @@ if __name__ == '__main__':
entry_points['console_scripts'].append(
'calibre_postinstall = calibre.linux:post_install')
optional = []
qmake = '/Volumes/sw/qt/bin/qmake' if isosx else 'qmake'
qmake = os.environ.get('QMAKE', qmake)
def qmake_query(arg=''):
return subprocess.Popen([qmake, '-query', arg],
return subprocess.Popen([QMAKE, '-query', arg],
stdout=subprocess.PIPE).stdout.read()
qt_inc = qt_lib = None
qt_inc = qmake_query('QT_INSTALL_HEADERS').splitlines()[0]

View File

@ -35,14 +35,10 @@ normally have been passed to the output plugin.
After specifying the input \
and output file you can customize the conversion by specifying various \
options. the available options depend on the input and output file types. \
options. The available options depend on the input and output file types. \
To get help on them specify the input and output file and then use the -h \
option.
You can also get detailed help on all the options any input/output pair \
of formats supports by specifying the -h flag after the input and output \
filenames.
For full documentation of the conversion system see
''') + 'http://calibre.kovidgoyal.net/user_manual/conversion.html'

View File

@ -17,7 +17,10 @@
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Format:</string>
<string>&amp;Format:</string>
</property>
<property name="buddy">
<cstring>opt_format</cstring>
</property>
</widget>
</item>

View File

@ -17,7 +17,10 @@
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Line Un-Wrapping Factor:</string>
<string>Line &amp;Un-Wrapping Factor:</string>
</property>
<property name="buddy">
<cstring>opt_unwrap_factor</cstring>
</property>
</widget>
</item>
@ -50,7 +53,7 @@
<item row="1" column="0">
<widget class="QCheckBox" name="opt_no_images">
<property name="text">
<string>No Images</string>
<string>No &amp;Images</string>
</property>
</widget>
</item>

View File

@ -17,7 +17,10 @@
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Paper Size:</string>
<string>&amp;Paper Size:</string>
</property>
<property name="buddy">
<cstring>opt_paper_size</cstring>
</property>
</widget>
</item>
@ -27,7 +30,10 @@
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Orientation:</string>
<string>&amp;Orientation:</string>
</property>
<property name="buddy">
<cstring>opt_orientation</cstring>
</property>
</widget>
</item>

View File

@ -17,7 +17,10 @@
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Line ending style:</string>
<string>&amp;Line ending style:</string>
</property>
<property name="buddy">
<cstring>opt_newline</cstring>
</property>
</widget>
</item>

View File

@ -54,7 +54,7 @@ class ChangelogFormatter(blog.LogFormatter):
for msg in entry[1]:
txt.append(u' * ' + msg)
return u'\n'.join(txt)
return (u'\n'.join(txt)).encode('ascii', 'replace')
def bzr_log_to_txt():
path = BZR_PATH

View File

@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: calibre 0.6.6\n"
"POT-Creation-Date: 2009-08-10 19:10+MDT\n"
"PO-Revision-Date: 2009-08-10 19:10+MDT\n"
"POT-Creation-Date: 2009-08-11 11:45+MDT\n"
"PO-Revision-Date: 2009-08-11 11:45+MDT\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@ -643,54 +643,52 @@ msgid ""
"\n"
"The output ebook format is guessed from the file extension of output_file. output_file can also be of the special format .EXT where EXT is the output file extension. In this case, the name of the output file is derived the name of the input file. Note that the filenames must not start with a hyphen. Finally, if output_file has no extension, then it is treated as a directory and an \"open ebook\" (OEB) consisting of HTML files is written to that directory. These files are the files that would normally have been passed to the output plugin.\n"
"\n"
"After specifying the input and output file you can customize the conversion by specifying various options. the available options depend on the input and output file types. To get help on them specify the input and output file and then use the -h option.\n"
"\n"
"You can also get detailed help on all the options any input/output pair of formats supports by specifying the -h flag after the input and output filenames.\n"
"After specifying the input and output file you can customize the conversion by specifying various options. The available options depend on the input and output file types. To get help on them specify the input and output file and then use the -h option.\n"
"\n"
"For full documentation of the conversion system see\n"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:101
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:97
msgid "INPUT OPTIONS"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:102
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:98
msgid "Options to control the processing of the input %s file"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:108
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:104
msgid "OUTPUT OPTIONS"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:109
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:105
msgid "Options to control the processing of the output %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:123
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:119
msgid "Options to control the look and feel of the output"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:138
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:134
msgid "Control auto-detection of document structure."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:148
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:144
msgid "Control the automatic generation of a Table of Contents. By default, if the source file has a Table of Contents, it will be used in preference to the automatically generated one."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:158
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:154
msgid "Options to set metadata in the output"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:161
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:157
msgid "Options to help with debugging the conversion"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:185
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:181
msgid "List builtin recipes"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:254
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:250
msgid "Output saved to"
msgstr ""
@ -4129,7 +4127,7 @@ msgid ""
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Set a regular expression pattern to use when trying to guess ebook metadata from filenames. </p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">A <a href=\"http://docs.python.org/lib/re-syntax.html\"><span style=\" text-decoration: underline; color:#0000ff;\">reference</span></a> on the syntax of regular expressions is available.</p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">A <a href=\"http://docs.python.org/lib/re-syntax.html\"><span style=\" text-decoration: underline; color:#0000ee;\">reference</span></a> on the syntax of regular expressions is available.</p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Use the <span style=\" font-weight:600;\">Test</span> functionality below to test your regular expression on a few sample filenames. The group names for the various metadata entries are documented in tooltips.</p></body></html>"
msgstr ""
@ -5889,47 +5887,51 @@ msgid ""
"%sUsage%s: %s\n"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:81
#: /home/kovid/work/calibre/src/calibre/utils/config.py:87
msgid "Created by "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:538
msgid "Path to the database in which books are stored"
#: /home/kovid/work/calibre/src/calibre/utils/config.py:88
msgid "Whenever you pass arguments to %prog that have spaces in them, enclose the arguments in quotation marks."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:540
msgid "Pattern to guess metadata from filenames"
msgid "Path to the database in which books are stored"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:542
msgid "Access key for isbndb.com"
msgid "Pattern to guess metadata from filenames"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:544
msgid "Default timeout for network operations (seconds)"
msgid "Access key for isbndb.com"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:546
msgid "Path to directory in which your library of books is stored"
msgid "Default timeout for network operations (seconds)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:548
msgid "The language in which to display the user interface"
msgid "Path to directory in which your library of books is stored"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:550
msgid "The language in which to display the user interface"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:552
msgid "The default output format for ebook conversions."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:554
#: /home/kovid/work/calibre/src/calibre/utils/config.py:556
msgid "Ordered list of formats to prefer for input."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:556
#: /home/kovid/work/calibre/src/calibre/utils/config.py:558
msgid "Read metadata from files"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:558
#: /home/kovid/work/calibre/src/calibre/utils/config.py:560
msgid "The priority of worker processes"
msgstr ""
@ -6406,272 +6408,3 @@ msgstr ""
msgid "Show detailed output information. Useful for debugging"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:12
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:38
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:45
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:103
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:141
msgid "title"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:13
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:39
msgid "slug"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:16
msgid "category"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:17
msgid "categories"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:35
msgid "Draft"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:36
msgid "Public"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:41
msgid "body"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:42
msgid "tease"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:43
msgid "status"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:44
msgid "allow comments"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:45
msgid "publish"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:46
msgid "created"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:47
msgid "modified"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:53
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:153
msgid "post"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/blog/models.py:54
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:154
msgid "posts"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/admin.py:31
msgid "Fields updated automatically by Feedjack"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:17
msgid "Date published."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:18
msgid "Date the post was first obtained."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:22
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:38
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:98
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:126
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:173
msgid "name"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:23
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:26
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:105
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:142
msgid "link"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:27
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:61
msgid "links"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:39
msgid "url"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:42
msgid "Example"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:46
msgid "description"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:47
msgid "welcome"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:48
msgid "greets"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:50
msgid "default site"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:51
msgid "posts per page"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:52
msgid "order posts by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:54
msgid "tagcloud level"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:55
msgid "show tagcloud"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:57
msgid "use internal cache"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:58
msgid "cache duration"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:59
msgid "Duration in seconds of the cached pages and data."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:63
msgid "template"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:65
msgid "This template must be a directory in your feedjack templates directory. Leave blank to use the default template."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:69
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:170
msgid "site"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:70
msgid "sites"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:96
msgid "feed url"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:99
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:175
msgid "shortname"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:100
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:178
msgid "is active"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:101
msgid "If disabled, this feed will not be further updated."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:104
msgid "tagline"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:108
msgid "etag"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:109
msgid "last modified"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:110
msgid "last checked"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:113
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:140
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:171
msgid "feed"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:114
msgid "feeds"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:129
msgid "tag"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:130
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:149
msgid "tags"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:143
msgid "content"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:144
msgid "date modified"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:145
msgid "guid"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:146
msgid "author"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:147
msgid "author email"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:148
msgid "comments"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:150
msgid "date created"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:174
msgid "Keep blank to use the Feed's original name."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:177
msgid "Keep blank to use the Feed's original shortname."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:179
msgid "If disabled, this subscriber will not appear in the site or in the site's feed."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:183
msgid "subscriber"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/www/apps/feedjack/models.py:184
msgid "subscribers"
msgstr ""

View File

@ -78,13 +78,15 @@ class OptionParser(_OptionParser):
def __init__(self,
usage='%prog [options] filename',
version='%%prog (%s %s)'%(__appname__, __version__),
epilog=_('Created by ')+terminal_controller.RED+__author__+terminal_controller.NORMAL,
epilog=None,
gui_mode=False,
conflict_handler='resolve',
**kwds):
usage = textwrap.dedent(usage)
usage += '''\n\nWhenever you pass arguments to %prog that have spaces in them, '''\
'''enclose the arguments in quotation marks.'''
if epilog is None:
epilog = _('Created by ')+terminal_controller.RED+__author__+terminal_controller.NORMAL
usage += '\n\n'+_('''Whenever you pass arguments to %prog that have spaces in them, '''
'''enclose the arguments in quotation marks.''')
_OptionParser.__init__(self, usage=usage, version=version, epilog=epilog,
formatter=CustomHelpFormatter(),
conflict_handler=conflict_handler, **kwds)

View File

@ -52,7 +52,7 @@ recipe_modules = ['recipe_' + r for r in (
'diagonales', 'miradasalsur', 'newsweek_argentina', 'veintitres',
'gva_be', 'hln', 'tijd', 'degentenaar', 'inquirer_net', 'uncrate',
'fastcompany', 'accountancyage', 'laprensa_hn', 'latribuna',
'eltiempo_hn', 'slate',
'eltiempo_hn', 'slate', 'tnxm', 'bbcvietnamese', 'vnexpress',
)]

View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Huan Komrade T <huantnh at gmail.com>'
'''
bbc.co.uk
'''
from calibre.web.feeds.news import BasicNewsRecipe
class BBCVietnamese(BasicNewsRecipe):
title = u'BBC Vietnamese'
__author__ = 'Huan Komrade T'
description = 'Vietnam news and current affairs from the British Broadcasting Corporation'
no_stylesheets = True
language = _('Vietnamese')
encoding = 'utf-8'
recursions = 0
remove_tags = [dict(name='div', attrs={'class':'footer'})]
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
feeds = [
('Index', 'http://www.bbc.co.uk/vietnamese/index.xml'),
('Vietnam', 'http://www.bbc.co.uk/vietnamese/vietnam/index.xml'),
('Business', 'http://www.bbc.co.uk/vietnamese/business/index.xml'),
('Culture', 'http://www.bbc.co.uk/vietnamese/culture/index.xml'),
('Football', 'http://www.bbc.co.uk/vietnamese/football/index.xml'),
('Forum', 'http://www.bbc.co.uk/vietnamese/forum/index.xml'),
('In Depth', 'http://www.bbc.co.uk/vietnamese/indepth/index.xml'),
]
def print_version(self, url):
return url.replace('http://www.bbc.co.uk/vietnamese/', 'http://www.bbc.co.uk/vietnamese/lg/')

View File

@ -1,39 +1,36 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.web.feeds.news import BasicNewsRecipe
class Slashdot(BasicNewsRecipe):
title = u'Slashdot.org'
oldest_article = 7
max_articles_per_feed = 100
language = _('English')
__author__ = 'floweros'
no_stylesheets = True
keep_only_tags = [dict(name='div',attrs={'id':'article'})]
remove_tags = [
dict(name='div',attrs={'id':'userlogin-title'}),
dict(name='div',attrs={'id':'userlogin-content'}),
dict(name='div',attrs={'id':'commentwrap'}),
dict(name='span',attrs={'id':'more_comments_num_a'}),
]
feeds = [
(u'Slashdot',
u'http://rss.slashdot.org/Slashdot/slashdot?m=5072'),
(u'/. IT',
u'http://rss.slashdot.org/Slashdot/slashdotIT'),
(u'/. Hardware',
u'http://rss.slashdot.org/Slashdot/slashdotHardware'),
(u'/. Linux',
u'http://rss.slashdot.org/Slashdot/slashdotLinux'),
(u'/. Your Rights Online',
u'http://rss.slashdot.org/Slashdot/slashdotYourRightsOnline')
]
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net> edited by Huan T'
from calibre.web.feeds.news import BasicNewsRecipe
class Slashdot(BasicNewsRecipe):
title = u'Slashdot.org'
oldest_article = 7
max_articles_per_feed = 100
language = _('English')
__author__ = 'floweros edited by Huan T'
no_stylesheets = True
# keep_only_tags = [
# dict(name='div',attrs={'class':'article'}),
# dict(name='div',attrs={'class':'commentTop'}),
# ]
feeds = [
(u'Slashdot',
u'http://rss.slashdot.org/Slashdot/slashdot'),
(u'/. IT',
u'http://rss.slashdot.org/Slashdot/slashdotIT'),
(u'/. Hardware',
u'http://rss.slashdot.org/Slashdot/slashdotHardware'),
(u'/. Linux',
u'http://rss.slashdot.org/Slashdot/slashdotLinux'),
(u'/. Your Rights Online',
u'http://rss.slashdot.org/Slashdot/slashdotYourRightsOnline')
]
def get_article_url(self, article):
return article.get('feedburner_origlink', None)

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Wasabi <wasabi at tnxm.net>'
'''
tnxm.net
'''
from calibre.web.feeds.news import BasicNewsRecipe
class TNXM(BasicNewsRecipe):
title = u'Thanh Nien Xa Me'
__author__ = 'Wasabi'
description = 'Vietnam news and current affairs from TNXM - the finest Vietnamese bulletin board.'
no_stylesheets = True
language = _('Vietnamese')
encoding = 'utf-8'
recursions = 0
remove_tags = [dict(name='div', attrs={'class':'footer'})]
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
feeds = [
('Index', 'http://tnxm.net/external.php?type=RSS'),
]
def print_version(self, url):
return url.replace('showthread.php?', 'printthread.php?pp=160&')

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Huan Komrade T <huantnh at gmail.com>'
'''
vnexpress.net
'''
from calibre.web.feeds.news import BasicNewsRecipe
class BBCVietnamese(BasicNewsRecipe):
title = u'VnExpress'
__author__ = 'Huan Komrade T'
description = 'Vietnam news and current affairs from the Food Production Technology Corporation'
no_stylesheets = True
language = _('Vietnamese')
encoding = 'utf-8'
recursions = 0
remove_tags = [dict(name='div', attrs={'class':'footer'})]
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
feeds = [
('Index', 'http://vnexpress.net/rss/gl/trang-chu.rss'),
('Vietnam', 'http://vnexpress.net/rss/gl/xa-hoi.rss'),
('World News', 'http://vnexpress.net/rss/gl/the-gioi.rss'),
('Business', 'http://vnexpress.net/rss/gl/kinh-doanh.rss'),
('Culture', 'http://vnexpress.net/rss/gl/van-hoa.rss'),
('Sports', 'http://vnexpress.net/rss/gl/the-thao.rss'),
('Lifestyle', 'http://vnexpress.net/rss/gl/doi-song.rss'),
('From The Readers', 'http://vnexpress.net/rss/gl/ban-doc-viet.rss'),
('From The Readers - Sharing', 'http://vnexpress.net/rss/gl/ban-doc-viet-tam-su.rss'),
]
def print_version(self, url):
return url + '?q=1'

View File

@ -1,10 +0,0 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'

View File

@ -1,10 +0,0 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'

View File

@ -1,85 +0,0 @@
changes:
date: 2008-09-17
change: Enabled the ability to override the default template names.
date: 2008-08-26
change: Upgraded post_detail.html to now use new Django refactored comments. Sidenote: basic.remarks have been removed.
date: 2008-07-14
change: Removed get_query_set from Blog manager to fix a problem where saving a post marked as Draft would not save.
change: Added a get_previous_post and get_next_post method for front end template. These will not return Draft posts.
date: 2008-06-17
change: BlogPostFeed is now BlogPostsFeed and there is a new BlogPostsByCategory.
date: 2008-05-18
change: Converted everything to 4 space tabs and made a few other changes to comply with Python Style Guide.
date: 2008-04-23
change: Added an inline admin interface helper for choosing inlines to go into posts.
change: The inline app is now a dependancy of the blog.
date: 2008-04-22
change: Removed the 'render_inlines' filter from the Blog template tags. The tag is now in an app called inlines which can be used with any django app.
date: 2008-02-27
change: Added 'allow_comments' field to the Post model.
change: Removed 'Closed' choice from status field of Post model
date: 2008-02-18
fix: Fixed feed pointing to hardcoded url.
date: 2008-02-15
change: Internationalized models
date: 2008-02-04
change: Added 'get_links' template filter.
change: Templates: added a {% block content_title %}
date: 2008-02-02
change: Added a sitemap
date: 2008-01-30
change: Renamed 'do_inlines' filter to 'render_inlines'
date: 2008-01-29
change: BeautifulSoup is no longer a dependancy unless you want to use the do_inlines filter.
date: 2008-01-27
fix: removed 'tagging.register(Post)' from model. It was causing too many unnecessary SQL JOINS.
change: Changed the inlines tag to a filter. (Example: {{ object.text|do_inlines }})
date: 2008-01-22
change: Registered the Post model with the tagging app
date: 2008-01-19
change: Renamed the 'list' class to 'link_list'
date: 2008-01-09
change: Changed urls.py so you can have /posts/page/2/ or /posts/?page=2
date: 2008-01-07
change: Removed PublicPostManager in favor of ManagerWithPublished.
change: Made wrappers for generic views.
date: 2008-01-06
fix: In blog.py changed 'beautifulsoup' to 'BeautifulSoup'
date: 2007-12-31
change: Changed some syntax in managers.py to hopefully fix a bug.
change: Removed an inline template that didn't belong.
date: 2007-12-21
change: Added markup tag that formats inlines.
date: 2007-12-12
change: Cleaned up unit tests.
date: 2007-12-11
change: Add documentation to templatetags and views.
change: Smartened up the previous/next blog part of the post_detail.html template.
date: 2007-12-09
change: Added feed templates and wrapped up feeds.py.
change: Changed Post.live manager to Post.public
change: Added a search view along with templates

View File

@ -1,18 +0,0 @@
===========================================
Django Basic Blog
http://code.google.com/p/django-basic-apps/
===========================================
A simple blog application for Django projects.
To install this app, simply create a folder somewhere in
your PYTHONPATH named 'basic' and place the 'blog'
app inside. Then add 'basic.blog' to your projects
INSTALLED_APPS list in your settings.py file.
=== Dependancies ===
* Basic Inlines
* [http://www.djangoproject.com/documentation/add_ons/#comments Django Comments]
* [http://code.google.com/p/django-tagging Django Tagging]
* [http://www.djangoproject.com/documentation/add_ons/#markup Markup]
* [http://www.crummy.com/software/BeautifulSoup/ BeautifulSoup] - only if you want to use the [http://code.google.com/p/django-basic-blog/wiki/BlogInlinesProposal render_inlines] filter, otherwise it's not necessary.

View File

@ -1,17 +0,0 @@
from django.contrib import admin
from calibre.www.apps.blog.models import *
class CategoryAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Category, CategoryAdmin)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'publish', 'status')
list_filter = ('publish', 'categories', 'status')
search_fields = ('title', 'body')
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Post, PostAdmin)

View File

@ -1,42 +0,0 @@
from django.contrib.syndication.feeds import FeedDoesNotExist
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.sites.models import Site
from django.contrib.syndication.feeds import Feed
from django.core.urlresolvers import reverse
from calibre.www.apps.blog.models import Post, Category
class BlogPostsFeed(Feed):
_site = Site.objects.get_current()
title = '%s feed' % _site.name
description = '%s posts feed.' % _site.name
def link(self):
return reverse('blog_index')
def items(self):
return Post.objects.published()[:10]
def item_pubdate(self, obj):
return obj.publish
class BlogPostsByCategory(Feed):
_site = Site.objects.get_current()
title = '%s posts category feed' % _site.name
def get_object(self, bits):
if len(bits) != 1:
raise ObjectDoesNotExist
return Category.objects.get(slug__exact=bits[0])
def link(self, obj):
if not obj:
raise FeedDoesNotExist
return obj.get_absolute_url()
def description(self, obj):
return "Posts recently categorized as %s" % obj.title
def items(self, obj):
return obj.post_set.published()[:10]

View File

@ -1,9 +0,0 @@
from django.db.models import Manager
import datetime
class PublicManager(Manager):
"""Returns published posts that are not in the future."""
def published(self):
return self.get_query_set().filter(status__gte=2, publish__lte=datetime.datetime.now())

View File

@ -1,80 +0,0 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.db.models import permalink
from django.contrib.auth.models import User
from tagging.fields import TagField
from calibre.www.apps.blog.managers import PublicManager
import tagging
class Category(models.Model):
"""Category model."""
title = models.CharField(_('title'), max_length=100)
slug = models.SlugField(_('slug'), unique=True)
class Meta:
verbose_name = _('category')
verbose_name_plural = _('categories')
db_table = 'blog_categories'
ordering = ('title',)
class Admin:
pass
def __unicode__(self):
return u'%s' % self.title
@permalink
def get_absolute_url(self):
return ('blog_category_detail', None, {'slug': self.slug})
class Post(models.Model):
"""Post model."""
STATUS_CHOICES = (
(1, _('Draft')),
(2, _('Public')),
)
title = models.CharField(_('title'), max_length=200)
slug = models.SlugField(_('slug'), unique_for_date='publish')
author = models.ForeignKey(User, blank=True, null=True)
body = models.TextField(_('body'))
tease = models.TextField(_('tease'), blank=True)
status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=2)
allow_comments = models.BooleanField(_('allow comments'), default=True)
publish = models.DateTimeField(_('publish'))
created = models.DateTimeField(_('created'), auto_now_add=True)
modified = models.DateTimeField(_('modified'), auto_now=True)
categories = models.ManyToManyField(Category, blank=True)
tags = TagField()
objects = PublicManager()
class Meta:
verbose_name = _('post')
verbose_name_plural = _('posts')
db_table = 'blog_posts'
ordering = ('-publish',)
get_latest_by = 'publish'
class Admin:
list_display = ('title', 'publish', 'status')
list_filter = ('publish', 'categories', 'status')
search_fields = ('title', 'body')
def __unicode__(self):
return u'%s' % self.title
@permalink
def get_absolute_url(self):
return ('blog_detail', None, {
'year': self.publish.year,
'month': self.publish.strftime('%b').lower(),
'day': self.publish.day,
'slug': self.slug
})
def get_previous_post(self):
return self.get_previous_by_publish(status__gte=2)
def get_next_post(self):
return self.get_next_by_publish(status__gte=2)

View File

@ -1,13 +0,0 @@
from django.contrib.sitemaps import Sitemap
from calibre.www.apps.blog.models import Post
class BlogSitemap(Sitemap):
changefreq = "never"
priority = 0.5
def items(self):
return Post.objects.published()
def lastmod(self, obj):
return obj.publish

View File

@ -1,56 +0,0 @@
{% extends "admin/change_form.html" %}
{% block extrahead %}
{% load adminmedia inlines %}
{{ block.super }}
<script type="text/javascript">
function InlineInit() {
var body_div = document.getElementById('id_body').parentNode;
var content = ''
content += '{% get_inline_types as inline_list %}'
content += '<label>Body inlines:</label>'
content += '<strong>Inline type:</strong> '
content += '<select id="id_inline_content_type" onchange="document.getElementById(\'lookup_id_inline\').href = \'../../../\'+this.value+\'/\';" style="margin-right:20px;">'
content += ' <option>----------</option>'
content += ' {% for inline in inline_list %}'
content += ' <option value="{{ inline.content_type.app_label }}/{{ inline.content_type.model }}">{{ inline.content_type.app_label|capfirst }}: {{ inline.content_type.model|capfirst }}</option>'
content += ' {% endfor %}'
content += '</select> '
content += '<strong>Object:</strong> '
content += '<input type="text" class="vIntegerField" id="id_inline" size="10" /> '
content += '<a id="lookup_id_inline" href="#" class="related-lookup" onclick="if(document.getElementById(\'id_inline_content_type\').value != \'----------\') { return showRelatedObjectLookupPopup(this); }" style="margin-right:20px;"><img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Loopup" /></a> '
content += '<strong>Class:</strong> '
content += '<select id="id_inline_class">'
content += ' <option value="small_left">Small left</option>'
content += ' <option value="small_right">Small right</option>'
content += ' <option value="medium_left">Medium left</option>'
content += ' <option value="medium_right">Medium right</option>'
content += ' <option value="large_left">Large left</option>'
content += ' <option value="large_right">Large right</option>'
content += ' <option value="full">Full</option>'
content += '</select>'
content += '<input type="button" value="Add" style="margin-left:10px;" onclick="return insertInline(document.getElementById(\'id_inline_content_type\').value, document.getElementById(\'id_inline\').value, document.getElementById(\'id_inline_class\').value)" />'
content += '<p class="help">Insert inlines into your body by choosing an inline type, then an object, then a class.</p>'
var div = document.createElement('div');
div.setAttribute('style', 'margin-top:10px;');
div.innerHTML = content;
body_div.insertBefore(div);
}
function insertInline(type, id, classname) {
if (type != '----------' && id != '') {
inline = '<inline type="'+type.replace('/', '.')+'" id="'+id+'" class="'+classname+'" />';
body = document.getElementById('id_body');
body.value = body.value + inline + '\n';
}
}
addEvent(window, 'load', InlineInit);
</script>
{% endblock %}

View File

@ -1,21 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>{% block title %}{% endblock %}</title>
</head>
<body id="{% block body_id %}{% endblock %}">
<div id="body">
{% block body %}
<div>
{% block content_title %}{% endblock %}
</div>
<div class="content">
{% block content %}{% endblock %}
</div>
{% endblock %}
</div>
</body>
</html>

View File

@ -1,4 +0,0 @@
{% extends "base.html" %}
{% block body_class %}blog{% endblock %}

View File

@ -1,25 +0,0 @@
{% extends "blog/base_blog.html" %}
{% block title %}Posts for {{ category.title }}{% endblock %}
{% block body_class %}{{ block.super }} category_detail{% endblock %}
{% block body_id %}category_{{ category.id }}{% endblock %}
{% block content_title %}
<h2>Posts for {{ category.title }}</h2>
{% endblock %}
{% block content %}
{% load markup %}
<div class="post_list">
{% for post in object_list %}
<div>
<h3 class="title"><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h3>
<p class="date">{{ post.publish|date:"Y F d" }}</p>
<p class="tease">{{ post.tease }}</p>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -1,20 +0,0 @@
{% extends "blog/base_blog.html" %}
{% block title %}Post categories{% endblock %}
{% block body_class %}{{ block.super }} category_list{% endblock %}
{% block content_title %}
<h2>Post categories</h2>
{% endblock %}
{% block content %}
{% load markup %}
<ul class="link_list">
{% for category in object_list %}
<li><a href="{{ category.get_absolute_url }}">{{ category }}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -1,23 +0,0 @@
{% extends "blog/base_blog.html" %}
{% block title %}Post archive for {{ day|date:"d F Y" }}{% endblock %}
{% block body_class %}{{ block.super }} post_archive_day{% endblock %}
{% block content_title %}
<h2>Post archive for {{ day|date:"d F Y" }}</h2>
{% endblock %}
{% block content %}
<div class="post_list">
{% for post in object_list %}
<div>
<h3 class="title"><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h3>
<p class="date">{{ post.publish|date:"Y F d" }}</p>
<p class="tease">{{ post.tease }}</p>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -1,23 +0,0 @@
{% extends "blog/base_blog.html" %}
{% block title %}Post archive for {{ month|date:"F Y" }}{% endblock %}
{% block body_class %}{{ block.super }} post_archive_month{% endblock %}
{% block content_title %}
<h2>Post archive for {{ month|date:"F Y" }}</h2>
{% endblock %}
{% block content %}
<div class="post_list">
{% for post in object_list %}
<div>
<h3 class="title"><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h3>
<p class="date">{{ post.publish|date:"Y F d" }}</p>
<p class="tease">{{ post.tease }}</p>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -1,21 +0,0 @@
{% extends "blog/base_blog.html" %}
{% block title %}Post archive for {{ year }}{% endblock %}
{% block body_class %}{{ block.super }} post_archive_year{% endblock %}
{% block content_title %}
<h2>Post archive for {{ year }}</h2>
{% endblock %}
{% block content %}
{% load markup %}
<ul class="link_list">
{% for month in date_list %}
<li><a href="{% url blog_index %}{{ year }}/{{ month|date:"b" }}/">{{ month|date:"F" }}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -1,67 +0,0 @@
{% extends "blog/base_blog.html" %}
{% block title %}{{ object.title }}{% endblock %}
{% block body_class %}{{ block.super }} post_detail{% endblock %}
{% block body_id %}post_{{ object.id }}{% endblock %}
{% block content_title %}
<h2>{{ object.title }}</h2>
<p class="other_posts">
{% if object.get_previous_by_publish %}
<a class="previous" href="{{ object.get_previous_post.get_absolute_url }}">&laquo; {{ object.get_previous_post }}</a>
{% endif %}
{% if object.get_next_by_publish %}
| <a class="next" href="{{ object.get_next_post.get_absolute_url }}">{{ object.get_next_post }} &raquo;</a>
{% endif %}
</p>
{% endblock %}
{% block content %}
{% load blog markup comments tagging_tags %}
<p class="date">{{ object.publish|date:"j F Y" }}</p>
<div class="body">
{{ object.body|markdown:"safe" }}
</div>
{% tags_for_object object as tag_list %}
{% if tag_list %}
<p class="inline_tag_list"><strong>Related tags:</strong>
{% for tag in tag_list %}
{{ tag }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
{% endif %}
{% get_comment_list for object as comment_list %}
{% if comment_list %}
<div id="comments">
<a name="comments"></a>
<h3 class="comments_title">Comments</h3>
{% for comment in comment_list %}
{% if comment.is_public %}
<div class="comment">
<h5 class="name">
<a name="c{{ comment.id }}" href="{{ comment.get_absolute_url }}" title="Permalink to {{ comment.person_name }}'s comment" class="count">{{ forloop.counter }}</a>
{% if comment.user_url %}<a href="{{ comment.user_url }}">{{ comment.user_name }}</a>{% else %}{{ comment.user_name }}{% endif %} says...
</h5>
{{ comment.comment|urlizetrunc:"60"|markdown:"safe" }}
<p class="date">Posted at {{ comment.submit_date|date:"P" }} on {{ comment.submit_date|date:"F j, Y" }}</p>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% if object.allow_comments %}
{% render_comment_form for object %}
{% else %}
<div id="comment_form">
<h3>Comments are closed.</h3>
<p>Comments have been close for this post.</p>
</div>
{% endif %}
{% endblock %}

View File

@ -1,35 +0,0 @@
{% extends "blog/base_blog.html" %}
{% block title %}Post archive{% endblock %}
{% block body_class %}{{ block.super }} post_list{% endblock %}
{% block content_title %}
<h2>Post archive</h2>
{% endblock %}
{% block content %}
<div class="post_list">
{% for post in object_list %}
<div>
<h3 class="title"><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h3>
<p class="date">{{ post.publish|date:"Y F d" }}</p>
<p class="tease">{{ post.tease }}</p>
</div>
{% endfor %}
</div>
{% if is_paginated %}
<p class="pagination">
{% if has_next %}
<a class="older" href="?page={{ next }}">Older</a>
{% endif %}
{% if has_next and has_previous %} | {% endif %}
{% if has_previous %}
<a class="newer" href="?page={{ previous }}">Newer</a>
{% endif %}
</p>
{% endif %}
{% endblock %}

View File

@ -1,37 +0,0 @@
{% extends "blog/base_blog.html" %}
{% block title %}Post search{% endblock %}
{% block body_class %}{{ block.super }} post_search{% endblock %}
{% block content_title %}
<h2>Search</h2>
{% endblock %}
{% block content %}
<form action="." method="get" id="post_search_form">
<p>
<input type="text" name="q" value="{{ search_term }}" id="search">
<input type="submit" class="button" value="Search">
</p>
</form>
{% if message %}
<p class="message">{{ message }}</p>
{% endif %}
{% if object_list %}
<div class="post_list">
{% for post in object_list %}
<div>
<h3 class="title"><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h3>
<p class="date">{{ post.publish|date:"Y F d" }}</p>
<p class="tease">{{ post.tease }}</p>
<p class="comments">{% if comment_count %}{{ comment_count }} comment{{ comment_count|pluralize }}{% endif %}</p>
</div>
{% endfor %}
</div>
{% endif %}
{% endblock %}

View File

@ -1 +0,0 @@
{{ obj.tease }}

View File

@ -1 +0,0 @@
{{ obj.title }}

View File

@ -1,7 +0,0 @@
{% if object %}
{{ object }}
{% else %}
{% for object in object_list %}
{{ object }}
{% endfor %}
{% endif %}

View File

@ -1,103 +0,0 @@
from django import template
from django.conf import settings
from django.db import models
import re
Post = models.get_model('blog', 'post')
Category = models.get_model('blog', 'category')
register = template.Library()
class LatestPosts(template.Node):
def __init__(self, limit, var_name):
self.limit = limit
self.var_name = var_name
def render(self, context):
posts = Post.objects.published()[:int(self.limit)]
if posts and (int(self.limit) == 1):
context[self.var_name] = posts[0]
else:
context[self.var_name] = posts
return ''
@register.tag
def get_latest_posts(parser, token):
"""
Gets any number of latest posts and stores them in a varable.
Syntax::
{% get_latest_posts [limit] as [var_name] %}
Example usage::
{% get_latest_posts 10 as latest_post_list %}
"""
try:
tag_name, arg = token.contents.split(None, 1)
except ValueError:
raise template.TemplateSyntaxError, "%s tag requires arguments" % token.contents.split()[0]
m = re.search(r'(.*?) as (\w+)', arg)
if not m:
raise template.TemplateSyntaxError, "%s tag had invalid arguments" % tag_name
format_string, var_name = m.groups()
return LatestPosts(format_string, var_name)
class BlogCategories(template.Node):
def __init__(self, var_name):
self.var_name = var_name
def render(self, context):
categories = Category.objects.all()
context[self.var_name] = categories
return ''
@register.tag
def get_blog_categories(parser, token):
"""
Gets all blog categories.
Syntax::
{% get_blog_categories as [var_name] %}
Example usage::
{% get_blog_categories as category_list %}
"""
try:
tag_name, arg = token.contents.split(None, 1)
except ValueError:
raise template.TemplateSyntaxError, "%s tag requires arguments" % token.contents.split()[0]
m = re.search(r'as (\w+)', arg)
if not m:
raise template.TemplateSyntaxError, "%s tag had invalid arguments" % tag_name
var_name = m.groups()[0]
return BlogCategories(var_name)
@register.filter
def get_links(value):
"""
Extracts links from a ``Post`` body and returns a list.
Template Syntax::
{{ post.body|markdown:"safe"|get_links }}
"""
try:
try:
from BeautifulSoup import BeautifulSoup
except ImportError:
from beautifulsoup import BeautifulSoup
soup = BeautifulSoup(value)
return soup.findAll('a')
except ImportError:
if settings.DEBUG:
raise template.TemplateSyntaxError, "Error in 'get_links' filter: BeautifulSoup isn't installed."
return value

View File

@ -1,66 +0,0 @@
"""
>>> from django.test import Client
>>> from calibre.www.apps.blog.models import Post, Category
>>> import datetime
>>> from django.core.urlresolvers import reverse
>>> client = Client()
>>> category = Category(title='Django', slug='django')
>>> category.save()
>>> category2 = Category(title='Rails', slug='rails')
>>> category2.save()
>>> post = Post(title='DJ Ango', slug='dj-ango', body='Yo DJ! Turn that music up!', status=2, publish=datetime.datetime(2008,5,5,16,20))
>>> post.save()
>>> post2 = Post(title='Where my grails at?', slug='where', body='I Can haz Holy plez?', status=2, publish=datetime.datetime(2008,4,2,11,11))
>>> post2.save()
>>> post.categories.add(category)
>>> post2.categories.add(category2)
>>> response = client.get(reverse('blog_index'))
>>> response.context[-1]['object_list']
[<Post: DJ Ango>, <Post: Where my grails at?>]
>>> response.status_code
200
>>> response = client.get(reverse('blog_category_list'))
>>> response.context[-1]['object_list']
[<Category: Django>, <Category: Rails>]
>>> response.status_code
200
>>> response = client.get(category.get_absolute_url())
>>> response.context[-1]['object_list']
[<Post: DJ Ango>]
>>> response.status_code
200
>>> response = client.get(post.get_absolute_url())
>>> response.context[-1]['object']
<Post: DJ Ango>
>>> response.status_code
200
>>> response = client.get(reverse('blog_search'), {'q': 'DJ'})
>>> response.context[-1]['object_list']
[<Post: DJ Ango>]
>>> response.status_code
200
>>> response = client.get(reverse('blog_search'), {'q': 'Holy'})
>>> response.context[-1]['object_list']
[<Post: Where my grails at?>]
>>> response.status_code
200
>>> response = client.get(reverse('blog_search'), {'q': ''})
>>> response.context[-1]['message']
'Search term was too vague. Please try again.'
>>> response = client.get(reverse('blog_detail', args=[2008, 'apr', 2, 'where']))
>>> response.context[-1]['object']
<Post: Where my grails at?>
>>> response.status_code
200
"""

View File

@ -1,41 +0,0 @@
from django.conf.urls.defaults import *
from calibre.www.apps.blog import views as blog_views
urlpatterns = patterns('',
url(r'^(?P<year>\d{4})/(?P<month>\w{3})/(?P<day>\d{1,2})/(?P<slug>[-\w]+)/$',
view=blog_views.post_detail,
name='blog_detail'),
url(r'^(?P<year>\d{4})/(?P<month>\w{3})/(?P<day>\d{1,2})/$',
view=blog_views.post_archive_day,
name='blog_archive_day'),
url(r'^(?P<year>\d{4})/(?P<month>\w{3})/$',
view=blog_views.post_archive_month,
name='blog_archive_month'),
url(r'^(?P<year>\d{4})/$',
view=blog_views.post_archive_year,
name='blog_archive_year'),
url(r'^categories/(?P<slug>[-\w]+)/$',
view=blog_views.category_detail,
name='blog_category_detail'),
url (r'^categories/$',
view=blog_views.category_list,
name='blog_category_list'),
url (r'^search/$',
view=blog_views.search,
name='blog_search'),
url(r'^page/(?P<page>\w)/$',
view=blog_views.post_list,
name='blog_index_paginated'),
url(r'^$',
view=blog_views.post_list,
name='blog_index'),
)

View File

@ -1,160 +0,0 @@
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.http import Http404
from django.views.generic import date_based, list_detail
from django.db.models import Q
from calibre.www.apps.blog.models import *
import datetime
import re
def post_list(request, page=0, **kwargs):
return list_detail.object_list(
request,
queryset = Post.objects.published(),
paginate_by = 20,
page = page,
**kwargs
)
post_list.__doc__ = list_detail.object_list.__doc__
def post_archive_year(request, year, **kwargs):
return date_based.archive_year(
request,
year = year,
date_field = 'publish',
queryset = Post.objects.published(),
make_object_list = True,
**kwargs
)
post_archive_year.__doc__ = date_based.archive_year.__doc__
def post_archive_month(request, year, month, **kwargs):
return date_based.archive_month(
request,
year = year,
month = month,
date_field = 'publish',
queryset = Post.objects.published(),
**kwargs
)
post_archive_month.__doc__ = date_based.archive_month.__doc__
def post_archive_day(request, year, month, day, **kwargs):
return date_based.archive_day(
request,
year = year,
month = month,
day = day,
date_field = 'publish',
queryset = Post.objects.published(),
**kwargs
)
post_archive_day.__doc__ = date_based.archive_day.__doc__
def post_detail(request, slug, year, month, day, **kwargs):
return date_based.object_detail(
request,
year = year,
month = month,
day = day,
date_field = 'publish',
slug = slug,
queryset = Post.objects.published(),
**kwargs
)
post_detail.__doc__ = date_based.object_detail.__doc__
def category_list(request, template_name = 'blog/category_list.html', **kwargs):
"""
Category list
Template: ``blog/category_list.html``
Context:
object_list
List of categories.
"""
return list_detail.object_list(
request,
queryset = Category.objects.all(),
template_name = template_name,
**kwargs
)
def category_detail(request, slug, template_name = 'blog/category_detail.html', **kwargs):
"""
Category detail
Template: ``blog/category_detail.html``
Context:
object_list
List of posts specific to the given category.
category
Given category.
"""
category = get_object_or_404(Category, slug__iexact=slug)
return list_detail.object_list(
request,
queryset = category.post_set.published(),
extra_context = {'category': category},
template_name = template_name,
**kwargs
)
# Stop Words courtesy of http://www.dcs.gla.ac.uk/idom/ir_resources/linguistic_utils/stop_words
STOP_WORDS = r"""\b(a|about|above|across|after|afterwards|again|against|all|almost|alone|along|already|also|
although|always|am|among|amongst|amoungst|amount|an|and|another|any|anyhow|anyone|anything|anyway|anywhere|are|
around|as|at|back|be|became|because|become|becomes|becoming|been|before|beforehand|behind|being|below|beside|
besides|between|beyond|bill|both|bottom|but|by|call|can|cannot|cant|co|computer|con|could|couldnt|cry|de|describe|
detail|do|done|down|due|during|each|eg|eight|either|eleven|else|elsewhere|empty|enough|etc|even|ever|every|everyone|
everything|everywhere|except|few|fifteen|fify|fill|find|fire|first|five|for|former|formerly|forty|found|four|from|
front|full|further|get|give|go|had|has|hasnt|have|he|hence|her|here|hereafter|hereby|herein|hereupon|hers|herself|
him|himself|his|how|however|hundred|i|ie|if|in|inc|indeed|interest|into|is|it|its|itself|keep|last|latter|latterly|
least|less|ltd|made|many|may|me|meanwhile|might|mill|mine|more|moreover|most|mostly|move|much|must|my|myself|name|
namely|neither|never|nevertheless|next|nine|no|nobody|none|noone|nor|not|nothing|now|nowhere|of|off|often|on|once|
one|only|onto|or|other|others|otherwise|our|ours|ourselves|out|over|own|part|per|perhaps|please|put|rather|re|same|
see|seem|seemed|seeming|seems|serious|several|she|should|show|side|since|sincere|six|sixty|so|some|somehow|someone|
something|sometime|sometimes|somewhere|still|such|system|take|ten|than|that|the|their|them|themselves|then|thence|
there|thereafter|thereby|therefore|therein|thereupon|these|they|thick|thin|third|this|those|though|three|through|
throughout|thru|thus|to|together|too|top|toward|towards|twelve|twenty|two|un|under|until|up|upon|us|very|via|was|
we|well|were|what|whatever|when|whence|whenever|where|whereafter|whereas|whereby|wherein|whereupon|wherever|whether|
which|while|whither|who|whoever|whole|whom|whose|why|will|with|within|without|would|yet|you|your|yours|yourself|
yourselves)\b"""
def search(request, template_name='blog/post_search.html'):
"""
Search for blog posts.
This template will allow you to setup a simple search form that will try to return results based on
given search strings. The queries will be put through a stop words filter to remove words like
'the', 'a', or 'have' to help imporve the result set.
Template: ``blog/post_search.html``
Context:
object_list
List of blog posts that match given search term(s).
search_term
Given search term.
"""
context = {}
if request.GET:
stop_word_list = re.compile(STOP_WORDS, re.IGNORECASE)
search_term = '%s' % request.GET['q']
cleaned_search_term = stop_word_list.sub('', search_term)
cleaned_search_term = cleaned_search_term.strip()
if len(cleaned_search_term) != 0:
post_list = Post.objects.published().filter(Q(body__icontains=cleaned_search_term) | Q(tags__icontains=cleaned_search_term) | Q(categories__title__icontains=cleaned_search_term))
context = {'object_list': post_list, 'search_term':search_term}
else:
message = 'Search term was too vague. Please try again.'
context = {'message':message}
return render_to_response(template_name, context, context_instance=RequestContext(request))

View File

@ -1,8 +0,0 @@
# -*- coding: utf-8 -*-
"""
feedjack
Gustavo Picón
__init__.py
"""

View File

@ -1,60 +0,0 @@
# -*- coding: utf-8 -*-
"""
feedjack
Gustavo Picón
admin.py
"""
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from calibre.www.apps.feedjack import models
class LinkAdmin(admin.ModelAdmin):
pass
class SiteAdmin(admin.ModelAdmin):
list_display = ('url', 'name')
filter_vertical = ('links',)
class FeedAdmin(admin.ModelAdmin):
list_display = ('name', 'feed_url', 'title', 'last_modified', \
'is_active')
fieldsets = (
(None,
{'fields':('feed_url', 'name', 'shortname', 'is_active')}),
(_('Fields updated automatically by Feedjack'),
{'classes':('collapse',),
'fields':('title', 'tagline', 'link', 'etag', 'last_modified',
'last_checked'),
})
)
search_fields = ['feed_url', 'name', 'title']
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'link', 'author', 'date_modified')
search_fields = ['link', 'title']
date_hierarchy = 'date_modified'
filter_vertical = ('tags',)
class SubscriberAdmin(admin.ModelAdmin):
list_display = ('name', 'site', 'feed')
list_filter = ('site',)
admin.site.register(models.Link, LinkAdmin)
admin.site.register(models.Site, SiteAdmin)
admin.site.register(models.Feed, FeedAdmin)
admin.site.register(models.Post, PostAdmin)
admin.site.register(models.Subscriber, SubscriberAdmin)
#~

View File

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
"""
feedjack
Gustavo Picón
fjcache.py
"""
import md5
from django.core.cache import cache
from django.conf import settings
T_HOST = 1
T_ITEM = 2
T_META = 3
def str2md5(key):
""" Returns the md5 hash of a string.
"""
ctx = md5.new()
ctx.update(key.encode('utf-8'))
return ctx.hexdigest()
def getkey(stype, site_id=None, key=None):
""" Returns the cache key depending on it's type.
"""
base = '%s.feedjack' % (settings.CACHE_MIDDLEWARE_KEY_PREFIX)
if stype == T_HOST:
return '%s.hostcache' % base
elif stype == T_ITEM:
return '%s.%d.item.%s' % (base, site_id, str2md5(key))
elif stype == T_META:
return '%s.%d.meta' % (base, site_id)
def hostcache_get():
""" Retrieves the hostcache dictionary
"""
return cache.get(getkey(T_HOST))
def hostcache_set(value):
""" Sets the hostcache dictionary
"""
cache.set(getkey(T_HOST), value)
def cache_get(site_id, key):
""" Retrieves cache data from a site.
"""
return cache.get(getkey(T_ITEM, site_id, key))
def cache_set(site, key, data):
""" Sets cache data for a site.
All keys related to a site are stored in a meta key. This key is per-site.
"""
tkey = getkey(T_ITEM, site.id, key)
mkey = getkey(T_META, site.id)
tmp = cache.get(mkey)
longdur = 365*24*60*60
if not tmp:
tmp = [tkey]
cache.set(mkey, [tkey], longdur)
elif tkey not in tmp:
tmp.append(tkey)
cache.set(mkey, tmp, longdur)
cache.set(tkey, data, site.cache_duration)
def cache_delsite(site_id):
""" Removes all cache data from a site.
"""
mkey = getkey(T_META, site_id)
tmp = cache.get(mkey)
if not tmp:
return
for tkey in tmp:
cache.delete(tkey)
cache.delete(mkey)

View File

@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
"""
feedjack
Gustavo Picón
fjcloud.py
"""
import math
from calibre.www.apps.feedjack import fjlib, fjcache
def getsteps(levels, tagmax):
""" Returns a list with the max number of posts per "tagcloud level"
"""
ntw = levels
if ntw < 2:
ntw = 2
steps = [(stp, 1 + (stp * int(math.ceil(tagmax * 1.0 / ntw - 1))))
for stp in range(ntw)]
# just to be sure~
steps[-1] = (steps[-1][0], tagmax+1)
return steps
def build(site, tagdata):
""" Returns the tag cloud for a list of tags.
"""
tagdata.sort()
# we get the most popular tag to calculate the tags' weigth
tagmax = 0
for tagname, tagcount in tagdata:
if tagcount > tagmax:
tagmax = tagcount
steps = getsteps(site.tagcloud_levels, tagmax)
tags = []
for tagname, tagcount in tagdata:
weight = [twt[0] \
for twt in steps if twt[1] >= tagcount and twt[1] > 0][0]+1
tags.append({'tagname':tagname, 'count':tagcount, 'weight':weight})
return tags
def cloudata(site):
""" Returns a dictionary with all the tag clouds related to a site.
"""
tagdata = fjlib.getquery("""
SELECT feedjack_post.feed_id, feedjack_tag.name, COUNT(*)
FROM feedjack_post, feedjack_subscriber, feedjack_tag,
feedjack_post_tags
WHERE feedjack_post.feed_id=feedjack_subscriber.feed_id AND
feedjack_post_tags.tag_id=feedjack_tag.id AND
feedjack_post_tags.post_id=feedjack_post.id AND
feedjack_subscriber.site_id=%d
GROUP BY feedjack_post.feed_id, feedjack_tag.name
ORDER BY feedjack_post.feed_id, feedjack_tag.name""" % site.id)
tagdict = {}
globaldict = {}
cloudict = {}
for feed_id, tagname, tagcount in tagdata:
if feed_id not in tagdict:
tagdict[feed_id] = []
tagdict[feed_id].append((tagname, tagcount))
try:
globaldict[tagname] += tagcount
except KeyError:
globaldict[tagname] = tagcount
tagdict[0] = globaldict.items()
for key, val in tagdict.items():
cloudict[key] = build(site, val)
return cloudict
def getcloud(site, feed_id=None):
""" Returns the tag cloud for a site or a site's subscriber.
"""
cloudict = fjcache.cache_get(site.id, 'tagclouds')
if not cloudict:
cloudict = cloudata(site)
fjcache.cache_set(site, 'tagclouds', cloudict)
# A subscriber's tag cloud has been requested.
if feed_id:
feed_id = int(feed_id)
if feed_id in cloudict:
return cloudict[feed_id]
return []
# The site tagcloud has been requested.
return cloudict[0]

View File

@ -1,280 +0,0 @@
# -*- coding: utf-8 -*-
"""
feedjack
Gustavo Picón
fjlib.py
"""
from django.db import connection
from django.core.paginator import Paginator, InvalidPage
from django.http import Http404
from django.utils.encoding import smart_unicode
from calibre.www.apps.feedjack import models, fjcache
class PageNotAnInteger(ValueError):
pass
# this is taken from django, it was removed in r8191
class ObjectPaginator(Paginator):
"""
Legacy ObjectPaginator class, for backwards compatibility.
Note that each method on this class that takes page_number expects a
zero-based page number, whereas the new API (Paginator/Page) uses one-based
page numbers.
"""
def __init__(self, query_set, num_per_page, orphans=0):
Paginator.__init__(self, query_set, num_per_page, orphans)
import warnings
warnings.warn("The ObjectPaginator is deprecated. Use django.core.paginator.Paginator instead.", DeprecationWarning)
# Keep these attributes around for backwards compatibility.
self.query_set = query_set
self.num_per_page = num_per_page
self._hits = self._pages = None
def validate_page_number(self, page_number):
try:
page_number = int(page_number) + 1
except ValueError:
raise PageNotAnInteger
return self.validate_number(page_number)
def get_page(self, page_number):
try:
page_number = int(page_number) + 1
except ValueError:
raise PageNotAnInteger
return self.page(page_number).object_list
def has_next_page(self, page_number):
return page_number < self.pages - 1
def has_previous_page(self, page_number):
return page_number > 0
def first_on_page(self, page_number):
"""
Returns the 1-based index of the first object on the given page,
relative to total objects found (hits).
"""
page_number = self.validate_page_number(page_number)
return (self.num_per_page * (page_number - 1)) + 1
def last_on_page(self, page_number):
"""
Returns the 1-based index of the last object on the given page,
relative to total objects found (hits).
"""
page_number = self.validate_page_number(page_number)
if page_number == self.num_pages:
return self.count
return page_number * self.num_per_page
# The old API called it "hits" instead of "count".
hits = Paginator.count
# The old API called it "pages" instead of "num_pages".
pages = Paginator.num_pages
def sitefeeds(siteobj):
""" Returns the active feeds of a site.
"""
return siteobj.subscriber_set.filter(is_active=True).select_related()
#return [subscriber['feed'] \
# for subscriber \
# in siteobj.subscriber_set.filter(is_active=True).values('feed')]
def getquery(query):
""" Performs a query and get the results.
"""
try:
conn = connection.cursor()
conn.execute(query)
data = conn.fetchall()
conn.close()
except:
data = []
return data
def get_extra_content(site, sfeeds_ids, ctx):
""" Returns extra data useful to the templates.
"""
# get the subscribers' feeds
if sfeeds_ids:
basefeeds = models.Feed.objects.filter(id__in=sfeeds_ids)
try:
ctx['feeds'] = basefeeds.order_by('name').select_related()
except:
ctx['feeds'] = []
# get the last_checked time
try:
ctx['last_modified'] = basefeeds.filter(\
last_checked__isnull=False).order_by(\
'-last_checked').select_related()[0].last_checked.ctime()
except:
ctx['last_modified'] = '??'
else:
ctx['feeds'] = []
ctx['last_modified'] = '??'
ctx['site'] = site
def get_posts_tags(object_list, sfeeds_obj, user_id, tag_name):
""" Adds a qtags property in every post object in a page.
Use "qtags" instead of "tags" in templates to avoid unnecessary DB hits.
"""
tagd = {}
user_obj = None
tag_obj = None
tags = models.Tag.objects.extra(\
select={'post_id':'%s.%s' % (\
connection.ops.quote_name('feedjack_post_tags'), \
connection.ops.quote_name('post_id'))}, \
tables=['feedjack_post_tags'], \
where=[\
'%s.%s=%s.%s' % (\
connection.ops.quote_name('feedjack_tag'), \
connection.ops.quote_name('id'), \
connection.ops.quote_name('feedjack_post_tags'), \
connection.ops.quote_name('tag_id')), \
'%s.%s IN (%s)' % (\
connection.ops.quote_name('feedjack_post_tags'), \
connection.ops.quote_name('post_id'), \
', '.join([str(post.id) for post in object_list]))])
for tag in tags:
if tag.post_id not in tagd:
tagd[tag.post_id] = []
tagd[tag.post_id].append(tag)
if tag_name and tag.name == tag_name:
tag_obj = tag
subd = {}
for sub in sfeeds_obj:
subd[sub.feed.id] = sub
for post in object_list:
if post.id in tagd:
post.qtags = tagd[post.id]
else:
post.qtags = []
post.subscriber = subd[post.feed.id]
if user_id and int(user_id) == post.feed.id:
user_obj = post.subscriber
return user_obj, tag_obj
def getcurrentsite(http_post, path_info, query_string):
""" Returns the site id and the page cache key based on the request.
"""
url = u'http://%s/%s' % (smart_unicode(http_post.rstrip('/')), \
smart_unicode(path_info.lstrip('/')))
pagecachekey = '%s?%s' % (smart_unicode(path_info), \
smart_unicode(query_string))
hostdict = fjcache.hostcache_get()
if not hostdict:
hostdict = {}
if url not in hostdict:
default, ret = None, None
for site in models.Site.objects.all():
if url.startswith(site.url):
ret = site
break
if not default or site.default_site:
default = site
if not ret:
if default:
ret = default
else:
# Somebody is requesting something, but the user didn't create
# a site yet. Creating a default one...
ret = models.Site(name='Default Feedjack Site/Planet', \
url='www.feedjack.org', \
title='Feedjack Site Title', \
description='Feedjack Site Description. ' \
'Please change this in the admin interface.')
ret.save()
hostdict[url] = ret.id
fjcache.hostcache_set(hostdict)
return hostdict[url], pagecachekey
def get_paginator(site, sfeeds_ids, page=0, tag=None, user=None):
""" Returns a paginator object and a requested page from it.
"""
if tag:
try:
localposts = models.Tag.objects.get(name=tag).post_set.filter(\
feed__in=sfeeds_ids)
except:
raise Http404
else:
localposts = models.Post.objects.filter(feed__in=sfeeds_ids)
if user:
try:
localposts = localposts.filter(feed=user)
except:
raise Http404
if site.order_posts_by == 2:
localposts = localposts.order_by('-date_created', '-date_modified')
else:
localposts = localposts.order_by('-date_modified')
paginator = ObjectPaginator(localposts.select_related(), \
site.posts_per_page)
try:
object_list = paginator.get_page(page)
except InvalidPage:
if page == 0:
object_list = []
else:
raise Http404
return (paginator, object_list)
def page_context(request, site, tag=None, user_id=None, sfeeds=None):
""" Returns the context dictionary for a page view.
"""
sfeeds_obj, sfeeds_ids = sfeeds
try:
page = int(request.GET.get('page', 0))
except ValueError:
page = 0
paginator, object_list = get_paginator(site, sfeeds_ids, \
page=page, tag=tag, user=user_id)
if object_list:
# This will hit the DB once per page instead of once for every post in
# a page. To take advantage of this the template designer must call
# the qtags property in every item, instead of the default tags
# property.
user_obj, tag_obj = get_posts_tags(object_list, sfeeds_obj, \
user_id, tag)
else:
user_obj, tag_obj = None, None
ctx = {
'object_list': object_list,
'is_paginated': paginator.pages > 1,
'results_per_page': site.posts_per_page,
'has_next': paginator.has_next_page(page),
'has_previous': paginator.has_previous_page(page),
'page': page + 1,
'next': page + 1,
'previous': page - 1,
'pages': paginator.pages,
'hits' : paginator.hits,
}
get_extra_content(site, sfeeds_ids, ctx)
from calibre.www.apps.feedjack import fjcloud
ctx['tagcloud'] = fjcloud.getcloud(site, user_id)
ctx['user_id'] = user_id
ctx['user'] = user_obj
ctx['tag'] = tag_obj
ctx['subscribers'] = sfeeds_obj
return ctx

View File

@ -1,202 +0,0 @@
# -*- coding: utf-8 -*-
# pylint: disable-msg=W0232, R0903, W0131
"""
feedjack
Gustavo Picón
models.py
"""
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode
from calibre.www.apps.feedjack import fjcache
SITE_ORDERBY_CHOICES = (
(1, _('Date published.')),
(2, _('Date the post was first obtained.'))
)
class Link(models.Model):
name = models.CharField(_('name'), max_length=100, unique=True)
link = models.URLField(_('link'), verify_exists=True)
class Meta:
verbose_name = _('link')
verbose_name_plural = _('links')
class Admin:
pass
def __unicode__(self):
return u'%s (%s)' % (self.name, self.link)
class Site(models.Model):
name = models.CharField(_('name'), max_length=100)
url = models.CharField(_('url'),
max_length=100,
unique=True,
help_text=u'%s: %s, %s' % (smart_unicode(_('Example')),
u'http://www.planetexample.com',
u'http://www.planetexample.com:8000/foo'))
title = models.CharField(_('title'), max_length=200)
description = models.TextField(_('description'))
welcome = models.TextField(_('welcome'), null=True, blank=True)
greets = models.TextField(_('greets'), null=True, blank=True)
default_site = models.BooleanField(_('default site'), default=False)
posts_per_page = models.IntegerField(_('posts per page'), default=20)
order_posts_by = models.IntegerField(_('order posts by'), default=1,
choices=SITE_ORDERBY_CHOICES)
tagcloud_levels = models.IntegerField(_('tagcloud level'), default=5)
show_tagcloud = models.BooleanField(_('show tagcloud'), default=True)
use_internal_cache = models.BooleanField(_('use internal cache'), default=True)
cache_duration = models.IntegerField(_('cache duration'), default=60*60*24,
help_text=_('Duration in seconds of the cached pages and data.') )
links = models.ManyToManyField(Link, verbose_name=_('links'),
null=True, blank=True)
template = models.CharField(_('template'), max_length=100, null=True,
blank=True,
help_text=_('This template must be a directory in your feedjack '
'templates directory. Leave blank to use the default template.') )
class Meta:
verbose_name = _('site')
verbose_name_plural = _('sites')
ordering = ('name',)
def __unicode__(self):
return self.name
def save(self):
if not self.template:
self.template = 'default'
# there must be only ONE default site
defs = Site.objects.filter(default_site=True)
if not defs:
self.default_site = True
elif self.default_site:
for tdef in defs:
if tdef.id != self.id:
tdef.default_site = False
tdef.save()
self.url = self.url.rstrip('/')
fjcache.hostcache_set({})
super(Site, self).save()
class Feed(models.Model):
feed_url = models.URLField(_('feed url'), unique=True)
name = models.CharField(_('name'), max_length=100)
shortname = models.CharField(_('shortname'), max_length=50)
is_active = models.BooleanField(_('is active'), default=True,
help_text=_('If disabled, this feed will not be further updated.') )
title = models.CharField(_('title'), max_length=200, blank=True)
tagline = models.TextField(_('tagline'), blank=True)
link = models.URLField(_('link'), blank=True)
# http://feedparser.org/docs/http-etag.html
etag = models.CharField(_('etag'), max_length=50, blank=True)
last_modified = models.DateTimeField(_('last modified'), null=True, blank=True)
last_checked = models.DateTimeField(_('last checked'), null=True, blank=True)
class Meta:
verbose_name = _('feed')
verbose_name_plural = _('feeds')
ordering = ('name', 'feed_url',)
def __unicode__(self):
return u'%s (%s)' % (self.name, self.feed_url)
def save(self):
super(Feed, self).save()
class Tag(models.Model):
name = models.CharField(_('name'), max_length=50, unique=True)
class Meta:
verbose_name = _('tag')
verbose_name_plural = _('tags')
ordering = ('name',)
def __unicode__(self):
return self.name
def save(self):
super(Tag, self).save()
class Post(models.Model):
feed = models.ForeignKey(Feed, verbose_name=_('feed'), null=False, blank=False)
title = models.CharField(_('title'), max_length=255)
link = models.URLField(_('link'), )
content = models.TextField(_('content'), blank=True)
date_modified = models.DateTimeField(_('date modified'), null=True, blank=True)
guid = models.CharField(_('guid'), max_length=200, db_index=True)
author = models.CharField(_('author'), max_length=50, blank=True)
author_email = models.EmailField(_('author email'), blank=True)
comments = models.URLField(_('comments'), blank=True)
tags = models.ManyToManyField(Tag, verbose_name=_('tags'))
date_created = models.DateField(_('date created'), auto_now_add=True)
class Meta:
verbose_name = _('post')
verbose_name_plural = _('posts')
ordering = ('-date_modified',)
unique_together = (('feed', 'guid'),)
def __unicode__(self):
return self.title
def save(self):
super(Post, self).save()
def get_absolute_url(self):
return self.link
class Subscriber(models.Model):
site = models.ForeignKey(Site, verbose_name=_('site') )
feed = models.ForeignKey(Feed, verbose_name=_('feed') )
name = models.CharField(_('name'), max_length=100, null=True, blank=True,
help_text=_('Keep blank to use the Feed\'s original name.') )
shortname = models.CharField(_('shortname'), max_length=50, null=True,
blank=True,
help_text=_('Keep blank to use the Feed\'s original shortname.') )
is_active = models.BooleanField(_('is active'), default=True,
help_text=_('If disabled, this subscriber will not appear in the site or '
'in the site\'s feed.') )
class Meta:
verbose_name = _('subscriber')
verbose_name_plural = _('subscribers')
ordering = ('site', 'name', 'feed')
unique_together = (('site', 'feed'),)
def __unicode__(self):
return u'%s in %s' % (self.feed, self.site)
def get_cloud(self):
from calibre.www.apps.feedjack import fjcloud
return fjcloud.getcloud(self.site, self.feed.id)
def save(self):
if not self.name:
self.name = self.feed.name
if not self.shortname:
self.shortname = self.feed.shortname
super(Subscriber, self).save()

View File

@ -1,506 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
feedjack
Gustavo Picón
update_feeds.py
"""
import os
import time
import optparse
import datetime
import socket
import traceback
import sys
import feedparser
try:
import threadpool
except ImportError:
threadpool = None
VERSION = '0.9.16'
URL = 'http://www.feedjack.org/'
USER_AGENT = 'Feedjack %s - %s' % (VERSION, URL)
SLOWFEED_WARNING = 10
ENTRY_NEW, ENTRY_UPDATED, ENTRY_SAME, ENTRY_ERR = range(4)
FEED_OK, FEED_SAME, FEED_ERRPARSE, FEED_ERRHTTP, FEED_ERREXC = range(5)
def encode(tstr):
""" Encodes a unicode string in utf-8
"""
if not tstr:
return ''
# this is _not_ pretty, but it works
try:
return tstr.encode('utf-8', "xmlcharrefreplace")
except UnicodeDecodeError:
# it's already UTF8.. sigh
return tstr.decode('utf-8').encode('utf-8')
def prints(tstr):
""" lovely unicode
"""
sys.stdout.write('%s\n' % (tstr.encode(sys.getdefaultencoding(),
'replace')))
sys.stdout.flush()
def mtime(ttime):
""" datetime auxiliar function.
"""
return datetime.datetime.fromtimestamp(time.mktime(ttime))
class ProcessEntry:
def __init__(self, feed, options, entry, postdict, fpf):
self.feed = feed
self.options = options
self.entry = entry
self.postdict = postdict
self.fpf = fpf
def get_tags(self):
""" Returns a list of tag objects from an entry.
"""
from calibre.www.apps.feedjack import models
fcat = []
if self.entry.has_key('tags'):
for tcat in self.entry.tags:
if tcat.label != None:
term = tcat.label
else:
term = tcat.term
qcat = term.strip()
if ',' in qcat or '/' in qcat:
qcat = qcat.replace(',', '/').split('/')
else:
qcat = [qcat]
for zcat in qcat:
tagname = zcat.lower()
while ' ' in tagname:
tagname = tagname.replace(' ', ' ')
tagname = tagname.strip()
if not tagname or tagname == ' ':
continue
if not models.Tag.objects.filter(name=tagname):
cobj = models.Tag(name=tagname)
cobj.save()
fcat.append(models.Tag.objects.get(name=tagname))
return fcat
def get_entry_data(self):
""" Retrieves data from a post and returns it in a tuple.
"""
try:
link = self.entry.link
except AttributeError:
link = self.feed.link
try:
title = self.entry.title
except AttributeError:
title = link
guid = self.entry.get('id', title)
if self.entry.has_key('author_detail'):
author = self.entry.author_detail.get('name', '')
author_email = self.entry.author_detail.get('email', '')
else:
author, author_email = '', ''
if not author:
author = self.entry.get('author', self.entry.get('creator', ''))
if not author_email:
# this should be optional~
author_email = 'nospam@nospam.com'
try:
content = self.entry.content[0].value
except:
content = self.entry.get('summary',
self.entry.get('description', ''))
if self.entry.has_key('modified_parsed'):
date_modified = mtime(self.entry.modified_parsed)
else:
date_modified = None
fcat = self.get_tags()
comments = self.entry.get('comments', '')
return (link, title, guid, author, author_email, content,
date_modified, fcat, comments)
def process(self):
""" Process a post in a feed and saves it in the DB if necessary.
"""
from calibre.www.apps.feedjack import models
(link, title, guid, author, author_email, content, date_modified,
fcat, comments) = self.get_entry_data()
if False and self.options.verbose:
prints(u'[%d] Entry\n' \
u' title: %s\n' \
u' link: %s\n' \
u' guid: %s\n' \
u' author: %s\n' \
u' author_email: %s\n' \
u' tags: %s' % (
self.feed.id,
title, link, guid, author, author_email,
u' '.join(tcat.name for tcat in fcat)))
if guid in self.postdict:
tobj = self.postdict[guid]
if tobj.content != content or (date_modified and
tobj.date_modified != date_modified):
retval = ENTRY_UPDATED
if self.options.verbose:
prints('[%d] Updating existing post: %s' % (
self.feed.id, link))
if not date_modified:
# damn non-standard feeds
date_modified = tobj.date_modified
tobj.title = title
tobj.link = link
tobj.content = content
tobj.guid = guid
tobj.date_modified = date_modified
tobj.author = author
tobj.author_email = author_email
tobj.comments = comments
tobj.tags.clear()
[tobj.tags.add(tcat) for tcat in fcat]
tobj.save()
else:
retval = ENTRY_SAME
if self.options.verbose:
prints('[%d] Post has not changed: %s' % (self.feed.id,
link))
else:
retval = ENTRY_NEW
if self.options.verbose:
prints('[%d] Saving new post: %s' % (self.feed.id, link))
if not date_modified and self.fpf:
# if the feed has no date_modified info, we use the feed
# mtime or the current time
if self.fpf.feed.has_key('modified_parsed'):
date_modified = mtime(self.fpf.feed.modified_parsed)
elif self.fpf.has_key('modified'):
date_modified = mtime(self.fpf.modified)
if not date_modified:
date_modified = datetime.datetime.now()
tobj = models.Post(feed=self.feed, title=title, link=link,
content=content, guid=guid, date_modified=date_modified,
author=author, author_email=author_email,
comments=comments)
tobj.save()
[tobj.tags.add(tcat) for tcat in fcat]
return retval
class ProcessFeed:
def __init__(self, feed, options):
self.feed = feed
self.options = options
self.fpf = None
def process_entry(self, entry, postdict):
""" wrapper for ProcessEntry
"""
entry = ProcessEntry(self.feed, self.options, entry, postdict,
self.fpf)
ret_entry = entry.process()
del entry
return ret_entry
def process(self):
""" Downloads and parses a feed.
"""
from calibre.www.apps.feedjack import models
ret_values = {
ENTRY_NEW:0,
ENTRY_UPDATED:0,
ENTRY_SAME:0,
ENTRY_ERR:0}
prints(u'[%d] Processing feed %s' % (self.feed.id,
self.feed.feed_url))
# we check the etag and the modified time to save bandwith and
# avoid bans
try:
self.fpf = feedparser.parse(self.feed.feed_url,
agent=USER_AGENT,
etag=self.feed.etag)
except:
prints('! ERROR: feed cannot be parsed')
return FEED_ERRPARSE, ret_values
if hasattr(self.fpf, 'status'):
if self.options.verbose:
prints(u'[%d] HTTP status %d: %s' % (self.feed.id,
self.fpf.status,
self.feed.feed_url))
if self.fpf.status == 304:
# this means the feed has not changed
if self.options.verbose:
prints('[%d] Feed has not changed since ' \
'last check: %s' % (self.feed.id,
self.feed.feed_url))
return FEED_SAME, ret_values
if self.fpf.status >= 400:
# http error, ignore
prints('[%d] !HTTP_ERROR! %d: %s' % (self.feed.id,
self.fpf.status,
self.feed.feed_url))
return FEED_ERRHTTP, ret_values
if hasattr(self.fpf, 'bozo') and self.fpf.bozo:
prints('[%d] !BOZO! Feed is not well formed: %s' % (
self.feed.id, self.feed.feed_url))
# the feed has changed (or it is the first time we parse it)
# saving the etag and last_modified fields
self.feed.etag = self.fpf.get('etag', '')
# some times this is None (it never should) *sigh*
if self.feed.etag is None:
self.feed.etag = ''
try:
self.feed.last_modified = mtime(self.fpf.modified)
except:
pass
self.feed.title = self.fpf.feed.get('title', '')[0:254]
self.feed.tagline = self.fpf.feed.get('tagline', '')
self.feed.link = self.fpf.feed.get('link', '')
self.feed.last_checked = datetime.datetime.now()
if False and self.options.verbose:
prints(u'[%d] Feed info for: %s\n' \
u' title %s\n' \
u' tagline %s\n' \
u' link %s\n' \
u' last_checked %s' % (
self.feed.id, self.feed.feed_url, self.feed.title,
self.feed.tagline, self.feed.link, self.feed.last_checked))
guids = []
for entry in self.fpf.entries:
if entry.get('id', ''):
guids.append(entry.get('id', ''))
elif entry.title:
guids.append(entry.title)
elif entry.link:
guids.append(entry.link)
self.feed.save()
if guids:
postdict = dict([(post.guid, post)
for post in models.Post.objects.filter(
feed=self.feed.id).filter(guid__in=guids)])
else:
postdict = {}
for entry in self.fpf.entries:
try:
ret_entry = self.process_entry(entry, postdict)
except:
(etype, eobj, etb) = sys.exc_info()
print '[%d] ! -------------------------' % (self.feed.id,)
print traceback.format_exception(etype, eobj, etb)
traceback.print_exception(etype, eobj, etb)
print '[%d] ! -------------------------' % (self.feed.id,)
ret_entry = ENTRY_ERR
ret_values[ret_entry] += 1
self.feed.save()
return FEED_OK, ret_values
class Dispatcher:
def __init__(self, options, num_threads):
self.options = options
self.entry_stats = {
ENTRY_NEW:0,
ENTRY_UPDATED:0,
ENTRY_SAME:0,
ENTRY_ERR:0}
self.feed_stats = {
FEED_OK:0,
FEED_SAME:0,
FEED_ERRPARSE:0,
FEED_ERRHTTP:0,
FEED_ERREXC:0}
self.entry_trans = {
ENTRY_NEW:'new',
ENTRY_UPDATED:'updated',
ENTRY_SAME:'same',
ENTRY_ERR:'error'}
self.feed_trans = {
FEED_OK:'ok',
FEED_SAME:'unchanged',
FEED_ERRPARSE:'cant_parse',
FEED_ERRHTTP:'http_error',
FEED_ERREXC:'exception'}
self.entry_keys = sorted(self.entry_trans.keys())
self.feed_keys = sorted(self.feed_trans.keys())
if threadpool:
self.tpool = threadpool.ThreadPool(num_threads)
else:
self.tpool = None
self.time_start = datetime.datetime.now()
def add_job(self, feed):
""" adds a feed processing job to the pool
"""
if self.tpool:
req = threadpool.WorkRequest(self.process_feed_wrapper,
(feed,))
self.tpool.putRequest(req)
else:
# no threadpool module, just run the job
self.process_feed_wrapper(feed)
def process_feed_wrapper(self, feed):
""" wrapper for ProcessFeed
"""
start_time = datetime.datetime.now()
try:
pfeed = ProcessFeed(feed, self.options)
ret_feed, ret_entries = pfeed.process()
del pfeed
except:
(etype, eobj, etb) = sys.exc_info()
print '[%d] ! -------------------------' % (feed.id,)
print traceback.format_exception(etype, eobj, etb)
traceback.print_exception(etype, eobj, etb)
print '[%d] ! -------------------------' % (feed.id,)
ret_feed = FEED_ERREXC
ret_entries = {}
delta = datetime.datetime.now() - start_time
if delta.seconds > SLOWFEED_WARNING:
comment = u' (SLOW FEED!)'
else:
comment = u''
prints(u'[%d] Processed %s in %s [%s] [%s]%s' % (
feed.id, feed.feed_url, unicode(delta),
self.feed_trans[ret_feed],
u' '.join(u'%s=%d' % (self.entry_trans[key],
ret_entries[key]) for key in self.entry_keys),
comment))
self.feed_stats[ret_feed] += 1
for key, val in ret_entries.items():
self.entry_stats[key] += val
return ret_feed, ret_entries
def poll(self):
""" polls the active threads
"""
if not self.tpool:
# no thread pool, nothing to poll
return
while True:
try:
time.sleep(0.2)
self.tpool.poll()
except KeyboardInterrupt:
prints('! Cancelled by user')
break
except threadpool.NoResultsPending:
prints(u'* DONE in %s\n* Feeds: %s\n* Entries: %s' % (
unicode(datetime.datetime.now() - self.time_start),
u' '.join(u'%s=%d' % (self.feed_trans[key],
self.feed_stats[key])
for key in self.feed_keys),
u' '.join(u'%s=%d' % (self.entry_trans[key],
self.entry_stats[key])
for key in self.entry_keys)
))
break
def main():
""" Main function. Nothing to see here. Move along.
"""
parser = optparse.OptionParser(usage='%prog [options]',
version=USER_AGENT)
parser.add_option('--settings',
help='Python path to settings module. If this isn\'t provided, ' \
'the DJANGO_SETTINGS_MODULE enviroment variable will be used.')
parser.add_option('-f', '--feed', action='append', type='int',
help='A feed id to be updated. This option can be given multiple ' \
'times to update several feeds at the same time ' \
'(-f 1 -f 4 -f 7).')
parser.add_option('-s', '--site', type='int',
help='A site id to update.')
parser.add_option('-v', '--verbose', action='store_true',
dest='verbose', default=False, help='Verbose output.')
parser.add_option('-t', '--timeout', type='int', default=10,
help='Wait timeout in seconds when connecting to feeds.')
parser.add_option('-w', '--workerthreads', type='int', default=10,
help='Worker threads that will fetch feeds in parallel.')
options = parser.parse_args()[0]
if options.settings:
os.environ["DJANGO_SETTINGS_MODULE"] = options.settings
from calibre.www.apps.feedjack import models, fjcache
# settting socket timeout (default= 10 seconds)
socket.setdefaulttimeout(options.timeout)
# our job dispatcher
disp = Dispatcher(options, options.workerthreads)
prints('* BEGIN: %s' % (unicode(datetime.datetime.now()),))
if options.feed:
feeds = models.Feed.objects.filter(id__in=options.feed)
known_ids = []
for feed in feeds:
known_ids.append(feed.id)
disp.add_job(feed)
for feed in options.feed:
if feed not in known_ids:
prints('! Unknown feed id: %d' % (feed,))
elif options.site:
try:
site = models.Site.objects.get(pk=int(options.site))
except models.Site.DoesNotExist:
site = None
prints('! Unknown site id: %d' % (options.site,))
if site:
feeds = [sub.feed for sub in site.subscriber_set.all()]
for feed in feeds:
disp.add_job(feed)
else:
for feed in models.Feed.objects.filter(is_active=True):
disp.add_job(feed)
disp.poll()
# removing the cached data in all sites, this will only work with the
# memcached, db and file backends
[fjcache.cache_delsite(site.id) for site in models.Site.objects.all()]
if threadpool:
tcom = u'%d threads' % (options.workerthreads,)
else:
tcom = u'no threadpool module available, no parallel fetching'
prints('* END: %s (%s)' % (unicode(datetime.datetime.now()), tcom))
if __name__ == '__main__':
main()

View File

@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
"""
feedjack
Gustavo Picón
urls.py
"""
from django.conf.urls.defaults import patterns
from django.views.generic.simple import redirect_to
from calibre.www.apps.feedjack import views
urlpatterns = patterns('',
(r'^rss20.xml$', redirect_to,
{'url':'/feed/rss/'}),
(r'^feed/$', redirect_to,
{'url':'/feed/atom/'}),
(r'^feed/rss/$', views.rssfeed),
(r'^feed/atom/$', views.atomfeed),
(r'^feed/user/(?P<user>\d+)/tag/(?P<tag>.*)/$', redirect_to,
{'url':'/feed/atom/user/%(user)s/tag/%(tag)s/'}),
(r'^feed/user/(?P<user>\d+)/$', redirect_to,
{'url':'/feed/atom/user/%(user)s/'}),
(r'^feed/tag/(?P<tag>.*)/$', redirect_to,
{'url':'/feed/atom/tag/%(tag)s/'}),
(r'^feed/atom/user/(?P<user>\d+)/tag/(?P<tag>.*)/$', views.atomfeed),
(r'^feed/atom/user/(?P<user>\d+)/$', views.atomfeed),
(r'^feed/atom/tag/(?P<tag>.*)/$', views.atomfeed),
(r'^feed/rss/user/(?P<user>\d+)/tag/(?P<tag>.*)/$', views.rssfeed),
(r'^feed/rss/user/(?P<user>\d+)/$', views.rssfeed),
(r'^feed/rss/tag/(?P<tag>.*)/$', views.rssfeed),
(r'^user/(?P<user>\d+)/tag/(?P<tag>.*)/$', views.mainview),
(r'^user/(?P<user>\d+)/$', views.mainview),
(r'^tag/(?P<tag>.*)/$', views.mainview),
(r'^opml/$', views.opml),
(r'^foaf/$', views.foaf),
(r'^$', views.mainview),
)
#~

View File

@ -1,152 +0,0 @@
# -*- coding: utf-8 -*-
"""
feedjack
Gustavo Picón
views.py
"""
from django.utils import feedgenerator
from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.utils.cache import patch_vary_headers
from django.template import RequestContext, loader
from calibre.www.apps.feedjack import models, fjlib, fjcache
def initview(request):
""" Retrieves the basic data needed by all feeds (host, feeds, etc)
Returns a tuple of:
1. A valid cached response or None
2. The current site object
3. The cache key
4. The subscribers for the site (objects)
5. The feeds for the site (ids)
"""
site_id, cachekey = fjlib.getcurrentsite(request.META.get('HTTP_HOST',
'planet.calibre-ebook.com'), \
request.META.get('REQUEST_URI', request.META.get('PATH_INFO', '/')), \
request.META['QUERY_STRING'])
response = fjcache.cache_get(site_id, cachekey)
if response:
return response, None, cachekey, [], []
site = models.Site.objects.get(pk=site_id)
sfeeds_obj = fjlib.sitefeeds(site)
sfeeds_ids = [subscriber.feed.id for subscriber in sfeeds_obj]
return None, site, cachekey, sfeeds_obj, sfeeds_ids
def blogroll(request, btype):
""" View that handles the generation of blogrolls.
"""
response, site, cachekey, sfeeds_obj, sfeeds_ids = initview(request)
if response:
return response
# for some reason this isn't working:
#
#response = render_to_response('feedjack/%s.xml' % btype, \
# fjlib.get_extra_content(site, sfeeds_ids))
#response.mimetype = 'text/xml; charset=utf-8'
#
# so we must use this:
template = loader.get_template('feedjack/%s.xml' % btype)
ctx = {}
fjlib.get_extra_content(site, sfeeds_ids, ctx)
ctx = RequestContext(request, ctx)
response = HttpResponse(template.render(ctx) , \
mimetype='text/xml; charset=utf-8')
patch_vary_headers(response, ['Host'])
fjcache.cache_set(site, cachekey, response)
return response
def foaf(request):
""" View that handles the generation of the FOAF blogroll.
"""
return blogroll(request, 'foaf')
def opml(request):
""" View that handles the generation of the OPML blogroll.
"""
return blogroll(request, 'opml')
def buildfeed(request, feedclass, tag=None, user=None):
""" View that handles the feeds.
"""
response, site, cachekey, sfeeds_obj, sfeeds_ids = initview(request)
if response:
return response
object_list = fjlib.get_paginator(site, sfeeds_ids, page=0, tag=tag, \
user=user)[1]
feed = feedclass(\
title=site.title,
link=site.url,
description=site.description,
feed_url='%s/%s' % (site.url, '/feed/rss/'))
for post in object_list:
feed.add_item( \
title = '%s: %s' % (post.feed.name, post.title), \
link = post.link, \
description = post.content, \
author_email = post.author_email, \
author_name = post.author, \
pubdate = post.date_modified, \
unique_id = post.link, \
categories = [tag.name for tag in post.tags.all()])
response = HttpResponse(mimetype=feed.mime_type)
# per host caching
patch_vary_headers(response, ['Host'])
feed.write(response, 'utf-8')
if site.use_internal_cache:
fjcache.cache_set(site, cachekey, response)
return response
def rssfeed(request, tag=None, user=None):
""" Generates the RSS2 feed.
"""
return buildfeed(request, feedgenerator.Rss201rev2Feed, tag, user)
def atomfeed(request, tag=None, user=None):
""" Generates the Atom 1.0 feed.
"""
return buildfeed(request, feedgenerator.Atom1Feed, tag, user)
def mainview(request, tag=None, user=None):
""" View that handles all page requests.
"""
response, site, cachekey, sfeeds_obj, sfeeds_ids = initview(request)
if response:
return response
ctx = fjlib.page_context(request, site, tag, user, (sfeeds_obj, \
sfeeds_ids))
response = render_to_response('feedjack/%s/post_list.html' % \
(site.template), ctx, context_instance=RequestContext(request))
# per host caching, in case the cache middleware is enabled
patch_vary_headers(response, ['Host'])
if site.use_internal_cache:
fjcache.cache_set(site, cachekey, response)
return response
#~

View File

@ -1,10 +0,0 @@
changes:
date: 2008-05-18
change: Converted everything to 4 space tabs and made a few other changes to comply with Python Style Guide.
date: 2008-04-23
change: Added a mode called InlineType and a template tag that returns this models objects.
date: 2008-04-22
change: Added an 'extract_inlines' filter so you can loop over a list of inlines in a body of text.
change: Creating new inlines app.

View File

@ -1,27 +0,0 @@
==============================================
Django Basic Inlines
http://code.google.com/p/django-basic-apps/
==============================================
A simple book library application for Django projects.
To install this app, simply create a folder somewhere in
your PYTHONPATH named 'basic' and place the 'inlines'
app inside. Then add 'basic.inlines' to your projects
INSTALLED_APPS list in your settings.py file.
Inlines is a template filter that can be used in
conjunction with inline markup to insert content objects
into other pieces of content. An example would be inserting
a photo into a blog post body.
An example of the markup is:
<inline type="media.photo" id="1" />
The type attribute is app_name.model_name and the id is
the object id. Pretty simple.
In your template you would say:
{% load inlines %}
{{ post.body|render_inlines }}

View File

@ -1,5 +0,0 @@
from django.contrib import admin
from calibre.www.apps.inlines.models import *
admin.site.register(InlineType)

View File

@ -1,17 +0,0 @@
from django.db import models
from django.contrib.contenttypes.models import ContentType
class InlineType(models.Model):
""" InlineType model """
title = models.CharField(max_length=200)
content_type = models.ForeignKey(ContentType)
class Meta:
db_table = 'inline_types'
class Admin:
pass
def __unicode__(self):
return self.title

View File

@ -1,92 +0,0 @@
from django.template import TemplateSyntaxError
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.http import Http404
from django.utils.encoding import smart_unicode
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
def inlines(value, return_list=False):
try:
from BeautifulSoup import BeautifulStoneSoup
except ImportError:
from beautifulsoup import BeautifulStoneSoup
content = BeautifulStoneSoup(value, selfClosingTags=['inline','img','br','input','meta','link','hr'])
inline_list = []
if return_list:
for inline in content.findAll('inline'):
rendered_inline = render_inline(inline)
inline_list.append(rendered_inline['context'])
return inline_list
else:
for inline in content.findAll('inline'):
rendered_inline = render_inline(inline)
inline.replaceWith(render_to_string(rendered_inline['template'], rendered_inline['context']))
return mark_safe(content)
def render_inline(inline):
"""
Replace inline markup with template markup that matches the
appropriate app and model.
"""
# Look for inline type, 'app.model'
try:
app_label, model_name = inline['type'].split('.')
except:
if settings.DEBUG:
raise TemplateSyntaxError, "Couldn't find the attribute 'type' in the <inline> tag."
else:
return ''
# Look for content type
try:
content_type = ContentType.objects.get(app_label=app_label, model=model_name)
model = content_type.model_class()
except ContentType.DoesNotExist:
if settings.DEBUG:
raise TemplateSyntaxError, "Inline ContentType not found."
else:
return ''
# Check for an inline class attribute
try:
inline_class = smart_unicode(inline['class'])
except:
inline_class = ''
try:
try:
id_list = [int(i) for i in inline['ids'].split(',')]
obj_list = model.objects.in_bulk(id_list)
obj_list = list(obj_list[int(i)] for i in id_list)
context = { 'object_list': obj_list, 'class': inline_class }
except ValueError:
if settings.DEBUG:
raise ValueError, "The <inline> ids attribute is missing or invalid."
else:
return ''
except KeyError:
try:
obj = model.objects.get(pk=inline['id'])
context = { 'content_type':"%s.%s" % (app_label, model_name), 'object': obj, 'class': inline_class, 'settings': settings }
except model.DoesNotExist:
if settings.DEBUG:
raise model.DoesNotExist, "Object matching '%s' does not exist"
else:
return ''
except:
if settings.DEBUG:
raise TemplateSyntaxError, "The <inline> id attribute is missing or invalid."
else:
return ''
template = ["inlines/%s_%s.html" % (app_label, model_name), "inlines/default.html"]
rendered_inline = {'template':template, 'context':context}
return rendered_inline

View File

@ -1,81 +0,0 @@
from django import template
from calibre.www.apps.inlines.parser import inlines
from calibre.www.apps.inlines.models import InlineType
import re
register = template.Library()
@register.filter
def render_inlines(value):
"""
Renders inlines in a ``Post`` by passing them through inline templates.
Template Syntax::
{{ post.body|render_inlines|markdown:"safe" }}
Inline Syntax (singular)::
<inline type="<app_name>.<model_name>" id="<id>" class="med_left" />
Inline Syntax (plural)::
<inline type="<app_name>.<model_name>" ids="<id>, <id>, <id>" />
An inline template will be used to render the inline. Templates will be
locaed in the following maner:
``inlines/<app_name>_<model_name>.html``
The template will be passed the following context:
``object``
An object for the corresponding passed id.
or
``object_list``
A list of objects for the corresponding ids.
It would be wise to anticipate both object_list and object unless
you know for sure one or the other will only be present.
"""
return inlines(value)
@register.filter
def extract_inlines(value):
return inlines(value, True)
class InlineTypes(template.Node):
def __init__(self, var_name):
self.var_name = var_name
def render(self, context):
types = InlineType.objects.all()
context[self.var_name] = types
return ''
@register.tag(name='get_inline_types')
def do_get_inline_types(parser, token):
"""
Gets all inline types.
Syntax::
{% get_inline_types as [var_name] %}
Example usage::
{% get_inline_types as inline_list %}
"""
try:
tag_name, arg = token.contents.split(None, 1)
except ValueError:
raise template.TemplateSyntaxError, "%s tag requires arguments" % token.contents.split()[0]
m = re.search(r'as (\w+)', arg)
if not m:
raise template.TemplateSyntaxError, "%s tag had invalid arguments" % tag_name
var_name = m.groups()[0]
return InlineTypes(var_name)

View File

@ -1,11 +0,0 @@
#!/usr/bin/env python
from django.core.management import execute_manager
try:
import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)

View File

@ -1,49 +0,0 @@
# Django settings for planet project.
from calibre.www.settings import DEBUG, TEMPLATE_DEBUG, ADMINS, MANAGERS, \
TEMPLATE_LOADERS, TEMPLATE_DIRS, MIDDLEWARE_CLASSES, MEDIA_ROOT, \
MEDIA_URL, ADMIN_MEDIA_PREFIX, TEMPLATE_CONTEXT_PROCESSORS
FORCE_LOWERCASE_TAGS = False
MAX_TAG_LENGTH = 50
if not DEBUG:
MEDIA_URL = 'http://kovid.calibre-ebook.com/site_media/'
ADMIN_MEDIA_PREFIX = 'http://kovid.calibre-ebook.com/admin_media/'
MEDIA_ROOT = '/usr/local/calibre/src/calibre/www/static/'
if DEBUG:
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = '/tmp/kovid.db' # Or path to database file if using sqlite3.
else:
DATABASE_ENGINE = 'mysql'
DATABASE_NAME = 'calibre_kovid'
DATABASE_USER = 'calibre_django'
DATABASE_PASSWORD = open('/var/www/calibre-ebook.com/dbpass').read().strip()
SITE_ID = 1
# Make this unique, and don't share it with anybody.
if DEBUG:
SECRET_KEY = '06mv&t$cobjkijgg#0ndwm5#&90_(tm=oqi1bv-x^vii$*33n5'
else:
SECRET_KEY = open('/var/www/kovid.calibre-ebook.com/django_secret_key').read().strip()
ROOT_URLCONF = 'calibre.www.kovid.urls'
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.comments',
'django.contrib.markup',
'calibre.www.apps.inlines',
'tagging',
'calibre.www.apps.blog',
)

View File

@ -1,26 +0,0 @@
from django.conf.urls.defaults import patterns, include, handler404, handler500
from django.conf import settings
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^comments/', include('django.contrib.comments.urls')),
(r'', include('calibre.www.apps.blog.urls')),
)
if settings.DEBUG:
urlpatterns += patterns('',
(r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': settings.MEDIA_ROOT}),
)

View File

@ -1,12 +0,0 @@
Test
=====
Calibre planet can be run either in development mode or deployment mode. For testing,
it should be run in development mode as follows:
* Install django
* ``cd test && ./test``
* Planet is at `http://localhost:8000`

View File

@ -1,11 +0,0 @@
#!/usr/bin/env python
from django.core.management import execute_manager
try:
import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)

View File

@ -1,42 +0,0 @@
# Django settings for planet project.
from calibre.www.settings import DEBUG, TEMPLATE_DEBUG, ADMINS, MANAGERS, \
TEMPLATE_LOADERS, TEMPLATE_DIRS, MIDDLEWARE_CLASSES, MEDIA_ROOT, \
MEDIA_URL, ADMIN_MEDIA_PREFIX, TEMPLATE_CONTEXT_PROCESSORS
if not DEBUG:
MEDIA_URL = 'http://planet.calibre-ebook.com/site_media/'
ADMIN_MEDIA_PREFIX = 'http://planet.calibre-ebook.com/admin_media/'
MEDIA_ROOT = '/usr/local/calibre/src/calibre/www/static/'
if DEBUG:
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = '/tmp/planet.db' # Or path to database file if using sqlite3.
else:
DATABASE_ENGINE = 'mysql'
DATABASE_NAME = 'calibre_planet'
DATABASE_USER = 'calibre_django'
DATABASE_PASSWORD = open('/var/www/calibre-ebook.com/dbpass').read().strip()
SITE_ID = 1
# Make this unique, and don't share it with anybody.
if DEBUG:
SECRET_KEY = '06mv&t$cobjkijgg#0ndwm5#&90_(tm=oqi1bv-x^vii$*33n5'
else:
SECRET_KEY = open('/var/www/planet.calibre-ebook.com/django_secret_key').read().strip()
ROOT_URLCONF = 'calibre.www.planet.urls'
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'calibre.www.apps.feedjack',
)

View File

@ -1,4 +0,0 @@
#!/bin/sh
cp planet.db /tmp
cd ..
python manage.py runserver

View File

@ -1,24 +0,0 @@
from django.conf.urls.defaults import patterns, include, handler404, handler500
from django.conf import settings
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'', include('calibre.www.apps.feedjack.urls')),
)
if settings.DEBUG:
urlpatterns += patterns('',
(r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': settings.MEDIA_ROOT}),
)

View File

@ -1,4 +0,0 @@
#!/bin/sh
ssh divok "cd /usr/local/calibre && bzr up"
ssh divok /etc/init.d/apache2 graceful

View File

@ -1,86 +0,0 @@
# Django settings
# Import base settings from here into the site specific settings files
import socket, os
DEBUG = socket.gethostname() != 'divok'
TEMPLATE_DEBUG = DEBUG
ADMINS = (
('Kovid Goyal', 'kovid@kovidgoyal.net'),
)
MANAGERS = ADMINS
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
if DEBUG:
MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'static/')
else:
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
if DEBUG:
MEDIA_URL = 'http://127.0.0.1:8000/site_media/'
else:
MEDIA_URL = 'http://planet.calibre-ebook.com/site_media/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/'
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'America/Los_Angeles'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
ROOT_URLCONF = 'planet.urls'
if DEBUG:
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
os.path.join(os.path.dirname(__file__), 'templates'),
)
else:
TEMPLATE_DIRS = (
'/usr/local/calibre/src/calibre/www/templates',
)
TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media"
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,40 +0,0 @@
body {
font-family: sansserif;
background-color: #f1fff1;
}
img {
border:0 none;
}
#_header {
width: 100%;
border-bottom: 1px solid black;
margin-bottom: 20px;
height: 100px;
background-color: #A2B964;
overflow: hidden;
}
#_header > h1 {
margin: 0px; padding: 0px;
font-size: 60px;
font-family: sansserif;
font-weight: normal;
}
#_logo {
vertical-align: middle;
}
#_footer {
font-size: small;
text-align: right;
width: 100%;
border-top: 1px solid black;
font-style: italic;
padding-top: 10px;
padding-bottom: 5px;
}
#_footer img { vertical-align: middle; }

View File

@ -1,115 +0,0 @@
/* codebox header */
.wp_codebox_msgheader {
width: 100%;
border: 1px solid #DEDEB8;
border-bottom: 0;
font-weight: bold;
background: #F3F8D7 url(../images/arrow-square.gif) no-repeat right 5px;
color: #000000;
}
.wp_codebox_msgheader.active {
background-position: right -51px;
}
.wp_codebox_msgheader .right {
float: right;
text-align: right;
padding: 5px;
margin-right: 20px;
}
.wp_codebox_msgheader .right a {
font: 12px Arial, Tahoma !important;
font: 11px Arial, Tahoma;
}
.wp_codebox_msgheader .left,.wp_codebox_msgheader .left2 {
float: left;
/* background-color:#FFFFFF;
border:1px solid #DCDCDC;
padding:8px 0px 2px 8px;*/
font-family: tahoma, arial, verdana;
/* display: block;
width:50%;
margin: 0 auto;*/
padding: 5px 5px 5px 20px;
margin-left: 5px;
}
.wp_codebox_msgheader .left {
background: url(../images/view_code.png) no-repeat left;
}
.wp_codebox_msgheader .left2 {
background: url(../images/down.gif) no-repeat left;
}
.wp_codebox_msgheader .left a { /* margin:0px 5px 0px 10px;*/
font-weight: bold;
}
.wp_codebox_msgheader .left2 a { /* margin:0px 5px 0px 5px;*/
font-weight: bold;
}
.wp_codebox_msgheader .codebox_clear {
clear: both;
}
/* codebox */
.wp_codebox {
color: #100;
background-color: #f9f9f9;
border: 1px solid silver;
margin: 0 0 1.5em 0;
overflow: auto;
}
/* IE FIX */
.wp_codebox {
overflow-x: auto;
overflow-y: hidden;
padding-bottom: expression(this.scrollWidth > this.offsetWidth ? 15 : 0)
;
width: 100%;
}
.wp_codebox table {
border-collapse: collapse;
border: none;
margin: 0px;
}
.wp_codebox div,.wp_codebox td {
vertical-align: top;
padding: 2px 4px;
}
.wp_codebox td.line_numbers {
text-align: right;
background-color: #def;
color: #666;
overflow: visible;
border-right: 1px solid #B0BEC7;
table-layout: auto;
width: 15px;
}
/* potential overrides for other styles */
.wp_codebox pre {
border: none;
background: none;
margin: 0;
padding: 0;
width: auto;
float: none;
clear: none;
overflow: visible;
font-size: 12px;
line-height: 1.333;
}
.line_numbers pre {
padding-left: 10px;
}

View File

@ -1,313 +0,0 @@
/*
* Feedjack LostWoods theme
**************************
* Simple and green (where's the brown? -brown doesn't count)
*
* Copyright Diego Escalante Urrelo <diegoe@gnome.org>
*
*/
body {
font-size: 0.8em;
font-family: verdana;
margin: 0;
}
div {
/*border: 1px solid blue;
padding: 5px;
margin: 5px;*/
}
/*
* Structure
*/
#logo {
padding-top: 3px;
padding-left: 1em;
}
#tags {
overflow: auto;
padding: 5px;
text-align: right;
}
#paginate {
margin-bottom: 15px;
margin-left: 0px;
margin-top: 5px;
padding-left: 0;
text-align: left;
vertical-align: middle;
float: left;
width: 60%;
color: black;
}
#buttons {
text-align: right;
float: right;
vertical-align: middle;
color: #aaa;
width: 40%;
line-height: 1.7em;
padding:0;
padding-bottom: 10px;
}
#usertags {
clear: both;
margin: 5px;
padding: 5px;
width: 75%;
text-align: center;
}
#post_list {
clear : both;
width: 75%;
}
#sidebar {
width: 20%;
position: absolute;
right:0;
top: 170px;
border-left: 10px outset #6E9C60;
padding-left: 10px;
padding-right: 10px;
}
div.date {
font-size: x-large;
text-align: center;
font-style: italic;
padding: 5px;
color: black;
border: 2px solid #6E9C60;
border-right: 0;
border-left: 0;
margin-bottom: 10px;
}
/*
* Post structure
*/
div.post {
overflow: auto;
margin-bottom: 50px;
padding-bottom: 30px;
border-bottom: 12px solid #6E9C60;
border-right: 1px inset #6E9C60;
padding-right: 10px;
}
div.avatar {
float: right;
width: 15%;
text-align: center;
}
div.post-title {
text-align: left;
font-size: 180%;
font-family: trebuchet ms;
font-weight: bold;
padding: 5px;
width: 75%;
}
div.post-content {
width: 72%;
overflow: auto;
text-align: justify;
font-size: 90%;
line-height: 1.8em;
padding-left: 4em;
padding-right: 4em;
border-right: 1px dotted #ccc;
}
div.post-content li {
line-height: 130%;
margin-bottom: 0.6em;
}
div.post-content table{
border: 0;
margin-left: 50px;
}
div.post-content td {
border: 2px solid #ccc;
}
div.post-meta {
color: #666;
margin-top: 20px;
border-top: 1px solid #ccc;
width: 100%;
text-align: right;
}
div.tags {
margin-top: 10px;
}
/*
* Elements
*/
blockquote {
color: #777;
margin: 15px 30px 0px 10px;
padding: 20px;
border: 1px solid #ddd;
border-left: 7px solid #ddd;
}
a:link {
color: #4C6B46;
}
a:hover {
color: #33408A;
}
h1 a:link {
text-decoration: none;
color: inherit;
}
#buttons img {
vertical-align: middle;
}
#head h1 {
font-style: italic;
font-size: xx-large;
border-bottom: 3px solid #6E9C60;
margin-bottom: 5px;
margin-top: 10px;
}
#head a:link, a:visited, #head a:active {
color: inherit;
}
.love_feedjack {
font-size: 145%;
font-weight: bold;
font-style: italic;
}
.cloud_1 {
font-size: 50%;
}
.cloud_2 {
font-size: 100%;
}
.cloud_3 {
font-size: 120%;
font-weight: bold;
}
.cloud_4 {
font-size: 140%;
font-weight: bold;
}
.cloud_5 {
font-size: 160%;
font-weight: bold;
}
#paginate ul {
margin:0;
padding:0;
}
#paginate li {
display: inline;
margin: 2px;
padding: 10px;
background-color: #fbfbfb;
border: 2px solid #ddd;
line-height: 1.7em;
margin-left: 3px;
margin-right: 3px;
text-align: center;
vertical-align: middle;
}
#paginate li.tagname {
font-weight: bold;
font-size: 140%;
}
#paginate a:link {
color: #4C6B46;
}
#paginate a { text-decoration: none; }
img {
border: 0;
}
#sidebar ul {
list-style-type: none;
padding: 0;
margin: 0;
}
#sidebar li {
display: block;
clear: both;
line-height: 25px;
}
#sidebar a.nombre {
display: inline;
color: #33408A;
vertical-align: middle;
margin-left: 2px;
margin-right: 2px;
}
#sidebar img.face {
vertical-align: middle;
margin-right: 5px;
}
#sidebar h4 {
font-style: italic;
border-bottom: 1px solid #ccc;
}
#sidebar h4 + p {
text-align: justify;
}
#tags ul, #usertags ul {
background-color: #fbfbfb;
padding: 5px;
border: 2px solid #ddd;
margin:0;
text-align: center;
}
#tags li, #usertags li {
display: inline;
margin: 0;
line-height: 1.7em;
margin-left: 3px;
margin-right: 3px;
}
#tags a:link, #usertags a:link {
color: #4C6B46;
}
span.name {
color: #333;
}
span.nick {
color: #555;
}
span.url a {
color: #bbb;
}
span.url a:hover {
color: #777;
}
div.tags ul {
background-color: #fbfbfb;
padding: 5px;
border: 2px solid #ddd;
margin:0;
text-align: center;
}
div.tags li {
display: inline;
margin: 0;
line-height: 1.7em;
margin-left: 3px;
margin-right: 3px;
}
#planet_donate
{
text-align: center;
border-top: 1px solid gray;
border-bottom: 1px solid gray;
padding-top: 5px; padding-bottom: 5px;
}
#planet_ads {
text-align: center;
}

View File

@ -1,12 +0,0 @@
{% extends "base.html" %}
{% block title %}Page not found{% endblock %}
{% block header_text %}Page not found{% endblock %}
{% block content %}
<p>Sorry, but the requested page could not be found.</p>
{% endblock %}
{% block footer_text %} {% endblock %}

View File

@ -1,16 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<title>Page unavailable</title>
</head>
<body>
<h1>Page unavailable</h1>
<p>Sorry, but the requested page is unavailable due to a
server hiccup.</p>
<p>Our engineers have been notified, so check back later.</p>
</body>
</html>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<!--
Base template that defines navigation/sidebars etc.
-->
<head>
<title>{% block title %}calibre - E-book management{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}/styles/base.css" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
{% block extra_header %} {% endblock %}
</head>
<body>
<div id="_header">
<h1>
<img id="_logo" alt="calibre" src="{{ MEDIA_URL }}/img/logo.png" />
{% block header_text %}e-book management{% endblock %}
</h1>
</div>
<div id="_content">
{% block content %}Hello world{% endblock %}
</div>
<div id="_footer">
{% block footer_text %}
Created by Kovid Goyal.
Powered by <a href="http://www.djangoproject.com">
<img alt="Django" src="{{ MEDIA_URL }}/img/button-django.png"/>
</a>&nbsp;&nbsp;
{% endblock %}
</div>
</body>
</html>

View File

@ -1,190 +0,0 @@
{% extends "base.html" %}
{% block title%}Calibre Planet{% endblock %}
{% block header_text %}Planet{% endblock %}
{% block extra_header %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}/styles/planet.css" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}/styles/codebox.css" />
{% endblock %}
{% block content %}
<div id="paginate">
<ul>
{% if has_previous %}
<li><a href="?page={{ previous }}">&lt;&lt;</a></li>
{% endif %}
<li>
Page {{ page }} of {{ pages }} ({{ hits }} posts)
</li>
{% if has_next %}
<li><a href="?page={{ next }}">&gt;&gt;</a></li>
{% endif %}
{% if user %}
<li class="username"><a href="{{ user.feed.link }}">{{ user.name }}</a>talks about »</li>
{% endif %}
{% if tag %}
<li class="tagname">{{ tag.name }}</li>
{% endif %}
</ul>
</div> <!-- end paginate -->
<div id="buttons">
<a href="{{ site.url }}/feed/rss/" title="RSS 2.0 feed"><img src="{{ MEDIA_URL }}/img/button-rss.png"/></a> &bull;
<a href="{{ site.url }}/feed/atom/" title="Atom 1.0 feed"><img src="{{ MEDIA_URL }}/img/button-atom.png"/></a> &bull;
<a href="{{ site.url }}/opml/" title="OPML"><img src="{{ MEDIA_URL }}/img/button-opml.png"/></a> &bull;
<a href="{{ site.url }}/foaf/" title="FOAF"><img src="{{ MEDIA_URL }}/img/button-foaf.png"/></a>
</div> <!-- end buttons -->
<div id="post_list">
{% for item in object_list %}
{% ifchanged %}
<div class="date">{{ item.date_modified|date:"F j, Y" }}</div>
{% endifchanged %}
<div class="post">
{% ifchanged %}
<!-- {{ item.date_modified|date:"F j, Y" }} -->
<div class="avatar">
<a href="{{ item.feed.link }}">
<img
src="{{ MEDIA_URL }}/img/faces/{{ item.subscriber.shortname}}.png" alt="" />
<div class="url">
{{ item.feed.name|safe }}
</div>
</a>
</div>
{% endifchanged %}
{% if item.title %}
<div class="post-title">» {{ item.title }}</div>
{% else %}
<div class="post-title">» {{ item.subscriber.name }}</div>
{% endif %}
<div class="post-content">
<p>{{ item.content|safe }}</p>
<div class="post-meta">
<a href="{{ item.link }}">
{% if item.author %}by {{ item.author }} at{% endif %}
{{ item.date_modified|date:"g:i A" }}</a>
{% for tag in item.qtags %}
{% if forloop.first %}under{% endif %}
<a href="{{ site.url }}/tag/{{ tag.name }}">{{ tag.name }}</a>
{% if not forloop.last %}, {% endif %}
{% endfor %}
{% if item.comments %}
<a href="{{ item.comments }}">(Comments)</a>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
<div id="sidebar">
<h4>{{ site.name }}</h4>
<p style="text-align:left">
Planet Calibre is a window into the world, work and lives of Calibre developers and contributors.
</p>
<p style="text-align:left">
If you have a question or would like your blog added to the planet, please email
<a href="mailto:kovid@kovidgoyal.net">Kovid Goyal</a>.
</p>
<div id="planet_donate">
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="3028915" />
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="Donate to support calibre development" />
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
<div>Donate to support the development of calibre.</div>
</form>
</div>
<div id="tags">
<ul id="cloud">
{% for tag in tagcloud %}
<li><a
{% if user_id %}
href="{{ site.url }}/user/{{ user_id }}/tag/{{ tag.tagname|urlencode }}/"
{% else %}
href="{{ site.url }}/tag/{{ tag.tagname|urlencode }}/"
{% endif %}
title="{{ tag.count }} posts"
class="cloud_{{ tag.weight }}">{{ tag.tagname }}</a></li>
{% endfor %}
</ul>
</div>
<div id='planet_ads' style="z-index:0">
<script type="text/javascript"><!--
google_ad_client = "pub-2595272032872519";
/* Calibre Planet */
google_ad_slot = "6940385240";
google_ad_width = 120;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<h4>Last update</h4>
<b>{{ last_modified }}</b>
<h4>People</h4>
<ul class="suscriptores">
{% for feed in subscribers %}
<li>
<a href="{{ feed.feed.feed_url }}"
{% if feed.feed.last_modified %}
title="feed (last modified: {{ feed.feed.last_modified }})"
{% else %}
title="feed"
{% endif %}
>
<img src="{{ MEDIA_URL }}/img/feed.png" alt="feed"></a>
<a class="nombre" href="{{ site.url }}/user/{{ feed.feed.id }}"
title="{{ feed.feed.title }}">{{ feed.name }}</a></li>
{% endfor %}
</ul>
</div>
<div id="paginate">
<ul>
{% if has_previous %}
<li><a href="?page={{ previous }}">&lt;&lt;</a></li>
{% endif %}
<li>
Page {{ page }} of {{ pages }} (
{{ hits }} posts
)
</li>
{% if has_next %}
<li><a href="?page={{ next }}">&gt;&gt;</a></li>
{% endif %}
{% if user %}
<li class="username"><a href="{{ user.feed.link }}">{{ user.name }}</a></li>
{% endif %}
{% if tag %}
<li class="tagname">{{ tag.name }}</li>
{% endif %}
</ul>
</div>
<div style="height:50px">&nbsp;</div>
{% endblock %}

View File

@ -1,34 +0,0 @@
<?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/"
>
<!-- based on http://www-128.ibm.com/developerworks/xml/library/x-pblog/ -->
<foaf:Group>
<foaf:name>{{ site.title }}</foaf:name>
<foaf:homepage>{{ site.url }}</foaf:homepage>
<rdfs:seeAlso rdf:resource="{{ site.url }}/foaf/" />
{% for feed in feeds %}
<foaf:member>
<foaf:Person>
<foaf:name>{{ feed.name }}</foaf:name>
<foaf:weblog>
<foaf:Document rdf:about="{{ feed.link }}">
<dc:title>{{ feed.title }}</dc:title>
<rdfs:seeAlso>
<rss:channel rdf:about="{{ feed.feed_url }}" />
</rdfs:seeAlso>
</foaf:Document>
</foaf:weblog>
</foaf:Person>
</foaf:member>
{% endfor %}
</foaf:Group>
</rdf:RDF>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf8"?>
<opml version="1.1">
<head>
<title>{{ site.title }}</title>
</head>
<body>
{% for feed in feeds %}
<outline type="rss" text="{{ feed.name }}" description="{{ feed.title }}" htmlUrl="{{ feed.link }}" xmlUrl="{{ feed.feed_url }}"/>
{% endfor %}
</body>
</opml>

View File

@ -1,30 +0,0 @@
from django.utils.translation import ugettext as _
from tagging.managers import ModelTaggedItemManager, TagDescriptor
VERSION = (0, 3, 'pre')
class AlreadyRegistered(Exception):
"""
An attempt was made to register a model more than once.
"""
pass
registry = []
def register(model, tag_descriptor_attr='tags',
tagged_item_manager_attr='tagged'):
"""
Sets the given model class up for working with tags.
"""
if model in registry:
raise AlreadyRegistered(
_('The model %s has already been registered.') % model.__name__)
registry.append(model)
# Add tag descriptor
setattr(model, tag_descriptor_attr, TagDescriptor())
# Add custom manager
ModelTaggedItemManager().contribute_to_class(model,
tagged_item_manager_attr)

View File

@ -1,5 +0,0 @@
from django.contrib import admin
from tagging.models import Tag, TaggedItem
admin.site.register(TaggedItem)
admin.site.register(Tag)

View File

@ -1,107 +0,0 @@
"""
A custom Model Field for tagging.
"""
from django.db.models import signals
from django.db.models.fields import CharField
from django.utils.translation import ugettext_lazy as _
from tagging import settings
from tagging.models import Tag
from tagging.utils import edit_string_for_tags
class TagField(CharField):
"""
A "special" character field that actually works as a relationship to tags
"under the hood". This exposes a space-separated string of tags, but does
the splitting/reordering/etc. under the hood.
"""
def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 255)
kwargs['blank'] = kwargs.get('blank', True)
super(TagField, self).__init__(*args, **kwargs)
def contribute_to_class(self, cls, name):
super(TagField, self).contribute_to_class(cls, name)
# Make this object the descriptor for field access.
setattr(cls, self.name, self)
# Save tags back to the database post-save
signals.post_save.connect(self._save, cls, True)
def __get__(self, instance, owner=None):
"""
Tag getter. Returns an instance's tags if accessed on an instance, and
all of a model's tags if called on a class. That is, this model::
class Link(models.Model):
...
tags = TagField()
Lets you do both of these::
>>> l = Link.objects.get(...)
>>> l.tags
'tag1 tag2 tag3'
>>> Link.tags
'tag1 tag2 tag3 tag4'
"""
# Handle access on the model (i.e. Link.tags)
if instance is None:
return edit_string_for_tags(Tag.objects.usage_for_model(owner))
tags = self._get_instance_tag_cache(instance)
if tags is None:
if instance.pk is None:
self._set_instance_tag_cache(instance, '')
else:
self._set_instance_tag_cache(
instance, edit_string_for_tags(Tag.objects.get_for_object(instance)))
return self._get_instance_tag_cache(instance)
def __set__(self, instance, value):
"""
Set an object's tags.
"""
if instance is None:
raise AttributeError(_('%s can only be set on instances.') % self.name)
if settings.FORCE_LOWERCASE_TAGS and value is not None:
value = value.lower()
self._set_instance_tag_cache(instance, value)
def _save(self, **kwargs): #signal, sender, instance):
"""
Save tags back to the database
"""
tags = self._get_instance_tag_cache(kwargs['instance'])
if tags is not None:
Tag.objects.update_tags(kwargs['instance'], tags)
def __delete__(self, instance):
"""
Clear all of an object's tags.
"""
self._set_instance_tag_cache(instance, '')
def _get_instance_tag_cache(self, instance):
"""
Helper: get an instance's tag cache.
"""
return getattr(instance, '_%s_cache' % self.attname, None)
def _set_instance_tag_cache(self, instance, tags):
"""
Helper: set an instance's tag cache.
"""
setattr(instance, '_%s_cache' % self.attname, tags)
def get_internal_type(self):
return 'CharField'
def formfield(self, **kwargs):
from tagging import forms
defaults = {'form_class': forms.TagField}
defaults.update(kwargs)
return super(TagField, self).formfield(**defaults)

Some files were not shown because too many files have changed in this diff Show More