Commit 9675647e by Pablo Molina

Merge branch 'issue/386' into develop

Conflicts:
	sails/src/assets/app/modules/supervisor/controllers/students.js
parents d0f6aea1 e39c1c30
/* global Student, PictoCoreCat, VStuLastInstruction, StuPicto, StuSup, sailsTokenAuth, sails */
/**
/* StudentController
*
......@@ -12,40 +14,39 @@ module.exports = {
// expected params in post body: username, password
//
login: function (req, res) {
var bcrypt = require('bcrypt-nodejs');
var username = req.body.username;
var password = req.body.password;
// no credentials
if (!username || !password)
return res.json(401, {error: 'No credentials sent'});
if (!username || !password) {
return res.json(401, { error: 'No credentials sent' });
}
// search user by username
Student.findOneByUsername(username).exec(function (err, stu) {
if (err)
if (err) {
return res.json(500, { error: 'DB error' });
}
if (!stu)
return res.json(401, {error: 'User not found'});
if (!stu) {
return res.json(401, { error: 'User not found' });
}
// if found, check password in encrypted form
bcrypt.compare(password, stu.password, function (err, match) {
if (err)
if (err) {
return res.json(500, { error: 'Server error' });
}
if (match) { // password match
// all supervisor information is passed as payload to the token
stu.isStudent = true;
return res.json({
user: stu,
token: sailsTokenAuth.issueToken(stu, sails.config.jwt.expiresInMinutes),
server_time: (new Date()).getTime()
});
} else {
return res.json(401, { error: 'Invalid password' });
}
......@@ -57,27 +58,30 @@ module.exports = {
// Get a student with the given id, population also the office and the last method/instruction
//
getInfo: function(req, res) {
if (!req.params.id_stu)
return res.json(500, {err: 'no id_stu'});
if (!req.params.id_stu) {
return res.json(500, { err: 'no id_stu' });
}
Student.findOne(req.params.id_stu).populate('office').populate('stuSup').exec(function(err, student){
Student.findOne(req.params.id_stu).populate('office').populate('stuSup')
.exec(function(err, student){
if (err) {
sails.log.debug(err);
console.log(JSON.stringify(err));
return res.json(500, err);
}
if (!student)
if (!student) {
return res(500, {err: JSON.stringify(err)});
}
// get student last method/instruction
VStuLastInstruction.findOne({ student : student.id }).exec(function(err, row) {
VStuLastInstruction.findOne({ student : student.id })
.exec(function(err, row) {
if (err) {
sails.log.debug(err);
console.log(JSON.stringify(err));
return res.json(500, err);
}
if (!row || row.length == 0) {
if (!row || row.length === 0) {
student.current_method = 'no_method';
student.current_instruction = 'no_instruction';
} else {
......@@ -99,7 +103,6 @@ module.exports = {
if (err) {
sails.log.debug(err);
console.log(JSON.stringify(err));
return res.json(500, err);
}
if (created) {
......@@ -107,7 +110,6 @@ module.exports = {
// Assign the initial collection of pictos to the student
PictoCoreCat.find().exec(function(err, pictoCoreCat) {
if (err || !pictoCoreCat || pictoCoreCat.length == 0){
sails.log.debug("PictoCoreCat: " + err);
return;
......@@ -116,11 +118,9 @@ module.exports = {
sails.log.debug("PictoCoreCat Length: " + pictoCoreCat.length);
sails.log.debug(pictoCoreCat);
// Every picto from 'picto_core_cat' is going to be created
// in 'stu_picto'
for (var i = 0; i < pictoCoreCat.length; i++) {
sails.log.debug("Loop: " + i);
sails.log.debug("Picto Category: " + pictoCoreCat[i].category);
sails.log.debug("User id: " + created.id);
......@@ -136,14 +136,17 @@ module.exports = {
color: pictoCoreCat[i].color
}
}).exec(function(err, added){
if(err) sails.log.debug("StuPicto.create: " + err);
if(added) sails.log.debug("Picto " + added.picto + " added to student " + created.id + " with attributes: " + JSON.stringify(added.attributes));
if(err) sails.log.debug('StuPicto.create: ' + err);
if(added) {
sails.log.debug(
'Picto ' + added.picto +
' added to student ' + created.id +
' with attributes: ' + JSON.stringify(added.attributes));
}
});
}
});
return res.json({student: created});
return res.json({ student: created });
}
});
},
......@@ -223,21 +226,55 @@ module.exports = {
});
},
// Add action
// Create a relation between supervisor and student (stu_sup)
//
/**
* Creates a relation between the student and a given supervisor.
* It broadcasts the even linkSupervisorToStudent to both the student room
* and the supervisor room.
*/
link_supervisor: function(req, res) {
var params = req.allParams();
console.log(JSON.stringify(params));
StuSup.create({student: params.id_stu, supervisor: params.id_sup}).exec(function (err, created) {
StuSup.create({
student: params.id_stu,
supervisor: params.id_sup
}).exec(function (err, created) {
var response;
var socketToOmit;
var linkSupervisorToStudentEvent;
if (err) {
console.log(err);
return res.json(500, {error: 'Relation student/supervisor NO created'});
response = res.serverError(
'Relation student/supervisor not created: ' + err
);
}
if (created) {
console.log("Relation student/supervisor created" );
return res.json(created);
socketToOmit = (req.isSocket) ? req.socket : undefined;
linkSupervisorToStudentEvent = sails.hooks.events.linkSupervisorToStudent(
params.id_sup,
params.id_stu
);
sails.hooks.events.broadcastEvent(
sails.hooks.rooms.supervisor(params.id_sup),
linkSupervisorToStudentEvent,
socketToOmit
);
sails.hooks.events.broadcastEvent(
sails.hooks.rooms.student(params.id_stu),
linkSupervisorToStudentEvent,
socketToOmit
);
response = res.ok(created);
} else {
response = res.serverError(
'Relation student/supervisor not created, ' +
'but no error was received'
);
}
return response;
});
},
......
......@@ -381,5 +381,59 @@ module.exports = {
} , 5);
}
);
},
/**
* Subscribe to supervisor's room
* @param {Object} request must have the following format:
*
* {
* action: subscribe,
* attributes: { id_sup: '' }
* }
*
* @param {Object} response
* @return {Object} response
*/
subscribe: function(request, response) {
var action = request.param('action');
var attributes = request.param('attributes');
if (action === 'subscribe' && request.isSocket && attributes.id_sup) {
sails.hooks.rooms.subscribeToRoom(
sails.hooks.rooms.supervisor(attributes.id_sup),
request.socket
);
return response.ok();
} else {
return response.badRequest();
}
},
/**
* Unsubscribe from supervisor's room
* @param {Object} request must have the following format:
*
* {
* action: unsubscribe,
* attributes: { id_sup: '' }
* }
*
* @param {Object} response
* @return {Object} response
*/
unsubscribe: function(request, response) {
var action = request.param('action');
var attributes = request.param('attributes');
if (action === 'unsubscribe' && request.isSocket && attributes.id_sup) {
sails.hooks.rooms.unsubscribeFromRoom(
sails.hooks.rooms.supervisor(attributes.ip_sup),
request.socket
);
return response.ok();
} else {
return response.badRequest();
}
}
};
/**
* Functions defining the events sent inside the sockets.
* In order to create a new event you have to call the corresponding
* function with the necesary parameters, and you will receive an Object:
*
* {
* name: 'eventName',
* data: 'eventData'
* }
*
* Where 'name' is the name of the event and 'data' is the block of
* information to send.
* @type {Object}
*/
module.exports = function eventsHook (sails) {
return {
/**
* Speacial Function wrapping sails.sockets.broadcast that uses a room
* from sails.hooks.rooms and an event from sails.hooks.events in
* order to broadcast information. This also logs the event as debug
* level information with the message:
*
* "websocketEvent": {
* "room",
* "name",
* "data": { ... }
* }
*
* @param {RoomId} rooms RoomID or IDs where the event will be sent
* @param {Object} event A tuple {name, data} returned by a
* function inside sails.sockets.events
* @param {socket} socketToOmit If specified, this socket will not receive
* the event
*/
broadcastEvent: function (rooms, event, socketToOmit) {
// Ensure rooms is an Array
if (!(rooms instanceof Array)) {
rooms = [rooms];
}
sails.log.debug('"websocketEvent":', JSON.stringify({
rooms: rooms,
name: event.name,
data: event.data,
socketToOmit: socketToOmit ? socketToOmit.id : undefined
}));
sails.sockets.broadcast(
rooms,
event.name,
event.data,
socketToOmit || undefined
);
},
/**
* A supervisor has been linked to a student.
* @param {ID} supId ID of the supervisor
* @param {ID} stu_id ID of the student
* @return {Object} {name, data}
*/
linkSupervisorToStudent: function (supId, stuId) {
return {
name: 'linkSupervisorToStudent',
data: {
sup_id: supId,
stu_id: stuId
}
};
},
/**
* Someone has subscribed/unsubscribed to/from a room. The number
* of subscribes is sent
* @param {number} subscriberCount Number of subscribers
* @return {Object} {name, data}
*/
roomSubscribersChange: function (subscriberCount) {
return {
name: 'update_peers',
data: {
count: subscriberCount
}
};
}
};
};
/**
* This hook is used for managing the rooms IDs.
* Every room created should call one of this functions in order
* to obtain an ID.
* @type {Object}
*/
module.exports = function roomsHook (sails) {
return {
/**
* Special function that subscribes a socket to a given room.
* This creates the connection and logs to debug with this format:
*
* {
* websocketSubscribe: {
* room,
* socket
* }
* }
*
* @param {RoomID} room Room to subscribe
* @param {Socket} socket Socket added to the subscription
*/
subscribeToRoom: function (room, socket) {
sails.log.debug('"websocketSubscribe":', JSON.stringify({
room: room,
socket: socket.id
}));
sails.sockets.join(socket, room, function () {
sails.io.sockets.in(room).clients(function(error, ids) {
if (!error) {
sails.hooks.events.broadcastEvent(
room,
sails.hooks.events.roomSubscribersChange(ids.length)
);
}
});
});
},
/**
* Special function that unsubscribes a socket from a given room.
* This remotes the connection and logs to debug with this format:
*
* {
* websocketUnsubscribe: {
* room,
* socket
* }
* }
*
* @param {RoomID} room Room to unsubscribe from
* @param {Socket} socket Socket removed from the subscription
*/
unsubscribeFromRoom: function (room, socket) {
sails.log.debug('"websocketUnsubscribe":', JSON.stringify({
room: room,
socket: socket.id
}));
sails.sockets.leave(socket, room, function () {
sails.io.sockets.in(room).clients(function(error, ids) {
if (!error) {
sails.hooks.events.broadcastEvent(
room,
sails.hooks.events.roomSubscribersChange(ids.length)
);
}
});
});
},
/**
* Student related rooms
* @param {ID} studentId Student's ID used for creating a room.
* @return {RoomID} RoomID generated
*/
student: function (studentId) {
return 'studentRoom' + studentId;
},
/**
* Supervisor related rooms
* @param {ID} supervisorId Supervisor's ID used for creating a room.
* @return {RoomID} RoomID generated
*/
supervisor: function (supervisorId) {
return 'supervisorRoom' + supervisorId;
}
};
};
'use strict';
/* Services */
/* global io */
//
// This defines an interceptor service in every HTTP request to add session
// token in the header and in every HTTP response to handle non authorized
// connections
//
'use strict';
var module = angular.module('dashboardServices', []);
angular.module('dashboardServices', [])
.service('authInterceptor', function ($rootScope, $q, $window) {
/**
* This defines an interceptor service in every HTTP request to add session
* token in the header and in every HTTP response to handle non authorized
* connections.
*/
module.factory('AuthInterceptorService', function ($rootScope, $q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
......@@ -30,7 +29,68 @@ angular.module('dashboardServices', [])
return $q.reject(rejection);
}
};
})
.config(function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
});
// Add AuthInterceptorService to $httpProvider.interceptors
module.config(function ($httpProvider) {
$httpProvider.interceptors.push('AuthInterceptorService');
});
/**
* IOService that must be used as communication with the server.
* It allows to subscribe/unsubscribe to/from rooms and listen/stop listening
* to events.
*/
module.factory('IOService', function ($window) {
return {
/**
* Send a get request to the server via websocket
* @param {String} url Url of the request
* @param {Function} callback Function to be called on response
*/
get: function(url, callback) {
io.socket.get(url, { token: $window.sessionStorage.token }, callback);
},
/**
* Send a post request to the server via websocket
* @param {String} url Url of the request
* @param {Object} data If this parameter is not an object, it will
* be wrapped inside an object:
* { data, token }, otherwise the token will be
* appended to the parameters
* @param {Function} callback Function to be called on response
*/
post: function (url, data, callback) {
// Create the request data and convert it to an object if necesary
// Then append the token
var requestData = data;
if (typeof requestData !== 'object') {
requestData = { data: data };
}
requestData.token = $window.sessionStorage.token;
io.socket.post(url, requestData, callback);
},
/**
* Subscribes to an event with the given function using websockets
* @param {String} event Event to subscribe to
* @param {Function} callback Function to be executed when the event occurs
*/
on: function (event, callback) {
io.socket.on(event, callback);
},
/**
* Unsubscribe from an event
* @param {String} event Event to unsubscribe from
* @param {Function} callback If specified, only this funcion will be
* unsuscribed from the event, otherwise all
* functions subscribed to this event will be
* removed
*/
off: function (event, callback) {
io.socket.off(event, callback);
}
};
});
......@@ -12,7 +12,8 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
config,
$window,
$translate,
ngToast) {
ngToast,
IOService) {
$scope.formdatastudent = {
username: '',
password: '',
......@@ -148,6 +149,10 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
$translate('student_deleted').then(function (translation) {
ngToast.success({ content: translation });
});
IOService.post('/stu/unsubscribe', {
action: 'unsubscribe'
});
})
.error(function () {
$translate('student_not_deleted').then(function (translation) {
......@@ -157,7 +162,30 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
}
};
io.socket.post('/stu/unsubscribe', { action: 'unsubscribe' });
// When a new student is added to the supervisor, we should update
// the student list if necesary
IOService.on('linkSupervisorToStudent', function (eventData) {
eventData.sup_id = parseInt(eventData.sup_id, 10);
eventData.stu_id = parseInt(eventData.stu_id, 10);
if (eventData.sup_id === $scope.user.id) {
IOService.get('/stu/' + eventData.stu_id, function (studentData) {
var i;
var studentAlreadyAdded = false;
for (i = 0; i < $scope.students.length; i++) {
if ($scope.students[i].id === eventData.stu_id) {
studentAlreadyAdded = true;
break;
}
}
if (!studentAlreadyAdded) {
$scope.students.push(studentData);
$scope.$apply();
}
});
}
});
});
/**
......
/* global io */
'use strict';
//-----------------------
// Supervisor Controller
//-----------------------
dashboardControllers.controller('SupervisorCtrl', function SupervisorCtrl($scope, $window, $location) {
dashboardControllers.controller('SupervisorCtrl', function SupervisorCtrl($scope, $window, $location, IOService) {
// Restore user data from session
var user = JSON.parse($window.sessionStorage.user);
......@@ -22,4 +24,11 @@ dashboardControllers.controller('SupervisorCtrl', function SupervisorCtrl($scope
$location.path('/setup');
};
// Subscribe to the supervisor's room
IOService.post('/sup/subscribe', {
action: 'subscribe',
attributes: {
id_sup: $scope.user.id
}
});
});
......@@ -45,7 +45,9 @@ module.exports.policies = {
destroy: ['tokenAuth', 'isAdmin'],
students: ['tokenAuth'],
pictos: ['tokenAuth'],
upload: ['tokenAuth']
upload: ['tokenAuth'],
subscribe: ['tokenAuth'],
unsubscribe: ['tokenAuth']
},
DeviceController: {
register: true,
......
......@@ -22,16 +22,7 @@
module.exports.routes = {
/***************************************************************************
* *
* Make the view located at `views/homepage.ejs` (or `views/homepage.jade`, *
* etc. depending on your default view engine) your home page. *
* *
* (Alternatively, remove this and add an `index.html` file in your *
* `assets` directory) *
* *
***************************************************************************/
// Supervisor
'post /sup/login': 'SupervisorController.login',
'post /sup': 'SupervisorController.create',
'put /sup': 'SupervisorController.update',
......@@ -40,13 +31,15 @@ module.exports.routes = {
'get /sup/get/:id': 'SupervisorController.findOne',
'post /sup/activate': 'SupervisorController.activate',
'get /sup/:id/students': 'SupervisorController.students',
'post /sup/subscribe': 'SupervisorController.subscribe',
'post /sup/unsubscribe': 'SupervisorController.unsubscribe',
'post /sup/upload': 'SupervisorController.upload', // upload profile img file
// Pictos
'get /sup/:id/pictos': 'SupervisorController.pictos',
'get /sup/:id/pic_categories/:id_cat': 'Picto.categories',
'get /sup/:id/pic_fromcategory/:id_cat': 'Picto.fromcategory',
'post /sup/upload': 'SupervisorController.upload', // upload profile img file
// Pictogram administration
'post /admin/login': 'AdminController.login',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment