Commit e39c1c30 by Pablo Molina

Solved #386, organizados eventos websocket

- Añadido hook "rooms" a sails
- Añadido hook "events" a sails
- Actualizado StudentController.link_supervisor para que use los nuevos hooks
- Añadido uso de sails responses (badRequest(), ok()...) para las
  peticiones
- Creado servicio IOService para manejar websockets en angular
- Añadidas urls /sup/subscribe y /sup/unsubscribe para crear salas de
  supervisores
parent 07f44854
Showing with 2716 additions and 2568 deletions
# EditorConfig is awesome: http://EditorConfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
[android/**]
indent_size = 4
......@@ -44,3 +44,18 @@ todo el repositorio, organizadas en 3 categorías:
- Estado:
- `in-progress`: incidencias en proceso de desarrollo
- `critical`: elementos importantes que deben tener prioridad
# Aspecto del código
- En el fichero [.editorconfig](./.editorconfig) se encuentra el estilo
utilizado en el código (en cuento a espacios, codificación y demás). Existen
numerosos plugins que pueden encontrarse en
[editorconfig.org](http://EditorConfig.org).
- En el fichero [/sails/src/.eslintrc](./sails/src/eslintrc) se encuentran los
estilos referentes a los ficheros de javascript. Tanto `eslint` como las
configuraciones necesarias se encuentran en el fichero
[/sails/src/package.json](./sails/src/package.json), y se instalarán como
dependencias de desarrollo.
> Puede configurarse eslint en el editor usado o ejecutando
> `eslint [ficheros]` desde el directorio [/sails/src](./sails/src).
......@@ -9,7 +9,7 @@ android {
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "com.yottacode.pictogram"
minSdkVersion 19
minSdkVersion 21
targetSdkVersion 21
versionCode 1
versionName "1.0"
......
......@@ -35,7 +35,7 @@ import java.util.ArrayList;
import java.util.Hashtable;
import java.util.LinkedList;
public class PanelAdapter extends ArrayAdapter {
public class PictoGridAdapter extends ArrayAdapter {
private LinkedList<Picto> lPictos;
int color;
......@@ -45,7 +45,7 @@ public class PanelAdapter extends ArrayAdapter {
// String constant for logs
private final String LOG_TAG = this.getClass().getSimpleName(); // Or .getCanonicalName()
public PanelAdapter(LinkedList<Picto> ll){
public PictoGridAdapter(LinkedList<Picto> ll){
super(PCBcontext.getContext(), R.layout.list_item_img ,ll);
//mContext = c;
lPictos = ll;
......@@ -117,7 +117,7 @@ public class PanelAdapter extends ArrayAdapter {
/*
try{
imageView.setBackgroundColor(lPictos.get(position).get_darkner_color());
//Log.d("PanelAdapter", "Position: " + position);
//Log.d("PictoGridAdapter", "Position: " + position);
} catch (java.lang.IllegalArgumentException e){
e.printStackTrace();
//imageView.setBackgroundColor(PCBcontext.getContext().getResources().getColor(R.color.blue));
......
......@@ -3,31 +3,31 @@ package com.yottacode.pictogram.gui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Point;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.EditText;
......@@ -37,21 +37,15 @@ import android.widget.RelativeLayout;
import android.widget.Toast;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.action.PictoAction;
import com.yottacode.pictogram.action.PictosAction;
import com.yottacode.pictogram.action.TalkAction;
import com.yottacode.pictogram.action.VocabularyAction;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.dao.User;
import com.yottacode.pictogram.grammar.Vocabulary;
import com.yottacode.pictogram.grammar.iLocalPicto;
import com.yottacode.pictogram.grammar.iVocabularyListener;
import com.yottacode.pictogram.net.PictoUploader;
import com.yottacode.pictogram.net.iImgDownloaderListener;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
......@@ -60,31 +54,44 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Vector;
import android.provider.Settings.Secure;
public class PictogramActivity extends Activity implements iVocabularyListener, TextToSpeech.OnInitListener {
PanelAdapter panelAdapter;
// Main layout for this activity
RelativeLayout mainLayout;
// Adapter for de grid showing the categories grid (and main pictos)
PictoGridAdapter pictoCategoriesGridAdapter;
// Grid showing the categories grid (and main pictos)
GridView pictoCategoriesGridView;
// Adapter for the grid showing pictos from a category (initially hidden)
PictoGridAdapter pictoGridAdapter;
// Grid showing pictos from a category (initially hidden)
GridView pictoGridView;
// Adapter for the tape view (list of pictos to send to the server)
TapeAdapter tapeAdapter;
// Tape view (list of pictos to send to the server)
GridView tapeGridView;
// Current picto category, if not null the corresponding category grid will be shown
Picto currentCategory;
// Object used for reading text
TextToSpeech tts;
// Element used for loading new pictos (while changing categories)
Vocabulary vocabulary;
// TODO describe this variable
static final int SELECT_PICTURE = 1;
// For disabling volume button (See method at the end of the class)
private final List blockedKeys = new ArrayList(Arrays.asList(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP));
// String constant for logs
private final String LOG_TAG = this.getClass().getSimpleName(); // Or .getCanonicalName()
// Count for delete long press (hidden exit)
private int count_deletelong = 0;
// Animation for hidding the picto category view
Animation hidePictoCategoryViewAnimation;
// Animation for showing the picto category view
Animation showPictoCategoryViewAnimation;
// Button used for showing the picto category view
ImageButton showPictoCategoriesViewButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -94,391 +101,180 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
Intent intent = getIntent();
currentCategory = null; // this for refresh vocabulary when a websocket action arrived from the dashboard
this.mainLayout = (RelativeLayout) findViewById(R.id.pictogramLayout);
this.currentCategory = null;
this.count_deletelong = 0;
count_deletelong = 0;
// Adapter for grid
panelAdapter = new PanelAdapter(new LinkedList<Picto>());
Vocabulary vocabulary = PCBcontext.getVocabulary();
if (vocabulary != null && vocabulary.size() > 0) {
this.vocabulary = PCBcontext.getVocabulary();
this.vocabulary.listen(PCBcontext.getRoom(), this);
if (this.vocabulary != null && this.vocabulary.size() > 0) {
Log.d(LOG_TAG, "Vocabulario correcto de tam:" + vocabulary.size());
LinkedList<Picto> ll;
if (currentCategory != null) ll = order(vocabulary.next(currentCategory));
else ll = order(vocabulary.startSentence());
panelAdapter.clear();
panelAdapter.addAll(ll);
}
// Open websocket connection
vocabulary.listen(PCBcontext.getRoom(), this);
// Connect the views
final GridView tapeGridView = (GridView) findViewById(R.id.tape_grid_view);
// Cojo el tamaño de la pantalla para poner el ancho dinámico
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
tapeGridView.getLayoutParams().width = width - 120;
//tapeGridView.getLayoutParams().height = height - 40;
tapeGridView.requestLayout();
/*
tapeGridView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final int actionPeformed = event.getAction();
switch (actionPeformed) {
case MotionEvent.ACTION_DOWN: {
final float x = event.getX();
final float y = event.getY();
lastXAxis = x;
lastYAxis = y;
Log.d(LOG_TAG, "TOUCH X:" + Float.toString(lastXAxis));
Log.d(LOG_TAG, "TOUCH Y:" + Float.toString(lastYAxis));
break;
}
this.tapeAdapter = new TapeAdapter();
this.tapeGridView = (GridView) this.findViewById(R.id.tape_grid_view);
this.tapeGridView.setAdapter(this.tapeAdapter);
this.pictoCategoriesGridAdapter = new PictoGridAdapter(new LinkedList<Picto>());
this.pictoCategoriesGridView = (GridView) this.findViewById(R.id.picto_categories_grid_view);
this.pictoCategoriesGridView.setAdapter(this.pictoCategoriesGridAdapter);
this.pictoGridAdapter = new PictoGridAdapter(new LinkedList<Picto>());
this.pictoGridView = (GridView) this.findViewById(R.id.picto_grid_view);
this.pictoGridView.setAdapter(this.pictoGridAdapter);
// @TODO take this value from user configuration (0: normal, 1: large)
int pictogramSize = 0;
if (pictogramSize == 0) {
this.pictoCategoriesGridView.setNumColumns(10);
this.pictoGridView.setNumColumns(10);
} else if (pictogramSize == 1) {
this.pictoCategoriesGridView.setNumColumns(4);
this.pictoGridView.setNumColumns(4);
}
case MotionEvent.ACTION_MOVE: {
final float x = event.getX();
final float y = event.getY();
// tts = new TextToSpeech(this, this, "IVONA Text-to-Speech HQ");
tts = new TextToSpeech(this, this);
final float dx = x - lastXAxis;
final float dy = y - lastYAxis;
this.tapeGridView.setOnDragListener(new OnPictoDragListener());
this.pictoCategoriesGridView.setOnDragListener(new OnPictoDragListener());
this.pictoGridView.setOnDragListener(new OnPictoDragListener());
xAxis += dx;
yAxis += dy;
this.pictoCategoriesGridView.setOnItemClickListener(new OnPictoClickListener());
this.pictoGridView.setOnItemClickListener(new OnPictoClickListener());
Log.d(LOG_TAG, "MOVE X:" + Float.toString(xAxis));
Log.d(LOG_TAG, "MOVE Y:" + Float.toString(yAxis));
break;
}
}
return true;
}
});
*/
this.pictoCategoriesGridView.setOnItemLongClickListener(new OnPictoLongClickListener());
this.pictoGridView.setOnItemLongClickListener(new OnPictoLongClickListener());
final GridView panelGridView = (GridView) findViewById(R.id.panel_grid_view);
final ImageButton deleteButton = (ImageButton) findViewById(R.id.button_delete);
//final Button deleteAllButton = (Button) findViewById(R.id.button_delete_all);
final ImageButton ttsButton = (ImageButton) findViewById(R.id.button_tts);
ttsButton.setOnClickListener(new OnTTSButtonClickListener());
deleteButton.setOnClickListener(new OnDeleteButtonClickListener());
deleteButton.setOnLongClickListener(new OnDeleteButtonLongClickListener());
// Si los pictogramas son grandes pongo una configuración. Si son pequeños otra
int pictogramSize = 0; // ESTE VALOR SE COGE DE LA CONFIGURACION DEL USUARIO (0:normal; 1:large)
if (pictogramSize == 0) panelGridView.setNumColumns(10);
else if (pictogramSize == 1) panelGridView.setNumColumns(4);
panelGridView.setAdapter(panelAdapter);
this.showPictoCategoriesViewButton = (ImageButton) this.findViewById(R.id.showPictoCategoriesViewButton);
this.showPictoCategoriesViewButton.setOnClickListener(new OnShowPictoCategoriesViewButtonClick());
// Adapter for tape
tapeAdapter = new TapeAdapter();
tapeGridView.setAdapter(tapeAdapter);
// Cargo en el TTS el motor definido para ese usuario
//tts = new TextToSpeech(this, this, "IVONA Text-to-Speech HQ");
tts = new TextToSpeech(this, this);
this.generateAnimations();
if (this.currentCategory != null) {
this.hidePictoCategoriesGridView();
} else {
this.showPictoCategoriesGridView();
}
}
/**
* Event LISTENERS
*/
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
tts.setLanguage(Locale.getDefault());
}
}
// Drag para el Panel y Tape
tapeGridView.setOnDragListener(new MyDragListener());
panelGridView.setOnDragListener(new MyDragListener());
@Override
protected void onDestroy() {
super.onDestroy();
if (tts != null){
tts.shutdown();
}
}
// onClick picto del panelGridView
panelGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
/**
* Creates the animations for moving the picto view
*/
private void generateAnimations() {
DisplayMetrics displayMetrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int animationDuration = 400;
int animationWidth = displayMetrics.widthPixels;
this.hidePictoCategoryViewAnimation = new TranslateAnimation(0, -animationWidth, 0, 0);
this.hidePictoCategoryViewAnimation.setDuration(animationDuration);
this.hidePictoCategoryViewAnimation.setFillAfter(true);
this.hidePictoCategoryViewAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
this.hidePictoCategoryViewAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Toast.makeText(getApplicationContext(), "Position: " + position + "/nid: " + id, Toast.LENGTH_SHORT).show();
//rowNumberAdapter.deleteView(position);
//rowNumberAdapter.notifyDataSetChanged();
Picto p = panelAdapter.getItem(position);
if (p != null && !p.is_invisible() && p.is_enabled()) {
Log.d(LOG_TAG, "Clic en picto: " + p.toString());
//Log.d(LOG_TAG, "STATUS: " + p.get_status());
//QUITAR PARA QUE HABLE panelAdapter.ttsPicto(p, tts);
public void onAnimationStart(Animation animation) {}
// If is not the blank picto, it isn't invisible or disabled
if (p.get_id() != 0 &&
!p.get_status().equalsIgnoreCase("invisible") &&
!p.get_status().equalsIgnoreCase("disabled")) {
LinkedList<Picto> ll = order(PCBcontext.getVocabulary().next(p));
//LinkedList<Picto> ll = vocabulary.next(p);
Log.d(LOG_TAG, "Lista de pictos recuperada: " + ll.toString());
int maxInTape = getResources().getInteger(R.integer.maxInTape);
// If the picto is a category
if (p.is_category()) {
panelAdapter.clear();
//if (panelAdapter.isEmpty())
// Log.d(LOG_TAG, "EMPTY !!!!");
//panelAdapter.deleteAll();
panelAdapter.addAll(ll);
//panelAdapter = new PanelAdapter(ll);
// Cambio el color del fondo
RelativeLayout rl = (RelativeLayout) findViewById(R.id.pictogramLayout);
//rl.setBackgroundColor(Color.RED);
if (p.get_color() != -1) {
rl.setBackgroundColor(p.get_color());
panelAdapter.setColor(p.get_darkner_color());
}
currentCategory = p;
PCBcontext.getActionLog().log(new TalkAction(TalkAction.SELECT, p));
} else if (tapeAdapter.getCount() < maxInTape) {
// If the picto selected is not a category and the tape is not full, it is placed in tape
tapeAdapter.addItem(p);
tapeAdapter.notifyDataSetChanged();
Log.d(LOG_TAG, "Picto añadido a la CINTA");
panelAdapter.clear();
panelAdapter.addAll(ll);
panelAdapter.setColor(Color.parseColor("#AFAFAF"));
RelativeLayout rl = (RelativeLayout) findViewById(R.id.pictogramLayout);
rl.setBackgroundColor(Color.parseColor("#BDBDBD"));
// Call to static log method
PCBcontext.getActionLog().log(new TalkAction(TalkAction.ADD, p));
// Send websocket notification
// when clicking a picto, the activity goes to the main category view
currentCategory = null;
}
}
}
}
});
// onLongClick picto del panelGridView (para empezar a arrastrarlo hacia el tape)
panelGridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (PCBcontext.getPcbdb().getCurrentUser().is_supervisor()) {
// Si es supervisor al hacer longClick deshabilito ese pictograma o lo habilito
Picto p = panelAdapter.getItem(position);
if (p == null) {
// No tengo pictograma. Abro una nueva ventana de selección desde el Carrete del device si no es categoria
if (PictogramActivity.this.currentCategory!=null) {
Log.d(LOG_TAG, "No tengo pictograma. Abro carrete...");
int cols = getResources().getInteger(R.integer.columns);
addPicto(position % cols, (int) (position / cols));
} else
Toast.makeText(PictogramActivity.this, PictogramActivity.this.getResources().getString(R.string.notNewCats), Toast.LENGTH_SHORT).show();
} else {
p.alter_status();
LinkedList<Picto> ll;
if (currentCategory != null) ll = order(PCBcontext.getVocabulary().next(currentCategory));
else ll = order(PCBcontext.getVocabulary().startSentence());
panelAdapter.clear();
panelAdapter.addAll(ll);
}
} else {
ClipData.Item item = new ClipData.Item("" + position);
String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
ClipData data = new ClipData("" + position, mimeTypes, item);
Picto p = panelAdapter.getItem(position);
if (p != null && !p.is_invisible() && p.is_enabled()) {
// If is not the blank picto, it isn't invisible or disabled
if (p.get_id() != 0 &&
!p.get_status().equalsIgnoreCase("invisible") &&
!p.get_status().equalsIgnoreCase("disabled") &&
tapeAdapter.getCount() < 8) {
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(data, //data to be dragged
shadowBuilder, //drag shadow
view, //local data about the drag and drop operation
0 //no needed flags
);
//view.setVisibility(View.INVISIBLE);
}
}
}
return true;
public void onAnimationEnd(Animation animation) {
pictoCategoriesGridView.setTranslationZ(-1000.0f);
pictoCategoriesGridView.setVisibility(View.GONE);
}
});
// onLongClick picto del tapeGridView (para empezar a arrastrarlo fuera del tape y eliminar de ahí)
/*
tapeGridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
ClipData.Item item = new ClipData.Item("" + position);
String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
ClipData data = new ClipData("" + position, mimeTypes, item);
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(data, //data to be dragged
shadowBuilder, //drag shadow
view, //local data about the drag and drop operation
0 //no needed flags
);
//view.setVisibility(View.INVISIBLE);
return true;
}
public void onAnimationRepeat(Animation animation) {}
});
*/
// onClick ttsButton (enviar frase a server o guardar en log local)
ttsButton.setOnClickListener(new View.OnClickListener() {
this.showPictoCategoryViewAnimation = new TranslateAnimation(-animationWidth, 0, 0, 0);
this.showPictoCategoryViewAnimation.setDuration(animationDuration);
this.showPictoCategoryViewAnimation.setFillAfter(true);
this.showPictoCategoryViewAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
this.showPictoCategoryViewAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onClick(View arg0) {
// Send websocket action
LinkedList<Picto> lp = tapeAdapter.getAll();
// Call to static log method
PCBcontext.getActionLog().log(new PictosAction(lp));
//send_phrase_action(tapeAdapter.getAll());
// Uncomment next line for locution when the version of Android be greater than 5.0
// (Unsupported on previous versions)
tapeAdapter.ttsAllNew(tts);
tapeAdapter.deleteAll();
tapeAdapter.notifyDataSetChanged();
panelAdapter.setColor(Color.parseColor("#AFAFAF"));
RelativeLayout rl = (RelativeLayout) findViewById(R.id.pictogramLayout);
rl.setBackgroundColor(Color.parseColor("#BDBDBD"));
// Load the categories
load_pictos(arg0);
public void onAnimationStart(Animation animation) {
pictoCategoriesGridView.setTranslationZ(1000.0f);
pictoCategoriesGridView.setVisibility(View.VISIBLE);
}
});
// onClick deleteButton (eliminar del tape el último picto)
deleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
if (tapeAdapter.hasElements()) {
Picto p = tapeAdapter.getLastItem();
public void onAnimationEnd(Animation animation) {}
// Call to static log method
PCBcontext.getActionLog().log(new TalkAction(TalkAction.DELETE, p));
// Send websocket action
tapeAdapter.deleteLastView();
tapeAdapter.notifyDataSetChanged();
}
}
});
// Acción para volver a la ventana de login
deleteButton.setOnLongClickListener(new AdapterView.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
count_deletelong++;
//Picto picto=PCBcontext.getVocabulary().saveLocalPicto("/sdcard/DCIM/camera/javo.jpg", "javier", 7515, 2, 2);
if (count_deletelong >= 3) {
//Log.d(LOG_TAG, "SALTO A LOGIN");
// Paso un parámetro a la SerialActivity, para controlar de dónde viene
Intent serialActivity = new Intent(getBaseContext(), SerialActivity.class);
serialActivity.putExtra("resetPrevUser", true);
startActivity(serialActivity);
}
return false;
}
public void onAnimationRepeat(Animation animation) {}
});
}
/**
* Show the main category grid view and hide the pictoGrid, unloading all pictos
* if necesary.
*/
private void showPictoCategoriesGridView() {
Log.d(LOG_TAG, "Showing pictoCategoriesGridView");
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
tts.setLanguage(Locale.getDefault());
}
}
@Override protected void onStart() {
super.onStart();
// Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show();
}
@Override protected void onResume() {
super.onResume();
//Toast.makeText(this, "onResume", Toast.LENGTH_SHORT).show();
this.pictoCategoriesGridAdapter.clear();
this.pictoCategoriesGridAdapter.addAll(this.sort(this.vocabulary.startSentence()));
this.pictoCategoriesGridAdapter.notifyDataSetChanged();
this.pictoGridAdapter.setColor(Color.parseColor("#AFAFAF"));
// String android_id = Settings.Secure.getString(this.getContentResolver(),
// Secure.ANDROID_ID);
this.pictoCategoriesGridView.setEnabled(true);
this.pictoGridView.setEnabled(false);
if (this.pictoCategoriesGridView.getAnimation() == this.hidePictoCategoryViewAnimation) {
this.pictoCategoriesGridView.startAnimation(this.showPictoCategoryViewAnimation);
}
// Toast.makeText(this, "onResume - Android ID: " + android_id, Toast.LENGTH_SHORT).show();
this.currentCategory = null;
}
@Override protected void onPause() {
// Toast.makeText(this, "onPause", Toast.LENGTH_SHORT).show();
super.onPause();
}
/**
* Hides the main category grid view and show a concrete category (this.currentCategory)
*/
private void hidePictoCategoriesGridView() {
Log.d(LOG_TAG, "Hiding pictoCategoriesGridView");
@Override protected void onStop() {
super.onStop();
// Toast.makeText(this, "onStop", Toast.LENGTH_SHORT).show();
}
this.pictoGridAdapter.clear();
this.pictoGridAdapter.addAll(this.sort(this.vocabulary.next(this.currentCategory)));
this.pictoGridAdapter.notifyDataSetChanged();
@Override protected void onRestart() {
super.onRestart();
// Toast.makeText(this, "onRestart", Toast.LENGTH_SHORT).show();
}
if (this.currentCategory.get_color() != -1) {
this.pictoGridAdapter.setColor(this.currentCategory.get_darkner_color());
this.pictoGridView.setBackgroundColor(this.currentCategory.get_color());
}
@Override protected void onDestroy() {
super.onDestroy();
if (tts != null){
tts.shutdown();
this.pictoCategoriesGridView.setEnabled(false);
this.pictoGridView.setEnabled(true);
if (this.pictoCategoriesGridView.getAnimation() != this.hidePictoCategoryViewAnimation) {
this.pictoCategoriesGridView.startAnimation(this.hidePictoCategoryViewAnimation);
}
// Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
}
/**
* Load pictos. Asociado a botón provisional para cargar el vocabulario en el adapter
* @param v
* Returns pictoGridAdapter or pictoCategoriesGridAdapter depending on the current View
*/
public void load_pictos(View v) {
if(PCBcontext.getVocabulary() != null)
Log.d(LOG_TAG, "4) Vocabulario: " + PCBcontext.getVocabulary().toString());
try {
currentCategory = null;
LinkedList<Picto> ll = order(PCBcontext.getVocabulary().startSentence());
panelAdapter.clear();
panelAdapter.addAll(ll);
}catch(NullPointerException e){
Log.d(LOG_TAG, "El vocabulario todavía no se ha cargado");
}
private PictoGridAdapter getCurrentPictoGridAdapter() {
return (currentCategory == null) ? this.pictoCategoriesGridAdapter : this.pictoGridAdapter;
}
/**
......@@ -486,7 +282,7 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
* @param list
* @return
*/
public LinkedList<Picto> order(LinkedList<Picto> list){
public LinkedList<Picto> sort(LinkedList<Picto> list){
LinkedList<Picto> ll = new LinkedList<>();
......@@ -522,27 +318,26 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
return ll;
}
/**
* Background task that updates the ui into the main thread
*/
public void refresh() {
// Background task that updates the ui into the main thread.
runOnUiThread(new Runnable() {
@Override
public void run() {
//stuff that updates ui
LinkedList<Picto> ll;
if (currentCategory != null) ll = order(PCBcontext.getVocabulary().next(currentCategory));
else ll = order(PCBcontext.getVocabulary().startSentence());
panelAdapter.clear();
panelAdapter.addAll(ll);
panelAdapter.notifyDataSetChanged();
if (currentCategory != null) {
hidePictoCategoriesGridView();
} else {
showPictoCategoriesGridView();
}
}
});
}
// De la interfaz iVocabularyListener
/**
* @TODO check documentation
* De la interfaz iVocabularyListener
*/
@Override
public void change(action action, int picto_cat, int picto_id, JSONObject args) {
Log.d(LOG_TAG, "Vocabulary action listened: " + action);
......@@ -550,7 +345,9 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
refresh();
}
// Disable Back Button --> Kiosk mode
/**
* Disable Back Button --> Kiosk mode
*/
@Override
public void onBackPressed() {
// Inflate the layout for the AlertDialog
......@@ -634,7 +431,10 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
}
// Disable long power button press
/**
* Disable long power button press (system menu)
* @param hasFocus
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
......@@ -645,7 +445,10 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
}
}
// Disable volume button
/**
* Disable volume button on key event
* @param event
*/
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (blockedKeys.contains(event.getKeyCode())) {
......@@ -655,33 +458,14 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
}
}
// Clase para el evento disparador del Drag & Drop
/*
class MyClickListener implements View.OnLongClickListener {
// called when the item is long-clicked
@Override
public boolean onLongClick(View view) {
// TODO Auto-generated method stub
// create it from the object's tag
ClipData.Item item = new ClipData.Item((CharSequence)view.getTag());
String[] mimeTypes = { ClipDescription.MIMETYPE_TEXT_PLAIN };
ClipData data = new ClipData(view.getTag().toString(), mimeTypes, item);
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag( data, //data to be dragged
shadowBuilder, //drag shadow
view, //local data about the drag and drop operation
0 //no needed flags
);
view.setVisibility(View.INVISIBLE);
return true;
}
}
*/
/* *********************************************************************************************
* Event listener classes
* ********************************************************************************************/
class MyDragListener implements View.OnDragListener {
/**
* Class used for dragging pictos to the tape
*/
private class OnPictoDragListener implements View.OnDragListener {
//Drawable normalShape = getResources().getDrawable(R.drawable.normal_shape);
//Drawable targetShape = getResources().getDrawable(R.drawable.target_shape);
......@@ -719,73 +503,25 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
// if the view is the tape_grid_view, we accept the drag item
// Destino tape_grid_view y origen panel_grid_view
if(v == findViewById(R.id.tape_grid_view) && viewgroup == findViewById(R.id.panel_grid_view)) {
//viewgroup.removeView(view);
if(v == findViewById(R.id.tape_grid_view) && viewgroup == findViewById(R.id.picto_grid_view)) {
Log.d("Drag:", "Posición: " + position);
Picto p = panelAdapter.getItem(position);
Picto p = pictoGridAdapter.getItem(position);
if(!p.is_category()) {
currentCategory = null;
tapeAdapter.addItem(p);
//tapeAdapterPictos.addView(v);
tapeAdapter.notifyDataSetChanged();
// Go to categories view
load_pictos(view);
// Call to static log method
showPictoCategoriesGridView();
PCBcontext.getActionLog().log(new TalkAction(TalkAction.ADD, p));
// Send websocket notification
/*
try {
Log.d(LOG_TAG, "Picto: " + p.toString());
//room.talk("/stu/action", "select", p);
room.talk("/stu/action", "add", p);
}catch(JSONException e){
e.printStackTrace();
Log.e(LOG_TAG, "Send action: " + e.getMessage());
}
*/
}
}
// Si el destino es el panel y el origen la cinta de frase
else if(v == findViewById(R.id.panel_grid_view) && viewgroup == findViewById(R.id.tape_grid_view)){
else if(v == findViewById(R.id.picto_grid_view) && viewgroup == findViewById(R.id.tape_grid_view)){
Log.d("Drag:", "Posición: " + position);
Picto p = tapeAdapter.getItem(position);
// Call to static log method
// PCBcontext.getActionLog().log(new Action(ActionLog.Type.Delete, p.get_translation()));
/*
try {
Log.d(LOG_TAG, "Picto: " + p.toString());
room.talk("/stu/action", "delete", p);
}catch(JSONException e){
e.printStackTrace();
Log.e(LOG_TAG, "Send action: " + e.getMessage());
}
*/
// Y se elimina del panel GridView
tapeAdapter.deleteItem(position);
tapeAdapter.notifyDataSetChanged();
}
/*
else {
view.setVisibility(View.VISIBLE);
Context context = getApplicationContext();
Toast.makeText(context, "No puedes mover la imagen aquí", Toast.LENGTH_LONG).show();
break;
}
*/
break;
......@@ -801,10 +537,180 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
}
}
/*
* Methods for add a new picto from pcb
*
*/
/**
* Class used for picto clicking feedback
*/
private class OnPictoClickListener implements AdapterView.OnItemClickListener{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Picto p = getCurrentPictoGridAdapter().getItem(position);
if (p != null && !p.is_invisible() && p.is_enabled()) {
Log.d(LOG_TAG, "Clic en picto: " + p.toString());
//Log.d(LOG_TAG, "STATUS: " + p.get_status());
//QUITAR PARA QUE HABLE pictoGridAdapter.ttsPicto(p, tts);
// If is not the blank picto, it isn't invisible or disabled
if (p.get_id() != 0 &&
!p.get_status().equalsIgnoreCase("invisible") &&
!p.get_status().equalsIgnoreCase("disabled")) {
LinkedList<Picto> ll = sort(PCBcontext.getVocabulary().next(p));
//LinkedList<Picto> ll = vocabulary.next(p);
Log.d(LOG_TAG, "Lista de pictos recuperada: " + ll.toString());
int maxInTape = getResources().getInteger(R.integer.maxInTape);
// If the picto is a category
if (p.is_category()) {
currentCategory = p;
PCBcontext.getActionLog().log(new TalkAction(TalkAction.SELECT, p));
hidePictoCategoriesGridView();
} else if (tapeAdapter.getCount() < maxInTape) {
currentCategory = null;
tapeAdapter.addItem(p);
tapeAdapter.notifyDataSetChanged();
PCBcontext.getActionLog().log(new TalkAction(TalkAction.ADD, p));
showPictoCategoriesGridView();
}
}
}
}
}
/**
* Class used for long pressing on pictos (start drag)
*/
private class OnPictoLongClickListener implements AdapterView.OnItemLongClickListener {
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (PCBcontext.getPcbdb().getCurrentUser().is_supervisor()) {
// Si es supervisor al hacer longClick deshabilito ese pictograma o lo habilito
Picto p = getCurrentPictoGridAdapter().getItem(position);
if (p == null) {
// No tengo pictograma. Abro una nueva ventana de selección desde el Carrete del device si no es categoria
if (currentCategory != null) {
Log.d(LOG_TAG, "No tengo pictograma. Abro carrete...");
int cols = getResources().getInteger(R.integer.columns);
addPicto(position % cols, (int) (position / cols));
} else
Toast.makeText(PictogramActivity.this, PictogramActivity.this.getResources().getString(R.string.notNewCats), Toast.LENGTH_SHORT).show();
} else {
p.alter_status();
showPictoCategoriesGridView();
}
} else {
ClipData.Item item = new ClipData.Item("" + position);
String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
ClipData data = new ClipData("" + position, mimeTypes, item);
Picto p = getCurrentPictoGridAdapter().getItem(position);
if (p != null && !p.is_invisible() && p.is_enabled()) {
// If is not the blank picto, it isn't invisible or disabled
if (p.get_id() != 0 &&
!p.get_status().equalsIgnoreCase("invisible") &&
!p.get_status().equalsIgnoreCase("disabled") &&
tapeAdapter.getCount() < 8) {
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(data, //data to be dragged
shadowBuilder, //drag shadow
view, //local data about the drag and drop operation
0 //no needed flags
);
//view.setVisibility(View.INVISIBLE);
}
}
}
return true;
}
}
/**
* Class used for reading pictos on TTS button click
*/
private class OnTTSButtonClickListener implements View.OnClickListener {
@Override
public void onClick(View arg0) {
// Send websocket action
LinkedList<Picto> lp = tapeAdapter.getAll();
// Call to static log method
PCBcontext.getActionLog().log(new PictosAction(lp));
//send_phrase_action(tapeAdapter.getAll());
// Uncomment next line for locution when the version of Android be greater than 5.0
// (Unsupported on previous versions)
tapeAdapter.ttsAllNew(tts);
tapeAdapter.deleteAll();
tapeAdapter.notifyDataSetChanged();
pictoGridAdapter.setColor(Color.parseColor("#AFAFAF"));
RelativeLayout rl = (RelativeLayout) findViewById(R.id.pictogramLayout);
rl.setBackgroundColor(Color.parseColor("#BDBDBD"));
// Load the categories
showPictoCategoriesGridView();
}
}
/**
* Class used for canceling a list of pictos on the tape
*/
private class OnDeleteButtonClickListener implements View.OnClickListener {
@Override
public void onClick(View arg0) {
if (tapeAdapter.hasElements()) {
Picto p = tapeAdapter.getLastItem();
// Call to static log method
PCBcontext.getActionLog().log(new TalkAction(TalkAction.DELETE, p));
// Send websocket action
tapeAdapter.deleteLastView();
tapeAdapter.notifyDataSetChanged();
}
}
}
/**
* Class used for long click on delete button. When a press count reaches 3, the application
* exists.
*/
private class OnDeleteButtonLongClickListener implements View.OnLongClickListener {
@Override
public boolean onLongClick(View v) {
count_deletelong++;
if (count_deletelong >= 3) {
// Paso un parámetro a la SerialActivity, para controlar de dónde viene
Intent serialActivity = new Intent(getBaseContext(), SerialActivity.class);
serialActivity.putExtra("resetPrevUser", true);
startActivity(serialActivity);
}
return false;
}
}
/**
* Listener used for bringing back the picto categories grid when clicked.
*/
private class OnShowPictoCategoriesViewButtonClick implements View.OnClickListener {
@Override
public void onClick(View v) {
showPictoCategoriesGridView();
}
}
/* *********************************************************************************************
* Methods for adding a new picto from pcb
* ********************************************************************************************/
/**
* add a local picto from pcb
......@@ -821,10 +727,11 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
}
/**
* función para la edición de un texto asociado a una nueva imagen y guardar el nuevo picto
*/
public void chooseTextAndSavePicto(final String selectedImagePath, final int row, final int col) {
public void chooseTextAndSavePicto(final String selectedImagePath, final int row, final int col) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getResources().getString(R.string.enterImgLabel));
......@@ -862,6 +769,7 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
builder.show();
}
/**
* Función para la selección de una foto del carrete
* @param requestCode
......@@ -908,6 +816,4 @@ public class PictogramActivity extends Activity implements iVocabularyListener,
return uri.getPath();
}
}
......@@ -2,61 +2,92 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp"
android:orientation="horizontal"
android:background="#BDBDBD"
android:keepScreenOn="true"
android:id="@+id/pictogramLayout"
tools:context=".PictogramActivity">
tools:context=".PictogramActivity"
android:padding="8dp">
<!-- android:keepScreenOn - To keep the screen bright as long as the app is visible (also forever) -->
<GridView
android:id="@+id/tape_grid_view"
android:layout_width="850dp"
android:layout_height="85dp"
android:layout_width="wrap_content"
android:layout_height="64dp"
android:padding="8dp"
android:gravity="center"
android:numColumns="@integer/columns"
android:accessibilityLiveRegion="none"
android:background="@android:color/holo_red_light"
android:clickable="false"
android:layout_alignParentTop="true">
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:paddingRight="192dp">
</GridView>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="850dp"
android:paddingTop="20dp"
android:src="@drawable/back64"
android:background="@null"
android:layout_width="96dp"
android:layout_height="64dp"
android:src="@drawable/ic_backspace_white_48dp"
android:id="@+id/button_delete"
android:layout_alignParentTop="true"/>
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/button_tts"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:clickable="true"
android:background="@null" />
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="20dp"
android:paddingTop="20dp"
android:src="@drawable/play64"
android:layout_width="96dp"
android:layout_height="64dp"
android:src="@drawable/ic_play_circle_filled_white_48dp"
android:background="@null"
android:id="@+id/button_tts"
android:layout_toRightOf="@+id/tape_grid_view"
android:adjustViewBounds="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"/>
android:layout_alignParentEnd="true"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:clickable="true" />
<ImageButton
android:layout_width="96dp"
android:layout_height="match_parent"
android:id="@+id/showPictoCategoriesViewButton"
android:layout_alignParentStart="true"
android:src="@drawable/back64"
android:background="#DDDDDD"
android:layout_below="@+id/tape_grid_view" />
<GridView
android:id="@+id/picto_grid_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_below="@+id/tape_grid_view"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:numColumns="10"
android:gravity="center_vertical|center|center_horizontal"
android:paddingTop="32dp"
android:verticalSpacing="24dp"
android:background="@color/common_signin_btn_default_background"
android:layout_toEndOf="@+id/showPictoCategoriesViewButton">
</GridView>
<GridView
android:id="@+id/panel_grid_view"
android:id="@+id/picto_categories_grid_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:paddingTop="20dp"
android:columnWidth="100dp"
android:verticalSpacing="1dp"
android:horizontalSpacing="15dp"
android:stretchMode="columnWidth"
android:layout_below="@+id/tape_grid_view">
android:layout_below="@+id/tape_grid_view"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:numColumns="10"
android:gravity="center_vertical|center|center_horizontal"
android:paddingTop="32dp"
android:verticalSpacing="24dp"
android:background="#DDDDDD">
</GridView>
</RelativeLayout>
\ No newline at end of file
......@@ -11,6 +11,7 @@ en el directorio [conf](./conf).
- Nodejs >= 0.10.37
- Npm >= 3.8.5
- Bower >= 1.4.1
- Sails 0.12.3
## Instalación del servidor y el cliente web
......
......@@ -9,7 +9,7 @@ sudo apt-get install -y nodejs
sudo npm install -g npm
# Sails install
sudo npm install -g sails
sudo npm install -g sails@0.12.3
# Sails log file
mkdir -p ./src/logs
......
{
"extends": "airbnb",
"rules": {
"comma-dangle": 0,
"func-names": 0,
"jsx-a11y/valid-aria-role": 0,
"jsx-a11y/img-uses-alt": 0,
"jsx-a11y/redundant-alt": 0,
"no-param-reassign": 0,
"no-var": 0,
"object-shorthand": 0,
"prefer-arrow-callback": 0,
"prefer-template": 0,
"strict": 0
}
}
/* global Student, PictoCoreCat, VStuLastInstruction, StuPicto, StuSup, sailsTokenAuth, sails */
/**
/* StudentController
*
......@@ -7,932 +9,967 @@
module.exports = {
//
// login action
// 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'});
// search user by username
Student.findOneByUsername(username).exec(function (err, stu) {
if (err)
return res.json(500, { error: 'DB error' });
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)
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' });
}
});
});
},
//
// 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'});
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)
return res(500, {err: JSON.stringify(err)});
// get student last method/instruction
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) {
student.current_method = 'no_method';
student.current_instruction = 'no_instruction';
} else {
student.current_method = row.met_name;
student.current_instruction = row.ins_name;
}
return res.json(student);
});
});
},
//
// create action
// adds a new student into the database
//
create: function(req, res) {
var params = req.params.all();
Student.create(params).exec(function (err, created) {
if (err) {
sails.log.debug(err);
console.log(JSON.stringify(err));
return res.json(500, err);
}
if (created) {
sails.log.debug("Student "+ created.id +" created: " + JSON.stringify(created));
// 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;
}
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);
StuPicto.create({
student: created.id,
picto: pictoCoreCat[i].picto,
attributes: {
id_cat: pictoCoreCat[i].category,
coord_x: pictoCoreCat[i].coord_x,
coord_y: pictoCoreCat[i].coord_y,
status: 'invisible', // Default, the pictos don't appear to the user
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));
});
}
});
return res.json({student: created});
}
});
},
//
// Deletes logically a student by removing id_off and linked supervisors
//
delete: function(req, res) {
if (!req.params.id_stu)
return res.json(500, {error: "No student defined"});
Student.logical_delete(req.params.id_stu, function(err) {
if (err)
return res.json(500, {error: err});
return res.json({result: 'Deleted'});
});
},
//
// Updates student information
//
update: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.findOne(req.params.id_stu).exec(function(err, stu){
if (err || !stu)
return res.json(500, {error: "No student found"});
// copy attributes
for (k in req.body) stu[k] = req.body[k];
stu.save(function(err, saved) {
if (err)
return res.json(500, {error: "Error when saving student"});
return res.json({result: 'Deleted'});
});
});
},
//
// Returns all supervisors (therapists + tutors) for the given student
//
supervisors: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.supervisors(req.params.id_stu, function(err, sups) {
if (err) throw err;
return res.json(sups);
});
},
//
// Returns all therapists for the given student
//
therapists: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.therapists(req.params.id_stu, function(err, sups) {
if (err) throw err;
return res.json(sups);
});
},
//
// Returns all tutors for the given student
//
tutors: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.tutors(req.params.id_stu, function(err, sups) {
if (err) throw err;
return res.json(sups);
});
},
// Add action
// Create a relation between supervisor and student (stu_sup)
//
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) {
if (err) {
console.log(err);
return res.json(500, {error: 'Relation student/supervisor NO created'});
}
if (created) {
console.log("Relation student/supervisor created" );
return res.json(created);
}
});
},
// destroy action
// remove the relation between supervisor and student (stu_sup)
//
unlink_supervisor: function(req, res) {
var params = req.allParams();
console.log(JSON.stringify(params));
StuSup.destroy({student: params.id_stu, supervisor: params.id_sup}).exec(function (err, destroyed) {
if (err) {
console.log(err);
return res.json(500, {error: 'Relation student/supervisor not removed'});
}
if (destroyed) {
console.log("Relation student/supervisor removed" );
return res.json({stusup: destroyed});
}
});
},
//
// Returns all the devices associated to the student
//
devices: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.devices(req.params.id_stu, function(err, devs) {
if (err) throw err;
return res.json(devs);
});
},
//
// Link a device to a student
//
link_device: function(req, res) {
var params = req.params.all();
console.log(params);
if (!params.id_stu)
return res.json(500, {error: "No student defined"});
if (!params.id_dev_firm)
return res.json(500, {error: "No device defined"});
Student.link_device(params.id_stu, params.id_dev_firm, function(err, created) {
if (err || !created)
return res.json(500, {error: 'The device could not be linked with the student'});
return res.json(created);
});
},
// destroy action
// remove the relation between student and device (stu_dev)
//
unlink_device: function(req, res) {
var params = req.allParams();
console.log(JSON.stringify(params));
StuDev.destroy({id_stu: params.id_stu, id_dev: params.id_dev}).exec(function (err, destroyed) {
if (err) {
console.log(err);
return res.json(500, {error: 'Relation student/device not removed'});
}
if (destroyed) {
console.log("Relation student/device removed");
return res.json(destroyed[0]);
}
});
},
// read action
// get methods of a student
//
methods: function(req, res) {
var params = req.allParams();
Method.find({ student: params.id_stu }).populate('instructions').exec(function (err, methods) {
if (err)
return res.json(500, {error: err});
if(!methods || methods.length == 0)
return res.json([]); // Return an empty array
if (methods) {
var l_met = [];
async.eachSeries(methods, function(m, callback1) {
sails.log.debug("Loop methods: " + m.name);
var l_ins = [];
var last_ins;
var last_time = 0;
async.eachSeries(m.instructions, function(ins, callback2) {
sails.log.debug("Loop instructions: " + ins.name);
Instruction.findOne({ id: ins.id }).populate('workingSessions', { sort: 'begin DESC' }).exec(function (err, instruction) {
if (err) {
sails.log.debug("Error in method " + m.name);
}
if(!instruction || !instruction.workingSessions || instruction.workingSessions.length == 0) {
sails.log.debug("No working sessions found for instruction " + instruction.id);
}else{
console.log("Total working sessions: " + instruction.workingSessions.length);
var last = instruction.workingSessions.length-1;
instruction.begin = instruction.workingSessions[last].end;
instruction.end = instruction.workingSessions[0].end;
if(instruction.end > last_time){
last_ins = instruction.id;
last_time = instruction.end;
}
}
// Add instruction to list (with or without tries)
l_ins.push(instruction);
callback2();
});
// Finish function when each callback is done
// Optionaly it can be passed and err parameter
}, function(err){
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('An error ocurred with a try');
return res.json(500, { error: "Error looping in tries: " + err });
}
m.instructions = l_ins;
m.last_ins = last_ins;
l_met.push(m);
callback1();
});
// Finish function when each callback is done
// Optionaly it can be passed and err parameter
}, function(err){
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('An error ocurred with an instruction');
return res.json(500, { error: "Error looping in method instructions: " + err });
} else {
// All end ok
console.log('All instructions have been processed successfully');
//Assing the built list
return res.json(l_met);
}
});
}
});
},
// read action
// get tries of the last working session
//
lasttries: function(req, res) {
var params = req.allParams();
//Student.findOne({ id: params.id_stu }).populate('workingSessions')
VStuLastInstruction.findOne({ where: {student: params.id_stu }}).exec(function (err, ws) {
if (err){
sails.log.debug("Finding student working sessions: " + err);
return res.json(500, {error: 'No student last working session found'});
}
if(!ws || ws.length == 0){
sails.log.debug("The user has not any working session");
return res.json([]); // Return an empty array of last tries
}
if (ws) {
// Find the tries of this working session populating actions
sails.log.debug("Find WS "+ JSON.stringify(ws));
Try.find({
workingSession: ws.workingSession
}).populate('actions',{supervisor:null}).exec(function (err, tries) {
if (err){
sails.log.debug("Finding student last tries: " + err);
return res.json(500, {error: 'No student last tries found'});
}
if(!tries || tries.length == 0){
sails.log.debug("The user has not any tries for working session id. "+ws.workingSession);
return res.json([]); // Return an empty array of tries
}
if(tries){
console.log("Found student tries for last working session: " + ws.workingSession);
console.log("Last tries: " + JSON.stringify(tries));
// A list for one element: The last working session
var l_ws = [];
l_ws.push({
"id": ws.workingSession,
"student": ws.student,
"begin": ws.ws_begin,
"end": ws.ws_end,
"description":ws.ws_description,
"tries": tries
});
return res.json(l_ws);
}
});
}
});
},
//
// Returns all the tries of the student
//
tries: function(req, res) {
if (!req.params.id_stu)
return res.json(500, {error: "No student defined"});
Student.tries(req.params.id_stu, function(err, l_met) {
if (err) throw err;
return res.json(l_met);
});
},
//
// Returns all pictos for the given student
//
pictos: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
sails.log.debug("Pictos requested for student " + req.params.id_stu);
Student.pictos(req.params.id_stu, function(err, pictos) {
if (err) throw err;
return res.json(pictos);
});
},
//
// Returns all working sessions for the given student
//
ws: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
sails.log.debug("Working Sessions requested for student " + req.params.id_stu);
Student.findOne(req.params.id_stu).populate('workingSessions').exec(function (err, stu) {
if (err)
return res.json(500, {error: err});
if(!stu || !stu.workingSessions || stu.workingSessions.length == 0)
return res.json([]); // Return an empty array
else
return res.json(stu.workingSessions);
});
},
// add action
// add picto to studentPicto
//
add_picto: function(req, res) {
var params = req.allParams();
console.log(JSON.stringify(params));
StuPicto.create({
student: params.id_stu,
picto: params.id_picto,
attributes: params.attributes
}).exec(function (err, added) {
if (err) {
console.log(err);
return res.json(500, {error: 'Not added picto to student'});
}
if (added) {
console.log("Added picto to student: " + JSON.stringify(added));
return res.json({
id: added.id,
picto: { id: added.picto, uri: params.uri },
attributes: added.attributes
});
}
});
},
// destroy action
// remove a picto for a student
//
delete_picto: function(req, res) {
var params = req.allParams();
console.log(JSON.stringify(params));
StuPicto.destroy({id: params.id_stuPicto}).exec(function (err, destroyed) {
if (err) {
console.log(err);
return res.json(500, {error: 'Not removed picto for student'});
}
if (destroyed) {
console.log("Removed picto "+destroyed[0].id_pic+ "for the student ");
return res.json({
id: destroyed[0].id,
attributes: JSON.parse(destroyed[0].attributes),
picto: { id: destroyed[0].id_pic }
});
}
});
},
// update action
// update picto atributes for a studentPicto
//
update_picto: function(req, res) {
var params = req.allParams();
console.log("Updating attributes for picto student "+ JSON.stringify(params));
console.log(JSON.stringify(params));
query=params.id_stuPicto ? {id: params.id_stuPicto}
: {id_stu:params.id_stu, id_pic:params.id_pic}
StuPicto.update(query, {attributes: params.attributes}).exec(function (err, updated) {
if (err) {
console.log(err);
return res.json(500, {error: 'Not updated picto for student'});
}
if (updated) {
console.log("Updated attributes for picto student:"+JSON.stringify(updated[0]));
// return res.json(updated[0]);
return res.json({
id: updated[0].id,
attributes: updated[0].attributes,
picto: { id:updated[0].picto }
});
}
});
},
//
// Upload pic file
//
upload: function (req, res) {
if(req.method === 'GET')
return res.json({'status':'GET not allowed'});
// Call to /upload via GET is error
var uploadFile = req.file('file');
console.log("Nombre de archivo: " + req.body.filename);
console.log("Nombre de carpeta: " + req.body.folder);
console.log("Id del usuario: " + req.body.id);
var fs = require('fs'), path = require('path');
var dirUpload = path.join(process.cwd(), '../upload/' + req.body.folder);
req.body.filename = sailsHash.hashCode(req.body.filename + Date().toString()) + '.' + req.body.extension;
uploadFile.upload({
dirname: dirUpload,
saveAs: req.body.filename
},
function onUploadComplete (err, files) {
// Files will be uploaded to .tmp/uploads
if (err) return res.serverError(err);
// IF ERROR Return and send 500 error with error
// Delete old pic (if it isn't the default pic)
Student.findOne({id: req.body.id}).exec(function(err, found){
if(found && found.pic != "/app/img/default.jpg"){
console.log("Found: " + found.pic);
// Path: ../upload/students/xxxx.jpg
fs.unlink('..' + found.pic, function (err) {
if (err) throw err;
console.log('Old pic destroyed');
});
}
});
// Store in DB new URL pic
Student.update({id: req.body.id}, {pic: '/upload/' + req.body.folder + '/' + req.body.filename}).exec(function(err, updated){
if(updated)
console.log("Student picture updated");
});
files[0].name = req.body.filename;
delete files[0].fd;
console.log(files);
var file = dirUpload + '/' + req.body.filename;
//
// login action
// 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' });
}
// search user by username
Student.findOneByUsername(username).exec(function (err, stu) {
if (err) {
return res.json(500, { error: 'DB error' });
}
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) {
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' });
}
});
});
},
//
// 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' });
}
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) {
return res(500, {err: JSON.stringify(err)});
}
// get student last method/instruction
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) {
student.current_method = 'no_method';
student.current_instruction = 'no_instruction';
} else {
student.current_method = row.met_name;
student.current_instruction = row.ins_name;
}
return res.json(student);
});
});
},
//
// create action
// adds a new student into the database
//
create: function(req, res) {
var params = req.params.all();
Student.create(params).exec(function (err, created) {
if (err) {
sails.log.debug(err);
console.log(JSON.stringify(err));
return res.json(500, err);
}
if (created) {
sails.log.debug("Student "+ created.id +" created: " + JSON.stringify(created));
// 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;
}
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);
StuPicto.create({
student: created.id,
picto: pictoCoreCat[i].picto,
attributes: {
id_cat: pictoCoreCat[i].category,
coord_x: pictoCoreCat[i].coord_x,
coord_y: pictoCoreCat[i].coord_y,
status: 'invisible', // Default, the pictos don't appear to the user
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));
}
});
}
});
return res.json({ student: created });
}
});
},
//
// Deletes logically a student by removing id_off and linked supervisors
//
delete: function(req, res) {
if (!req.params.id_stu)
return res.json(500, {error: "No student defined"});
Student.logical_delete(req.params.id_stu, function(err) {
if (err)
return res.json(500, {error: err});
return res.json({result: 'Deleted'});
});
},
//
// Updates student information
//
update: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.findOne(req.params.id_stu).exec(function(err, stu){
if (err || !stu)
return res.json(500, {error: "No student found"});
// copy attributes
for (k in req.body) stu[k] = req.body[k];
stu.save(function(err, saved) {
if (err)
return res.json(500, {error: "Error when saving student"});
return res.json({result: 'Deleted'});
});
});
},
//
// Returns all supervisors (therapists + tutors) for the given student
//
supervisors: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.supervisors(req.params.id_stu, function(err, sups) {
if (err) throw err;
return res.json(sups);
});
},
//
// Returns all therapists for the given student
//
therapists: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.therapists(req.params.id_stu, function(err, sups) {
if (err) throw err;
return res.json(sups);
});
},
//
// Returns all tutors for the given student
//
tutors: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.tutors(req.params.id_stu, function(err, sups) {
if (err) throw err;
return res.json(sups);
});
},
/**
* 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();
StuSup.create({
student: params.id_stu,
supervisor: params.id_sup
}).exec(function (err, created) {
var response;
var socketToOmit;
var linkSupervisorToStudentEvent;
if (err) {
response = res.serverError(
'Relation student/supervisor not created: ' + err
);
}
if (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;
});
},
// destroy action
// remove the relation between supervisor and student (stu_sup)
//
unlink_supervisor: function(req, res) {
var params = req.allParams();
console.log(JSON.stringify(params));
StuSup.destroy({student: params.id_stu, supervisor: params.id_sup}).exec(function (err, destroyed) {
if (err) {
console.log(err);
return res.json(500, {error: 'Relation student/supervisor not removed'});
}
if (destroyed) {
console.log("Relation student/supervisor removed" );
return res.json({stusup: destroyed});
}
});
},
//
// Returns all the devices associated to the student
//
devices: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
Student.devices(req.params.id_stu, function(err, devs) {
if (err) throw err;
return res.json(devs);
});
},
//
// Link a device to a student
//
link_device: function(req, res) {
var params = req.params.all();
console.log(params);
if (!params.id_stu)
return res.json(500, {error: "No student defined"});
if (!params.id_dev_firm)
return res.json(500, {error: "No device defined"});
Student.link_device(params.id_stu, params.id_dev_firm, function(err, created) {
if (err || !created)
return res.json(500, {error: 'The device could not be linked with the student'});
return res.json(created);
});
},
// destroy action
// remove the relation between student and device (stu_dev)
//
unlink_device: function(req, res) {
var params = req.allParams();
console.log(JSON.stringify(params));
StuDev.destroy({id_stu: params.id_stu, id_dev: params.id_dev}).exec(function (err, destroyed) {
if (err) {
console.log(err);
return res.json(500, {error: 'Relation student/device not removed'});
}
if (destroyed) {
console.log("Relation student/device removed");
return res.json(destroyed[0]);
}
});
},
// read action
// get methods of a student
//
methods: function(req, res) {
var params = req.allParams();
Method.find({ student: params.id_stu }).populate('instructions').exec(function (err, methods) {
if (err)
return res.json(500, {error: err});
if(!methods || methods.length == 0)
return res.json([]); // Return an empty array
if (methods) {
var l_met = [];
async.eachSeries(methods, function(m, callback1) {
sails.log.debug("Loop methods: " + m.name);
var l_ins = [];
var last_ins;
var last_time = 0;
async.eachSeries(m.instructions, function(ins, callback2) {
sails.log.debug("Loop instructions: " + ins.name);
Instruction.findOne({ id: ins.id }).populate('workingSessions', { sort: 'begin DESC' }).exec(function (err, instruction) {
if (err) {
sails.log.debug("Error in method " + m.name);
}
if(!instruction || !instruction.workingSessions || instruction.workingSessions.length == 0) {
sails.log.debug("No working sessions found for instruction " + instruction.id);
}else{
console.log("Total working sessions: " + instruction.workingSessions.length);
var last = instruction.workingSessions.length-1;
instruction.begin = instruction.workingSessions[last].end;
instruction.end = instruction.workingSessions[0].end;
if(instruction.end > last_time){
last_ins = instruction.id;
last_time = instruction.end;
}
}
// Add instruction to list (with or without tries)
l_ins.push(instruction);
callback2();
});
// Finish function when each callback is done
// Optionaly it can be passed and err parameter
}, function(err){
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('An error ocurred with a try');
return res.json(500, { error: "Error looping in tries: " + err });
}
m.instructions = l_ins;
m.last_ins = last_ins;
l_met.push(m);
callback1();
});
// Finish function when each callback is done
// Optionaly it can be passed and err parameter
}, function(err){
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('An error ocurred with an instruction');
return res.json(500, { error: "Error looping in method instructions: " + err });
} else {
// All end ok
console.log('All instructions have been processed successfully');
//Assing the built list
return res.json(l_met);
}
});
}
});
},
// read action
// get tries of the last working session
//
lasttries: function(req, res) {
var params = req.allParams();
//Student.findOne({ id: params.id_stu }).populate('workingSessions')
VStuLastInstruction.findOne({ where: {student: params.id_stu }}).exec(function (err, ws) {
if (err){
sails.log.debug("Finding student working sessions: " + err);
return res.json(500, {error: 'No student last working session found'});
}
if(!ws || ws.length == 0){
sails.log.debug("The user has not any working session");
return res.json([]); // Return an empty array of last tries
}
if (ws) {
// Find the tries of this working session populating actions
sails.log.debug("Find WS "+ JSON.stringify(ws));
Try.find({
workingSession: ws.workingSession
}).populate('actions',{supervisor:null}).exec(function (err, tries) {
if (err){
sails.log.debug("Finding student last tries: " + err);
return res.json(500, {error: 'No student last tries found'});
}
if(!tries || tries.length == 0){
sails.log.debug("The user has not any tries for working session id. "+ws.workingSession);
return res.json([]); // Return an empty array of tries
}
if(tries){
console.log("Found student tries for last working session: " + ws.workingSession);
console.log("Last tries: " + JSON.stringify(tries));
// A list for one element: The last working session
var l_ws = [];
l_ws.push({
"id": ws.workingSession,
"student": ws.student,
"begin": ws.ws_begin,
"end": ws.ws_end,
"description":ws.ws_description,
"tries": tries
});
return res.json(l_ws);
}
});
}
});
},
//
// Returns all the tries of the student
//
tries: function(req, res) {
if (!req.params.id_stu)
return res.json(500, {error: "No student defined"});
Student.tries(req.params.id_stu, function(err, l_met) {
if (err) throw err;
return res.json(l_met);
});
},
//
// Returns all pictos for the given student
//
pictos: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
sails.log.debug("Pictos requested for student " + req.params.id_stu);
Student.pictos(req.params.id_stu, function(err, pictos) {
if (err) throw err;
return res.json(pictos);
});
},
//
// Returns all working sessions for the given student
//
ws: function(req, res) {
if (!req.params.id_stu) {
return res.json(500, {error: "No student defined"});
}
sails.log.debug("Working Sessions requested for student " + req.params.id_stu);
Student.findOne(req.params.id_stu).populate('workingSessions').exec(function (err, stu) {
if (err)
return res.json(500, {error: err});
if(!stu || !stu.workingSessions || stu.workingSessions.length == 0)
return res.json([]); // Return an empty array
else
return res.json(stu.workingSessions);
});
},
// add action
// add picto to studentPicto
//
add_picto: function(req, res) {
var params = req.allParams();
console.log(JSON.stringify(params));
StuPicto.create({
student: params.id_stu,
picto: params.id_picto,
attributes: params.attributes
}).exec(function (err, added) {
if (err) {
console.log(err);
return res.json(500, {error: 'Not added picto to student'});
}
if (added) {
console.log("Added picto to student: " + JSON.stringify(added));
return res.json({
id: added.id,
picto: { id: added.picto, uri: params.uri },
attributes: added.attributes
});
}
});
},
// destroy action
// remove a picto for a student
//
delete_picto: function(req, res) {
var params = req.allParams();
console.log(JSON.stringify(params));
StuPicto.destroy({id: params.id_stuPicto}).exec(function (err, destroyed) {
if (err) {
console.log(err);
return res.json(500, {error: 'Not removed picto for student'});
}
if (destroyed) {
console.log("Removed picto "+destroyed[0].id_pic+ "for the student ");
return res.json({
id: destroyed[0].id,
attributes: JSON.parse(destroyed[0].attributes),
picto: { id: destroyed[0].id_pic }
});
}
});
},
// update action
// update picto atributes for a studentPicto
//
update_picto: function(req, res) {
var params = req.allParams();
console.log("Updating attributes for picto student "+ JSON.stringify(params));
console.log(JSON.stringify(params));
query=params.id_stuPicto ? {id: params.id_stuPicto}
: {id_stu:params.id_stu, id_pic:params.id_pic}
StuPicto.update(query, {attributes: params.attributes}).exec(function (err, updated) {
if (err) {
console.log(err);
return res.json(500, {error: 'Not updated picto for student'});
}
if (updated) {
console.log("Updated attributes for picto student:"+JSON.stringify(updated[0]));
// return res.json(updated[0]);
return res.json({
id: updated[0].id,
attributes: updated[0].attributes,
picto: { id:updated[0].picto }
});
}
});
},
//
// Upload pic file
//
upload: function (req, res) {
if(req.method === 'GET')
return res.json({'status':'GET not allowed'});
// Call to /upload via GET is error
var uploadFile = req.file('file');
console.log("Nombre de archivo: " + req.body.filename);
console.log("Nombre de carpeta: " + req.body.folder);
console.log("Id del usuario: " + req.body.id);
var fs = require('fs'), path = require('path');
var dirUpload = path.join(process.cwd(), '../upload/' + req.body.folder);
req.body.filename = sailsHash.hashCode(req.body.filename + Date().toString()) + '.' + req.body.extension;
uploadFile.upload({
dirname: dirUpload,
saveAs: req.body.filename
},
function onUploadComplete (err, files) {
// Files will be uploaded to .tmp/uploads
if (err) return res.serverError(err);
// IF ERROR Return and send 500 error with error
// Delete old pic (if it isn't the default pic)
Student.findOne({id: req.body.id}).exec(function(err, found){
if(found && found.pic != "/app/img/default.jpg"){
console.log("Found: " + found.pic);
// Path: ../upload/students/xxxx.jpg
fs.unlink('..' + found.pic, function (err) {
if (err) throw err;
console.log('Old pic destroyed');
});
}
});
// Store in DB new URL pic
Student.update({id: req.body.id}, {pic: '/upload/' + req.body.folder + '/' + req.body.filename}).exec(function(err, updated){
if(updated)
console.log("Student picture updated");
});
files[0].name = req.body.filename;
delete files[0].fd;
console.log(files);
var file = dirUpload + '/' + req.body.filename;
//setTimeout(function(){ alert("Hello"); }, 3000);
var interval = setInterval(
function(){
fs.exists(file, function(exists) {
if (exists) {
console.log("Existe el archivo");
clearInterval(interval);
res.json({status:200,file:files[0]});
} else {
console.log("No Existe el archivo");
}
});
} , 5);
}
);
},
// ***************************************************************
// WEBSOCKETS
// ***************************************************************
//
// Subscribe to websockets events
//
subscribe: function(req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
var roomName = 'studentRoom' + attributes.id_stu;
sails.log.debug("Inside /stu/subscribe. Action: " + action);
if (req.isSocket) {
sails.log.debug("Inside /stu/subscribe - isSocket");
sails.sockets.join(req.socket, roomName);
sails.io.sockets.in(roomName).clients(function(err, ids) {
sails.log.debug("number of clients in room: " + ids.length);
if (!err)
sails.io.to(roomName).emit('update_peers', {count: ids.length});
});
res.json({
msg: "Subscribed to student " + roomName
});
}
},
//
// Unsubscribe to websockets events
//
unsubscribe: function(req, res) {
var action = req.param('action');
//var attributes = req.param('attributes');
sails.log.debug("Inside /stu/unsubscribe. Action: " + action);
if (req.isSocket) {
sails.log.debug("Inside /stu/unsubscribe - isSocket");
var rooms = sails.sockets.socketRooms(req.socket);
console.log("Subscribed rooms in socket: " + JSON.stringify(rooms));
// Leave all rooms
for(var i=0; i<rooms.length; i++){
sails.sockets.leave(req.socket, rooms[i]);
sails.log.debug("Unsusbscribe from room " + rooms[i]);
}
res.json({
msg: "Unsubscribed from all rooms"
});
}
},
//
// Logs a vocabulary action and broadcast to anyone subscribed to this student
vocabulary: function(req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
var roomName = 'studentRoom' + attributes.id_stu;
sails.log.debug("Inside vocabulary");
if (req.isSocket) {
sails.log.debug("Inside vocabulary - isSocket");
// Send to all sockets subscribed to this room except the own socket that sends the message
// Parameters: room, action, data to send, socket to avoid sending (the socket that send this)
sails.sockets.broadcast(roomName, 'vocabulary', { "action": action, "attributes": attributes }, req.socket);
res.json({
msg: "Vocabulary "+ action +" action from student " + attributes.id_stu
});
}
},
//
// Logs a TRY action and broadcast to anyone subscribed to this student
action: function(req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
var roomName = 'studentRoom' + attributes.id_stu;
sails.log.debug("Inside action. Student:"+attributes.id_stu);
if (req.isSocket) {
sails.log.debug("websockets - room "+roomName);
// BROADCAST to everyone subscribed to this student
sails.sockets.broadcast(roomName, 'action', { "action": action, "attributes": attributes });
// PROBAR DESDE AQUÍ
// NEW DB STORAGE in table action
var dev = null;
if(attributes.device) dev = attributes.device;
var sup = null;
if(attributes.supervisor) sup = attributes.supervisor;
var desc = null;
if(attributes.picto) desc = attributes.picto; // select, add and delete actions data
if(attributes.pictos) desc = attributes.pictos; // show action data
Action.create({
type: action,
timestamp: attributes.timestamp,
supervisor: sup,
student: attributes.id_stu,
device: dev,
description: desc
}).exec(function (err, created) {
if (err)
res.serverError(err.details);
else if (created)
res.json({action: created});
});
/*
res.json({
msg: "Action "+ action +" action from student " + attributes.id_stu
});
*/
}
},
//
// Logs a config action and broadcast to anyone subscribed to this student
config: function(req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
var roomName = 'studentRoom' + attributes.id_stu;
sails.log.debug("Inside config");
if (req.isSocket) {
sails.log.debug("Inside config - isSocket");
sails.sockets.broadcast(roomName, 'config', { "action": action, "attributes": attributes });
res.json({
msg: "Config "+ action +" action from student " + attributes.id_stu
});
}
},
//
// Stores in the action table a bulk of actions loaded from of the
// recorded action log
//
actions_batch: function(req, res) {
var params = req.allParams();
var count = 0;
sails.log.debug("Actions_batch request");
if (!params.actions)
return res.json(400, {'error': "no actions"});
// We loop through the actions and store them in the database
async.forEach(params.actions, function(action, cb){
sails.log.debug("Actions batch is recording action: "+JSON.stringify(action));
var id_dev = null;
if (action.attributes.id_dev)
id_dev = action.attributes.id_dev;
var id_sup = null;
if(action.attributes.id_sup)
id_sup = action.attributes.sup;
var id_stu = null;
if (action.attributes.id_stu)
id_stu = action.attributes.id_stu;
var desc = null;
if(action.attributes.picto)
desc = action.attributes.picto; // select, add and delete actions data
if(action.attributes.pictos)
desc = action.attributes.pictos; // show action data
Action.create({
type: action.action,
timestamp: action.attributes.timestamp,
supervisor: id_sup,
student: id_stu,
device: id_dev,
description: desc
}).exec(function (err, created) {
if (err) {
console.log(err.details);
sails.log.error(err.details);
return cb(err);
}
else if (created)
count++;
cb();
});
},
var interval = setInterval(
function(){
fs.exists(file, function(exists) {
if (exists) {
console.log("Existe el archivo");
clearInterval(interval);
res.json({status:200,file:files[0]});
} else {
console.log("No Existe el archivo");
}
});
} , 5);
}
);
},
// ***************************************************************
// WEBSOCKETS
// ***************************************************************
//
// Subscribe to websockets events
//
subscribe: function(req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
var roomName = 'studentRoom' + attributes.id_stu;
sails.log.debug("Inside /stu/subscribe. Action: " + action);
if (req.isSocket) {
sails.log.debug("Inside /stu/subscribe - isSocket");
sails.sockets.join(req.socket, roomName);
sails.io.sockets.in(roomName).clients(function(err, ids) {
sails.log.debug("number of clients in room: " + ids.length);
if (!err)
sails.io.to(roomName).emit('update_peers', {count: ids.length});
});
res.json({
msg: "Subscribed to student " + roomName
});
}
},
//
// Unsubscribe to websockets events
//
unsubscribe: function(req, res) {
var action = req.param('action');
//var attributes = req.param('attributes');
sails.log.debug("Inside /stu/unsubscribe. Action: " + action);
if (req.isSocket) {
sails.log.debug("Inside /stu/unsubscribe - isSocket");
var rooms = sails.sockets.socketRooms(req.socket);
console.log("Subscribed rooms in socket: " + JSON.stringify(rooms));
// Leave all rooms
for(var i=0; i<rooms.length; i++){
sails.sockets.leave(req.socket, rooms[i]);
sails.log.debug("Unsusbscribe from room " + rooms[i]);
}
res.json({
msg: "Unsubscribed from all rooms"
});
}
},
//
// Logs a vocabulary action and broadcast to anyone subscribed to this student
vocabulary: function(req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
var roomName = 'studentRoom' + attributes.id_stu;
sails.log.debug("Inside vocabulary");
if (req.isSocket) {
sails.log.debug("Inside vocabulary - isSocket");
// Send to all sockets subscribed to this room except the own socket that sends the message
// Parameters: room, action, data to send, socket to avoid sending (the socket that send this)
sails.sockets.broadcast(roomName, 'vocabulary', { "action": action, "attributes": attributes }, req.socket);
res.json({
msg: "Vocabulary "+ action +" action from student " + attributes.id_stu
});
}
},
//
// Logs a TRY action and broadcast to anyone subscribed to this student
action: function(req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
var roomName = 'studentRoom' + attributes.id_stu;
sails.log.debug("Inside action. Student:"+attributes.id_stu);
if (req.isSocket) {
sails.log.debug("websockets - room "+roomName);
// BROADCAST to everyone subscribed to this student
sails.sockets.broadcast(roomName, 'action', { "action": action, "attributes": attributes });
// PROBAR DESDE AQUÍ
// NEW DB STORAGE in table action
var dev = null;
if(attributes.device) dev = attributes.device;
var sup = null;
if(attributes.supervisor) sup = attributes.supervisor;
var desc = null;
if(attributes.picto) desc = attributes.picto; // select, add and delete actions data
if(attributes.pictos) desc = attributes.pictos; // show action data
Action.create({
type: action,
timestamp: attributes.timestamp,
supervisor: sup,
student: attributes.id_stu,
device: dev,
description: desc
}).exec(function (err, created) {
if (err)
res.serverError(err.details);
else if (created)
res.json({action: created});
});
/*
res.json({
msg: "Action "+ action +" action from student " + attributes.id_stu
});
*/
}
},
//
// Logs a config action and broadcast to anyone subscribed to this student
config: function(req, res) {
var action = req.param('action');
var attributes = req.param('attributes');
var roomName = 'studentRoom' + attributes.id_stu;
sails.log.debug("Inside config");
if (req.isSocket) {
sails.log.debug("Inside config - isSocket");
sails.sockets.broadcast(roomName, 'config', { "action": action, "attributes": attributes });
res.json({
msg: "Config "+ action +" action from student " + attributes.id_stu
});
}
},
//
// Stores in the action table a bulk of actions loaded from of the
// recorded action log
//
actions_batch: function(req, res) {
var params = req.allParams();
var count = 0;
sails.log.debug("Actions_batch request");
if (!params.actions)
return res.json(400, {'error': "no actions"});
// We loop through the actions and store them in the database
async.forEach(params.actions, function(action, cb){
sails.log.debug("Actions batch is recording action: "+JSON.stringify(action));
var id_dev = null;
if (action.attributes.id_dev)
id_dev = action.attributes.id_dev;
var id_sup = null;
if(action.attributes.id_sup)
id_sup = action.attributes.sup;
var id_stu = null;
if (action.attributes.id_stu)
id_stu = action.attributes.id_stu;
var desc = null;
if(action.attributes.picto)
desc = action.attributes.picto; // select, add and delete actions data
if(action.attributes.pictos)
desc = action.attributes.pictos; // show action data
Action.create({
type: action.action,
timestamp: action.attributes.timestamp,
supervisor: id_sup,
student: id_stu,
device: id_dev,
description: desc
}).exec(function (err, created) {
if (err) {
console.log(err.details);
sails.log.error(err.details);
return cb(err);
}
else if (created)
count++;
cb();
});
},
function(err) { // function called when loop is done
if (err) {
console.log(err.details);
sails.log.error(err.details);
return res.json({'error': err.details});
} else
return res.json({'result': 'Ok', 'total': count});
});
},
//
// Returns the last instruction for the student
//
last_instruction: function(req, res) {
if (!req.params.id_stu)
return res.json(400, {err: 'id_stu parameter is missing'});
VStuLastInstruction.find({id_stu: req.params.id_stu}).exec(function(err, found) {
if (err)
return res.json(500, err);
if (!found)
return res.json({});
return res.json(found);
});
},
if (err) {
console.log(err.details);
sails.log.error(err.details);
return res.json({'error': err.details});
} else
return res.json({'result': 'Ok', 'total': count});
});
},
//
// Returns the last instruction for the student
//
last_instruction: function(req, res) {
if (!req.params.id_stu)
return res.json(400, {err: 'id_stu parameter is missing'});
VStuLastInstruction.find({id_stu: req.params.id_stu}).exec(function(err, found) {
if (err)
return res.json(500, err);
if (!found)
return res.json({});
return res.json(found);
});
},
//
// Test raw sql on models
sqlquery: function(req, res) {
/* Example of data recieved via POST
{
"id_stu": 80,
"begin": "2015-07-01",
"end": "2015-07-31"
}
*/
var params = req.params.all();
console.log(JSON.stringify(params));
// Data to return
var l_queries = {
"results" : [],
"totals" : 0
};
if (!params.id_stu)
return res.json(500, {error: "No student defined"});
var sql = "SELECT result, COUNT(result) percentage" +
" FROM method m, student s, instruction i, working_session w, try t" +
" WHERE m.id_stu=s.id and i.id_met=m.id and w.id_ins=i.id and t.id_ws=w.id and s.id="+ params.id_stu +
" and t.begin>='"+ params.begin +"' and t.end<='" + params.end + "'" +
" GROUP BY result" +
" ORDER BY result";
// 1st query
Student.sqlquery(sql, function (err,data) {
if (err) sails.log.debug(err);
else l_queries.results = data;
sql = "SELECT COUNT(result)" +
" FROM method m, student s, instruction i, working_session w, try t" +
" WHERE m.id_stu=s.id and i.id_met=m.id and w.id_ins=i.id and t.id_ws=w.id and s.id="+ params.id_stu;
// 2nd query
Student.sqlquery(sql, function (err,data) {
if (err) sails.log.debug(err);
else l_queries.totals = data;
return res.json(l_queries);
});
});
/* Example of data recieved via POST
{
"id_stu": 80,
"begin": "2015-07-01",
"end": "2015-07-31"
}
*/
var params = req.params.all();
console.log(JSON.stringify(params));
// Data to return
var l_queries = {
"results" : [],
"totals" : 0
};
if (!params.id_stu)
return res.json(500, {error: "No student defined"});
var sql = "SELECT result, COUNT(result) percentage" +
" FROM method m, student s, instruction i, working_session w, try t" +
" WHERE m.id_stu=s.id and i.id_met=m.id and w.id_ins=i.id and t.id_ws=w.id and s.id="+ params.id_stu +
" and t.begin>='"+ params.begin +"' and t.end<='" + params.end + "'" +
" GROUP BY result" +
" ORDER BY result";
// 1st query
Student.sqlquery(sql, function (err,data) {
if (err) sails.log.debug(err);
else l_queries.results = data;
sql = "SELECT COUNT(result)" +
" FROM method m, student s, instruction i, working_session w, try t" +
" WHERE m.id_stu=s.id and i.id_met=m.id and w.id_ins=i.id and t.id_ws=w.id and s.id="+ params.id_stu;
// 2nd query
Student.sqlquery(sql, function (err,data) {
if (err) sails.log.debug(err);
else l_queries.totals = data;
return res.json(l_queries);
});
});
}
};
......@@ -10,19 +10,19 @@ module.exports = {
// login action
// expected params in post body: email, password
//
login: function (req, res) {
login: function (req, res) {
var bcrypt = require('bcrypt-nodejs');
var email = req.body.email;
var password = req.body.password;
// no credentials
if (!email || !password)
return res.json(401, {error: 'No credentials sent'});
sails.log.debug("Supervisor Login request:"+email);
// search user by email
Supervisor.findOneByEmail(email).exec(function (err, sup) {
if (err)
if (err)
return res.json(500, { error: 'DB error' });
if (!sup)
......@@ -30,16 +30,16 @@ module.exports = {
// if found, check password in encrypted form
bcrypt.compare(password, sup.password, function (err, match) {
if (err)
if (err)
return res.json(500, { error: 'Server error' });
if (match) { // password match
var user = sup;
// Check if user is an Office administrator
Office.findOne(user.office).exec(function(err, off) {
if (off){
if (off){
if (off.admin == sup.id) {
console.log("This supervisor with id "+ user.id + " is admin of the office " + off.id);
user.isSupAdmin = true;
......@@ -52,10 +52,10 @@ module.exports = {
// all supervisor information is passed as payload to the token
return res.json({
user: user,
user: user,
server_time: (new Date()).getTime(),
token: sailsTokenAuth.issueToken(user, sails.config.jwt.expiresInMinutes)});
});
});
} else {
return res.json(401, { error: 'Invalid password' });
}
......@@ -69,17 +69,17 @@ module.exports = {
//
activate: function(req, res) {
if (!req.body.code)
return res.json(500, {error: "Code must be specified"});
return res.json(500, {error: "Code must be specified"});
if (!req.body.email)
return res.json(500, {error: "Email must be specified"});
return res.json(500, {error: "Email must be specified"});
// Find the supervisor by email and check the code
Supervisor.findOne({email: req.body.email}).exec(function findOneCB(err,sup){
if (err)
return res.json(500, {error: "Error finding the user"});
// PROBAR A DEVOLVER EL SUPERVISOR VACÍO!!
// PROBAR A DEVOLVER EL SUPERVISOR VACÍO!!
if(sup){
var hash_code = sailsHash.hashCode(sup.name + sup.email + sup.phone);
......@@ -94,8 +94,8 @@ module.exports = {
// Model.update(find criteria, updated records).exec(callbacks returns(error, updated records))
Supervisor.update({email: req.body.email},{active:'true', password: req.body.password }).exec(function afterwards(err,updated){
if (err)
return res.json(500, {error: "Error activating account"});
return res.json(500, {error: "Error activating account"});
console.log('User activated: '+updated[0].email);
return res.json(200, {user: updated[0]});
});
......@@ -103,8 +103,8 @@ module.exports = {
// Model.update(find criteria, updated records).exec(callbacks returns(error, updated records))
Supervisor.update({email: req.body.email},{active:'true' }).exec(function afterwards(err,updated){
if (err)
return res.json(500, {error: "Error activating account"});
return res.json(500, {error: "Error activating account"});
console.log('User activated: '+updated[0].email);
return res.json(200, {user: updated[0]});
});
......@@ -124,19 +124,19 @@ module.exports = {
//
deactivate: function(req, res) {
if (!req.body.email)
return res.json(500, {error: "Email must be specified"});
return res.json(500, {error: "Email must be specified"});
// Find the supervisor by email
Supervisor.findOne({email: req.body.email}).exec(function findOneCB(err,sup){
if (err)
return res.json(500, {error: "Error finding the user"});
if(sup){
// Model.update(find criteria, updated records).exec(callbacks returns(error, updated records))
Supervisor.update({email: req.body.email},{active:'false'}).exec(function afterwards(err,updated){
if (err)
return res.json(500, {error: "Error activating account"});
if (err)
return res.json(500, {error: "Error activating account"});
console.log('User deactivated: '+updated[0].email);
return res.json(200, {user: updated[0]});
});
......@@ -158,16 +158,16 @@ module.exports = {
Supervisor.create(params).exec(function (err, created) {
if (err) {
console.log(err);
return res.json(500, {error: 'User not created'});
return res.json(500, {error: 'User not created'});
}
if (created) {
// TODO: Send email confirmation
// created.sendMail();
return res.json({
user: created,
token: sailsTokenAuth.issueToken(created.id)});
user: created,
token: sailsTokenAuth.issueToken(created.id)});
} else {
return res.json(500, {error: 'User not created'});
return res.json(500, {error: 'User not created'});
}
});
},
......@@ -202,7 +202,7 @@ module.exports = {
[
//
// First, let's get the list of students of the supervisor
//
//
function (cb) {
console.log("getting supervisor's students");
StuSup.find({supervisor: idSup}).populate('student').exec(function(err, stusups){
......@@ -217,7 +217,7 @@ module.exports = {
stusup.student.supervision = 1;
l_stu.push(stusup.student);
next();
},
},
function(err) { // when forEach has finished
if (err) return cb(err);
cb();
......@@ -232,7 +232,7 @@ module.exports = {
function (cb) {
console.log("getting office students");
if (req.token.isSupAdmin) {
Student.find({office: req.token.office.id }).exec(function(err, students){
Student.find({office: req.token.office.id }).exec(function(err, students){
if (err) return cb(err);
var _ = require('lodash');
var l_stu_id = _.pluck(l_stu, 'id'); // get a list of supervised student ids
......@@ -264,9 +264,9 @@ module.exports = {
if (err) {
sails.log.debug(err);
console.log(JSON.stringify(err));
return res.json(500, 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 {
......@@ -279,7 +279,7 @@ module.exports = {
function(err) {
if (err) return cb(err);
cb();
});
});
}
],
function(err) { // All steps done! return list of students if no error
......@@ -295,7 +295,7 @@ module.exports = {
// returns all the supervisor's pictos
pictos: function(req, res){
var idSup = req.params.id;
Supervisor.findOne(idSup).exec(function(err, sup) {
if(err)
return res.json(500, {error: 'DB error'});
......@@ -317,7 +317,7 @@ module.exports = {
//
upload: function (req, res) {
if(req.method === 'GET')
return res.json({'status':'GET not allowed'});
return res.json({'status':'GET not allowed'});
// Call to /upload via GET is error
var uploadFile = req.file('file');
......@@ -331,16 +331,16 @@ module.exports = {
var dirUpload = path.join(process.cwd(), '../upload/' + req.body.folder);
req.body.filename = sailsHash.hashCode(req.body.filename + Date().toString()) + '.' + req.body.extension;
uploadFile.upload({
dirname: dirUpload,
uploadFile.upload({
dirname: dirUpload,
saveAs: req.body.filename
},
function onUploadComplete (err, files) {
function onUploadComplete (err, files) {
// Files will be uploaded to .tmp/uploads
if (err) return res.serverError(err);
// Files will be uploaded to .tmp/uploads
if (err) return res.serverError(err);
// IF ERROR Return and send 500 error with error
// Delete old pic (if it isn't the default pic)
Supervisor.findOne({id: req.body.id}).exec(function(err, found){
if(found && found.pic != "/app/img/default.jpg"){
......@@ -352,13 +352,13 @@ module.exports = {
console.log('Old pic destroyed');
});
}
});
// Store in DB new URL pic
});
// Store in DB new URL pic
Supervisor.update({id: req.body.id}, {pic: '/upload/' + req.body.folder + '/' + req.body.filename}).exec(function(err, updated){
if(updated)
console.log("Supervisor picture updated");
});
});
files[0].name = req.body.filename;
delete files[0].fd;
......@@ -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;
}
};
};
{
"rules": {
"comma-dangle": 0,
"func-names": 0,
"jsx-a11y/valid-aria-role": 0,
"jsx-a11y/img-uses-alt": 0,
"jsx-a11y/redundant-alt": 0,
"no-param-reassign": 0,
"no-var": 0,
"object-shorthand": 0,
"prefer-arrow-callback": 0,
"prefer-template": 0,
"strict": 0
}
}
......@@ -26,11 +26,11 @@
"alert": "Alert",
"all": "All",
"animation": "Animation",
"April": "April",
"April": "April",
"attributes_not_updated": "Changes not saved",
"attributes_updated": "Changes saved",
"attributes_updating": "Saving...",
"August": "August",
"August": "August",
"background": "Background",
"beep": "Beep",
"birthdate": "Birthdate",
......@@ -84,6 +84,10 @@
"english": "English",
"enlarge": "Enlarge",
"enormous": "Enormous",
"error_downloading_supervisors": "Error downloading supervisors",
"error_downloading_offices": "Error downloading offices",
"error_only_support_images": "Only images are supported (JPG, PNG or GIF files)",
"error_loading_pictos": "Error loading pictos information",
"expand_navigation": "Expand navigation",
"expand_navigation": "Expand navigation",
"expression": "Expression:",
......@@ -106,9 +110,9 @@
"instructions": "Instructions",
"invalid_fields": "Invalid values. Please, check fields introduced.",
"invisible": "Invisible. Clic for enable",
"January": "January",
"July": "July",
"June": "June",
"January": "January",
"July": "July",
"June": "June",
"language": "Language",
"large": "Large",
"last_session": "Last session",
......@@ -123,10 +127,10 @@
"login_success": "Login succeed. Welcome {{name}}",
"logout": "Log Out",
"man": "Man",
"March": "March",
"March": "March",
"mark": "Mark",
"max_licenses_reached": "Maximum number of licenses reached by the office",
"May": "May",
"May": "May",
"method_name_duplicated": "template already exists.Please, change the name",
"method_save": "Method saved as a new template",
"methods": "Methods",
......@@ -146,8 +150,8 @@
"num_peers": "Peers",
"num_sessions_per_day_in": "Sessions per day in",
"num_sessions_per_month_in": "Sessions per months in",
"objetive": "Objetive",
"October": "October",
"objetive": "Objetive",
"October": "October",
"office": "Office",
"office_added": "Office added",
"office_deleted": "Office deleted",
......@@ -156,7 +160,7 @@
"office_not_updated": "Office not updated",
"office_updated": "Office updated",
"offices": "Offices",
"own_labels": "Your labels",
"own_labels": "Your labels",
"own_pictos": "Your pictograms",
"pages": "Pages",
"password": "Password",
......@@ -196,7 +200,7 @@
"select_a_method": "-- Select a method --",
"select_method": "-- Select a method or create it --",
"send": "Send",
"September": "September",
"September": "September",
"serial": "Serial number",
"serial_created": "Serial number {{serial}} created",
"serial_list": "Free serial numbers",
......@@ -213,7 +217,7 @@
"spanish": "Spanish",
"started": "Started",
"state": "State",
"state_broken": "Broken",
"state_broken": "Broken",
"state_correct": "Correct",
"state_demonstration": "Demonstration",
"state_discarded": "Discarded",
......@@ -229,9 +233,9 @@
"student_pictograms": "Student's pictograms",
"student_updated": "Student updated",
"students": "Students",
"sup_not_added": "Supervisor not added to the student.",
"sup_not_added": "Supervisor not added to the student",
"sup_not_deleted": "The supervisor couldn't be deleted by the student",
"sup_not_found": "There is no supervisor account in Pictogram with this email.",
"sup_not_found": "There is no supervisor account in Pictogram with this email",
"supervisor_added": "Supervisor added",
"supervisor_deleted": "Supervisor deleted",
"supervisor_not_added": "Supervisor not added",
......@@ -258,8 +262,10 @@
"tries_per_instruction_method": "Tries per instruction of method",
"tries_per_months": "Tries per months in",
"tries_results_instruction": "Tries results in instruction",
"tutor_added": "Tutor added to the student",
"tutor_deleted": "Tutor removed from the student",
"tutor_not_added": "Tutor not added to the student.",
"tutor_not_deleted": "The tutor couldn't be deleted by the student",
"tutor_not_deleted": "Tutor couldn't be removed from the student",
"tutor_not_found": "There is no tutor account in Pictogram with this email.",
"tutors": "Parents or tutors",
"unlink": "Unlink",
......
......@@ -26,16 +26,16 @@
"alert": "Alerta",
"all": "Todos",
"animation": "Animación",
"April": "Abril",
"April": "Abril",
"attributes_not_updated": "Cambios no guardados",
"attributes_updated": "Cambios guardados",
"attributes_updating": "Guardando...",
"August": "Agosto",
"August": "Agosto",
"background": "Fondo",
"beep": "Pitido",
"birthdate": "Fecha de nacimiento",
"cancel": "Cancelar",
"cannot_delete_method": "No se pudo eliminar el método, tal vez porque existen sesiones asociadas.",
"cannot_delete_method": "No se pudo eliminar el método, tal vez porque existen sesiones asociadas.",
"categories": "Categorías",
"category_pictograms": "Pictogramas de la categoría",
"change_password": "Cambiar contraseña",
......@@ -87,6 +87,10 @@
"expand_navigation": "Desplegar navegación",
"expand_navigation": "Desplegar navegación",
"expression": "Expresión:",
"error_downloading_supervisors": "Error al descargar los supervisores",
"error_downloading_offices": "Error al descargar las oficinas",
"error_only_support_images": "Sólo se soportan imágenes (ficheros JPG, PNG o GIF)",
"error_loading_pictos": "Error cargando información de los pictos",
"February": "Febrero",
"feedback_picto": "Feedback al colocar un pictograma",
"filter": "Filtrar",
......@@ -106,9 +110,9 @@
"instructions": "Instrucciones",
"invalid_fields": "Valores inválidos. Compruebe los datos introducidos.",
"invisible": "Invisible. Clic para activar",
"January": "Enero",
"July": "Julio",
"June": "Junio",
"January": "Enero",
"July": "Julio",
"June": "Junio",
"language": "Idioma",
"large": "Grande",
"last_session": "Última sesión",
......@@ -123,10 +127,10 @@
"login_success": "Login correcto. Bienvenido {{name}}",
"logout": "Salir",
"man": "Hombre",
"March": "Marzo",
"March": "Marzo",
"mark": "Marcar",
"max_licenses_reached": "Número de licencias máximo alcanzado por la oficina",
"May": "Mayo",
"May": "Mayo",
"method_name_duplicated": "plantilla ya existe. Por favor, cambie el nombre",
"method_save": "Método guardado como nueva plantilla",
"methods": "Métodos",
......@@ -146,8 +150,8 @@
"num_peers": "Conectados",
"num_sessions_per_day_in": "Número de sesiones por día en",
"num_sessions_per_month_in": "Número de sesiones por meses en",
"objetive": "Objetivo",
"October": "Octubre",
"objetive": "Objetivo",
"October": "Octubre",
"office": "Gabinete",
"office_added": "Gabinete añadido",
"office_deleted": "Gabinete eliminado",
......@@ -156,8 +160,8 @@
"office_not_updated": "El gabinete no se ha podido actualizar",
"office_updated": "Gabinete actualizado",
"offices": "Gabinetes",
"own_labels": "Propias",
"own_pictos": "Propios",
"own_labels": "Propias",
"own_pictos": "Propios",
"pages": "Páginas",
"password": "Contraseña",
"password_confirm": "Repita la contraseña",
......@@ -196,7 +200,7 @@
"select_a_method": "-- Selecciona un método --",
"select_method": "-- Selecciona un método o créalo --",
"send": "Enviar",
"September": "Septiembre",
"September": "Septiembre",
"serial": "Número de serie",
"serial_created": "Número de serie {{serial}} creado",
"serial_list": "Números de serie libres",
......@@ -258,9 +262,11 @@
"tries_per_instruction_method": "Ensayos por instrucción del método",
"tries_per_months": "Número de ensayos por meses en",
"tries_results_instruction": "Resultados ensayos en instrucción",
"tutor_not_added": "El tutor no se ha podido añadir al estudiante.",
"tutor_not_deleted": "El tutor no se ha podido desvincular del alumno.",
"tutor_not_found": "No hay ningún tutor en Pictoram con ese correo electrónico.",
"tutor_added": "Tutor añadido al estudiante",
"tutor_deleted": "Tutor desvinculado del estudiante",
"tutor_not_added": "El tutor no se ha podido añadir al estudiante",
"tutor_not_deleted": "El tutor no se ha podido desvincular del alumno",
"tutor_not_found": "No hay ningún tutor en Pictoram con ese correo electrónico",
"tutors": "Padres o tutores",
"undefined": "Sin definir",
"unlink": "Desvincular",
......
......@@ -81,6 +81,7 @@
<body ng-controller="MainCtrl">
<!-- <div ng-view></div> -->
<toast></toast>
<div ui-view></div>
<!-- Librería jQuery requerida por los plugins de JavaScript
......
'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');
});
\ No newline at end of file
});
// 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);
}
};
});
......@@ -3,7 +3,7 @@
//--------------------------
// Admin Supervisors Controller
//--------------------------
dashboardControllers.controller('AdminSupervisorsCtrl', function AdminSupervisorsCtrl($scope, $window, $http, config) {
dashboardControllers.controller('AdminSupervisorsCtrl', function AdminSupervisorsCtrl($scope, $window, $http, config, $translate, ngToast) {
// The last parameter, config, is injected from config.js (defined in dashboardConfig module)
// Don't show the message at the begining
......@@ -25,26 +25,24 @@ dashboardControllers.controller('AdminSupervisorsCtrl', function AdminSupervisor
$http
.get(config.backend+'/sup')
.success(function(data, status, headers, config) {
// Add to list
$scope.supervisors = data;
console.log("Supervisors listed");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
$translate('error_downloading_supervisors').then(function (translation) {
ngToast.danger({ content: translation });
});
});
// List of offices (for the select form)
$http
.get(config.backend+'/office/get_all')
.success(function(data, status, headers, config) {
// Add to list
$scope.offices = data;
console.log("Offices listed");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
$translate('error_downloading_offices').then(function (translation) {
ngToast.danger({ content: translation });
});
});
// Reset form Supervisor
......@@ -69,7 +67,7 @@ dashboardControllers.controller('AdminSupervisorsCtrl', function AdminSupervisor
// Open Update Supervisor Form
$scope.update_supervisor = function(supervisor){
// Hide (if it's show) the add form
$scope.hidesupervisoradd = true;
// Show the update form
......@@ -87,7 +85,7 @@ dashboardControllers.controller('AdminSupervisorsCtrl', function AdminSupervisor
gender: supervisor.gender,
lang: supervisor.lang,
office: supervisor.office
};
};
};
// Save a Supervisor updated
......@@ -108,37 +106,33 @@ dashboardControllers.controller('AdminSupervisorsCtrl', function AdminSupervisor
$http
.put(config.backend+'/supervisor/'+supid, supervisor)
.success(function(data, status, headers, config) {
$scope.showmessagesupervisor = true;
$scope.alertsupervisor = "alert-success";
$scope.messagesupervisor = "supervisor_updated";
$translate('supervisor_updated').then(function (translation) {
ngToast.success({ content: translation });
});
// Update the view: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.supervisors.length; i++) {
if(supid == $scope.supervisors[i].id){
$scope.supervisors[i].name = data.name;
$scope.supervisors[i].surname = data.surname;
$scope.supervisors[i].address = data.address;
$scope.supervisors[i].country = data.country;
$scope.supervisors[i].email = data.email;
$scope.supervisors[i].phone = data.phone;
$scope.supervisors[i].gender = data.gender;
$scope.supervisors[i].lang = data.lang;
$scope.supervisors[i].office = data.office;
if(supid == $scope.supervisors[i].id){
$scope.supervisors[i].name = data.name;
$scope.supervisors[i].surname = data.surname;
$scope.supervisors[i].address = data.address;
$scope.supervisors[i].country = data.country;
$scope.supervisors[i].email = data.email;
$scope.supervisors[i].phone = data.phone;
$scope.supervisors[i].gender = data.gender;
$scope.supervisors[i].lang = data.lang;
$scope.supervisors[i].office = data.office;
}
}
// Delete the fields of the form to avoid data binding
// between the new element created and the form fields
$scope.resetForm();
console.log("Supervisor updated");
})
.error(function(data, status, headers, config) {
$scope.showmessagesupervisor = true;
$scope.alertsupervisor = "alert-danger";
$scope.messagesupervisor = "supervisor_not_updated";
console.log("Error from API: " + data.error);
$translate('supervisor_not_updated').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
......@@ -153,9 +147,9 @@ dashboardControllers.controller('AdminSupervisorsCtrl', function AdminSupervisor
$http
.post(config.backend+'/sup', supervisor)
.success(function(data, status, headers, config) {
$scope.showmessagesupervisor = true;
$scope.alertsupervisor = "alert-success";
$scope.messagesupervisor = "supervisor_added";
$translate('supervisor_added').then(function (translation) {
ngToast.success({ content: translation });
});
// Add to the list of supervisors in view
$scope.supervisors.push(data.user);
......@@ -164,15 +158,11 @@ dashboardControllers.controller('AdminSupervisorsCtrl', function AdminSupervisor
$scope.resetForm();
// Show the add form to new adding
$scope.hidesupervisoradd = false;
console.log("Supervisor added");
})
.error(function(data, status, headers, config) {
$scope.showmessagesupervisor = true;
$scope.alertsupervisor = "alert-danger";
$scope.messagesupervisor = "supervisor_not_added";
console.log("Error from API: " + data.error);
$translate('supervisor_not_added').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
......@@ -183,29 +173,25 @@ dashboardControllers.controller('AdminSupervisorsCtrl', function AdminSupervisor
$http
.delete(config.backend+'/supervisor/'+ supervisor.id)
.success(function(data, status, headers, config) {
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.supervisors.length; i++) {
if(supervisor.id == $scope.supervisors[i].id)
if(supervisor.id == $scope.supervisors[i].id)
$scope.supervisors.splice(i,1);
}
console.log("Supervisor deleted:" + supervisor.name);
$scope.showmessagesupervisor = true;
$scope.alertsupervisor = "alert-success";
$scope.messagesupervisor = "supervisor_deleted";
$translate('supervisor_deleted').then(function (translation) {
ngToast.success({ content: translation });
});
// Empty the form
$scope.resetForm();
})
.error(function(data, status, headers, config) {
console.log("Error deleting supervisor from API: " + data.error);
console.log("It could be students associated");
$scope.showmessagesupervisor = true;
$scope.alertsupervisor = "alert-danger";
$scope.messagesupervisor = "supervisor_not_deleted";
$translate('supervisor_not_deleted').then(function (translation) {
ngToast.danger({ content: translation });
});
});
}
};
});
\ No newline at end of file
});
<!-- Admin Supervisors -->
<!-- Alert and success messages -->
<div ng-show="{{ 'showmessagesupervisor' }}" class="alert" ng-class="alertsupervisor">{{ messagesupervisor | translate }}</div>
<div class="row">
<div ng-class="{'col-md-12':hidesupervisoradd === true && hidesupervisorupdate === true, 'col-md-8':hidesupervisoradd === false || hidesupervisorupdate === false}">
......
/* global dashboardControllers */
'use strict';
//------------------
// Login Controller
//------------------
dashboardControllers.controller('LoginCtrl', function LoginCtrl($scope, $http, $window, $translate, $filter, $location, config, $stateParams) {
// The last parameter, config, is injected from config.js (defined in dashboardConfig module)
dashboardControllers.controller('LoginCtrl',
function LoginCtrl(
$scope,
$http,
$window,
$translate,
$filter,
$location,
config,
$stateParams,
ngToast) {
$scope.credentials = {
email: '',
password: '',
lang: 'es-es'
};
// Pop-up blocker check
// var popup = window.open('http://yottacode.com','','',true);
// setTimeout( function() {
// if(!popup || popup.outerHeight === 0) {
// alert("cáspita");
// } else {
// popup && popup.close();
// }
// }, 100);
// Array of key terms to translate
$translate(['login_success', 'login_fail']).then(function (translations) {
//notification_email = translations.email_match;
$scope.message_success = translations.login_success;
$scope.message_fail = translations.login_fail;
});
// Don't show the message at the begining
$scope.showmessage = false;
// Validation of account
// if the code has been sent in the url "...app/login/code/email"
if($stateParams.code && $stateParams.email){
console.log("Code: " + $stateParams.code + "\nEmail: " + $stateParams.email);
if ($stateParams.code && $stateParams.email) {
$http
.post(config.backend+'/sup/activate', $stateParams)
.success(function(data, status, headers, config) {
$scope.showmessage = true;
$scope.alert = "alert-success";
$scope.message = "account_activate";
console.log("Supervisor: " + data.user.name);
//console.log("Account validated: " + data.user.email + " --> " + data.user.active);
.post(config.backend + '/sup/activate', $stateParams)
.success(function () {
$translate('account_activate').then(function (translation) {
ngToast.success({ content: translation });
});
})
.error(function(data, status, headers, config) {
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "account_no_activate";
console.log("Account NOT validated! Error: " + data.error);
.error(function () {
$translate('account_no_activate').then(function (translation) {
ngToast.danger({ content: translation });
});
});
}
$scope.login = function () {
$http
.post(config.backend+'/sup/login', $scope.credentials)
.success(function(data, status, headers, config) {
// Save token and user data y sessionStorage
.post(config.backend + '/sup/login', $scope.credentials)
.success(function (data) {
$window.sessionStorage.token = data.token;
// Adapt language en-us to en-gb (the latter is the one supported for 'en')
if (data.user) {
if (data.user.lang === 'en-us')
if (data.user.lang === 'en-us') {
data.user.lang = 'en-gb';
}
$scope.lang = data.user.lang;
} else {
$translate.use($scope.lang);
}
else
console.log("ERROR: no data.user!");
$translate.use($scope.lang);
// Change
// Change
$window.sessionStorage.user = JSON.stringify(data.user);
// Quitar 4 líneas siguientes. Al hacer redirección no hace falta
$scope.showmessage = true;
$scope.alert = "alert-success";
$scope.message = "login_success";
$translate('login_success').then(function (translation) {
ngToast.success({ content: translation });
});
// Name in login success message
$scope.name = data.user.name;
console.log("logged in!");
// Redirección
$location.path('/students');
})
.error(function(data, status, headers, config) {
// Delete token if user fails to login
.error(function () {
delete $window.sessionStorage.token;
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "login_fail";
$translate('login_fail').then(function (translation) {
ngToast.danger({ content: translation });
});
});
}
});
\ No newline at end of file
};
});
......@@ -3,7 +3,7 @@
//------------------
// Login Controller
//------------------
dashboardControllers.controller('LoginAdminCtrl', function LoginAdminCtrl($scope, $http, $window, $translate, $location, config) {
dashboardControllers.controller('LoginAdminCtrl', function LoginAdminCtrl($scope, $http, $window, $translate, $location, config, ngToast) {
// The last parameter, config, is injected from config.js (defined in dashboardConfig module)
$scope.credentials = {
......@@ -11,16 +11,6 @@ dashboardControllers.controller('LoginAdminCtrl', function LoginAdminCtrl($scope
password: ''
};
// Array of key terms to translate
$translate(['login_success', 'login_fail']).then(function (translations) {
//notification_email = translations.email_match;
$scope.message_success = translations.login_success;
$scope.message_fail = translations.login_fail;
});
// Don't show the message at the begining
$scope.showmessage = false;
$scope.login = function () {
$http
.post(config.backend+'/admin/login', $scope.credentials)
......@@ -29,15 +19,18 @@ dashboardControllers.controller('LoginAdminCtrl', function LoginAdminCtrl($scope
$window.sessionStorage.token = data.token;
// Redirect to admin panel
$location.path('/devices');
$translate('login_success').then(function(translation) {
ngToast.success({ content: translation });
});
})
.error(function(data, status, headers, config) {
// Delete token if user fails to login
delete $window.sessionStorage.token;
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "login_fail";
$translate('login_fail').then(function(translation) {
ngToast.danger({ content: translation });
});
});
};
});
\ No newline at end of file
});
......@@ -3,33 +3,26 @@
//-----------------------------------
// Login Setting Password Controller
//-----------------------------------
dashboardControllers.controller('LoginSettingPasswordCtrl', function LoginSettingPasswordCtrl($scope, $http, $window, $translate, $location, config, $stateParams) {
// The last parameter, config, is injected from config.js (defined in dashboardConfig module)
dashboardControllers.controller('LoginSettingPasswordCtrl', function LoginSettingPasswordCtrl($scope, $http, $window, $translate, $location, config, $stateParams, ngToast) {
// Array of key terms to translate
$translate(['validate_success', 'validate_fail']).then(function (translations) {
//notification_email = translations.email_match;
$scope.message_success = translations.validate_success;
$scope.message_fail = translations.validate_fail;
});
// Don't show the message at the begining
$scope.showmessage = false;
$scope.showlink = false;
$scope.login_setting_password = function () {
// Validation of account
// if the code has been sent in the url "...app/login/code/email"
if($stateParams.code && $stateParams.email){
console.log("Code: " + $stateParams.code + "\nEmail: " + $stateParams.email);
if ($stateParams.code && $stateParams.email) {
// Validate password match
if($scope.password != $scope.password_confirm){
// ToDo: Highlight with Angular the input
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "password_match";
if ($scope.password != $scope.password_confirm) {
$translate([
'validate_fail',
'password_match'
], function (translations) {
ngToast.danger({
content: [
translations.validate_fail,
translations.password_match
].join(': ')
});
});
return;
}
......@@ -42,23 +35,15 @@ dashboardControllers.controller('LoginSettingPasswordCtrl', function LoginSettin
$http
.post(config.backend+'/sup/activate', data)
.success(function(data, status, headers, config) {
$scope.showmessage = true;
$scope.showlink = true;
$scope.hideform = true;
$scope.alert = "alert-success";
$scope.message = "validate_success";
console.log("Supervisor: " + data.user.name);
//console.log("Account validated: " + data.user.email + " --> " + data.user.active);
$translate('validate_success').then(function (translation) {
ngToast.success({ content: translation });
});
})
.error(function(data, status, headers, config) {
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "validate_fail";
console.log("Account NOT validated! Error: " + data.error);
$translate('validate_fail').then(function (translation) {
ngToast.danger({ content: translation });
});
});
}
};
});
\ No newline at end of file
});
/* global dashboardControllers */
'use strict';
//-------------------
// SignIn Controller
//-------------------
dashboardControllers.controller('SignInCtrl', function SignInCtrl($scope, $http, $window, reCAPTCHA, $translate, config) {
// The last parameter, config, is injected from config.js (defined in dashboardConfig module)
// The previous parameter, $translate, is a dependency injection to translate text in JavaScript controller code
// Data
dashboardControllers.controller('SignInCtrl',
function SignInCtrl($scope,
$http,
$window,
reCAPTCHA,
$translate,
config,
ngToast) {
$scope.formdata = {
name: '',
surname: '',
......@@ -23,69 +27,43 @@ dashboardControllers.controller('SignInCtrl', function SignInCtrl($scope, $http,
reCAPTCHA.setPublicKey('6LdLjh0TAAAAANblo_KUGNnmRZuIetOkdjdhj1b6');
/* NOT NECESSARY
// Array of key terms to translate
$translate(['password_match', 'email_match']).then(function (translations) {
//notification_email = translations.email_match;
$scope.email_message = translations.email_match;
$scope.password_message = translations.password_match;
});
*/
// Verificar. Si no es necesario eliminar
//reCAPTCHA.setPublicKey('gazpacho andaluz');
// Don't show the message at the begining
$scope.showmessage = false;
// Form submit
$scope.signin = function () {
//$scope.showmessage = false;
// Validate email match
if($scope.formdata.email != $scope.formdata.email_confirm){
// ToDo: Highlight with Angular the input
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "email_match";
if ($scope.formdata.email !== $scope.formdata.email_confirm) {
$translate('email_match').then(function (translation) {
ngToast.danger({ content: translation });
});
return;
}
// Validate password match
if($scope.formdata.password != $scope.formdata.password_confirm){
// ToDo: Highlight with Angular the input
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "password_match";
if ($scope.formdata.password !== $scope.formdata.password_confirm) {
$translate('password_match').then(function (translation) {
ngToast.danger({ content: translation });
});
return;
}
$scope.captcha = "";
$scope.captcha = '';
if($scope.signInForm.$valid) {
$scope.showdialog = true;
console.log('Form is valid');
}else{
if ($scope.signInForm.$valid) {
$scope.showdialog = true;
} else {
return;
}
$http
.post(config.backend+'/sup', $scope.formdata)
.success(function(data, status, headers, config) {
// ToDo: When this run well, redirect to main page
$scope.showmessage = true;
$scope.alert = "alert-success";
$scope.message = "user_created";
console.log("User created");
.post(config.backend + '/sup', $scope.formdata)
.success(function () {
$translate('user_created').then(function (translation) {
ngToast.success({ content: translation });
});
})
.error(function(data, status, headers, config) {
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "user_exists";
console.log("Error from API: " + status);
.error(function () {
$translate('user_exists').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
});
......@@ -13,14 +13,11 @@
<!-- LoginCtrl controls here, see app.js -->
<form name="loginForm" ng-submit="login()" novalidate>
<!-- Alert and success messages -->
<div ng-show="{{ 'showmessage' }}" class="alert" ng-class="alert" translate translate-value-name="{{name}}">{{message}}</div>
<div class="form-group">
<input type="email" class="form-control" id="login_email" placeholder="{{ 'your_email' | translate}}" required ng-model="credentials.email" ng-focus="showmessage = false" />
<input type="email" class="form-control" id="login_email" placeholder="{{ 'your_email' | translate}}" required ng-model="credentials.email" />
</div>
<div class="form-group">
<input type="password" class="form-control" id="login_password" placeholder="{{ 'your_password' | translate}}" required ng-model="credentials.password" ng-focus="showmessage = false" />
<input type="password" class="form-control" id="login_password" placeholder="{{ 'your_password' | translate}}" required ng-model="credentials.password" />
</div>
<div class="checkbox">
<label>
......
......@@ -12,9 +12,6 @@
<!-- Formulario -->
<!-- LoginAdminCtrl controls here, see app.js -->
<form name="loginForm" ng-submit="login()" novalidate>
<!-- Alert and success messages -->
<div ng-show="{{ 'showmessage' }}" class="alert" ng-class="alert" translate translate-value-name="{{name}}">{{message}}</div>
<h3 class="text-center" translate>admin_login</h3>
<div class="form-group">
......
......@@ -11,9 +11,6 @@
<img src="img/logo_pictogram.png" alt="Pictogram" title="Pictogram" />
</p>
<!-- Alert and success messages -->
<div ng-show="{{ 'showmessage' }}" class="alert" ng-class="alert" translate translate-value-name="{{name}}">{{message}}</div>
<div ng-show="{{ 'showlink' }}" class="text-center">
<a href="/app/#/login" translate>click_login</a>
</div>
......@@ -22,11 +19,11 @@
<form ng-hide="{{ 'hideform' }}" name="loginSettingPasswordForm" ng-submit="login_setting_password()">
<div class="form-group">
<input type="password" class="form-control" id="login_password" placeholder="{{ 'password_type' | translate}}" required ng-model="password" ng-focus="showmessage = false" />
<input type="password" class="form-control" id="login_password" placeholder="{{ 'password_type' | translate}}" required />
</div>
<div class="form-group">
<input type="password" class="form-control" id="login_password_confirm" placeholder="{{ 'password_confirm' | translate}}" required ng-model="password_confirm" ng-focus="showmessage = false" />
<input type="password" class="form-control" id="login_password_confirm" placeholder="{{ 'password_confirm' | translate}}" required ng-model="password_confirm" />
</div>
<p class="color_red text_sm text-center" ng-show="password != password_confirm" translate>password_match</p>
......
......@@ -18,10 +18,6 @@
<!-- Formulario -->
<form name="signInForm" role="form" ng-submit="signin()">
<!-- Alert and success messages -->
<div ng-show="{{ 'showmessage' }}" class="alert" ng-class="alert" translate translate-values="{name: formdata.name, surname: formdata.surname, email: formdata.email}">{{message}}</div>
<fieldset>
<legend translate>personal_data</legend>
<div class="form-group">
......
/* global dashboardControllers */
'use strict';
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance, $http, config, $window, $filter, $upload, stu_id, sup, studentPictos, categories, $modal) {
dashboardControllers.controller('AddPictoCtrl', function (
$scope,
$modalInstance,
$http,
config,
$window,
$filter,
$upload,
stu_id,
sup,
studentPictos,
categories,
$modal,
$translate,
ngToast) {
var i;
var cat_id;
// Last 4 parameters passed from collections.js
// Basic data passed from the main window
$scope.sup = sup;
......@@ -11,16 +29,16 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
// Initially the source is symbolstx collection
$scope.source = 'symbolstx';
// List of studentPictos
//console.log(JSON.stringify(studentPictos));
$scope.studentPictos = studentPictos;
// List of picto categories
// console.log(JSON.stringify(categories));
$scope.categories = categories;
// Array scope for pictos dragged into categories
$scope.pictos_in_category = {};
for(var i=0; i<$scope.categories.length;i++){
var cat_id = $scope.categories[i].picto.id;
$scope.pictos_in_category['cat'+cat_id]=[];
for (i = 0; i < $scope.categories.length; i++) {
cat_id = $scope.categories[i].picto.id;
$scope.pictos_in_category['cat' + cat_id] = [];
}
// List of actual pictos (showed by navigation)
......@@ -28,78 +46,72 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
// List of categories (symbolstx) to show in navigation
$scope.symbolstxCats = [];
// List of breadcrumbs to show in navigation
$scope.breadcrumbs = [];
$scope.breadcrumbs.push({ id: 0, exp: 'Inicio', glyphicon: 'glyphicon glyphicon-home'});
$scope.breadcrumbs = [];
$scope.breadcrumbs.push({ id: 0, exp: 'Inicio', glyphicon: 'glyphicon glyphicon-home' });
// Request of general pictos categories (symbolstx)
$http
.get(config.backend+'/sup/'+ $scope.sup.id +'/pic_categories/0')
.success(function(data, status, headers, config) {
// Add to list
$scope.symbolstxCats = data;
console.log("Symbolstx categories recovered");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
$http.get(config.backend + '/sup/' + $scope.sup.id + '/pic_categories/0')
.success(function (data) {
$scope.symbolstxCats = data;
})
.error(function () {
$translate('error_loading_pictos').then(function (translation) {
ngToast.danger({ content: translation });
});
});
//
// Load pictos from a category
//
$scope.load_pictos = function (id_cat){
$http
.get(config.backend+'/sup/'+ $scope.sup.id +'/pic_fromcategory/' + id_cat)
.success(function(data, status, headers, config) {
// Add to list
if (data)
$scope.pictos = data;
else $scope.pictos = [];
//console.log(JSON.stringify($scope.pictos));
console.log("Pictos listed" + JSON.stringify(data));
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
};
$scope.load_pictos = function (id_cat) {
$http.get(config.backend + '/sup/' + $scope.sup.id + '/pic_fromcategory/' + id_cat)
.success(function (data) {
if (data) {
$scope.pictos = data;
} else {
$scope.pictos = [];
}
})
.error(function () {
$translate('error_loading_pictos').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
//
// Load pictos from owned by the actual supervisor
// TEST --> change with real supervisor pictos
//
$scope.load_own_pictos = function (){
$scope.load_own_pictos = function () {
$scope.source = 'ownpictos';
$http
.get(config.backend+'/sup/'+ $scope.sup.id +'/pictos')
.success(function(data, status, headers, config) {
// Add to list
if (data)
$scope.pictos = data;
else $scope.pictos = [];
console.log(JSON.stringify($scope.pictos));
console.log("Pictos listed");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
};
$http.get(config.backend + '/sup/' + $scope.sup.id + '/pictos')
.success(function (data) {
if (data) {
$scope.pictos = data;
} else {
$scope.pictos = [];
}
})
.error(function () {
$translate('error_loading_pictos').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
//
// Load the folders of a category
//
$scope.load_category = function (id_cat){
$http
.get(config.backend+'/sup/'+ $scope.sup.id +'/pic_categories/' + id_cat)
$http.get(config.backend+'/sup/'+ $scope.sup.id +'/pic_categories/' + id_cat)
.success(function(data, status, headers, config) {
// Add to list
if (data)
$scope.symbolstxCats = data;
else $scope.symbolstxCats = [];
console.log("Symbolstx categories recovered");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
};
......@@ -108,9 +120,9 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
//
$scope.open_category = function (cat) {
// Add to breadcrumbs
$scope.breadcrumbs.push({
id: cat.id,
exp: cat.exps[0].exp,
$scope.breadcrumbs.push({
id: cat.id,
exp: cat.exps[0].exp,
glyphicon: 'glyphicon glyphicon-chevron-right'
});
......@@ -136,7 +148,7 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
};
$scope.alert = { type: 'danger', msg: 'Category is full of pictos.', show: false };
//
// Close alert message
//
......@@ -148,27 +160,27 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
// Upload to own supervisor collection when a picto is selected from file
//
$scope.onPictoSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
// Check accepted file types
if(file.type == "image/jpeg" || file.type == "image/png" || file.type == "image/gif"){
// Get the file extension
if(file.type == "image/jpeg" || file.type == "image/png" || file.type == "image/gif"){
// Get the file extension
var extension = file.name.split('.').pop();
var filename = sup.id + file.name;
console.log("Archivo: " + filename);
//file.extra="{'filename': filename, 'extension': extension, 'folder': 'custompictos', 'owner': $scope.sup.id, 'source': '2'}";
$upload.upload({
url: '/picto/upload', //upload.php script, node.js route, or servlet url
method: 'POST', // or 'PUT',
url: '/picto/upload', //upload.php script, node.js route, or servlet url
method: 'POST', // or 'PUT',
// Fields to be sent in the body of the request
fields: {'filename': filename, 'extension': extension, 'folder': 'custompictos', 'owner': $scope.sup.id, 'source': '2'},
// or ['1.jpg', '2.jpg', ...] // to modify the name of the file(s)
file: file, // or list of files ($files) for html5 only
fields: {'filename': filename, 'extension': extension, 'folder': 'custompictos', 'owner': $scope.sup.id, 'source': '2'},
// or ['1.jpg', '2.jpg', ...] // to modify the name of the file(s)
file: file, // or list of files ($files) for html5 only
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
......@@ -204,7 +216,7 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
var cat_y = parseInt(v_dest[2]);
var pictosCategory = $filter('filter')($scope.studentPictos, { attributes: { id_cat: cat_id }}, true);
//console.log(JSON.stringify(pictosCategory));
//console.log(JSON.stringify(pictosCategory));
// Build the pcb_cat with the coords of the pictos to assign the new picto near category picto
var rows = 5, cols = 10;
......@@ -223,7 +235,7 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
for(var i=0; i < pictosCategory.length; i++) {
var pic = pictosCategory[i];
// Compute the position in array
pcb_cat[pic.attributes.coord_x][pic.attributes.coord_y] = pic;
pcb_cat[pic.attributes.coord_x][pic.attributes.coord_y] = pic;
}
// Only for test
......@@ -247,7 +259,7 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
var x, y;
while((iU>=0 || iD<rows) && !find){
if(iD<rows && !find){
if(iD<rows && !find){
// Actualizar iL e iR a la columna de la categoría
var iR = cat_y+1;
var iL = cat_y;
......@@ -286,7 +298,7 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
}
iU--; iD++;
}
var uri = "";
var p = {};
......@@ -296,19 +308,19 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
// Show alert
$scope.alert.show = true;
console.log("Category is full. There is no free space for new pictos");
}else{
// Close alert (if it's open)
$scope.alert.show = false;
// Search the item in left pictos
for(var i=0; i<$scope.pictos.length; i++){
if(origin == $scope.pictos[i].id){
//$scope.pictos_in_category['cat'+cat_id].push($scope.pictos[i]);
//console.log("PICTO CATEGORY: " + JSON.stringify($scope.pictos_in_category['cat'+cat_id]));
//console.log($scope.pictos_in_category['cat'+cat_id][0].uri);
//uri = $scope.pictos[i].uri;
p = $scope.pictos[i];
//console.log("PICTO FOUND: " + JSON.stringify(p));
......@@ -318,7 +330,7 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
// Update the origin picto coords in server (if the picto is not empty)
$http
.post(config.backend+'/stu/'+ $scope.stu_id + '/picto/' +
.post(config.backend+'/stu/'+ $scope.stu_id + '/picto/' +
origin, { 'uri': p.uri, 'attributes': { 'id_cat': cat_id, 'coord_x': x , 'coord_y': y }})
.success(function(data, status, headers, config) {
console.log('Picto ' + origin +' has been dropped to user '+ $scope.stu_id +' into picto --> ' + cat_id + '('+ cat_x +','+ cat_y +')');
......@@ -340,10 +352,10 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
io.socket.post('/stu/vocabulary', {
action: 'add',
attributes: {
id_stu: $scope.stu_id,
id_stu: $scope.stu_id,
picto: data
}
},
},
function(res) {
console.log("Vocabulary emitted: " + JSON.stringify(res.msg));
});
......@@ -352,30 +364,30 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
alert('Pictogram is already part of this vocabulary');
alert('Pictogram is already part of this vocabulary');
});
}
};
};
//
// Delete picto added
//
$scope.remove_picto = function (stuPicto, id_cat){
console.log("delete_studentPicto: " + JSON.stringify(stuPicto));
$http
.delete(config.backend+'/stu/'+ $scope.stu_id + '/picto/' + stuPicto.id)
.success(function(data, status, headers, config) {
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.studentPictos.length; i++) {
if(stuPicto.id == $scope.studentPictos[i].id)
if(stuPicto.id == $scope.studentPictos[i].id)
$scope.studentPictos.splice(i,1);
}
for(var i=0; i < $scope.pictos_in_category['cat'+id_cat].length; i++) {
if(stuPicto.id == $scope.pictos_in_category['cat'+id_cat][i].id)
if(stuPicto.id == $scope.pictos_in_category['cat'+id_cat][i].id)
$scope.pictos_in_category['cat'+id_cat].splice(i,1);
}
......@@ -386,10 +398,10 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
io.socket.post('/stu/vocabulary', {
action: 'delete',
attributes: {
id_stu: $scope.stu_id,
id_stu: $scope.stu_id,
picto: data
}
},
},
function(res) {
console.log("Vocabulary emitted: " + JSON.stringify(res.msg));
});
......@@ -399,7 +411,7 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
};
//
......@@ -412,10 +424,10 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
$http
.delete(config.backend+'/picto/'+ id_picto)
.success(function(data, status, headers, config) {
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.pictos.length; i++) {
if(id_picto == $scope.pictos[i].id)
if(id_picto == $scope.pictos[i].id)
$scope.pictos.splice(i,1);
}
......@@ -424,7 +436,7 @@ dashboardControllers.controller('AddPictoCtrl', function ($scope, $modalInstance
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
}
}
};
//
......
/* global dashboardControllers */
'use strict';
//-----------------------
// Student Setup Controller
//-----------------------
dashboardControllers.controller('StudentSetupCtrl', function StudentSetupCtrl($scope, $http, config, $stateParams, $window, $upload) {
dashboardControllers.controller('StudentSetupCtrl', function StudentSetupCtrl(
$scope,
$http,
config,
$stateParams,
$window,
$upload,
$translate,
ngToast) {
// For tab navigation (here too, if the user refresh the page...)
$scope.nav.tab = 'setup';
// When a picture is selected, launch the request
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
$scope.onFileSelect = function ($files) {
var i;
var file;
var extension;
var filename;
for (i = 0; i < $files.length; i++) {
file = $files[i]; // { name, size, type }
// Check accepted file types
if(file.type == "image/jpeg" || file.type == "image/png" || file.type == "image/gif"){
// Get the file extension
var extension = file.name.split('.').pop();
var filename = $scope.studentData.id + file.name;
console.log("Archivo: " + filename);
if (file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif') {
extension = file.name.split('.').pop();
filename = $scope.studentData.id + file.name;
$upload.upload({
url: '/stu/upload', //upload.php script, node.js route, or servlet url
method: 'POST', // or 'PUT',
// Fields to sent in the body of the request
fields: {'filename': filename, 'extension': extension, 'folder': 'students', 'id': $scope.studentData.id},
// or ['1.jpg', '2.jpg', ...] // to modify the name of the file(s)
file: file, // or list of files ($files) for html5 only
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(JSON.stringify(data.file));
// Assign the new image with new name
$scope.studentData.pic = "/upload/students/" + data.file.name;
//$scope.picture = user.id + "." + extension + "?t=" + new Date().getTime();
url: '/stu/upload', // upload.php script, node.js route, or servlet url
method: 'POST', // or 'PUT',
fields: { // Fields to sent in the body of the request
filename: filename,
extension: extension,
folder: 'students',
id: $scope.studentData.id
},
file: file,
}).progress(function () {
// TODO show progress to user
}).success(function (data) {
$translate('student_updated').then(function (translation) {
ngToast.success({ content: translation });
});
$scope.studentData.pic = '/upload/students/' + data.file.name;
});
} else {
$translate('error_only_support_images').then(function (translation) {
ngToast.danger({ content: translation });
});
}else{
alert("It's only supported JPG, PNG and GIF files");
}
}
};
// Open calendar
$scope.open_calendar = function($event) {
$scope.open_calendar = function ($event) {
$event.preventDefault();
$event.stopPropagation();
......@@ -53,33 +66,30 @@ dashboardControllers.controller('StudentSetupCtrl', function StudentSetupCtrl($s
};
// Save personal info updated
$scope.update_student = function(){
$scope.update_student = function () {
// Validate password match
if( $scope.formUser.password_confirm &&
!$scope.formUser.password_confirm.length &&
$scope.formUser.password != $scope.formUser.password_confirm) {
// ToDo: Highlight with Angular the input
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "password_match";
if ($scope.formUser.password_confirm &&
!$scope.formUser.password_confirm.length &&
$scope.formUser.password !== $scope.formUser.password_confirm) {
$translate('password_match').then(function (translation) {
ngToast.danger({ content: translation });
});
return;
}
// password not changed (don't send it to DB)
if ($scope.formUser.password_confirm &&
!$scope.formUser.password_confirm.length) { // password not changed (don't send it to DB)
!$scope.formUser.password_confirm.length) {
delete $scope.formUser.password;
delete $scope.formUser.password_confirm;
}
$http
.put(config.backend+'/stu/'+$scope.studentData.id, $scope.formUser)
.success(function(data, status, headers, config) {
$scope.showmessagestudent = true;
$scope.alertstudent = "alert-success";
$scope.messagestudent = "student_updated";
$http.put(config.backend + '/stu/' + $scope.studentData.id, $scope.formUser)
.success(function (data) {
$translate('student_updated').then(function (translation) {
ngToast.success({ content: translation });
});
// studentData with new values
$scope.studentData.surname = data.surname;
$scope.studentData.birthdate = data.birthdate;
$scope.studentData.country = data.country;
......@@ -87,145 +97,131 @@ dashboardControllers.controller('StudentSetupCtrl', function StudentSetupCtrl($s
$scope.studentData.gender = data.gender;
$scope.studentData.lang = data.lang;
$scope.studentData.notes = data.notes;
console.log("Student updated");
})
.error(function(data, status, headers, config) {
$scope.showmessagestudent = true;
$scope.alertstudent = "alert-danger";
$scope.messagestudent = "student_not_updated";
console.log("Error from API: " + data.error);
.error(function () {
$translate.danger('student_not_updated', function (translation) {
ngToast.danger({ content: translation });
});
});
};
// Search supervisor by email
$scope.search_sup = function(){
$scope.search_sup = function () {
// Find tutor by email
$http
.get(config.backend+'/sup?where={"email":"'+ $scope.email_sup +'", "office":{"!": null}}')
.success(function(data, status, headers, config) {
// If it found the length is > 0
if(data.length > 0){
console.log("Supervisor found");
$http.get([
config.backend,
'/sup?where={"email":"',
$scope.email_sup,
'", "office":{"!": null}}'
].join(''))
.success(function (data) {
if (data.length > 0) {
$scope.supToAdd = data[0];
// Show message for validate
$scope.showmessagesupfound = true;
$scope.showmessagesupnotfound = false;
}else{
console.log("Supervisor NOT found");
$scope.showmessagesupnotfound = true;
$scope.messagesup = "sup_not_found";
} else {
$translate('sup_not_found').then(function (translation) {
ngToast.danger({ content: translation });
});
// Hide the success message (if it exists by other query)
$scope.showmessagesupfound = false;
}
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
.error(function () {
$translate('sup_not_found').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
};
// Add supervisor (father or mother)
$scope.add_sup = function(){
$scope.add_sup = function () {
var stusup = {
student: $scope.studentData.id,
supervisor: $scope.supToAdd.id
}
};
$http
.get(config.backend+'/stu/'+ $scope.studentData.id + '/sup/' + $scope.supToAdd.id)
.success(function(data, status, headers, config) {
$http.get(config.backend + '/stu/' + $scope.studentData.id + '/sup/' + $scope.supToAdd.id)
.success(function (data) {
// Assign the info of supervisor to add
stusup.supervisor = $scope.supToAdd;
stusup.id = data.id;
console.log(JSON.stringify(stusup));
// Add to the list of tutors in view
$scope.studentSupervisors.push($scope.supToAdd);
// Delete the email form field
$scope.email_sup = '';
// Hide the message of supervisor founded
$scope.showmessagesupfound = false;
console.log("Supervisor added");
})
.error(function(data, status, headers, config) {
$scope.showmessagesupnotfound = true;
$scope.messagetutor = "sup_not_added";
console.log("Error from API: " + data.error);
.error(function () {
$translate('sup_not_added').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
// Delete tutor
$scope.delete_sup = function (id_sup){
console.log("delete_supervisor:" + id_sup);
$scope.delete_sup = function (id_sup) {
var deleteSup = $window.confirm('Are you absolutely sure you want to delete?');
if(deleteSup){
$http
.delete(config.backend+'/stu/'+ $scope.studentData.id + '/sup/' + id_sup)
.success(function(data, status, headers, config) {
if (deleteSup) {
$http.delete(config.backend + '/stu/' + $scope.studentData.id + '/sup/' + id_sup)
.success(function () {
var i;
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.studentSupervisors.length; i++) {
if(id_sup == $scope.studentSupervisors[i].id)
$scope.studentSupervisors.splice(i,1);
for (i = 0; i < $scope.studentSupervisors.length; i++) {
if (id_sup === $scope.studentSupervisors[i].id) {
$scope.studentSupervisors.splice(i, 1);
}
}
console.log("Supervisor deleted");
$translate('supervisor_deleted').then(function (translation) {
ngToast.success({ content: translation });
});
})
.error(function(data, status, headers, config) {
$scope.showmessagesupnotfound = true;
$scope.messagetutor = "sup_not_deleted";
console.log("Error from API: " + data.error);
.error(function () {
$translate('sup_not_deleted').then(function (translation) {
ngToast.error({ content: translation });
});
});
}
};
// Search tutor by email
$scope.search_tutor = function(){
$scope.search_tutor = function () {
// Find tutor by email
$http
.get(config.backend+'/sup?where={"email":"'+ $scope.email_tutor +'", "office":null}')
.success(function(data, status, headers, config) {
$http.get(config.backend + '/sup?where={"email":"' + $scope.email_tutor + '", "office":null}')
.success(function (data) {
// If it found the length is > 0
if(data.length > 0){
console.log("Tutor found");
if (data.length > 0) {
$scope.tutorToAdd = data[0];
// Show message for validate
$scope.showmessagetutorfound = true;
$scope.showmessagetutornotfound = false;
}else{
console.log("Tutor NOT found");
$scope.showmessagetutornotfound = true;
$scope.messagetutor = "tutor_not_found";
} else {
$translate('tutor_not_found').then(function (translation) {
ngToast.danger({ content: translation });
});
// Hide the success message (if it exists by other query)
$scope.showmessagetutorfound = false;
}
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
.error(function () {
$translate('tutor_not_found').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
};
// Add tutor
$scope.add_tutor = function(){
$scope.add_tutor = function () {
var stusup = {
student: $scope.studentData.id,
supervisor: $scope.tutorToAdd.id
}
};
$http
.get(config.backend+'/stu/'+ $scope.studentData.id + '/sup/' + $scope.tutorToAdd.id)
.success(function(data, status, headers, config) {
$http.get(config.backend + '/stu/' + $scope.studentData.id + '/sup/' + $scope.tutorToAdd.id)
.success(function (data) {
// Assign the info of supervisor to add
stusup.supervisor = $scope.tutorToAdd;
stusup.id = data.id;
......@@ -236,54 +232,57 @@ dashboardControllers.controller('StudentSetupCtrl', function StudentSetupCtrl($s
// Hide the message of tutor founded
$scope.showmessagetutorfound = false;
console.log("Tutor added");
$translate('tutor_added').then(function (translation) {
ngToast.success({ content: translation });
});
})
.error(function(data, status, headers, config) {
$scope.showmessagetutornotfound = true;
$scope.messagetutor = "tutor_not_added";
console.log("Error from API: " + data.error);
.error(function () {
$translate('tutor_not_added').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
// Delete tutor
$scope.delete_tutor = function (id_sup){
console.log("delete_tutor:" + id_sup);
$scope.delete_tutor = function (id_sup) {
var deleteTutor = $window.confirm('Are you absolutely sure you want to delete?');
if(deleteTutor){
$http
.delete(config.backend+'/stu/'+ $scope.studentData.id + '/sup/' + id_sup)
.success(function(data, status, headers, config) {
if (deleteTutor) {
$http.delete(config.backend + '/stu/' + $scope.studentData.id + '/sup/' + id_sup)
.success(function () {
var i;
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.studentTutors.length; i++) {
if(id_sup == $scope.studentTutors[i].id)
$scope.studentTutors.splice(i,1);
for (i = 0; i < $scope.studentTutors.length; i++) {
if (id_sup === $scope.studentTutors[i].id) {
$scope.studentTutors.splice(i, 1);
}
}
console.log("Tutor deleted");
$translate('tutor_deleted').then(function (translation) {
ngToast.success({ content: translation });
});
})
.error(function(data, status, headers, config) {
$scope.showmessagetutornotfound = true;
$scope.messagetutor = "tutor_not_deleted";
console.log("Error from API: " + data.error);
.error(function () {
$translate('tutor_not_deleted').then(function (translation) {
ngToast.danger({ content: translation });
});
});
}
};
// TODO issue/357
$scope.delete_device = function (id_dev){
var deleteDevice = $window.confirm('Are you absolutely sure you want to delete?');
if(deleteDevice){
$http
.delete(config.backend+'/stu/'+ $scope.studentData.id +'/dev/' + id_dev)
if (deleteDevice) {
$http.delete(config.backend + '/stu/' + $scope.studentData.id + '/dev/' + id_dev)
.success(function(data, status, headers, config) {
var i;
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.studentDevices.length; i++) {
if(id_dev == $scope.studentDevices[i].id)
for (i = 0; i < $scope.studentDevices.length; i++) {
if (id_dev == $scope.studentDevices[i].id) {
$scope.studentDevices.splice(i,1);
}
}
console.log("Device deleted with id " + id_dev);
......@@ -297,6 +296,7 @@ dashboardControllers.controller('StudentSetupCtrl', function StudentSetupCtrl($s
}
};
// TODO issue/357
$scope.associate_dev = function(){
var stu_dev = {
"id_stu": $scope.studentData.id,
......@@ -306,7 +306,7 @@ dashboardControllers.controller('StudentSetupCtrl', function StudentSetupCtrl($s
$http
.post(config.backend+'/stu/dev/', stu_dev)
.success(function(data, status, headers, config) {
console.log("Created relation");
console.log(JSON.stringify(data));
......@@ -315,7 +315,7 @@ dashboardControllers.controller('StudentSetupCtrl', function StudentSetupCtrl($s
$scope.studentDevices.push(data);
// Remove the id from the input text form
$scope.device_id_firmware = '';
// Hide not found message if it is displayed
// Hide not found message if it is displayed
$scope.showmessagedevnotfound = false;
})
.error(function(data, status, headers, config) {
......@@ -357,4 +357,4 @@ dashboardControllers.controller('StudentSetupCtrl', function StudentSetupCtrl($s
console.log("Error from API: " + data.error);
});
};
});
\ No newline at end of file
});
/* global dashboardControllers, io */
'use strict';
//-----------------------
// Student Controller
//-----------------------
dashboardControllers.controller('StudentCtrl', function StudentCtrl($scope, config, $state, $http, $window, $filter, $rootScope) {
/**
* StudentCtrl
*/
dashboardControllers.controller('StudentCtrl', function StudentCtrl(
$scope,
config,
$state,
$http,
$window,
$filter,
$translate,
ngToast) {
var i;
var j;
$scope.studentData = {
id: '',
username: '',
password: '',
password_confirm: '',
name: '',
surname: '',
birthdate: '',
country: '',
pic: '',
gender: '',
lang: '',
notes: '',
attributes: {},
office: {
id: '',
name: ''
},
stuSup: [],
num_peers: 1
id: '',
username: '',
password: '',
password_confirm: '',
name: '',
surname: '',
birthdate: '',
country: '',
pic: '',
gender: '',
lang: '',
notes: '',
attributes: {},
office: {
id: '',
name: ''
},
stuSup: [],
num_peers: 1
};
// For the user form data in setup section
......@@ -34,29 +46,29 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl($scope, conf
// Accessing the idStudent defined in the states of tabs (student/XX/collections)
// from the abstract parent (it has no url)
$scope.studentData.id = $state.$current.locals.globals.$stateParams.idStudent;
//
// WebSockets communication
//
io.socket.on('update_peers', function(data) {
console.log(data.count + ' peers connected');
$scope.studentData.num_peers = data.count;
io.socket.on('update_peers', function (data) {
$translate('num_peers').then(function (translation) {
ngToast.success(translation + ': ' + data.count);
});
$scope.studentData.num_peers = data.count;
});
io.socket.on('reconnect', function () {
// Subscribe to student's socket room
console.log("reconnecting to socket");
io.socket.post('/stu/subscribe', {
action: 'subscribe',
attributes:{
id_stu: $scope.studentData.id
},
token: $window.sessionStorage.token
},
function(res) {
console.log("Connected to student: " + res.msg);
});
// Subscribe to student's socket room
io.socket.post('/stu/subscribe', {
action: 'subscribe',
attributes: {
id_stu: $scope.studentData.id
},
token: $window.sessionStorage.token
},
function () {
// TODO ngToast res.msg (connected to student room)
});
});
// ----------------------------------------------------------------------
......@@ -65,214 +77,142 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl($scope, conf
//
//
$http
.get(config.backend+'/stu/' + $scope.studentData.id)
.success(function(data, status, headers, config) {
// studentData
$scope.studentData.id = data.id;
$scope.studentData.username = data.username;
$scope.studentData.name = data.name;
$scope.studentData.surname = data.surname;
$scope.studentData.birthdate = data.birthdate;
$scope.studentData.country = data.country;
$scope.studentData.pic = data.pic;
$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;
$scope.studentData.current_instruction = data.current_instruction;
// Setup section: Fill formUser (data able to be modified) from studentData parent object
// It must go here to assign the values when studentData is recovered
$scope.formUser.name = $scope.studentData.name;
$scope.formUser.username = $scope.studentData.username;
$scope.formUser.surname = $scope.studentData.surname;
$scope.formUser.birthdate = $scope.studentData.birthdate;
$scope.formUser.country = $scope.studentData.country;
$scope.formUser.pic = $scope.studentData.pic;
$scope.formUser.gender = $scope.studentData.gender;
$scope.formUser.lang = $scope.studentData.lang;
$scope.formUser.notes = $scope.studentData.notes;
console.log("Student personal data recovered");
console.log(JSON.stringify($scope.studentData));
console.log("Attributes: " + JSON.stringify($scope.studentData.attributes));
console.log("Sups: " + JSON.stringify($scope.studentData.stuSup));
//////////////////////////////////////////////////////////
// Subscribe to student's socket room
console.log("connecting to socket");
io.socket.post('/stu/subscribe', {
action: 'subscribe',
attributes:{
id_stu: $scope.studentData.id
},
token: $window.sessionStorage.token
},
function(res) {
console.log("Connected to student: " + res.msg);
});
//////////////////////////////////////////////////////////
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
$http.get(config.backend + '/stu/' + $scope.studentData.id)
.success(function (data) {
$scope.studentData.id = data.id;
$scope.studentData.username = data.username;
$scope.studentData.name = data.name;
$scope.studentData.surname = data.surname;
$scope.studentData.birthdate = data.birthdate;
$scope.studentData.country = data.country;
$scope.studentData.pic = data.pic;
$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;
$scope.studentData.current_instruction = data.current_instruction;
// Setup section: Fill formUser (data able to be modified) from studentData parent object
// It must go here to assign the values when studentData is recovered
$scope.formUser.name = $scope.studentData.name;
$scope.formUser.username = $scope.studentData.username;
$scope.formUser.surname = $scope.studentData.surname;
$scope.formUser.birthdate = $scope.studentData.birthdate;
$scope.formUser.country = $scope.studentData.country;
$scope.formUser.pic = $scope.studentData.pic;
$scope.formUser.gender = $scope.studentData.gender;
$scope.formUser.lang = $scope.studentData.lang;
$scope.formUser.notes = $scope.studentData.notes;
// Subscribe to student's socket room
io.socket.post('/stu/subscribe', {
action: 'subscribe',
attributes: {
id_stu: $scope.studentData.id
},
token: $window.sessionStorage.token
},
function () {
// TODO ngToast res.msg (connected to student room)
});
})
.error(function () {
// TODO ngToast res.msg (cant connect to student room)
});
// For tab navigation, initially blank goes to collections
// Defined as JSON object to be available in in children as the same scope
$scope.nav = {
tab : 'collections'
};
// ----------------------------------------------------------------------
// COLLECTION
// Load student collection
//
tab: 'collections'
};
// List of all student pictos
$scope.studentPictos = [];
$scope.studentPictos = [];
// List of categories pictos
$scope.pictosCategory = [];
// List of pictos from a catgory
$scope.pictosFromCategory = [];
// The view of pictos: All | Categories
$scope.pictos = {
$scope.pictos = {
category: null,
idCat: null
};
// Initialization of PCB (the tablet view) and PCB-Categories
$scope.pcb_rows = 5, $scope.pcb_cols = 10;
$scope.pcb_rows = 5;
$scope.pcb_cols = 10;
$scope.pcb = [];
$scope.pcb_cat = [];
$scope.pcb = new Array(); $scope.pcb_cat = new Array();
// Set the empty elements
for (var i=0;i<$scope.pcb_rows;i++) {
$scope.pcb[i]=new Array(); $scope.pcb_cat[i]=new Array();
for (var j=0;j<$scope.pcb_cols; j++) {
// Default value
$scope.pcb[i][j]={'picto': {'uri': '/app/img/empty.gif'}, 'attributes': {'coord_x': i, 'coord_y': j}};
$scope.pcb_cat[i][j]={'picto': {'uri': '/app/img/empty.gif'}, 'attributes': {'coord_x': i, 'coord_y': j}};
for (i = 0; i < $scope.pcb_rows; i++) {
$scope.pcb[i] = [];
$scope.pcb_cat[i] = [];
for (j = 0; j < $scope.pcb_cols; j++) {
$scope.pcb[i][j] = {
picto: { uri: '/app/img/empty.gif' },
attributes: { coord_x: i, coord_y: j }
};
$scope.pcb_cat[i][j] = {
picto: { uri: '/app/img/empty.gif' },
attributes: { coord_x: i, coord_y: j }
};
}
}
$http
.get(config.backend+'/stu/'+ $scope.studentData.id +'/pictos')
.success(function(data, status, headers, config) {
// Add to list
$scope.studentPictos = data;
//console.log(JSON.stringify($scope.pcb_cat));
$scope.pictosCategory = $filter('filter')($scope.studentPictos, {attributes: { id_cat: null }});
console.log("Pictos Categories: " + JSON.stringify($scope.pictosCategory));
// Build the pcb_cat with the coords of the pictos
for(var i=0; i < $scope.pictosCategory.length; i++) {
var pic = $scope.pictosCategory[i];
$scope.pcb_cat[pic.attributes.coord_x][pic.attributes.coord_y] = pic;
console.log("Pic " + pic.id + ": " + pic.attributes.color );
}
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
}); // Filtered only the categories pictos
////////////////////////////////////////////////////////////////////////////
// Devices /////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/*
// For mark in list the actual device (Received from PCB via websocket)
$scope.active_device = null;
// 1) Leer los dispositivos del alumno y mostrar en listado
// Array with student's devices
$scope.devices = [];
// Query to obtain an array of student devices
$http
.get(config.backend+'/stu/'+ $scope.studentData.id +'/devices')
.success(function(data, status, headers, config) {
// Add to list
$scope.devices = data;
console.log(JSON.stringify($scope.devices));
console.log("Devices recovered");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
*/
// ----------------------------------------------------------------------
// REPORTS
// Load information for reports
//
//
// NOTE: this information is created when the tab is selected, so information
// is always updated
$http.get(config.backend + '/stu/' + $scope.studentData.id + '/pictos')
.success(function (data) {
var k;
var pic;
// ----------------------------------------------------------------------
// SETUP
// Load student configuration
//
$scope.studentPictos = data;
$scope.pictosCategory = $filter('filter')($scope.studentPictos, {
attributes: {
id_cat: null
}
});
// Build the pcb_cat with the coords of the pictos
for (k = 0; k < $scope.pictosCategory.length; k++) {
pic = $scope.pictosCategory[k];
$scope.pcb_cat[pic.attributes.coord_x][pic.attributes.coord_y] = pic;
}
})
.error(function () {
$translate('error_loading_pictos').then(function (translation) {
ngToast.danger({ content: translation });
});
});
// *******************************************************
// Therapists
// List of student's therapists
$scope.studentSupervisors = [];
$http.get(config.backend + '/stu/' + $scope.studentData.id + '/therapists')
.success(function (data) {
$scope.studentSupervisors = data;
})
.error(function () {
// TODO show error with ngToast
});
$http
.get(config.backend+'/stu/'+ $scope.studentData.id +'/therapists')
.success(function(data, status, headers, config) {
// Add to list
$scope.studentSupervisors = data;
console.log(JSON.stringify($scope.studentSupervisors));
console.log("Supervisors listed");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
// List of student's tutors (parents)
$scope.studentTutors = [];
$http
.get(config.backend+'/stu/'+ $scope.studentData.id +'/tutors')
.success(function(data, status, headers, config) {
// Add to list
$scope.studentTutors = data;
console.log(JSON.stringify($scope.studentTutors));
console.log("Tutors listed");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
// *******************************************************
// Devices
// List of student's devices
$http.get(config.backend + '/stu/' + $scope.studentData.id + '/tutors')
.success(function (data) {
$scope.studentTutors = data;
})
.error(function () {
// TODO show error with ngToast
});
$scope.studentDevices = [];
$http
.get(config.backend+'/stu/'+ $scope.studentData.id +'/devices')
.success(function(data, status, headers, config) {
// Add to list
$scope.studentDevices = data;
console.log("Devices listed: ");
console.log(JSON.stringify($scope.studentDevices));
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
/*
// Events handling
io.socket.on('action', function(data) {
console.log('"action" event received with the following data:');
console.log(data.msg);
});*/
$http.get(config.backend + '/stu/' + $scope.studentData.id + '/devices')
.success(function (data) {
$scope.studentDevices = data;
})
.error(function () {
// TODO show error with ngToast
});
});
'use strict';
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
/* global dashboardControllers */
dashboardControllers.controller('TagsCtrl', function ($scope, $modalInstance, $http, config, stuPicto, sup) {
// Last parameter passed from collections.js
// Basic data passed from the main window
'use strict';
/**
* TagsCtrl
*/
dashboardControllers.controller('TagsCtrl', function TagsCtrl(
$scope,
$modalInstance,
$http,
config,
stuPicto,
sup,
$translate,
ngToast) {
// Supervisor
$scope.sup = sup;
// List of general tags
......@@ -15,66 +22,62 @@ dashboardControllers.controller('TagsCtrl', function ($scope, $modalInstance, $h
$scope.ownTags = [];
// Request of general tags
$http
.get(config.backend+'/picto/' + stuPicto.picto.id)
.success(function(data, status, headers, config) {
// Add to lists
$scope.tags = data.tags;
$scope.ownTags = data.tagsSup;
console.log("Tags recovered");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
$http.get(config.backend + '/picto/' + stuPicto.picto.id)
.success(function (data) {
$scope.tags = data.tags;
$scope.ownTags = data.tagsSup;
})
.error(function () {
$translate('error_loading_pictos').then(function (translation) {
ngToast.danger({ content: translation });
});
});
// Add own tag
$scope.add = function () {
$http
.post(config.backend+'/picto/tag',
{ 'picto': stuPicto.picto.id,
'supervisor': $scope.sup.id,
'tag': $scope.tagToAdd,
'lang': $scope.sup.lang
})
.success(function(data, status, headers, config) {
console.log("Tag added: " + JSON.stringify(data));
$scope.ownTags.push(data);
$scope.tagToAdd = "";
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
$http.post(config.backend + '/picto/tag',
{
picto: stuPicto.picto.id,
supervisor: $scope.sup.id,
tag: $scope.tagToAdd,
lang: $scope.sup.lang
}
)
.success(function (data) {
$scope.ownTags.push(data);
$scope.tagToAdd = '';
})
.error(function () {
// TODO show error with ngToast
});
};
// Destroy own tag
$scope.del = function (tag) {
$http
.delete(config.backend+'/picto/tag/' + tag.id)
.success(function(data, status, headers, config) {
console.log("Tag destroyed: " + JSON.stringify(data));
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.ownTags.length; i++) {
if(tag.id == $scope.ownTags[i].id)
$scope.ownTags.splice(i,1);
$http.delete(config.backend + '/picto/tag/' + tag.id)
.success(function () {
var i;
// TODO show success with ngToast
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for (i = 0; i < $scope.ownTags.length; i++) {
if (tag.id === $scope.ownTags[i].id) {
$scope.ownTags.splice(i, 1);
}
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
}
})
.error(function () {
// TODO show error with ngToast
});
};
$scope.close = function () {
$modalInstance.close("Ejemplo de elemento devuelto");
// TODO ¿?¿?
$modalInstance.close('Ejemplo de elemento devuelto');
};
// No usado
$scope.cancel = function () {
// TODO ¿?¿?
$modalInstance.dismiss('cancel');
};
});
<toast></toast>
<!-- Instrucction tab-->
<div class="panel panel-default student_tab_panel">
......
<div class="panel panel-default student_tab_panel">
<div class="panel-body">
<!-- Alert and success messages -->
<div ng-show="{{ 'showmessagestudent' }}" class="alert" ng-class="alertstudent">{{ messagestudent | translate }}</div>
<div class="panel-body">
<div class="row">
<!-- Parte izquierda: Datos personales -->
<div class="col-md-6">
......@@ -19,7 +15,7 @@
</div>
<!-- Preview -->
<img class="thumbnail preview" ng-src="{{studentData.pic}}" />
<img class="thumbnail preview" ng-src="{{studentData.pic}}" />
<!-- Fin Cambiar imagen de perfil -->
<!-- Datos personales del alumno -->
......@@ -100,7 +96,7 @@
<!-- Supervisores (terapeutas) del alumno -->
<div id="student_sups">
<h4 translate>supervisors</h4>
<!-- Buscador de supervisores -->
<!-- Buscador de supervisores -->
<p>
<form role="search" ng-submit="search_sup()">
<div class="input-group">
......@@ -117,11 +113,9 @@
<!-- Alert and success messages for supervisor found -->
<div ng-show="{{ 'showmessagesupfound' }}" class="alert alert-info">
<!-- Imagen de perfil del tutor -->
<img ng-src="{{supToAdd.pic}}" class="profile" alt="" title="" /> {{ supToAdd.name }} {{ supToAdd.surname }}
<img ng-src="{{supToAdd.pic}}" class="profile" alt="" title="" /> {{ supToAdd.name }} {{ supToAdd.surname }}
<a class="btn btn-default btn-lg pull-right" role="button" ng-click="add_sup()" translate>add</a>
</div>
<div ng-show="{{ 'showmessagesupnotfound' }}" class="alert alert-warning">{{ messagesup | translate }}</div>
<!-- Fin de buscador de supervisores -->
<!-- Supervisores asignados -->
......@@ -135,14 +129,14 @@
</a>
</li>
</ul>
<!-- Fin de Supervisores asignados -->
<!-- Fin de Supervisores asignados -->
</div>
<!-- Fin de id student-sups -->
<!-- Tutores (Padres) -->
<div id="student_tutors">
<h4>{{ 'tutors' | translate }}</h4>
<!-- Buscador de tutores -->
<!-- Buscador de tutores -->
<p>
<form role="search" ng-submit="search_tutor()">
<div class="input-group">
......@@ -159,11 +153,9 @@
<!-- Alert and success messages for tutor found -->
<div ng-show="{{ 'showmessagetutorfound' }}" class="alert alert-info">
<!-- Imagen de perfil del tutor -->
<img ng-src="{{tutorToAdd.pic}}" class="profile" alt="" title="" /> {{ tutorToAdd.name }} {{ tutorToAdd.surname }}
<img ng-src="{{tutorToAdd.pic}}" class="profile" alt="" title="" /> {{ tutorToAdd.name }} {{ tutorToAdd.surname }}
<a class="btn btn-default btn-sm pull-right" role="button" ng-click="add_tutor()" translate>add</a>
</div>
<div ng-show="{{ 'showmessagetutornotfound' }}" class="alert alert-warning">{{ messagetutor | translate }}</div>
<!-- Fin de buscador de tutores -->
<!-- Tutores asignados -->
......@@ -177,7 +169,7 @@
</a>
</li>
</ul>
<!-- Fin de Tutores asignados -->
<!-- Fin de Tutores asignados -->
<!-- Info message -->
<div class="alert alert-info">{{ 'supervisor_note' | translate }}</div>
......@@ -187,7 +179,7 @@
<!-- Student devices -->
<div id="student_devices">
<h4 translate>devices</h4>
<!-- Formulario para introducir ID de dispositivo -->
<!-- Formulario para introducir ID de dispositivo -->
<p>
<form role="search" ng-submit="associate_dev()">
<div class="input-group">
......@@ -208,17 +200,17 @@
<!-- Dispositivos asignados -->
<ul class="list-group" id="user_devices">
<li class="list-group-item" ng-repeat="dev in studentDevices">
<span class="glyphicon glyphicon-phone" aria-hidden="true"></span> {{dev.desc}}
<span class="glyphicon glyphicon-phone" aria-hidden="true"></span> {{dev.desc}}
<!-- aquí mejor stusupdev.id -->
<a ng-click="delete_device(dev.id)" class="delete_device" title="{{ 'unlink' | translate }}">
<span class="color_red glyphicon glyphicon-remove-circle" aria-hidden="true"></span>
</a>
</li>
</ul>
<!-- Fin de Dispositivos asignados -->
<!-- Fin de Dispositivos asignados -->
</div>
<!-- Fin de id student-devices -->
</div>
</div>
</div>
<!-- Fin de row -->
......@@ -239,9 +231,9 @@
<legend translate>pictograms</legend>
<div class="input-group">
<label>{{ 'background' | translate }}:
<label>{{ 'background' | translate }}:
<input id="picto_background" type="color" ng-model="studentData.attributes.picto_background" ng-mouseleave="update_attributes()" />
</label>
</label>
</div>
<h4>{{ 'size' | translate }}:</h4>
......@@ -293,9 +285,9 @@
<legend translate>phrase_tape</legend>
<div class="input-group">
<label>{{ 'background' | translate }}:
<label>{{ 'background' | translate }}:
<input id="phrase_background" type="color" ng-model="studentData.attributes.phrase_background" ng-mouseleave="update_attributes()" />
</label>
</label>
</div>
<h4>{{ 'place_pictos' | translate }}:</h4>
......@@ -306,7 +298,7 @@
<label class="btn btn-success" ng-model="studentData.attributes.input_selection" btn-radio="'press'" ng-change="update_attributes()">{{ 'press' | translate }}</label>
-->
<label class="btn btn-success" ng-model="studentData.attributes.input_selection" btn-radio="'drag'" ng-change="update_attributes()">{{ 'drag' | translate }}</label>
</div>
</div>
<h4>{{ 'feedback_picto' | translate }}:</h4>
<div class="btn-group">
......@@ -315,7 +307,7 @@
<label class="btn btn-success" ng-model="studentData.attributes.input_feedback.read" btn-checkbox ng-change="update_attributes()">{{ 'read_picto' | translate }}</label>
<label class="btn btn-success" ng-model="studentData.attributes.input_feedback.highlight" btn-checkbox ng-change="update_attributes()">{{ 'highlight' | translate }}</label>
</div>
</fieldset>
<!-- Ajustes de voz -->
......@@ -349,6 +341,6 @@
<!-- Fin de Configuración de los dispositivos -->
</div>
<!-- Fin de panel body -->
</div>
<!-- Fin de panel -->
\ No newline at end of file
<!-- Fin de panel -->
/* global dashboardControllers */
'use strict';
//------------------
// Setup Controller
//------------------
dashboardControllers.controller('SetupCtrl', function SetupCtrl($scope, $http, $window, $translate, $location, $upload, config) {
// The last parameter, config, is injected from config.js (defined in dashboardConfig module)
/**
* SetupCtrl
*/
dashboardControllers.controller('SetupCtrl', function SetupCtrl(
$scope,
$http,
$window,
$translate,
$location,
$upload,
ngToast,
config) {
var user;
// Don't show the message at the begining
$scope.showmessage = false;
// Restore user values from sessionStorage
var user = JSON.parse($window.sessionStorage.user);
// Fill the form with the user data
user = JSON.parse($window.sessionStorage.user);
$scope.formdata = user;
// Testing --> DELETE
console.log("User id: " + user.id.toString());
console.log("User id to hashcode: " + user.id.toString().hashCode());
// When a picture is selected, launch the request
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
// Check accepted file types
if(file.type == "image/jpeg" || file.type == "image/png" || file.type == "image/gif"){
// Get the file extension
var extension = file.name.split('.').pop();
var filename = user.id + file.name;
console.log("Archivo: " + filename);
$scope.onFileSelect = function ($files) {
var i;
var file;
var extension;
var filename;
for (i = 0; i < $files.length; i++) {
file = $files[i]; // { name, size, type }
if (file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif') {
extension = file.name.split('.').pop();
filename = user.id + file.name;
$upload.upload({
url: '/sup/upload', //upload.php script, node.js route, or servlet url
method: 'POST', // or 'PUT',
// Fields to sent in the body of the request
fields: {'filename': filename, 'extension': extension, 'folder': 'supervisors', 'id': user.id},
// or ['1.jpg', '2.jpg', ...] // to modify the name of the file(s)
file: file, // or list of files ($files) for html5 only
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(JSON.stringify(data.file));
// Assign the new image with new name
$scope.user.pic = "/upload/supervisors/" + data.file.name;
//$scope.picture = user.id + "." + extension + "?t=" + new Date().getTime();
// Modify the user in session
var user = JSON.parse($window.sessionStorage.user);
url: '/sup/upload', // upload.php script, node.js route, or servlet url
method: 'POST', // or 'PUT',
fields: {
filename: filename,
extension: extension,
folder: 'supervisors',
id: user.id
},
file: file
})
.progress(function () {
// TODO show file upload progress to user
})
.success(function (data) {
// TODO show success ngToast
$scope.user.pic = '/upload/supervisors/' + data.file.name;
user.pic = $scope.user.pic;
$window.sessionStorage.user = JSON.stringify(user);
});
}else{
alert("It's only supported JPG, PNG and GIF files");
} else {
$translate('error_only_support_images').then(function (translation) {
ngToast.error({ content: translation });
});
}
}
};
// Form submit
$scope.setup = function () {
var supervisor;
var actualEmail;
//$scope.showmessage = false;
// Validate password match
if($scope.formdata.password != $scope.formdata.password_confirm){
// ToDo: Highlight with Angular the input
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "password_match";
if ($scope.formdata.password !== $scope.formdata.password_confirm) {
$translate('password_match').then(function (translation) {
ngToast.danger({ content: translation });
});
return;
}
if($scope.setupForm.$valid) {
// $scope.showdialog = true;
console.log('Form is valid');
}else{
if (!$scope.setupForm.$valid) {
$translate('invalid_fields').then(function (translation) {
ngToast.danger({ content: translation });
});
return;
}
var supervisor = $scope.formdata;
var actual_email = JSON.parse($window.sessionStorage.user).email;
supervisor = $scope.formdata;
actualEmail = JSON.parse($window.sessionStorage.user).email;
// Comprobation for new email
// If no change, it is deleted from supervisor object
if(supervisor.email == actual_email)
if (supervisor.email === actualEmail) {
delete supervisor.email;
// console.log($scope.formdata.uploadFile);
}
$http
.put(config.backend+'/sup', supervisor)
.success(function(data, status, headers, config) {
// ToDo: When this run well, redirect to main page
$scope.showmessage = true;
$scope.alert = "alert-success";
$scope.message = "data_saved";
$http.put(config.backend + '/sup', supervisor)
.success(function (data) {
$translate('data_saved').then(function (translation) {
ngToast.success({ content: translation });
});
// Modify the name in the header
$scope.name = data.name + " " + data.surname;
$scope.name = data.name + ' ' + data.surname;
// Modify the user data in session
user.name = data.name;
user.surname = data.surname;
......@@ -113,16 +114,11 @@ dashboardControllers.controller('SetupCtrl', function SetupCtrl($scope, $http, $
delete user.password_confirm;
$window.sessionStorage.user = JSON.stringify(user);
console.log("User modified");
})
.error(function(data, status, headers, config) {
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "data_no_saved";
console.log("Error from API: " + data.error);
.error(function () {
$translate('data_no_saved').then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
});
\ No newline at end of file
});
'use strict';
/* global dashboardControllers, io */
//------------------
// Students Controller
//------------------
dashboardControllers.controller('StudentsCtrl', function StudentsCtrl($scope, $state, $http, config, $window, $translate, ngToast) {
'use strict';
/**
* StudentsCtrl
*/
dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
$scope,
$state,
$http,
config,
$window,
$translate,
ngToast,
IOService) {
$scope.formdatastudent = {
"username": '',
"password": '',
"name": '',
"surname": '',
"birthdate": '',
"country": '',
"gender": 'F',
"lang": 'es-es',
"notes": '',
"pic": "/app/img/default.jpg",
"office": $scope.user.office || {name: ""}
username: '',
password: '',
name: '',
surname: '',
birthdate: '',
country: '',
gender: 'F',
lang: 'es-es',
notes: '',
pic: '/app/img/default.jpg',
office: $scope.user.office || { name: '' }
};
//
// Flags for showing buttons according to role
//
$scope.user = JSON.parse($window.sessionStorage.user);
if ($scope.user.office) {
if ($scope.user.office.admin === $scope.user.id) // The user is administrator of the office
if ($scope.user.office.admin === $scope.user.id) {
$scope.user.isAdmin = true;
}
if ($scope.user.office.currentStudents >= $scope.user.office.maxStudents)
if ($scope.user.office.currentStudents >= $scope.user.office.maxStudents) {
$scope.num_licenses_left = 0;
else
$scope.num_licenses_left = $scope.user.office.maxStudents - $scope.user.office.currentStudents;
} else {
$scope.num_licenses_left =
$scope.user.office.maxStudents -
$scope.user.office.currentStudents;
}
} else {
$scope.user.office = {name: ""}
$scope.user.office = { name: '' };
}
//
// Get list of supervisor's students
//
$http
.get(config.backend+'/sup/' + $scope.user.id + '/students')
.success(function(data, status, headers, config) {
// Add to list
$scope.students = data;
console.log("Students listed:" + JSON.stringify(data));
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
$http.get(config.backend + '/sup/' + $scope.user.id + '/students')
.success(function (data) {
$scope.students = data;
})
.error(function () {
// TODO show error with ngToast
});
// Reset form Student
$scope.resetForm = function(){
$scope.resetForm = function () {
// Empty the form
$scope.formdatastudent = {
"username": '',
"password": '',
"password_confirm": '',
"name": '',
"surname": '',
"birthdate": '',
"country": '',
"gender": 'F',
"lang": 'es-es',
"notes": '',
"pic": "/app/img/default.jpg",
"office": $scope.user.office || {name: ""},
"current_method": "no_method",
"current_instruction": "no_instruction"
username: '',
password: '',
password_confirm: '',
name: '',
surname: '',
birthdate: '',
country: '',
gender: 'F',
lang: 'es-es',
notes: '',
pic: '/app/img/default.jpg',
office: $scope.user.office || { name: '' },
current_method: 'no_method',
current_instruction: 'no_instruction'
};
// Hide the form
......@@ -75,41 +81,31 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl($scope, $s
};
// Add Student
$scope.add_student = function(){
$scope.add_student = function () {
var student = $scope.formdatastudent;
console.log(JSON.stringify(student));
// Validate password match
if(student.password_confirm.length && student.password != student.password_confirm) {
// ToDo: Highlight with Angular the input
$scope.showmessage = true;
$scope.alert = "alert-danger";
$scope.message = "password_match";
if (student.password_confirm.length && student.password !== student.password_confirm) {
$translate('password_match').then(function (translation) {
ngToast.danger({ content: translation });
});
return;
}
if (!student.password_confirm.length) { // password not changed (don't send it to DB)
// password not changed (don't send it to DB)
if (!student.password_confirm.length) {
delete student.password;
delete student.password_confirm;
}
$http
.post(config.backend+'/stu', student)
.success(function(data, status, headers, config) {
// Translate
$http.post(config.backend + '/stu', student)
.success(function (data) {
$translate('student_added').then(function (translation) {
// Show message
ngToast.success({
content: translation,
timeout: 6000 // By default 4000
});
});
ngToast.success({ content: translation });
});
// default values
data.student.supervision = 0 // by default, only related to office administrator
data.student.supervision = 0; // by default, only related to office administrator
data.student.current_method = 'no_method';
data.student.current_instruction = 'no_instruction';
......@@ -121,106 +117,80 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl($scope, $s
$scope.resetForm();
// Show the add form to new adding
$scope.hidestudentadd = false;
console.log("Student added");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + JSON.stringify(data));
var msg = "";
// Custom message for a generic error or for the maximum number of licenses reached by the office
if(data && data.raw && data.raw.sqlState && data.raw.sqlState == "20001") msg = "max_licenses_reached";
else if (data && data.status == 400)
msg = "invalid_fields";
else
msg = "student_not_added";
.error(function (data) {
var errorMessage = 'student_not_added';
// Translate
$translate(msg).then(function (translation) {
// Show message
ngToast.danger({
content: translation,
timeout: 6000 // By default 4000
});
});
if (data && data.raw && data.raw.sqlState && data.raw.sqlState === '20001') {
errorMessage = 'max_licenses_reached';
} else if (data && data.status === 400) {
errorMessage = 'invalid_fields';
}
$translate(errorMessage).then(function (translation) {
ngToast.danger({ content: translation });
});
});
};
// Delete Student
$scope.delete_student = function(student){
$scope.delete_student = function (student) {
var deleteStudent = $window.confirm('Are you absolutely sure you want to delete?');
if(deleteStudent){
$http
.delete(config.backend+'/stu/'+ student.id)
.success(function(data, status, headers, config) {
console.log("Student deleted:" + student.name);
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.students.length; i++) {
if(student.id == $scope.students[i].id)
$scope.students.splice(i,1);
}
console.log("Student deleted:" + student.name);
// Translate
if (deleteStudent) {
$http.delete(config.backend + '/stu/' + student.id)
.success(function () {
var i;
for (i = 0; i < $scope.students.length; i++) {
if (student.id === $scope.students[i].id) {
$scope.students.splice(i, 1);
}
}
$translate('student_deleted').then(function (translation) {
// Show message
ngToast.success({
content: translation,
timeout: 6000 // By default 4000
});
});
// Empty the form
//$scope.resetForm();
ngToast.success({ content: translation });
});
})
.error(function(data, status, headers, config) {
console.log("Error deleting student from API: " + data.error);
//console.log("It could be students associated");
// Translate
.error(function () {
$translate('student_not_deleted').then(function (translation) {
// Show message
ngToast.danger({
content: translation,
timeout: 6000 // By default 4000
});
});
ngToast.danger({ content: translation });
});
});
//
// Due to a bug with http.delete, callback functions above are not
// called. So we do it again here until the bug is fixed.
//
// for(var i=0; i < $scope.students.length; i++) {
// if(student.id == $scope.students[i].id)
// $scope.students.splice(i,1);
// }
}
};
//////////////////////////////////////////////////////////
// Unsubscribe to student's socket room
io.socket.post('/stu/unsubscribe', {
action: 'unsubscribe'
},
function(res) {
console.log("Disconnected socket: " + res.msg);
});
//////////////////////////////////////////////////////////
// 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();
}
});
}
});
});
//----------------------------------------------
// Student Add Controller --> Only for calendar
//----------------------------------------------
/**
* StudentAddCtrl
*/
dashboardControllers.controller('StudentAddCtrl', function StudentsCtrl($scope) {
// Open calendar
$scope.open_calendar = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.opened_cal_student = true;
};
});
\ No newline at end of file
$scope.open_calendar = function ($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.opened_cal_student = true;
};
});
/* 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);
// Assign values this way (like an object) to ensure it's the parent scope
$scope.user.id = user.id;
$scope.user.name = user.name;
......@@ -22,4 +24,11 @@ dashboardControllers.controller('SupervisorCtrl', function SupervisorCtrl($scope
$location.path('/setup');
};
});
\ No newline at end of file
// Subscribe to the supervisor's room
IOService.post('/sup/subscribe', {
action: 'subscribe',
attributes: {
id_sup: $scope.user.id
}
});
});
......@@ -9,9 +9,6 @@
<!-- Formulario -->
<form name="setupForm" enctype="multipart/form-data" role="form" ng-submit="setup()">
<!-- Alert and success messages -->
<div ng-show="{{ 'showmessage' }}" class="alert" ng-class="alert" translate>{{message}}</div>
<!-- Change profile picture -->
<input type="file" ng-file-select="onFileSelect($files)" ng-model="picFile" accept="image/*">
......@@ -102,4 +99,3 @@
</div>
<toast></toast>
<!-- StudentsCtrl controls here, see app.js -->
<div class="panel panel-default">
<!-- Default panel contents -->
......
......@@ -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,
......
/**
* Route Mappings
* (sails.config.routes)
*
*
* Your routes map URLs to views and controllers.
*
* If Sails receives a URL that doesn't match any of the routes below,
......@@ -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',
......@@ -65,7 +58,7 @@ module.exports.routes = {
'get /office/get/:id': 'OfficeController.get',
'get /office/get_all': 'OfficeController.getAll',
'get /office/get/:id/supervisors': 'OfficeController.supervisors',
/////////////////////////////////////////////////////////////////////////
// STUDENTS /////////////////////////////////////////////////////////////
// get /student --> List all students
......@@ -127,16 +120,16 @@ module.exports.routes = {
// (check them by inspecting the policies.js file)
// Reports
'get /ws/:id_stu/year/:year': 'WorkingSessionController.per_year',
'get /ws/:id_stu/month/:month': 'WorkingSessionController.per_month',
// Working Sessions > Tries
'get /ws/:id_ws/tries': 'WorkingSession.tries',
'post /workingsession/:id_ws/close': 'WorkingSession.close',
'get /ws/:id_ws/tries': 'WorkingSession.tries',
'post /workingsession/:id_ws/close': 'WorkingSession.close',
'put /try/:id': 'Try.update',
'post /try': 'Try.create',
// Instructions > Working Sessions
'get /instruction/:id_ins/ws': 'Instruction.ws',
//'get /instruction/:id_ins/tries': 'Instruction.tries', // BORRAR aquí y en wiki
......
......@@ -14,7 +14,7 @@
"lodash": "^3.10.1",
"mocha": "^2.3.4",
"rc": "~0.5.0",
"sails": "^0.11.3",
"sails": "^0.12.3",
"sails-disk": "~0.10.0",
"sails-generate-auth": "^0.2.0",
"sails-mysql": "^0.10.12",
......@@ -35,6 +35,10 @@
"author": "emblanco, amontejo",
"license": "",
"devDependencies": {
"eslint": "^2.8.0",
"eslint-config-airbnb": "^7.0.0",
"eslint-plugin-jsx-a11y": "^1.0.1",
"eslint-plugin-react": "^5.0.1",
"node-inspector": "^0.12.8"
}
}
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