Browse code
Adding resharing
fiddlerwoaroof authored on 20/10/2015 01:28:21
Showing 8 changed files
Showing 8 changed files
- db/functions.sql
- db/schema.sql
- src/marrow/bone.py
- static/js/directives/bone-list/bone-list.html
- static/js/directives/bone-list/bone-list.js
- static/js/new/controller.js
- static/partials/random.html
- static/partials/subscription.html
... | ... |
@@ -14,6 +14,24 @@ END |
14 | 14 |
$$ LANGUAGE plpgsql; |
15 | 15 |
|
16 | 16 |
|
17 |
+DROP FUNCTION IF EXISTS subscribe_link(text,int); |
|
18 |
+CREATE OR REPLACE FUNCTION subscribe_link(username text, linkid int) |
|
19 |
+ RETURNS bool AS $$ |
|
20 |
+DECLARE |
|
21 |
+ uid int; |
|
22 |
+ result bool; |
|
23 |
+ n_title text; |
|
24 |
+ n_url text; |
|
25 |
+BEGIN |
|
26 |
+ SELECT INTO result NOT exists(SELECT * FROM user_links WHERE user_id=uid AND link_id=linkid); |
|
27 |
+ IF result THEN |
|
28 |
+ SELECT title,url INTO n_title,n_url FROM links WHERE links.id=linkid; |
|
29 |
+ PERFORM put_link(username, n_url, n_title); |
|
30 |
+ END IF; |
|
31 |
+ RETURN result; |
|
32 |
+END |
|
33 |
+$$ LANGUAGE plpgsql; |
|
34 |
+ |
|
17 | 35 |
DROP FUNCTION IF EXISTS delete_link(text,int); |
18 | 36 |
CREATE OR REPLACE FUNCTION delete_link(username text, linkid int) |
19 | 37 |
RETURNS bool AS $$ |
... | ... |
@@ -30,10 +48,25 @@ BEGIN |
30 | 48 |
END |
31 | 49 |
$$ LANGUAGE plpgsql; |
32 | 50 |
|
51 |
+DROP FUNCTION IF EXIST has_user_shared(text, text); |
|
52 |
+CREATE OR REPLACE FUNCTION has_user_shared(username text, linkurl text) RETURNS bool AS $$ |
|
53 |
+DECLARE |
|
54 |
+ result bool; |
|
55 |
+BEGIN |
|
56 |
+ SELECT INTO result EXISTS( |
|
57 |
+ SELECT 1 FROM user_links ul |
|
58 |
+ LEFT JOIN users u ON user_id=u.id |
|
59 |
+ LEFT JOIN links l ON link_id=l.id |
|
60 |
+ WHERE linkurl=l.url AND username=u.name |
|
61 |
+ ); |
|
62 |
+ RETURN result; |
|
63 |
+END |
|
64 |
+$$ LANGUAGE plpgsql; |
|
65 |
+ |
|
33 | 66 |
DROP FUNCTION IF EXISTS get_bones(text, timestamp, int); |
34 | 67 |
DROP FUNCTION IF EXISTS get_bones(text, int, timestamp); |
35 | 68 |
CREATE OR REPLACE FUNCTION get_bones(username text, lim int, before timestamp) |
36 |
- RETURNS TABLE(url text, title text, posted timestamp, poster text, total_votes bigint, subscriber_vote int) AS $$ |
|
69 |
+ RETURNS TABLE(linkid int, url text, title text, posted timestamp, poster text, total_votes bigint, subscriber_vote int, shared bool) AS $$ |
|
37 | 70 |
DECLARE |
38 | 71 |
subscriber_id int; |
39 | 72 |
BEGIN |
... | ... |
@@ -43,7 +76,9 @@ BEGIN |
43 | 76 |
AS |
44 | 77 |
SELECT |
45 | 78 |
DISTINCT ON (links.url) |
46 |
- links.url,links.title,links.posted,users1.name,total_votes(links.id),user_vote(subscriber_id,links.id) |
|
79 |
+ links.id,links.url,links.title,links.posted,users1.name, |
|
80 |
+ total_votes(links.id),user_vote(subscriber_id,links.id), |
|
81 |
+ has_user_shared(username, links.url) |
|
47 | 82 |
FROM user_subscriptions |
48 | 83 |
RIGHT JOIN user_links ON user_subscriptions.to_id=user_links.user_id |
49 | 84 |
INNER JOIN links ON link_id=links.id |
... | ... |
@@ -58,7 +93,8 @@ $$ LANGUAGE plpgsql; |
58 | 93 |
|
59 | 94 |
DROP FUNCTION IF EXISTS get_bones(text, int); |
60 | 95 |
CREATE OR REPLACE FUNCTION get_bones(username text, lim int) |
61 |
- RETURNS TABLE(url text, title text, posted timestamp, poster text, total_votes bigint, subscriber_vote int) AS $$ |
|
96 |
+ RETURNS TABLE(linkid int, url text, title text, posted timestamp, poster text, total_votes bigint, |
|
97 |
+ subscriber_vote int, shared bool) AS $$ |
|
62 | 98 |
DECLARE |
63 | 99 |
subscriber_id int; |
64 | 100 |
BEGIN |
... | ... |
@@ -68,7 +104,9 @@ BEGIN |
68 | 104 |
AS |
69 | 105 |
SELECT |
70 | 106 |
DISTINCT ON (links.url) |
71 |
- links.url,links.title,links.posted,users1.name,total_votes(links.id),user_vote(subscriber_id,links.id) |
|
107 |
+ links.id,links.url,links.title,links.posted,users1.name, |
|
108 |
+ total_votes(links.id),user_vote(subscriber_id,links.id), |
|
109 |
+ has_user_shared(username, links.url) |
|
72 | 110 |
FROM user_subscriptions |
73 | 111 |
RIGHT JOIN user_links ON user_subscriptions.to_id=user_links.user_id |
74 | 112 |
INNER JOIN links ON link_id=links.id |
... | ... |
@@ -81,7 +119,8 @@ $$ LANGUAGE plpgsql; |
81 | 119 |
|
82 | 120 |
DROP FUNCTION IF EXISTS get_bones(text); |
83 | 121 |
CREATE OR REPLACE FUNCTION get_bones(username text) |
84 |
- RETURNS TABLE(url text, title text, posted timestamp, poster text, total_votes bigint, subscriber_vote int) AS $$ |
|
122 |
+ RETURNS TABLE(linkid int, url text, title text, posted timestamp, poster text, total_votes bigint, |
|
123 |
+ subscriber_vote int, shared bool) AS $$ |
|
85 | 124 |
DECLARE |
86 | 125 |
subscriber_id int; |
87 | 126 |
BEGIN |
... | ... |
@@ -91,7 +130,9 @@ BEGIN |
91 | 130 |
AS |
92 | 131 |
SELECT |
93 | 132 |
DISTINCT ON (links.url) |
94 |
- links.url,links.title,links.posted,users1.name,total_votes(links.id),user_vote(subscriber_id,links.id) |
|
133 |
+ links.id,links.url,links.title,links.posted,users1.name, |
|
134 |
+ total_votes(links.id),user_vote(subscriber_id,links.id), |
|
135 |
+ has_user_shared(username, links.url) |
|
95 | 136 |
FROM user_subscriptions |
96 | 137 |
RIGHT JOIN user_links ON user_subscriptions.to_id=user_links.user_id |
97 | 138 |
INNER JOIN links ON link_id=links.id |
... | ... |
@@ -104,7 +145,7 @@ $$ LANGUAGE plpgsql; |
104 | 145 |
|
105 | 146 |
DROP FUNCTION IF EXISTS get_bone(text); |
106 | 147 |
CREATE OR REPLACE FUNCTION get_bone(username text) |
107 |
- RETURNS TABLE(name text, url text, title text, posted timestamp, linkid int, votes bigint) AS $$ |
|
148 |
+ RETURNS TABLE(name text, url text, title text, posted timestamp, linkid int, votes bigint, shared bool) AS $$ |
|
108 | 149 |
BEGIN |
109 | 150 |
RETURN QUERY SELECT users.name, links.url, links.title, links.posted, links.id, total_votes(links.id) |
110 | 151 |
FROM users |
... | ... |
@@ -66,4 +66,4 @@ WITH recent_users AS ( |
66 | 66 |
RIGHT JOIN users ON user_id=users.id |
67 | 67 |
WHERE posted > now() - interval '1 week' |
68 | 68 |
ORDER BY posted desc, user_id) |
69 |
-SELECT DISTINCT ON (name) user_id,name,posted FROM recent_users; |
|
69 |
+SELECT DISTINCT ON (user_id) user_id,name,posted FROM recent_users; |
... | ... |
@@ -1,5 +1,6 @@ |
1 | 1 |
import flask |
2 | 2 |
from flask import Blueprint, session, redirect, url_for, escape, request, abort, g |
3 |
+import flask_login; |
|
3 | 4 |
from flask.ext.cors import cross_origin |
4 | 5 |
from flask.ext.login import login_required, current_user |
5 | 6 |
import urllib2 |
... | ... |
@@ -31,7 +32,7 @@ def as_json(f): |
31 | 32 |
return res |
32 | 33 |
return _inner |
33 | 34 |
|
34 |
-@bone_blueprint.route('/link/<linkid>', methods=['GET','DELETE']) |
|
35 |
+@bone_blueprint.route('/link/<linkid>', methods=['GET','POST','DELETE']) |
|
35 | 36 |
@login_required |
36 | 37 |
def delete_link(linkid): |
37 | 38 |
db = database.get_db() |
... | ... |
@@ -42,6 +43,11 @@ def delete_link(linkid): |
42 | 43 |
cur.execute('SELECT id,url,title,posted FROM links WHERE id=%s', (linkid,)) |
43 | 44 |
nid,url,title,posted = cur.fetchone() |
44 | 45 |
result = dict(id=nid,url=url,title=title,posted=posted.isoformat()) |
46 |
+ elif request.method == 'POST': |
|
47 |
+ result = False |
|
48 |
+ if 'username' in session: |
|
49 |
+ cur.execute('SELECT subscribe_link(%s,%s)', (current_user.id,linkid)) |
|
50 |
+ result = cur.fetchone()[0] |
|
45 | 51 |
elif request.method == 'DELETE': |
46 | 52 |
result = False |
47 | 53 |
if 'username' in session: |
... | ... |
@@ -163,10 +169,14 @@ def data(username): |
163 | 169 |
|
164 | 170 |
result = {'marrow':[], 'sectionTitle': sectionTitle} |
165 | 171 |
with database.get_db().cursor() as cur: |
166 |
- cur.execute("SELECT url, title, posted, linkid, votes from get_bone(%s);", (username,)) |
|
172 |
+ cur_username = 'anonymous' |
|
173 |
+ if current_user.is_authenticated: |
|
174 |
+ cur_username = current_user.id |
|
175 |
+ cur.execute("SELECT url, title, posted, linkid, votes, has_user_shared(%s, url) from get_bone(%s);", |
|
176 |
+ (cur_username, username,)) |
|
167 | 177 |
result['marrow'] = [ |
168 |
- dict(id=linkid, url=url,title=title,posted=posted.isoformat(),votes=votes) |
|
169 |
- for url,title,posted,linkid,votes |
|
178 |
+ dict(id=linkid, url=url,title=title,posted=posted.isoformat(),votes=votes,shared=shared) |
|
179 |
+ for url,title,posted,linkid,votes,shared |
|
170 | 180 |
in cur.fetchall() |
171 | 181 |
] |
172 | 182 |
return json.dumps(result) |
... | ... |
@@ -230,8 +240,9 @@ def subscriptions(before, count): |
230 | 240 |
args = args + (before,) |
231 | 241 |
cur.callproc("get_bones", args) |
232 | 242 |
result['marrow'] = [ |
233 |
- dict(poster=poster, url=url,title=title,posted=posted.isoformat(), votes=votes, myVote=myvote) |
|
234 |
- for url,title,posted,poster,votes,myvote |
|
243 |
+ dict(id=id,poster=poster, url=url,title=title,posted=posted.isoformat(), votes=votes, |
|
244 |
+ myVote=myvote, shared=shared) |
|
245 |
+ for id,url,title,posted,poster,votes,myvote,shared |
|
235 | 246 |
in cur.fetchall() |
236 | 247 |
] |
237 | 248 |
return (json.dumps(result), 200, {'Content-Type': 'application/json'}) |
... | ... |
@@ -7,6 +7,9 @@ |
7 | 7 |
data-title="{{marrow.title}}" |
8 | 8 |
data-poster="{{marrow.poster}}" |
9 | 9 |
data-votes="{{marrow.votes}}"> |
10 |
+ <a href class="add-link" ng-click="reshare({item:marrow})" title="Reshare Link" |
|
11 |
+ analytics-on="click" analytics-category="link" analytics-label="reshare" |
|
12 |
+ analytics-event="{{marrow.url}}">{{!marrow.shared? '(+)': '✓'}}</a> |
|
10 | 13 |
<span class="vote-disp">{{marrow.votes}}</span> |
11 | 14 |
<span ng-if="marrow.title"> |
12 | 15 |
<a href="{{marrow.url}}" class="list-item" >{{marrow.title}}</a> |
... | ... |
@@ -84,6 +84,15 @@ marrowApp.controller('RootCtrl', function ($scope,$http,$location,$route, Subscr |
84 | 84 |
$scope.url = ""; |
85 | 85 |
$scope.title = ""; |
86 | 86 |
|
87 |
+ $scope.reshare = function (marrow) { |
|
88 |
+ if (!marrow.shared ) { |
|
89 |
+ $http.post('/api/bones/link/'+marrow.id).success(function(shared) { |
|
90 |
+ marrow.shared = true; |
|
91 |
+ if (shared === true) { $scope.update(); } |
|
92 |
+ }); |
|
93 |
+ }; |
|
94 |
+ }; |
|
95 |
+ |
|
87 | 96 |
$scope.toggleSubscribe = function (txt) { |
88 | 97 |
var postObj = {"from":$scope.bone.sectionTitle, "to":$scope.bone.sectionTitle}; |
89 | 98 |
var promise = null; |
... | ... |
@@ -30,12 +30,17 @@ |
30 | 30 |
<span class="voting"> |
31 | 31 |
<span class="score">{{marrow.votes}}</span> |
32 | 32 |
<button class="upVote vote-button fa fa-plus" ng-class="{selected: marrow.myVote===1}" ng-click="upVote(marrow)" |
33 |
- analytics-on="click" analytics-event="vote" analytics-category="{{marrow.myVote===0? 'up' : 'zero'}}"></button> |
|
33 |
+ analytics-on="click" analytics-event="vote" analytics-category="{{marrow.myVote===0? 'up' : 'zero'}}"> |
|
34 |
+ </button> |
|
34 | 35 |
</span> |
35 | 36 |
<span ng-if="marrow.title"> |
36 | 37 |
<a href="{{marrow.url}}" class="list-item">{{marrow.title}}</a> |
37 | 38 |
<br /> |
38 | 39 |
</span> |
40 |
+ <a class="add-link" ng-click="reshare(marrow)" title="Reshare Link" |
|
41 |
+ analytics-on="click" analytics-category="link" analytics-label="reshare" |
|
42 |
+ analytics-event="{{marrow.url}}">{{!marrow.shared? 'Reshare': '✓'}}</a> |
|
43 |
+ — |
|
39 | 44 |
<a href="{{marrow.url}}" ng-class="{'de-emphasize':marrow.title}" >{{marrow.url}}</a> |
40 | 45 |
</div> |
41 | 46 |
<user-badge poster="{{marrow.poster}}"></user-badge> |