1 |
# -*- coding: utf-8 -*- |
2 |
# Copyright (C) 2012, Sebastian Silva |
3 |
# |
4 |
# This program is free software: you can redistribute it and/or modify |
5 |
# it under the terms of the GNU General Public License as published by |
6 |
# the Free Software Foundation, either version 3 of the License, or |
7 |
# (at your option) any later version. |
8 |
# |
9 |
# This program is distributed in the hope that it will be useful, |
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
# GNU General Public License for more details. |
13 |
# |
14 |
# You should have received a copy of the GNU General Public License |
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 |
|
17 |
import logging |
18 |
import os |
19 |
import sys |
20 |
import signal |
21 |
import locale |
22 |
from math import ceil |
23 |
from string import join,split |
24 |
|
25 |
import gevent |
26 |
from urllib2 import urlopen |
27 |
|
28 |
from flask import flash,Flask,request,url_for,abort,jsonify,Response,\ |
29 |
redirect,session,render_template,render_template_string,g,\ |
30 |
send_from_directory,Markup |
31 |
from flaskext.babel import Babel, gettext as _, lazy_gettext as __, format_datetime |
32 |
from werkzeug import Headers |
33 |
import simplejson |
34 |
|
35 |
from sugar_network import Client, sugar, ServerError |
36 |
|
37 |
_BUFFER_SIZE = 1024 * 10 |
38 |
|
39 |
""" Initialization of local app """ |
40 |
app = Flask(__name__) |
41 |
app.secret_key="ilovesugar" #necessary to initialize sessions |
42 |
babel = Babel(app) |
43 |
app.config['BABEL_DEFAULT_LOCALE'] = 'en' |
44 |
app.config['BABEL_DEFAULT_TIMEZONE'] = 'America/Lima' |
45 |
|
46 |
from cursors import * |
47 |
mounts = network_mount.client.mounts() |
48 |
|
49 |
@babel.localeselector |
50 |
def get_locale(): |
51 |
# try to guess the language from the user accept |
52 |
# header the browser transmits. |
53 |
lang = request.accept_languages.best_match(['es', 'en']) |
54 |
if not lang: |
55 |
# otherwise we're probably embedded, get from env |
56 |
lang = locale.getdefaultlocale()[0].split('_')[0] |
57 |
return lang |
58 |
|
59 |
@app.template_filter('special_str') |
60 |
def special_str(arg): |
61 |
if isinstance(arg, basestring): # Python 3: isinstance(arg, str) |
62 |
return arg |
63 |
else: |
64 |
return(arg[0]) |
65 |
|
66 |
@app.template_filter('timedelta') |
67 |
def timedelta(mtime): |
68 |
return format_datetime(mtime, _('MMMM d, yyyy')) |
69 |
|
70 |
def get_colors(): |
71 |
return sugar.color().split(',') |
72 |
|
73 |
def mount_event(e): |
74 |
if e.get('event')=='mount': |
75 |
if not [True for m in mounts if m['mountpoint']==e['mountpoint']]: |
76 |
mounts.append({'name':e['name'], |
77 |
'mountpoint':e['mountpoint']}) |
78 |
elif e.get('event')=='unmount': |
79 |
mounts[:] = [m for m in mounts if m.get('mountpoint') != e.get('mountpoint')] |
80 |
|
81 |
Client.connect(mount_event) |
82 |
|
83 |
@app.context_processor |
84 |
def inject_vars(): |
85 |
# Here we can inject variables into every template call |
86 |
stroke,fill = get_colors() |
87 |
connected = session.get('connected') |
88 |
#TODO: Mark which mount is active |
89 |
if connected==True: |
90 |
mountpoint="/" |
91 |
elif connected==False: |
92 |
mountpoint="~" |
93 |
else: |
94 |
mountpoint=connected |
95 |
kwvar = { |
96 |
'userid': sugar.uid(), |
97 |
'mounts': mounts, |
98 |
'current_mount': mountpoint |
99 |
} |
100 |
return dict(stroke=stroke, fill=fill, **kwvar) |
101 |
|
102 |
@app.before_request |
103 |
def before_request(): |
104 |
if 'connected' not in session: |
105 |
session['connected'] = network_mount.client.mounted |
106 |
if session['connected']==True and not network_mount.client.mounted: |
107 |
session['connected']=False |
108 |
if session['connected']==False: |
109 |
g.client = home_mount.client |
110 |
g.Contexts = home_mount.Contexts |
111 |
g.Activities= home_mount.Activities |
112 |
g.Projects = home_mount.Projects |
113 |
g.autocomplete_Contexts = home_mount.autocomplete_Contexts |
114 |
g.Questions = home_mount.Questions |
115 |
g.Ideas = home_mount.Ideas |
116 |
g.Problems = home_mount.Problems |
117 |
g.Solutions = home_mount.Solutions |
118 |
g.Comments = home_mount.Comments |
119 |
g.Reviews = home_mount.Reviews |
120 |
g.Resources = home_mount.Resources |
121 |
elif session['connected']==True: |
122 |
g.client = network_mount.client |
123 |
g.Contexts = network_mount.Contexts |
124 |
g.Activities= network_mount.Activities |
125 |
g.Projects = network_mount.Projects |
126 |
g.autocomplete_Contexts = network_mount.autocomplete_Contexts |
127 |
g.Questions = network_mount.Questions |
128 |
g.Ideas = network_mount.Ideas |
129 |
g.Problems = network_mount.Problems |
130 |
g.Solutions = network_mount.Solutions |
131 |
g.Comments = network_mount.Comments |
132 |
g.Reviews = network_mount.Reviews |
133 |
g.Resources = network_mount.Resources |
134 |
else: |
135 |
try: |
136 |
cm = g.custom_mount |
137 |
if cm.mountpoint!=session['connected']: |
138 |
g.custom_mount = Mount(session['connected']) |
139 |
except AttributeError: |
140 |
g.custom_mount = Mount(session['connected']) |
141 |
cm = g.custom_mount |
142 |
g.client = cm.client |
143 |
g.Contexts = cm.Contexts |
144 |
g.Activities= cm.Activities |
145 |
g.Projects = cm.Projects |
146 |
g.autocomplete_Contexts = cm.autocomplete_Contexts |
147 |
g.Questions = cm.Questions |
148 |
g.Ideas = cm.Ideas |
149 |
g.Problems = cm.Problems |
150 |
g.Solutions = cm.Solutions |
151 |
g.Comments = cm.Comments |
152 |
g.Reviews = cm.Reviews |
153 |
g.Resources = cm.Resources |
154 |
|
155 |
|
156 |
@app.route('/') |
157 |
def home(): |
158 |
return redirect(url_for('context_grid')) |
159 |
|
160 |
@app.route('/_toggle_connect') |
161 |
def toggle_connect(): |
162 |
if session.get('connected'): |
163 |
session['connected'] = False |
164 |
else: |
165 |
session['connected'] = True |
166 |
|
167 |
# Palette mount selector |
168 |
if request.args.get('select')=='~': |
169 |
session['connected'] = False |
170 |
elif request.args.get('select')=='/': |
171 |
session['connected'] = True |
172 |
elif request.args.get('select'): |
173 |
session['connected'] = request.args.get('select') |
174 |
g.custom_mount = Mount(session['connected']) |
175 |
|
176 |
return redirect(url_for('context_grid')) |
177 |
|
178 |
@app.route('/context/icon/<context_guid>') |
179 |
def gen_icon(context_guid): |
180 |
type=request.args.get('type') or session['current_type'] |
181 |
offset=int(request.args.get('cursor_offset') or 0) |
182 |
if offset and type: |
183 |
if type in ['activity']: |
184 |
context_cursor = g.Activities |
185 |
elif type in ['project']: |
186 |
context_cursor = g.Projects |
187 |
else: |
188 |
context_cursor = g.Contexts |
189 |
context=context_cursor[offset] |
190 |
else: |
191 |
context=g.client.Context(context_guid) |
192 |
def download(context): |
193 |
with context.get_blob('icon') as icon: |
194 |
if icon.closed: |
195 |
icon=app.open_resource('static/icons/document-generic.png') |
196 |
while True: |
197 |
chunk = icon.read(_BUFFER_SIZE) |
198 |
if not chunk: |
199 |
break |
200 |
yield chunk |
201 |
header = Headers() |
202 |
header.add("Content-Type", "image/png") |
203 |
return Response(download(context), headers=header, direct_passthrough=True) |
204 |
|
205 |
@app.route('/launch/<context_guid>') |
206 |
def launch(context_guid): |
207 |
g.client.launch(context_guid) |
208 |
return redirect('/context/reviews/%s' % context_guid) |
209 |
|
210 |
@app.route('/new/resource') |
211 |
@app.route('/new/resource/<context_guid>') |
212 |
def case_1(context_guid=None): |
213 |
if not context_guid: |
214 |
context_guid=session.get('last_context') |
215 |
if not context_guid: |
216 |
return redirect(url_for('context_grid')) |
217 |
|
218 |
offset=int(request.args.get('cursor_offset') or 0) |
219 |
if offset: |
220 |
context=g.Contexts[offset] |
221 |
else: |
222 |
context=g.client.Context(context_guid, reply=['title', 'keep', 'keep_impl']) |
223 |
return render_template('resource-form.html', context=context) |
224 |
|
225 |
@app.route('/_report') |
226 |
def report(): |
227 |
return render_template('report-form.html', |
228 |
context=request.args.get('context') or '', |
229 |
implementation=request.args.get('implementation') or '', |
230 |
filename=request.args.get('filename') or '') |
231 |
|
232 |
@app.route('/favorites') |
233 |
def favorites(): |
234 |
if session.get('favorites-filter'): |
235 |
session['favorites-filter']=False |
236 |
else: |
237 |
session['favorites-filter']=True |
238 |
return redirect(url_for('context_grid')) |
239 |
|
240 |
@app.route('/query') |
241 |
def autocomplete_context(): |
242 |
query = "title:"+request.args.get('term') |
243 |
r, total_pages, total, info = paginate(g.autocomplete_Contexts, query) |
244 |
result = [] |
245 |
for item in r: |
246 |
result.append( {'value':item['guid'], |
247 |
'label':item['title']} ) |
248 |
return simplejson.dumps(result) |
249 |
|
250 |
@app.route('/search/<query>') |
251 |
def search(query=None): |
252 |
return redirect(url_for('context_grid', query=query)) |
253 |
|
254 |
@app.route('/_tags/<tag>', methods=['DELETE']) |
255 |
def del_tag(tag): |
256 |
try: |
257 |
session['tags'].pop(session['tags'].index(tag)) |
258 |
session.modified = True |
259 |
except: |
260 |
pass |
261 |
return "True" |
262 |
|
263 |
@app.route('/_tags', methods=['POST', 'GET']) |
264 |
def tags(): |
265 |
hashtag = request.form['tag'][1:] |
266 |
try: |
267 |
session['tags'].append( hashtag ) |
268 |
session.modified = True |
269 |
except KeyError: |
270 |
session['tags'] = [hashtag, ] |
271 |
return render_template_string( |
272 |
"""{% for tag in tags %} |
273 |
<span class="tag">{{tag}}</span> |
274 |
{% endfor %}""", |
275 |
tags=session['tags']) |
276 |
|
277 |
@app.route('/_stars/<context>') |
278 |
def stars(context=None): |
279 |
if not context: |
280 |
return jsonify() |
281 |
|
282 |
guid = context[5:] #remove "stars-" from id |
283 |
keep = request.args.get('keep') |
284 |
|
285 |
context = g.client.Context(guid) |
286 |
context['keep'] = (keep == 'true') |
287 |
context.post() |
288 |
|
289 |
# TODO Need to reset query object until supporting notifications |
290 |
g.Contexts._reset() |
291 |
|
292 |
return jsonify(keep=keep) |
293 |
|
294 |
@app.route('/_moon/<context>') |
295 |
def moon(context=None): |
296 |
if not context: |
297 |
return jsonify() |
298 |
|
299 |
keep_impl = request.args.get('keep_impl', None) |
300 |
guid = context[5:] #remove "moon-" from id |
301 |
|
302 |
context = g.client.Context(guid) |
303 |
context['keep_impl'] = 1 if keep_impl == 'true' else 0 |
304 |
context.post() |
305 |
|
306 |
return jsonify(keep_impl=keep_impl) |
307 |
|
308 |
def paginate(resource, full_query, _PAGE_SIZE=6, page=1, context=None): |
309 |
""" This function attempts to query SN resources and |
310 |
return a specific page of result items. """ |
311 |
r = [] |
312 |
|
313 |
resource.update_filter(full_query, context=context) |
314 |
result=resource |
315 |
|
316 |
page_offset = _PAGE_SIZE * (page - 1) |
317 |
if page_offset>result.total: |
318 |
raise KeyError |
319 |
|
320 |
for i in range(page_offset, page_offset + _PAGE_SIZE): |
321 |
try: |
322 |
r.append(result[i]) |
323 |
except KeyError: |
324 |
pass |
325 |
|
326 |
total_pages = int(ceil(result.total / float(_PAGE_SIZE))) |
327 |
|
328 |
if total_pages==0: |
329 |
info = _(u'zero results') |
330 |
else: |
331 |
info = _(u'page %(page)s of %(total)s', page=page, total=total_pages) |
332 |
if page > total_pages and total_pages > 0: |
333 |
abort(404) |
334 |
|
335 |
return r, total_pages, result.total, info |
336 |
|
337 |
@app.route('/resource/search/') |
338 |
@app.route('/resource/search/<query>') |
339 |
@app.route('/resource/reviews') |
340 |
@app.route('/resource/reviews/<query>') |
341 |
@app.route('/resource/questions') |
342 |
@app.route('/resource/questions/<query>') |
343 |
@app.route('/resource/ideas') |
344 |
@app.route('/resource/ideas/<query>') |
345 |
@app.route('/resource/problems') |
346 |
@app.route('/resource/problems/<query>') |
347 |
@app.route('/resource') |
348 |
def resource_list(query=None): |
349 |
""" |
350 |
Feedback Resource Browser (list) |
351 |
""" |
352 |
page=request.args.get('page') |
353 |
if page: |
354 |
page=int(page) |
355 |
else: |
356 |
return redirect(request.path + "?page=1") |
357 |
|
358 |
if request.path=="/resource": |
359 |
resource='all' |
360 |
else: |
361 |
resource=split(request.path, "/")[2] |
362 |
|
363 |
if resource=='search': |
364 |
resource = session.get('last_resource') or 'all' |
365 |
|
366 |
if resource=='questions': |
367 |
resource_object=g.Questions |
368 |
resource_label=_("questions") |
369 |
elif resource=='problems': |
370 |
resource_object=g.Problems |
371 |
resource_label=_("problems") |
372 |
elif resource=='ideas': |
373 |
resource_object=g.Ideas |
374 |
resource_label=_("ideas") |
375 |
elif resource=='reviews': |
376 |
resource_object=g.Reviews |
377 |
resource_label=_("reviews") |
378 |
elif resource=='all': |
379 |
resource_object=g.Resources |
380 |
resource_label=_("resources") |
381 |
resource='all_' # avoid chop |
382 |
resource_type=resource[:-1] |
383 |
|
384 |
session['last_resource']=resource |
385 |
session.modified = True |
386 |
|
387 |
try: |
388 |
r, total_pages, total, info = paginate(resource_object, |
389 |
query, page=page, _PAGE_SIZE=4) |
390 |
except KeyError: |
391 |
return redirect(url_for('resource_browser', |
392 |
context_guid=context_guid, page=1)) |
393 |
|
394 |
meta=_("browsing %(total)s %(resource_label)s", |
395 |
total=total, resource_label=resource_label) |
396 |
|
397 |
if '_pjax' in request.args: |
398 |
template='_resource-list.html' |
399 |
else: |
400 |
template='resource-list.html' |
401 |
kwargs = {str(resource): 'button_selected'} |
402 |
return render_template(template, query=query, resource=resource, |
403 |
result=r, type='resource', page=page, info=info, |
404 |
total_pages=total_pages, meta=meta, resource_view='true', |
405 |
resource_type=resource_type, **kwargs) |
406 |
|
407 |
@app.errorhandler(404) |
408 |
def page_not_found(error): |
409 |
template='browser-view.html' |
410 |
return render_template(template, total=0, info=_('Error'), resource_type='context', |
411 |
query='', total_pages=0, browser_view='true', |
412 |
result=[], type='context', meta=_('Object not found.'), page=1) |
413 |
|
414 |
@app.errorhandler(500) |
415 |
def page_not_found(error): |
416 |
template='browser-view.html' |
417 |
return render_template(template, total=0, info=_('Error'), resource_type='context', |
418 |
query='', total_pages=0, browser_view='true', |
419 |
result=[], type='context', meta=_('Server error.'), page=1) |
420 |
|
421 |
@app.route('/resource/contexts/<query>') |
422 |
@app.route('/resource/contexts/') |
423 |
@app.route('/context/search/') |
424 |
@app.route('/context/search/<query>') |
425 |
@app.route('/context') |
426 |
def context_grid(query=None, page=None): |
427 |
""" |
428 |
Context Grid |
429 |
""" |
430 |
try: |
431 |
page=int(request.args['page']) |
432 |
session['page']=page |
433 |
session.modified = True |
434 |
except KeyError: |
435 |
return redirect(url_for('context_grid', |
436 |
type=request.args.get('type'), |
437 |
query=query, page=session.get('page',1))) |
438 |
|
439 |
#try: |
440 |
# terms = session['tags'][:] |
441 |
#except KeyError: |
442 |
terms = [] |
443 |
|
444 |
type=request.args.get('type') or session.get('current_type') |
445 |
if query: |
446 |
terms.append(query) |
447 |
type='all' |
448 |
|
449 |
if type in ['activity']: |
450 |
context_cursor = g.Activities |
451 |
resource_label=_('activities') |
452 |
elif type in ['project']: |
453 |
context_cursor = g.Projects |
454 |
resource_label=_('projects') |
455 |
else: # type in [None, '', 'all']: |
456 |
type='all' |
457 |
context_cursor = g.Contexts |
458 |
resource_label=_('contexts') |
459 |
session['current_type']=type |
460 |
|
461 |
full_query = join(terms, " AND ") |
462 |
|
463 |
try: |
464 |
r, total_pages, total, info = paginate(context_cursor, full_query, page=page) |
465 |
except KeyError: |
466 |
if '_pjax' in request.args: |
467 |
return '' |
468 |
else: |
469 |
return redirect(url_for('context_grid',resource_type='context', |
470 |
query=query, page=1)) |
471 |
|
472 |
meta = _("browsing %(total)s %(label)s", total=total, label=resource_label) |
473 |
|
474 |
if '_pjax' in request.args: |
475 |
template='_browser-grid.html' |
476 |
else: |
477 |
template='browser-view.html' |
478 |
kwargs = {str(type): 'tab_selected button_selected'} |
479 |
return render_template(template, total=total, meta=meta, resource_type='context', |
480 |
query=query, total_pages=total_pages, browser_view='true', |
481 |
result=r, type=type, info=info, page=page, **kwargs) |
482 |
|
483 |
@app.route('/user/search/') |
484 |
@app.route('/user/search/<query>') |
485 |
@app.route('/user') |
486 |
def users_grid(query=None): |
487 |
""" |
488 |
Users Grid |
489 |
""" |
490 |
result = g.client.User.cursor(query) |
491 |
return render_template('users-grid.html',query=query, |
492 |
result=result, type='user') |
493 |
|
494 |
@app.route('/_comments/<resource_guid>', methods=['DELETE']) |
495 |
def del_comment(resource_guid): |
496 |
g.client.Comment.delete(resource_guid) |
497 |
return "true" |
498 |
|
499 |
@app.route('/_comments/<resource_guid>') |
500 |
def comments_browser(resource_guid=None): |
501 |
resource_type = request.args.get('resource_type') |
502 |
Comments.filter(parent=resource_guid) |
503 |
result = Comments |
504 |
if not result: |
505 |
result = [] |
506 |
return render_template('_context-comment-list.html', |
507 |
result=result, resource_guid=resource_guid) |
508 |
|
509 |
@app.route('/article/<context_guid>') |
510 |
def project_browser(context_guid=None): |
511 |
inner_template='_context-article-view.html' |
512 |
if '_pjax' in request.args: |
513 |
template=inner_template |
514 |
context=None |
515 |
else: |
516 |
template='context-view.html' |
517 |
context=g.client.Context(context_guid, |
518 |
reply=['guid', 'title', 'description', 'author', 'summary', 'user', 'keep', 'keep_impl', 'type']) |
519 |
try: |
520 |
session['last_context_title']=context['title'] |
521 |
except RuntimeError: |
522 |
abort(404) |
523 |
session['last_context']=context['guid'] |
524 |
session.modified = True |
525 |
|
526 |
return render_template(template, context=context, |
527 |
inner_template=inner_template) |
528 |
|
529 |
|
530 |
@app.route('/context/reviews/<resource_guid>') |
531 |
@app.route('/context/wikiwiki/<resource_guid>') |
532 |
@app.route('/context/gallery/<resource_guid>') |
533 |
@app.route('/review/<review_guid>') |
534 |
def reviews_browser(resource_guid=None, review_guid=None): |
535 |
if review_guid: |
536 |
r = g.client.Review(guid=review_guid, reply=['context']) |
537 |
resource_guid = r['context'] |
538 |
return redirect ('/context/reviews/'+resource_guid) |
539 |
g.Reviews.filter(context=resource_guid, type='review') |
540 |
|
541 |
inner_template='_context-review-list.html' |
542 |
if '_pjax' in request.args: |
543 |
template=inner_template |
544 |
context=None |
545 |
else: |
546 |
template='context-view.html' |
547 |
context=g.client.Context(resource_guid, |
548 |
reply=['guid', 'title', 'description', 'author', 'summary', 'user', 'keep', 'keep_impl', 'type']) |
549 |
try: |
550 |
session['last_context_title']=context['title'] |
551 |
except RuntimeError: |
552 |
abort(404) |
553 |
session['last_context']=context['guid'] |
554 |
session.modified = True |
555 |
|
556 |
kwargs = {'reviews':'button_selected'} |
557 |
return render_template(template, context=context, |
558 |
result=g.Reviews, inner_template=inner_template, |
559 |
resource_label=_('reviews'),resource_type="review", **kwargs |
560 |
) |
561 |
|
562 |
@app.route('/question/<resource_guid>') |
563 |
@app.route('/idea/<resource_guid>') |
564 |
@app.route('/problem/<resource_guid>') |
565 |
def solution_browser(resource_guid=None): |
566 |
resource_type=split(request.path, "/")[1] |
567 |
if resource_type=='question': |
568 |
resource_cursor=g.Questions |
569 |
resource_class=g.client.Question |
570 |
elif resource_type=='problem': |
571 |
resource_cursor=g.Problems |
572 |
resource_class=g.client.Problem |
573 |
elif resource_type=='idea': |
574 |
resource_cursor=g.Ideas |
575 |
resource_class=g.client.Idea |
576 |
|
577 |
offset=int(request.args.get('cursor_offset') or 0) |
578 |
if offset: |
579 |
resource=resource_cursor[offset] |
580 |
else: |
581 |
resource=resource_class(resource_guid, |
582 |
reply=['guid', 'title', 'content', 'author', 'user', 'context', 'tags', 'mtime']) |
583 |
|
584 |
g.Solutions.filter(parent=resource['guid']) |
585 |
|
586 |
inner_template='_context-solution-list.html' |
587 |
if '_pjax' in request.args: |
588 |
template=inner_template |
589 |
context=None |
590 |
else: |
591 |
template='context-view.html' |
592 |
context=g.client.Context(resource['context'], |
593 |
reply=['guid', 'title', 'description', 'summary', 'author', 'keep', 'keep_impl', 'type']) |
594 |
|
595 |
return render_template(template, context=context, |
596 |
result=g.Solutions, inner_template=inner_template, |
597 |
resource=resource, resource_type=resource_type, |
598 |
cursor_offset=offset) |
599 |
|
600 |
@app.route('/context/view/<context_guid>/<query>') |
601 |
@app.route('/context/view/<context_guid>') |
602 |
@app.route('/context/questions/<context_guid>') |
603 |
@app.route('/context/ideas/<context_guid>') |
604 |
@app.route('/context/problems/<context_guid>') |
605 |
@app.route('/context/all/<context_guid>') |
606 |
def resource_browser(context_guid=None, query=None): |
607 |
""" |
608 |
Context Resources View |
609 |
""" |
610 |
page=request.args.get('page') |
611 |
if page: |
612 |
page=int(page) |
613 |
else: |
614 |
return redirect(request.path + "?page=1") |
615 |
|
616 |
resource_type=split(request.path, "/")[2][:-1] |
617 |
if resource_type=='question': |
618 |
resource_object = g.Questions |
619 |
resource_label = _("questions") |
620 |
elif resource_type=='problem': |
621 |
resource_object = g.Problems |
622 |
resource_label = _("problems") |
623 |
elif resource_type=='idea': |
624 |
resource_object = g.Ideas |
625 |
resource_label = _("ideas") |
626 |
elif resource_type=='al': |
627 |
resource_object = g.Resources |
628 |
resource_label = _("resources") |
629 |
resource_type='all' |
630 |
|
631 |
try: |
632 |
r, total_pages, total, info = paginate(resource_object, |
633 |
query, page=page, _PAGE_SIZE=4, context=context_guid) |
634 |
except KeyError: |
635 |
return redirect(url_for('resource_browser', |
636 |
context_guid=context_guid, page=1)) |
637 |
|
638 |
meta=_("browsing %(total)s %(resource_label)s", |
639 |
total=total, resource_label=resource_label) |
640 |
|
641 |
offset=int(request.args.get('cursor_offset') or 0) |
642 |
if offset: |
643 |
context=resource_cursor[offset] |
644 |
else: |
645 |
context=g.client.Context(context_guid, reply=['guid', 'title', 'author', 'summary', 'description', 'keep', 'keep_impl', 'type']) |
646 |
try: |
647 |
session['last_context']=context['guid'] |
648 |
session['last_context_title']=context['title'] |
649 |
session.modified = True |
650 |
except RuntimeError: |
651 |
abort(404) |
652 |
|
653 |
stroke, fill = get_colors() |
654 |
if '_pjax' in request.args: |
655 |
template='_context-resource-list.html' |
656 |
_pjax=True |
657 |
else: |
658 |
template='context-view.html' |
659 |
_pjax=False |
660 |
kwargs = {str(resource_type) + 's': 'button_selected'} |
661 |
return render_template(template, result=r, |
662 |
total_pages=total_pages, info=info, meta=meta, |
663 |
resource_label=resource_label, stroke=stroke, |
664 |
resource_type=resource_type, context=context, |
665 |
page=page, fill=fill, _pjax=_pjax, **kwargs) |
666 |
|
667 |
@app.route('/submit_edit', methods=['POST']) |
668 |
def edit_resource(): |
669 |
resource_type = request.form['resource_type'] |
670 |
resource_guid = request.form['edit_guid'] |
671 |
if resource_type=='question': |
672 |
resource = g.client.Question(resource_guid) |
673 |
resource_cursor = g.Questions |
674 |
elif resource_type=='idea': |
675 |
resource = g.client.Idea(resource_guid) |
676 |
resource_cursor = g.Ideas |
677 |
elif resource_type=='problem': |
678 |
resource = g.client.Problem(resource_guid) |
679 |
resource_cursor = g.Problems |
680 |
elif resource_type=='review': |
681 |
resource = g.client.Review(resource_guid) |
682 |
resource_cursor = g.Reviews |
683 |
elif resource_type=='solution': |
684 |
resource = g.client.Solution(resource_guid) |
685 |
resource_cursor = g.Solutions |
686 |
|
687 |
if request.form.get('title'): |
688 |
resource['title']=request.form['title'] |
689 |
if request.form.get('content'): |
690 |
resource['content']=request.form['content'] |
691 |
try: |
692 |
resource.post() |
693 |
except NotFound: |
694 |
abort(500) |
695 |
resource_cursor._reset() |
696 |
return redirect(request.form.get('href')) |
697 |
|
698 |
@app.route('/submit_resource/<resource_type>', methods=['POST']) |
699 |
def new_resource(resource_type): |
700 |
if resource_type=='question': |
701 |
resource = g.client.Question() |
702 |
resource_cursor = g.Questions |
703 |
elif resource_type=='idea': |
704 |
resource = g.client.Idea() |
705 |
resource_cursor = g.Ideas |
706 |
elif resource_type=='problem': |
707 |
resource = g.client.Problem() |
708 |
resource_cursor = g.Problems |
709 |
resource['content'] = request.form['content'] |
710 |
resource['title'] = request.form['title'] |
711 |
resource['context'] = request.form['guid'] |
712 |
resource['type'] = resource_type |
713 |
if resource['title']: |
714 |
resource.post() |
715 |
resource_cursor._reset() |
716 |
return redirect('/context/%ss/%s' % (resource_type, resource['context'])) |
717 |
|
718 |
@app.route('/update_context', methods=['POST']) |
719 |
def update_context(): |
720 |
context=g.client.Context(request.form['edit_guid']) |
721 |
context['title'] = request.form['title'] |
722 |
context['summary'] = request.form['summary'] |
723 |
context['description'] = request.form['content'] |
724 |
if context['title'] and context['description'] and context['summary']: |
725 |
context.post() |
726 |
return redirect('/article/%s' % request.form['edit_guid']) |
727 |
|
728 |
@app.route('/submit_context', methods=['POST']) |
729 |
def new_context(): |
730 |
context=g.client.Context() |
731 |
context['type'] = ['project'] |
732 |
context['title'] = request.form['title'] |
733 |
context['summary'] = request.form['summary'] |
734 |
context['description'] = request.form['content'] |
735 |
if context['title'] and context['description'] and context['summary']: |
736 |
context.post() |
737 |
g.Contexts._reset() |
738 |
return redirect(url_for('context_grid', page=1, type='project')) |
739 |
|
740 |
@app.route('/submit_solution', methods=['POST']) |
741 |
def new_solution(): |
742 |
solution=g.client.Solution() |
743 |
solution['content'] = request.form['solution'] |
744 |
solution['parent'] = request.form['resource_guid'] |
745 |
solution['parent_resource'] = request.form['resource_type'] |
746 |
if solution['content']: |
747 |
solution.post() |
748 |
g.Solutions._reset() |
749 |
return redirect('/%s/%s' % (solution['parent_resource'], solution['parent'])) |
750 |
|
751 |
@app.route('/submit_review', methods=['POST']) |
752 |
def new_review(): |
753 |
review=g.client.Review() |
754 |
review['content'] = request.form['review'] |
755 |
review['title'] = '' |
756 |
review['type'] = 'review' |
757 |
context = review['context'] = request.form['resource_guid'] |
758 |
if review['content']: |
759 |
review.post() |
760 |
g.Reviews._reset() |
761 |
return redirect('/context/reviews/%s' % context) |
762 |
|
763 |
@app.route('/submit_report', methods=['POST']) |
764 |
def new_report(): |
765 |
report = g.client.Report() |
766 |
report['context'] = request.form['context'] |
767 |
report['implementation'] = request.form['implementation'] |
768 |
report['description'] = request.form['content'] |
769 |
report.post() |
770 |
|
771 |
filename = request.form.get('filename') |
772 |
if filename: |
773 |
report.upload_blob('data', filename) |
774 |
|
775 |
return redirect('/resource/problems') |
776 |
|
777 |
@app.route('/submit_comment', methods=['POST']) |
778 |
def new_comment(): |
779 |
comment=g.client.Comment() |
780 |
comment['message'] = request.form['comment'] |
781 |
comment['parent'] = request.form['resource_guid'] |
782 |
comment['parent_resource'] = request.form['resource_type'] |
783 |
if comment['message']: |
784 |
comment.post() |
785 |
g.Comments._reset() |
786 |
return redirect('_comments/%s?resource_type=%s' % |
787 |
(comment['parent'], comment['parent_resource']) ) |
788 |
|
789 |
@app.route('/_events/comment') |
790 |
@app.route('/_events/solution') |
791 |
def event_comment(): |
792 |
resource_type=split(request.path, "/")[2] |
793 |
if resource_type=='comment': |
794 |
cursor = g.Comments |
795 |
elif resource_type=='solution': |
796 |
cursor = g.Solutions |
797 |
def feed(): |
798 |
for i in cursor.read_events(): |
799 |
yield 'data: %s\n\n' % i |
800 |
return Response(feed(), mimetype='text/event-stream', |
801 |
direct_passthrough=True) |
802 |
|
803 |
@app.route('/_events/network') |
804 |
def event_network(): |
805 |
def feed(): |
806 |
while True: |
807 |
gevent.sleep(1) |
808 |
for i in g.Solutions.read_events(): |
809 |
yield 'data: %s\n\n' % i |
810 |
return Response(feed(), mimetype='text/event-stream', |
811 |
direct_passthrough=True) |
812 |
|
813 |
@app.route('/_shutdown') |
814 |
def shutdown_server(): |
815 |
func = request.environ.get('werkzeug.server.shutdown') |
816 |
if func is None: |
817 |
raise RuntimeError('Not running with the Werkzeug Server') |
818 |
func() |
819 |
return 'Goodbye' |
820 |
|
821 |
@app.route('/_debug') |
822 |
def debug(): |
823 |
raise Warning('All your base are belong to us.') |
824 |
return 'Take off every Zig' |
825 |
|
826 |
if __name__=="__main__": |
827 |
host = '0.0.0.0' #'0.0.0.0' for all |
828 |
|
829 |
try: |
830 |
port=int(sys.argv[1]) |
831 |
except IndexError: |
832 |
port=5000 |
833 |
|
834 |
app.debug = True |
835 |
from gevent.wsgi import WSGIServer |
836 |
http_server = WSGIServer((host, port), app) |
837 |
http_server.serve_forever() |
838 |
#app.run(host=host, port=port) |