Commit 159e140d by Fernando Martínez Santiago Committed by root

local and dev manual merge

parents d19e5033 13f9c9bf
......@@ -65,14 +65,6 @@
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
......@@ -81,6 +73,14 @@
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotations" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
......@@ -106,6 +106,7 @@
<orderEntry type="library" exported="" name="play-services-base-9.2.1" level="project" />
<orderEntry type="library" exported="" name="socket.io-client-0.5.0" level="project" />
<orderEntry type="library" exported="" name="okhttp-2.3.0" level="project" />
<orderEntry type="library" exported="" name="androidasync-2.1.9" level="project" />
<orderEntry type="library" exported="" name="play-services-clearcut-9.2.1" level="project" />
<orderEntry type="library" exported="" name="animated-vector-drawable-24.1.1" level="project" />
<orderEntry type="library" exported="" name="okio-1.3.0" level="project" />
......@@ -122,11 +123,10 @@
<orderEntry type="library" exported="" name="support-vector-drawable-24.1.1" level="project" />
<orderEntry type="library" exported="" name="play-services-auth-9.2.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
<orderEntry type="library" exported="" name="androidasync-2.1.8" level="project" />
<orderEntry type="library" exported="" name="play-services-ads-9.2.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" />
<orderEntry type="library" exported="" name="ion-2.1.9" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="json-20090211" level="project" />
<orderEntry type="library" exported="" name="ion-2.1.8" level="project" />
<orderEntry type="library" exported="" name="play-services-ads-lite-9.2.1" level="project" />
</component>
</module>
\ No newline at end of file
......@@ -165,6 +165,7 @@
<orderEntry type="library" exported="" name="play-services-base-9.2.1" level="project" />
<orderEntry type="library" exported="" name="socket.io-client-0.5.0" level="project" />
<orderEntry type="library" exported="" name="okhttp-2.3.0" level="project" />
<orderEntry type="library" exported="" name="androidasync-2.1.9" level="project" />
<orderEntry type="library" exported="" name="play-services-clearcut-9.2.1" level="project" />
<orderEntry type="library" exported="" name="animated-vector-drawable-24.1.1" level="project" />
<orderEntry type="library" exported="" name="play-services-wearable-9.2.1" level="project" />
......@@ -182,9 +183,8 @@
<orderEntry type="library" exported="" name="support-annotations-24.1.1" level="project" />
<orderEntry type="library" exported="" name="support-vector-drawable-24.1.1" level="project" />
<orderEntry type="library" exported="" name="play-services-auth-9.2.1" level="project" />
<orderEntry type="library" exported="" name="androidasync-2.1.8" level="project" />
<orderEntry type="library" exported="" name="play-services-ads-9.2.1" level="project" />
<orderEntry type="library" exported="" name="ion-2.1.8" level="project" />
<orderEntry type="library" exported="" name="ion-2.1.9" level="project" />
<orderEntry type="library" exported="" name="play-services-ads-lite-9.2.1" level="project" />
<orderEntry type="module" module-name="commonlibrary" exported="" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
......
......@@ -17,12 +17,13 @@ mostradas más adelante hacen todo el trabajo.
1. Descargar upload.zip y symbolstx.zip desde el servidor de Yottacode:
```
scp ec2-user@pre.yottacode.com:~/upload.zip .
scp ec2-user@pre.yottacode.com:~/symbolstx.zip .
unzip upload.zip
unzip symbolstx.zip
rm upload.zip
rm symbolstx.zip
scp ec2-user@dev.yottacode.com:~/upload.tgz .
scp ec2-user@dev.yottacode.com:~/symbolstx.tgz .
tar zxvf upload.tgz
tar zxvf symbolstx.tgz
ln -s symbolstx_96x82 symbolstx
rm upload.tgz
rm symbolstx.tgz
```
2. Cambiar la contraseña de root de mysql a root: `mysqladmin -u root password root`. Este paso
es necesario para la creación de la base de datos `pictodb` y el usuario `pictodbuser`.
......@@ -37,13 +38,15 @@ mostradas más adelante hacen todo el trabajo.
### Opción B (desarrollo): ejecución en una máquina virtual generada automáticamente
1. Descargar upload.zip y symbolstx.zip desde el servidor de Yottacode:
```
scp ec2-user@pre.yottacode.com:~/upload.zip .
scp ec2-user@pre.yottacode.com:~/symbolstx.zip .
unzip upload.zip
unzip symbolstx.zip
rm upload.zip
rm symbolstx.zip
scp ec2-user@dev.yottacode.com:~/upload.tgz .
scp ec2-user@dev.yottacode.com:~/symbolstx.tgz .
tar zxvf upload.tgz
tar zxvf symbolstx.tgz
ln -s symbolstx_96x82 symbolstx
rm upload.tgz
rm symbolstx.tgz
```
2. Instalar [virtualbox][1] y [vagrant][2] (version >1.5 para este último).
3. Ejecutar `vagrant up` desde este directorio.
......
......@@ -4,9 +4,10 @@
Vagrant.configure(2) do |config|
# "ubuntu/trusty64" for ubuntu environment
# "boxcutter/centos71" for AWS similar environment
config.vm.box = "boxcutter/centos71"
config.vm.box = "boxcutter/centos72"
config.vm.network "forwarded_port", guest: 1337, host: 1337
config.vm.network "forwarded_port", guest: 80, host: 8080
config.ssh.insert_key = false
config.vm.provider "virtualbox" do |vb|
vb.memory = 1024
......
......@@ -7,7 +7,6 @@
name: all
state: import
target: "{{ server_path }}/{{ database_files_relative_path }}/init.sql"
tags: reset-test-database
- name: Imports the pictodb Schema
mysql_db:
......@@ -16,8 +15,6 @@
name: "{{ database_name }}"
state: import
target: "{{ server_path }}/{{ database_files_relative_path }}/pictodb-schema.sql"
tags:
- reset-test-database
- name: Imports symbolstx categories and metadata
mysql_db:
......@@ -26,8 +23,6 @@
name: "{{ database_name }}"
state: import
target: "{{ server_path }}/{{ database_files_relative_path }}/symbolstx-{{ item }}.sql"
tags:
- reset-test-database
with_items:
- metadata
- categories
......@@ -39,8 +34,6 @@
name: "{{ database_name }}"
state: import
target: "{{ server_path }}/{{ database_files_relative_path }}/pictodb-data.sql"
tags:
- reset-test-database
- name: Imports selected tests
mysql_db:
......@@ -49,8 +42,6 @@
name: "{{ database_name }}"
state: import
target: "{{ server_path }}/{{ database_files_relative_path }}/test-{{ item }}.sql"
tags:
- reset-test-database
with_items: "{{ database_tests }}"
- name: Creates triggers
......@@ -60,8 +51,6 @@
name: "{{ database_name }}"
state: import
target: "{{ server_path }}/{{ database_files_relative_path }}/triggers-{{ item }}.sql"
tags:
- reset-test-database
with_items:
- enrolments-integrity-constraints
- sessions-integrity-constraints
---
database_files_relative_path: 'roles/database/files'
database_root_passwd: root
database_name: pictodb
database_user: pictodbuser
database_user_passwd: p1KT015
database_user: pictodbu
database_user_passwd: p1KT0!15.
database_tests: [caja] #[caja, autismojaen] # can be empty
---
- name: Set mysql root password
debconf:
name: mysql-server
question: "mysql-server/{{ item }}"
value: "{{ mysql_root_passwd | quote }}"
vtype: password
with_items:
- root_password
- root_password_again
when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
- name: Install MySQL repos
apt:
deb: http://dev.mysql.com/get/mysql-apt-config_0.7.3-1_all.deb
......@@ -61,6 +50,22 @@
when: ansible_distribution == 'CentOS' or ansible_distribution == 'Amazon'
- name: Set mysql root password
shell: "/usr/bin/mysqladmin -u root password {{ mysql_root_passwd | quote }}"
debconf:
name: mysql-server
question: "mysql-server/{{ item }}"
value: "{{ database_root_passwd | quote }}"
vtype: password
with_items:
- root_password
- root_password_again
when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
- name: Reading temporary password
command: bash -c "cat /var/log/mysqld.log | grep \"temporary password\" | sed \"s/.* \\+//g\""
register: tmp_mysql_password
when: ansible_distribution == 'CentOS' or ansible_distribution == 'Amazon'
- name: Set mysql root password
shell: "/usr/bin/mysqladmin -u root -p\"{{ tmp_mysql_password.stdout }}\" password \"{{ database_root_passwd | quote }}\""
ignore_errors: yes
when: ansible_distribution == 'CentOS' or ansible_distribution == 'Amazon'
---
webapp_path: '/vagrant/src/assets/app'
server_path: '/vagrant'
database_root_passwd: r00...Tt
......@@ -977,11 +977,13 @@ module.exports = {
subscribe: function (req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
attributes.ui = attributes.ui ? attributes.ui : 'PCB';
if (req.isSocket) {
sails.hooks.rooms.subscribeToRoom(
sails.hooks.rooms.student(attributes.id_stu),
req.socket
req.socket,
attributes.ui
);
}
res.ok({msg: "Subscribed to student "});
......@@ -1002,10 +1004,7 @@ module.exports = {
// Leave all rooms
for (var i = 0; i < rooms.length; i++) {
//sails.sockets.leave(req.socket, rooms[i]); MODIFICADO POR FERNANDO. SI NO, NO SE ACTUALIZA UPDATE_PEERS
sails.hooks.rooms.unsubscribeFromRoom(
rooms[i],
req.socket
);
sails.hooks.rooms.unsubscribeFromRoom(rooms[i], req.socket);
sails.log.debug("Unsusbscribe from room " + rooms[i]);
}
......
......@@ -533,11 +533,13 @@ module.exports = {
subscribe: function (req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
attributes.ui = attributes.ui ? attributes.ui : 'PCB';
if (action === 'subscribe' && req.isSocket && attributes.id_sup) {
sails.hooks.rooms.subscribeToRoom(
sails.hooks.rooms.supervisor(attributes.id_sup),
req.socket
req.socket,
attributes.ui
);
res.ok();
} else {
......
......@@ -7,12 +7,24 @@
* to obtain an ID.
* @type {Object}
*/
var socketRooms={};
var socketRooms={}; // {socketId => [roomId, ...]}
module.exports = function roomsHook (sails) {
return {
/**
* Returns room information
* @param {Socket} socket
* @return {return} room information
{
id: room_id,
ui: 'PCB'|'PDB' // type of user interface
}
*/
getRoom: function (socket) {
return socketRooms[sails.sockets.getId(socket)];
},
/**
* Special function that subscribes a socket to a given room.
* This creates the connection and logs to debug with this format:
......@@ -20,25 +32,33 @@ module.exports = function roomsHook (sails) {
* {
* websocketSubscribe: {
* room,
* socket
* socket,
* interface
* }
* }
*
* @param {RoomID} room Room to subscribe
* @param {Socket} socket Socket added to the subscription<
* @param {Socket} socket Socket added to the subscription
* @param {UI} ui Type of UI that sends the suscription ('PDB' or 'PCB')
*/
subscribeToRoom: function (room, socket) {
subscribeToRoom: function (room, socket, ui) {
sails.log.debug('"websocketSubscribe":', JSON.stringify({
room: room,
socket: socket.id
socket: socket.id,
ui: ui
}));
sails.sockets.join(socket, room, function () {
sails.io.sockets.in(room).clients(function(error, ids) {
if (!error) {
socketRooms[sails.sockets.getId(socket)] =
socketRooms[sails.sockets.getId(socket)] ? [socketRooms[sails.sockets.getId(socket)],room]
var socket_id = sails.sockets.getId(socket);
// append to socketRooms
socketRooms[socket_id] = socketRooms[socket_id] ? [socketRooms[socket_id],room]
: [room];
// broadcast data
sails.hooks.events.broadcastEvent(
room,
sails.hooks.events.roomSubscribersChange(ids.length)
......@@ -70,12 +90,17 @@ module.exports = function roomsHook (sails) {
sails.sockets.leave(socket, room, function () {
sails.io.sockets.in(room).clients(function(error, ids) {
if (!error) {
// broadcast event
sails.hooks.events.broadcastEvent(
room,
sails.hooks.events.roomSubscribersChange(ids.length)
);
// look for the room the socket is subscribed to and remove it
if (socketRooms[sails.sockets.getId(socket)]) {
var index=socketRooms[sails.sockets.getId(socket)].indexOf(room);
var index = socketRooms[sails.sockets.getId(socket)].indexOf(room);
if (index > -1) {
socketRooms[sails.sockets.getId(socket)].splice(index, 1);
}
......
......@@ -137,7 +137,7 @@
"max_licenses_reached": "Maximum number of licenses reached by the office",
"May": "May",
"method_name_duplicated": "Template '{{method_name}}' already exists",
"method_save": "'{{method_name}}' saved as a new template",
"method_saved": "'{{method_name}}' saved as a new template",
"methods": "Methods",
"minutes": "minutes",
"month_totals": "Month totals",
......@@ -148,6 +148,7 @@
"no": "No",
"nobegin": "No started",
"no_method": "No method defined",
"no_subscribed": "No connection to student account",
"no_instruction": "No instruction defined",
"no_students_for_user": "You are not associated to any students. Please ask your office to link your account to a Pictogram student.",
"no_space_in_category": "No space left in category",
......@@ -303,7 +304,7 @@
"vibration": "Vibration",
"view": "View",
"voice": "Voice",
"warning_last_session_bad": "&nbsp;Last session was bad closed. <p/> &nbsp;Please, you must evaluate last tries and press 'close seesion' button",
"warning_last_session_bad": "Last session was interrupted. Please, you must evaluate last tries and press 'close seesion' button",
"warning_no_tablet_online":"No Pictogran Tablet online detected",
"woman": "Woman",
"year_totals": "Year totals",
......
......@@ -137,7 +137,7 @@
"max_licenses_reached": "Número de licencias máximo alcanzado por la oficina",
"May": "Mayo",
"method_name_duplicated": "La plantilla '{{method_name}}' ya existe",
"method_save": "'{{method_name}}' guardado como nueva plantilla",
"method_saved": "'{{method_name}}' guardado como nueva plantilla",
"methods": "Métodos",
"minutes": "minutos",
"month_totals": "Totales mes",
......@@ -150,6 +150,7 @@
"no_instruction": "Instrucción sin definir",
"no_students_for_user": "Su cuenta no está asociada a ningún estudiante. Por favor, contacto con su gabinete para enlazar su cuenta a un estudiante.",
"no_space_in_category": "No queda espacio en la categoría",
"no_subscribed": "Sin conexión a la cuenta del estudiante",
"nobegin": "Sin iniciar",
"normal": "Normal",
"notes": "Notas",
......@@ -304,7 +305,7 @@
"vibration": "Vibración",
"view": "Vista",
"voice": "Voz",
"warning_last_session_bad": "&nbsp;La última sesión no se cerró correctamente. <p/> &nbsp;Por favor, evalúe los ensayos y pulse 'cerrar sesión' ",
"warning_last_session_bad": "La última sesión no se cerró correctamente. Por favor, evalúe los ensayos y pulse 'cerrar sesión' ",
"warning_no_tablet_online":"No se detectó ningún usuario de Pictogram Tablet online",
"woman": "Mujer",
"year_totals": "Totales año",
......
......@@ -64,7 +64,8 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl(
io.socket.post('/stu/subscribe', {
action: 'subscribe',
attributes: {
id_stu: $scope.studentData.id
id_stu: $scope.studentData.id,
ui: 'PDB'
},
token: $window.sessionStorage.token
},
......@@ -115,7 +116,8 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl(
io.socket.post('/stu/subscribe', {
action: 'subscribe',
attributes: {
id_stu: $scope.studentData.id
id_stu: $scope.studentData.id,
ui: 'PDB'
},
token: $window.sessionStorage.token
},
......@@ -124,7 +126,7 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl(
});
})
.error(function () {
// TODO ngToast res.msg (cant connect to student room)
ngToast.danger({ content: $translate.instant('no_subscribed') });
});
// For tab navigation, initially blank goes to collections
......
......@@ -89,7 +89,9 @@
class="picto-grid__row">
<div
class="picto pull-left"
ng-repeat="studentPicto in studentPictoRow track by $index">
ng-repeat="studentPicto in studentPictoRow track by $index"
popover="{{studentPicto.expression.text}}"
popover-trigger="mouseenter">
<img
src="/app/img/redcross.png"
class="disabled"
......@@ -97,8 +99,6 @@
<img
ng-src="{{studentPicto.picto.uri}}"
class="unselectable"
popover="{{studentPicto.expression.text}}"
popover-trigger="mouseenter"
ng-click="show_category(studentPicto)"
ng-class="{
'novisible': studentPicto.attributes.status == 'invisible',
......
......@@ -19,11 +19,29 @@
<img ng-src="{{studentData.pic}}" alt="" title="" />
</div>
</div>
<div class="col-md-11">
<div class="col-md-10">
<div style="margin-left: 5px"><h4>{{studentData.name}} {{studentData.surname}}</h4></div>
<div style="margin-left: 5px" class="text-left">{{studentData.current_method | translate}}</div>
<div style="margin-left: 5px" class="text-left">{{studentData.current_instruction | translate}}</div>
<div style="margin-left: 5px; color: green" class="text-left">{{ 'num_peers' | translate}} : {{studentData.num_peers}}</div>
<div style="margin-left: 5px" class="text-left">
<span>{{studentData.current_instruction | translate}} </span>
<span class="text-muted">({{studentData.current_method | translate}})</span>
</div>
</div>
<div class="col-md-1">
<div style="margin-left: 5px; margin-top: 10px" class="text-left text-success" ng-if="studentData.num_peers > 1"
popover="{{ 'num_peers' | translate }}" popover-trigger="mouseenter">
<span class="glyphicon glyphicon-transfer" aria-hidden></span>
{{studentData.num_peers}}
</div>
<div style="margin-left: 5px; margin-top: 10px" class="text-left text-warning" ng-if="studentData.num_peers == 1"
popover="{{ 'num_peers' | translate }}" popover-trigger="mouseenter">
<span class="glyphicon glyphicon-transfer" aria-hidden></span>
{{studentData.num_peers}}
</div>
<div style="margin-left: 5px; margin-top: 10px" class="text-left text-danger" ng-if="studentData.num_peers == 0"
popover="{{ 'num_peers' | translate }}" popover-trigger="mouseenter">
<span class="glyphicon glyphicon-transfer" aria-hidden="true"></span>
{{studentData.num_peers}}
</div>
</div>
</div>
<div class="row">
......
<div class="panel panel-default student_tab_panel">
<div class="panel-body">
<div ng-show="studentData.num_peers<2" >
<table style="border: 1px solid #666666; padding:5px; background-color:#f5f5f5;" width="50%">
<tr>
<td><h4 translate>warning_no_tablet_online</h4></td>
</tr>
</table>
<div class="alert alert-danger" role="alert" ng-show="studentData.num_peers<2">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span>
{{ 'warning_no_tablet_online' | translate }}
</div>
<div class="row">
<div class="col-md-6">
......@@ -46,9 +44,15 @@
</timer>
</div>
<div class="try">
<<<<<<< HEAD
<div class="pull-left" ng-repeat="a in actual_try.actions track by $index">
<div ng-if="a.action != 'initsession'" class="picto" popover="{{ a.attributes.stu_picto.expression.text | translate }}" popover-trigger="mouseenter">
<img ng-src="{{ a.attributes.stu_picto.picto.uri }}" style="{{a.attributes.id_sup!=null ? 'border:2px dotted red': ''}}"/>
=======
<div class="pull-left" ng-repeat="a in actual_try.actions track by $index">
<div class="picto" popover="{{ a.attributes.stu_picto.expression.text | translate }}" popover-trigger="mouseenter">
<img ng-src="{{ a.attributes.stu_picto.picto.uri }}" />
>>>>>>> 13f9c9bff4582a0d96036c25304bf8bc2056587a
<div class="action-type">
<span ng-if="a.action == 'Add'" aria-hidden="true"></span>
<span ng-if="a.action == 'Select'" aria-hidden="true"></span>
......@@ -122,16 +126,13 @@
</a>
</div>
<div ng-show="ws_recover" >
<table style="border: 1px solid #666666; padding:5px; background-color:#f5f5f5;" width="50%">
<tr>
<td><h4 translate>warning_last_session_bad</h4></td>
<td>
<button class="btn btn-success btn-sm" type="button" ng-click="close_ws()" translate>close_session</button>
</td>
</tr>
</table>
<div class="alert alert-danger" role="alert" ng-show="ws_recover">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span>
{{ 'warning_last_session_bad' | translate }}
<button class="btn btn-warning btn-sm" type="button" ng-click="close_ws()" translate>close_session</button>
</div>
<div class="list-group">
<div class="list-group-item" ng-repeat="s in wsessions | orderBy: '-begin' | limitTo: numPerPage:(currentPage-1)*numPerPage">
<div ng-show="showLastTry && wsessions.length > 0">
......
......@@ -62,6 +62,7 @@
&__title {
margin-top: 0;
color: white;
text-transform: capitalize;
}
}
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