| 1 |
|
|---|
| 2 |
import datetime, time, os, sys, traceback |
|---|
| 3 |
import cherrypy as cpy |
|---|
| 4 |
import FileCabinet |
|---|
| 5 |
from buffet import BuffetTool |
|---|
| 6 |
from utils import config, run_callback |
|---|
| 7 |
|
|---|
| 8 |
class BlogRoot(object): |
|---|
| 9 |
_cp_config = {"tools.buffet.on": True, |
|---|
| 10 |
"tools.staticdir.root": os.path.abspath(os.curdir), |
|---|
| 11 |
"tools.encode.on": True, |
|---|
| 12 |
"tools.encode.encoding": config("blog_encoding", "utf-8")} |
|---|
| 13 |
|
|---|
| 14 |
def __init__(self): |
|---|
| 15 |
self.months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', |
|---|
| 16 |
'sep', 'oct', 'nov', 'dec'] |
|---|
| 17 |
self.timeformats = [["%Y", "%d", "%m", "%b", "%B"], |
|---|
| 18 |
["%Y %b", "%Y %m", "%Y %b", "%Y %B", "%m %d", "%b %d", "%B %d"], |
|---|
| 19 |
["%Y %m %d", "%Y %b %d", "%Y %B %d"]] |
|---|
| 20 |
self.plugins = [] |
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
cpy.tools.buffet = BuffetTool(config("template_engine")) |
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
self._cp_config["cpy.tools.encode.encoding"] = "utf-8" |
|---|
| 27 |
|
|---|
| 28 |
self.now = datetime.datetime.now |
|---|
| 29 |
self.last_update = self.now() |
|---|
| 30 |
self.num_entries = config('num_entries') |
|---|
| 31 |
self.datadir = config('datadir') |
|---|
| 32 |
self.ignore_directories = config('ignore_directories') |
|---|
| 33 |
self.fp = '' |
|---|
| 34 |
self.index() |
|---|
| 35 |
|
|---|
| 36 |
self.init_plugins(config('plugins')) |
|---|
| 37 |
|
|---|
| 38 |
def files(self, offset): |
|---|
| 39 |
return FileCabinet.get_most_recent(self.datadir, self.num_entries, \ |
|---|
| 40 |
self.ignore_directories, offset) |
|---|
| 41 |
|
|---|
| 42 |
@cpy.expose |
|---|
| 43 |
def index(self, offset=0): |
|---|
| 44 |
try: |
|---|
| 45 |
offset = int(offset) |
|---|
| 46 |
except ValueError: |
|---|
| 47 |
offset = 0 |
|---|
| 48 |
return self.render_page(self.files(offset), 'index', offset) |
|---|
| 49 |
|
|---|
| 50 |
def init_plugins(self, pluginlist): |
|---|
| 51 |
""" |
|---|
| 52 |
Initialize plugins. Assumes that each plugin contains a class of the |
|---|
| 53 |
same name as the file. If it does, this function attaches an instance |
|---|
| 54 |
of that class to self, and adds that instance to the plugins array. |
|---|
| 55 |
""" |
|---|
| 56 |
plugindir = config('plugin_dir', None) |
|---|
| 57 |
if not plugindir or not os.path.isdir(plugindir): |
|---|
| 58 |
cpy.log("Invalid Plugin Directory, no plugins loaded") |
|---|
| 59 |
return |
|---|
| 60 |
else: |
|---|
| 61 |
sys.path.append(plugindir) |
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 |
for p in pluginlist: |
|---|
| 67 |
try: |
|---|
| 68 |
mod = __import__(p) |
|---|
| 69 |
if not hasattr(self, p): |
|---|
| 70 |
instance = getattr(mod, p)(self) |
|---|
| 71 |
setattr(self, p, instance) |
|---|
| 72 |
self.plugins.append(instance) |
|---|
| 73 |
cpy.log("successfully imported plugin module %s" % p) |
|---|
| 74 |
else: |
|---|
| 75 |
raise ImportError |
|---|
| 76 |
|
|---|
| 77 |
|
|---|
| 78 |
except: |
|---|
| 79 |
cpy.log("import failed on module %s, module not loaded" % p) |
|---|
| 80 |
cpy.log("%s" % sys.exc_info()[0]) |
|---|
| 81 |
cpy.log("%s" % traceback.format_exc()) |
|---|
| 82 |
|
|---|
| 83 |
def error_page(self, error, status=404): |
|---|
| 84 |
cpy.response.status = status |
|---|
| 85 |
ns = cpy.config.get('/') |
|---|
| 86 |
ns.update({'error': error}) |
|---|
| 87 |
return (('head', ns), ('error', ns), ('foot', ns)) |
|---|
| 88 |
|
|---|
| 89 |
def render_page(self, entries, pagename='', offset=0): |
|---|
| 90 |
"""renders a collection of entries into a web page""" |
|---|
| 91 |
page = [] |
|---|
| 92 |
|
|---|
| 93 |
ns = cpy.config.get('/').copy() |
|---|
| 94 |
|
|---|
| 95 |
|
|---|
| 96 |
ns['pagename'] = pagename |
|---|
| 97 |
|
|---|
| 98 |
|
|---|
| 99 |
|
|---|
| 100 |
ns['offset'] = offset |
|---|
| 101 |
if len(entries) == ns['num_entries']: |
|---|
| 102 |
ns['offset_next'] = offset + ns['num_entries'] |
|---|
| 103 |
|
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
for dict_ in run_callback(self.plugins, 'cb_add_data'): |
|---|
| 107 |
for key, val in dict_.iteritems(): |
|---|
| 108 |
ns[key] = val |
|---|
| 109 |
|
|---|
| 110 |
|
|---|
| 111 |
if len(entries): |
|---|
| 112 |
for key in ('title', 'relpath', 'tmstr'): |
|---|
| 113 |
ns[key] = getattr(entries[0], key) |
|---|
| 114 |
page.append(('head', ns)) |
|---|
| 115 |
|
|---|
| 116 |
|
|---|
| 117 |
|
|---|
| 118 |
|
|---|
| 119 |
|
|---|
| 120 |
|
|---|
| 121 |
page.extend(run_callback(self.plugins, 'cb_story_start', entries)) |
|---|
| 122 |
|
|---|
| 123 |
for e in entries: |
|---|
| 124 |
|
|---|
| 125 |
|
|---|
| 126 |
run_callback(self.plugins, 'cb_story', e) |
|---|
| 127 |
|
|---|
| 128 |
|
|---|
| 129 |
storyns = ns.copy() |
|---|
| 130 |
storyns.update([(key, getattr(e, key)) |
|---|
| 131 |
for key in dir(e) if not key.startswith('_')]) |
|---|
| 132 |
|
|---|
| 133 |
page.append(('story', storyns)) |
|---|
| 134 |
|
|---|
| 135 |
|
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 |
page.extend(run_callback(self.plugins, 'cb_story_end', entries)) |
|---|
| 139 |
page.append(('foot', ns)) |
|---|
| 140 |
|
|---|
| 141 |
|
|---|
| 142 |
run_callback(self.plugins, 'cb_page_end') |
|---|
| 143 |
|
|---|
| 144 |
return page |
|---|
| 145 |
|
|---|
| 146 |
def stripall(self, str_, *strippers): |
|---|
| 147 |
"""return a string stripped of all extensions in strippers""" |
|---|
| 148 |
for stripper in strippers: |
|---|
| 149 |
if str_.endswith(stripper): |
|---|
| 150 |
str_ = str_[:-len(stripper)] |
|---|
| 151 |
return str_ |
|---|
| 152 |
|
|---|
| 153 |
@cpy.expose |
|---|
| 154 |
def default(self, *args, **kwargs): |
|---|
| 155 |
|
|---|
| 156 |
|
|---|
| 157 |
call_result = run_callback(self.plugins, 'cb_default', args) |
|---|
| 158 |
if call_result != []: return self.render_page(call_result[1:], call_result[0]) |
|---|
| 159 |
|
|---|
| 160 |
try: |
|---|
| 161 |
offset = int(kwargs.get('offset', 0)) |
|---|
| 162 |
except ValueError: |
|---|
| 163 |
offset = 0 |
|---|
| 164 |
|
|---|
| 165 |
z = args[0] |
|---|
| 166 |
l = len(args) |
|---|
| 167 |
if l <= len(self.timeformats): |
|---|
| 168 |
|
|---|
| 169 |
for fmt in self.timeformats[l-1]: |
|---|
| 170 |
try: |
|---|
| 171 |
t = time.strptime(' '.join(args), fmt) |
|---|
| 172 |
if "%Y" in fmt: |
|---|
| 173 |
year = t[0] |
|---|
| 174 |
else: |
|---|
| 175 |
year = self.now().year |
|---|
| 176 |
if "%m" in fmt or "%b" in fmt or "%B" in fmt: |
|---|
| 177 |
month = t[1] |
|---|
| 178 |
else: |
|---|
| 179 |
month = None |
|---|
| 180 |
if "%d" in fmt: |
|---|
| 181 |
day = t[2] |
|---|
| 182 |
else: |
|---|
| 183 |
day = None |
|---|
| 184 |
entries = FileCabinet.get_entries_by_date(year, month, day) |
|---|
| 185 |
if entries: |
|---|
| 186 |
entries = entries[offset:offset + config('num_entries')] |
|---|
| 187 |
return self.render_page(entries, ' '.join(args), offset) |
|---|
| 188 |
except ValueError: |
|---|
| 189 |
|
|---|
| 190 |
pass |
|---|
| 191 |
z = os.path.join(*args) |
|---|
| 192 |
fname = self.stripall(z, '.html', '.htm', '.txt') |
|---|
| 193 |
e = FileCabinet.get_one(fname, self.datadir) |
|---|
| 194 |
if e: |
|---|
| 195 |
return self.render_page([e]) |
|---|
| 196 |
return self.error_page('Page Not Found', 404) |
|---|
| 197 |
|
|---|
| 198 |
if __name__ == '__main__': |
|---|
| 199 |
|
|---|
| 200 |
os.chdir(os.path.dirname(os.path.abspath(__file__))) |
|---|
| 201 |
|
|---|
| 202 |
|
|---|
| 203 |
|
|---|
| 204 |
|
|---|
| 205 |
cpy.config.update("site.conf") |
|---|
| 206 |
|
|---|
| 207 |
cpy.config.update("cherryblossom.conf") |
|---|
| 208 |
|
|---|
| 209 |
cpy.quickstart(BlogRoot(), "/", "cherryblossom.conf") |
|---|