students signup ready

parent fe86a2d6
......@@ -84,27 +84,26 @@ END;;
-- Integrity rule 3: office.current_enrolments and supervisor assigments updating.
DROP TRIGGER IF EXISTS TRG_MODIFY_STUDENT_ENROLMENTS;
CREATE TRIGGER TRG_MODIFY_STUDENT_ENROLMENTS
AFTER UPDATE ON student
FOR EACH ROW
thisTrigger: BEGIN
IF ((@TRIGGER_CHECKS = FALSE)
OR (@TRIGGER_AFTER_UPDATE_CHECKS = FALSE))
AND (USER() = 'root@localhost')
THEN
LEAVE thisTrigger;
END IF;
IF NOT (old.id_off<=>new.id_off) THEN
IF (old.id_off IS NOT NULL) THEN
DELETE
stu_sup
FROM
stu_sup INNER JOIN supervisor ON (stu_sup.id_sup=supervisor.id)
WHERE
id_stu=new.id AND old.id_off=supervisor.id_off;
END IF;
END IF;
END;;
DELIMITER ;
-- CREATE TRIGGER TRG_MODIFY_STUDENT_ENROLMENTS
-- AFTER UPDATE ON student
-- FOR EACH ROW
-- thisTrigger: BEGIN
-- IF ((@TRIGGER_CHECKS = FALSE)
-- OR (@TRIGGER_AFTER_UPDATE_CHECKS = FALSE))
-- AND (USER() = 'root@localhost')
-- THEN
-- LEAVE thisTrigger;
-- END IF;
--
-- IF NOT (old.id_off<=>new.id_off) THEN
-- IF (old.id_off IS NOT NULL) THEN
-- DELETE
-- stu_sup
-- FROM
-- stu_sup INNER JOIN supervisor ON (stu_sup.id_sup=supervisor.id)
-- WHERE
-- id_stu=new.id AND old.id_off=supervisor.id_off;
-- END IF;
-- END IF;
-- END;;
# Changes
#Database changes
#Para actualizar instalaciones antiguas ejecutar en vagrant/roles/database/files
# mysql -u root -p pictodb < upgrade.sql
# Database changes
Para actualizar instalaciones antiguas ejecutar en vagrant/roles/database/files
mysql -u root -p pictodb < upgrade.sql
Relanzar trigger-enrolments-integrity-constraints
......@@ -2,11 +2,11 @@
Picto */
/**
/* StudentController
*
* @description :: Server-side logic for managing students
* @help :: See http://links.sailsjs.org/docs/controllers
*/
/* StudentController
*
* @description :: Server-side logic for managing students
* @help :: See http://links.sailsjs.org/docs/controllers
*/
module.exports = {
......@@ -61,7 +61,7 @@ module.exports = {
if (bcrypt.compareSync(req.body.password, student.password)) {
student.isStudent = true;
if (!student.license || !student.license[0]) {
sails.log.error(`Tried to login with non valid license ${req.body.username}`);
sails.log.error('Tried to login with non valid license ${req.body.username}');
res.unauthorized("Student has an invalid license");
} else {
var hasExpired = student.license[0].hasExpired();
......@@ -75,17 +75,16 @@ module.exports = {
});
}
} else {
sails.log.error(`Invalid student login: user ${student.username}, password\
"${req.body.password}"`);
sails.log.error('Invalid student login: user ${student.username}, password "${req.body.password}"');
res.unauthorized("Invalid username/password");
}
} else {
sails.log.error(`Tried to login as non-existing student ${req.body.username}`);
sails.log.error('Tried to login as non-existing student ${req.body.username}');
res.notFound("Student not found");
}
})
.catch(function (err) {
sails.log.error(`Error getting student ${req.body.username} for login: ` + err);
sails.log.error('Error getting student ${req.body.username} for login: ' + err);
res.serverError("Error when connecting to database");
});
},
......@@ -148,30 +147,15 @@ module.exports = {
.then(function (stu_sup) {
return stu_sup;
})
.error(err => {throw err});
.catch(function (err) { throw err });
return [student, stu_sup];
})
.spread(function (student, stu_sup) {
// requester has no relation
student.supervision = -1;
if (!stu_sup && req.token.office && student.office == req.token.office.id && req.token.isSupAdmin)
student.supervision = 0; // requester is admin of the office
else if (stu_sup && !req.token.office)
student.supervision = 1; // requester is tutor of the studend
else if (stu_sup && req.token.office)
student.supervision = 2; // requester is supervisor of student
else if (req.token.isStudent && req.token.id == student.id)
student.supervision = 3 // requester is the student himself
if (student.supervision == -1) // should not hace access!!!
return res.forbidden("Access to this student should not be granted to you");
// Promisify asynchronous call to Student.supervisors
var supervisors = new Promise(function(resolve, reject) {
Student.supervisors(student.id, (err, ss) => {
var supervisors = new Promise(function (resolve, reject) {
Student.supervisors(student.id, function (err, ss) {
if (err) return reject(err);
return resolve(ss);
});
......@@ -179,10 +163,9 @@ module.exports = {
return [student, supervisors];
})
.spread((student, supervisors) => {
.spread(function (student, supervisors) {
student = student.toJSON();
student.supervisors = supervisors;
console.log(JSON.stringify(supervisors));
console.log(JSON.stringify(student.supervisors));
return res.ok(student);
})
.catch(function (err) {
......@@ -209,10 +192,11 @@ module.exports = {
// License number, if passed is used, otherwise, a trial one is generated
//
new Promise(function(resolve, reject) {
if (params.license)
resolve(params.license);
if (params.license_number)
resolve(params.license_number);
else {
License.newTrial(params.id_sup, function(license, err) {
License.newTrial(params.id_sup, function(err, license) {
console.log(license.number);
if (err)
reject(err);
else
......@@ -220,12 +204,13 @@ module.exports = {
});
}
})
.then((license) => {
.then((license_number) => {
// Check license
License.isActivable(params.license, function(err) {
if (err)
return res.serverError(err);
License.isActivable(license_number, function(err) {
if (err) {
return res.badRequest(err.message);
}
// Create student
Student.create(params)
......@@ -233,22 +218,29 @@ module.exports = {
sails.log.debug('Student ' + created.id + ' created: ' + JSON.stringify(created));
// Activate license
License.activate(params.license, created.id, function(err, license) {
if (err)
return res.serverError(err);
License.activate(license_number, created.id, function(err, license) {
if (err) {
Student.destroy({id: created.id});
return res.badRequest(err);
}
created = created.toJSON();
created.license = license.toObject();
// Link to supervisor
StuSup.create({id_stu: created.id, id_sup: params.id_sup})
StuSup.create({student: created.id, supervisor: params.id_sup})
.then((stu_sup) => {
if (!stu_sup)
throw new Error("Unable to link to supervisor");
if (!stu_sup) {
Student.destroy({id: created.id});
return res.serverError("Unable to link to supervisor");
}
return res.ok(created);
})
.catch((err) => {throw err});
})
.catch((err) => {throw err});
.catch((err) => {
Student.destroy({id: created.id});
throw err
});
});
})
.catch(function(err) {
sails.log.debug(err.message);
......@@ -256,7 +248,7 @@ module.exports = {
});
});
})
.catch((err) => {res.serverError()};
.catch((err) => {res.serverError(err)});
},
/**
......@@ -463,53 +455,58 @@ module.exports = {
if (!params.id_sup || !params.id_stu && !params.license)
return res.badRequest("Invalid params");
var stu, stuSup;
//
// Get student ID either because we got it or through a license
// As it can be resolved through two possibilities, better to user promises!
// As it can be resolved through two possibilities, better to use promises!
//
new Promise(funcion(resolve, reject) {
var getStudent = new Promise(function(resolve, reject) {
if (!params.id_stu && params.license) {
// get student, license attribute will be completed
License.getStudent(params.license, function(err, stu) {
License.getStudent(params.license, function(err, s) {
if (err)
reject(err);
else
resolve(stu);
resolve(s);
});
} else
Student.findOne(params.id_stu)
.populate('license')
.then((stu) => {
if (!stu)
.then((s) => {
if (!s)
throw new Error("Student not found");
resolve(stu);
resolve(s);
})
.catch((err) => {reject(err)});
})
.then((stu) => {
});
getStudent
.then((s) => {
stu = s;
//
// Let's check it is not already linked
//
StuSup.findOne({ student: stu.id, supervisor: params.id_sup })
.then((stuSup) => {
// It was already there!
if (stuSup)
throw new Error("Already linked");
return StuSup.findOne({ student: stu.id, supervisor: params.id_sup });
})
.then((ss) => {
//
// Non existing relation, let's create it!
//
else {
if (ss)
throw new Error('Student already linked');
StuSup.create({
student: id_stu,
return StuSup.create({
student: stu.id,
supervisor: params.id_sup
});
})
.then(function (stuSup) {
if (!stuSup)
.then((ss) => {
if (!ss)
throw new Error('stusup not created');
stuSup = ss;
//
// Send sockets messages
//
......@@ -518,7 +515,6 @@ module.exports = {
stuSup.supervisor,
stuSup.student
);
sails.hooks.events.broadcastEvent(
sails.hooks.rooms.supervisor(stuSup.supervisor),
linkSupervisorToStudentEvent,
......@@ -534,12 +530,10 @@ module.exports = {
// Done!
//
return res.ok(stu);
}
})
.catch((err) => {throw err});
})
.catch((err) => {
res.serverError(err);
console.log("Error " + err.message);
return res.serverError(err);
});
},
......@@ -1066,7 +1060,7 @@ module.exports = {
})
.then((student) => {
if (!student) {
sails.log.error(`Student ${params.id_stu} not found`);
sails.log.error('Student ${params.id_stu} not found');
throw new Error("Student not found");
}
......@@ -1079,7 +1073,7 @@ module.exports = {
})
.spread((picto, student) => {
if (!picto) {
sails.log.error(`Picto ${params.id_picto} not found`);
sails.log.error('Picto ${params.id_picto} not found');
throw new Error("Picto not found");
}
......
......@@ -387,7 +387,6 @@ module.exports = {
delete supervisor.password;
if (req.body.password && req.body.password.length > 0)
supervisor.password = req.body.password;
console.log(supervisor.password);
supervisor.name = req.body.name || supervisor.name;
supervisor.surname = req.body.surname || supervisor.surname;
supervisor.gender = req.body.gender || supervisor.gender;
......
......@@ -69,18 +69,21 @@ module.exports = {
/**
Class methods
*/
hasExpired: function (license) {
return (new Date(license.expiration_ts) - new Date() < 0);
},
/**
* Generates a new trial license
* @param {ID} id_sup ID of the supervisor who creates the license
* @param {function} callback Callback function: license, err
* @param {function} callback Callback function: (err, license)
*/
newTrial: function(id_sup, callback) {
var now = new Date();
License.genLicenseNumber(function(number) {
if (!number)
callback(null, new Error("Unable to generate new license"));
callback(new Error("Unable to generate new license"), null);
License.create({
type: 'trial',
......@@ -90,10 +93,10 @@ module.exports = {
})
.then((l) => {
if (!l) throw new Error("Unable to create license");
callback(l, null);
callback(null, l);
})
.catch((err) => {
callback(null, err);
callback(err, null);
});
});
},
......@@ -136,7 +139,7 @@ module.exports = {
callback(license);
}
);
}
},
/**
......@@ -148,14 +151,16 @@ module.exports = {
getStudent: function(number, callback) {
License.findOne({ number: number })
.then((l) => {
if (!l || !l.activation_ts || !l.id_stu)
if (!l || !l.activation_ts || !l.student)
throw new Error("Invalid license: " + number);
// License ok, check student
Student.findOne(l.id_stu)
Student.findOne(l.student)
.then((s) => {
if (!s)
throw new Error("Student not found");
// convert model to normal object to be able to add attributes
s = s.toJSON();
s.license = l;
callback(null, s);
})
......@@ -176,6 +181,10 @@ module.exports = {
@param {function} callback Callback function with prototype: function(err, license)
*/
activate: function(number, id_stu, callback) {
if (!number || number.length < 16)
callback(new Error("Invalid license number"));
// Check license
License.findOne({ number: number })
.then((l) => {
......@@ -231,6 +240,7 @@ module.exports = {
* Callback function gets instantiated error if not available
*/
isActivable: function(number, cb) {
License.findOne({number: number})
.then ((l) => {
if (!l)
......
......@@ -109,8 +109,12 @@ module.exports = {
toJSON: function () {
var student = this.toObject();
student.pic = sails.config.pictogram.urls.getStudentAvatarUrl(student.pic);
if (student.license)
if (student.license) {
student.license = student.license[0] ? student.license[0] : null;
student.license.isValid = !License.hasExpired(student.license);
student.license.isTrial = student.license.type == 'trial';
student.license.isOfficial = student.license.type == 'official';
}
student.attributes = Student.getValidAttributes(student.attributes);
delete student.password;
return student;
......@@ -294,7 +298,8 @@ module.exports = {
if (!stuSups || stuSups.length == 0)
return callback(null, []);
var sups = stuSups.map((st) => {return st.supervisor});
// filter null entries and map them to the supervisor object
var sups = _.compact(stuSups).map((st) => {return st.supervisor});
return callback(null, sups);
});
},
......@@ -302,7 +307,6 @@ module.exports = {
//
// Class method for getting the list of therapists associated to a given
// student
// NOTE: A therapist is a supervisor assigned to an office
therapists: function(id_stu, callback) {
StuSup.find({id_stu: id_stu}).populate('supervisor').exec(function(err, stuSups) {
var l = [];
......@@ -310,18 +314,8 @@ module.exports = {
if (err || !stuSups || stuSups.length == 0)
return callback(err, l);
async.eachSeries(stuSups,
function(stuSup, next) {
// stuSup.supervisor.id > 0 for not retrieving the -1 default supervisor
if (stuSup.supervisor && stuSup.supervisor.office && stuSup.supervisor.id > 0) {
l.push(stuSup.supervisor);
}
next();
},
function (err) {
return callback(err, l);
}
);
var sups = _.compact(stuSups).filter((st) => {st.supervisor && st.supervisor.role == 'therapist'});
return callback(null, sups);
});
},
......@@ -329,7 +323,6 @@ module.exports = {
//
// Class method for getting the list of tutors associated to a given
// student
// NOTE: A tutor is a supervisor not assigned to any office
tutors: function(id_stu, callback) {
StuSup.find({id_stu: id_stu}).populate('supervisor').exec(function(err, stuSups) {
var l = [];
......@@ -337,18 +330,8 @@ module.exports = {
if (err || !stuSups || stuSups.length == 0)
return callback(err, l);
async.eachSeries(stuSups,
function(stuSup, next) {
// stuSup.supervisor.id > 0 for not retrieving the -1 default supervisor
if (stuSup.supervisor && !stuSup.supervisor.office && stuSup.supervisor.id > 0) {
l.push(stuSup.supervisor);
}
next();
},
function (err) {
return callback(err, l);
}
);
var sups = _.compact(stuSups).filter((st) => {st.supervisor && st.supervisor.role == 'tutor'});
return callback(null, sups);
});
},
......@@ -525,7 +508,10 @@ module.exports = {
});
},
// Removes logically a student
/**
* Removes logically a student
* The user name is set with a random prefix and the license is removed
*/
logical_delete: function(id_stu, cb) {
Student.findOne(id_stu).exec(function(err, student) {
if (err || !student)
......@@ -533,7 +519,6 @@ module.exports = {
Student.update(id_stu,
{
username: Math.floor((Math.random() * 100000000) + 1) + "_" + student.username,
id_off: null
})
.then((updated) => {
License.destroy({id_stu: id_stu}).exec(cb);
......
......@@ -138,7 +138,6 @@ module.exports = {
//
beforeCreate: function (attrs, next) {
var async = require('async');
console.log("-->\n" + JSON.stringify(attrs));
async.series(
[
function (cb) {
......@@ -241,25 +240,24 @@ module.exports = {
})
.spread(function (sup, stuSups) {
async.eachSeries(stuSups, function(stuSup, next_cb) {
// Filter logically deleted students
if (stuSup.student.office == null)
return next_cb();
// set current method and instruction if any
Student.findOne(stuSup.student.id)
.populate('lastInstruction')
.populate('license')
.then(function (s) {
if (!s)
return next_cb();
s = s.toJSON();
s.current_method = s.lastInstruction[0] ? s.lastInstruction[0].met_name : "no_method";
s.current_instruction = s.lastInstruction[0] ? s.lastInstruction[0].ins_name : "no_instruction";
if (typeof(s.license[0]) != 'undefined') {
s.licenseIsValid = new Date(s.license[0].expiration_ts) - new Date() > 0 ? true : false;
s.license = s.license[0];
} else {
s.licenseIsValid = false;
s.license = null;
}
s.supervision = sup.office ? 2 : 1; // if Supervisor has office, then is a therapist (2), a tutor (1) otherwise
if (!s.license)
return next_cb();
//
// El alumno tiene licencia, es válida y no es de prueba
//
if (s.license.isValid || s.license.isOfficial)
l.push(s);
next_cb();
});
......
......@@ -17,7 +17,7 @@ module.exports = function isStudentOrSupervisorOfStudent (req, res, next) {
return res.json(401, {error: "This student has no supervisors associated"});
// if supervisor is not in the list of supervisors
if (sups.map(function(e) {return e.id}).indexOf(req.token.id) < 0)
if (_.compact(sups).map(function(e) {return e.id}).indexOf(req.token.id) < 0)
return res.json(401, {error: 'Access denied 3'});
// Finally, if the user has a clean record, we'll call the `next()` function
......
......@@ -5,17 +5,22 @@ module.exports = function isSupervisorOfStudent(req, res, next) {
if (!req.params.id_stu || !req.token.id)
return res.json(401, {error: 'Access denied'});
// Get list of supervisors for the student
Student.supervisors(req.params.id_stu, function(err, sups) {
if (err || !sups || sups.length == 0)
if (err)
return res.json(401, {error: err});
if (!sups || sups.length == 0)
return res.json(401, {error: "This student has no supervisors associated"});
if (sups.map(function(e) {return e.id}).indexOf(req.token.id) >= 0)
return next(); // Is Supervisor of Student
// if supervisor is not in the list of supervisors
if (_.compact(sups).map(function(e) {return e.id}).indexOf(req.token.id) < 0)
return res.json(401, {error: 'Access denied 3'});
return res.json(401, {error: "No valid credentials"});
})
.catch((err) => {
return res.json(401, {error: "Student not found"});
// Finally, if the user has a clean record, we'll call the `next()` function
// to let them through to the next policy or our controller
next();
});
};
......@@ -27,7 +27,7 @@ module.exports = function badRequest(data, options) {
// Log error to console
if (data !== undefined) {
sails.log.verbose('Sending 400 ("Bad Request") response: \n',data);
sails.log.verbose('Sending 400 ("Bad Request") response: \n', data);
}
else sails.log.verbose('Sending 400 ("Bad Request") response');
......
{
"account": "Account",
"account_activate": "The account has been activated",
"account_available": "Account available",
"account_desc_office": "Manage students, intervention teams along with all therapist related functionalities.",
"account_desc_therapist": "Manage pictograms, devices, record supervised sessions and get progress reports.",
"account_desc_tutor": "Configure your child's pictograms and his/her communication device.",
......@@ -188,6 +189,7 @@
"keep_strip_and_deliveries": "Keep strip and allow several deliveries",
"keep_strip_and_one_delivery": "Keep strip and allow only one delivery",
"language": "Language",
"language_change_warning": "Your preferred language has been changed. Session restarting is recommended.",
"large": "Large",
"large_picto": "Large pictograms",
"last_session": "Last session",
......@@ -201,9 +203,12 @@
"licenses": "Licenses",
"license_already_activated": "License already activated",
"license_created": "License created",
"license_expires": "PRO license expires on ",
"license_expired": "PRO license expired, you can",
"license_expired_official": "PRO license expired. To maintain access to therapeutical functionalities you can",
"license_expired_trial": "Trial license expired. To keep the account you should",
"license_expired_buy": "buy one",
"license_expired_renew": "renew it",
"license_expires_official": "PRO license expires on ",
"license_expires_trial": "Trial license expires on ",
"license_invalid": "Invalid license number",
"license_missing": "Account without PRO license",
"license_number": "License number",
......@@ -438,6 +443,7 @@
"tpl_date_frame": "de {{ begin | date:'dd-MM-yyyy' }} a {{ end | date:'dd-MM-yyyy' }}",
"tpl_day": "{{ day | date:'yyyy-MM-dd' }}",
"tpl_hours_frame": "from {{ begin | date:'HH:mm:ss' }} to {{ end | date:'HH:mm:ss' }}",
"trial_license": "Trial license",
"tries": "Tries",
"tries_done": "Tries done",
"tries_length": "Tries length",
......
{
"account": "Cuenta",
"account_activate": "La cuenta ha sido activada",
"account_available": "Cuenta disponible",
"account_desc_office": "Gestione alumnos y equipos de intervención, además de todas las funcionalidades propias de un terapeuta.",
"account_desc_tutor": "Gestione los pictogramas de su hijo o hija y configure su dispositivo de comunicación.",
"account_desc_therapist": "Gestione pictogramas, dispositivos, grabe sesiones de terapia y obtenga estadísticas de progreso.",
......@@ -188,6 +189,7 @@
"keep_strip_and_deliveries": "Mantener cinta y permitir varias entregas",
"keep_strip_and_one_delivery": "Mantener cinta y permitir sólo una entrega",
"language": "Idioma",
"language_change_warning": "Ha cambiado el idioma. Se recomienda reiniciar sesión.",
"large": "Grande",
"large_picto": "Pictogramas grandes",
"last_session": "Última sesión",
......@@ -201,10 +203,13 @@
"licenses": "Licencias",
"licenses_left": "{{number}} licencias disponibles",
"license_already_activated": "Licencia ya activada previamente",
"license_expires": "La licencia PRO expira el ",
"license_expired": "La licencia PRO expiró, puede",
"license_expired_renew": "renovarla",
"license_created": "Licencia creada",
"license_expired_official": "La licencia PRO expiró. Para mantener acceso a funcionalidades terapéuticas puede",
"license_expired_trial": "La licencia de prueba expiró. Para mantener la cuenta debería",
"license_expired_buy": "adquirir una",
"license_expired_renew": "renovarla",
"license_expires_official": "La licencia PRO finaliza el ",
"license_expires_trial": "La licencia de prueba finaliza el ",
"license_invalid": "Licencia inválida",
"license_number": "Número de licencia",
"license_pro": "Licencia Pictogram PRO",
......@@ -438,6 +443,7 @@
"tpl_date_frame": "de {{ begin | date:'dd-MM-yyyy' }} a {{ end | date:'dd-MM-yyyy' }}",
"tpl_day": "{{ day | date:'dd-MM-yyyy' }}",
"tpl_hours_frame": "de {{ begin | date:'HH:mm:ss' }} a {{ end | date:'HH:mm:ss' }}",
"trial_license": "Licencia de prueba",
"tries": "Ensayos",
"tries_done": "Ensayos realizados",
"tries_length": "Duración ensayos",
......
......@@ -32,10 +32,6 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl(
number: ''
},
license_expired: false,
office: {
id: '',
name: ''
},
stuSup: [],
pcb_count: 0,
pdb_count: 1
......@@ -84,7 +80,6 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl(
$scope.studentData.license_expired = new Date($scope.studentData.license.expiration_ts) - new Date() < 0;
moment.locale($translate.use().substr(0, 2));
$scope.studentData.expiration_date = moment($scope.studentData.license.expiration_ts).format('L');
console.log("updateLicenseExpiration");
};
// ----------------------------------------------------------------------
......@@ -105,8 +100,6 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl(
$scope.studentData.gender = data.gender;
$scope.studentData.lang = data.lang;
$scope.studentData.notes = data.notes;
$scope.studentData.office.id = data.office.id;
$scope.studentData.office.name = data.office.name;
$scope.studentData.stuSup = data.stuSup;
$scope.studentData.attributes = data.attributes;
$scope.studentData.current_method = data.current_method;
......
......@@ -8,7 +8,7 @@
<button class="btn btn-default" btn-radio="'device'" ng-model="section">
<i class="fa fa-tablet" aria-hidden="true"></i> {{ 'device' | translate }}
</button>
<button class="btn btn-default" btn-radio="'supervisors'" ng-model="section">
<button class="btn btn-default" btn-radio="'supervisors'" ng-model="section" ng-if="user.isOffice">
<i class="fa fa-users" aria-hidden="true"></i> {{ 'supervisors' | translate }}
</button>
</div>
......@@ -51,25 +51,42 @@
<label translate>license_number</label>
<input type="text" id="setup_license" class="form-control" mask="wwww-wwww-wwww-wwww" clean="true" placeholder="{{ 'license_number' | translate }}" ng-model="formUser.license_number" required>
<!-- With license -->
<div ng-show="studentData.license && !studentData.license_expired" class="alert alert-info" role="alert">
<!-- With official license -->
<div ng-show="studentData.license && studentData.license.isValid && studentData.license.isOfficial" class="alert alert-info" role="alert">
<div class="row">
<div class="col-xs-2"><i class="fa fa-info-circle fa-lg" aria-hidden="true"></i></div>
<div class="col-xs-2"><i class="fa fa-certificate fa-lg" aria-hidden="true"></i></div>
<div class="col-xs-10">
{{ 'license_expires' | translate }} <b>{{ studentData.expiration_date }}</b>
{{ 'license_expires_official' | translate }} <b>{{ studentData.expiration_date }}</b>
</div>
</div>
</div>
<!-- License expired -->
<div ng-show="studentData.license && studentData.license_expired" class="alert alert-warning" role="alert">
<!-- With trial license -->
<div ng-show="studentData.license && studentData.license.isValid && studentData.license.isTrial" class="alert alert-info" role="alert">
<div class="row">
<div class="col-xs-2"><i class="fa fa-exclamation-circle fa-lg" aria-hidden="true"></i></div>
<div class="col-xs-2"><i class="fa fa-flask fa-lg" aria-hidden="true"></i></div>
<div class="col-xs-10">
<h4 class="alert-heading"> {{ 'license_expired' | translate }} </h4>
<p>
<a href="http://pictogramweb.com/caracteristicas-de-pictogram/">{{ 'license_expired_renew' | translate }}</a>
</p>
{{ 'license_expires_trial' | translate }} <b>{{ studentData.expiration_date }}</b>
</div>
</div>
</div>
<!-- Official license expired -->
<div ng-show="studentData.license && !studentData.license.isValid && studentData.license.isOfficial" class="alert alert-warning" role="alert">
<div class="row">
<div class="col-xs-2"><i class="fa fa-certificate fa-lg text-danger" aria-hidden="true"></i></div>
<div class="col-xs-10">
{{ 'license_expired_official' | translate }} <a href="http://pictogramweb.com/caracteristicas-de-pictogram/">{{ 'license_expired_renew' | translate }}</a>
</div>
</div>
</div>
<!-- Trial license expired -->
<div ng-show="studentData.license && !studentData.license.isValid && studentData.license.isTrial" class="alert alert-warning" role="alert">
<div class="row">
<div class="col-xs-2"><i class="fa fa-flask fa-lg text-danger" aria-hidden="true"></i></div>
<div class="col-xs-10">
{{ 'license_expired_trial' | translate }} <a href="http://pictogramweb.com/caracteristicas-de-pictogram/">{{ 'license_expired_buy' | translate }}</a>
</div>
</div>
</div>
......@@ -312,14 +329,14 @@
<div id="supervisors_section" ng-show="section == 'supervisors'">
<div class="row">
<div class="col-md-5" ng-if="studentData.supervision != 1">
<div class="col-md-5" ng-if="user.isOffice">
<!-- Supervisores (terapeutas) del alumno -->
<div id="student_sups">
<legend translate>therapists</legend>
<!-- Buscador de supervisores -->
<p>
<form role="search" ng-submit="search_sup()">
<div class="input-group" ng-if="user.isSupAdmin">
<div class="input-group">
<input type="email" class="form-control" placeholder="{{ 'search_sup_email' | translate }}" name="email_sup" id="email_sup" ng-model="supsForm.email_sup" required>
<div class="input-group-btn">
<button class="btn btn-default" type="submit">
......@@ -344,7 +361,7 @@
<!-- Imagen de perfil del supervisor -->
<img ng-src="{{sup.pic}}" class="profile" alt="" title="" />
{{sup.name}} {{sup.surname}}
<a ng-if="user.isSupAdmin" ng-click="delete_sup(sup.id)" class="delete_sup" title="{{ 'unlink' | translate}}">
<a ng-click="delete_sup(sup.id)" class="delete_sup" title="{{ 'unlink' | translate}}">
<span class="color_red glyphicon glyphicon-remove-circle" aria-hidden="true"></span>
</a>
</li>
......@@ -356,12 +373,12 @@
<div class="col-md-5">
<!-- Tutores (Padres) -->
<div id="student_tutors" ng-if="studentData.supervision != 1">
<div id="student_tutors" ng-if="user.isOffice">
<legend translate>tutors</legend>
<!-- Buscador de tutores -->
<p>
<form role="search" ng-submit="search_tutor()">
<div class="input-group" ng-if="user.isSupAdmin">
<div class="input-group">
<input type="email" class="form-control" placeholder="{{ 'search_tutor_email' | translate }}" name="email_tutor" id="email_tutor" ng-model="supsForm.email_tutor" required>
<div class="input-group-btn">
<button class="btn btn-default" type="submit">
......@@ -387,7 +404,7 @@
<!-- Imagen de perfil del tutor -->
<img ng-src="{{tutor.pic}}" class="profile" alt="" title="" />
{{tutor.name}} {{tutor.surname}}
<a ng-if="user.isSupAdmin" ng-click="delete_tutor(tutor.id)" class="delete_tutor" title="{{ 'unlink' | translate}}">
<a ng-click="delete_tutor(tutor.id)" class="delete_tutor" title="{{ 'unlink' | translate}}">
<span class="color_red glyphicon glyphicon-remove-circle" aria-hidden="true"></span>
</a>
</li>
......
......@@ -14,7 +14,8 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
$translate,
ngToast,
$timeout,
IOService) {
IOService,
CONSTANTS) {
// Flags for showing buttons according to role
......@@ -24,9 +25,12 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
// Create new account
// --------------------------------------------------------
$scope.minlength = CONSTANTS.password_minlength;
var formdata_empty = {
username: '',
password: '',
password_confirm: '',
name: $translate.instant('name'),
surname: $translate.instant('surname'),
birthdate: Date(),
......@@ -37,7 +41,7 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
current_method: 'no_method',
current_instruction: 'no_instruction',
license_number: null,
id_sup: -1
id_sup: $scope.user.id
};
// Hide new student form
......@@ -96,10 +100,10 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
/**
* Add existing account
*/
function addExisting () {
$scope.addExisting = function () {
// Send link call to server
$http.post(config.backend + '/stu/license/id_sup/' + $scope.user.id, {license: $scope.formdata.license})
$http.post(config.backend + '/stu/license/sup/' + $scope.user.id, {license: $scope.formdata.license_number})
.success(function (data) {
ngToast.success({ content: $translate.instant('student_added') });
......@@ -117,12 +121,12 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
errorMessage = 'license_invalid';
else if (err.message && err.message.search('in use') > 0)
errorMessage = 'license_already_activated';
else if (typeof err == "string" && err.search("lready linked") > 0)
else if (err.message && err.message.search("lready linked") > 0)
errorMessage = 'student_already_linked';
else if (err && err.status === 400)
errorMessage = 'invalid_fields';
ngToast.danger({ content: $translate.instant(errorMessage) });
ngToast.danger($translate.instant(errorMessage));
});
}
......@@ -130,22 +134,19 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
/**
* Add new account
*/
function addNew(type) {
$scope.addNew = function (type) {
// set language according to interface settings
$scope.formdata.lang = $translate.use();
// Validate password match
if (student.password_confirm.length && student.password !== student.password_confirm) {
ngToast.danger({ content: $translate.instant('password_match') });
if ($scope.formdata.password_confirm.length && $scope.formdata.password !== $scope.formdata.password_confirm) {
ngToast.danger($translate.instant('password_match'));
return;
}
// Select API call according to type of account to create: office or test
var apicall = type == 'test' ? '/stu/test' : '/stu';
// Send creating call to server
$http.post(config.backend + apicall, $scope.formdata)
$http.post(config.backend + '/stu', $scope.formdata)
.success(function (data) {
ngToast.success({ content: $translate.instant('student_added') });
......@@ -159,12 +160,14 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
$scope.slide.rightTo('confirmation');
})
.error(function (err) {
console.log(typeof err);
console.log(err);
var errorMessage = 'student_not_added';
if (err.message && err.message.search('nvalid license') > 0)
if (err.message && err.message.search('nvalid license') > 0 || typeof err == "string" && err.search('nvalid license') > 0)
errorMessage = 'license_invalid';
else if (err.message && err.message.search('in use') > 0)
else if (err.message && err.message.search('in use') > 0 || typeof err == "string" && err.search('in use') > 0)
errorMessage = 'license_already_activated';
else if (typeof err == "string" && err.search("already exists") > 0)
else if (err.message && err.message.search('already exists') > 0 || typeof err == "string" && err.search("already exists") > 0)
errorMessage = 'student_already_exists';
else if (err && err.status === 400)
errorMessage = 'invalid_fields';
......
......@@ -39,8 +39,9 @@
</div>
</td>
<td>
<i ng-show="!student.licenseIsValid" class="fa fa-exclamation-circle fa-lg text-danger license-warning" aria-hidden="true" popover="{{ 'license_missing' | translate}}" popover-trigger="mouseenter"></i>
<i ng-show="student.licenseIsValid" class="fa fa-certificate fa-lg text-primary license-warning" aria-hidden="true" popover="{{ 'license_pro' | translate}}" popover-trigger="mouseenter"></i>
<i ng-show="student.license.isValid && !student.license.isTrial" class="fa fa-certificate fa-lg text-primary license-warning" aria-hidden="true" popover="{{ 'license_pro' | translate}}" popover-trigger="mouseenter"></i>
<i ng-show="student.license.isTrial && student.license.isValid" class="fa fa-flask fa-lg text-warning license-warning" aria-hidden="true" popover="{{ 'trial_license' | translate}}" popover-trigger="mouseenter"></i>
<i ng-show="!student.license.isValid" class="fa fa-exclamation-circle fa-lg text-danger license-warning" aria-hidden="true" popover="{{ 'license_missing' | translate}}" popover-trigger="mouseenter"></i>
</td>
<td>
<h4>{{student.surname}}, {{student.name}}</h4>
......@@ -52,45 +53,50 @@
<a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/collections"
alt="{{ 'collections' | translate}}" popover="{{ 'collections' | translate}}" popover-trigger="mouseenter">
alt="{{ 'collections' | translate }}" popover="{{ 'collections' | translate }}" popover-trigger="mouseenter" ng-if="student.license.isValid">
<span class="glyphicon glyphicon-th" aria-hidden="true"></span>
</a>
<span
class="btn btn-default btn-lg" role="button"
alt="{{ 'collections' | translate }}" popover="{{ 'collections' | translate }}" popover-trigger="mouseenter" ng-if="!student.license.isValid">
<span class="glyphicon glyphicon-th" aria-hidden="true" style="color: #bbb" ></span>
</span>
<a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/instructions"
alt="{{ 'instructions' | translate}}" popover="{{ 'instructions' | translate}}" popover-trigger="mouseenter" ng-if="!user.isTutor">
alt="{{ 'instructions' | translate }}" popover="{{ 'instructions' | translate }}" popover-trigger="mouseenter" ng-if="!user.isTutor && student.license.isValid">
<span class="glyphicon glyphicon-tasks" aria-hidden="true"></span>
</a>
<span
class="btn btn-default btn-lg" role="button"
alt="{{ 'instructions' | translate}}" popover="{{ 'instructions' | translate}}" popover-trigger="mouseenter" ng-if="user.isTutor">
alt="{{ 'instructions' | translate }}" popover="{{ 'instructions' | translate }}" popover-trigger="mouseenter" ng-if="user.isTutor || !student.license.isValid">
<span class="glyphicon glyphicon-tasks" aria-hidden="true" style="color: #bbb" ></span>
</span>
<a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/session"
alt="{{ 'session' | translate}}" popover="{{ 'session' | translate}}" popover-trigger="mouseenter" ng-if="!user.isTutor">
alt="{{ 'session' | translate }}" popover="{{ 'session' | translate }}" popover-trigger="mouseenter" ng-if="!user.isTutor && student.license.isValid">
<span class="glyphicon glyphicon-transfer" aria-hidden="true"></span>
</a>
<span
class="btn btn-default btn-lg" role="button"
alt="{{ 'session' | translate}}" popover="{{ 'session' | translate}}" popover-trigger="mouseenter" ng-if="user.isTutor">
alt="{{ 'session' | translate }}" popover="{{ 'session' | translate }}" popover-trigger="mouseenter" ng-if="user.isTutor || !student.license.isValid">
<span class="glyphicon glyphicon-transfer" aria-hidden="true" style="color: #bbb"></span>
</span>
<a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/reports"
alt="{{ 'reports' | translate}}" popover="{{ 'reports' | translate}}" popover-trigger="mouseenter" ng-if="!user.isTutor">
alt="{{ 'reports' | translate }}" popover="{{ 'reports' | translate }}" popover-trigger="mouseenter" ng-if="!user.isTutor && student.license.isValid">
<i class="fa fa-bar-chart" aria-hidden="true"></i>
</a>
<span class="btn btn-default btn-lg" role="button"
alt="{{ 'reports' | translate}}" popover="{{ 'reports' | translate}}" popover-trigger="mouseenter" ng-if="user.isTutor">
alt="{{ 'reports' | translate }}" popover="{{ 'reports' | translate }}" popover-trigger="mouseenter" ng-if="user.isTutor || !student.license.isValid">
<i class="fa fa-bar-chart" aria-hidden="true" style="color: #bbb"></i>
</span>
<a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/setup"
alt="{{ 'setup' | translate}}" popover="{{ 'setup' | translate}}" popover-trigger="mouseenter">
alt="{{ 'setup' | translate }}" popover="{{ 'setup' | translate}}" popover-trigger="mouseenter">
<span class="glyphicon glyphicon-cog" aria-hidden="true"></span>
</a>
......
......@@ -9,8 +9,6 @@
<div class="col-xs-9"></div>
</div>
<form name="AddStudentForm" role="form" ng-submit="add_student()" ng-controller="StudentAddCtrl">
<div class="switch-panel-body height400" ng-switch="slide.state">
<!--
......@@ -96,11 +94,11 @@
</div>
<div class="form-group">
<label translate>password</label>
<input type="password" class="form-control" id="setup_password1" placeholder="{{ 'password_new_type' | translate }}" ng-model="formdata.password" required />
<input type="password" class="form-control" id="setup_password1" placeholder="{{ 'password_new_type' | translate }}" name="password" ng-model="formdata.password" required />
<span class="color_red text_sm pull-right" ng-show="formdata.password.length < minlength && forms.new.password.$dirty && forms.new.password_confirm.$dirty"> {{ 'password_short' | translate:'{ minlength: minlength }' }}</span>
</div>
<div class="form-group">
<input type="password" class="form-control" id="setup_password2" placeholder="{{ 'password_confirm' | translate }}" ng-model="formdata.password_confirm" required />
<input type="password" class="form-control" id="setup_password2" placeholder="{{ 'password_confirm' | translate }}" name="password_confirm" ng-model="formdata.password_confirm" required />
<span class="color_red text_sm pull-right" ng-show="formdata.password != formdata.password_confirm && forms.new.password.$dirty && forms.new.password_confirm.$dirty" translate>password_match</span>
</div>
<div class="form-group">
......@@ -140,11 +138,11 @@
</div>
<div class="form-group">
<label translate>password</label>
<input type="password" class="form-control" id="setup_password1" placeholder="{{ 'password_new_type' | translate }}" ng-model="formdata.password" required />
<input type="password" class="form-control" id="setup_password1" placeholder="{{ 'password_new_type' | translate }}" name="password" ng-model="formdata.password" required />
<span class="color_red text_sm pull-right" ng-show="formdata.password.length < minlength && forms.test.password.$dirty && forms.test.password_confirm.$dirty"> {{ 'password_short' | translate:'{ minlength: minlength }' }}</span>
</div>
<div class="form-group">
<input type="password" class="form-control" id="setup_password2" placeholder="{{ 'password_confirm' | translate }}" ng-model="formdata.password_confirm" required />
<input type="password" class="form-control" id="setup_password2" placeholder="{{ 'password_confirm' | translate }}" name="password_confirm" ng-model="formdata.password_confirm" required />
<span class="color_red text_sm pull-right" ng-show="formdata.password != formdata.password_confirm && forms.test.password.$dirty && forms.test.password_confirm.$dirty" translate>password_match</span>
</div>
</div>
......@@ -167,7 +165,7 @@
-->
<div ng-class="slide.back ? 'switch-animation-back' : 'switch-animation'" ng-switch-when="confirmation">
<h2>{{ 'user_created' | translate }} </h2>
<h2>{{ 'account_available' | translate }} </h2>
<p translate>student_account_confirm</p>
<br>
<img src="img/child.png"/>
......@@ -176,6 +174,4 @@
</div>
</form>
<hr />
......@@ -8,6 +8,7 @@ dashboardControllers.controller('TranslateController', function(
$scope,
$window,
$http,
$timeout,
config,
ngToast,
vcRecaptchaService
......@@ -44,7 +45,7 @@ dashboardControllers.controller('TranslateController', function(
//Server PUT
$http.put(config.backend + '/sup/' + $scope.user.id, { "lang": langKey })
.success(function (data) {
ngToast.success({ content: $translate.instant('data_saved') });
ngToast.success({ content: $translate.instant('language_change_warning') });
console.log("OK: Update supervisor language");
})
.error(function () {
......@@ -63,7 +64,5 @@ dashboardControllers.controller('TranslateController', function(
vcRecaptchaService.useLang(0, langKey.substr(0,2));
} catch (err) {}
// Reload page
$window.location.reload();
};
});
......@@ -103,7 +103,7 @@ module.exports.policies = {
supervisors: ['tokenAuth'],
therapists: ['tokenAuth'],
tutors: ['tokenAuth'],
link_supervisor: ['tokenAuth', 'isOffice'],
link_supervisor: ['tokenAuth'],
pictos: ['tokenAuth'],
methods: ['tokenAuth'],
lasttries: ['tokenAuth'],
......@@ -115,7 +115,7 @@ module.exports.policies = {
update_legend: ['tokenAuth'],
update_category: ['tokenAuth', 'isSupervisorOfStudent'],
login: true,
create: ['tokenAuth', 'isOffice'],
create: ['tokenAuth'],
upload: ['tokenAuth'],
upload_sound: ['tokenAuth'],
add_picto: ['tokenAuth', 'isSupervisorOfStudent'],
......@@ -125,8 +125,8 @@ module.exports.policies = {
action: true,
config: true,
actions_batch: ['tokenAuth'],
delete: ['tokenAuth', 'isOffice'],
unlink_supervisor: ['tokenAuth', 'isOffice'],
delete: ['tokenAuth', 'isAdmin'],
unlink_supervisor: ['tokenAuth', 'isSupervisorOfStudent'],
delete_picto: ['tokenAuth', 'isSupervisorOfStudent'],
getActiveScene: ['tokenAuth'],
getScenes: ['tokenAuth'],
......
......@@ -89,8 +89,8 @@ module.exports.routes = {
'GET /stu/:id_stu/supervisors': 'StudentController.supervisors',
'GET /stu/:id_stu/therapists': 'StudentController.therapists',
'GET /stu/:id_stu/tutors': 'StudentController.tutors',
'POST /stu/:id_stu/sup/:id_sup': 'StudentController.link_supervisor',
'POST /stu/license/sup/:id_sup': "StudentController.link_supervisor",
'POST /stu/:id_stu/sup/:id_sup': 'StudentController.link_supervisor',
'GET /stu/:id_stu/pictos': 'StudentController.pictos',
'GET /stu/:id_stu/activeScene': 'StudentController.getActiveScene',
'GET /stu/:id_stu/scenes': 'StudentController.getScenes',
......@@ -108,7 +108,6 @@ module.exports.routes = {
'PUT /stu/:id_stu/activeScene/:id_scene': 'StudentController.updateActiveScene',
'POST /stu/login': 'StudentController.login',
'POST /stu': 'StudentController.create',
'POST /stu/license': 'StudentController.createTest',
'POST /stu/upload': 'StudentController.upload',
'POST /stu/:id_stu/upload_sound/:id_picto': 'StudentController.upload_sound',
'POST /stu/:id_stu/picto/:id_picto': 'StudentController.add_picto',
......
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