PageRenderTime 56ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/src/main.py

https://github.com/xgenvn/jupo
Python | 2964 lines | 2898 code | 46 blank | 20 comment | 45 complexity | 61aabd1f337c08fece12c14dd462cceb MD5 | raw file
Possible License(s): AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. #! coding: utf-8
  2. # pylint: disable-msg=W0311, W0611, E1103, E1101
  3. #@PydevCodeAnalysisIgnore
  4. from raven.contrib.flask import Sentry
  5. from flask import (Flask, request,
  6. render_template, render_template_string,
  7. redirect, abort,
  8. url_for, session, g, flash,
  9. make_response, Response)
  10. from flask_sslify import SSLify
  11. from flask.ext.oauth import OAuth
  12. from flask.ext.seasurf import SeaSurf
  13. from flask.ext.assets import Bundle, Environment as WebAssets
  14. from werkzeug.wrappers import BaseRequest
  15. from werkzeug.utils import cached_property
  16. from werkzeug.contrib.securecookie import SecureCookie
  17. from werkzeug.contrib.profiler import ProfilerMiddleware, MergeStream
  18. from datetime import timedelta
  19. from commands import getoutput
  20. from mimetypes import guess_type
  21. from simplejson import dumps, loads
  22. from time import mktime, strptime, sleep
  23. from urlparse import urlparse, urlunparse
  24. from jinja2 import Environment
  25. from werkzeug.contrib.cache import MemcachedCache
  26. from lib import cache
  27. from lib.img_utils import zoom
  28. from lib.json_util import default as BSON
  29. from helpers import extensions
  30. from helpers.decorators import *
  31. from helpers.converters import *
  32. import os
  33. import sys
  34. import logging
  35. import requests
  36. import traceback
  37. import werkzeug.serving
  38. import flask_debugtoolbar
  39. from flask_debugtoolbar_lineprofilerpanel.profile import line_profile
  40. import api
  41. import filters
  42. import settings
  43. from app import CURRENT_APP, render
  44. # switch from the default ASCII to UTF-8 encoding
  45. reload(sys)
  46. sys.setdefaultencoding("utf-8") #@UndefinedVariable
  47. requests.adapters.DEFAULT_RETRIES = 3
  48. app = CURRENT_APP
  49. assets = WebAssets(app)
  50. if settings.SENTRY_DSN:
  51. sentry = Sentry(app, dsn=settings.SENTRY_DSN, logging=False)
  52. csrf = SeaSurf(app)
  53. oauth = OAuth()
  54. @line_profile
  55. def render_homepage(session_id, title, **kwargs):
  56. """ Render homepage for signed in user """
  57. if session_id:
  58. user_id = api.get_user_id(session_id)
  59. if user_id:
  60. owner = api.get_user_info(user_id)
  61. else:
  62. owner = None
  63. else:
  64. owner = None
  65. if owner:
  66. friends_online = [i for i in owner.contacts \
  67. if i.status in ['online', 'away']]
  68. friends_online.sort(key=lambda k: k.last_online, reverse=True)
  69. if not kwargs.has_key('groups'):
  70. groups = api.get_groups(session_id, limit=3)
  71. else:
  72. groups = kwargs.pop('groups')
  73. for group in groups[:3]:
  74. group.unread_posts = api.get_unread_posts_count(session_id, group.id)
  75. unread_messages = api.get_unread_messages(session_id)
  76. unread_messages_count = sum([i.get('unread_count') for i in unread_messages])
  77. unread_notification_count = api.get_unread_notifications_count(session_id)\
  78. + unread_messages_count
  79. else:
  80. friends_online = []
  81. groups = []
  82. unread_messages = []
  83. unread_messages_count = 0
  84. unread_notification_count = 0
  85. if kwargs.has_key('stats'):
  86. stats = kwargs.pop('stats')
  87. else:
  88. stats = {}
  89. hostname = request.headers.get('Host')
  90. logo_text = 'Jupo'
  91. if hostname != settings.PRIMARY_DOMAIN:
  92. network_info = api.get_network_info(hostname.replace('.', '_'))
  93. if network_info and network_info.has_key('name'):
  94. logo_text = network_info['name']
  95. resp = Response(render_template('home.html',
  96. owner=owner,
  97. title=title,
  98. groups=groups,
  99. friends_online=friends_online,
  100. unread_messages=unread_messages,
  101. unread_messages_count=unread_messages_count,
  102. unread_notification_count=unread_notification_count,
  103. stats=stats,
  104. debug=settings.DEBUG,
  105. logo_text=logo_text,
  106. domain=hostname,
  107. **kwargs))
  108. if owner:
  109. resp.set_cookie('channel_id', api.get_channel_id(session_id))
  110. else:
  111. resp.delete_cookie('channel_id')
  112. # disallows rendering of the document when inside an iframe
  113. # http://javascript.info/tutorial/clickjacking
  114. resp.headers['X-Frame-Options'] = 'DENY'
  115. return resp
  116. def event_stream(channel):
  117. pubsub = api.PUBSUB.pubsub()
  118. pubsub.subscribe(channel)
  119. for message in pubsub.listen():
  120. yield 'retry: 100\ndata: %s\n\n' % message['data']
  121. #===============================================================================
  122. # URL routing and handlers
  123. #===============================================================================
  124. @app.route('/ping')
  125. def ping():
  126. session_id = session.get('session_id')
  127. if session_id:
  128. api.update_pingpong_timestamp(session_id)
  129. return 'pong'
  130. @app.route('/stream')
  131. def stream():
  132. session_id = session.get('session_id')
  133. if not session_id:
  134. abort(400)
  135. channel_id = request.cookies.get('channel_id')
  136. resp = Response(event_stream(channel_id),
  137. mimetype="text/event-stream")
  138. resp.headers['X-Accel-Buffering'] = 'no'
  139. return resp
  140. @csrf.exempt
  141. @app.route("/autocomplete")
  142. @line_profile
  143. def autocomplete():
  144. session_id = session.get("session_id")
  145. query = request.args.get('query')
  146. type = request.args.get('type')
  147. if not query: # preload all groups & coworkers
  148. owners = api.get_contacts(session_id)
  149. user_ids = [i.id for i in owners]
  150. for user in api.get_all_users():
  151. if user.id not in user_ids:
  152. user_ids.append(user.id)
  153. owners.append(user)
  154. if type != 'user':
  155. groups = api.get_groups(session_id)
  156. group_ids = [i.id for i in groups]
  157. owners.extend(groups)
  158. items = [{'id': 'public',
  159. 'name': 'Public',
  160. 'avatar': '/public/images/public-group.png',
  161. 'type': 'group'}]
  162. for group in api.get_all_groups():
  163. if group.id not in group_ids:
  164. group_ids.append(group.id)
  165. owners.append(group)
  166. else:
  167. items = []
  168. for i in owners:
  169. if i.is_group():
  170. info = {'name': i.name,
  171. 'id': str(i.id),
  172. 'avatar': '/public/images/group-icon2.png',
  173. 'type': 'group'}
  174. items.append(info)
  175. else:
  176. names = [i.name]
  177. for name in names:
  178. info = {'name': name,
  179. 'id': str(i.id),
  180. 'avatar': i.avatar,
  181. 'type': 'user'}
  182. items.append(info)
  183. else:
  184. if type == 'user': # mentions
  185. _items = api.autocomplete(session_id, query)
  186. items = []
  187. for item in _items:
  188. if not api.is_group(item.get('id')): # only show user, not group
  189. info = api.get_user_info(item.get('id'))
  190. avatar = info.avatar
  191. type = 'user'
  192. items.append({'id': str(item.get('id')),
  193. 'name': item.get('name', item.get('email')),
  194. 'avatar': avatar,
  195. 'type': type})
  196. else:
  197. _items = api.autocomplete(session_id, query)
  198. items = []
  199. for item in _items:
  200. info = api.get_owner_info_from_uuid(item.get('id'))
  201. avatar = '/public/images/group.png' if info.is_group() else info.avatar
  202. items.append({'id': str(item.get('id')),
  203. 'name': item.get('name', item.get('email')),
  204. 'avatar': avatar,
  205. 'type': item.get('type')})
  206. # emoticons = [{'id': 'happy',
  207. # 'type': 'emoticon',
  208. # 'name': 'happy',
  209. # 'avatar': 'https://5works.s3.amazonaws.com/emoticons/1.gif'},
  210. # {'id': 'sad',
  211. # 'type': 'emoticon',
  212. # 'name': 'sad',
  213. # 'avatar': 'https://5works.s3.amazonaws.com/emoticons/2.gif'},
  214. # ]
  215. # items.extend(emoticons)
  216. return dumps(items)
  217. @app.route("/search", methods=['GET', 'OPTIONS', 'POST'])
  218. @login_required
  219. @line_profile
  220. def search():
  221. t0 = api.utctime()
  222. session_id = session.get("session_id")
  223. query = request.form.get('query', request.args.get('query', '')).strip()
  224. item_type = request.args.get('type')
  225. page = int(request.args.get('page', 1))
  226. ref_user_id = request.args.get('user')
  227. ref = request.args.get('ref')
  228. if ref and 'group-' in ref:
  229. group_id = ref.split('-')[1]
  230. group = api.get_group_info(session_id, group_id)
  231. if item_type == 'people':
  232. title = 'Add people to group'
  233. else:
  234. title = 'Invite your friends'
  235. elif ref == 'everyone':
  236. title = 'Invite your friends'
  237. group_id = group = None
  238. else:
  239. group_id = group = None
  240. title = 'Add Contacts'
  241. user_id = api.get_user_id(session_id)
  242. owner = api.get_user_info(user_id)
  243. if item_type in ['people', 'email']:
  244. if request.method == 'OPTIONS':
  245. if query:
  246. if item_type == 'people':
  247. users = api.people_search(query, group_id)
  248. if users:
  249. users = [i for i in users \
  250. if i.id not in owner.contact_ids and i.id != owner.id]
  251. else:
  252. users = [i for i in api.get_coworkers(session_id) \
  253. if i.id not in group.member_ids and i.id != owner.id]
  254. else:
  255. users = [i for i in owner.google_contacts \
  256. if api.get_user_id_from_email_address(i.email) not in group.member_ids]
  257. elif group_id:
  258. if item_type == 'email':
  259. users = [i for i in owner.google_contacts \
  260. if api.get_user_id_from_email_address(i.email) not in group.member_ids]
  261. else:
  262. users = [i for i in api.get_coworkers(session_id) \
  263. if i.id not in group.member_ids and i.id != owner.id]
  264. else:
  265. if item_type == 'email':
  266. users = [i for i in owner.google_contacts]
  267. else:
  268. users = [i for i in api.get_coworkers(session_id) \
  269. if i.id not in owner.contact_ids and i.id != owner.id]
  270. return dumps({'title': title,
  271. 'body': render_template('people_search.html',
  272. title=title,
  273. mode='search',
  274. type=item_type,
  275. group_id=group_id,
  276. group=group,
  277. users=users,
  278. query=query,
  279. owner=owner)})
  280. else:
  281. if item_type == 'people':
  282. users = api.people_search(query)
  283. else:
  284. q = query.lower()
  285. users = [i for i in owner.google_contacts \
  286. if i.email and i.email.lower().startswith(q)]
  287. if group_id:
  288. out = [i for i in users if i.email and i.id not in group.member_ids and i.id != owner.id]
  289. out.extend([i for i in users if i.email and i.id != owner.id and i.id in group.member_ids])
  290. users = out
  291. else:
  292. users = [i for i in users if i.email]
  293. if users:
  294. return ''.join(render_template('user.html',
  295. mode='search',
  296. user=user,
  297. group_id=group_id,
  298. group=group,
  299. type=item_type,
  300. query=query,
  301. owner=owner,
  302. title=title) \
  303. for user in users if user.email)
  304. else:
  305. if item_type == 'email':
  306. return "<li>Type your friend's email address</li>"
  307. else:
  308. return "<li>0 results found</li>"
  309. # search posts
  310. results = api.search(session_id, query, item_type,
  311. ref_user_id=ref_user_id, page=page)
  312. if results and results.has_key('counters'):
  313. hits = results.get('hits', 0)
  314. total = sum([i['count'] for i in results.get('counters')])
  315. counters = results['counters']
  316. else:
  317. hits = 0
  318. total = 0
  319. counters = {}
  320. due = api.utctime() - t0
  321. owner = api.get_owner_info(session_id)
  322. coworkers = api.get_coworkers(session_id)
  323. if request.method == 'GET':
  324. return render_homepage(session_id, 'Results for "%s"' % query,
  325. query=query,
  326. type=item_type,
  327. due='%.3f' % due,
  328. total=total,
  329. hits=hits,
  330. coworkers=coworkers,
  331. user=user,
  332. view='results',
  333. page=page,
  334. counters=counters)
  335. if page == 1:
  336. body = render_template('results.html', owner=owner,
  337. query=query,
  338. type=item_type,
  339. due='%.3f' % due,
  340. total=total,
  341. hits=hits,
  342. coworkers=coworkers,
  343. user=user,
  344. view='results',
  345. page=page,
  346. counters=counters)
  347. else:
  348. posts = [render(hits, "feed", owner, 'results')]
  349. if len(hits) == 0:
  350. posts.append(render_template('more.html', more_url=None))
  351. else:
  352. more_url = '/search?query=%s&page=%s' % (query, page+1)
  353. if item_type:
  354. more_url += '&type=%s' % item_type
  355. if user:
  356. more_url += '&user=%s' % user
  357. posts.append(render_template('more.html', more_url=more_url))
  358. return ''.join(posts)
  359. # TODO: render 1 lần thôi :(
  360. if body.split('<ul id="stream">')[-1].split('<script>')[0].split('right-sidebar')[0].count(query) < 2:
  361. spelling_suggestion = api.get_spelling_suggestion(query)
  362. app.logger.debug('spelling suggestion: %s' % spelling_suggestion)
  363. body = render_template('results.html', owner=owner,
  364. spelling_suggestion=spelling_suggestion,
  365. query=query,
  366. type=item_type,
  367. due='%.3f' % due,
  368. total=total,
  369. hits=hits,
  370. coworkers=coworkers,
  371. user=user,
  372. view='results',
  373. counters=counters)
  374. json = dumps({'title': '%s - Jupo Search' % query,
  375. 'body': body})
  376. return Response(json, content_type='application/json')
  377. #@app.route("/", methods=["GET", "OPTIONS"])
  378. @app.route('/<any(discover, starred, hot, incoming, shared_by_me):name>', methods=['GET', 'OPTIONS'])
  379. @app.route('/<any(discover, starred, hot, incoming, shared_by_me):name>/page<int:page>', methods=['OPTIONS'])
  380. def discover(name='discover', page=1):
  381. code = request.args.get('code')
  382. user_id = api.get_user_id(code)
  383. if user_id:
  384. session['session_id'] = code
  385. return render_homepage(code, 'Complete Profile',
  386. code=code, user_id=user_id)
  387. session_id = session.get("session_id")
  388. app.logger.debug('session_id: %s' % session_id)
  389. user_id = api.get_user_id(session_id)
  390. if user_id:
  391. owner = api.get_user_info(user_id)
  392. if name == 'starred':
  393. category = name
  394. feeds = api.get_starred_posts(session_id, page=page)
  395. elif name == 'hot':
  396. category = name
  397. feeds = api.get_hot_posts(page=page)
  398. elif name == 'shared_by_me':
  399. category = name
  400. feeds = api.get_shared_by_me_posts(session_id, page=page)
  401. elif request.path in ['/', '/incoming', '/discover']:
  402. # feeds = api.get_incoming_posts(session_id, page=page)
  403. feeds = api.get_discover_posts(session_id, page=page)
  404. # feeds = api.get_public_posts(session_id=session_id, page=page)
  405. category = None
  406. else:
  407. feeds = api.get_public_posts(session_id=session_id, page=page)
  408. category = None
  409. else:
  410. feeds = api.get_public_posts(session_id=session_id, page=page)
  411. owner = category = None
  412. if session.has_key('session_id'):
  413. session.pop('session_id')
  414. public_groups = api.get_open_groups(limit=10)
  415. if request.method == 'OPTIONS':
  416. if page == 1:
  417. menu = render_template('menu.html',
  418. owner=owner,
  419. category=category,
  420. view='discover')
  421. body = render_template('discover.html',
  422. view='discover',
  423. category=category,
  424. public_groups=public_groups,
  425. owner=owner,
  426. title='Discover',
  427. feeds=feeds)
  428. json = dumps({"body": body,
  429. "menu": menu,
  430. "title": 'Discover'})
  431. resp = Response(json, mimetype='application/json')
  432. return resp
  433. else:
  434. posts = []
  435. for feed in feeds:
  436. posts.append(render(feed, "feed", owner, 'discover'))
  437. if len(feeds) == 0:
  438. posts.append(render_template('more.html', more_url=None))
  439. else:
  440. posts.append(render_template('more.html',
  441. more_url='/%s/page%d' \
  442. % (request.path.split('/')[1], page+1)))
  443. return ''.join(posts)
  444. else:
  445. if not user_id:
  446. # return render_homepage(session_id, 'Get work done. Faster. | Jupo',
  447. # view='intro')
  448. return render_template('landing_page.html')
  449. else:
  450. return render_homepage(session_id, 'Discover',
  451. view='discover',
  452. feeds=feeds)
  453. @app.route('/invite', methods=['GET', 'POST'])
  454. def invite():
  455. email = request.form.get('email', request.args.get('email'))
  456. group_id = request.form.get('group_id', request.args.get('group_id'))
  457. session_id = session.get("session_id")
  458. api.invite(session_id, email, group_id)
  459. return ' ✔ Done '
  460. @app.route('/welcome', methods=['GET', 'OPTIONS'])
  461. def welcome():
  462. if request.method == 'GET':
  463. session_id = session.get("session_id")
  464. return render_homepage(session_id, 'Getting Started', view='welcome')
  465. else:
  466. body = render_template('welcome.html', view='welcome')
  467. return dumps({'body': body,
  468. 'title': 'Welcome to Jupo'})
  469. @app.route('/privacy')
  470. def privacy():
  471. return redirect('https://www.jupo.com/note/340925645733232641')
  472. @app.route('/terms')
  473. def terms():
  474. return redirect('https://www.jupo.com/note/340921286333038593')
  475. @app.route('/about')
  476. def about():
  477. return render_template('about.html')
  478. @app.route('/notify_me', methods=['POST'])
  479. def notify_me():
  480. email = request.form.get('email')
  481. if not email:
  482. abort(400, 'Missing email')
  483. state = api.notify_me(email)
  484. return 'Thank you!'
  485. @app.route('/jobs')
  486. def jobs():
  487. return render_template('jobs_v2.html', title='Jupo - Jobs')
  488. @app.route("/<any(sign_in, sign_up, sign_out, forgot_password, reset_password):action>", methods=["GET", "OPTIONS", "POST"])
  489. def authentication(action=None):
  490. hostname = request.headers.get('Host')
  491. db_name = hostname.replace('.', '_')
  492. primary_domain = '.'.join(settings.PRIMARY_DOMAIN.rsplit('.', 2)[-2:])
  493. if hostname != settings.PRIMARY_DOMAIN:
  494. network_info = api.get_network_info(db_name)
  495. else:
  496. network_info = {'name': 'Jupo'}
  497. if request.path.endswith('sign_in'):
  498. if request.method == 'OPTIONS':
  499. data = dumps({'title': 'Sign in to Jupo',
  500. 'body': render_template('sign_in.html',
  501. domain=hostname,
  502. PRIMARY_DOMAIN=settings.PRIMARY_DOMAIN)})
  503. resp = Response(data, mimetype='application/json')
  504. return resp
  505. elif request.method == "GET":
  506. resp = Response(render_template('sign_in.html',
  507. domain=hostname,
  508. PRIMARY_DOMAIN=settings.PRIMARY_DOMAIN,
  509. network_info=network_info))
  510. return resp
  511. email = request.form.get("email")
  512. password = request.form.get("password")
  513. back_to = request.form.get('back_to', request.args.get('back_to'))
  514. user_agent = request.environ.get('HTTP_USER_AGENT')
  515. app.logger.debug(user_agent)
  516. remote_addr = request.environ.get('REMOTE_ADDR')
  517. session_id = api.sign_in(email, password,
  518. user_agent=user_agent, remote_addr=remote_addr)
  519. app.logger.debug(session_id)
  520. if session_id:
  521. # Sign in for all networks
  522. if db_name:
  523. db_names = api.get_db_names(email)
  524. if db_name not in db_names:
  525. api.add_db_name(email, db_name)
  526. for db in db_names:
  527. if db != db_name:
  528. api.update_session_id(email, session_id, db)
  529. session.permanent = True
  530. session['session_id'] = session_id
  531. if back_to:
  532. resp = redirect(back_to)
  533. resp.set_cookie('channel_id', api.get_channel_id(session_id))
  534. else:
  535. resp = redirect('/news_feed')
  536. resp.set_cookie('channel_id', api.get_channel_id(session_id))
  537. return resp
  538. else:
  539. message = 'Wrong email address and password combination.'
  540. resp = redirect('/?email=%s&message=%s' % (email, message))
  541. resp.set_cookie('new_user', '0')
  542. return resp
  543. elif request.path.endswith('sign_up'):
  544. if request.method == 'GET':
  545. welcome = request.args.get('welcome')
  546. resp = Response(render_template('sign_up.html',
  547. welcome=welcome,
  548. domain=hostname,
  549. PRIMARY_DOMAIN=settings.PRIMARY_DOMAIN,
  550. network_info=network_info))
  551. return resp
  552. email = request.form.get('email').strip()
  553. name = request.form.get('name')
  554. password = request.form.get('password', '')
  555. alerts = {}
  556. if email and api.is_exists(email):
  557. alerts['email'] = '"%s" is already in use.' % email
  558. if len(password) < 6:
  559. alerts['password'] = 'Your password must be at least 6 characters long.'
  560. if alerts.keys():
  561. data = dumps(alerts)
  562. return Response(data, mimetype='application/json')
  563. else:
  564. session_id = api.sign_up(email, password, name)
  565. if session_id:
  566. # Sign in for all networks
  567. if db_name:
  568. db_names = api.get_db_names(email)
  569. if db_name not in db_names:
  570. api.add_db_name(email, db_name)
  571. for db in db_names:
  572. if db != db_name:
  573. api.update_session_id(email, session_id, db)
  574. session['session_id'] = session_id
  575. return redirect('/reminders')
  576. else:
  577. return redirect('/')
  578. elif request.path.endswith('sign_out'):
  579. session_id = session.get('session_id')
  580. if session_id:
  581. user_id = api.get_user_id(session_id)
  582. email = api.get_user_info(user_id).email
  583. app.logger.debug('sign out: %s' % session_id)
  584. api.set_status(session_id, 'offline')
  585. api.sign_out(session_id)
  586. session.pop('session_id')
  587. # Sign out all networks
  588. if db_name:
  589. db_names = api.get_db_names(email)
  590. if db_name not in db_names:
  591. api.add_db_name(email, db_name)
  592. for db in db_names:
  593. if db != db_name:
  594. api.sign_out(session_id, db_name=db)
  595. return redirect('/')
  596. elif request.path.endswith('forgot_password'):
  597. if request.method == 'GET':
  598. return render_template('reset_password.html')
  599. if request.method == 'OPTIONS':
  600. data = dumps({'title': 'Jupo - Forgot your password?',
  601. 'body': render_template('forgot_password.html')})
  602. return Response(data, mimetype='application/json')
  603. else:
  604. email = request.form.get('email')
  605. if not email:
  606. abort(400)
  607. api.forgot_password(email)
  608. return redirect("/?message=Please check your inbox and follow the instructions in the email")
  609. elif request.path.endswith('reset_password'):
  610. if request.method == 'GET':
  611. code = request.args.get('code')
  612. if not code:
  613. return redirect('/news_feed')
  614. email = api.FORGOT_PASSWORD.get(code)
  615. if email:
  616. resp = {'title': 'Reset your password',
  617. 'body': render_template('reset_password.html',
  618. email=email, code=code)}
  619. return render_homepage(None, 'Reset your password',
  620. view='reset_password', email=email, code=code)
  621. return redirect('/?message=Link expired')
  622. else:
  623. code = request.form.get('code')
  624. new_password = request.form.get('password')
  625. if not code or not new_password:
  626. abort(400)
  627. email = api.FORGOT_PASSWORD.get(code)
  628. if not email:
  629. abort(410)
  630. user_id = api.get_user_id_from_email_address(email)
  631. if not user_id:
  632. abort(400)
  633. else:
  634. api.reset_password(user_id, new_password)
  635. session_id = api.sign_in(email, new_password)
  636. session['session_id'] = session_id
  637. return redirect('/news_feed')
  638. @app.route('/oauth/google')
  639. def google_login():
  640. domain = request.args.get('domain', settings.PRIMARY_DOMAIN)
  641. return redirect('https://accounts.google.com/o/oauth2/auth?response_type=code&scope=https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile+https://www.google.com/m8/feeds/&redirect_uri=%s&approval_prompt=auto&state=%s&client_id=%s&hl=en&from_login=1&as=-773fbbe34097e4fd&pli=1&authuser=0' \
  642. % (settings.GOOGLE_REDIRECT_URI, domain, settings.GOOGLE_CLIENT_ID))
  643. @app.route('/oauth/google/authorized')
  644. def google_authorized():
  645. code = request.args.get('code')
  646. domain = request.args.get('state', settings.PRIMARY_DOMAIN)
  647. # get access_token
  648. url = 'https://accounts.google.com/o/oauth2/token'
  649. resp = requests.post(url, data={'code': code,
  650. 'client_id': settings.GOOGLE_CLIENT_ID,
  651. 'client_secret': settings.GOOGLE_CLIENT_SECRET,
  652. 'redirect_uri': settings.GOOGLE_REDIRECT_URI,
  653. 'grant_type': 'authorization_code'})
  654. data = loads(resp.text)
  655. # app.logger.debug(data)
  656. # fetch user info
  657. url = 'https://www.googleapis.com/oauth2/v1/userinfo'
  658. resp = requests.get(url, headers={'Authorization': '%s %s' \
  659. % (data.get('token_type'),
  660. data.get('access_token'))})
  661. user = loads(resp.text)
  662. url = 'https://www.google.com/m8/feeds/contacts/default/full/?max-results=5000'
  663. resp = requests.get(url, headers={'Authorization': '%s %s' \
  664. % (data.get('token_type'),
  665. data.get('access_token'))})
  666. contacts = api.re.findall("address='(.*?)'", resp.text)
  667. if contacts:
  668. contacts = list(set(contacts))
  669. db_name = domain.lower().strip().replace('.', '_')
  670. session_id = api.sign_in_with_google(email=user.get('email'),
  671. name=user.get('name'),
  672. gender=user.get('gender'),
  673. avatar=user.get('picture'),
  674. link=user.get('link'),
  675. locale=user.get('locale'),
  676. verified=user.get('verified_email'),
  677. google_contacts=contacts,
  678. db_name=db_name)
  679. if db_name:
  680. email = user.get('email')
  681. if email:
  682. db_names = api.get_db_names(email)
  683. if db_name not in db_names:
  684. api.add_db_name(email, db_name)
  685. for db in db_names:
  686. if db != db_name:
  687. api.update_session_id(email, session_id, db)
  688. if domain == settings.PRIMARY_DOMAIN:
  689. session['session_id'] = session_id
  690. return redirect('/')
  691. else:
  692. url = 'http://%s/?session_id=%s' % (domain, session_id)
  693. resp = redirect(url)
  694. return resp
  695. if settings.FACEBOOK_APP_ID and settings.FACEBOOK_APP_SECRET:
  696. facebook = oauth.remote_app('facebook',
  697. base_url='https://graph.facebook.com/',
  698. request_token_url=None,
  699. access_token_url='/oauth/access_token',
  700. authorize_url='https://www.facebook.com/dialog/oauth',
  701. consumer_key=settings.FACEBOOK_APP_ID,
  702. consumer_secret=settings.FACEBOOK_APP_SECRET,
  703. request_token_params={'scope': 'email'}
  704. )
  705. @app.route('/oauth/facebook')
  706. def facebook_login():
  707. if not facebook:
  708. abort(501)
  709. # return facebook.authorize(callback='http://play.jupo.com/oauth/facebook/authorized')
  710. callback_url = url_for('facebook_authorized',
  711. domain=request.args.get('domain', settings.PRIMARY_DOMAIN),
  712. _external=True)
  713. app.logger.debug(callback_url)
  714. return facebook.authorize(callback=callback_url)
  715. @app.route('/oauth/facebook/authorized')
  716. @facebook.authorized_handler
  717. def facebook_authorized(resp):
  718. domain = request.args.get('domain', settings.PRIMARY_DOMAIN)
  719. if resp is None:
  720. return 'Access denied: reason=%s error=%s' % (request.args['error_reason'],
  721. request.args['error_description'])
  722. session['facebook_access_token'] = (resp['access_token'], '')
  723. if request.args.get('fb_source') == 'notification':
  724. return redirect('/')
  725. retry_count = 0
  726. while retry_count < 3:
  727. try:
  728. me = facebook.get('/me')
  729. break
  730. except:
  731. retry_count += 1
  732. sleep(1)
  733. retry_count = 0
  734. while retry_count < 3:
  735. try:
  736. friends = facebook.get('/me/friends?limit=5000')
  737. break
  738. except:
  739. retry_count += 1
  740. sleep(1)
  741. facebook_id = me.data['id']
  742. friend_ids = [i['id'] for i in friends.data['data'] if isinstance(i, dict)]
  743. db_name = domain.lower().strip().replace('.', '_')
  744. session_id = api.sign_in_with_facebook(email=me.data.get('email'),
  745. name=me.data.get('name'),
  746. gender=me.data.get('gender'),
  747. avatar='https://graph.facebook.com/%s/picture' % facebook_id,
  748. link=me.data.get('link'),
  749. locale=me.data.get('locale'),
  750. timezone=me.data.get('timezone'),
  751. verified=me.data.get('verified'),
  752. facebook_id=facebook_id,
  753. facebook_friend_ids=friend_ids,
  754. db_name=db_name)
  755. if db_name:
  756. email = me.data.get('email')
  757. if email:
  758. db_names = api.get_db_names(email)
  759. if db_name not in db_names:
  760. api.add_db_name(email, db_name)
  761. for db in db_names:
  762. if db != db_name:
  763. api.update_session_id(email, session_id, db)
  764. if domain == settings.PRIMARY_DOMAIN:
  765. session['session_id'] = session_id
  766. return redirect('/')
  767. else:
  768. url = 'http://%s/?session_id=%s' % (domain, session_id)
  769. resp = redirect(url)
  770. return resp
  771. @facebook.tokengetter
  772. def get_facebook_token():
  773. return session.get('facebook_access_token')
  774. @app.route('/reminders', methods=['GET', 'OPTIONS', 'POST'])
  775. @app.route('/reminder/new', methods=["POST"])
  776. @app.route('/reminder/<int:reminder_id>/check', methods=["POST"])
  777. @app.route('/reminder/<int:reminder_id>/uncheck', methods=["POST"])
  778. @login_required
  779. @line_profile
  780. def reminders(reminder_id=None):
  781. session_id = session.get("session_id")
  782. message = request.form.get('message')
  783. if request.path.endswith('/new'):
  784. id = api.new_reminder(session_id, message)
  785. return str(id)
  786. elif request.path.endswith('/check'):
  787. api.check(session_id, reminder_id)
  788. return 'Done'
  789. elif request.path.endswith('/uncheck'):
  790. api.uncheck(session_id, reminder_id)
  791. return 'Done'
  792. else:
  793. reminders_list = api.get_reminders(session_id)
  794. if request.method == 'GET':
  795. return render_homepage(session_id, 'Reminders',
  796. view='reminders',
  797. reminders=reminders_list)
  798. else:
  799. body = render_template('reminders.html',
  800. view='reminders',
  801. reminders=reminders_list)
  802. json = dumps({"body": body,
  803. "title": 'Reminders'})
  804. return Response(json, mimetype='application/json')
  805. @app.route("/notes", methods=['GET', 'OPTIONS'])
  806. @app.route("/notes/page<int:page>", methods=['OPTIONS'])
  807. @login_required
  808. @line_profile
  809. def notes(page=1):
  810. view = 'notes'
  811. session_id = session.get("session_id")
  812. user_id = api.get_user_id(session_id)
  813. owner = api.get_user_info(user_id)
  814. title = "Notes"
  815. notes = api.get_notes(session_id, page=page)
  816. if page == 1:
  817. reference_notes = api.get_reference_notes(session_id, 10)
  818. else:
  819. reference_notes = []
  820. app.logger.debug(notes)
  821. if request.method == "OPTIONS":
  822. if page > 1:
  823. stream = ''
  824. posts = []
  825. for note in notes:
  826. posts.append(render(note, "note", owner, view, request=request))
  827. if len(notes) == 0:
  828. posts.append(render_template('more.html', more_url=None))
  829. else:
  830. posts.append(render_template('more.html',
  831. more_url='/notes/page%d' % (page+1)))
  832. return ''.join(posts)
  833. else:
  834. menu = render_template('menu.html',
  835. view=view)
  836. body = render_template('notes.html',
  837. view=view,
  838. title=title,
  839. owner=owner,
  840. request=request,
  841. reference_notes=reference_notes,
  842. notes=notes)
  843. json = dumps({"body": body,
  844. "menu": menu,
  845. "title": title})
  846. return Response(json, mimetype='application/json')
  847. else:
  848. return render_homepage(session_id, title,
  849. view=view,
  850. request=request,
  851. reference_notes=reference_notes,
  852. notes=notes)
  853. @app.route("/note/new", methods=["GET", "OPTIONS", "POST"])
  854. @app.route("/note/<int:note_id>", methods=["GET", "OPTIONS"])
  855. @app.route("/note/<int:note_id>/edit", methods=["OPTIONS"])
  856. @app.route("/note/<int:note_id>/v<int:version>", methods=["OPTIONS", "POST"])
  857. @app.route("/note/<int:note_id>/<action>", methods=["GET", "OPTIONS", "POST"])
  858. @login_required
  859. def note(note_id=None, action=None, version=None):
  860. session_id = session.get("session_id")
  861. owner = api.get_owner_info(session_id)
  862. content = info = None
  863. if request.path == '/note/new':
  864. if request.method == 'GET':
  865. note = {}
  866. title = 'New Note'
  867. mode = 'edit'
  868. view = 'notes'
  869. return render_homepage(session_id, title,
  870. view=view,
  871. note=note, mode=mode)
  872. elif request.method == 'OPTIONS':
  873. title = 'New Note'
  874. mode = 'edit'
  875. note = {}
  876. else:
  877. title = request.form.get('title', 'Untitled Note')
  878. content = request.form.get('content')
  879. note_id = request.form.get('note_id')
  880. viewers = request.form.get('viewers')
  881. if viewers:
  882. viewers = viewers.split(',')
  883. else:
  884. viewers = []
  885. attachments = request.form.get('attachments')
  886. if attachments:
  887. attachments = attachments.rstrip(',').split(',')
  888. else:
  889. attachments = []
  890. note_id = api.new_note(session_id, title, content, attachments, viewers)
  891. return dumps({'redirect': '/note/%s' % note_id})
  892. elif action and action == 'last_changes':
  893. note = api.compare_with_previous_version(session_id, note_id, revision=0)
  894. mode = 'view'
  895. action = 'compare'
  896. title = 'Notes - Track changes'
  897. elif version is not None:
  898. app.logger.debug(version)
  899. note = api.get_note(session_id, note_id, version)
  900. mode = 'view'
  901. title = '%s v%s | Jupo Notes' % (note.title, version)
  902. elif request.path.endswith('/edit'):
  903. note = api.get_note(session_id, note_id)
  904. title = 'Notes - EditMode - %s' % note.title
  905. mode = 'edit'
  906. elif action == 'update':
  907. title = request.form.get('title', 'Untitled Note')
  908. content = request.form.get('content')
  909. viewers = request.form.get('viewers')
  910. if viewers:
  911. viewers = viewers.split(',')
  912. else:
  913. viewers = []
  914. attachments = request.form.get('attachments')
  915. app.logger.debug(attachments)
  916. if attachments:
  917. attachments = attachments.rstrip(',').split(',')
  918. else:
  919. attachments = []
  920. if note_id:
  921. api.update_note(session_id, note_id, title, content, attachments, viewers)
  922. else:
  923. note_id = api.new_note(session_id, title, content, attachments, viewers)
  924. return dumps({'redirect': '/note/%s' % note_id})
  925. elif action == 'remove':
  926. session_id = session.get("session_id")
  927. id = api.remove_note(session_id, note_id)
  928. return id
  929. elif action == 'mark_official':
  930. api.mark_official(session_id, note_id)
  931. return 'Done'
  932. elif action == 'mark_unofficial':
  933. api.mark_unofficial(session_id, note_id)
  934. return 'Done'
  935. elif action == 'mark_as_read':
  936. api.mark_as_read(session_id, note_id)
  937. return ':)'
  938. # elif action and action.startswith('restore_from_'):
  939. # revision = action.lstrip('restore_from_')
  940. # if revision.isdigit():
  941. # api.restore_note(session_id, note_id, int(revision))
  942. # note = api.get_note(session_id, note_id)
  943. # title = 'Notes - %s' % info.title
  944. # mode = 'view'
  945. # else:
  946. # pass #TODO: return an error page with code = 405
  947. elif action == 'comment':
  948. message = request.form.get('message')
  949. comment = api.new_comment(session_id, message, note_id)
  950. return render_template('comment.html',
  951. comment=comment,
  952. owner=owner,
  953. prefix='note')
  954. else:
  955. note = api.get_note(session_id, note_id)
  956. if not session_id and note is False:
  957. return redirect('/?back_to=%s' % request.path)
  958. if not note.id:
  959. abort(404)
  960. mode = 'view'
  961. recents = api.get_notes(session_id, limit=5)
  962. view = 'notes'
  963. if version is None and info:
  964. version = len(note.to_dict()['version'])
  965. group_id = request.args.get('group_id')
  966. if group_id:
  967. group = api.get_group_info(session_id, group_id)
  968. else:
  969. group = None
  970. if request.method in ["POST", "OPTIONS"]:
  971. body = render_template('notes.html',
  972. view='notes',
  973. mode=mode,
  974. action=action,
  975. version=version,
  976. recents=recents,
  977. note=note,
  978. group=group,
  979. owner=owner,
  980. content=content)
  981. info = {'body': body}
  982. if note:
  983. info['title'] = note.title
  984. else:
  985. info['title'] = 'Untitle Note'
  986. return Response(dumps(info), mimetype='application/json')
  987. else:
  988. return render_homepage(session_id, note.title,
  989. version=version,
  990. group=group, note=note, view='notes', mode='view')
  991. @app.route('/u<key>')
  992. @app.route('/u/<int:note_id>')
  993. def share_to_anyone_with_the_link(note_id=None, key=None):
  994. session_id = session.get("session_id")
  995. if key:
  996. doc = api.get_doc_by_key(key)
  997. return render_homepage(session_id, 'Note',
  998. view='discover', mode='view', doc=doc)
  999. else:
  1000. key = api.get_key(session_id, note_id)
  1001. if not key:
  1002. abort(404)
  1003. return redirect('/u' + key)
  1004. #===============================================================================
  1005. # Files
  1006. #===============================================================================
  1007. @app.route("/public/<path:filename>")
  1008. def public_files(filename):
  1009. filedata = cache.get('file:%s' % os.path.basename(filename)) \
  1010. if not settings.DEBUG else None
  1011. if not filedata:
  1012. path = os.path.join(os.path.dirname(__file__), 'public', filename)
  1013. if not os.path.exists(path):
  1014. abort(404, 'File not found')
  1015. filedata = open(path).read()
  1016. response = make_response(filedata)
  1017. response.headers['Content-Length'] = len(filedata)
  1018. response.headers['Content-Type'] = guess_type(filename)[0]
  1019. response.headers['Cache-Control'] = 'public'
  1020. response.headers['Expires'] = '31 December 2037 23:59:59 GMT'
  1021. return response
  1022. @app.route("/favicon.ico")
  1023. def favicon():
  1024. path = 'public/favicon.ico'
  1025. filedata = open(path).read()
  1026. response = make_response(filedata)
  1027. response.headers['Content-Length'] = len(filedata)
  1028. response.headers['Content-Type'] = 'image/x-icon'
  1029. return response
  1030. @app.route('/update_profile_picture', methods=['POST'])
  1031. @app.route('/user/<int:user_id>', methods=['GET', 'OPTIONS'])
  1032. @app.route('/user/<int:user_id>/page<int:page>', methods=['GET', 'OPTIONS'])
  1033. @app.route('/user/<int:user_id>/update', methods=['POST'])
  1034. @app.route('/user/<int:user_id>/complete_profile', methods=['POST'])
  1035. @app.route('/user/<int:user_id>/follow', methods=['POST'])
  1036. @app.route('/user/<int:user_id>/unfollow', methods=['POST'])
  1037. @app.route('/user/<int:user_id>/<view>', methods=['GET', 'OPTIONS'])
  1038. @login_required
  1039. @line_profile
  1040. def user(user_id=None, page=1, view=None):
  1041. session_id = session.get("session_id")
  1042. if request.path == '/update_profile_picture':
  1043. fid = request.args.get('fid')
  1044. api.update_user_info(session_id, {'avatar': long(fid)})
  1045. return 'OK'
  1046. owner = api.get_owner_info(session_id)
  1047. user = api.get_user_info(user_id)
  1048. if not user.id:
  1049. abort(404)
  1050. if view in ['edit', 'update_profile']:
  1051. resp = {'title': 'Update Profile',
  1052. 'body': render_template('user.html',
  1053. mode='edit',
  1054. view='update_profile',
  1055. owner=owner)}
  1056. return Response(dumps(resp), mimetype='application/json')
  1057. elif view == 'change_password':
  1058. resp = {'title': 'Change Password',
  1059. 'body': render_template('user.html',
  1060. mode='change_password',
  1061. owner=user)}
  1062. return Response(dumps(resp), mimetype='application/json')
  1063. elif request.path.endswith('/complete_profile'):
  1064. name = request.form.get('name')
  1065. gender = request.form.get('gender')
  1066. password = request.form.get('password')
  1067. avatar = request.files.get('file')
  1068. fid = api.new_attachment(session_id, avatar.filename, avatar.stream.read())
  1069. new_session_id = api.complete_profile(session_id,
  1070. name, password, gender, fid)
  1071. resp = redirect('/news_feed')
  1072. if new_session_id:
  1073. session['session_id'] = new_session_id
  1074. return resp
  1075. elif request.path.endswith('/update'):
  1076. old_password = request.form.get('current_password')
  1077. new_password = request.form.get('new_password')
  1078. confirm_new_password = request.form.get('confirm_new_password')
  1079. if old_password:
  1080. if new_password != confirm_new_password:
  1081. return redirect('/?message=New password does not match')
  1082. else:
  1083. ok = api.change_password(session_id, old_password, new_password)
  1084. api.set_status(session_id, 'offline')
  1085. api.sign_out(session_id)
  1086. session.pop('session_id')
  1087. return redirect('/?message=Password updated successfully.')
  1088. if not old_password and new_password and new_password == confirm_new_password:
  1089. if owner.has_password():
  1090. return redirect('/?message=Please enter your current password.')
  1091. else:
  1092. user_id = api.get_user_id(session_id)
  1093. api.reset_password(user_id, new_password)
  1094. return redirect('/?message=New password updated successfully.')
  1095. name = request.form.get('name')
  1096. gender = request.form.get('gender')
  1097. intro = request.form.get('intro')
  1098. location = request.form.get('location')
  1099. if location:
  1100. info = {'name': name,
  1101. 'gender': gender,
  1102. 'location': location,
  1103. 'introduction': intro}
  1104. else:
  1105. info = {'name': name,
  1106. 'gender': gender,
  1107. 'introduction': intro}
  1108. disabled_notifications = []
  1109. if not request.form.get('comments'):
  1110. disabled_notifications.append('comments')
  1111. if not request.form.get('share_posts'):
  1112. disabled_notifications.append('share_posts')
  1113. if not request.form.get('mentions'):
  1114. disabled_notifications.append('mentions')
  1115. info['disabled_notifications'] = disabled_notifications
  1116. birthday_day = request.form.get('birthday-day')
  1117. birthday_month = request.form.get('birthday-month')
  1118. birthday_year = request.form.get('birthday-year')
  1119. if birthday_day.isdigit() and birthday_month.isdigit() and birthday_year.isdigit():
  1120. info['birthday'] = '%s/%s/%s' % (birthday_day, birthday_month, birthday_year)
  1121. phone = request.form.get('phone')
  1122. if phone.replace('+', '').replace(' ', '').isdigit():
  1123. info['phone'] = phone
  1124. fid = request.form.get('fid')
  1125. if fid:
  1126. info['avatar'] = long(fid)
  1127. api.update_user_info(session_id, info)
  1128. return redirect('/news_feed')
  1129. elif request.path.endswith('/follow'):
  1130. api.add_to_contacts(session_id, user_id)
  1131. # api.follow(session_id, user_id)
  1132. return 'Done'
  1133. elif request.path.endswith('/unfollow'):
  1134. api.remove_from_contacts(session_id, user_id)
  1135. # api.unfollow(session_id, user_id)
  1136. return 'Done'
  1137. elif view == 'groups':
  1138. return dumps({'body': render_template('groups.html',
  1139. user=user,
  1140. owner=owner,
  1141. groups=user.groups)})
  1142. elif view == 'followers':
  1143. users = [api.get_user_info(user_id) for user_id in user.followers]
  1144. return dumps({'body': render_template('users.html',
  1145. title='Followers',
  1146. users=users)})
  1147. elif view == 'following':
  1148. users = [api.get_user_info(user_id) for user_id in user.following_users]
  1149. return dumps({'body': render_template('users.html',
  1150. title='Following',
  1151. users=users)})
  1152. elif view == 'starred':
  1153. posts = api.get_starred_posts(api.get_session_id(user_id))
  1154. body = render_template('user.html',
  1155. title='Starred',
  1156. category = 'starred',
  1157. view='user',
  1158. user=user,
  1159. owner=owner,
  1160. feeds=posts)
  1161. json = dumps({'body': body, 'title': 'Intelliview'})
  1162. return Response(json, mimetype='application/json')
  1163. else:
  1164. view = 'user'
  1165. title = user.name
  1166. if not session_id or owner.id == user.id:
  1167. feeds = api.get_public_posts(user_id=user.id, page=page)
  1168. else:
  1169. feeds = api.get_user_posts(session_id, user_id, page=page)
  1170. user.recent_files = api.get_user_files(session_id, user_id=user.id, limit=3)
  1171. user.recent_notes = api.get_user_notes(session_id, user_id=user.id, limit=3)
  1172. coworkers = [user]
  1173. if request.method == "OPTIONS":
  1174. if page == 1:
  1175. body = render_template('user.html',
  1176. view=view,
  1177. user=user,
  1178. owner=owner,
  1179. title=title,
  1180. coworkers=coworkers,
  1181. feeds=feeds)
  1182. json = dumps({"body": body,
  1183. "title": title})
  1184. return Response(json, mimetype='application/json')
  1185. else:
  1186. posts = [render(feeds, 'feed', owner, 'user')]
  1187. if len(feeds) == 0:
  1188. posts.append(render_template('more.html', more_url=None))
  1189. else:
  1190. posts.append(render_template('more.html',
  1191. more_url='/user/%s/page%d' \
  1192. % (user_id, page+1)))
  1193. return ''.jo

Large files files are truncated, but you can click here to view the full file