Permalink
Browse files

New feature: favorite/unfavorite nodes.

  • Loading branch information...
1 parent 358c6ff commit 71342cffed7a398674a45de02b637a4775321c41 @livid committed Nov 4, 2010
Showing with 307 additions and 8 deletions.
  1. +9 −0 app.yaml.example
  2. +90 −0 favorite.py
  3. +6 −0 index.yaml
  4. +2 −0 main.py
  5. +72 −0 my.py
  6. +17 −5 static/css/desktop/style.css
  7. +21 −1 template.py
  8. +55 −0 tpl/desktop/my_nodes.html
  9. +1 −1 tpl/desktop/node.html
  10. +8 −0 tpl/desktop/rightbar/user.html
  11. +26 −1 v2ex/babel/__init__.py
View
@@ -135,6 +135,15 @@ handlers:
- url: /webluker-verif.html
script: misc.py
+- url: /favorite/(.*)
+ script: favorite.py
+
+- url: /unfavorite/(.*)
+ script: favorite.py
+
+- url: /my/(.*)
+ script: my.py
+
- url: .*
script: main.py
View
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+import os
+import re
+import time
+import datetime
+import hashlib
+import string
+import random
+
+from google.appengine.ext import webapp
+from google.appengine.api import memcache
+from google.appengine.ext import db
+from google.appengine.ext.webapp import util
+from google.appengine.ext.webapp import template
+
+from v2ex.babel import Member
+from v2ex.babel import Counter
+from v2ex.babel import Section
+from v2ex.babel import Node
+from v2ex.babel import Topic
+from v2ex.babel import Reply
+from v2ex.babel import Note
+from v2ex.babel import NodeBookmark
+
+from v2ex.babel import SYSTEM_VERSION
+
+from v2ex.babel.security import *
+from v2ex.babel.ua import *
+from v2ex.babel.da import *
+from v2ex.babel.ext.cookies import Cookies
+
+template.register_template_library('v2ex.templatetags.filters')
+
+class FavoriteNodeHandler(webapp.RequestHandler):
+ def get(self, node_name):
+ self.response.out.write('FUCK')
+ if 'Referer' in self.request.headers:
+ go = self.request.headers['Referer']
+ else:
+ go = '/'
+ member = CheckAuth(self)
+ if member:
+ node = GetKindByName('Node', node_name)
+ if node is not False:
+ q = db.GqlQuery("SELECT * FROM NodeBookmark WHERE node = :1 AND member = :2", node, member)
+ if q.count() == 0:
+ bookmark = NodeBookmark(parent=member)
+ bookmark.node = node
+ bookmark.member = member
+ bookmark.put()
+ member.favorited_nodes = member.favorited_nodes + 1
+ member.put()
+ n = 'r/n' + str(node.num) + '/m' + str(member.num)
+ memcache.set(n, True, 86400 * 14)
+ self.redirect(go)
+
+class UnfavoriteNodeHandler(webapp.RequestHandler):
+ def get(self, node_name):
+ self.response.out.write('FUCK')
+ if 'Referer' in self.request.headers:
+ go = self.request.headers['Referer']
+ else:
+ go = '/'
+ member = CheckAuth(self)
+ if member:
+ node = GetKindByName('Node', node_name)
+ if node is not False:
+ q = db.GqlQuery("SELECT * FROM NodeBookmark WHERE node = :1 AND member = :2", node, member)
+ if q.count() > 0:
+ bookmark = q[0]
+ bookmark.delete()
+ member.favorited_nodes = member.favorited_nodes - 1
+ member.put()
+ n = 'r/n' + str(node.num) + '/m' + str(member.num)
+ memcache.delete(n)
+ self.redirect(go)
+
+def main():
+ application = webapp.WSGIApplication([
+ ('/favorite/node/([a-zA-Z0-9]+)', FavoriteNodeHandler),
+ ('/unfavorite/node/([a-zA-Z0-9]+)', UnfavoriteNodeHandler)
+ ],
+ debug=True)
+ util.run_wsgi_app(application)
+
+
+if __name__ == '__main__':
+ main()
View
@@ -47,6 +47,12 @@ indexes:
- name: topics
direction: desc
+- kind: NodeBookmark
+ properties:
+ - name: member
+ - name: created
+ direction: desc
+
- kind: Note
properties:
- name: member
View
@@ -690,6 +690,8 @@ def get(self, node_name):
if node:
template_values['canonical'] = 'http://' + site.domain + '/go/' + node.name
if member:
+ favorited = member.hasFavorited(node)
+ template_values['favorited'] = favorited
recent_nodes = memcache.get('member::' + str(member.num) + '::recent_nodes')
recent_nodes_ids = memcache.get('member::' + str(member.num) + '::recent_nodes_ids')
if recent_nodes and recent_nodes_ids:
View
72 my.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+import os
+import re
+import time
+import datetime
+import hashlib
+import string
+import random
+
+from google.appengine.ext import webapp
+from google.appengine.api import memcache
+from google.appengine.ext import db
+from google.appengine.ext.webapp import util
+from google.appengine.ext.webapp import template
+
+from v2ex.babel import Member
+from v2ex.babel import Counter
+from v2ex.babel import Section
+from v2ex.babel import Node
+from v2ex.babel import Topic
+from v2ex.babel import Reply
+from v2ex.babel import Note
+
+from v2ex.babel import SYSTEM_VERSION
+
+from v2ex.babel.security import *
+from v2ex.babel.ua import *
+from v2ex.babel.da import *
+from v2ex.babel.l10n import *
+from v2ex.babel.ext.cookies import Cookies
+
+template.register_template_library('v2ex.templatetags.filters')
+
+class MyNodesHandler(webapp.RequestHandler):
+ def get(self):
+ member = CheckAuth(self)
+ if member:
+ site = GetSite()
+ l10n = GetMessages(self, member, site)
+ template_values = {}
+ template_values['site'] = site
+ template_values['member'] = member
+ template_values['l10n'] = l10n
+ template_values['page_title'] = site.title + u' › 我收藏的节点'
+ template_values['rnd'] = random.randrange(1, 100)
+ if member.favorited_nodes > 0:
+ template_values['has_nodes'] = True
+ q = db.GqlQuery("SELECT * FROM NodeBookmark WHERE member = :1 ORDER BY created DESC LIMIT 0,10", member)
+ template_values['column_1'] = q
+ if member.favorited_nodes > 10:
+ q2 = db.GqlQuery("SELECT * FROM NodeBookmark WHERE member = :1 ORDER BY created DESC LIMIT 10,10", member)
+ template_values['column_2'] = q2
+ else:
+ template_values['has_nodes'] = False
+ path = os.path.join(os.path.dirname(__file__), 'tpl', 'desktop', 'my_nodes.html')
+ output = template.render(path, template_values)
+ self.response.out.write(output)
+ else:
+ self.redirect('/')
+
+def main():
+ application = webapp.WSGIApplication([
+ ('/my/nodes', MyNodesHandler)
+ ],
+ debug=True)
+ util.run_wsgi_app(application)
+
+
+if __name__ == '__main__':
+ main()
@@ -22,15 +22,15 @@ body {
h1 {
font-size: 22px;
- line-height: 22px;
+ line-height: 25px;
font-weight: bold;
padding: 10px 0px 10px 0px;
margin: 0px;
}
h2 {
font-size: 18px;
- line-height: 18px;
+ line-height: 20px;
font-weight: bold;
padding: 10px 0px 10px 0px;
margin: 0px;
@@ -95,7 +95,7 @@ a.dark:link, a.dark:visited, a.dark:active {
a.dark:hover {
color: #385f8a;
- text-decoration: underline;
+ text-decoration: none;
}
/* IDs */
@@ -203,8 +203,8 @@ a.dark:hover {
background-color: #fff;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
- -moz-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.15);
- -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: 0px 1px 5px rgba(0, 0, 18, 0.4);
+ -webkit-box-shadow: 0px 1px 5px rgba(0, 0, 18, 0.4);
border-top: 1px solid #cccccf;
border-bottom: 2px solid #e2e2e2;
}
@@ -774,4 +774,16 @@ a.op:hover {
.topic_content .imgly {
max-width: 635px;
+}
+
+.howmany {
+ display: inline-block;
+ font-size: 14px;
+ line-height: 14px;
+ padding: 3px 5px 3px 5px;
+ background-color: #f5f5ff;
+ border: 1px solid #f0f0ff;
+ color: #99a;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
}
View
@@ -27,13 +27,33 @@
from v2ex.babel.security import *
from v2ex.babel.ua import *
+from v2ex.babel.da import *
+from v2ex.babel.l10n import *
from v2ex.babel.ext.cookies import Cookies
template.register_template_library('v2ex.templatetags.filters')
+class MyNodesHandler(webapp.RequestHandler):
+ def get(self):
+ member = CheckAuth(self)
+ if member:
+ site = GetSite()
+ l10n = GetMessages(self, member, site)
+ template_values = {}
+ template_values['site'] = site
+ template_values['member'] = member
+ template_values['l10n'] = l10n
+ template_values['page_title'] = site.title + u' › 我收藏的节点'
+ template_values['rnd'] = random.randrange(1, 100)
+ path = os.path.join(os.path.dirname(__file__), 'tpl', 'desktop', 'my_nodes.html')
+ output = template.render(path, template_values)
+ self.response.out.write(output)
+ else:
+ self.redirect('/')
+
def main():
application = webapp.WSGIApplication([
- ('/', HomeHandler)
+ ('/my/nodes', MyNodesHandler)
],
debug=True)
util.run_wsgi_app(application)
View
@@ -0,0 +1,55 @@
+{% include 'common/head.html' %}
+<body>
+ {% include 'common/top.html' %}
+ <div id="Wrapper">
+ <div id="Main">
+ <div id="Sidebar">
+ </div>
+ <div id="Rightbar">
+ {% include 'rightbar/user.html' %}
+ <div class="sep20"></div>
+ {% include 'rightbar/ads.html' %}
+ </div>
+ <div id="Content">
+ <div class="box">
+ <div class="cell"><span class="bigger"><a href="/">{{ site.title }}</a> {{ l10n.chevron }} 我收藏的节点</span></div>
+ <div class="inner">
+ {% if has_nodes %}
+ <table cellpadding="0" cellspacing="0" border="0" width="100%">
+ <tr>
+ <td width="50%" align="left" valign="top">
+ <table cellpadding="5" cellspacing="0" border="0" width="100%">
+ {% for bookmark in column_1 %}
+ <tr>
+ <td width="50" align="right" valign="middle"><div class="howmany">{{ bookmark.node.topics }}</div></td>
+ <td width="auto" align="left"><h2><a href="/go/{{ bookmark.node.name }}">{{ bookmark.node.title }}</a></h2></td>
+ </tr>
+ {% endfor %}
+ </table>
+ </td>
+ <td width="50%" align="left" valign="top">
+ <table cellpadding="5" cellspacing="0" border="0" width="100%">
+ {% for bookmark in column_2 %}
+ <tr>
+ <td width="50" align="right" valign="middle"><div class="howmany">{{ bookmark.node.topics }}</div></td>
+ <td width="auto" align="left"><h2><a href="/go/{{ bookmark.node.name }}">{{ bookmark.node.title }}</a></h2></td>
+ </tr>
+ {% endfor %}
+ </table>
+ </td>
+ </tr>
+ </table>
+ {% else %}
+ <div class="sep20"></div>
+ <div align="center"><h2 class="snow">目前你还没有收藏任何节点</h2></div>
+ <div class="sep20"></div>
+ {% endif %}
+ </div>
+ </div>
+ </div>
+ <div class="c"></div>
+ </div>
+ </div>
+ {% include 'common/bottom.html' %}
+</body>
+</html>
View
@@ -17,7 +17,7 @@
</div>
<div id="Content">
<div class="box">
- <div class="cell"><div class="fr"><span class="snow">{{ l10n.total_topics }}</span> <span class="fade"><strong>{{ node.topics }}</strong></span></div>
+ <div class="cell"><div class="fr" align="right"><span class="snow">{{ l10n.total_topics }}</span> <span class="fade"><strong>{{ node.topics }}</strong>{% if member %}<div class="sep3"></div>{% if favorited %}<a href="/unfavorite/node/{{ node.name }}">取消收藏</a>{% else %}<a href="/favorite/node/{{ node.name }}">加入收藏</a>{% endif %}{% endif %}</span></div>
<span class="bigger"><a href="/">{{ site.title }}</a> <span class="chevron">&nbsp;&nbsp;</span> {{ node.title|escape }}</span>
{% if node.header %}
<div class="sep10"></div>
@@ -15,6 +15,14 @@
</td>
</tr>
</table>
+ <div class="sep10"></div>
+ <table cellpadding="0" cellspacing="0" border="0" width="100%">
+ <tr>
+ <td width="33%" align="center"><a href="/my/nodes" class="dark" style="display: block;"><span class="bigger">{{ member.favorited_nodes }}</span><div class="sep3"></div><span class="fade">节点收藏</span></a></td>
+ <td width="34%" style="border-left: 1px solid #e2e2e2; border-right: 1px solid #e2e2e2;" align="center"></td>
+ <td width="33%" align="center"></td>
+ </tr>
+ </table>
{% if member.twitter_oauth %}
<div class="sep10"></div>
<form method="post" action="/twitter/tweet">
Oops, something went wrong.

0 comments on commit 71342cf

Please sign in to comment.