{{ _("Tags") }}
8 |-
10 | {%- for tag in tags %}
11 |
- {{ tag.name }} 12 | {%- endfor %} 13 |
├── pypress ├── logs │ ├── debug.log │ └── error.log ├── templates │ ├── blog │ │ ├── comment.html │ │ ├── _postnow.html │ │ ├── _archive.html │ │ ├── _search.html │ │ ├── _tags.html │ │ ├── _comment.html │ │ ├── _links.html │ │ ├── tags.html │ │ ├── archive.html │ │ ├── template_edit.html │ │ ├── search_result.html │ │ ├── about.html │ │ ├── links.html │ │ ├── add_link.html │ │ ├── add_comment.html │ │ ├── list.html │ │ ├── people.html │ │ ├── view.html │ │ └── submit.html │ ├── errors │ │ ├── 500.html │ │ ├── 404.html │ │ └── 403.html │ ├── macros │ │ ├── _forms.html │ │ ├── _page.html │ │ ├── _twitter.html │ │ └── _post.html │ └── account │ │ ├── login.html │ │ └── signup.html ├── static │ ├── favicon.ico │ └── js │ │ ├── public.js │ │ └── jquery.idTabs.min.js ├── models │ ├── __init__.py │ ├── types.py │ ├── users.py │ └── blog.py ├── themes │ └── default │ │ ├── static │ │ ├── comment.gif │ │ ├── bg_button.png │ │ ├── feed-14x14.png │ │ ├── highlight.css │ │ └── style.css │ │ ├── info.json │ │ └── templates │ │ └── layout.html ├── translations │ └── zh │ │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── views │ ├── __init__.py │ ├── comment.py │ ├── feeds.py │ ├── link.py │ ├── post.py │ ├── account.py │ └── frontend.py ├── forms │ ├── __init__.py │ ├── validators.py │ ├── blog.py │ └── account.py ├── signals.py ├── extensions.py ├── permissions.py ├── config.cfg ├── __init__.py └── helpers.py ├── babel.cfg ├── .gitignore ├── fcgi.py ├── requirements.txt ├── README.md ├── manage.py └── messages.pot /pypress/logs/debug.log: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pypress/logs/error.log: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pypress/templates/blog/comment.html: -------------------------------------------------------------------------------- 1 | Your comment javascript code... 2 | -------------------------------------------------------------------------------- /pypress/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UlricQin/pypress/master/pypress/static/favicon.ico -------------------------------------------------------------------------------- /babel.cfg: -------------------------------------------------------------------------------- 1 | [python: **.py] 2 | [jinja2: **/templates/**.html] 3 | extensions=jinja2.ext.autoescape,jinja2.ext.with_ 4 | -------------------------------------------------------------------------------- /pypress/models/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python from .users import User, UserCode, Twitter from .blog import Post, Tag, Comment, Link -------------------------------------------------------------------------------- /pypress/themes/default/static/comment.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UlricQin/pypress/master/pypress/themes/default/static/comment.gif -------------------------------------------------------------------------------- /pypress/themes/default/static/bg_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UlricQin/pypress/master/pypress/themes/default/static/bg_button.png -------------------------------------------------------------------------------- /pypress/themes/default/static/feed-14x14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UlricQin/pypress/master/pypress/themes/default/static/feed-14x14.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *.pyo 4 | *.egg-info 5 | *.bak 6 | *.sql 7 | *.pid 8 | *.db 9 | *.swp 10 | # virtualenv 11 | env 12 | -------------------------------------------------------------------------------- /pypress/translations/zh/LC_MESSAGES/messages.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UlricQin/pypress/master/pypress/translations/zh/LC_MESSAGES/messages.mo -------------------------------------------------------------------------------- /pypress/views/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python from .frontend import frontend from .post import post from .account import account from .comment import comment from .link import link from .feeds import feeds -------------------------------------------------------------------------------- /fcgi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pypress import create_app 3 | 4 | app = create_app('config.cfg') 5 | 6 | from flup.server.fcgi import WSGIServer 7 | WSGIServer(app,bindAddress='/tmp/pypress.sock').run() 8 | -------------------------------------------------------------------------------- /pypress/templates/blog/_postnow.html: -------------------------------------------------------------------------------- 1 | {% if g.user %} 2 |
3 | {% endif %} 4 | -------------------------------------------------------------------------------- /pypress/forms/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python from .account import LoginForm, SignupForm, RecoverPasswordForm, \ ChangePasswordForm, DeleteAccountForm, TwitterForm from .blog import PostForm, CommentForm, LinkForm, TemplateForm -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | Flask-OAuth 3 | Flask-Cache 4 | Flask-SQLAlchemy 5 | Flask-Principal 6 | Flask-WTF 7 | Flask-Mail 8 | Flask-Script 9 | Flask-Babel 10 | Flask-Themes 11 | Flask-Uploads 12 | pygments 13 | markdown 14 | blinker 15 | -------------------------------------------------------------------------------- /pypress/signals.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding=utf-8 3 | 4 | from blinker import Namespace 5 | 6 | signals = Namespace() 7 | 8 | comment_added = signals.signal("comment-added") 9 | comment_deleted = signals.signal("comment-deleted") 10 | 11 | -------------------------------------------------------------------------------- /pypress/themes/default/info.json: -------------------------------------------------------------------------------- 1 | { 2 | "application": "pypress", 3 | "identifier": "default", 4 | "name": "default", 5 | "author": "LaoQiu", 6 | "description": "theme for pypress", 7 | "license": "MIT/X11", 8 | "doctype": "html5" 9 | } 10 | 11 | -------------------------------------------------------------------------------- /pypress/templates/errors/500.html: -------------------------------------------------------------------------------- 1 | {% extends theme("layout.html") %} 2 | 3 | {% block content %} 4 |7 |10 |The requested page,
8 |an error has occurred —
9 |
7 |11 |The requested page,
8 |cannot be found —
9 |We're sorry.
10 |
{{ _("Maybe you can try search:") }}
13 | 16 |7 |11 |The requested page,
8 |cannot be allowed —
9 |Your permission is not enough
10 |
{{ _("Maybe we can help you out though:") }}
13 |{{ _('Not a member yet ? Sign up !') }}
32 | {%- endblock %} 33 | {%- block sidebar %}{%- endblock %} 34 | -------------------------------------------------------------------------------- /pypress/static/js/public.js: -------------------------------------------------------------------------------- 1 | var ajax_post = function(url, params, on_success){ 2 | var _callback = function(response){ 3 | if (response.success) { 4 | if (response.redirect_url){ 5 | window.location.href = response.redirect_url; 6 | } else if (response.reload){ 7 | window.location.reload(); 8 | } else if (on_success) { 9 | return on_success(response); 10 | } 11 | } else { 12 | return message(response.error, "error"); 13 | } 14 | } 15 | 16 | $.post(url, params, _callback, "json"); 17 | 18 | } 19 | 20 | var message = function(message, category){ 21 | $('ul#messages').html('我在2011年2月14日开始写这个blog系统, 基于flask.
8 |flask跟django, pylons, turboGears, uliweb, bottle, web.py等一样, 同为python的web框架之一. 虽然之前用过上面讲的大部分框架, 但最后选择了flask来写这个blog系统, 原因是它很灵活, 有一个比较稳定的核心(Werkzeug), 更新够快, 据说是作者精力比较旺盛. :)
9 |最后, 我希望能持续完善这个博客功能, 暂定名为pypress.
35 |{{ link.description }}
{% endif %} 13 | 14 | {%- if not link.passed and link.permissions.edit %} 15 | {{ _("pass") }} 16 | {%- endif %} 17 | {%- if link.permissions.delete %} 18 | {{ _("delete") }} 19 | 24 | {%- endif %} 25 | 26 |{{ _("Nobody's posted anything yet.") }}
32 | {%- endif %} 33 |{{ _("Joined in") }}:{{ people.date_joined }}
13 |{{ _("You has not posted anything yet.") }} {{ _("Submit") }}
48 | {%- else %} 49 |{{ _("%(name)s has not posted anything yet.", name=people.nickname) }}
50 | {%- endif %} 51 | {%- endif %} 52 |{{ _('No comments have been posted yet.') }}
72 | {%- endif %} 73 |(?P[\w\W]+?)')
68 | _lang_re = re.compile(r'l=[\'"]?(?P)', self.content) 202 | if not s: 203 | return self.content 204 | p, more_id = s[0] 205 | addlink = '
' % (self.url, more_id, _("Read more...")) 206 | return self.content.split(p)[0] + addlink 207 | 208 | @cached_property 209 | def comments(self): 210 | """ 211 | Returns comments in tree. Each parent comment has a "comments" 212 | attribute appended and a "depth" attribute. 213 | """ 214 | comments = Comment.query.filter(Comment.post_id==self.id).all() 215 | 216 | def _get_comments(parent, depth): 217 | 218 | parent.comments = [] 219 | parent.depth = depth 220 | 221 | for comment in comments: 222 | if comment.parent_id == parent.id: 223 | parent.comments.append(comment) 224 | _get_comments(comment, depth + 1) 225 | 226 | parents = [c for c in comments if c.parent_id is None] 227 | 228 | for parent in parents: 229 | _get_comments(parent, 0) 230 | 231 | return parents 232 | 233 | @cached_property 234 | def json(self): 235 | """ 236 | Returns dict of safe attributes for passing into 237 | a JSON request. 238 | """ 239 | 240 | return dict(id=self.id, 241 | title=self.title, 242 | content=self.content, 243 | author=self.author.username) 244 | 245 | def _url(self, _external=False): 246 | return url_for('frontend.post', 247 | year=self.created_date.year, 248 | month=self.created_date.month, 249 | day=self.created_date.day, 250 | slug=self.slug, 251 | _external=_external) 252 | 253 | @cached_property 254 | def url(self): 255 | return self._url() 256 | 257 | @cached_property 258 | def permalink(self): 259 | return self._url(True) 260 | 261 | 262 | post_tags = db.Table("post_tags", db.Model.metadata, 263 | db.Column("post_id", db.Integer, 264 | db.ForeignKey('posts.id', ondelete='CASCADE'), 265 | primary_key=True), 266 | db.Column("tag_id", db.Integer, 267 | db.ForeignKey('tags.id', ondelete='CASCADE'), 268 | primary_key=True)) 269 | 270 | 271 | class TagQuery(BaseQuery): 272 | 273 | def cloud(self): 274 | 275 | tags = self.filter(Tag.num_posts > 0).all() 276 | 277 | if not tags: 278 | return [] 279 | 280 | max_posts = max(t.num_posts for t in tags) 281 | min_posts = min(t.num_posts for t in tags) 282 | 283 | diff = (max_posts - min_posts) / 10.0 284 | if diff < 0.1: 285 | diff = 0.1 286 | 287 | for tag in tags: 288 | tag.size = int(tag.num_posts / diff) 289 | if tag.size < 1: 290 | tag.size = 1 291 | 292 | random.shuffle(tags) 293 | 294 | return tags 295 | 296 | 297 | class Tag(db.Model): 298 | 299 | __tablename__ = "tags" 300 | 301 | query_class = TagQuery 302 | 303 | id = db.Column(db.Integer, primary_key=True) 304 | slug = db.Column(db.Unicode(80), unique=True) 305 | posts = db.dynamic_loader(Post, secondary=post_tags, query_class=PostQuery) 306 | 307 | _name = db.Column("name", db.Unicode(80), unique=True) 308 | 309 | def __init__(self, *args, **kwargs): 310 | super(Tag, self).__init__(*args, **kwargs) 311 | 312 | def __str__(self): 313 | return self.name 314 | 315 | def _get_name(self): 316 | return self._name 317 | 318 | def _set_name(self, name): 319 | self._name = name.lower().strip() 320 | self.slug = slugify(name) 321 | 322 | name = db.synonym("_name", descriptor=property(_get_name, _set_name)) 323 | 324 | @cached_property 325 | def url(self): 326 | return url_for("frontend.tag", slug=self.slug) 327 | 328 | num_posts = db.column_property( 329 | db.select([db.func.count(post_tags.c.post_id)]).\ 330 | where(db.and_(post_tags.c.tag_id==id, 331 | Post.id==post_tags.c.post_id)).as_scalar()) 332 | 333 | 334 | class Comment(db.Model): 335 | 336 | __tablename__ = "comments" 337 | 338 | PER_PAGE = 40 339 | 340 | id = db.Column(db.Integer, primary_key=True) 341 | 342 | post_id = db.Column(db.Integer, 343 | db.ForeignKey(Post.id, ondelete='CASCADE'), 344 | nullable=False) 345 | 346 | author_id = db.Column(db.Integer, 347 | db.ForeignKey(User.id, ondelete='CASCADE')) 348 | 349 | parent_id = db.Column(db.Integer, 350 | db.ForeignKey("comments.id", ondelete='CASCADE')) 351 | 352 | email = db.Column(db.String(50)) 353 | nickname = db.Column(db.Unicode(50)) 354 | website = db.Column(db.String(100)) 355 | 356 | comment = db.Column(db.UnicodeText) 357 | created_date = db.Column(db.DateTime, default=datetime.utcnow) 358 | 359 | ip = db.Column(db.Integer) 360 | 361 | _author = db.relation(User, backref="posts", lazy="joined") 362 | 363 | post = db.relation(Post, innerjoin=True, lazy="joined") 364 | 365 | parent = db.relation('Comment', remote_side=[id]) 366 | 367 | __mapper_args__ = {'order_by' : id.asc()} 368 | 369 | class Permissions(object): 370 | 371 | def __init__(self, obj): 372 | self.obj = obj 373 | 374 | @cached_property 375 | def reply(self): 376 | return Permission(UserNeed(self.obj.post.author_id)) 377 | 378 | @cached_property 379 | def delete(self): 380 | return Permission(UserNeed(self.obj.author_id), 381 | UserNeed(self.obj.post.author_id)) & moderator 382 | 383 | def __init__(self, *args, **kwargs): 384 | super(Comment, self).__init__(*args, **kwargs) 385 | 386 | @cached_property 387 | def permissions(self): 388 | return self.Permissions(self) 389 | 390 | def _get_author(self): 391 | if self._author: 392 | return self._author 393 | return storage(email = self.email, 394 | nickname = self.nickname, 395 | website = self.website) 396 | 397 | def _set_author(self, author): 398 | self._author = author 399 | 400 | author = db.synonym("_author", descriptor=property(_get_author, _set_author)) 401 | 402 | def _url(self, _external=False): 403 | return '%s#comment-%d' % (self.post._url(_external), self.id) 404 | 405 | @cached_property 406 | def url(self): 407 | return self._url() 408 | 409 | @cached_property 410 | def permalink(self): 411 | return self._url(True) 412 | 413 | @cached_property 414 | def markdown(self): 415 | return Markup(markdown(self.comment or '')) 416 | 417 | 418 | class Link(db.Model): 419 | 420 | __tablename__ = "links" 421 | 422 | PER_PAGE = 80 423 | 424 | id = db.Column(db.Integer, primary_key=True) 425 | name = db.Column(db.Unicode(50), nullable=False) 426 | link = db.Column(db.String(100), nullable=False) 427 | logo = db.Column(db.String(100)) 428 | description = db.Column(db.Unicode(100)) 429 | email = db.Column(db.String(50)) 430 | passed = db.Column(db.Boolean, default=False) 431 | created_date = db.Column(db.DateTime, default=datetime.utcnow) 432 | 433 | class Permissions(object): 434 | 435 | def __init__(self, obj): 436 | self.obj = obj 437 | 438 | @cached_property 439 | def edit(self): 440 | return moderator 441 | 442 | @cached_property 443 | def delete(self): 444 | return moderator 445 | 446 | def __init__(self, *args, **kwargs): 447 | super(Link, self).__init__(*args, **kwargs) 448 | 449 | @cached_property 450 | def permissions(self): 451 | return self.Permissions(self) 452 | 453 | def __str__(self): 454 | return self.name 455 | 456 | # ------------- SIGNALS ----------------# 457 | 458 | def update_num_comments(sender): 459 | sender.num_comments = \ 460 | Comment.query.filter(Comment.post_id==sender.id).count() 461 | db.session.commit() 462 | 463 | 464 | signals.comment_added.connect(update_num_comments) 465 | signals.comment_deleted.connect(update_num_comments) 466 | 467 | -------------------------------------------------------------------------------- /pypress/translations/zh/LC_MESSAGES/messages.po: -------------------------------------------------------------------------------- 1 | # Chinese translations for PROJECT. 2 | # Copyright (C) 2011 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR
47 | {% for child_comment in comment.comments %} 48 | {{ render_comment(child_comment) }} 49 | {% endfor %} 50 |
51 |