├── README
├── app.yaml
├── config
├── css
└── style.css
├── index.yaml
├── main.py
└── templates
├── base.html
├── error.html
├── form.html
├── index.html
├── rss.xml
└── upload.html
/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realbasic/python-blog-system/4ae0ae25cbe41a711fad50bccbfd469550693081/README
--------------------------------------------------------------------------------
/app.yaml:
--------------------------------------------------------------------------------
1 | application: python-blog-system
2 | version: 1
3 | runtime: python
4 | api_version: 1
5 |
6 | handlers:
7 | - url: /css/(.*\.css)
8 | static_files: css/\1
9 | upload: css/(.*\.css)
10 |
11 | - url: .*
12 | script: main.py
13 |
14 |
--------------------------------------------------------------------------------
/config:
--------------------------------------------------------------------------------
1 | [default]
2 | step=10
3 |
4 | [log]
5 | google_analytics=UA-XXXXXXXX-X
6 |
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | div.entry{margin-bottom: 10px;}
2 | div.entryHeader{width:400px;}
3 | div.entry{width:400px;}
4 | div.entryDate{
5 | font-size:small;
6 | text-align:right;
7 | margin-bottom:2px;
8 | }
9 | div.entryFooter{
10 | margin-top:5px;
11 | font-size:small;
12 | }
13 | div.entryDate{
14 | font-size: x-small;
15 | }
16 | div.body{
17 | font-size:small;
18 | margin-left:5px;
19 | }
20 | h1{
21 | padding:5px 0 5px 5px;
22 | border-left:#003399 8px solid;
23 | border-bottom:#003399 1px solid;
24 | font-weight: bold;
25 | color:#333;
26 | width:400px;
27 | margin-bottom:0;
28 | }
29 | h2{
30 | padding:5px 0 5px 5px;
31 | border-left:#009900 8px solid;
32 | border-bottom:#009900 1px solid;
33 | font-size: small;
34 | font-weight: bold;
35 | color:#333;
36 | margin-bottom:0;
37 | width:390px;
38 | }
39 | h3{
40 | padding:5px 0 5px 5px;
41 | border-left:#99cc00 3px solid;
42 | border-bottom:#009900 1px solid;
43 | font-size: smaller;
44 | color:#333;
45 | margin-bottom:0;
46 | }
47 | .title a{text-decoration: none;}
48 | .delcommentbutton{
49 | font-size: xx-small;
50 | }
51 | .delcommentpassword{
52 | font-size: xx-small;
53 | }
--------------------------------------------------------------------------------
/index.yaml:
--------------------------------------------------------------------------------
1 | indexes:
2 |
3 | # AUTOGENERATED
4 |
5 | # This index.yaml is automatically updated whenever the dev_appserver
6 | # detects that a new type of query is run. If you want to manage the
7 | # index.yaml file manually, remove the above marker line (the line
8 | # saying "# AUTOGENERATED"). If you want to manage some indexes
9 | # manually, move them above the marker line. The index.yaml file is
10 | # automatically uploaded to the admin console when you next deploy
11 | # your application using appcfg.py.
12 |
13 | - kind: Comment
14 | properties:
15 | - name: entry
16 | - name: datetime
17 |
18 | - kind: Entry
19 | properties:
20 | - name: public
21 | - name: datetime
22 |
23 | - kind: Entry
24 | properties:
25 | - name: public
26 | - name: datetime
27 | direction: desc
28 |
29 | - kind: Entry
30 | properties:
31 | - name: public
32 | - name: tags
33 | - name: datetime
34 | direction: desc
35 |
36 | - kind: Entry
37 | properties:
38 | - name: tags
39 | - name: datetime
40 | direction: desc
41 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from google.appengine.ext import webapp, db
4 | from google.appengine.dist import use_library
5 | use_library('django', '1.2')
6 | from google.appengine.ext.webapp import util, template
7 | from google.appengine.api import users, memcache
8 | import urllib, datetime, re, os
9 | from ConfigParser import ConfigParser
10 | config = os.path.join(os.path.dirname(__file__), "config")
11 | default = {"step" : "10",
12 | "analytics" : "UA-XXXXXXXX-X"}
13 | parser = ConfigParser(default)
14 | f_in = open(config, "r")
15 | parser.readfp(f_in)
16 | f_in.close()
17 | step = parser.getint("default","step")
18 | google_analytics = parser.get("log", "google_analytics")
19 |
20 | class AuthHandler(webapp.RequestHandler):
21 | def get(self, key = ""):
22 | if users.get_current_user() == None:
23 | self.response.out.write("Sign in or register." % users.create_login_url("/admin"))
24 | elif users.is_current_user_admin() != True:
25 | self.response.out.write('Your account %s is not admin. Log out and log in with an admin account.' % (users.get_current_user(), users.create_logout_url("/admin")))
26 | else:
27 | if key:
28 | self.get2(key)
29 | else:
30 | self.get2()
31 | def post(self, key = ""):
32 | if users.is_current_user_admin():
33 | if key:
34 | self.post2(key)
35 | else:
36 | self.post2()
37 |
38 | class MainHandler(AuthHandler):
39 | def get(self, pageStr):
40 | page = parseInt(pageStr)
41 | entries = filter_entries(Entry.all().order("-datetime")).fetch(step + 1, page * step)
42 | params = {'entries': entries[:step]}
43 | if len(entries) > step:
44 | params['next'] = page + 1
45 | if page > 0:
46 | params['prev'] = page - 1
47 | print_with_template(self, 'index.html', params)
48 |
49 | class RSSHandler(AuthHandler):
50 | def get(self, pageStr):
51 | print_with_template(self, 'rss.xml', {'entries':filter_entries(Entry.all().order("-datetime")).fetch(30)})
52 |
53 | class AdminHandler(AuthHandler):
54 | def get2(self):
55 | self.redirect("/")
56 |
57 | class PostHandler(AuthHandler):
58 | def get2(self,key = ""):
59 | entry = Entry.get(key) if key != '' else Entry()
60 | print_with_template(self, 'form.html',{'entry':entry, 'key':key})
61 | def post2(self, key = ""):
62 | if self.request.get("title") != '' and self.request.get("body") != '':
63 | entry = Entry.get(key) if key != '' else Entry()
64 | entry.title = self.request.get("title")
65 | entry.body = self.request.get("body")
66 | entry.datetime = datetime.datetime.strptime(self.request.get("datetime"), "%Y-%m-%d %H:%M:%S") - datetime.timedelta(hours=9)
67 | entry.public = self.request.get("public") == "1"
68 | entry.tags = []
69 | for tagStr in self.request.get('tags').replace(u' ',' ').replace(' ',' ').replace(',',' ').split(' '):
70 | tag = Tag.all().filter("tag =", tagStr).get()
71 | if tag == None:
72 | tag = Tag(tag = tagStr)
73 | tag.put()
74 | entry.tags.append(tag.key())
75 | entry.put()
76 | self.redirect('/')
77 |
78 | class PostCommentHandler(AuthHandler):
79 | def post(self, key):
80 | if self.request.get("comment") != '':
81 | Comment(
82 | entry = Entry.get(key),
83 | comment = self.request.get("comment"),
84 | delpass = self.request.get("delpass"),
85 | nickname = self.request.get("nickname")
86 | ).put()
87 | self.redirect("/entry/%s" % key)
88 |
89 | class DeleteHandler(AuthHandler):
90 | def get2(self, key):
91 | db.delete(Entry.get(key))
92 | self.redirect('/')
93 |
94 | class DeleteCommentHandler(AuthHandler):
95 | def post(self, key):
96 | comment = Comment.get(key)
97 | entry_key = comment.entry.key()
98 | if self.request.get("delpass") == comment.delpass:
99 | db.delete(comment)
100 | self.redirect('/entry/%s' % entry_key)
101 |
102 | class TagHandler(AuthHandler):
103 | def get(self, key, pageStr):
104 | page = parseInt(pageStr)
105 | tagStr = urllib.unquote(key).decode('utf-8')
106 | tag = Tag.all().filter("tag =", tagStr).get()
107 | if tag:
108 | entries = filter_entries(tag.entries.order("-datetime")).fetch(step + 1, page * step)
109 | params = {'entries': entries[:step]}
110 | if len(entries) > step:
111 | params['next'] = page + 1
112 | if page > 0:
113 | params['prev'] = page - 1
114 | params['tag'] = tagStr
115 | print_with_template(self, 'index.html', params)
116 | else:
117 | print_with_template(self, 'error.html', {'message':"Tag %s does not exist" % h(tagStr)})
118 |
119 | class EntryHandler(AuthHandler):
120 | def get(self, key):
121 | entry = Entry.get(key);
122 | if users.is_current_user_admin() or (entry.datetime > datetime.datetime.now() and entry.public):
123 | print_with_template(self, 'index.html', {'entries':[Entry.get(key)], 'detail':True})
124 | else:
125 | self.redirect("/")
126 |
127 | class UploaderHandler(AuthHandler):
128 | def get2(self, key):
129 | print_with_template(self, 'upload.html', {'images': Entry.get(key).images, 'key': key})
130 | def post2(self, key):
131 | if self.request.get('file'):
132 | image = Image()
133 | image.image = self.request.POST.get('file').file.read()
134 | image.contentType = self.request.body_file.vars['file'].headers['content-type']
135 | image.entry = Entry.get(key)
136 | image.put()
137 | self.redirect('/uploader/' + key)
138 |
139 | class DeleteImageHandler(AuthHandler):
140 | def get2(self, key):
141 | Image.get(key).delete()
142 | self.redirect('/uploader')
143 |
144 | class ImageHandler(AuthHandler):
145 | def get(self, key):
146 | image = quickGet(key)
147 | self.response.headers['Content-Type'] = image.contentType.encode('utf-8')
148 | self.response.out.write(image.image)
149 |
150 | ## Functions
151 | def quickGet(key):
152 | data = memcache.get(key)
153 | if data == None:
154 | data = db.get(key)
155 | memcache.set(key = key, value = data, time=3600)
156 | return data
157 |
158 | def apply_filters(str):
159 | str = h(str)
160 | str = nl2br(str)
161 | str = linkURLs(str)
162 | str = replaceStrongs(str)
163 | str = replaceImages(str)
164 | str = replaceLists(str)
165 | return str
166 |
167 | def urlReplacer(match, limit = 45):
168 | return '%s' % (match.group(), match.group()[:limit] + ('...' if len(match.group()) > limit else ''))
169 |
170 | def linkURLs(str):
171 | return re.sub(r'([^"]|^)(https?|ftp)(://[\w:;/.?%#&=+-]+)', urlReplacer, str)
172 |
173 | def replaceImages(str):
174 | return re.sub(r'\[img:(.*)\]', r'', str)
175 |
176 | def replaceStrongs(str):
177 | str = re.sub(r'\[s(trong)?\]', r'', str)
178 | str = re.sub(r'\[/s(trong)?\]', r'', str)
179 | return str
180 |
181 | def replaceLists(str):
182 | return re.sub(re.compile('^-(.+)$', re.MULTILINE), r'
{{comment.nickname}}: {{comment.comment}} 60 |
61 |
65 |
67 | {%endfor%} 68 |66 |
72 |