Commit fb5ce783 by Fernando Martínez Santiago

Merge remote-tracking branch 'origin/develop' into develop

parents 2a978514 80e9c9a3
Showing with 221 additions and 105 deletions
......@@ -7,6 +7,8 @@ elif [ -e "/vagrant/src/app.js" ]; then
cd /vagrant/src && forever start app.js --debug
elif [ -e "/home/vagrant/sync/src/app.js" ]; then
cd /home/vagrant/sync/src && forever start app.js --debug
elif [ -e "/home/ubuntu/pictogram/sails/src/app.js" ]; then
cd /home/ubuntu/pictogram/sails/src && forever start app.js --debug
else
echo "-- app.js not found, cannot run pictogram server"
exit
......
......@@ -2,10 +2,9 @@ SET foreign_key_checks=0;
DROP USER IF EXISTS 'pictodbu';
DELETE FROM mysql.user WHERE user='pictodbu';
FLUSH PRIVILEGES;
CREATE USER 'pictodbu'@'localhost' identified by 'p1KT0!15.';
GRANT USAGE ON *.* TO 'pictodbu'@'localhost';
CREATE USER 'pictodbu'@'%' identified by 'r"YjtnB+a4$.M*nJ';
DROP DATABASE IF EXISTS pictodb;
CREATE DATABASE pictodb;
SET foreign_key_checks=1;
GRANT all privileges ON pictodb.* to 'pictodbu'@'localhost' identified by 'p1KT0!15.';
GRANT all privileges ON pictodb.* to 'pictodbu'@'localhost' identified by 'r"YjtnB+a4$.M*nJ';
FLUSH PRIVILEGES;
......@@ -138,6 +138,7 @@ COMMENT="Methods predefined in the platform or stored by users for cloning";
CREATE TABLE IF NOT EXISTS `office` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(80) COLLATE utf8_unicode_ci NOT NULL,
`logo_url` varchar(240) COLLATE utf8_unicode_ci NOT NULL,
`address` varchar(180) COLLATE utf8_unicode_ci NOT NULL,
`country` varchar(2) COLLATE utf8_unicode_ci NOT NULL,
`lang` varchar(5) COLLATE utf8_unicode_ci NOT NULL,
......
......@@ -73,7 +73,7 @@ INSERT IGNORE INTO `student` (
'es-es',
(SELECT id from office WHERE email='centrodestrezas@gmail.com'),
'{"stu-att":[{"categories":"on","input feedback":["vibration","tts"],"input selection":"click","pictogram size":"medium"}]}'
)
);
INSERT IGNORE INTO `stu_picto` (
`id_stu`,
......
......@@ -53,6 +53,16 @@ thisTrigger: BEGIN
current_students=current_students+1
WHERE
office.id=new.id_off;
INSERT INTO stu_picto(id_stu,id_pic,attributes)
SELECT new.id,id_pic, concat('{"id_cat":', if (id_cat_pic is null, 'null',id_cat_pic),
',"coord_x":',coord_x,
',"coord_y":',coord_y,
',"status":"invisible"',
',"highlight":false',
',"color":', if (color is null, 'null',concat('"',color,'"')),
'}') as attributes
FROM picto_core P;
END;;
-- Integrity rule 3: office.current_enrolments and supervisor assigments updating.
......
......@@ -2,5 +2,5 @@
database_files_relative_path: 'roles/database/files'
database_name: pictodb
database_user: pictodbu
database_user_passwd: p1KT0!15.
database_user_passwd: r"YjtnB+a4$.M*nJ
database_tests: [caja] #[caja, autismojaen] # can be empty
......@@ -36,11 +36,14 @@ module.exports = {
* responses over https:// and/or use websockets over the wss:// protocol
* (recommended for HTTP, strongly encouraged for WebSockets)
*/
ssl: {
// ca: fs.readFileSync(path.join(__dirname, 'ssl', 'development-bundle.crt')),
key: fs.readFileSync(path.join(__dirname, 'ssl', 'development.key')),
cert: fs.readFileSync(path.join(__dirname, 'ssl', 'development.crt')),
/* UNCOMMENT FOR HTTPS */
/* ssl: {
ca: fs.readFileSync(path.join(__dirname, 'ssl', '/etc/letsencrypt/live/xxx.yottacode.com/chain.pem')),
key: fs.readFileSync(path.join(__dirname, 'ssl', '/etc/letsencrypt/live/xxx.yottacode.com/privkey.pem')),
cert: fs.readFileSync(path.join(__dirname, 'ssl', '/etc/letsencrypt/live/xxx.yottacode.com/fullchain.pem')),
},
*/
/**
* The `port` setting determines which TCP port your app will be
......
......@@ -50,6 +50,25 @@ module.exports = {
},
/**
* Get basic informacion (logo_url and name) about an office identified by a code
*/
getBasic: function (req, res) {
// We simply remove first 3 characters
var id = req.params.code.substr(3, req.params.code.length-1);
Office.findOne({ id: id }).populate('admin').then(function (office) {
if (office) {
res.ok({logo_url: office.logo_url, name: office.name});
} else {
res.notFound();
}
})
.catch(function () {
res.badRequest();
});
},
/**
* Return all offices
* @param {request} req {}
* @param {response} res
......
......@@ -158,7 +158,7 @@ module.exports = {
Student.create(params)
.then(function(created) {
sails.log.debug('Student ' + created.id + ' created: ' + JSON.stringify(created));
return res.ok({ student: created });
return res.ok(created);
})
.error(function(err) {
if (err.message.search("Maximum number of enrolments reached") > 0) {
......@@ -169,8 +169,8 @@ module.exports = {
return res.serverError(err.message);
}
else {
sails.log.debug(err);
return res.serverError(err);
sails.log.debug(err.message);
return res.serverError(err.message);
}
});
},
......
......@@ -31,6 +31,11 @@ module.exports = {
type: "string",
size: 80
},
logo_url: {
required: true,
type: "string",
size: 240
},
contactPerson: {
columnName: "contact_person",
required: true,
......@@ -85,4 +90,4 @@ module.exports = {
via: 'office'
}
}
};
\ No newline at end of file
};
......@@ -238,53 +238,6 @@ module.exports = {
},
/**
* Sets initial vocabulary of students to Picto Core
* @param {Object} attrs All student properties stored
* @param {Function} next Function to be executed when the check process
* has been completed (an error object will be passed
* to the function if necesary)
*/
afterCreate: function (attrs, next) {
// Assign the initial collection of pictos to the student
PictoCore.find()
.then(function(pictoCore) {
var i;
// Every picto from 'picto_core_cat' is going to be created
// in 'stu_picto'
for (var i = 0; i < pictoCore.length; i++) {
StuPicto.create({
student: created.id,
picto: pictoCore[i].picto,
attributes: {
id_cat: pictoCore[i].category,
coord_x: pictoCore[i].coord_x,
coord_y: pictoCore[i].coord_y,
status: 'invisible', // Default, the pictos don't appear to the user
color: pictoCore[i].color
}
})
.then(function (stuPictoError, added) {
if (added) {
sails.log.debug(
'Picto ' + added.picto +
' added to student ' + created.id +
' with attributes: ' + JSON.stringify(added.attributes));
}
})
.error(function(err) {
sails.log.debug('StuPicto.create: ' + err);
throw err;
});
}
next();
})
.error(function(err) {
next(err);
});
},
/**
* Checks the given properties before updating a new Student
* @param {Object} attrs All student properties to be stored
* @param {Function} next Function to be executed when the check process
......
......@@ -26,7 +26,7 @@
"ngtoast": "~1.5.4",
"angular-animate": "~1.4.1",
"angular-sanitize": "~1.4.1",
"angular-chart.js": "~0.7.2",
"angular-chart.js": "latest",
"ng-lodash": "~0.3.0"
},
"resolutions": {
......
No preview for this file type
This diff could not be displayed because it is too large.
......@@ -26,6 +26,7 @@
"alert": "Alert",
"all": "All",
"animation": "Animation",
"annual": "Annual",
"April": "April",
"attributes_not_updated": "Changes not saved",
"attributes_updated": "Changes saved",
......@@ -102,6 +103,7 @@
"general_labels": "General labels",
"generate": "Generate",
"generate_serial": "Generate serial number",
"global":"Global",
"hide": "Hide",
"highlight": "highlight",
"highlighted": "Highlighted",
......@@ -142,6 +144,7 @@
"methods": "Methods",
"minutes": "minutes",
"month_totals": "Month totals",
"monthly":"Monthly",
"name": "Name",
"new_instruction": "New instruction",
"new_method": "New method",
......@@ -269,10 +272,12 @@
"tag_deleted": "Tag deleted",
"tape_background": "Tape background",
"template_deleted": "Template deleted",
"time_instruction_method": "Time instructions of method",
"time_hours": "Time: {{hours}} hours",
"time_instruction_method": "Time instructions of method",
"time_sessions_total": "Total sessions time",
"time_sessions_per_days": "Time of sessions per days in",
"time_sessions_per_month": "Time of sessions per months in",
"time_tries_total": "Total tries time",
"time_tries_per_days": "Time of tries per days in",
"time_tries_per_month": "Time of tries per months in",
"title": "Title",
......@@ -281,6 +286,7 @@
"tpl_day": "{{ day | date:'yyyy-MM-dd' }}",
"tpl_hours_frame": "from {{ begin | date:'HH:mm:ss' }} to {{ end | date:'HH:mm:ss' }}",
"tries": "Tries",
"tries_done": "Tries done",
"tries_length": "Tries length",
"tries_mean_length": "Tries mean length",
"tries_per_days": "Tries per days in",
......
......@@ -26,6 +26,7 @@
"alert": "Alerta",
"all": "Todos",
"animation": "Animación",
"annual": "Anual",
"April": "Abril",
"attributes_not_updated": "Cambios no guardados",
"attributes_updated": "Cambios guardados",
......@@ -102,6 +103,7 @@
"general_labels": "Generales",
"generate": "Generar",
"generate_serial": "Generar número de serie",
"global":"Global",
"hide": "Ocultar",
"highlight": "Resaltar",
"highlighted": "Resaltado",
......@@ -142,6 +144,7 @@
"methods": "Métodos",
"minutes": "minutos",
"month_totals": "Totales mes",
"monthly":"Mensual",
"name": "Nombre",
"new_instruction": "Nueva instrucción",
"new_method": "Nuevo método",
......@@ -272,8 +275,10 @@
"template_deleted": "Plantilla eliminada",
"time_hours": "Tiempo: {{hours}} horas",
"time_instruction_method": "Tiempo instrucciones del método",
"time_sessions_total": "Tiempo total de sesiones",
"time_sessions_per_days": "Tiempo de sesiones por días en",
"time_sessions_per_month": "Tiempo de sesiones por meses en",
"time_tries_total": "Tiempo total de ensayos",
"time_tries_per_days": "Tiempo de ensayos por días en",
"time_tries_per_month": "Tiempo de ensayos por meses en",
"title": "Título",
......@@ -282,6 +287,7 @@
"tpl_day": "{{ day | date:'dd-MM-yyyy' }}",
"tpl_hours_frame": "de {{ begin | date:'HH:mm:ss' }} a {{ end | date:'HH:mm:ss' }}",
"tries": "Ensayos",
"tries_done": "Ensayos realizados",
"tries_length": "Duración ensayos",
"tries_mean_length": "Duración media ensayos",
"tries_per_days": "Número de ensayos por días en",
......
......@@ -56,6 +56,11 @@ dashboardApp.config(function ($stateProvider, $urlRouterProvider) {
templateUrl: 'modules/login/views/login.html',
controller: 'LoginCtrl'
})
.state('login_office', {
url: '/login/:office',
templateUrl: 'modules/login/views/login.html',
controller: 'LoginCtrl'
})
.state('login_validate', {
url: '/login/:code/:email',
templateUrl: 'modules/login/views/login.html',
......
......@@ -16,12 +16,18 @@ function LoginCtrl(
config,
$stateParams,
ngToast) {
$scope.credentials = {
email: '',
password: '',
lang: 'es-es'
};
$scope.office = {
logo_url : 'img/logo_pictogram.png',
name : 'Pictogram'
};
// Validation of account
// if the code has been sent in the url "...app/login/code/email"
if ($stateParams.code && $stateParams.email) {
......@@ -39,7 +45,17 @@ function LoginCtrl(
});
}
// Corporate login, office code has been passed
if ($stateParams.office) {
$http
.get(config.backend + '/office/' + $stateParams.office)
.success(function (data) {
$scope.office = data;
});
}
$scope.login = function () {
$scope.submitted = true;
$http
.post(config.backend + '/sup/login', $scope.credentials)
.success(function (data) {
......@@ -69,6 +85,7 @@ function LoginCtrl(
$location.path('/students');
})
.error(function (err) {
$scope.submitted = false;
delete $window.sessionStorage.token;
if (err.search("without students") > 0) {
$translate('no_students_for_user').then(function (translation) {
......
......@@ -2,13 +2,17 @@
<!-- Rejilla 3 elementos de igual ancho -->
<div class="col-md-4">&nbsp;</div>
<div class="col-md-4">
<div class="col-md-4">
<div id="login">
<!-- Logo Pictogram -->
<p class="text-center">
<!-- Logo Pictogram
<p>
<img src="img/logo_pictogram.png" alt="Pictogram" title="Pictogram" />
</p>
-->
<p class="text-center">
<img src="{{office.logo_url}}" alt="{{office.name}}" title="{{office.name}}">
</p>
<!-- Formulario -->
<!-- LoginCtrl controls here, see app.js -->
<form name="loginForm" ng-submit="login()" novalidate>
......@@ -27,7 +31,9 @@
<p class="text-center">
<button type="submit" class="btn btn-primary" translate>login</button>
</p>
<p class="text-center">
<i ng-class="{'fa fa-spinner fa-spin fa-2x fa-fw margin-bottom': true, 'spin_disabled': !submitted}"></i>
</p>
<p class="text-center">
<a href="/app/#/signin" translate>create_an_account</a>
</p>
......@@ -36,11 +42,7 @@
<!-- Fin login -->
</div>
<div class="col-md-4">&nbsp;</div>
</div>
<!-- Fin de row -->
<footer-translate></footer-translate>
......@@ -184,6 +184,7 @@
<label class="form-control" for="studentSetupUseCategories">{{ 'use_categories' | translate }}</label>
</div>
</fieldset>
<!-- DISABLED, NOT IMPLEMENTED YET
<fieldset>
<legend>{{ 'feedback_picto' | translate }}</legend>
<div class="input-group">
......@@ -389,6 +390,7 @@
</div>
</fieldset>
</fieldset>
-->
</form>
</div>
</div>
......
......@@ -137,6 +137,7 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
errorMessage = 'invalid_fields';
ngToast.danger({ content: $translate.instant(errorMessage) });
});
};
......
......@@ -8,9 +8,9 @@
href="/app/#/students">
<img
class="topbar__logo__image"
src="img/logo_pictogram.png"
alt="Pictogram"
title="Pictogram" />
src="{{user.office.logo_url}}"
alt="{{user.office.name}}"
title="{{user.office.name}}" />
</a>
</div>
<div class="topbar__supervisor nav navbar-nav navbar-right">
......
......@@ -2,8 +2,8 @@
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading"><h3 class="panel-title" translate>students</h3>
<span ng-if="user.isAdmin">({{user.office.currentStudents}}/{{user.office.maxStudents}} - <span translate="licenses_left" translate-values="{number: num_licenses_left}"></span>)</span>
</divuser.office.currentStudents}}/ivuser.office.maxStudents}} <div class="panel-body">
<!-- span ng-if="user.isAdmin">({{user.office.currentStudents}}/{{user.office.maxStudents}} - <span translate="licenses_left" translate-values="{number: num_licenses_left}"></span>)</span -->
<div class="panel-body">
<!-- Add Student Form -->
<div ng-include="'modules/supervisor/views/students_add.html'" ng-init="hidestudentadd = true" ng-hide="hidestudentadd"></div>
......
......@@ -4,4 +4,9 @@
<button type="button" class="btn btn-link">|</button>
<button type="button" class="btn btn-link" ng-click="changeLanguage('en-gb')">English</button>
</div>
</div>
\ No newline at end of file
<div>
<p class="text-center" style="margin-top:10px">
<small>Powered by <a href="http://www.yottacode.com"><img src="img/logo_pictogram.png" width="40px" alt="Pictogram" title="Pictogram" /></a></small>
</p>
</div>
</div>
......@@ -47,6 +47,12 @@
padding: 8px;
}
/* For hidding spinner preserving space */
.spin_disabled {
visibility: hidden;
cursor: default;
}
/* For span that are links */
.pointer{ cursor: pointer; }
......@@ -101,7 +107,7 @@ html {
/* Pie de página con idiomas: Siempre en la parte inferior de la página; */
div.languages {
position: absolute;
bottom: -50px;
/*bottom: -10px;*/
width: 100%;
/* Set the fixed height of the footer here */
height: 50px;
......
......@@ -2,9 +2,9 @@
@import (less) '../app/bower_components/bootstrap/dist/css/bootstrap-theme.css';
@import (less) '../app/bower_components/ngtoast/dist/ngToast.css';
@import (less) '../app/bower_components/ngtoast/dist/ngToast-animations.css';
@import (less) '../app/bower_components/angular-chart.js/dist/angular-chart.css';
@import (less) 'cropper.css';
@import (less) 'main.css';
@import (less) 'font-awesome.min.css';
@import 'forms.less';
@import 'picto-grid.less';
......
......@@ -26,7 +26,7 @@ module.exports.connections = {
host: 'localhost',
port: 3306,
user: 'pictodbu',
password: 'p1KT0!15.',
password: 'r"YjtnB+a4$.M*nJ',
database: 'pictodb',
charset : 'utf8',
collation : 'utf8_unicode_ci'
......
......@@ -36,12 +36,16 @@ module.exports = {
* responses over https:// and/or use websockets over the wss:// protocol
* (recommended for HTTP, strongly encouraged for WebSockets)
*/
ssl: {
/* ssl: {
// ca: fs.readFileSync(path.join(__dirname, 'ssl', 'bundle.crt')),
key: fs.readFileSync(path.join(__dirname, 'ssl', 'key.key')),
cert: fs.readFileSync(path.join(__dirname, 'ssl', 'cert.crt')),
key: fs.readFileSync(path.join(__dirname, 'ssl', 'yottacode.com.key')),
cert: fs.readFileSync(path.join(__dirname, 'ssl', 'yottacode.com.crt')),
},
*/
ssl: {
key: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/cert.pem')
}
/**
* The `port` setting determines which TCP port your app will be
* deployed on.
......@@ -52,7 +56,7 @@ module.exports = {
* In env/production.js, you'll probably want to change this setting
* to 80 (http://) or 443 (https://) if you have an SSL certificate
*/
port: process.env.PORT || 1337,
port: process.env.PORT || 443,
/*
* The runtime "environment" of your Sails app is either typically
......
......@@ -62,6 +62,7 @@ module.exports.policies = {
OfficeController: {
getAll: ['tokenAuth', 'isAdmin'],
get: ['tokenAuth'],
getBasic: true,
supervisors: ['tokenAuth', 'isAdmin']
},
......
......@@ -52,6 +52,7 @@ module.exports.routes = {
'DELETE /method/template/:id': 'MetaMethodController.destroy',
'GET /office/get_all': 'OfficeController.getAll',
'GET /office/:code': 'OfficeController.getBasic',
'GET /office/get/:id': 'OfficeController.get',
'GET /office/get/:id/supervisors': 'OfficeController.supervisors',
......
-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAPkWZj6TTJQon1rupP1SUY8dQiSv3+1xByQAGYaf
J9vZWfe6ZAy0ndFH26m0lc2S76BkQRfhWBTvWcsFhACWefSlGxpIiwxWdQOcF6cf
WyFi9VBPUXvTPyd64hCd+9+XML/0hzU4BpzJsEbPyhOv7VZA2VCZ+GAP86b/MGCS
s2QgWi7ZW0bTX0tO1WDC8JdHeaUXspwfJaqsw25SiDsNfWQmAZOa5F9I0vl4GCku
ivy16Cn/1XnpBw9M9DH18H1ENDFkOnUJYa8NU/t9lJJqNapDZ3zTLIlNd28nl3LP
HfbPGhq7qMAYmtiYt6rxHRBazL1OEFf2xb9DIVVlIKQXf/cCAwEAAaAAMA0GCSqG
SIb3DQEBCwUAA4IBAQArBhbVjPadSIWK9yGO7oca2TT+ZMp2kw4OY7+NHj+4NUFk
CKe2ZOAdvKM9wqY5P4QHux1WnqzEtga78ykaJofHeB/EFFqMkun5ETFbd/wIGKMy
WdB/vNNbsv5Hf2/ezGiw1HUsCGp2P/JTDO9+a5O3ioFhp4tvfJ7TBfdzD5eq7Xlq
DAHXPimbtnqiGGBf6efefG/Rl4cvvLyH5k/mUj00/stx3rjVayvedzOL8W4oRoxJ
SQEaqlp/MtPLXBuG0RkZsJloon+aZfcR3WE4iG2BVQe7Dqtdto+mRa9e1ba0ZUQK
Fq1iIGvsp9YIHCS3NXeJBeIllYkFK2hI7o6hALPf
-----END CERTIFICATE REQUEST-----
Para tener el servidor funcionando con certificados gratuitos de Let's encrypt los pasos son los siguientes:
```
$ cd
$ git clone https://github.com/letsencrypt/letsencrypt
$ sudo mkdir /etc/letsencrypt
$ sudo chown ubuntu:ubuntu /etc/letsencrypt
$ vim /etc/letsencrypt/cli.ini
```
Con el contenido siguiente:
```
authenticator = webroot
webroot-path = /home/ubuntu/pictogram/sails/src/assets/
server = https://acme-v01.api.letsencrypt.org/directory
renew-by-default
agree-dev-preview
agree-tos
email = info@yottacode.com
```
Para permitir a letsencrypt verificar nuestro servidor, debemos facilitar la entrada por HTTP. Para ello instalamos nginx y lo configuramos para que redirija a HTTPS:
```
$ sudo apt-get install nginx
$ sudo vim /etc/nginx/sites-enabled/default
```
Con el siguiente contenido:
```
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name _;
return 301 https://$host$request_uri;
}
```
Ahora relanzamos nginx y solicitamos el certificado
```
$ sudo service nginx reload
$ /home/ubuntu/letsencrypt/letsencrypt-auto --config /etc/letsencrypt/cli.ini -d dev.yottacode.com certonly
```
Ya tenemos nuestros certificados. Finalmente configuramos Sails para que apunte a ellos:
```
$ vim /home/ubuntu/pictogram/sails/src/config/local.js
```
Con el contenido para ssl:
```
ssl: {
key: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/fullchain.pem'),
ca: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/chain.pem')
},
```
Ya podemos relanzar sails
```
$ cd /home/ubuntu/pictograms/sails/src
$ sudo forever stopall
$ sudo forever start app.js --debug
```
Y ahora configurar cron para que el certificado se renueve solito cada mes
```
$ sudo crontab -e
```
Añadimos la línea:
```
@monthly /home/ubuntu/letsencrypt/letsencrypt-auto --config /etc/letsencrypt/cli.ini -d dev.yottacode.com certonly
```
y voila!
\ No newline at end of file
......@@ -7,6 +7,7 @@
"dependencies": {
"async": "^2.0.0-rc.4",
"bcrypt-nodejs": "0.0.3",
"chart.js": "^2.3.0",
"connect-redis": "3.0.2",
"connect-timeout": "^1.7.0",
"ejs": "^0.8.8",
......
......@@ -21,8 +21,8 @@ module.exports = function (grunt) {
'assets/app/bower_components/angular-sanitize/angular-sanitize.js',
'assets/app/bower_components/ngtoast/dist/ngToast.js',
'assets/app/bower_components/Chart.js/Chart.js',
'assets/app/bower_components/angular-chart.js/dist/angular-chart.js',
'assets/app/bower_components/chart.js/dist/Chart.min.js',
'assets/app/bower_components/angular-chart.js/dist/angular-chart.min.js',
'assets/app/bower_components/ng-lodash/build/ng-lodash.js',
'assets/app/bower_components/ng-file-upload/angular-file-upload-shim.js',
......
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