Skip to content

Commit 52ea52e

Browse files
committedJan 19, 2014
Merge branch 'master' into releases
2 parents 171a4a4 + 56ac201 commit 52ea52e

12 files changed

+1243
-51
lines changed
 

‎README.md

+19-18
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,49 @@ blogstrap.py
33

44
Author: [mox1](http://moxone.me)
55

6-
blogstrap.py is a simple, no frills blog system, powered by Bootstrap and web.py
6+
blogstrap.py is a simple, no frills blog content management system, powered by Bootstrap and web.py
77
In its default configruation the full software stack consists of:
88
* Bootstrap3 (http://getbootstrap.com/)
99
* web.py (http://webpy.org/)
1010
* Peewee ORM (https://github.com/coleifer/peewee)
1111
* sqlite (http://www.sqlite.org/)
1212

13-
1413
blogstrap.py has been designed to be simple and "just work", if you experience problems please create an issue on github!
1514

15+
Blogstrap.py includes an awesome default theme written by [HackerThemes](http://www.hackerthemes.com). Any of HackerThemes "Tales" themes
16+
will work by default with blogstrap.py.
17+
1618
Example
1719
==============
18-
[My Blog](http://moxone.me) - Consider this site a real world example / demo of blogstrap.py
19-
20-
While blogstrap.py is open-source, my blog does include a copyrighted bootstrap template, called [Tales](https://wrapbootstrap.com/theme/tales-responsive-blog-theme-WB034M8P5).
21-
Tales is not required, but you will notice the default blogstrap.py is different from the example above. Please don't steal this guys hard work! (I am not affiliated with him)
20+
[mox1's Blog](http://moxone.me) - A live real world example / demo of blogstrap.py
2221

2322
Setup / Initial Config
2423
==============
2524
mkdir my_blog
2625
cd my_blog
2726
git clone https://github.com/mox1/webpy-bootstrap-blog .
28-
pip install -r requirements.txt (or you can do this through apt/rpm etc.)
29-
(Optional) open config.py with your favorite editor and edit a few config settings
27+
pip install -r requirements.txt
28+
vim config.py
3029
python ./app.py
31-
In your favorite browser navigate to localhost:8080/admin/admin
32-
Create A User
33-
Modify the Global Settings
30+
31+
In your favorite browser navigate to localhost:8080/admin/admin
3432

33+
Usage
34+
==============
35+
Have a look at the [QuickStart guide](http://www.moxone.me/post?pid=2)
3536

3637

3738
Running with apache / lighttpd / nginx
38-
====
39-
While the Usage instruction above will get you up and running with a quick site (via CherryPi), for production / real-world environemnts,
40-
you will probably want to run web.py under apache or lighttpd. google "web.py + " your desired web server for instruction.
39+
==============
40+
While the usage instructions above will get you up and running with a quick site for production / real-world environemnts,
41+
you will probably want to run web.py under apache or lighttpd. Google "web.py + " your desired web server for instruction.
4142
For apache mod_wsgi see: http://webpy.org/cookbook/mod_wsgi-apache
4243
For lighttpd mod_fastcgi see: http://webpy.org/cookbook/fastcgi-lighttpd
4344

44-
Running the tests
45-
====
46-
There aren't any tests yet, eventually some will be here.
47-
python test.py
45+
Getting Involed
46+
==============
47+
It is my goal to eventually turn blogstrap.py into a fully functional, feature complete Blogging platform. But, I can't do all of that by myself!
48+
Anyone wishing to get involed into the project should join the mailing list at [blogstrap-py@googlegroups.com](blogstrap-py@googlegroups.com)
4849

4950

5051
Copyright

‎app.py

+23-14
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import model as m
2424
import hashlib
2525

26-
VERSION = "0.9.5-BETA"
26+
VERSION = "0.9.6-BETA"
2727

2828
logger.info("You are running version %s" % VERSION)
2929

@@ -61,18 +61,19 @@
6161

6262
def update_db():
6363
global t_globals
64-
logger.debug("Start updating blog data")
64+
logger.info("Start updating blog data")
6565
m.BlogData.update_stats()
66-
logger.debug("Finish updating blog data")
66+
logger.info("Finish updating blog data")
6767

6868

6969
#this function allows us to do periodic things
7070
#not perfect, but eh.
7171
#for now this simply updates the statistics
7272
#but could easily be used to do more "cron" like things
73+
#this gets called for EVERY page, so don't do any heavy lifting
7374
next_cron_run = 0
7475
def my_processor(handler):
75-
global next_cron_run,td_next
76+
global next_cron_run
7677
if (time.time() > next_cron_run):
7778
logger.debug("DOING CRON RUN")
7879
thr = threading.Thread(target=update_db)
@@ -148,7 +149,7 @@ def blog_data(update = False):
148149
t_globals["dt_as_ago"] = m.datetime_ago
149150
t_globals["hashlib"] = hashlib
150151
t_globals["csrf_token"] = csrf_token
151-
152+
t_globals["max_comment"] = config.MAX_COMMENT
152153

153154
#get statistics
154155
#This is now updated in the Admin class, method globalsettings
@@ -379,14 +380,14 @@ def GET(self):
379380
query = websafe(data["q"])
380381
#short querys are a heachache, lets avoid them
381382
if len(query) < 3:
382-
flash("error","Search string \"%s\" is too short." % q)
383+
flash("error","Search string \"%s\" is too short." % query)
383384
return web.seeother(urls[0])
384385
prev,next = calcprevnext(data)
385386
#if we drop in here, qo query
386387
posts = m.Post.search(query,page=next,max=10)
387388
content = render.blog_teasers(posts,"search?q=%s&n=%s" % (query,(prev-1)),
388389
"search?q=%s&n=%s" % (query,(next+1)))
389-
return render.index("Search results for %s" % query, content, "Search Results",query)
390+
return render.index("Search results for \"%s\"" % query, content, "Search Results","\"%s\""% query)
390391

391392
class IndexFull:
392393
def GET(self):
@@ -472,15 +473,15 @@ def POST(self):
472473
print "New comment SPAM test failed by %s" % ip
473474
return web.seeother(url[0])
474475

475-
postid = websafe(int(data.get("pid",-1)))
476+
postid = websafe(int(data.get("pid","-1")))
476477
text = websafe(data.get("message",None))
477-
if text == None:
478+
if text == None or len(text) < 1:
478479
flash("error","Please add some text to that comment!")
479480
#TODO: can we use web.py to get the url referrer and send them back there?
480-
return web.seeother("post?pid=%d" % postid)
481+
return web.seeother("post?pid=%s" % postid)
481482
if len(text) > config.MAX_COMMENT:
482-
flash("error","Comment too large, max %d characters" % config.MAX_COMMENT)
483-
return web.seeother("post?pid=%d" % postid)
483+
flash("error","Comment length of %d too large, max %d characters" % (len(text),config.MAX_COMMENT))
484+
return web.seeother("post?pid=%s" % postid)
484485

485486

486487
#spam check passed, continue
@@ -525,13 +526,21 @@ def calcprevnext(data):
525526
def internalerror():
526527
logger.error("Internal error:",exc_info=True)
527528
msg = """
528-
An internal server error occurred. Please try your request again by
529+
We're sorry, an internal error occurred has. Please try your request again by
529530
hitting back on your web browser. You can also <a href="/"> go back
530531
to the main page.</a>
531532
"""
532533
return web.internalerror(msg)
533534

534-
535+
def notfound():
536+
logger.error("404 Not found error has occurred")
537+
#return the homepage with not found
538+
539+
return web.notfound(render.fullpageindex("404 Error Not Found","<p>404 Error: The page you have requested was not found!</p>",""))
540+
541+
#add our 404 handler
542+
app.notfound = notfound
543+
535544
# Setup the application's error handler
536545
app.internalerror = web.debugerror if web.config.debug else internalerror
537546

‎config.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
TIME_FORMAT = "%a, %d %b %Y" # %H:%M:%S
1515
#double %% required
1616
LONG_TIME_FORMAT = "%%A, %%b %s %%Y"
17+
#The Database limit for this field is 16656, values larger than that will error out.
1718
MAX_COMMENT = 4096
1819
#how often to update stats (in seconds)
19-
STAT_UPDATES = 60 * 60
20+
STAT_UPDATES = 60 * 60 #every hour by default
2021
####END USER EDITABLE####
2122

2223
valid_upload_ext = [".png",".jpg",".jpeg",".gif",".ico"]

‎model.py

+26-8
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ class User(BaseModel):
152152
crypted_password = pw.CharField(max_length=40, null=False)
153153
salt = pw.CharField(max_length=40, null=False)
154154
remember_token = pw.CharField(max_length=64, null=True)
155+
#for future use
156+
sm_links = pw.TextField(null=True)
155157

156158
@staticmethod
157159
def is_setup():
@@ -298,6 +300,7 @@ class Post(BaseModel):
298300
#1 = moderated (admin must approve comments)
299301
#2 = disabled (no new comments allowed)
300302
moderate = pw.IntegerField(null=False,default=0)
303+
teaser_txt = pw.TextField(null=False)
301304

302305
#data is web.input, mapping is
303306
# title = data.nptitle
@@ -322,6 +325,7 @@ def update_from_input(data,userid):
322325
tags = data["uptags"]
323326
cat = data["upcat"]
324327
scat = data["upsubcat"]
328+
teaser_txt = data["uptease"]
325329
html = data["uphtml"]
326330
mod = int(data["upmod"])
327331
if data.get("upfav","false") == "true":
@@ -353,6 +357,7 @@ def update_from_input(data,userid):
353357
post.public = public
354358
post.updated = datetime.now()
355359
post.moderate = mod
360+
post.teaser_txt = teaser_txt
356361
post.save()
357362
return (post,"Successfully updated post!")
358363

@@ -442,6 +447,7 @@ def new_from_input(data,userid):
442447
cat = data["npcat"]
443448
scat = data["npsubcat"]
444449
html = data["nphtml"]
450+
teaser_txt = data["nptease"]
445451
mod = int(data["npmod"])
446452
if data.get("npfav","false") == "true":
447453
fav = True
@@ -459,17 +465,19 @@ def new_from_input(data,userid):
459465
return (None,"Sorry there was an error: %s" % e.message)
460466

461467
post = Post.new(title=title,tags=tags,author_id=userid,image=image,
462-
small_image=small_image,html=html,cat=cat,subcat=scat,fav=fav,public=public,moderate=mod)
468+
small_image=small_image,html=html,cat=cat,subcat=scat,
469+
fav=fav,public=public,moderate=mod,teaser_txt=teaser_txt)
470+
463471
return (post,"Successfully created new post!")
464472

465473

466474
@staticmethod
467-
def new(title,tags,author_id,html,image,small_image,cat=None,subcat=None,fav = False,public=True,moderate=0):
475+
def new(title,tags,author_id,html,image,small_image,cat=None,subcat=None,fav=False,public=True,moderate=0,teaser_txt=""):
468476
#get user by id
469477
user = User.by_id(author_id)
470478
p = Post.create(title=title,tags=tags,author=user,html=html,
471479
image=image,small_image=small_image,created_at=datetime.now(),
472-
favorite=fav,category=cat,subcategory=subcat,moderate=moderate)
480+
favorite=fav,category=cat,subcategory=subcat,moderate=moderate,teaser_txt=teaser_txt)
473481
return p
474482

475483
#every time this is called, a pageview count is updated"
@@ -512,7 +520,7 @@ class Comment(BaseModel):
512520
ip = pw.CharField(max_length=20,null=True)
513521
post = pw.ForeignKeyField(Post,null=False)
514522
text = pw.TextField(max_length=16656,null=False)
515-
email = pw.CharField(max_length=1024,null=False,default="none@none.net")
523+
email = pw.CharField(max_length=512,null=False,default="none@none.net")
516524
parent = pw.ForeignKeyField('self',related_name='children',null=True)
517525
rank = pw.IntegerField(null=False,default=0)
518526
indent = pw.IntegerField(null=False,default=0)
@@ -608,11 +616,19 @@ def get_comments(postid,show_mod):
608616

609617
@staticmethod
610618
def new(postid,parentid,title,author,text,email="none@none.net",admin=False,ip=None):
611-
#1st get post, check if comments are allowed
619+
#check for size violations
620+
if (len(text) > config.MAX_COMMENT) or \
621+
(len(text) < 1) or \
622+
(len(email) > 512) or \
623+
(len(author) > 512) or \
624+
(len(title) > 512):
625+
return None
626+
627+
#2nd get post, check if comments are allowed
612628
post = Post.by_id(postid)
613629
if (post == None) or (post.moderate == 2):
614630
return None
615-
#put this post into a "review by admin state"
631+
#If moderation is turned on for this post,put this comment into a "review by admin state"
616632
if post.moderate ==1:
617633
status = 5
618634
else:
@@ -623,6 +639,8 @@ def new(postid,parentid,title,author,text,email="none@none.net",admin=False,ip=N
623639
status = 0
624640
#we need to convert \n into <br>
625641
text = newline_to_break(text)
642+
#now, actually create and insert comment
643+
##see :http://evolt.org/node/4047/ for algorithm
626644
rank = 0
627645
indent = 0
628646
lastcomment = None
@@ -647,10 +665,10 @@ def new(postid,parentid,title,author,text,email="none@none.net",admin=False,ip=N
647665
new_comment = Comment.create(email=email,parent=parent,title=title,
648666
author=author,post=postid,text=text,rank=start_rank+1,indent=parent.indent+1,
649667
created_at=datetime.now(),from_admin=admin,status=status,ip=ip)
650-
651-
652668
return new_comment
653669

670+
671+
654672
#this class / table performs 2 function
655673
# 1. it is used as an optimization
656674
# holding the results of other queries that rarely change

‎static/css/app.css

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
body {
23
padding-top: 0px; /* 50px to make the container go all the way to the bottom of the topbar */
34
}

‎static/css/hackertheme-tales-bluered.css

+1,147
Large diffs are not rendered by default.

‎templates/add_comment.html

+7-5
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@ <h2><i class="icon-heart"></i> Add Comment</h2>
88
$if logged_in == False:
99
<div class="row">
1010
<div class="col-md-6">
11-
<input type="text" name="name" id="comment-name" placeholder="Name" class="form-control input-lg">
11+
<input type="text" name="name" id="comment-name" placeholder="Name" class="form-control input-lg"
12+
rel="tooltip" data-original-title="This will be shown as your username with this post. Minimum 3 characters."
13+
pattern=".{3,}" title="At least 3 characters please.">
1214
</div>
1315
<div class="col-md-6">
14-
<input type="email" name="e" id="comment-email" placeholder="Email" class="form-control input-lg">
16+
<input type="email" name="e" id="comment-email" placeholder="Email" class="form-control input-lg"
17+
rel="tooltip" data-original-title="This is only used to fetch your gravatar. It is *NOT* shown publically. Leave blank for a default gravatar.">
1518
</div>
1619
</div>
1720
$else:
1821
<p>You are logged in, your current username and e-mail will be used for this post.</p>
19-
2022
<input type="text" name="title" id="input-comment-title" placeholder="Title..." class="form-control input-lg">
21-
<textarea rows="10" name="message" id="comment-body" placeholder="Your thoughts..." class="form-control input-lg" maxlength="4096"></textarea>
22-
23+
<p> HTML not allowed. Maximum length is $max_comment characters. </p>
24+
<textarea rows="10" name="message" id="comment-body" placeholder="Your thoughts..." class="form-control input-lg" maxlength="$max_comment"></textarea>
2325
<input type="hidden" name="pid" id="input-pid" value="$(cur_post.id)">
2426
<input type="hidden" name="replyto" id="input-replyto" value="-1">
2527
<input type="hidden" name="email" id="input-email" value="">

‎templates/admin.html

+9-1
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,17 @@ <h3 class="panel-title"> New Post </h3>
8080
</div>
8181
</div>
8282
<div class="row">
83+
<div class="col-md-12">
84+
<label class="control-label" for="nptease">Leading Text / Teaser</label>
85+
<textarea class="form-control" id="nptease" name="nptease" style="height: 100px: width: 100%;"
86+
placeholder="A short description of this post. Displayed before showing the full blog post."></textarea>
87+
</div>
88+
</div>
89+
<div class="row">
8390
<div class="col-md-12">
8491
<label class="control-label" for="nphtml">Blog post/html:</label>
85-
<textarea class="form-control" id="nphtml" name="nphtml" style="height: 300px; width:100%;" placeholder="Blog post goes here, HTML is allowed."> </textarea>
92+
<textarea class="form-control" id="nphtml" name="nphtml" style="height: 300px; width:100%;"
93+
placeholder="Blog post goes here, HTML is allowed."></textarea>
8694
</div>
8795
</div>
8896
<div class="row">

‎templates/adminsinglepost.html

+6
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ <h4 class="modal-title" id="myModalLabel">Editing Post #$(post.id) - "$(post.tit
5353
</div>
5454
</div>
5555
<div class="row">
56+
<div class="col-md-12">
57+
<label class="control-label" for="uptease">Leading Text / Teaser</label>
58+
<textarea class="form-control" id="uptease" name="uptease" style="height: 100px: width: 100%;">$(post.teaser_txt)</textarea>
59+
</div>
60+
</div>
61+
<div class="row">
5662
<div class="col-md-12">
5763
<label class="control-label" for="uphtml">Blog post/html:</label>
5864
<textarea class="form-control" id="uphtml" name="uphtml" style="height: 300px; width:100%;">$(post.html)</textarea>

‎templates/copyright.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
$def with()
22

3-
<p>powered by blogstrap.py - <a href="https://github.com/mox1/webpy-bootstrap-blog"> Fork me on GitHub</a></p>
3+
<p><a href="http://hackerthemes.com/">designed by HackerThemes</a> - <a href="https://github.com/mox1/webpy-bootstrap-blog">powered by blogstrap.py</a></p>

‎templates/singleteaser.html

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
$def with(blogpost)
2-
$ preview_text = blogpost.html[0:128] + "..."
32
<div class="col-md-6 col-sm-6">
43
<article class=" blog-teaser">
54
<header>
@@ -12,7 +11,7 @@ <h3><a href="/post?pid=$(blogpost.id)">$:(blogpost.title)</a></h3>
1211
<hr>
1312
</header>
1413
<div class="body">
15-
$:preview_text
14+
$:(blogpost.teaser_txt)
1615
</div>
1716
<div class="clearfix">
1817
<a href="/post?pid=$:(blogpost.id)" class="btn btn-tales-one">Read more</a>

‎templates/top.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,600|PT+Serif:400,400italic' rel='stylesheet' type='text/css'>
2323

2424
<!-- Styles -->
25-
<link rel="stylesheet" href="/static/css/styles-basic.css" id="theme-styles">
25+
<link rel="stylesheet" href="/static/css/hackertheme-tales-bluered.css" id="theme-styles">
2626

2727
<!--[if lt IE 9]>
2828
<link rel="stylesheet" href="css/ie8.css">

0 commit comments

Comments
 (0)
Please sign in to comment.