Web · Wiki · Activities · Blog · Lists · Chat · Meeting · Bugs · Git · Translate · Archive · People · Donate
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)