Commit 1699423aa0ac21473f0346b5dff77ee9f588a04e

Authored by Miguel Barão
1 parent b339e748
Exists in master and in 1 other branch dev

- show browser focus/unfocus events in /admin

- disabled content-security-policy because of MathJax
- removed column from tables in /admin to simplify
@@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
22 22
23 # FIXED 23 # FIXED
24 24
  25 +- server nao esta a receber eventos focus/blur dos utilizadores diferentes de '0', estranho...
25 - permitir adicionar imagens nas perguntas. 26 - permitir adicionar imagens nas perguntas.
26 - detect_unfocus.js so funciona se estiver inline no html. porquê??? 27 - detect_unfocus.js so funciona se estiver inline no html. porquê???
27 - inserir novo aluno /admin não fecha. 28 - inserir novo aluno /admin não fecha.
@@ -218,7 +218,8 @@ class App(object): @@ -218,7 +218,8 @@ class App(object):
218 'password_defined': pw != '', 218 'password_defined': pw != '',
219 'grades': self.get_student_grades_from_test(uid, self.testfactory['ref']), 219 'grades': self.get_student_grades_from_test(uid, self.testfactory['ref']),
220 'ip_address': self.online.get(uid, {}).get('student', {}).get('ip_address',''), 220 'ip_address': self.online.get(uid, {}).get('student', {}).get('ip_address',''),
221 - 'user_agent': self.online.get(uid, {}).get('student', {}).get('user_agent','') 221 + 'user_agent': self.online.get(uid, {}).get('student', {}).get('user_agent',''),
  222 + 'focus': self.online.get(uid, {}).get('student', {}).get('focus', True),
222 }) 223 })
223 return l 224 return l
224 225
@@ -263,3 +264,6 @@ class App(object): @@ -263,3 +264,6 @@ class App(object):
263 logger.error('Insert failed: student {} already exists.'.format(uid)) 264 logger.error('Insert failed: student {} already exists.'.format(uid))
264 else: 265 else:
265 logger.info('New student inserted into database: {}, {}'.format(uid, name)) 266 logger.info('New student inserted into database: {}, {}'.format(uid, name))
  267 +
  268 + def set_student_focus(self, uid, value):
  269 + self.online[uid]['student']['focus'] = value
@@ -63,7 +63,8 @@ def secureheaders(): @@ -63,7 +63,8 @@ def secureheaders():
63 headers = cherrypy.response.headers 63 headers = cherrypy.response.headers
64 headers['X-Frame-Options'] = 'DENY' 64 headers['X-Frame-Options'] = 'DENY'
65 headers['X-XSS-Protection'] = '1; mode=block' 65 headers['X-XSS-Protection'] = '1; mode=block'
66 - headers['Content-Security-Policy'] = "default-src='self'" 66 + # FIXME disabled because MathJax requires unsafe javascript eval:
  67 + # headers['Content-Security-Policy'] = "default-src 'self'"
67 if (cherrypy.server.ssl_certificate != None and cherrypy.server.ssl_private_key != None): 68 if (cherrypy.server.ssl_certificate != None and cherrypy.server.ssl_private_key != None):
68 headers['Strict-Transport-Security'] = 'max-age=31536000' # one year 69 headers['Strict-Transport-Security'] = 'max-age=31536000' # one year
69 70
@@ -88,6 +89,7 @@ class AdminWebService(object): @@ -88,6 +89,7 @@ class AdminWebService(object):
88 } 89 }
89 return json.dumps(data, default=str) 90 return json.dumps(data, default=str)
90 91
  92 + @cherrypy.tools.accept(media='application/json') # FIXME
91 def POST(self, **args): 93 def POST(self, **args):
92 # print('POST', args) # FIXME 94 # print('POST', args) # FIXME
93 if args['cmd'] == 'allow': 95 if args['cmd'] == 'allow':
@@ -106,6 +108,25 @@ class AdminWebService(object): @@ -106,6 +108,25 @@ class AdminWebService(object):
106 print(args) 108 print(args)
107 109
108 # ============================================================================ 110 # ============================================================================
  111 +# Student webservice
  112 +# ============================================================================
  113 +class StudentWebService(object):
  114 + exposed = True
  115 + _cp_config = {
  116 + 'auth.require': []
  117 + }
  118 +
  119 + def __init__(self, app):
  120 + self.app = app
  121 +
  122 + @cherrypy.tools.accept(media='application/json') # FIXME
  123 + def POST(self, **args):
  124 + uid = cherrypy.session.get(SESSION_KEY)
  125 + if args['cmd'] == 'focus':
  126 + v = json.loads(args['value'])
  127 + self.app.set_student_focus(uid=args['number'], value=v)
  128 +
  129 +# ============================================================================
109 # Webserver root 130 # Webserver root
110 # ============================================================================ 131 # ============================================================================
111 class Root(object): 132 class Root(object):
@@ -287,6 +308,7 @@ if __name__ == '__main__': @@ -287,6 +308,7 @@ if __name__ == '__main__':
287 # --- create webserver 308 # --- create webserver
288 webapp = Root(app) 309 webapp = Root(app)
289 webapp.adminwebservice = AdminWebService(app) 310 webapp.adminwebservice = AdminWebService(app)
  311 + webapp.studentwebservice = StudentWebService(app)
290 312
291 # --- site wide configuration (valid for all apps) 313 # --- site wide configuration (valid for all apps)
292 cherrypy.tools.secureheaders = cherrypy.Tool('before_finalize', secureheaders, priority=60) 314 cherrypy.tools.secureheaders = cherrypy.Tool('before_finalize', secureheaders, priority=60)
@@ -312,6 +334,11 @@ if __name__ == '__main__': @@ -312,6 +334,11 @@ if __name__ == '__main__':
312 'tools.response_headers.on': True, 334 'tools.response_headers.on': True,
313 'tools.response_headers.headers': [('Content-Type', 'text/plain')], 335 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
314 }, 336 },
  337 + '/studentwebservice': {
  338 + 'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
  339 + 'tools.response_headers.on': True,
  340 + 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
  341 + },
315 '/static': { 342 '/static': {
316 'tools.auth.on': False, # everything in /static is public 343 'tools.auth.on': False, # everything in /static is public
317 'tools.staticdir.on': True, 344 'tools.staticdir.on': True,
static/css/test.css 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +/* Fixes navigation panel overlaying content */
  2 +body {
  3 + padding-top: 80px;
  4 + background: #aaa;
  5 +}
  6 +/* Hack to avoid name clash between pygments and mathjax */
  7 +.MathJax .mo,
  8 +.MathJax .mi {
  9 + color: inherit;
  10 +}
  11 +.drop-shadow {
  12 + -webkit-box-shadow: 0 0 5px 2px rgba(0, 0, 0, .5);
  13 + box-shadow: 0px 2px 10px 3px rgba(0, 0, 0, .2);
  14 + border-radius:5px;
  15 +}
  16 +textarea {
  17 + font-family: monospace !important;
  18 +}
static/js/admin.js
@@ -57,23 +57,24 @@ $(document).ready(function() { @@ -57,23 +57,24 @@ $(document).ready(function() {
57 var active = []; 57 var active = [];
58 $.each(students, function(i, r) { 58 $.each(students, function(i, r) {
59 if (r['start_time'] != '') { 59 if (r['start_time'] != '') {
60 - active.push([r['uid'], r['name'], r['start_time'], r['ip_address'], r['user_agent']]); 60 + active.push([r['uid'], r['name'], r['start_time'], r['ip_address'], r['user_agent'], r['focus']]);
61 } 61 }
62 }); 62 });
63 // sort by start time 63 // sort by start time
64 active.sort(function(a,b){return a[2] < b[2] ? -1 : (a[2] == b[2] ? 0 : 1);}); 64 active.sort(function(a,b){return a[2] < b[2] ? -1 : (a[2] == b[2] ? 0 : 1);});
65 n = active.length; 65 n = active.length;
66 for(var i = 0; i < n; i++) { 66 for(var i = 0; i < n; i++) {
67 - rows += "<tr>\  
68 - <td>" + active[i][0] + "</td>\  
69 - <td>" + active[i][1] + "</td>\  
70 - <td>" + active[i][2].slice(0,10) + "</td>\  
71 - <td>" + active[i][2].slice(11,19) + '</td>\  
72 - <td><div data-toggle="tooltip" data-placement="top" title="' + active[i][4] + '">' + active[i][3] + '</div></td>\ 67 + rows += '<tr' + (active[i][5]? '' : ' class="danger"') + '>\
  68 + <td>' + active[i][0] + '</td>\
  69 + <td>' + active[i][1] + '</td>\
  70 + <td>' + active[i][2].slice(11,19) + '</td>\
  71 + <td><div data-toggle="tooltip" data-placement="top" title="'+active[i][4]+'">' + active[i][3] + '</div></td>\
  72 + <td>' + (active[i][5]? '' : '<span class="label label-danger">unfocus</span>') + '</td>\
73 </tr>'; 73 </tr>';
74 } 74 }
75 $("#online_students").html(rows); 75 $("#online_students").html(rows);
76 $("#online-header").html(n + " Activo(s)"); 76 $("#online-header").html(n + " Activo(s)");
  77 +
77 } 78 }
78 79
79 // ---------------------------------------------------------------------- 80 // ----------------------------------------------------------------------
@@ -111,14 +112,12 @@ $(document).ready(function() { @@ -111,14 +112,12 @@ $(document).ready(function() {
111 rows += '<tr id="' + uid + '" + class="">'; 112 rows += '<tr id="' + uid + '" + class="">';
112 113
113 rows += '\ 114 rows += '\
114 - <td><input type="checkbox" name="' + uid + '" value="true"' + (d['allowed'] ? 'checked' : '') + '></td>\  
115 - <td>' + uid + '</td>\  
116 - <td>' + d['name'] + (d['password_defined'] ? ' <span class="label label-default">pw</span>' : '') +'</td>\  
117 - <td>' +  
118 - 115 + <td><input type="checkbox" name="' + uid + '" value="true"' + (d['allowed'] ? 'checked' : '') + '>' +
  116 + (d['start_time']=='' ? '' : ' <span class="label label-success">teste</span>') +
119 // (d['online'] ? '<span class="label label-warning">online</span>' : '') + 117 // (d['online'] ? '<span class="label label-warning">online</span>' : '') +
120 - (d['start_time']==''?'':'<span class="label label-success">teste</span>') +  
121 '</td>\ 118 '</td>\
  119 + <td>' + uid + '</td>\
  120 + <td>' + d['name'] + (d['password_defined'] ? ' <span class="label label-default">pw</span>' : '') +'</td>\
122 <td>'; 121 <td>';
123 var g = d['grades']; 122 var g = d['grades'];
124 var glength = g.length; 123 var glength = g.length;
static/js/detect_unfocus.js
@@ -2,10 +2,11 @@ $(document).ready(function() { @@ -2,10 +2,11 @@ $(document).ready(function() {
2 $(window).focus(function(){ 2 $(window).focus(function(){
3 $.ajax({ 3 $.ajax({
4 type: "POST", 4 type: "POST",
5 - url: "/adminwebservice", 5 + url: "/studentwebservice",
6 data: { 6 data: {
7 "cmd": "focus", 7 "cmd": "focus",
8 - "name": $("#number").text() 8 + "number": $("#number").text(),
  9 + "value": true
9 } 10 }
10 }); 11 });
11 }); 12 });
@@ -13,10 +14,11 @@ $(document).ready(function() { @@ -13,10 +14,11 @@ $(document).ready(function() {
13 $(window).blur(function(e){ 14 $(window).blur(function(e){
14 $.ajax({ 15 $.ajax({
15 type: "POST", 16 type: "POST",
16 - url: "/adminwebservice", 17 + url: "/studentwebservice",
17 data: { 18 data: {
18 - "cmd": "blur",  
19 - "name": $("#number").text() 19 + "cmd": "focus",
  20 + "number": $("#number").text(),
  21 + "value": false
20 } 22 }
21 }); 23 });
22 }); 24 });
templates/admin.html
@@ -90,9 +90,10 @@ @@ -90,9 +90,10 @@
90 <tr> 90 <tr>
91 <th>Número</th> 91 <th>Número</th>
92 <th>Nome</th> 92 <th>Nome</th>
93 - <th>Data de início</th> 93 + <!-- <th>Data de início</th> -->
94 <th>Hora de início</th> 94 <th>Hora de início</th>
95 <th>IP</th> 95 <th>IP</th>
  96 + <th>Estado</th>
96 </tr> 97 </tr>
97 </thead> 98 </thead>
98 <tbody id="online_students"> 99 <tbody id="online_students">
@@ -113,7 +114,7 @@ @@ -113,7 +114,7 @@
113 <th>Perm.</th> 114 <th>Perm.</th>
114 <th>Número</th> 115 <th>Número</th>
115 <th>Nome</th> 116 <th>Nome</th>
116 - <th>Estado</th> 117 + <!-- <th>Estado</th> -->
117 <th>Nota</th> 118 <th>Nota</th>
118 </tr> 119 </tr>
119 </thead> 120 </thead>
templates/test.html
@@ -10,18 +10,19 @@ @@ -10,18 +10,19 @@
10 <!-- MathJax --> 10 <!-- MathJax -->
11 <script type="text/x-mathjax-config"> 11 <script type="text/x-mathjax-config">
12 MathJax.Hub.Config({ 12 MathJax.Hub.Config({
13 - tex2jax: {inlineMath: [["$$$","$$$"], ["$","$"], ["\\(","\\)"]]} 13 + tex2jax: {
  14 + inlineMath: [["$$$","$$$"], ["$","$"], ["\\(","\\)"]]
  15 + }
14 }); 16 });
15 </script> 17 </script>
16 -  
17 - <script type="text/javascript" src="/static/js/mathjax/MathJax.js?config=TeX-AMS_CHTML-full">  
18 - </script> 18 + <script type="text/javascript" src="/static/js/mathjax/MathJax.js?config=TeX-AMS_CHTML-full"></script>
19 19
20 <!-- Bootstrap --> 20 <!-- Bootstrap -->
21 <link rel="stylesheet" href="/static/css/bootstrap.min.css"> 21 <link rel="stylesheet" href="/static/css/bootstrap.min.css">
22 <link rel="stylesheet" href="/static/css/bootstrap-theme.min.css"> <!-- optional --> 22 <link rel="stylesheet" href="/static/css/bootstrap-theme.min.css"> <!-- optional -->
23 <link rel="stylesheet" href="/static/css/github.css"> <!-- syntax highlight --> 23 <link rel="stylesheet" href="/static/css/github.css"> <!-- syntax highlight -->
24 <link rel="stylesheet" href="/static/css/sticky-footer-navbar.css"> 24 <link rel="stylesheet" href="/static/css/sticky-footer-navbar.css">
  25 + <link rel="stylesheet" href="/static/css/test.css">
25 26
26 <script src="/static/js/jquery.min.js"></script> 27 <script src="/static/js/jquery.min.js"></script>
27 <script src="/static/js/bootstrap.min.js"></script> 28 <script src="/static/js/bootstrap.min.js"></script>
@@ -32,26 +33,6 @@ @@ -32,26 +33,6 @@
32 <script src="/static/js/tabkey_in_textarea.js"></script> 33 <script src="/static/js/tabkey_in_textarea.js"></script>
33 <script src="/static/js/detect_unfocus.js"></script> 34 <script src="/static/js/detect_unfocus.js"></script>
34 35
35 - <style>  
36 - /* Fixes navigation panel overlaying content */  
37 - body {  
38 - padding-top: 80px;  
39 - background: #aaa;  
40 - }  
41 - /* Hack to avoid name clash between pygments and mathjax */  
42 - .MathJax .mo,  
43 - .MathJax .mi {  
44 - color: inherit;  
45 - }  
46 - .drop-shadow {  
47 - -webkit-box-shadow: 0 0 5px 2px rgba(0, 0, 0, .5);  
48 - box-shadow: 0px 2px 10px 3px rgba(0, 0, 0, .2);  
49 - border-radius:5px;  
50 - }  
51 - textarea {  
52 - font-family: monospace !important;  
53 - }  
54 - </style>  
55 </head> 36 </head>
56 <!-- ===================================================================== --> 37 <!-- ===================================================================== -->
57 <body> 38 <body>
@@ -59,7 +40,6 @@ @@ -59,7 +40,6 @@
59 <div class="container-fluid drop-shadow"> 40 <div class="container-fluid drop-shadow">
60 <div class="navbar-header"> 41 <div class="navbar-header">
61 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar"> 42 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar">
62 - <!-- <span class="glyphicon glyphicon-menu-hamburger"></span> -->  
63 <span class="icon-bar"></span> 43 <span class="icon-bar"></span>
64 <span class="icon-bar"></span> 44 <span class="icon-bar"></span>
65 <span class="icon-bar"></span> 45 <span class="icon-bar"></span>
@@ -85,7 +65,6 @@ @@ -85,7 +65,6 @@
85 <li><a href="/results">Ver resultados</a></li> 65 <li><a href="/results">Ver resultados</a></li>
86 % endif 66 % endif
87 <li><a data-toggle="modal" data-target="#sair" id="form-button-submit"><span class="glyphicon glyphicon-log-out" aria-hidden="true"></span> Sair</a></li> 67 <li><a data-toggle="modal" data-target="#sair" id="form-button-submit"><span class="glyphicon glyphicon-log-out" aria-hidden="true"></span> Sair</a></li>
88 - <!-- <li><a href="#">Change password</a></li> -->  
89 </ul> 68 </ul>
90 </li> 69 </li>
91 </ul> 70 </ul>
@@ -22,7 +22,6 @@ def load_yaml(filename, default=None): @@ -22,7 +22,6 @@ def load_yaml(filename, default=None):
22 try: 22 try:
23 return yaml.load(f) 23 return yaml.load(f)
24 except yaml.YAMLError as e: 24 except yaml.YAMLError as e:
25 - # except yaml.parser.ParserError:  
26 mark = e.problem_mark 25 mark = e.problem_mark
27 logger.error('In YAML file "{0}" near line {1}, column {2}.'.format(filename, mark.line, mark.column+1)) 26 logger.error('In YAML file "{0}" near line {1}, column {2}.'.format(filename, mark.line, mark.column+1))
28 return default 27 return default