#308 Split off the views in their own module
Merged 7 years ago by abompard. Opened 7 years ago by abompard.
abompard/fedora-hubs split-views  into  develop

file modified
+6 -730
@@ -1,23 +1,17 @@ 

- from __future__ import print_function, unicode_literals

+ from __future__ import unicode_literals

  

  import datetime

- import functools

  import json

  import logging

  import os

- import six

- from six.moves.urllib import parse as urlparse

  

  import flask

  import flask.json

  import munch

  

  from flask.ext.oidc import OpenIDConnect

- import requests

  

  import hubs.models

- import hubs.widgets

- import hubs.stream

  

  import datanommer.models

  
@@ -36,11 +30,6 @@ 

  

  logging.basicConfig()

  

- # TODO - put this in config so we can migrate to pagure

- # TODO - instead of 'develop', use the version from pkg_resources to figure out

- #        the right tag to link people to.  AGPL ftw.

- SOURCE_URL = 'https://pagure.io/fedora-hubs/blob/develop/f'  # /hubs/widgets/badges.py'

- 

  app.config.from_object('hubs.default_config')

  if 'HUBS_CONFIG' in os.environ:

      app.config.from_envvar('HUBS_CONFIG')
@@ -54,6 +43,10 @@ 

  session = hubs.models.init(fedmsg_config['hubs.sqlalchemy.uri'])

  datanommer.models.init(fedmsg_config['datanommer.sqlalchemy.uri'])

  

+ @app.before_request

+ def store_db():

+     flask.g.db = session

+ 

  

  class CustomJSONEncoder(flask.json.JSONEncoder):

      def default(self, o):
@@ -69,24 +62,6 @@ 

  app.json_encoder = CustomJSONEncoder

  

  

- def authenticated():

-     """ Utility function checking if the current auth is set or not."""

-     return hasattr(flask.g, 'auth') \

-         and flask.g.auth is not None \

-         and flask.g.auth.logged_in

- 

- 

- def is_safe_url(target):

-     """ Checks that the target url is safe and sending to the current

-     website not some other malicious one.

-     """

-     ref_url = urlparse.urlparse(flask.request.host_url)

-     test_url = urlparse.urlparse(

-         urlparse.urljoin(flask.request.host_url, target))

-     return test_url.scheme in ('http', 'https') and \

-         ref_url.netloc == test_url.netloc

- 

- 

  @app.template_filter('commas')

  def commas(numeric):

      return "{:,.2f}".format(numeric)
@@ -98,490 +73,10 @@ 

      session.remove()

  

  

- @app.route('/')

- def index():

-     if not authenticated():

-         return flask.redirect(flask.url_for('login'))

- 

-     return flask.redirect(flask.url_for('stream', name=flask.g.auth.nickname))

- 

- 

- @app.route('/groups')

- def groups():

-     if not authenticated():

-         return flask.redirect(flask.url_for('login'))

- 

-     # Get the list of promoted and non-promoted group hubs from the DB

-     promoted_names = app.config.get('PROMOTED_GROUPS')

-     groups = hubs.models.Hub.all_group_hubs(session)

-     promoted = [g for g in groups if g.name in promoted_names]

-     secondary = [g for g in groups if g.name not in promoted_names]

- 

-     name_of_the_month = app.config.get('HUB_OF_THE_MONTH')

-     hub_of_the_month = hubs.models.Hub.by_name(session, name_of_the_month)

- 

-     return flask.render_template(

-         'groups.html',

-         promoted=promoted,

-         secondary=secondary,

-         hub_of_the_month=hub_of_the_month,

-         session=session,

-     )

- 

- 

- @app.route('/<name>')

- @app.route('/<name>/')

- def hub(name):

-     hub = get_hub(session, name)

-     return flask.render_template(

-         'hubs.html', hub=hub, session=session, edit=False)

- 

- 

- @app.route('/<name>/json/')

- @app.route('/<name>/json')

- def hub_json(name):

-     hub = get_hub(session, name)

-     response = flask.jsonify(hub.__json__(session))

-     # TODO -- modify headers with response.headers['X-fedora-hubs-wat'] = 'foo'

-     return response

- 

- 

- @app.route('/<name>/edit/', methods=['GET', 'POST'])

- @app.route('/<name>/edit', methods=['GET', 'POST'])

- def hub_edit(name):

-     if flask.request.method == 'POST':

-         return hub_edit_post(name)

-     else:

-         return hub_edit_get(name)

- 

- 

- def hub_edit_get(name):

-     hub = get_hub(session, name)

-     return flask.render_template(

-         'hubs.html', hub=hub, session=session, edit=True)

- 

- 

- def hub_edit_post(name):

-     hub = get_hub(session, name)

-     is_js = flask.request.form.get('js', False)

- 

-     error = False

- 

-     # Check the input submitted

-     # Right side

-     r_widget_ids = [

-         w.strip().replace('widget-', '')

-         for w in flask.request.form.getlist('right_widgets[]')

-         if w.strip()

-         ]

-     try:

-         r_widget_ids = [int(w) for w in r_widget_ids]

-     except:

-         flask.flash('Invalid widget identifiers submitted', 'error')

-         error = True

- 

-     r_indexes = [

-         i.strip() for i in flask.request.form.getlist('right_indexes[]')

-         if i.strip()

-         ]

- 

-     try:

-         r_indexes = [int(i) for i in r_indexes]

-     except:

-         if not is_js:

-             flask.flash('Invalid indexes submitted', 'error')

-         error = True

- 

-     if len(r_widget_ids) != len(r_indexes):

-         if not is_js:

-             flask.flash(

-                 'The number of indexes and the number of widgets are not of '

-                 'the same length', 'error')

-         error = True

- 

-     # Left side

-     l_widget_ids = [

-         w.strip().replace('widget-', '')

-         for w in flask.request.form.getlist('left_widgets[]')

-         if w.strip()

-         ]

-     try:

-         l_widget_ids = [int(w) for w in l_widget_ids]

-     except:

-         flask.flash('Invalid widget identifiers submitted', 'error')

-         error = True

- 

-     l_indexes = [

-         i.strip() for i in flask.request.form.getlist('left_indexes[]')

-         if i.strip()

-         ]

- 

-     try:

-         l_indexes = [int(i) for i in l_indexes]

-     except:

-         if not is_js:

-             flask.flash('Invalid indexes submitted', 'error')

-         error = True

- 

-     if len(l_widget_ids) != len(l_indexes):

-         if not is_js:

-             flask.flash(

-                 'The number of indexes and the number of widgets are not of '

-                 'the same length', 'error')

-         error = True

- 

-     # If all good, update the database

-     if not error:

-         for cnt, wid in enumerate(r_widget_ids):

-             widget = hubs.models.Widget.get(session, wid)

-             if widget.index != r_indexes[cnt]:

-                 widget.index = r_indexes[cnt]

-                 session.add(widget)

-         for cnt, wid in enumerate(l_widget_ids):

-             widget = hubs.models.Widget.get(session, wid)

-             if widget.index != l_indexes[cnt]:

-                 widget.index = l_indexes[cnt]

-                 session.add(widget)

-         hub.last_edited = datetime.datetime.utcnow()

-         session.add(hub)

-         try:

-             session.commit()

-         except Exception as err:

-             error = True

-             if not is_js:

-                 flask.flash(

-                     'Could not save your changes to the database '

-                     'if the error persists, please warn an admin',

-                     'error')

-     if is_js:

-         if error:

-             flask.abort(400)

-         else:

-             return ('ok', 200)

-     else:

-         return flask.redirect(flask.url_for('hub', name=name))

- 

- 

- @app.route('/<name>/add/', methods=['GET', 'POST'])

- @app.route('/<name>/add', methods=['GET', 'POST'])

- def hub_add_widgets(name):

-     if flask.request.method == 'POST':

-         return hub_add_widget_post(name)

-     else:

-         return hub_add_widget_get(name)

- 

- 

- @app.route('/<name>/add/<widget_name>/', methods=['GET'])

- @app.route('/<name>/add/<widget_name>', methods=['GET'])

- def hub_add_widget(name, widget_name):

-     hub = get_hub(session, name)

-     side = str(flask.request.args.get('position')).lower()

-     if side not in ['right', 'left']:

-         flask.abort(400, 'Invalid position provided')

- 

-     widget = None

-     w_name = None

-     for widgt in hubs.widgets.registry:

-         if hubs.widgets.registry[widgt].position in ['both', side] \

-                 and widgt == widget_name:

-             w_name = widgt

-             widget = hubs.widgets.registry[widgt]

- 

-     return flask.render_template(

-         'add_widget.html',

-         hub=hub,

-         widget=widget,

-         widget_name=w_name,

-         side=side,

-         url_to=flask.url_for('hub_add_widgets', name=name),

-     )

- 

- 

- def hub_add_widget_get(name):

-     hub = get_hub(session, name)

-     side = str(flask.request.args.get('position')).lower()

-     if side not in ['right', 'left']:

-         flask.abort(400, 'Invalid position provided')

- 

-     widgets = [

-         widget

-         for widget in hubs.widgets.registry

-         if hubs.widgets.registry[widget].position in ['both', side]

-         ]

-     return flask.render_template(

-         'add_widget.html',

-         hub=hub,

-         widgets=widgets,

-         side=side,

-     )

- 

- 

- def hub_add_widget_post(name):

-     widget_name = flask.request.form.get('widget_name')

-     position = flask.request.form.get('position', '').lower()

-     if not widget_name:

-         flask.abort(400, 'Invalid request sent')

-     if widget_name not in hubs.widgets.registry:

-         flask.abort(404, 'Unknown widget called')

- 

-     hub = get_hub(session, name)

- 

-     widget = widget = hubs.models.Widget(

-         plugin=widget_name, index=-1, left=position == 'left')

-     error = False

-     config = {}

-     for arg in widget.module.data.widget_arguments:

-         val = flask.request.form.get(arg.name)

-         if not val:

-             flask.flash(

-                 'You must provide a value for: %s' % arg.name, 'error')

-             error = True

-             break

-         try:

-             arg.validator(session, val)

-             config[arg.name] = val

-         except Exception as err:

-             flask.flash('Invalid data provided, error: %s' % err, 'error')

-             error = True

-     if not error:

-         widget.hub = hub

-         widget.config = config

-         try:

-             session.add(widget)

-             session.flush()

-             widget.hub.last_edited = datetime.datetime.utcnow()

-             session.commit()

-         except Exception as err:

-             print(err)

-             flask.flash(

-                 'Could not save the configuration to the database '

-                 'if the error persists, please warn an admin',

-                 'error')

- 

-     return flask.render_template(

-         'hubs.html', hub=hub, session=session, edit=True)

- 

- 

- @app.route('/<hub>/<int:idx>/')

- @app.route('/<hub>/<int:idx>')

- def widget_render(hub, idx):

-     widget = get_widget(session, hub, idx)

-     return widget.render(session)  # , edit=False)

-     # was blocking all widgets from working, sorry!

- 

- 

- @app.route('/<hub>/<int:idx>/json')

- @app.route('/<hub>/<int:idx>/json/')

- def widget_json(hub, idx):

-     widget = get_widget(session, hub, idx)

-     response = flask.jsonify(widget.__json__(session))

-     # TODO -- modify headers with response.headers['X-fedora-hubs-wat'] = 'foo'

-     return response

- 

- 

- @app.route('/<hub>/<int:idx>/edit/', methods=['GET', 'POST'])

- @app.route('/<hub>/<int:idx>/edit', methods=['GET', 'POST'])

- def widget_edit(hub, idx):

-     if flask.request.method == 'POST':

-         return widget_edit_post(hub, idx)

-     else:

-         return widget_edit_get(hub, idx)

- 

- 

- def widget_edit_get(hub, idx):

-     widget = get_widget(session, hub, idx)

-     return flask.render_template(

-         'edit.html',

-         hub=hub,

-         widget=widget,

-         url_to=flask.url_for('widget_edit', hub=hub, idx=idx)

-     )

- 

- 

- def widget_edit_post(hub, idx):

-     widget = get_widget(session, hub, idx)

-     error = False

-     config = {}

-     for arg in widget.module.data.widget_arguments:

-         val = flask.request.form.get(arg.name)

-         if not val:

-             flask.flash(

-                 'You must provide a value for: %s' % arg.name, 'error')

-             error = True

-             break

-         try:

-             val = arg.validator(session, val)

-             config[arg.name] = val

-         except Exception as err:

-             flask.flash('Invalid data provided, error: %s' % err, 'error')

-             error = True

-     if not error:

-         cur_config = widget.config

-         cur_config.update(config)

-         widget.config = cur_config

-         widget.hub.last_edited = datetime.datetime.utcnow()

-         session.add(widget)

-         try:

-             session.commit()

-         except Exception as err:

-             flask.flash(

-                 'Could not save the configuration to the database '

-                 'if the error persists, please warn an admin',

-                 'error')

-     return flask.redirect(flask.url_for('hub', name=hub))

- 

- 

- @app.route('/<hub>/<int:idx>/delete/', methods=['POST'])

- @app.route('/<hub>/<int:idx>/delete', methods=['POST'])

- def widget_edit_delete(hub, idx):

-     ''' Remove a widget from a hub. '''

-     widget = get_widget(session, hub, idx)

-     session.delete(widget)

-     try:

-         session.commit()

-     except Exception as err:

-         flask.flash(

-             'Could not delete this widget from this hub in the database '

-             'if the error persists, please warn an admin',

-             'error')

-     return flask.redirect(flask.url_for('hub_edit', name=hub))

- 

- 

- @app.route('/source/<name>/')

- @app.route('/source/<name>')

- def widget_source(name):

-     from hubs.widgets import registry

-     base = '/hubs/'

-     fname = ''

- 

-     try:

-         fname = base + registry[name].__file__.split(base)[-1]

-     except KeyError:

-         flask.abort(404)

- 

-     fname = fname.replace('.pyc', '.py')

-     return flask.redirect(SOURCE_URL + fname)

- 

- 

  # Set up OpenIDConnect

  OIDC = OpenIDConnect(app, credentials_store=flask.session )

  

  

- @app.route('/login/', methods=('GET', 'POST'))

- @app.route('/login', methods=('GET', 'POST'))

- @OIDC.require_login

- def login():

-     default = flask.url_for('index')

-     next_url = flask.request.args.get('next', default)

-     if is_safe_url(next_url):

-         return_point = next_url

-     else:

-         return_point = default

- 

-     hubs.models.User.get_or_create(

-         session, username=flask.g.auth.nickname, fullname=flask.g.auth.fullname)

- 

-     return flask.redirect(return_point)

- 

- 

- @app.route('/logout/')

- @app.route('/logout')

- def logout():

-     next_url = flask.url_for('index')

-     if 'next' in flask.request.values:  # pragma: no cover

-         if is_safe_url(flask.request.args['next']):

-             next_url = flask.request.values['next']

- 

-     if next_url == flask.url_for('login'):  # pragma: no cover

-         next_url = flask.url_for('index')

- 

-     if authenticated():

-         OIDC.logout()

-         flask.session.auth = None

-         flask.flash('You have been logged out')

- 

-     return flask.redirect(next_url)

- 

- 

- def login_required(function):

-     """ Flask decorator to restrict access to logged-in users. """

- 

-     @functools.wraps(function)

-     def decorated_function(*args, **kwargs):

-         """ Decorated function, actually does the work. """

-         if not authenticated():

-             flask.flash('Login required', 'errors')

-             return flask.redirect(flask.url_for(

-                 'login', next=flask.request.url))

- 

-         return function(*args, **kwargs)

- 

-     return decorated_function

- 

- @app.route('/<name>/stream')

- @app.route('/<name>/stream/')

- @login_required

- def stream(name):

-     hub = get_hub(session, name)

-     saved = hubs.models.SavedNotification.by_username(session, name)

-     saved = [n.__json__() for n in saved]

- 

-     stream = hubs.stream.Stream()

-     actions = stream.get_json()

- 

-     return flask.render_template(

-         'stream.html',

-         hub=hub,

-         saved=json.dumps(saved),

-         actions=actions

-     )

- 

- 

- @app.route('/<user>/notifications', methods=['GET', 'POST'])

- @app.route('/<user>/notifications/', methods=['GET', 'POST'])

- @login_required

- def notifications(user):

-     if flask.request.method == 'GET':

-         notifications = hubs.models.SavedNotification.by_username(session, user)

-         notifications = [n.__json__() for n in notifications]

-         return flask.jsonify(notifications)

- 

-     if flask.request.method == 'POST':

-         data = flask.request.get_json()

-         user = hubs.models.User.by_username(session, user)

-         if not user:

-             return flask.abort(400)

-         try:

-             markup = data['markup']

-             link = data['link']

-             icon = data['secondary_icon']

-             dom_id = data['dom_id']

-         except:

-             return flask.abort(400)

-         notification = hubs.models.SavedNotification(

-             username=user.username,

-             markup=markup,

-             link=link,

-             secondary_icon=icon,

-             dom_id=dom_id

-         )

-         session.add(notification)

-         session.commit()

-         return flask.jsonify(

-             dict(notification=notification.__json__(), success=True)

-         )

- 

- @app.route('/<user>/notifications/<int:idx>', methods=['DELETE'])

- @app.route('/<user>/notifications/<int:idx>/', methods=['DELETE'])

- @login_required

- def delete_notifications(user, idx):

-     notification = session.query(hubs.models.SavedNotification).filter_by(idx=idx).first()

-     if not notification:

-         return flask.abort(400)

-     session.delete(notification)

-     session.commit()

-     return flask.jsonify(dict(status_code=200))

- 

  @app.before_request

  def check_auth():

      flask.session.permanent = True
@@ -610,223 +105,4 @@ 

          flask.g.auth = munch.Munch(logged_in=False)

  

  

- def get_hub(session, name):

-     """ Utility shorthand to get a hub and 404 if not found. """

-     hub = session.query(hubs.models.Hub) \

-         .filter(hubs.models.Hub.name == name) \

-         .first()

- 

-     if not hub:

-         flask.abort(404)

- 

-     return hub

- 

- 

- @app.route('/visit/<visited_hub>/', methods=['GET', 'POST'])

- @app.route('/visit/<visited_hub>', methods=['GET', 'POST'])

- @login_required

- def increment_counter(visited_hub):

-     nickname = flask.g.auth.nickname

- 

-     if str(visited_hub) != str(nickname):

-         try:

-             vc = hubs.models.VisitCounter.get_or_create(session=session,

-                                                         username=nickname,

-                                                         visited_hub=visited_hub)

-         except ValueError:  # this should never trip

-             # flask will 405 if visited_hub is blank

-             # @login_required forces flask.g.auth to be sets

-             flask.abort(404)

-         if flask.request.method == 'POST':

-             vc.increment_visits(session=session,

-                                 username=nickname,

-                                 visited_hub=visited_hub)

- 

-             return flask.jsonify({'count': vc.count})

- 

-         elif flask.request.method == 'GET':

-             return flask.jsonify({'count': vc.count})

-     else:

-         return flask.abort(403)

- 

- 

- def get_widget(session, hub, idx):

-     """ Utility shorthand to get a widget and 404 if not found. """

-     try:

-         idx = int(idx)

-     except (TypeError, ValueError):

-         flask.abort(404)

- 

-     hub = get_hub(session, hub)

- 

-     for widget in hub.widgets:

-         if widget.idx == idx:

-             return widget

- 

-     flask.abort(404)

- 

- 

- # Here are a bunch of API methods that should probably be broken out into

- # their own file

- @app.route('/api/hub/<hub>/subscribe', methods=['POST'])

- @login_required

- def hub_subscribe(hub):

-     hub = get_hub(session, hub)

-     user = hubs.models.User.by_username(session, flask.g.auth.nickname)

-     hub.subscribe(session, user)

-     session.commit()

-     return flask.redirect(flask.url_for('hub', name=hub.name))

- 

- 

- @app.route('/api/hub/<hub>/unsubscribe', methods=['POST'])

- @login_required

- def hub_unsubscribe(hub):

-     hub = get_hub(session, hub)

-     user = hubs.models.User.by_username(session, flask.g.auth.nickname)

-     try:

-         hub.unsubscribe(session, user)

-     except KeyError:

-         return flask.abort(400)

-     session.commit()

-     return flask.redirect(flask.url_for('hub', name=hub.name))

- 

- 

- @app.route('/api/hub/<hub>/star', methods=['POST'])

- @login_required

- def hub_star(hub):

-     hub = get_hub(session, hub)

-     user = hubs.models.User.by_username(session, flask.g.auth.nickname)

-     hub.subscribe(session, user, role='stargazer')

-     session.commit()

-     return flask.redirect(flask.url_for('hub', name=hub.name))

- 

- 

- @app.route('/api/hub/<hub>/unstar', methods=['POST'])

- @login_required

- def hub_unstar(hub):

-     hub = get_hub(session, hub)

-     user = hubs.models.User.by_username(session, flask.g.auth.nickname)

-     try:

-         hub.unsubscribe(session, user, role='stargazer')

-     except KeyError:

-         return flask.abort(400)

-     session.commit()

-     return flask.redirect(flask.url_for('hub', name=hub.name))

- 

- 

- @app.route('/api/hub/<hub>/join', methods=['POST'])

- @login_required

- def hub_join(hub):

-     hub = get_hub(session, hub)

-     user = hubs.models.User.by_username(session, flask.g.auth.nickname)

-     hub.subscribe(session, user, role='member')

-     session.commit()

-     return flask.redirect(flask.url_for('hub', name=hub.name))

- 

- 

- @app.route('/api/hub/<hub>/leave', methods=['POST'])

- @login_required

- def hub_leave(hub):

-     hub = get_hub(session, hub)

-     user = hubs.models.User.by_username(session, flask.g.auth.nickname)

-     try:

-         hub.unsubscribe(session, user, role='member')

-     except KeyError:

-         return flask.abort(400)

-     session.commit()

-     return flask.redirect(flask.url_for('hub', name=hub.name))

- 

- 

- @app.route('/plus_plus/<user>/status', methods=['GET'])

- def plus_plus_status(user):

-     receiver = hubs.models.User.by_username(session, user)

-     if not receiver:

-         return 'User does not exist', 403

-     pp_url = app.config['PLUS_PLUS_URL'] + str(receiver.username)

-     req = None

-     try:

-         req = requests.get(pp_url)

-     except:

-         flask.abort(500)

-     if req.status_code == 200:

-         return flask.jsonify(req.json())

-     else:

-         return req.text, req.status_code

- 

- 

- def plus_plus_update_bool_helper(val):

-     if isinstance(val, bool):

-         return val

-     elif isinstance(val, six.string_types):

-         fmt_str = str(val).replace("'", "").replace('"', '').lower()

-         return fmt_str in ("yes", "true", "t", "1")

-     else:

-         raise ValueError

- 

- 

- @app.route('/plus_plus/<user>/update', methods=['POST'])

- @login_required

- def plus_plus_update(user):

-     receiver = hubs.models.User.by_username(session, user)

- 

-     if not receiver:

-         return 'User does not exist', 403

- 

-     if user == flask.g.auth.nickname:

-         return 'You may not modify your own karma.', 403

- 

-     if 'decrement' not in flask.request.form \

-             and 'increment' not in flask.request.form:

-         return "You must set 'decrement' or 'increment' " \

-                "with a boolean value in the body", 403

- 

-     update = 'increment' if 'increment' in flask.request.form else 'decrement'

- 

-     update_bool_val = plus_plus_update_bool_helper(flask.request.form[update])

-     pp_url = app.config['PLUS_PLUS_URL'] + str(receiver.username)

-     sender = hubs.models.User.by_username(session, flask.g.auth.nickname)

-     pp_token = app.config['PLUS_PLUS_TOKEN']

-     auth_header = {'Authorization': 'token {}'.format(pp_token)}

-     data = {'sender': sender.username, update: update_bool_val}

-     req = None

-     try:

-         req = requests.post(url=pp_url, headers=auth_header, data=data)

-     except:

-         flask.abort(500)

- 

-     if req.status_code == 200:

-         return flask.jsonify(req.json())

-     else:

-         return req.text, req.status_code

- 

- 

- #

- # Add widget-specific routes

- #

- 

- def _widget_view_decorator(func):

-     """

-     This internal decorator will edit the view function arguments.

- 

-     It will:

-     - remove the hub name and the widget primary key

-     - add the database session and the widget instance

-     """

-     @functools.wraps(func)

-     def inner(*args, **kwargs):

-         hubname = kwargs.pop("hub")

-         widgetidx = kwargs.pop("idx")

-         widget = get_widget(session, hubname, widgetidx)

-         return func(session, widget, *args, **kwargs)

-     return inner

- 

- for widget in session.query(hubs.models.Widget):

-     for params in getattr(widget.module, 'ROUTES', []):

-         params["rule"] = "/<hub>/<int:idx>/widget/" \

-                          + params["rule"].lstrip("/")

-         if not params.get("endpoint"):

-             params["endpoint"] = params["view_func_name"]

-         params["endpoint"] = "%s_%s" % (widget.plugin, params["endpoint"])

-         params["view_func"] = _widget_view_decorator(

-             getattr(widget.module, params.pop("view_func_name")))

-         app.add_url_rule(**params)

+ import hubs.views

file modified
+4
@@ -24,3 +24,7 @@ 

  OIDC_REQUIRE_VERIFIED_EMAIL = False

  OIDC_OPENID_REALM = 'http://localhost:5000/oidc_callback'

  OIDC_SCOPES = ['openid', 'email', 'profile', 'fedora']

+ 

+ # TODO - instead of 'develop', use the version from pkg_resources to figure out

+ #        the right tag to link people to.  AGPL ftw.

+ SOURCE_URL = 'https://pagure.io/fedora-hubs/blob/develop/f'  # /hubs/widgets/badges.py'

file modified
+7 -4
@@ -33,6 +33,7 @@ 

              hubs.app.fedmsg_config['hubs.sqlalchemy.uri'],

              create=True,

          )

+         hubs.app.app.config['TESTING'] = True

          self.app = hubs.app.app.test_client()

          self.app.testing = True

          self.session = hubs.app.session
@@ -106,11 +107,13 @@ 

      :param auth: A FakeAuthorization instance to set as g.auth.

      """

  

-     # Hack used to remove the before_request function set by

-     # flask.ext.fas_openid.FAS which otherwise kills our effort to set a

-     # flask.g.auth.

+     # Hack used to remove the check_auth function set on `before_request`

+     # which otherwise kills our effort to set a flask.g.auth.

      from flask import appcontext_pushed, g

-     APP.before_request_funcs[None] = []

+     APP.before_request_funcs[None] = [

+         f for f in APP.before_request_funcs[None]

+         if f.__name__ != 'check_auth'

+     ]

  

      def handler(sender, **kwargs):

          g.auth = auth

empty or binary file added
@@ -0,0 +1,67 @@ 

+ from __future__ import unicode_literals

+ 

+ import hubs.widgets

+ 

+ from mock import Mock

+ from hubs.models import Hub, Widget

+ from hubs.tests import APPTest

+ 

+ class WidgetRoutesTest(APPTest):

+ 

+     def setUp(self):

+         super(WidgetRoutesTest, self).setUp()

+         # Backup the URL map

+         self._old_url_map = (

+             self.app.application.url_map._rules.copy(),

+             self.app.application.url_map._rules_by_endpoint.copy()

+             )

+ 

+     def tearDown(self):

+         for module in hubs.widgets.registry.values():

+             if hasattr(module, 'ROUTES'):

+                 delattr(module, 'ROUTES')

+         # Restore the URL map

+         self.app.application.url_map._rules = self._old_url_map[0]

+         self.app.application.url_map._rules_by_endpoint = self._old_url_map[1]

+         super(WidgetRoutesTest, self).tearDown()

+ 

+     def test_routes_variable(self):

+         mock_view = Mock(return_value='')

+         hubs.widgets.contact.mock_view = mock_view

+         hubs.widgets.contact.ROUTES = [{

+             'rule': 'dummy-url',

+             'endpoint': 'dummy-endpoint',

+             'view_func_name': 'mock_view',

+         }]

+         hubs.views._load_widget_views()

+         added_rules = list(

+             self.app.application.url_map.iter_rules(

+                 endpoint='contact_dummy-endpoint')

+             )

+         self.assertEqual(len(added_rules), 1)

+         self.assertEqual(

+             added_rules[0].rule,

+             '/<hub>/<int:idx>/widget/dummy-url'

+             )

+         widget = self.session.query(Widget).filter_by(

+             plugin="contact").first()

+         self.app.get('/%s/%s/widget/dummy-url'

+                      % (widget.hub.name, widget.idx))

+         self.assertTrue(mock_view.called)

+         call = mock_view.call_args_list[0]

+         # Check arguments: session and widget

+         self.assertEqual(call[0][0].__class__, self.session.__class__)

+         self.assertEqual(call[0][1], widget)

+ 

+     def test_decorator(self):

+         mock_view = Mock(return_value='')

+         mock_view.__name__ = 'mock_view'

+         mock_view.__module__ = 'hubs.widgets.feed'

+         hubs.widgets.feed.mock_view = hubs.widgets.base.widget_route(

+             rule='dummy-url', endpoint='dummy-endpoint')(mock_view)

+         self.assertEqual(

+             hubs.widgets.feed.ROUTES,

+             [{'view_func_name': 'mock_view',

+               'endpoint': 'dummy-endpoint',

+               'rule': 'dummy-url'}]

+             )

@@ -0,0 +1,51 @@ 

+ from __future__ import absolute_import

+ 

+ from .root import *

+ from .hub import *

+ from .widget import *

+ from .user import *

+ from .api import *

+ from .plus_plus import *

+ 

+ #

+ # Add widget-specific routes

+ #

+ 

+ import flask

+ import functools

+ import hubs.models

+ import hubs.widgets

+ from sqlalchemy.orm.exc import NoResultFound

+ from hubs.app import app, session

+ from .utils import get_widget

+ 

+ def _widget_view_decorator(func):

+     """

+     This internal decorator will edit the view function arguments.

+ 

+     It will:

+     - remove the hub name and the widget primary key

+     - add the database session and the widget instance

+     """

+     @functools.wraps(func)

+     def inner(*args, **kwargs):

+         hubname = kwargs.pop("hub")

+         widgetidx = kwargs.pop("idx")

+         widget = get_widget(hubname, widgetidx)

+         return func(session, widget, *args, **kwargs)

+     return inner

+ 

+ def _load_widget_views():

+     for name, module in hubs.widgets.registry.items():

+         #print(module, hasattr(module, 'ROUTES'))

+         for params in getattr(module, 'ROUTES', []):

+             params["rule"] = "/<hub>/<int:idx>/widget/" \

+                              + params["rule"].lstrip("/")

+             if not params.get("endpoint"):

+                 params["endpoint"] = params["view_func_name"]

+             params["endpoint"] = "%s_%s" % (name, params["endpoint"])

+             params["view_func"] = _widget_view_decorator(

+                 getattr(module, params.pop("view_func_name")))

+             app.add_url_rule(**params)

+ 

+ _load_widget_views()

file added
+78
@@ -0,0 +1,78 @@ 

+ from __future__ import unicode_literals, absolute_import

+ 

+ 

+ import flask

+ import hubs.models

+ 

+ from hubs.app import app

+ from .utils import get_hub, login_required

+ 

+ 

+ @app.route('/api/hub/<hub>/subscribe', methods=['POST'])

+ @login_required

+ def hub_subscribe(hub):

+     hub = get_hub(hub)

+     user = hubs.models.User.by_username(flask.g.db, flask.g.auth.nickname)

+     hub.subscribe(flask.g.db, user)

+     flask.g.db.commit()

+     return flask.redirect(flask.url_for('hub', name=hub.name))

+ 

+ 

+ @app.route('/api/hub/<hub>/unsubscribe', methods=['POST'])

+ @login_required

+ def hub_unsubscribe(hub):

+     hub = get_hub(hub)

+     user = hubs.models.User.by_username(flask.g.db, flask.g.auth.nickname)

+     try:

+         hub.unsubscribe(flask.g.db, user)

+     except KeyError:

+         return flask.abort(400)

+     flask.g.db.commit()

+     return flask.redirect(flask.url_for('hub', name=hub.name))

+ 

+ 

+ @app.route('/api/hub/<hub>/star', methods=['POST'])

+ @login_required

+ def hub_star(hub):

+     hub = get_hub(hub)

+     user = hubs.models.User.by_username(flask.g.db, flask.g.auth.nickname)

+     hub.subscribe(flask.g.db, user, role='stargazer')

+     flask.g.db.commit()

+     return flask.redirect(flask.url_for('hub', name=hub.name))

+ 

+ 

+ @app.route('/api/hub/<hub>/unstar', methods=['POST'])

+ @login_required

+ def hub_unstar(hub):

+     hub = get_hub(hub)

+     user = hubs.models.User.by_username(flask.g.db, flask.g.auth.nickname)

+     try:

+         hub.unsubscribe(flask.g.db, user, role='stargazer')

+     except KeyError:

+         return flask.abort(400)

+     flask.g.db.commit()

+     return flask.redirect(flask.url_for('hub', name=hub.name))

+ 

+ 

+ @app.route('/api/hub/<hub>/join', methods=['POST'])

+ @login_required

+ def hub_join(hub):

+     hub = get_hub(hub)

+     user = hubs.models.User.by_username(flask.g.db, flask.g.auth.nickname)

+     hub.subscribe(flask.g.db, user, role='member')

+     flask.g.db.commit()

+     return flask.redirect(flask.url_for('hub', name=hub.name))

+ 

+ 

+ @app.route('/api/hub/<hub>/leave', methods=['POST'])

+ @login_required

+ def hub_leave(hub):

+     hub = get_hub(hub)

+     user = hubs.models.User.by_username(flask.g.db, flask.g.auth.nickname)

+     try:

+         hub.unsubscribe(flask.g.db, user, role='member')

+     except KeyError:

+         return flask.abort(400)

+     flask.g.db.commit()

+     return flask.redirect(flask.url_for('hub', name=hub.name))

+ 

file added
+241
@@ -0,0 +1,241 @@ 

+ from __future__ import unicode_literals, absolute_import

+ 

+ import datetime

+ import flask

+ import hubs.models

+ 

+ from hubs.app import app, session

+ from .utils import get_hub

+ 

+ 

+ @app.route('/<name>')

+ @app.route('/<name>/')

+ def hub(name):

+     hub = get_hub(name)

+     return flask.render_template(

+         'hubs.html', hub=hub, edit=False)

+ 

+ 

+ @app.route('/<name>/json/')

+ @app.route('/<name>/json')

+ def hub_json(name):

+     hub = get_hub(name)

+     response = flask.jsonify(hub.__json__(flask.g.db))

+     # TODO -- modify headers with response.headers['X-fedora-hubs-wat'] = 'foo'

+     return response

+ 

+ 

+ @app.route('/<name>/edit/', methods=['GET', 'POST'])

+ @app.route('/<name>/edit', methods=['GET', 'POST'])

+ def hub_edit(name):

+     if flask.request.method == 'POST':

+         return hub_edit_post(name)

+     else:

+         return hub_edit_get(name)

+ 

+ 

+ def hub_edit_get(name):

+     hub = get_hub(name)

+     return flask.render_template(

+         'hubs.html', hub=hub, edit=True)

+ 

+ 

+ def hub_edit_post(name):

+     hub = get_hub(name)

+     is_js = flask.request.form.get('js', False)

+ 

+     error = False

+ 

+     # Check the input submitted

+     # Right side

+     r_widget_ids = [

+         w.strip().replace('widget-', '')

+         for w in flask.request.form.getlist('right_widgets[]')

+         if w.strip()

+         ]

+     try:

+         r_widget_ids = [int(w) for w in r_widget_ids]

+     except:

+         flask.flash('Invalid widget identifiers submitted', 'error')

+         error = True

+ 

+     r_indexes = [

+         i.strip() for i in flask.request.form.getlist('right_indexes[]')

+         if i.strip()

+         ]

+ 

+     try:

+         r_indexes = [int(i) for i in r_indexes]

+     except:

+         if not is_js:

+             flask.flash('Invalid indexes submitted', 'error')

+         error = True

+ 

+     if len(r_widget_ids) != len(r_indexes):

+         if not is_js:

+             flask.flash(

+                 'The number of indexes and the number of widgets are not of '

+                 'the same length', 'error')

+         error = True

+ 

+     # Left side

+     l_widget_ids = [

+         w.strip().replace('widget-', '')

+         for w in flask.request.form.getlist('left_widgets[]')

+         if w.strip()

+         ]

+     try:

+         l_widget_ids = [int(w) for w in l_widget_ids]

+     except:

+         flask.flash('Invalid widget identifiers submitted', 'error')

+         error = True

+ 

+     l_indexes = [

+         i.strip() for i in flask.request.form.getlist('left_indexes[]')

+         if i.strip()

+         ]

+ 

+     try:

+         l_indexes = [int(i) for i in l_indexes]

+     except:

+         if not is_js:

+             flask.flash('Invalid indexes submitted', 'error')

+         error = True

+ 

+     if len(l_widget_ids) != len(l_indexes):

+         if not is_js:

+             flask.flash(

+                 'The number of indexes and the number of widgets are not of '

+                 'the same length', 'error')

+         error = True

+ 

+     # If all good, update the database

+     if not error:

+         for cnt, wid in enumerate(r_widget_ids):

+             widget = flask.g.db.query(hubs.models.Widget).get(wid)

+             if widget.index != r_indexes[cnt]:

+                 widget.index = r_indexes[cnt]

+                 flask.g.db.add(widget)

+         for cnt, wid in enumerate(l_widget_ids):

+             widget = flask.g.db.query(hubs.models.Widget).get(wid)

+             if widget.index != l_indexes[cnt]:

+                 widget.index = l_indexes[cnt]

+                 flask.g.db.add(widget)

+         hub.last_edited = datetime.datetime.utcnow()

+         flask.g.db.add(hub)

+         try:

+             flask.g.db.commit()

+         except Exception as err:

+             error = True

+             if not is_js:

+                 flask.flash(

+                     'Could not save your changes to the database '

+                     'if the error persists, please warn an admin',

+                     'error')

+     if is_js:

+         if error:

+             flask.abort(400)

+         else:

+             return ('ok', 200)

+     else:

+         return flask.redirect(flask.url_for('hub', name=name))

+ 

+ 

+ @app.route('/<name>/add/', methods=['GET', 'POST'])

+ @app.route('/<name>/add', methods=['GET', 'POST'])

+ def hub_add_widgets(name):

+     if flask.request.method == 'POST':

+         return hub_add_widget_post(name)

+     else:

+         return hub_add_widget_get(name)

+ 

+ 

+ @app.route('/<name>/add/<widget_name>/', methods=['GET'])

+ @app.route('/<name>/add/<widget_name>', methods=['GET'])

+ def hub_add_widget(name, widget_name):

+     hub = get_hub(name)

+     side = str(flask.request.args.get('position')).lower()

+     if side not in ['right', 'left']:

+         flask.abort(400, 'Invalid position provided')

+ 

+     widget = None

+     w_name = None

+     for widgt in hubs.widgets.registry:

+         if hubs.widgets.registry[widgt].position in ['both', side] \

+                 and widgt == widget_name:

+             w_name = widgt

+             widget = hubs.widgets.registry[widgt]

+ 

+     return flask.render_template(

+         'add_widget.html',

+         hub=hub,

+         widget=widget,

+         widget_name=w_name,

+         side=side,

+         url_to=flask.url_for('hub_add_widgets', name=name),

+     )

+ 

+ 

+ def hub_add_widget_get(name):

+     hub = get_hub(name)

+     side = str(flask.request.args.get('position')).lower()

+     if side not in ['right', 'left']:

+         flask.abort(400, 'Invalid position provided')

+ 

+     widgets = [

+         widget

+         for widget in hubs.widgets.registry

+         if hubs.widgets.registry[widget].position in ['both', side]

+         ]

+     return flask.render_template(

+         'add_widget.html',

+         hub=hub,

+         widgets=widgets,

+         side=side,

+     )

+ 

+ 

+ def hub_add_widget_post(name):

+     widget_name = flask.request.form.get('widget_name')

+     position = flask.request.form.get('position', '').lower()

+     if not widget_name:

+         flask.abort(400, 'Invalid request sent')

+     if widget_name not in hubs.widgets.registry:

+         flask.abort(404, 'Unknown widget called')

+ 

+     hub = get_hub(name)

+ 

+     widget = widget = hubs.models.Widget(

+         plugin=widget_name, index=-1, left=position == 'left')

+     error = False

+     config = {}

+     for arg in widget.module.data.widget_arguments:

+         val = flask.request.form.get(arg.name)

+         if not val:

+             flask.flash(

+                 'You must provide a value for: %s' % arg.name, 'error')

+             error = True

+             break

+         try:

+             arg.validator(flask.g.db, val)

+             config[arg.name] = val

+         except Exception as err:

+             flask.flash('Invalid data provided, error: %s' % err, 'error')

+             error = True

+     if not error:

+         widget.hub = hub

+         widget.config = config

+         try:

+             flask.g.db.add(widget)

+             flask.g.db.flush()

+             widget.hub.last_edited = datetime.datetime.utcnow()

+             flask.g.db.commit()

+         except Exception as err:

+             print(err)

+             flask.flash(

+                 'Could not save the configuration to the database '

+                 'if the error persists, please warn an admin',

+                 'error')

+ 

+     return flask.render_template(

+         'hubs.html', hub=hub, edit=True)

@@ -0,0 +1,77 @@ 

+ from __future__ import unicode_literals, absolute_import

+ 

+ import flask

+ import requests

+ import six

+ import hubs.models

+ 

+ from hubs.app import app, session

+ from .utils import login_required

+ 

+ 

+ # These views should be converted to widget-specific views, now that we can do

+ # that.

+ 

+ 

+ @app.route('/plus_plus/<user>/status', methods=['GET'])

+ def plus_plus_status(user):

+     receiver = hubs.models.User.by_username(flask.g.db, user)

+     if not receiver:

+         return 'User does not exist', 403

+     pp_url = app.config['PLUS_PLUS_URL'] + str(receiver.username)

+     req = None

+     try:

+         req = requests.get(pp_url)

+     except:

+         flask.abort(500)

+     if req.status_code == 200:

+         return flask.jsonify(req.json())

+     else:

+         return req.text, req.status_code

+ 

+ 

+ def plus_plus_update_bool_helper(val):

+     if isinstance(val, bool):

+         return val

+     elif isinstance(val, six.string_types):

+         fmt_str = str(val).replace("'", "").replace('"', '').lower()

+         return fmt_str in ("yes", "true", "t", "1")

+     else:

+         raise ValueError

+ 

+ 

+ @app.route('/plus_plus/<user>/update', methods=['POST'])

+ @login_required

+ def plus_plus_update(user):

+     receiver = hubs.models.User.by_username(flask.g.db, user)

+ 

+     if not receiver:

+         return 'User does not exist', 403

+ 

+     if user == flask.g.auth.nickname:

+         return 'You may not modify your own karma.', 403

+ 

+     if 'decrement' not in flask.request.form \

+             and 'increment' not in flask.request.form:

+         return "You must set 'decrement' or 'increment' " \

+                "with a boolean value in the body", 403

+ 

+     update = 'increment' if 'increment' in flask.request.form else 'decrement'

+ 

+     update_bool_val = plus_plus_update_bool_helper(flask.request.form[update])

+     pp_url = app.config['PLUS_PLUS_URL'] + str(receiver.username)

+     sender = hubs.models.User.by_username(flask.g.db, flask.g.auth.nickname)

+     pp_token = app.config['PLUS_PLUS_TOKEN']

+     auth_header = {'Authorization': 'token {}'.format(pp_token)}

+     data = {'sender': sender.username, update: update_bool_val}

+     req = None

+     try:

+         req = requests.post(url=pp_url, headers=auth_header, data=data)

+     except:

+         flask.abort(500)

+ 

+     if req.status_code == 200:

+         return flask.jsonify(req.json())

+     else:

+         return req.text, req.status_code

+ 

file added
+73
@@ -0,0 +1,73 @@ 

+ from __future__ import unicode_literals, absolute_import

+ 

+ import flask

+ import hubs.models

+ 

+ from hubs.app import app, session, OIDC

+ from .utils import authenticated, is_safe_url

+ 

+ 

+ @app.route('/')

+ def index():

+     if not authenticated():

+         return flask.redirect(flask.url_for('login'))

+ 

+     return flask.redirect(flask.url_for('stream', name=flask.g.auth.nickname))

+ 

+ 

+ @app.route('/groups')

+ def groups():

+     if not authenticated():

+         return flask.redirect(flask.url_for('login'))

+ 

+     # Get the list of promoted and non-promoted group hubs from the DB

+     promoted_names = app.config.get('PROMOTED_GROUPS')

+     groups = hubs.models.Hub.all_group_hubs(flask.g.db)

+     promoted = [g for g in groups if g.name in promoted_names]

+     secondary = [g for g in groups if g.name not in promoted_names]

+ 

+     name_of_the_month = app.config.get('HUB_OF_THE_MONTH')

+     hub_of_the_month = hubs.models.Hub.by_name(flask.g.db, name_of_the_month)

+ 

+     return flask.render_template(

+         'groups.html',

+         promoted=promoted,

+         secondary=secondary,

+         hub_of_the_month=hub_of_the_month,

+     )

+ 

+ 

+ @app.route('/login/', methods=('GET', 'POST'))

+ @app.route('/login', methods=('GET', 'POST'))

+ @OIDC.require_login

+ def login():

+     default = flask.url_for('index')

+     next_url = flask.request.args.get('next', default)

+     if is_safe_url(next_url):

+         return_point = next_url

+     else:

+         return_point = default

+ 

+     hubs.models.User.get_or_create(

+         flask.g.db, username=flask.g.auth.nickname, fullname=flask.g.auth.fullname)

+ 

+     return flask.redirect(return_point)

+ 

+ 

+ @app.route('/logout/')

+ @app.route('/logout')

+ def logout():

+     next_url = flask.url_for('index')

+     if 'next' in flask.request.values:  # pragma: no cover

+         if is_safe_url(flask.request.args['next']):

+             next_url = flask.request.values['next']

+ 

+     if next_url == flask.url_for('login'):  # pragma: no cover

+         next_url = flask.url_for('index')

+ 

+     if authenticated():

+         OIDC.logout()

+         flask.session.auth = None

+         flask.flash('You have been logged out')

+ 

+     return flask.redirect(next_url)

file added
+100
@@ -0,0 +1,100 @@ 

+ from __future__ import unicode_literals, absolute_import

+ 

+ import flask

+ import json

+ import hubs.models

+ import hubs.stream

+ 

+ from hubs.app import app, session

+ from .utils import login_required, get_hub

+ 

+ 

+ @app.route('/<name>/stream')

+ @app.route('/<name>/stream/')

+ @login_required

+ def stream(name):

+     hub = get_hub(name)

+     saved = hubs.models.SavedNotification.by_username(flask.g.db, name)

+     saved = [n.__json__() for n in saved]

+ 

+     stream = hubs.stream.Stream()

+     actions = stream.get_json()

+ 

+     return flask.render_template(

+         'stream.html',

+         hub=hub,

+         saved=json.dumps(saved),

+         actions=actions

+     )

+ 

+ 

+ @app.route('/<user>/notifications', methods=['GET', 'POST'])

+ @app.route('/<user>/notifications/', methods=['GET', 'POST'])

+ @login_required

+ def notifications(user):

+     if flask.request.method == 'GET':

+         notifications = hubs.models.SavedNotification.by_username(flask.g.db, user)

+         notifications = [n.__json__() for n in notifications]

+         return flask.jsonify(notifications)

+ 

+     if flask.request.method == 'POST':

+         data = flask.request.get_json()

+         user = hubs.models.User.by_username(flask.g.db, user)

+         if not user:

+             return flask.abort(400)

+         try:

+             markup = data['markup']

+             link = data['link']

+             icon = data['secondary_icon']

+             dom_id = data['dom_id']

+         except:

+             return flask.abort(400)

+         notification = hubs.models.SavedNotification(

+             username=user.username,

+             markup=markup,

+             link=link,

+             secondary_icon=icon,

+             dom_id=dom_id

+         )

+         flask.g.db.add(notification)

+         flask.g.db.commit()

+         return flask.jsonify(

+             dict(notification=notification.__json__(), success=True)

+         )

+ 

+ @app.route('/<user>/notifications/<int:idx>', methods=['DELETE'])

+ @app.route('/<user>/notifications/<int:idx>/', methods=['DELETE'])

+ @login_required

+ def delete_notifications(user, idx):

+     notification = flask.g.db.query(hubs.models.SavedNotification).filter_by(idx=idx).first()

+     if not notification:

+         return flask.abort(400)

+     flask.g.db.delete(notification)

+     flask.g.db.commit()

+     return flask.jsonify(dict(status_code=200))

+ @app.route('/visit/<visited_hub>/', methods=['GET', 'POST'])

+ @app.route('/visit/<visited_hub>', methods=['GET', 'POST'])

+ @login_required

+ def increment_counter(visited_hub):

+     nickname = flask.g.auth.nickname

+ 

+     if str(visited_hub) != str(nickname):

+         try:

+             vc = hubs.models.VisitCounter.get_or_create(session=flask.g.db,

+                                                         username=nickname,

+                                                         visited_hub=visited_hub)

+         except ValueError:  # this should never trip

+             # flask will 405 if visited_hub is blank

+             # @login_required forces flask.g.auth to be sets

+             flask.abort(404)

+         if flask.request.method == 'POST':

+             vc.increment_visits(session=flask.g.db,

+                                 username=nickname,

+                                 visited_hub=visited_hub)

+ 

+             return flask.jsonify({'count': vc.count})

+ 

+         elif flask.request.method == 'GET':

+             return flask.jsonify({'count': vc.count})

+     else:

+         return flask.abort(403)

file added
+74
@@ -0,0 +1,74 @@ 

+ from __future__ import unicode_literals

+ 

+ import flask

+ import functools

+ import hubs.models

+ import hubs.widgets

+ 

+ from six.moves.urllib import parse as urlparse

+ from sqlalchemy.orm.exc import NoResultFound

+ 

+ 

+ def get_hub(name, session=None):

+     """ Utility shorthand to get a hub and 404 if not found. """

+     if session is None:

+         session = flask.g.db

+     try:

+         return session.query(hubs.models.Hub).filter(

+             hubs.models.Hub.name == name).one()

+     except NoResultFound:

+         flask.abort(404)

+ 

+ 

+ def get_widget(hub, idx, session=None):

+     """ Utility shorthand to get a widget and 404 if not found. """

+     if session is None:

+         session = flask.g.db

+     try:

+         idx = int(idx)

+     except (TypeError, ValueError):

+         flask.abort(400)

+     try:

+         return session.query(

+                 hubs.models.Widget

+             ).join(hubs.models.Hub).filter(

+                 hubs.models.Hub.name == hub,

+                 hubs.models.Widget.idx == idx,

+             ).one()

+     except NoResultFound:

+         flask.abort(404)

+ 

+ 

+ def authenticated():

+     """ Utility function checking if the current auth is set or not."""

+     return hasattr(flask.g, 'auth') \

+         and flask.g.auth is not None \

+         and flask.g.auth.logged_in

+ 

+ 

+ def login_required(function):

+     """ Flask decorator to restrict access to logged-in users. """

+ 

+     @functools.wraps(function)

+     def decorated_function(*args, **kwargs):

+         """ Decorated function, actually does the work. """

+         if not authenticated():

+             flask.flash('Login required', 'errors')

+             return flask.redirect(flask.url_for(

+                 'login', next=flask.request.url))

+ 

+         return function(*args, **kwargs)

+ 

+     return decorated_function

+ 

+ 

+ def is_safe_url(target):

+     """ Checks that the target url is safe and sending to the current

+     website not some other malicious one.

+     """

+     ref_url = urlparse.urlparse(flask.request.host_url)

+     test_url = urlparse.urlparse(

+         urlparse.urljoin(flask.request.host_url, target))

+     return test_url.scheme in ('http', 'https') and \

+         ref_url.netloc == test_url.netloc

+ 

file added
+109
@@ -0,0 +1,109 @@ 

+ from __future__ import unicode_literals, absolute_import

+ 

+ import datetime

+ import flask

+ 

+ from hubs.app import app, session

+ from .utils import get_widget

+ 

+ 

+ @app.route('/<hub>/<int:idx>/')

+ @app.route('/<hub>/<int:idx>')

+ def widget_render(hub, idx):

+     widget = get_widget(hub, idx)

+     return widget.render(flask.g.db)  # , edit=False)

+     # was blocking all widgets from working, sorry!

+ 

+ 

+ @app.route('/<hub>/<int:idx>/json')

+ @app.route('/<hub>/<int:idx>/json/')

+ def widget_json(hub, idx):

+     widget = get_widget(hub, idx)

+     response = flask.jsonify(widget.__json__(flask.g.db))

+     # TODO -- modify headers with response.headers['X-fedora-hubs-wat'] = 'foo'

+     return response

+ 

+ 

+ @app.route('/<hub>/<int:idx>/edit/', methods=['GET', 'POST'])

+ @app.route('/<hub>/<int:idx>/edit', methods=['GET', 'POST'])

+ def widget_edit(hub, idx):

+     if flask.request.method == 'POST':

+         return widget_edit_post(hub, idx)

+     else:

+         return widget_edit_get(hub, idx)

+ 

+ 

+ def widget_edit_get(hub, idx):

+     widget = get_widget(hub, idx)

+     return flask.render_template(

+         'edit.html',

+         hub=hub,

+         widget=widget,

+         url_to=flask.url_for('widget_edit', hub=hub, idx=idx)

+     )

+ 

+ 

+ def widget_edit_post(hub, idx):

+     widget = get_widget(hub, idx)

+     error = False

+     config = {}

+     for arg in widget.module.data.widget_arguments:

+         val = flask.request.form.get(arg.name)

+         if not val:

+             flask.flash(

+                 'You must provide a value for: %s' % arg.name, 'error')

+             error = True

+             break

+         try:

+             val = arg.validator(flask.g.db, val)

+             config[arg.name] = val

+         except Exception as err:

+             flask.flash('Invalid data provided, error: %s' % err, 'error')

+             error = True

+     if not error:

+         cur_config = widget.config

+         cur_config.update(config)

+         widget.config = cur_config

+         widget.hub.last_edited = datetime.datetime.utcnow()

+         flask.g.db.add(widget)

+         try:

+             flask.g.db.commit()

+         except Exception as err:

+             flask.flash(

+                 'Could not save the configuration to the database '

+                 'if the error persists, please warn an admin',

+                 'error')

+     return flask.redirect(flask.url_for('hub', name=hub))

+ 

+ 

+ @app.route('/<hub>/<int:idx>/delete/', methods=['POST'])

+ @app.route('/<hub>/<int:idx>/delete', methods=['POST'])

+ def widget_edit_delete(hub, idx):

+     ''' Remove a widget from a hub. '''

+     widget = get_widget(hub, idx)

+     flask.g.db.delete(widget)

+     try:

+         flask.g.db.commit()

+     except Exception as err:

+         flask.flash(

+             'Could not delete this widget from this hub in the database '

+             'if the error persists, please warn an admin',

+             'error')

+     return flask.redirect(flask.url_for('hub_edit', name=hub))

+ 

+ 

+ @app.route('/source/<name>/')

+ @app.route('/source/<name>')

+ def widget_source(name):

+     from hubs.widgets import registry

+     base = '/hubs/'

+     fname = ''

+ 

+     try:

+         fname = base + registry[name].__file__.split(base)[-1]

+     except KeyError:

+         flask.abort(404)

+ 

+     fname = fname.replace('.pyc', '.py')

+     return flask.redirect(app.config.get('SOURCE_URL') + fname)

+ 

A couple comments:

  • I've created a hubs/views directories, and all views are sub-modules.
  • They are all imported in hubs/app.py as usually done on Flask apps
  • The widget-specific routes are added in hubs/views/__init__.py after importing the other views.

Also, I've added a before_request hook that stores the database session in the flask.g.db variable, and modified the views to use it instead of importing the session from the hubs/app.py file. I did this because otherwise the unit tests would fail when the session was imported, for the following reason: in the setUp method of the main unit test class in hubs/tests/__init__.py, the app is imported and then the session variable is replaced in hubs.app by a dummy database. However, at that point the views have already imported the session from hubs.app, and don't see the replacement, they are thus using the non-dummy database, and tests fail. Using the shared flask.g.db variable solves this problem.

Apart from that, I haven't changed the views.

Looks good to me.

On what basis do we break the views? either similar functionality or area of functionality right?

Currently it's a bit arbitrary, I'd say by area of functionality. I've regrouped the views that are below a hub id in the URL in the same module.

Thanks for reviewing it Sayan. I have a little improvement in the way we discover widget routes, I'll squeeze that in and merge it if it's OK with you all (it's really simple, and add a unit test, yay ;-) ).

1 new commit added

  • Use the registry to discover widget routes
7 years ago

Pull-Request has been merged by abompard

7 years ago