Skip to content

Commit cbc0a47

Browse files
committed
Merge branch 'master' into releases
1 parent 52ea52e commit cbc0a47

11 files changed

+283
-81
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@ nosetests.xml
3737
.pydevproject
3838

3939
sessions/
40-
logs/*.log
40+
logs/*.log*
4141
static/upload/
4242
peewee.db

app.py

+101-26
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
#err under Apache this is going to cause some issues
44
#because Apache does add "." to path
55
import os,sys
6+
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
7+
sys.path.insert(0,CURRENT_DIR)
8+
import config
69
import logging
7-
import dolog
8-
dolog.setup_logging(logdir="logs/",scrnlog=False,loglevel=logging.INFO)
9-
logger = logging.getLogger("")
10+
logger = logging.getLogger("blogstrap")
1011
logger.info("Starting blogstrap.py")
12+
logger.debug("app.py DEBUG messages enabled!")
1113

12-
#redirect stderr to the log
13-
sys.stderr = dolog.LoggerWriter(logger, logging.DEBUG)
14+
from dolog import LoggerWriter
15+
#redirect stderr to stdout and the logger created above
16+
sys.stderr = LoggerWriter(logger)
1417

1518
from collections import defaultdict
1619
from datetime import datetime
@@ -19,17 +22,18 @@
1922
import web
2023
from web import websafe
2124
import datetime
22-
import config
2325
import model as m
2426
import hashlib
27+
import xml.etree.ElementTree as ET
2528

26-
VERSION = "0.9.6-BETA"
29+
VERSION = "0.9.8-BETA"
2730

2831
logger.info("You are running version %s" % VERSION)
2932

30-
#if you change urls, make sure url[0] is your homepage!!
33+
#if you change urls, make sure urls[0] is your homepage!!
3134
urls = (
3235
r"/", "Index",
36+
r"/sitemap.xml","SiteMap",
3337
r"/newest", "IndexFull",
3438
r"/post", "BlogPost",
3539
r"/about", "About",
@@ -39,6 +43,7 @@
3943
r"/contactme", "ContactMe",
4044
r"/tags", "Tags",
4145
r"/search", "Search",
46+
r"/admin/preview", "Preview",
4247
r"/admin/(.*)","Admin")
4348

4449
app = web.application(urls, globals())
@@ -51,18 +56,29 @@
5156

5257
# Allow session to be reloadable in development mode.
5358
if web.config.get("_session") is None:
54-
session = web.session.Session(app, web.session.DiskStore("sessions"),
59+
session = web.session.Session(app, web.session.DiskStore("%s/sessions" % CURRENT_DIR),
5560
initializer=sinit)
5661

5762
web.config._session = session
5863
else:
5964
session = web.config._session
6065

6166

67+
b_data = m.BlogData.get(update=True)
68+
def blog_data(update = False):
69+
global b_data
70+
if update == False:
71+
return b_data
72+
else:
73+
b_data = m.BlogData.get(update=True)
74+
return b_data
75+
76+
6277
def update_db():
6378
global t_globals
6479
logger.info("Start updating blog data")
6580
m.BlogData.update_stats()
81+
blog_data(update=True)
6682
logger.info("Finish updating blog data")
6783

6884

@@ -107,6 +123,7 @@ def csrf_protected(f):
107123
def decorated(*args,**kwargs):
108124
inp = web.input()
109125
if not (inp.has_key('csrf_token') and inp.csrf_token==session.pop('csrf_token',None)):
126+
logger.debug("CSRF Detected!!!: ip=%s" % web.ctx.ip )
110127
raise web.HTTPError(
111128
"400 Bad request",
112129
{'content-type':'text/html'},
@@ -115,22 +132,13 @@ def decorated(*args,**kwargs):
115132
return f(*args,**kwargs)
116133
return decorated
117134

118-
b_data = m.BlogData.get(update=True)
119-
def blog_data(update = False):
120-
global b_data
121-
if update == False:
122-
return b_data
123-
else:
124-
b_data = m.BlogData.get(update=True)
125-
return b_data
126-
127135
#lets check if out database tables exist, if not create them
128136
for x in [m.User,m.Post,m.Image,m.Comment,m.BlogData]:
129137
#this will fail silently if they already exist
130138
x.create_table(True)
131139

132140

133-
render = web.template.render("templates/",
141+
render = web.template.render("%s/templates/" % CURRENT_DIR,
134142
#base="base",
135143
cache=config.cache)
136144
t_globals = web.template.Template.globals
@@ -158,6 +166,42 @@ def blog_data(update = False):
158166
print "Admin page currently set to: /admin/%s" % blog_data().adminurl
159167
logger.info("Admin page currently set to: /admin/%s" % blog_data().adminurl)
160168

169+
#Dynamically Generated sitemap.xml
170+
#By default simply includes links to all blog posts that are public
171+
class SiteMap:
172+
def GET(self):
173+
host = web.ctx.home
174+
print web.ctx
175+
#1st get all public posts
176+
posts = m.Post.all()
177+
#generate opening xml
178+
urlset = ET.Element("urlset",xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")
179+
#add root
180+
url = ET.SubElement(urlset,"url")
181+
loc = ET.SubElement(url,"loc")
182+
loc.text = "%s/" % host
183+
lastmod = ET.SubElement(url,"lastmod")
184+
lastmod.text = datetime.datetime.now().strftime("%Y-%m-%d")
185+
changefreq = ET.SubElement(url,"changefreq")
186+
changefreq.text = "daily"
187+
priority = ET.SubElement(url,"priority")
188+
priority.text = "0.8"
189+
190+
for post in posts:
191+
url = ET.SubElement(urlset,"url")
192+
loc = ET.SubElement(url,"loc")
193+
loc.text = "%s/post?pid=%d" % (host,post.id)
194+
lastmod = ET.SubElement(url,"lastmod")
195+
lastmod.text = post.updated.strftime("%Y-%m-%d")
196+
changefreq = ET.SubElement(url,"changefreq")
197+
changefreq.text = "yearly"
198+
199+
#ok return xml
200+
web.header("Content-Type", "text/xml; charset=utf-8")
201+
return ET.tostring(urlset, encoding="utf-8", method="xml")
202+
203+
204+
161205

162206
#This is the main Admin handler class. All admin functions are executed through POST's
163207
#utilizing a method=XXX variable passed by an ajax call, a form, etc.
@@ -187,7 +231,7 @@ def GET(self,url):
187231
#show login page
188232
return render.fullpageindex("Please Login to Continue",render.login(),"")
189233
else:
190-
images = m.Image.get_all()
234+
images = m.Image.get_all(private=True)
191235
return render.fullpageindex("Logged in as %s" % session.dispname,render.admin(images),render.bottom_admin())
192236

193237
def POST(self,url):
@@ -265,7 +309,7 @@ def POST(self,url):
265309
resl = m.Post.all(evenprivate=True)
266310
return render.datatable_posts(resl)
267311
elif method == "getallimages":
268-
images = m.Image.get_all()
312+
images = m.Image.get_all(private=True)
269313
return render.datatable_images(images)
270314
elif method == "getcomments":
271315
id = data.get("id","-1")
@@ -274,7 +318,7 @@ def POST(self,url):
274318
elif method == "getsinglepost":
275319
id = data.get("id","-1")
276320
resl = m.Post.get(m.Post.id==id)
277-
images = m.Image.get_all()
321+
images = m.Image.get_all(private=True)
278322
return render.adminsinglepost(resl,images)
279323
elif method == "getsingleimage":
280324
id = data.get("id","-1")
@@ -409,6 +453,35 @@ def GET(self):
409453

410454
return render.blogdetail(post,count,comments,next,prev,session.logged_in)
411455

456+
457+
class Preview:
458+
def POST(self):
459+
if session.logged_in == False:
460+
logger.info("Attempt to get preview page while not logged in!?")
461+
return web.notfound()
462+
else:
463+
data = web.input(nifile={})
464+
(resl,msg) = m.Post.update_from_input(data,session.uid)
465+
if resl == None:
466+
flash("There was a problem previewing: %s" % msg)
467+
return web.seeother(urls[0])
468+
469+
post = resl
470+
flash("success","Below is a preview of your posting. To save this switch to the other tab and click \"Update Post\"")
471+
count,comments = m.Comment.get_comments(post.id,False)
472+
#for next and prev links
473+
try:
474+
next = m.Post.get_next(post.created_at,1).get()
475+
except:
476+
next = None
477+
try:
478+
prev = m.Post.get_prev(post.created_at,1).get()
479+
except:
480+
prev = None
481+
482+
return render.blogdetail(post,count,comments,next,prev,session.logged_in)
483+
484+
412485
class BlogPost:
413486
def GET(self):
414487
pid = -1
@@ -419,7 +492,7 @@ def GET(self):
419492
raise Exception
420493
except:
421494
flash("error", "Sorry, that post doesn't exist!")
422-
return web.seeother("/")
495+
return web.seeother(urls[0])
423496

424497
#for next and prev links
425498
try:
@@ -435,7 +508,9 @@ def GET(self):
435508
return render.blogdetail(post,count,comments,next,prev,session.logged_in)
436509
class About:
437510
def GET(self):
438-
return render.about(m.User.by_id(1))
511+
#TODO: Eventually if we support multple users, fix this
512+
user = m.User.by_id(1)
513+
return render.about(user)
439514

440515
class ContactMe:
441516
def POST(self):
@@ -471,7 +546,7 @@ def POST(self):
471546
#failure, return him to homepage with a flash msg
472547
flash("error","Sorry, you failed the spam test, post not accepted. Turn on javascript.")
473548
print "New comment SPAM test failed by %s" % ip
474-
return web.seeother(url[0])
549+
return web.seeother(urls[0])
475550

476551
postid = websafe(int(data.get("pid","-1")))
477552
text = websafe(data.get("message",None))
@@ -495,7 +570,7 @@ def POST(self):
495570
user = m.User.by_id(session.uid)
496571
author = user.name
497572
email = user.email
498-
c1 = m.Comment.new(postid,parentid,title,author,text,email,session.logged_in,ip)
573+
c1 = m.Comment.new(postid,parentid,title,author,text,email,session.logged_in,ip,web.sendmail)
499574
if c1 != None:
500575
flash("success","Thanks for joining the discussion!")
501576
else:

config.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
import logging
2-
logger = logging.getLogger("")
31
import os
42
import sys
3+
4+
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
5+
#sys.path.insert(0,os.path.dirname(os.path.realpath(__file__)))
6+
import dolog
7+
import logging
8+
logger = dolog.setup_logging("blogstrap",logdir="%s/logs/" % CURRENT_DIR,scrnlog=False,loglevel=logging.DEBUG)
59
import web
610

11+
12+
713
####USER EDITABLE####
814
#disabled by default, set to true to enable
915
EMAIL_ERRORS = False
@@ -52,7 +58,7 @@
5258
if env == "production":
5359
web.config.debug = False
5460
cache = True
55-
logging.getLogger("").setLevel(logging.INFO)
61+
#logging.getLogger("").setLevel(logging.INFO)
5662
email_errors.to_address = ERROR_EMAIL_ADDR
5763
elif env == "staging":
5864
dac_host = "localhost:8000"

dolog.py

+11-14
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
from logging.handlers import RotatingFileHandler
33
import os
44

5-
def setup_logging(logdir=None, scrnlog=True, txtlog=True, loglevel=logging.DEBUG):
5+
def setup_logging(name,logdir=None, scrnlog=True, txtlog=True, loglevel=logging.DEBUG):
66
logdir = os.path.abspath(logdir)
77

88
if not os.path.exists(logdir):
99
os.mkdir(logdir)
1010

11-
log = logging.getLogger("")
11+
log = logging.getLogger(name)
1212
log.setLevel(loglevel)
13-
13+
log.propagate = False
14+
1415
log_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
1516

1617
if txtlog:
@@ -25,21 +26,17 @@ def setup_logging(logdir=None, scrnlog=True, txtlog=True, loglevel=logging.DEBUG
2526
console_handler.setFormatter(log_formatter)
2627
log.addHandler(console_handler)
2728

28-
29+
return log
30+
31+
2932
#this allows us to redirect stderr and stdout to a logfile
3033
#mainly because looking at stderr with lighttpd + mod_fastcgi is important
3134
class LoggerWriter:
32-
def __init__(self,logger, level):
35+
def __init__(self,logger):
3336
self.logger = logger
34-
self.level = level
35-
self.msg = ""
3637

37-
#do some funky things to help print tracebacks better
3838
def write(self, message):
3939
#pump message to stdout
40-
print message,
41-
self.msg += message.replace("\n"," ")
42-
if len(message) < 2:
43-
return
44-
self.logger.log(self.level, self.msg)
45-
self.msg = ""
40+
print message
41+
#and to log
42+
self.logger.error(message)

0 commit comments

Comments
 (0)