Commit 427df6dadcb9594532cf04160a8559056cb9caee

Persist user authorization of an app

   - Add authorized_clients table, a normalized table to store authorization
     done by user for an app. Now when a user authorizes an app(grants
permission), it is remembered by swtstore and it does not ask the user to allow
the app every time.

  - Add route /users/me/authorized_apps to list all the apps authorized by
    current user, and an interface to revoke access to specific apps.

  - Structured the templates folder.
  • Diff rendering mode:
  • inline
  • side by side

dbsetup.py

2020
21# Import all modules which represents a SQLAlchemy model;21# Import all modules which represents a SQLAlchemy model;
22# they have corresponding tables that are needed to be created22# they have corresponding tables that are needed to be created
23from swtstore.classes.models import Sweet, Context, Client
23from swtstore.classes.models import Sweet, Context, Client, AuthorizedClients
24from swtstore.classes.models.um import User, Group, Membership24from swtstore.classes.models.um import User, Group, Membership
2525
26# Create them!26# Create them!

runserver.py

19# Run the server if this script is directly executed19# Run the server if this script is directly executed
20# Presumably, this is development mode20# Presumably, this is development mode
21if __name__ == '__main__':21if __name__ == '__main__':
22 app.run(host='0.0.0.0', port=5001)
22 app.run(debug=True, host='0.0.0.0', port=5001)

swtstore/classes/models/__init__.py

1from context import Context1from context import Context
2from sweet import Sweet2from sweet import Sweet
3from client import Client
3from client import Client, AuthorizedClients

swtstore/classes/models/client.py

219@oauth.usergetter219@oauth.usergetter
220def getUser():220def getUser():
221 return User.getCurrentUser()221 return User.getCurrentUser()
222
223
224
225# Authorized Clients
226class AuthorizedClients(db.Model):
227 """
228 The clients authorized by users
229 """
230
231 __tablename__ = 'authorized_clients'
232
233 id = db.Column(db.Integer, primary_key=True)
234
235 client_id = db.Column(db.String(40), db.ForeignKey('clients.id'),
236 nullable=False)
237 client = db.relationship('Client')
238
239 user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
240 user = db.relationship('User')
241
242 def persist(self):
243 db.session.add(self)
244 db.session.commit()
245
246 @staticmethod
247 def revoke(**kwargs):
248 user = kwargs.get('user')
249 client = kwargs.get('client')
250 authorization = AuthorizedClients.query.filter_by(user_id=user.id,
251 client_id=client.client_id).first()
252 current_app.logger.debug('authorization to be revoked-- %s',
253 authorization)
254 db.session.delete(authorization)
255 db.session.commit()
256
257 @staticmethod
258 def getByUser(user):
259 authorized_clients = [row.client for row in \
260 AuthorizedClients.query.filter_by(user_id=user.id).all()]
261
262 current_app.logger.debug('authorized clients %s', authorized_clients)
263
264 return authorized_clients

swtstore/classes/views/app.py

22 return redirect(url_for('frontend.index'))22 return redirect(url_for('frontend.index'))
2323
24 if request.method == 'GET':24 if request.method == 'GET':
25 return render_template('register_app.html')
25 return render_template('app/register.html')
2626
27 elif request.method == 'POST':27 elif request.method == 'POST':
28 req_fields = ['name', 'host_url', 'redirect_uris', 'scopes']28 req_fields = ['name', 'host_url', 'redirect_uris', 'scopes']

swtstore/classes/views/context.py

17 return redirect(url_for('frontend.index'))17 return redirect(url_for('frontend.index'))
1818
19 if request.method == 'GET':19 if request.method == 'GET':
20 return render_template('register_context.html')
20 return render_template('context/register.html')
2121
22 if request.method == 'POST':22 if request.method == 'POST':
23 if not request.form.get('name') or not request.form.get('defn'):23 if not request.form.get('name') or not request.form.get('defn'):

swtstore/classes/views/frontend.py

1919
20 user = User.getCurrentUser()20 user = User.getCurrentUser()
2121
22 return render_template('index.html', sweets=sweets)
22 return render_template('frontend/index.html', sweets=sweets)
2323
2424
25# Create a new sweet context25# Create a new sweet context

swtstore/classes/views/oauth.py

33
4from flask import Module, jsonify, request, render_template, redirect,\4from flask import Module, jsonify, request, render_template, redirect,\
5 url_for, current_app5 url_for, current_app
6import requests
67
7from swtstore.classes import oauth8from swtstore.classes import oauth
8from swtstore.classes.models.um import User9from swtstore.classes.models.um import User
9from swtstore.classes.models import Client
10from swtstore.classes.models import Client, AuthorizedClients
1011
1112
12Oauth = Module(__name__)13Oauth = Module(__name__)
1314
15
14@Oauth.route('/authorize', methods=['GET', 'POST'])16@Oauth.route('/authorize', methods=['GET', 'POST'])
15@oauth.authorize_handler17@oauth.authorize_handler
16def authorize(*args, **kwargs):18def authorize(*args, **kwargs):
17 current_user = User.getCurrentUser()19 current_user = User.getCurrentUser()
18 if current_user is None:20 if current_user is None:
19 return render_template('oauth_login.html')
21 return render_template('oauth/login.html')
2022
21 if request.method == 'GET':23 if request.method == 'GET':
22 client_id = kwargs.get('client_id')24 client_id = kwargs.get('client_id')
27 kwargs['client'] = client27 kwargs['client'] = client
28 kwargs['user'] = current_user28 kwargs['user'] = current_user
29 current_app.logger.debug('kwargs %s', kwargs)29 current_app.logger.debug('kwargs %s', kwargs)
30 return render_template('authorize.html', **kwargs)
3130
31 # See if this client is already authorized by user. If not then return
32 # a HTML to allow access.
33 authorized_clients = AuthorizedClients.getByUser(current_user)
34 if client in authorized_clients:
35 return render_template('oauth/authorized.html', **kwargs)
36 else:
37 return render_template('oauth/authorize.html', **kwargs)
38
39
32 confirm = request.form.get('confirm', 'no')40 confirm = request.form.get('confirm', 'no')
41 authorized = request.form.get('authorized', 'no')
33 current_app.logger.debug('confirm authorize from user: %s', confirm)42 current_app.logger.debug('confirm authorize from user: %s', confirm)
34 return confirm == 'yes'
43 client = Client.query.get(request.form.get('client_id'))
44
45 if authorized == 'yes':
46 return True
47 else:
48 if confirm == 'yes':
49 authorization = AuthorizedClients(user=current_user, client=client)
50 authorization.persist()
51 return True
52 else:
53 return False
54
3555
36@Oauth.route('/token', methods=['GET', 'POST'])56@Oauth.route('/token', methods=['GET', 'POST'])
37@oauth.token_handler57@oauth.token_handler

swtstore/classes/views/sweet.py

23 sweet = Sweet.query.get(id)23 sweet = Sweet.query.get(id)
24 if sweet:24 if sweet:
25 print "sweet found " + str(sweet)25 print "sweet found " + str(sweet)
26 return render_template('specific_sweet.html', sweet=sweet)
26 return render_template('sweet/specific.html', sweet=sweet)
27 else:27 else:
28 abort(404)28 abort(404)

swtstore/classes/views/user.py

99
10# swtstore imports10# swtstore imports
11from swtstore.classes.models.um import User11from swtstore.classes.models.um import User
12from swtstore.classes.models import Sweet, Context, Client
12from swtstore.classes.models import Sweet, Context, Client, AuthorizedClients
1313
14from swtstore.classes.utils.httputils import makeCORSHeaders14from swtstore.classes.utils.httputils import makeCORSHeaders
15from swtstore.config import DefaultConfig15from swtstore.config import DefaultConfig
89 return redirect(url_for('frontend.index'))89 return redirect(url_for('frontend.index'))
9090
91 if request.method == 'GET':91 if request.method == 'GET':
92 return render_template('me.html', user=current_user)
92 return render_template('user/me.html', user=current_user)
9393
94 # else POST request
94 username = request.form.get('username')95 username = request.form.get('username')
9596
96 current_app.logger.debug('Updating username of %s to %s',97 current_app.logger.debug('Updating username of %s to %s',
110 return redirect(url_for('frontend.index'))110 return redirect(url_for('frontend.index'))
111111
112 swts = Sweet.getByCreator(user)112 swts = Sweet.getByCreator(user)
113 return render_template('my_sweets.html', sweets=swts)
113 return render_template('user/sweets.html', sweets=swts)
114114
115115
116@user.route('/me/contexts', methods=['GET'])116@user.route('/me/contexts', methods=['GET'])
121 return redirect(url_for('frontend.index'))121 return redirect(url_for('frontend.index'))
122122
123 contexts = Context.getByCreator(user.id)123 contexts = Context.getByCreator(user.id)
124 return render_template('my_contexts.html', contexts=contexts)
124 return render_template('user/contexts.html', contexts=contexts)
125125
126126
127@user.route('/me/apps', methods=['GET'])127@user.route('/me/apps', methods=['GET'])
133 return redirect(url_for('frontend.index'))133 return redirect(url_for('frontend.index'))
134134
135 apps = Client.getClientsByCreator(user.id)135 apps = Client.getClientsByCreator(user.id)
136 return render_template('my_apps.html', apps=apps)
136 return render_template('user/apps.html', apps=apps)
137
138@user.route('/me/authorized_apps', methods=['GET', 'POST'])
139def authorizedApps():
140
141 user = User.getCurrentUser()
142 if user is None:
143 return redirect(url_for('frontend.index'))
144
145 if request.method == 'GET':
146 authorized_clients = AuthorizedClients.getByUser(user)
147 return render_template('user/authorized_apps.html',
148 authorized_clients=authorized_clients)
149
150 # else POST request
151 client_id = request.form.get('revoke-id', '')
152 if client_id:
153 client = Client.query.get(client_id)
154 current_app.logger.info('user %s revoking access to %s', user, client)
155 AuthorizedClients.revoke(user=user, client=client)
156
157 return redirect(url_for('authorizedApps'))

swtstore/templates/app/list_apps.html

1{% extends "layout.html" %}
2{% block body %}
3<div>
4 {% if apps|length > 0 %}
5 <ul>
6 {% for app in apps %}
7 <div class="well">
8 <div class="pull-right">
9 <span class="glyphicon glyphicon-trash"></span>
10 </div>
11 <div><h4> {{ app.name }} </h4></div>
12 <div><h5> {{ app.description }} </h5></div>
13 <div>APP ID: {{ app.id }} </div>
14 <div> APP Secret: {{ app.client_secret }} </div>
15 <div>Host URL: {{ app.host_url }} </div>
16 <div>Redirect URL: {{ app.redirect_uris|join(', ') }} </div>
17 <div>Scopes: {{ app.default_scopes|join(', ') }} </div>
18 </div>
19 {% endfor %}
20 </ul>
21 {% else %}
22 <em> You haven't registered any apps yet! </em>
23 {% endif %}
24</div>
25{% endblock %}

swtstore/templates/app/register.html

1{% extends "layout.html" %}
2{% block body %}
3<h4> Register a new app with the <i>swt web</i> platform </h4>
4<p class="text-muted"> Please fill in the details below and click Register button </p>
5
6<form role="form" method="POST" action="{{ url_for('app.register') }}">
7 <div class="form-group">
8 <label for="name">Name </label>
9 <input type="text" class="form-control" name="name" id="name" placeholder="Name of the application">
10 </div>
11 <div class="form-group">
12 <label for="desc">Description</label>
13 <p class="help-block"> Brief description of your application. (Optional)</p>
14 <textarea class="form-control" rows="4" id="desc" name="desc">
15 </textarea>
16 </div>
17 <div class="form-group">
18 <label for="host_url">Host URL</label>
19 <p class="help-block">URL where your app is deployed</p>
20 <input type="text" class="form-control" name="host_url" id="host_url" placeholder="host URL">
21 </div>
22 <div class="form-group">
23 <label for="redirect_uris">Redirect URLs</label>
24 <p class="help-block">Your app URL where it should be redirected once the user is authorized.</p>
25 <input type="text" class="form-control" name="redirect_uris" id="redirect_uris" placeholder="redirect URLs">
26 </div>
27 <div class="form-group">
28 <label for="scopes">Scopes</label>
29 <input type="text" class="form-control" name="scopes" id="scopes" value="email">
30 </div>
31 <button type="submit" class="btn btn-primary">Register</button>
32</form>
33{% endblock %}
34{% block scripts %}
35<script>
36 // form validation
37 /*$('form').on('submit', function(event) {
38 console.log('here..');
39 if(!$('#name').val()) {
40 event.preventDefault();
41 $('#name').parent().addClass('has-feedback');
42 $('#name').parent().addClass('has-error');
43 return false;
44 }
45 if(!$('#host_url').val()) {
46 event.preventDefault();
47 $('#host_url').parent().addClass('has-feedback');
48 $('#name').parent().addClass('has-error');
49 return false;
50 }
51 if(!$('#redirect_uris').val()) {
52 event.preventDefault();
53 $('#redirect_uris').parent().addClass('has-feedback');
54 $('#name').parent().addClass('has-error');
55 return false;
56 }
57 if(!$('#scopes').val()) {
58 event.preventDefault();
59 $('#scopes').parent().addClass('has-feedback');
60 $('#name').parent().addClass('has-error');
61 return false;
62 }
63 });*/
64</script>
65{% endblock %}

swtstore/templates/authorize.html

1{% extends "layout.html" %}
2{% block body %}
3<h4> Allow Access ?</h4>
4<p class="text-muted">
5 The following application wants to get permission to do stuff(?) on the <i>swt
6 web</i> platform on your behalf.
7</p>
8
9<form role="form" method="POST" action="{{ url_for('oauth.authorize') }}">
10 <div class="form-group">
11 <p class="form-control-static text-primary">
12 {{ client.name }} at {{ client.host_url }} wants
13 to get permission to post data to swt store
14 </p>
15 </div>
16 <div class="form-group">
17 <label class="control-label">Scopes</label>
18 <div class="">
19 <p class="form-control-static">{{ scopes|join(',') }}</p>
20 </div>
21 </div>
22 <input type="hidden" name="client_id" value="{{ client.id }}">
23 <input type="hidden" name="scope" value="{{ scopes|join(' ') }}">
24 <input type="hidden" name="response_type" value="{{ response_type }}">
25 {% if state %}
26 <input type="hidden" name="state" value="{{ state }}">
27 {% endif %}
28 <button type="submit" name="confirm" value="yes" class="btn btn-primary">Allow</button>
29 <button type="submit" name="confirm" value="no" class="btn btn-default">Deny</button>
30</form>
31
32{% endblock %}
33
34{% block scripts %}
35
36<script>
37 var created = new Date("{{ user.created }}");
38 window.onload = function() {
39 $('#user-created').html(created.toString());
40 };
41</script>
42
43{% endblock %}

swtstore/templates/context/list_contexts.html

1{% extends "layout.html" %}
2{% block body %}
3<div>
4 {% if contexts|length > 0 %}
5 <ul>
6 {% for context in contexts %}
7 <div class="well">
8 <div class="pull-right">
9 <span class="glyphicon glyphicon-trash"></span>
10 </div>
11 <div> {{ context.name }} </div>
12 <div> {{ context.definition }} </div>
13 </div>
14 {% endfor %}
15 </ul>
16 {% else %}
17 <em> You haven't registered any contexts yet! </em>
18 {% endif %}
19</div>
20{% endblock %}

swtstore/templates/context/register.html

1{% extends "layout.html" %}
2{% block body %}
3<h4> Register a new context with the <i>swt web</i> platform </h4>
4<p class="text-muted"> Please fill in the details below and click Register button </p>
5
6<form role="form" method="POST" action="{{ url_for('context.register') }}">
7 <div class="form-group">
8 <label for="name">Name </label>
9 <input type="text" class="form-control" name="name" id="name" placeholder="Name of the context">
10 </div>
11 <div class="form-group">
12 <label for="defn">Definition</label>
13 <p class="help-block"> JSON-LD definition. Insert a JSON-LD.</p>
14 <textarea class="form-control" rows="4" id="defn" name="defn">
15 </textarea>
16 </div>
17 <button type="submit" class="btn btn-primary">Register</button>
18</form>
19{% endblock %}

swtstore/templates/frontend/index.html

1{% extends "layout.html" %}
2
3{% block body %}
4 <div id="store-stats">
5 </div>
6
7 {% if sweets|length > 0 %}
8
9 <ul class="entries unstyled">
10 {% for sweet in sweets %}
11 <li>
12 <span class="who">
13 <a href="#">
14 @{{ sweet.who.username }}
15 </a>
16 </span>
17 <span class="what">
18 <b> #{{ sweet.what.name }} </b>
19 </span>
20 <span class="where">
21 {{ sweet.where }}
22 </span>
23 <p></p>
24 <span class="how">
25 {{ sweet.how|escape|safe }}
26 </span>
27
28 <small><i>created: {{sweet.created }}</i></small>
29
30 <span class="pull-right permalink">
31 <a href="{{ url_for('sweet.showSweet', id=sweet.id) }}">
32 <i class="glyphicon glyphicon-share"></i>
33 </a>
34 </li>
35 {% endfor %}
36 </ul>
37
38 {% else %}
39 <div class="row">
40 <div class="col-md-5">
41 <h4><em>Unbelievable! No sweets yet!!</em></h4>
42 </div>
43 </div>
44 {% endif %}
45
46{% endblock %}

swtstore/templates/index.html

1{% extends "layout.html" %}
2
3{% block body %}
4 <div id="store-stats">
5 </div>
6
7 {% if sweets|length > 0 %}
8
9 <ul class="entries unstyled">
10 {% for sweet in sweets %}
11 <li>
12 <span class="who">
13 <a href="#">
14 @{{ sweet.who.username }}
15 </a>
16 </span>
17 <span class="what">
18 <b> #{{ sweet.what.name }} </b>
19 </span>
20 <span class="where">
21 {{ sweet.where }}
22 </span>
23 <p></p>
24 <span class="how">
25 {{ sweet.how|escape|safe }}
26 </span>
27
28 <small><i>created: {{sweet.created }}</i></small>
29
30 <span class="pull-right permalink">
31 <a href="{{ url_for('sweet.showSweet', id=sweet.id) }}">
32 <i class="glyphicon glyphicon-share"></i>
33 </a>
34 </li>
35 {% endfor %}
36 </ul>
37
38 {% else %}
39 <div class="row">
40 <div class="col-md-5">
41 <h4><em>Unbelievable! No sweets yet!!</em></h4>
42 </div>
43 </div>
44 {% endif %}
45
46{% endblock %}

swtstore/templates/layout.html

51 {{ session.email }}51 {{ session.email }}
52 </small></a>52 </small></a>
53 </li>53 </li>
54 <li>
55 <a href="{{ url_for('user.authorizedApps') }}">
56 My authorized apps
57 </a>
58 </li>
54 <li class="divider"></li>59 <li class="divider"></li>
55 <li> <a href="#" id="logout"> Logout </a> </li>60 <li> <a href="#" id="logout"> Logout </a> </li>
56 </ul>61 </ul>

swtstore/templates/list_apps.html

1{% extends "layout.html" %}
2{% block body %}
3<div>
4 {% if apps|length > 0 %}
5 <ul>
6 {% for app in apps %}
7 <div class="well">
8 <div class="pull-right">
9 <span class="glyphicon glyphicon-trash"></span>
10 </div>
11 <div><h4> {{ app.name }} </h4></div>
12 <div><h5> {{ app.description }} </h5></div>
13 <div>APP ID: {{ app.id }} </div>
14 <div> APP Secret: {{ app.client_secret }} </div>
15 <div>Host URL: {{ app.host_url }} </div>
16 <div>Redirect URL: {{ app.redirect_uris|join(', ') }} </div>
17 <div>Scopes: {{ app.default_scopes|join(', ') }} </div>
18 </div>
19 {% endfor %}
20 </ul>
21 {% else %}
22 <em> You haven't registered any apps yet! </em>
23 {% endif %}
24</div>
25{% endblock %}

swtstore/templates/list_contexts.html

1{% extends "layout.html" %}
2{% block body %}
3<div>
4 {% if contexts|length > 0 %}
5 <ul>
6 {% for context in contexts %}
7 <div class="well">
8 <div class="pull-right">
9 <span class="glyphicon glyphicon-trash"></span>
10 </div>
11 <div> {{ context.name }} </div>
12 <div> {{ context.definition }} </div>
13 </div>
14 {% endfor %}
15 </ul>
16 {% else %}
17 <em> You haven't registered any contexts yet! </em>
18 {% endif %}
19</div>
20{% endblock %}

swtstore/templates/me.html

1
2{% extends "layout.html" %}
3{% block body %}
4
5<h4> Update your profile details </h4>
6
7<p class="text-muted"> Please fill in the details below and click update button </p>
8
9<form role="form" method="POST" action="{{ url_for('user.profile') }}">
10 <div class="form-group">
11 <label for="username">Username</label>
12 <input type="text" class="form-control" name="username" id="username"
13 placeholder="Username you are going to use" value="{{ user.username}}">
14 </div>
15 <div class="form-group">
16 <label class="control-label">Email</label>
17 <div class="">
18 <p class="form-control-static">{{ user.email }}</p>
19 </div>
20 </div>
21 <div class="form-group">
22 <label class="control-label">Account created </label>
23 <div class="">
24 <p class="form-control-static" id="user-created">
25 {{ user.created }}
26 </p>
27 </div>
28 </div>
29 <button type="submit" class="btn btn-primary">Update</button>
30</form>
31
32{% endblock %}
33
34{% block scripts %}
35
36<script>
37 var created = new Date("{{ user.created }}");
38 window.onload = function() {
39 $('#user-created').html(created.toString());
40 };
41</script>
42
43{% endblock %}

swtstore/templates/my_apps.html

1{% extends "layout.html" %}
2{% block body %}
3<div>
4 {% if apps|length > 0 %}
5 <ul>
6 {% for app in apps %}
7 <div class="well">
8 <div class="pull-right">
9 <span class="glyphicon glyphicon-trash"></span>
10 </div>
11 <div><h4> {{ app.name }} </h4></div>
12 <div><h5> {{ app.description }} </h5></div>
13 <div>APP ID: {{ app.id }} </div>
14 <div> APP Secret: {{ app.client_secret }} </div>
15 <div>Host URL: {{ app.host_url }} </div>
16 <div>Redirect URL: {{ app.redirect_uris|join(', ') }} </div>
17 <div>Scopes: {{ app.default_scopes|join(', ') }} </div>
18 </div>
19 {% endfor %}
20 </ul>
21 {% else %}
22 <em> You haven't registered any apps yet! </em>
23 {% endif %}
24</div>
25{% endblock %}

swtstore/templates/my_contexts.html

1{% extends "layout.html" %}
2{% block body %}
3<div>
4 {% if contexts|length > 0 %}
5 <ul>
6 {% for context in contexts %}
7 <div class="well">
8 <div class="pull-right">
9 <span class="glyphicon glyphicon-trash"></span>
10 </div>
11 <div> {{ context.name }} </div>
12 <div> {{ context.definition }} </div>
13 </div>
14 {% endfor %}
15 </ul>
16 {% else %}
17 <em> You haven't registered any contexts yet! </em>
18 {% endif %}
19</div>
20{% endblock %}

swtstore/templates/my_sweets.html

1{% extends "layout.html" %}
2
3{% block body %}
4
5 {% if sweets|length > 0 %}
6
7 <ul class="entries unstyled">
8 {% for sweet in sweets %}
9 <li>
10 <span class="who">
11 <a href="#">
12 @{{ sweet.who.username }}
13 </a>
14 </span>
15 <span class="what">
16 <b> #{{ sweet.what.name }} </b>
17 </span>
18 <span class="where">
19 {{ sweet.where }}
20 </span>
21 <p></p>
22 <span class="how">
23 {{ sweet.how|tojson }}
24 </span>
25
26 <small><i>created: {{sweet.created }}</i></small>
27
28 <span class="pull-right permalink">
29 <a href="#">
30 <i class="glyphicon glyphicon-share"></i>
31 </a>
32 </span>
33 <span class="pull-right edit-sweet" for="{{ sweet.id }}">
34 <a href="#">
35 <i class="glyphicon glyphicon-edit"></i>
36 </a>
37 </span>
38 </li>
39 {% endfor %}
40 </ul>
41
42 {% else %}
43 <div class="row">
44 <div class="col-md-5">
45 <h4><em>Unbelievable! No sweets yet!!</em></h4>
46 </div>
47 </div>
48 {% endif %}
49
50 <div class="modal fade" id="edit-sweet-modal">
51 <div class="modal-dialog">
52 <div class="modal-content">
53 <div class="modal-header">
54 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
55 <h4 class="modal-title">Edit Sweet </h4>
56 </div>
57 <div class="modal-body"></div>
58 <div
59 class="modal-footer">
60 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
61 <button type="button" class="btn btn-primary" id="save-edited-sweet">Save changes</button>
62 </div>
63 </div><!-- /.modal-content -->
64 </div><!-- /.modal-dialog -->
65 </div><!-- /.modal -->
66
67{% endblock %}

swtstore/templates/register_app.html

1{% extends "layout.html" %}
2{% block body %}
3<h4> Register a new app with the <i>swt web</i> platform </h4>
4<p class="text-muted"> Please fill in the details below and click Register button </p>
5
6<form role="form" method="POST" action="{{ url_for('app.register') }}">
7 <div class="form-group">
8 <label for="name">Name </label>
9 <input type="text" class="form-control" name="name" id="name" placeholder="Name of the application">
10 </div>
11 <div class="form-group">
12 <label for="desc">Description</label>
13 <p class="help-block"> Brief description of your application. (Optional)</p>
14 <textarea class="form-control" rows="4" id="desc" name="desc">
15 </textarea>
16 </div>
17 <div class="form-group">
18 <label for="host_url">Host URL</label>
19 <p class="help-block">URL where your app is deployed</p>
20 <input type="text" class="form-control" name="host_url" id="host_url" placeholder="host URL">
21 </div>
22 <div class="form-group">
23 <label for="redirect_uris">Redirect URLs</label>
24 <p class="help-block">Your app URL where it should be redirected once the user is authorized.</p>
25 <input type="text" class="form-control" name="redirect_uris" id="redirect_uris" placeholder="redirect URLs">
26 </div>
27 <div class="form-group">
28 <label for="scopes">Scopes</label>
29 <input type="text" class="form-control" name="scopes" id="scopes" value="email">
30 </div>
31 <button type="submit" class="btn btn-primary">Register</button>
32</form>
33{% endblock %}
34{% block scripts %}
35<script>
36 // form validation
37 /*$('form').on('submit', function(event) {
38 console.log('here..');
39 if(!$('#name').val()) {
40 event.preventDefault();
41 $('#name').parent().addClass('has-feedback');
42 $('#name').parent().addClass('has-error');
43 return false;
44 }
45 if(!$('#host_url').val()) {
46 event.preventDefault();
47 $('#host_url').parent().addClass('has-feedback');
48 $('#name').parent().addClass('has-error');
49 return false;
50 }
51 if(!$('#redirect_uris').val()) {
52 event.preventDefault();
53 $('#redirect_uris').parent().addClass('has-feedback');
54 $('#name').parent().addClass('has-error');
55 return false;
56 }
57 if(!$('#scopes').val()) {
58 event.preventDefault();
59 $('#scopes').parent().addClass('has-feedback');
60 $('#name').parent().addClass('has-error');
61 return false;
62 }
63 });*/
64</script>
65{% endblock %}

swtstore/templates/register_context.html

1{% extends "layout.html" %}
2{% block body %}
3<h4> Register a new context with the <i>swt web</i> platform </h4>
4<p class="text-muted"> Please fill in the details below and click Register button </p>
5
6<form role="form" method="POST" action="{{ url_for('context.register') }}">
7 <div class="form-group">
8 <label for="name">Name </label>
9 <input type="text" class="form-control" name="name" id="name" placeholder="Name of the context">
10 </div>
11 <div class="form-group">
12 <label for="defn">Definition</label>
13 <p class="help-block"> JSON-LD definition. Insert a JSON-LD.</p>
14 <textarea class="form-control" rows="4" id="defn" name="defn">
15 </textarea>
16 </div>
17 <button type="submit" class="btn btn-primary">Register</button>
18</form>
19{% endblock %}

swtstore/templates/specific_sweet.html

1{% extends "layout.html" %}
2
3{% block body %}
4
5<ul class="entries unstyled">
6 <li>
7 <span class="who">
8 <a href="#">
9 @{{ sweet.who.username }}
10 </a>
11 </span>
12 <span class="what">
13 <b> #{{ sweet.what.name }} </b>
14 </span>
15 <span class="where">
16 {{ sweet.where }}
17 </span>
18 <p></p>
19 <span class="how">
20 {{ sweet.how|escape|safe }}
21 </span>
22
23 <small><i>created: {{sweet.created }}</i></small>
24 </li>
25</ul>
26
27{% endblock %}

swtstore/templates/sweet/specific.html

1{% extends "layout.html" %}
2
3{% block body %}
4
5<ul class="entries unstyled">
6 <li>
7 <span class="who">
8 <a href="#">
9 @{{ sweet.who.username }}
10 </a>
11 </span>
12 <span class="what">
13 <b> #{{ sweet.what.name }} </b>
14 </span>
15 <span class="where">
16 {{ sweet.where }}
17 </span>
18 <p></p>
19 <span class="how">
20 {{ sweet.how|escape|safe }}
21 </span>
22
23 <small><i>created: {{sweet.created }}</i></small>
24 </li>
25</ul>
26
27{% endblock %}

swtstore/templates/user/apps.html

1{% extends "layout.html" %}
2{% block body %}
3<div>
4 {% if apps|length > 0 %}
5 <ul>
6 {% for app in apps %}
7 <div class="well">
8 <div class="pull-right">
9 <span class="glyphicon glyphicon-trash"></span>
10 </div>
11 <div><h4> {{ app.name }} </h4></div>
12 <div><h5> {{ app.description }} </h5></div>
13 <div>APP ID: {{ app.id }} </div>
14 <div> APP Secret: {{ app.client_secret }} </div>
15 <div>Host URL: {{ app.host_url }} </div>
16 <div>Redirect URL: {{ app.redirect_uris|join(', ') }} </div>
17 <div>Scopes: {{ app.default_scopes|join(', ') }} </div>
18 </div>
19 {% endfor %}
20 </ul>
21 {% else %}
22 <em> You haven't registered any apps yet! </em>
23 {% endif %}
24</div>
25{% endblock %}

swtstore/templates/user/authorized_apps.html

1{% extends "layout.html" %}
2{% block body %}
3
4{% if authorized_clients | length > 0 %}
5<h4> Your authorized apps </h4>
6
7<p class="text-muted">
8 You have given access to these apps to post sweets
9 to the swtstore on your behalf. You can revoke access to specific apps.
10</p>
11
12<form role="form" method="POST" action="{{ url_for('user.authorizedApps') }}">
13 <div class="form-group">
14 <ul>
15 {% for client in authorized_clients %}
16 <li class="authorized_apps">
17 <p class="form-control-static">
18 <b>{{ client.name }}</b>: {{ client.description }}
19 <button type="submit" name="revoke-id" value="{{ client.client_id }}"
20 class="btn btn-sm btn-primary pull-right">Revoke Access</button>
21 </p>
22 </li>
23 {% endfor %}
24 </ul>
25 </div>
26</form>
27{% else %}
28<i> You have not authorized any apps to access swtstore yet.</i>
29{% endif %}
30
31{% endblock %}
32
33{% block scripts %}
34
35<script>
36</script>
37
38{% endblock %}

swtstore/templates/user/contexts.html

1{% extends "layout.html" %}
2{% block body %}
3<div>
4 {% if contexts|length > 0 %}
5 <ul>
6 {% for context in contexts %}
7 <div class="well">
8 <div class="pull-right">
9 <span class="glyphicon glyphicon-trash"></span>
10 </div>
11 <div> {{ context.name }} </div>
12 <div> {{ context.definition }} </div>
13 </div>
14 {% endfor %}
15 </ul>
16 {% else %}
17 <em> You haven't registered any contexts yet! </em>
18 {% endif %}
19</div>
20{% endblock %}

swtstore/templates/user/me.html

1
2{% extends "layout.html" %}
3{% block body %}
4
5<h4> Update your profile details </h4>
6
7<p class="text-muted"> Please fill in the details below and click update button </p>
8
9<form role="form" method="POST" action="{{ url_for('user.profile') }}">
10 <div class="form-group">
11 <label for="username">Username</label>
12 <input type="text" class="form-control" name="username" id="username"
13 placeholder="Username you are going to use" value="{{ user.username}}">
14 </div>
15 <div class="form-group">
16 <label class="control-label">Email</label>
17 <div class="">
18 <p class="form-control-static">{{ user.email }}</p>
19 </div>
20 </div>
21 <div class="form-group">
22 <label class="control-label">Account created </label>
23 <div class="">
24 <p class="form-control-static" id="user-created">
25 {{ user.created }}
26 </p>
27 </div>
28 </div>
29 <button type="submit" class="btn btn-primary">Update</button>
30</form>
31
32{% endblock %}
33
34{% block scripts %}
35
36<script>
37 var created = new Date("{{ user.created }}");
38 window.onload = function() {
39 $('#user-created').html(created.toString());
40 };
41</script>
42
43{% endblock %}

swtstore/templates/user/sweets.html

1{% extends "layout.html" %}
2
3{% block body %}
4
5 {% if sweets|length > 0 %}
6
7 <ul class="entries unstyled">
8 {% for sweet in sweets %}
9 <li>
10 <span class="who">
11 <a href="#">
12 @{{ sweet.who.username }}
13 </a>
14 </span>
15 <span class="what">
16 <b> #{{ sweet.what.name }} </b>
17 </span>
18 <span class="where">
19 {{ sweet.where }}
20 </span>
21 <p></p>
22 <span class="how">
23 {{ sweet.how|tojson }}
24 </span>
25
26 <small><i>created: {{sweet.created }}</i></small>
27
28 <span class="pull-right permalink">
29 <a href="#">
30 <i class="glyphicon glyphicon-share"></i>
31 </a>
32 </span>
33 <span class="pull-right edit-sweet" for="{{ sweet.id }}">
34 <a href="#">
35 <i class="glyphicon glyphicon-edit"></i>
36 </a>
37 </span>
38 </li>
39 {% endfor %}
40 </ul>
41
42 {% else %}
43 <div class="row">
44 <div class="col-md-5">
45 <h4><em>Unbelievable! No sweets yet!!</em></h4>
46 </div>
47 </div>
48 {% endif %}
49
50 <div class="modal fade" id="edit-sweet-modal">
51 <div class="modal-dialog">
52 <div class="modal-content">
53 <div class="modal-header">
54 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
55 <h4 class="modal-title">Edit Sweet </h4>
56 </div>
57 <div class="modal-body"></div>
58 <div
59 class="modal-footer">
60 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
61 <button type="button" class="btn btn-primary" id="save-edited-sweet">Save changes</button>
62 </div>
63 </div><!-- /.modal-content -->
64 </div><!-- /.modal-dialog -->
65 </div><!-- /.modal -->
66
67{% endblock %}