Commit 0197e841 authored by HERBERT Ryan's avatar HERBERT Ryan
Browse files

merging 21 commits from branch notifications

parents 55e02082 e788e208
......@@ -1549,6 +1549,43 @@ table.log textarea {
background-image: linear-gradient(45deg, transparent -25%, #000000 100%, #000000 100%, transparent 0%);
}
.message_container {
float: left;
display: none;
}
.urgent_message {
background-color: #652D1A;
}
.info_message {
background-color: #1F5A0B;
}
.notification {
margin: 5px;
padding: 3px;
display: inline-flex;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
}
#login_messages .notification {
display: block;
}
#login_messages .notification pre {
padding: 3px;
margin: 0px;
display: block;
width: auto;
}
#login_messages.message_container {
width: 100%;
}
#listLocus, #listVsegment, #listDsegment, #listJsegment {
display: none;
}
......@@ -11,5 +11,8 @@
@background : #000000;
@border: #333333;
@urgent_message : #652D1A;
@info_message : #1F5A0B;
@import "vidjil.less";
......@@ -1549,6 +1549,44 @@ table.log textarea {
background-image: linear-gradient(45deg, transparent -25%, #ffffff 100%, #ffffff 100%, transparent 0%);
}
.message_container {
float: left;
display: none;
}
.urgent_message {
background-color: #D4A190;
}
.info_message {
background-color: #A1D490;
}
.notification {
margin: 5px;
padding: 3px;
display: inline-flex;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
}
#login_messages .notification {
display: block;
}
#login_messages .notification pre {
padding: 3px;
margin: 0px;
display: block;
width: auto;
}
#login_messages.message_container {
width: 100%;
}
#listLocus, #listVsegment, #listDsegment, #listJsegment {
display: none;
}
\ No newline at end of file
......@@ -11,6 +11,9 @@
@background : #ffffff;
@border: #cccccc;
@urgent_message : #D4A190;
@info_message : #A1D490;
@import "vidjil.less";
......
......@@ -1754,6 +1754,43 @@ flex-grow : 1;
background-image : linear-gradient(45deg, transparent -25%, @background 100%, @background 100%, transparent 0%)
}
.message_container {
float: left;
display: none;
}
.urgent_message {
background-color: @urgent_message;
}
.info_message {
background-color: @info_message;
}
.notification {
margin: 5px;
padding: 3px;
display: inline-flex;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
}
#login_messages .notification {
display: block;
}
#login_messages .notification pre {
padding: 3px;
margin: 0px;
display: block;
width: auto;
}
#login_messages.message_container {
width: 100%;
}
#listLocus, #listVsegment, #listDsegment, #listJsegment {
display: none;
}
......@@ -214,6 +214,7 @@
</div></div>
</div>
<div id="debug_menu" class="menu" onmouseover="showSelector('adminSelector');" style="display : none"> development
<div id="adminSelector" class="selector"><div>
<input type="checkbox" onclick=" this.checked ? $('#fps').css('display', '') : $('#fps').css('display', 'none');">display fps</input></br>
......@@ -221,6 +222,8 @@
<div class="buttonSelector" onclick="m.changeTimeFormat('delta_date_no_zero', true)"><input type="radio" name="time" value="delta_date_no_zero">day after first sample (no zero)</input></div>
</div></div>
</div>
<div id="header_messages" class="message_container"></div>
<div id="logo" style="float: right;" onclick="console.log({'type': 'popup', 'default':'welcome'})">Vidjil <span class='logo'>(beta)</span></div>
......@@ -283,6 +286,4 @@
</body>
</html>
</html>
\ No newline at end of file
......@@ -2,6 +2,7 @@ DB_ADDRESS = ""
DB_TIMEOUT_CALL = 5000 // Regular call
DB_TIMEOUT_GET_DATA = 15000 // Get patient/sample .data
DB_TIMEOUT_GET_CUSTOM_DATA = 1200000 // Launch custum fused sample .data
NOTIFICATION_PERIOD = 30000 // Time interval to check for notifications periodically
/**
......@@ -189,6 +190,9 @@ Database.prototype = {
//
this.fixed_header()
// New page displayed, attempt to display header and login notifications
this.loadNotifications();
return 0 ;
}
......@@ -601,6 +605,85 @@ Database.prototype = {
console.log({"type": "flash", "msg": "server : save analysis error : this file is nor from the database" , "priority": 2});
}
},
// periodically query the server for notifications
// And loads them into elements with id 'header_messages' and 'login_messages'
// TODO : Tidy up
loadNotifications: function() {
var self = this;
if (DB_ADDRESS != "") {
$.ajax({
type: "GET",
crossDomain: true,
url: DB_ADDRESS + 'notification/get_active_notifications',
contentType: 'text/plain',
timeout: DB_TIMEOUT_CALL,
success: function (result) {
var messages;
try {
messages = JSON.parse(result);
var header_messages = [];
var login_messages = [];
for (var i = 0; i < messages.length; ++i) {
if (messages[i]['message_type'] == 'header') {
header_messages.push(messages[i]);
} else if (messages[i]['message_type'] == 'login') {
login_messages.push(messages[i]);
}
}
//TODO see if we can remove this hard coupling to classes
var hm = $('#header_messages');
self.integrateMessages(hm, header_messages);
var lm = $('#login_messages');
self.integrateMessages(lm, login_messages);
} catch (err) {
console.log("ERROR: " + err);
}
},
error: function (request, status, error) {
if (status === "timeout") {
console.log({"type": "flash", "default" : "database_timeout", "priority": 2});
} else {
console.log("unable to get notifications");
console.log(DB_ADDRESS + url + ": " + error);
}
}
});
} else {
console.log("Database has not been initialised");
}
},
// takes a jQuery elem
integrateMessages: function(elem, messages, classNames) {
var message, preformat;
// empty container because prototype is destined to be called periodically
elem.empty();
//set default classes if they are undefined
classNames = classNames === undefined ? {'urgent': 'urgent_message', 'info': 'info_message'} : classNames;
if (messages.length > 0) {
for (var i=0; i < messages.length; ++i) {
message = document.createElement('div');
message.className = classNames[messages[i]['priority']] + " notification";
$(message).attr('onclick', "db.call('notification/info', {'id': '" + messages[i]['id'] + "'})");
$(message).append(
// message is sanitized by the server so we unescape the string to include links and formatting
document.createTextNode(unescape(messages[i]['title']))
);
elem.append(message);
}
elem.fadeIn();
} else {
// No messages to display so hide message container
elem.fadeOut();
}
},
//affiche la fenetre de dialogue avec le serveur et affiche ses réponses
display: function (msg) {
......@@ -1010,4 +1093,4 @@ function suggest_box(id, list) {
suggest_list.style.display = "none"
}, 200)
};
}
}
\ No newline at end of file
......@@ -91,4 +91,9 @@ var shortcut = new Shortcut()
//TODO
initMenu();
\ No newline at end of file
initMenu();
(function worker(){
db.loadNotifications();
setTimeout(worker, 300000);
})();
\ No newline at end of file
# coding: utf8
from datetime import date
import gluon.contrib.simplejson
if request.env.http_origin:
response.headers['Access-Control-Allow-Origin'] = request.env.http_origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Access-Control-Max-Age'] = 86400
ACCESS_DENIED = "access denied"
def index():
if not auth.is_admin() :
res = {"message": ACCESS_DENIED}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
query = db(db.notification).select(orderby=~db.notification.id)
return dict(message="Notifications",
query=query)
def info():
query = db.notification[request.vars['id']]
return dict(query=query)
# serve for to add a notification
def add():
if (auth.is_admin()):
return dict(message=T('add notification'))
res = {"message": ACCESS_DENIED}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
# validate the form the user has posted
def add_form():
if (not auth.is_admin()):
res = {"message": ACCESS_DENIED}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
error = ""
if request.vars['title'] =="":
error += "title needed, "
if request.vars["message_content"] == "" :
error += "message content needed, "
if request.vars["message_type"] == "" :
error += "type needed, "
if request.vars["priority"] == "" :
error += "priority needed, "
if request.vars["expiration"] != "" :
try:
datetime.datetime.strptime(""+request.vars['expiration'], '%Y-%m-%d')
except ValueError:
error += "date (wrong format)"
if error=="" :
id = db.notification.insert(title=request.vars["title"],
message_content=XML(request.vars["message_content"], sanitize=True).xml(),
message_type=request.vars["message_type"],
priority=request.vars["priority"],
expiration=request.vars["expiration"],
creator=auth.user_id)
res = {"redirect": "notification/index",
"args" : { "id" : id },
"message": "notification added"}
log.info(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
else :
res = {"success" : "false",
"message" : error}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
# edit existing notification
def edit():
if (auth.is_admin()):
return dict(message=T('edit notification'))
res = {"message": ACCESS_DENIED}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
# process submitted edit form
def edit_form():
if (not auth.is_admin()):
res = {"message": ACCESS_DENIED}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
error = ""
if request.vars["message_content"] == "" :
error += "message body needed, "
if request.vars["message_type"] == "" :
error += "type needed, "
if request.vars["priority"] == "" :
error += "priority needed, "
if request.vars["expiration"] != "" :
try:
datetime.datetime.strptime(""+request.vars['expiration'], '%Y-%m-%d')
except ValueError:
error += "date (wrong format)"
if error=="" :
db.notification[request.vars['id']] = dict(title=request.vars["title"],
message_content=XML(request.vars["message_content"], sanitize=True).xml(),
message_type=request.vars["message_type"],
priority=request.vars["priority"],
expiration=request.vars["expiration"])
res = {"redirect": "notification/index",
"args" : { "id" : request.vars['id'] },
"message": "notification updated"}
log.info(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
else :
res = {"success" : "false",
"message" : error}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
def delete():
if (not auth.is_admin()):
res = {"message": ACCESS_DENIED}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
db(db.notification.id==request.vars['id']).delete()
res = {"redirect": "notification/index",
"success": "true",
"message": "notification " + request.vars['id'] + " deleted"}
log.info(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
#
def get_active_notifications():
today = date.today()
if (request.vars['type'] is not None):
query = db(
((db.notification.expiration >= today)
| (db.notification.expiration == None))
& (db.notification.message_type == request.vars['type'])
).select()
else :
query = db(
(db.notification.expiration >= today) | (db.notification.expiration == None)
).select()
#TODO sanitize this response
return query.as_json()
......@@ -175,6 +175,14 @@ db.define_table('analysis_file',
uploadfolder=defs.DIR_RESULTS,
length=LENGTH_UPLOAD, autodelete=AUTODELETE))
db.define_table('notification',
Field('creator', 'integer'),
Field('title', 'string'),
Field('message_content', 'text'),
Field('message_type', 'string'),
Field('priority', 'string'),
Field('expiration', 'datetime'))
## after defining tables, uncomment below to enable auditing
auth.enable_record_versioning(db)
......
......@@ -8,6 +8,7 @@
<a class="button" onclick="db.call('group/index');">groups</a>
<a class="button" onclick="db.call('user/index');">users</a>
<a class="button" onclick="db.call('admin/index');">admin</a>
<a class="button" onclick="db.call('notification/index');">notifications</a>
- - -
{{pass}}
</div>
......
{{extend 'db_layout.html'}}
{{extend 'db_layout.html'}}
<h2>{{=T( request.args(0).replace('_',' ').capitalize() )}}</h2>
......@@ -35,7 +35,7 @@ demo login: <i>demo@vidjil.org</i>
You can <a href="mailto:contact@vidjil.org?Subject=%5BVidjil%5D%20Account%20on%20rbx.vidjil.org&Body=%0ADear%20Vidjil%20Team%2C%0A%0AI%20would%20like%20to%20have%20an%20account%20on%20the%20Vidjil%20server.%0A%0A">request an account</a> on this server to test your data.
</p>
<div id="login_messages" class="message_container"></div>
<hr />
<h3>About</h3>
......
{{extend 'db_layout.html'}}
<h3>{{=message}}</h3>
<form id="data_form" action="DB_ADDRESS/notification/add_form" enctype="multipart/form-data" method="post">
<table>
<tbody>
<tr>
<td ><label for="notification_message_type" id="notification_message_type__label">type: </label></td>
<td >
{{=SELECT(
OPTION('Login page', _value='login'),
OPTION('Header', _value='header'),
_name="message_type"
)}}
<span>*</span></td>
<td ></td>
</tr>
<tr>
<td><label for="notification_priority" id="notification_priority__label">priority: </label></td>
<td>
{{=SELECT(
OPTION('Information', _value='info'),
OPTION('Urgent', _value='urgent'),
_name="priority"
)}}
<span>*</span></td>
<td></td>
</tr>
<tr>
<td><label for="notification_title" id="notification_title__label">title: </label></td>
<td><input class="string" id="notification_title" name="title" type="text" value="" /><span>*</span></td>
<td></td>
</tr>
<tr>
<td><label for="notification_message_content" id="notification_message_content__label">message: </label></td>
<td><textarea class="string" id="notification_message_content" name="message_content" type="text" value=""></textarea><span>*</span></td>
<td></td>
</tr>
<tr>
<td><label for="notification_expiration" id="notification_expiration__label">expiration date: </label></td>
<td><input class="date" id="notification_expiration" name="expiration" type="text" value=""><span>(format: yyyy-mm-dd)</span></td>
<td></td>
</tr>
<tr>
<td></td>
<td ><input type="submit" value="save" class="btn"></td>
<td></td>
</tr>
</tbody>
</table>
</form>
<div>
(* required fields)
</div>
{{extend 'db_layout.html'}}
{{
info = db.notification[request.vars["id"]]
}}
<h3>{{=message}}</h3>
<form id="data_form" action="DB_ADDRESS/notification/edit_form" enctype="multipart/form-data" method="post">
<input type="HIDDEN" id="notification_id" name="id" value="{{=request.vars["id"]}}">
<table>
<tbody>
<tr>
<td ><label for="notification_message_type" id="notification_message_type__label">type: </label></td>
<td >
{{=SELECT(
OPTION('Login page', _value='login'),
OPTION('Header', _value='header'),
value=info.message_type,
_name="message_type"
)}}
<span>*</span></td>
<td ></td>
</tr>
<tr>
<td><label for="notification_priority" id="notification_priority__label">priority: </label></td>
<td>
{{=SELECT(
OPTION('Information', _value='info'),
OPTION('Urgent', _value='urgent'),
value=info.priority,
_name="priority"
)}}
<span>*</span></td>
<td></td>
</tr>
<tr>
<td><label for="notification_title" id="notification_title__label">title: </label></td>
<td><input class="string" id="notification_title" name="title" type="text" value="{{=info.title}}" /><span>*</span></td>
<td></td>
</tr>