Commit 3964e2f8 by Jose Antonio

Merged branch develop into develop

parents b40073e3 d9faa166
Showing with 868 additions and 170 deletions
......@@ -15,6 +15,8 @@ sails/arasaac*
sails/arasaac
sails/src/assets/symbolstx*
sails/src/assets/upload
sails/src/assets/arasaac*
sails/src/assets/arasaac
sails/upload.tgz
# Webapp #
......@@ -24,12 +26,13 @@ sails/src/assets/app/bower_components
sails/src/assets/app/modules
sails/src/assets/app/css
sails/src/assets/app/js
sails/src/assets/scripts/config.js
sails/src/config/ssl/**/*.key
sails/src/config/ssl/**/*.crt
sails/src/config/ssl/**/*.csr
sails/src/config/ssl/**/*.pem
sails/src/config/local.js
sails/src/config/local.js
sails/src/assets/scripts/config.js
sails/src/node_modules
sails/src/certbot.log
sails/.vagrant
......
......@@ -178,6 +178,19 @@ public class Device extends SQLiteOpenHelper {
return user;
}
public Vector<User> recoverSupervisors(Integer id_stu) throws JSONException{
SQLiteDatabase db = this.getReadableDatabase();
Vector<User> users = new Vector<>();
Cursor cursor = db.query("users_detail", null, null, null, null, null, null, null);
while (cursor.moveToNext())
if (cursor.getInt(0) == id_stu)
users.add(new User(cursor.getInt(0), cursor.getString(1), cursor.getString(2), cursor.getString(3), cursor.getString(4), cursor.getString(5), cursor.getString(6), cursor.getString(7), cursor.getString(8),
cursor.getInt(9), cursor.getString(10), cursor.getString(11), cursor.getString(12), cursor.getString(13), cursor.getString(14), cursor.getString(15), cursor.getString(16), cursor.getString(17), cursor.getString(18)));
cursor.close();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
return users;
}
public Vector<User> recoverStudents(Integer id_sup) throws JSONException{
SQLiteDatabase db = this.getReadableDatabase();
Vector<User> users = new Vector<>();
......@@ -284,7 +297,14 @@ public class Device extends SQLiteOpenHelper {
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
}
public void deleteUser(User user) {
SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
db.execSQL("DELETE FROM users_detail WHERE id_sup = " + user.get_id_sup());
db.setTransactionSuccessful();
db.endTransaction();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
}
/**
* delete the list of images (students, supervisors, pictograms) which are no longer used
......
......@@ -52,7 +52,6 @@ public class Picto extends Img {
public static String LEGEND = "legend";
public static String URI_SOUND = "uri_sound";
public static String USER_AVATAR = "user_avatar";
public static String ASSOCIATED_PERSON = "associated_person";
}
......@@ -91,7 +90,7 @@ public class Picto extends Img {
Log.e(LOG_TAG,e.getMessage());
}
}
public Picto(int id, String url, String translation, int cat, int row, int column, int freeRow, int freeColumn, int stupicto_id, String uri_sound, String user_avatar, String associated_person) throws JSONException {
public Picto(int id, String url, String translation, int cat, int row, int column, int freeRow, int freeColumn, int stupicto_id, String uri_sound, String user_avatar) throws JSONException {
this(id, url, new JSONObject()
.put(JSON_ATTTRS.CATEGORY, cat)
.put(JSON_ATTTRS.COLUMN, column)
......@@ -104,7 +103,6 @@ public class Picto extends Img {
.put(JSON_ATTTRS.EXPRESSION,translation)
.put(JSON_ATTTRS.URI_SOUND,uri_sound)
.put(JSON_ATTTRS.USER_AVATAR,user_avatar)
.put(JSON_ATTTRS.ASSOCIATED_PERSON,associated_person)
);
}
public Picto(int id, String url,String translation, String attributes) throws JSONException {
......@@ -305,14 +303,14 @@ public class Picto extends Img {
*
* @return the associated person of a picto
*/
public String get_associated_person() {
String ass_person = null;
public String get_user_avatar() {
String associated_user = null;
try {
ass_person = this.attributes.getString(JSON_ATTTRS.ASSOCIATED_PERSON);
associated_user = this.attributes.getString(JSON_ATTTRS.USER_AVATAR);
} catch (JSONException e) {
e.printStackTrace();
}
return ass_person;
return associated_user;
}
/**
......@@ -377,6 +375,18 @@ public class Picto extends Img {
/**
*
* @return the uri of associated sound of the picto
*/
public void setUriSound(String path){
try {
this.attributes.put(JSON_ATTTRS.URI_SOUND, path);
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
*
* @return the associated user avatar of the picto
*/
public String getUserAvatar(){
......
......@@ -427,7 +427,7 @@ public class Vocabulary implements Iterable<Picto> {
/*
* It saves locally a new picto obtained from the PCB
*/
public void saveLocalPicto(String url, String exp, int cat, int coord_x, int coord_y, int free_category_coord_x, int free_category_coord_y,String uri_sound,String user_avatar,String associated_person, final iLocalPicto listener) {
public void saveLocalPicto(String url, String exp, int cat, int coord_x, int coord_y, int free_category_coord_x, int free_category_coord_y,String uri_sound,String user_avatar, final iLocalPicto listener) {
Picto prev_picto=PCBcontext.getPcbdb().getCurrentUser().has_categories() ? find_picto(cat, coord_x,coord_y) : find_picto(coord_x,coord_y); //¿estamos reemplazanddo un picto que ya existe?
......@@ -439,7 +439,7 @@ public class Vocabulary implements Iterable<Picto> {
int id=PCBcontext.getDevice().getNextLocalPictoID();
try {
final Picto picto = new Picto(id, url, exp, cat, coord_x, coord_y, free_category_coord_x, free_category_coord_y,prev_picto!=null ? prev_picto.get_stupicto_id() : Picto.STUPICTO_NULL,uri_sound,user_avatar,associated_person);
final Picto picto = new Picto(id, url, exp, cat, coord_x, coord_y, free_category_coord_x, free_category_coord_y,prev_picto!=null ? prev_picto.get_stupicto_id() : Picto.STUPICTO_NULL,uri_sound,user_avatar);
addPicto(picto, ImgDownloader.tsource.local, new ImgDownloader.iImgDownloaderListener() {
@Override
public void loadComplete() {
......
......@@ -9,6 +9,7 @@ import com.koushikdutta.ion.Response;
import com.yottacode.net.RestapiWrapper;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.action.VocabularyAction;
import com.yottacode.pictogram.dao.Device;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.pictogram.tools.PCBcontext;
......@@ -23,7 +24,7 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
......@@ -98,7 +99,66 @@ public class PictoUploader {
return success;
}
//TODO: Refinar el metodo
private boolean uploadSound(File audioFile) throws UnsupportedEncodingException {
boolean success;
Response<JsonObject> response=null;
String [] path = audioFile.getPath().split("\\.");
String extension = path[1];
if (extension != "mp3")
throw new UnsupportedEncodingException("Extension "+extension+" is not supported. Only mp3 files");
Ion ion = Ion.getDefault(PCBcontext.getContext());
try {
response=ion.with(PCBcontext.getContext())
.load("POST", PCBcontext.getContext().getResources().getString(R.string.server) + "/picto/upload_sound/"+picto.get_stupicto_id())
.setMultipartParameter("filename", "id_del _sonido")
.setMultipartParameter("extension", "mp3")
.setMultipartParameter("owner", Integer.toString(PCBcontext.getPcbdb().getCurrentUser().get_id_sup()))
.setMultipartParameter("folder", "pictoSound")
.setMultipartParameter("token", PCBcontext.getRestapiWrapper().getToken())
.setMultipartFile("file", "audio/mp3", audioFile)
.asJsonObject()
.withResponse().get();
if (response != null && response.getHeaders().code() == 200) {
Log.i(LOG_TAG, "Uploaded image result: " + response.getHeaders() + ":" + response.getResult());
//int img_id=response.getResult().get("id").getAsInt();
//String img_uri=response.getResult().get("uri").getAsString();
//img.set_url(img_uri);
//img.update_id(img_id);
success=true;
} else {
success=false;
Log.i(LOG_TAG, "Uploaded Sound failed ");
if (response != null)
Log.i(LOG_TAG, "Uploaded Sound failed, headers: " + response.getHeaders());
}
} catch (InterruptedException e) {
Log.e(LOG_TAG, "Sound upload error: " + e.getMessage()+ "Code: "+
(response == null ? -1 : response.getHeaders().code()));
success=false;
} catch (ExecutionException e) {
Log.e(LOG_TAG, "Sound upload error: " + e.getMessage()+
(response == null ? -1 : response.getHeaders().code()));
success=false;
} /*catch (IOException e) {
Log.e(Img.class.getCanonicalName(), "Error when decoding "+audioFile.getName());
GUITools.show_alert(PCBcontext.getContext(), R.string.imguserLoadingErrMsg);
success=false;
}*/
// ion.dump();
return success;
}
/**
* if a picto was included from the PCB, the translation is uploaded to the server
......@@ -121,6 +181,7 @@ public class PictoUploader {
e.printStackTrace();
Log.e(LOG_TAG, " Error: " + e.getLocalizedMessage());
}
PCBcontext.getRestapiWrapper().ask(PCBcontext.getPcbdb().getCurrentUser().get_restapi_operation_stu() + "/picto/"+id_picto, params, "post", true, new RestapiWrapper.iRestapiListener() {
@Override
public void preExecute() {
......@@ -238,15 +299,26 @@ public class PictoUploader {
(stupicto_id!=Picto.STUPICTO_NULL ? ", stupicto to be deleted:"+stupicto_id+")" : " .New picto"));
iPictoUploaderListener listener = new PictoUploaderListener(local_id,elements_to_be_uploaded);
if (imgUpload_success) {
if (imgUpload_success){
if(this.picto.getUriSound() != "" && this.picto.getUriSound() != null ){ //Si el picto tiene audio en local
Log.i("TAG_PRUEBAS","Hay uri: "+this.picto.getUriSound()+" -Procede a subir archivo");
File file = new File(picto.getUriSound()); //Obtengo el fichero de audio local
boolean soundUpload_success = uploadSound(file); //Llamo a la subida
if(soundUpload_success) {
Log.i("TAG_PRUEBAS","Se sube sonido hay EXITO");
}else {
GUITools.show_alert(PCBcontext.getActivityContext(),Integer.parseInt(R.string.upload_error+"Audio"), PictoUploader.this.picto.get_translation());
}
}
if (stupicto_id!=Picto.STUPICTO_NULL) {
Log.i(LOG_TAG, "Remote Picto to be deleted:"+this.picto.get_translation()+"("+stupicto_id+")");
deletePicto(stupicto_id,listener);
} else
} else {
uploadTranslation(picto.get_id(), listener);
}
else {
GUITools.show_alert(PCBcontext.getActivityContext(), R.string.upload_error, PictoUploader.this.picto.get_translation());
}
}else{
GUITools.show_alert(PCBcontext.getActivityContext(), Integer.parseInt(R.string.upload_error+"Imagen"), PictoUploader.this.picto.get_translation());
}
}
......
......@@ -1155,18 +1155,18 @@ protected void showOnlyTape(boolean onlyTape) {
int freeRow = edit ? data.getExtras().getInt(Picto.JSON_ATTTRS.FREE_ROW) : getIntent().getIntExtra(Picto.JSON_ATTTRS.FREE_ROW, -1);
int freeColumn = edit ? data.getExtras().getInt(Picto.JSON_ATTTRS.FREE_COLUMN) : getIntent().getIntExtra(Picto.JSON_ATTTRS.FREE_COLUMN, -1);
String uri_sound = edit ? data.getExtras().getString(Picto.JSON_ATTTRS.URI_SOUND) : getIntent().getStringExtra(Picto.JSON_ATTTRS.URI_SOUND);
String associated_person = edit ? data.getExtras().getString(Picto.JSON_ATTTRS.ASSOCIATED_PERSON) : getIntent().getStringExtra(Picto.JSON_ATTTRS.ASSOCIATED_PERSON);
String uri_sound = data.getExtras().getString(Picto.JSON_ATTTRS.URI_SOUND);
String user_avatar = data.getExtras().getString(Picto.JSON_ATTTRS.USER_AVATAR);
int cat = edit ? data.getIntExtra(Picto.JSON_ATTTRS.CATEGORY, -1) : getIntent().getIntExtra(Picto.JSON_ATTTRS.CATEGORY, -1);
String path = data.getExtras().getString(PictoMenu.PATH);
String legend = data.getExtras().getString(Picto.JSON_ATTTRS.EXPRESSION);
Log.i(EditPictoActivity.DEBUG_MESSAGE,"Antes de chooseText...DATOS--> ASS_PERSON: "+associated_person+"..URI_SOUND: "+uri_sound+"..PATH_IMAGE: "+path+"..EXPRESION: "+legend);
Log.i(EditPictoActivity.DEBUG_MESSAGE,"Antes de chooseText...DATOS--> ASS_PERSON: "+user_avatar+"..URI_SOUND: "+uri_sound+"..PATH_IMAGE: "+path+"..EXPRESION: "+legend);
//TODO: COGER URI DEL SONIDO,EL USER AVATAR Y LA PERSONA ASOCIADA AL PICTO
chooseTextAndSavePicto(path, row, col, freeRow, freeColumn, cat, legend, uri_sound, null, associated_person);
chooseTextAndSavePicto(path, row, col, freeRow, freeColumn, cat, legend, uri_sound,user_avatar);
refresh();
}
break;
......@@ -1178,7 +1178,7 @@ protected void showOnlyTape(boolean onlyTape) {
* 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, final int freeRow, final int freeColumn,
final int category, final String legend,final String uri_sound,final String user_avatar, String associated_person) {
final int category, final String legend,final String uri_sound,final String user_avatar) {
// Set up the buttons
int cat = category != -1 ? category : Picto.NO_CATEGORY;
......@@ -1194,7 +1194,6 @@ protected void showOnlyTape(boolean onlyTape) {
freeColumn,
uri_sound,
user_avatar,
associated_person,
new iLocalPicto() {
@Override
public void saved(Picto localPicto) {
......
......@@ -57,7 +57,9 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import pl.droidsonroids.gif.GifTextView;
import static java.lang.Thread.sleep;
......@@ -233,27 +235,39 @@ public class EditPictoActivity extends Activity {
ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION);
//Editar la lista desplegable de supervisores asociados
mDrawerList = (ListView)findViewById(R.id.navList);
/**Obtener la lista de supervisores y aplicarle formato*/
Log.i(DEBUG_MESSAGE,"Usuario: "+ PCBcontext.getPcbdb().getCurrentUser().get_name_stu());
String supervisors = PCBcontext.getPcbdb().getCurrentUser().get_Supervisors();
Log.i(DEBUG_MESSAGE,"Supervisores: "+ supervisors);
ArrayList<String> supervisores = new ArrayList<>();
ArrayList<String> supervisoresAdapter = new ArrayList<>();
Vector<User> supervisores = null;
try {
supervisores = PCBcontext.getDevice().recoverSupervisors(PCBcontext.getPcbdb().getCurrentUser().get_id_stu());
} catch (JSONException e) {
e.printStackTrace();
}
if (supervisores != null){
for(User supervisor: supervisores){
supervisoresAdapter.add(supervisor.get_name_sup()+", "+supervisor.get_surname_sup()+ "\n" +supervisor.get_email_sup() );
}
}
//String supervisors = PCBcontext.getPcbdb().getCurrentUser().get_Supervisors();
//Log.i(DEBUG_MESSAGE,"Supervisores: "+ supervisors);
//ArrayList<String> supervisores = new ArrayList<>();
if(supervisors != null) {
/*if(supervisors != null) {
String[] separated = supervisors.split(";");
for (String supervisor : separated) {
String[] detalles = supervisor.split(",");
supervisores.add(detalles[0] + "\n" + detalles[1]);
}
}else
Log.i(DEBUG_MESSAGE,"No tiene supervisores...");
Log.i(DEBUG_MESSAGE,"No tiene supervisores...");*/
mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, supervisores);
mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, supervisoresAdapter);
mDrawerList.setAdapter(mAdapter);
mDrawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
......@@ -304,7 +318,7 @@ public class EditPictoActivity extends Activity {
if(!fileAudio.exists()){
fileAudio.mkdirs();
}
previewAudioPath = dirAudioPath+File.separator+"test.3gp";
previewAudioPath = dirAudioPath+File.separator+"test.mp3";
// Initialize Views.
......@@ -352,7 +366,7 @@ public class EditPictoActivity extends Activity {
if(editar){
p = PCBcontext.getVocabulary().get_picto(getIntent().getExtras().getInt(Picto.JSON_ATTTRS.CATEGORY),getIntent().getExtras().getInt(PictoMenu.ID_PICTO_IMAGE));
legend.setText(p.get_translation());
supAsociado.setText(p.get_associated_person());
supAsociado.setText(p.get_user_avatar());
}
legend.setHorizontallyScrolling(false);
legend.setMaxLines(1);
......@@ -360,7 +374,7 @@ public class EditPictoActivity extends Activity {
//Obtener imagen del intent
byte[] byteArray = getIntent().getByteArrayExtra(PictoMenu.IMAGE_PICTO);
Bitmap imagePicto = scale_image(BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length));
final Bitmap imagePicto = scale_image(BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length));
cropImageView.setAspectRatio(editar ? imagePicto.getWidth() : 4,editar ? imagePicto.getHeight() : 3); //Si es editar un picto la ventana de recorte mantiene el tamaño de la imagen, sino 4:3
cropImageView.setImageBitmap(imagePicto);
......@@ -447,8 +461,8 @@ public class EditPictoActivity extends Activity {
if (file.exists()) {
File from = new File(previewAudioPath);
File to = new File(editar ? dirAudioPath + File.separator + legend.getText().toString() + "_" + id_pic + "_audio.3gp" :
dirAudioPath + File.separator + legend.getText().toString() + "_new_" + pathNumber + "_audio.3gp");
File to = new File(editar ? dirAudioPath + File.separator + legend.getText().toString() + "_" + id_pic + "_audio.mp3" :
dirAudioPath + File.separator + legend.getText().toString() + "_new_" + pathNumber + "_audio.mp3");
if (from.exists()){
from.renameTo(to);
Log.i(DEBUG_MESSAGE,to.getPath().toString());
......@@ -457,13 +471,16 @@ public class EditPictoActivity extends Activity {
}
}
Intent intent = getIntent(); //Mandar a pictogram activity el path y el texto de la imagen
intent.putExtra(PictoMenu.IS_EDIT, editar); //Para saber despues si estas editando o añadiendo nuevo y coger los datos de intent o de data en OnActivityResult
intent.putExtra(PictoMenu.PATH, filepath); //Mandar Path imagen
intent.putExtra(Picto.JSON_ATTTRS.URI_SOUND,audioPath); //Mandar el path del audio
intent.putExtra(Picto.JSON_ATTTRS.EXPRESSION, legend.getText().toString()); //Mandar expresion nueva
intent.putExtra(Picto.JSON_ATTTRS.CATEGORY, getIntent().getIntExtra(Picto.JSON_ATTTRS.CATEGORY, -1));
intent.putExtra(Picto.JSON_ATTTRS.ASSOCIATED_PERSON, supAsociado.getText());
intent.putExtra(Picto.JSON_ATTTRS.USER_AVATAR, supAsociado.getText());
//p.setUriSound(audioPath);
setResult(RESULT_OK, intent);
......@@ -613,7 +630,7 @@ public class EditPictoActivity extends Activity {
private void startRecording() {
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mRecorder.setOutputFile(previewAudioPath);
......
......@@ -203,7 +203,7 @@ public class StudentFragmentGrid extends Fragment{
downloader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imgs);
}
private void download_supervisors(int stu_id) {
private void download_supervisors(final int stu_id) {
String token = getActivity().getIntent().getExtras().getString("token");
RestapiWrapper wrapper = PCBcontext.getRestapiWrapper();
......@@ -225,17 +225,40 @@ public class StudentFragmentGrid extends Fragment{
}
@Override
public void result(JSONArray supervisors) {
String supervisorsFormat = "";
Vector<Integer> idSupervisoresJSON = new Vector<Integer>();
for (int i=0;i<supervisors.length();i++) {
JSONObject supervisor;
JSONObject supervisor = null;
try {
supervisor = supervisors.getJSONObject(i);
supervisorsFormat += supervisor.get("name") +" " + supervisor.get("surname") + "," + supervisor.get("email") + ";";
idSupervisoresJSON.add((Integer) supervisor.get("id"));
User user = PCBcontext.getDevice().findUser(stu_id,(int) supervisor.get("id")); //Para comprobar si ya existe en local
if(user==null) {
PCBcontext.getDevice().insertUser(new User(stu_id, PCBcontext.getPcbdb().getCurrentUser().get_nickname_stu(), PCBcontext.getPcbdb().getCurrentUser().get_pwd_stu()
, PCBcontext.getPcbdb().getCurrentUser().get_name_stu(), PCBcontext.getPcbdb().getCurrentUser().get_surname_stu(), PCBcontext.getPcbdb().getCurrentUser().get_url_img_stu()
, PCBcontext.getPcbdb().getCurrentUser().get_gender_stu(), PCBcontext.getPcbdb().getCurrentUser().get_lang_stu(), PCBcontext.getPcbdb().getCurrentUser().get_json_attrs(),
(int) supervisor.get("id"), supervisor.get("email").toString(), null, supervisor.get("name").toString(), supervisor.get("surname").toString(), supervisor.get("pic").toString(),
supervisor.get("gender").toString(), supervisor.get("lang").toString(), supervisor.get("ttsEngine").toString(), supervisor.get("office").toString()));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
PCBcontext.getPcbdb().getCurrentUser().set_Supervisors(supervisorsFormat);
try {
Vector<User> supervisorsLocal = PCBcontext.getDevice().recoverSupervisors(stu_id);
for(User user: supervisorsLocal){
if(!idSupervisoresJSON.contains(user.get_id_sup())){
PCBcontext.getDevice().deleteUser(user);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
//PCBcontext.getPcbdb().getCurrentUser().set_Supervisors(supervisorsFormat);
}
@Override
......
......@@ -62,13 +62,6 @@
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
......@@ -76,6 +69,13 @@
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotations" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
......@@ -121,17 +121,5 @@
<orderEntry type="library" exported="" name="ion-2.1.9" level="project" />
<orderEntry type="library" exported="" name="play-services-ads-lite-9.2.1" level="project" />
<orderEntry type="module" module-name="commonlibrary" exported="" />
<orderEntry type="library" exported="" name="android-android-24" level="project" />
<orderEntry type="library" exported="" name="okhttp-ws-2.3.0" level="project" />
<orderEntry type="library" exported="" name="socket.io-client-0.5.0" level="project" />
<orderEntry type="library" exported="" name="okhttp-2.3.0" level="project" />
<orderEntry type="library" exported="" name="support-annotations-23.0.0" level="project" />
<orderEntry type="library" exported="" name="okio-1.3.0" level="project" />
<orderEntry type="library" exported="" name="gson-2.3" level="project" />
<orderEntry type="library" exported="" name="engine.io-client-0.5.0" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-21.0.3" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="json-20090211" level="project" />
</component>
</module>
\ No newline at end of file
Estos son scripts de automatización para las siguientes tareas en el servidor:
- Renovación de certificados
- Backup automático
## Renovación de certificados
Ver [letsencrypt.md](http://gitlab.ujaen.es/yotta/pictogram/blob/master/sails/src/config/ssl/letsencrypt.md)
## Backup automático
### Dependencias:
- Se está usando el script [ec2-automate-backup](https://github.com/colinbjohnson/aws-missing-tools/tree/master/ec2-automate-backup).
- Es necesario, para su uso, instalar [AWS-CLI](http://docs.aws.amazon.com/cli/latest/userguide/installing.html)
### Configuración:
Desde AWS IAM, debemos crear un usuario dentro de un grupo. Los permisos son los siguientes:
```
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1489386668000",
"Effect": "Allow",
"Action": [
"ec2:CreateSnapshot",
"ec2:CreateTags",
"ec2:DeleteSnapshot",
"ec2:DeleteTags",
"ec2:DescribeSnapshotAttribute",
"ec2:DescribeSnapshots",
"ec2:DescribeVolumes",
"ec2:ModifySnapshotAttribute"
],
"Resource": "*"
}
]
}
```
El comando cron que hay de muestra en `crontab` se encargará de hacer una copia cada día, para los últimos 30 días.
\ No newline at end of file
#!/bin/bash
# This script is to be run in a remote machine with access to pre
DUMPFILE=/home/ubuntu/backup/`date +%d`-mysqldump.sql.gz
ssh pre "nice mysqldump -u root -p'r00...Tt' pictodb | gzip > $DUMPFILE"
scp pre:$DUMPFILE /home/amontejo/backup
#!/bin/bash -
# export PATH=/bin is required for cut, date, grep
# export PATH=/usr/bin is required for AWS Command Line Interface tools
export PATH=/bin:/usr/bin:/home/ubuntu/.local/bin
# AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY needed for AWS CLI tools
export AWS_ACCESS_KEY_ID=AKIAJJZNE3V5QMKSMU6A
export AWS_SECRET_ACCESS_KEY=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
# AWS_CONFIG_FILE required for AWS Command Line Interface tools (f.e. ".aws")
export AWS_CONFIG_FILE=/root/.aws/config
@reboot /home/ubuntu/pictogram/sails/bootstrap.sh
# Let's encrypt certs autorenew
@monthly /home/ubuntu/cron/renewcerts.sh
# AWS Volume backups
@daily /home/ubuntu/cron/aws-missing-tools/ec2-automate-backup/ec2-automate-backup.sh -c /home/ubuntu/cron/cron-primer.sh -v vol-0cd5c496 -k 30 -p -u -r "eu-central-1"
#!/bin/bash
# Renew certs
/home/ubuntu/letsencrypt/letsencrypt-auto --config /etc/letsencrypt/cli.ini -d login.pictogramweb.com -d api.pictogramweb.com -d app.pictogramweb.com -d pre.yottacode.com -d pictogram.yottacode.com -d apk.pictogramweb.com certonly
# Reload Sails
(cd /home/ubuntu/pictogram/sails/src; /usr/bin/forever stopall; /usr/bin/forever start app.js)
......@@ -309,6 +309,7 @@ CREATE TABLE IF NOT EXISTS `license` (
`activation_ts` datetime NULL,
`expiration_ts` datetime NULL,
`duration` int(11) DEFAULT 0,
`creator` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `number` (`number`),
KEY `id_stu` (`id_stu`)
......@@ -327,6 +328,7 @@ CREATE TABLE IF NOT EXISTS `stu_picto` (
`id_stu` int(11) NOT NULL,
`id_pic` int(11) NOT NULL,
`attributes` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'JSON object describing the properties of the picto',
`id_scene` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_picto` (`id_pic`),
KEY `id_stu` (`id_stu`)
......@@ -430,6 +432,25 @@ CREATE TABLE IF NOT EXISTS `working_session` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1
COMMENT="This table stores working session information. Every working session is related to one instruction and one supervisor (and the instruction is related to one method which is related to one student)";
-- --------------------------------------------------------
--
-- Estructura de tabla para la tabla `scene`
--
CREATE TABLE IF NOT EXISTS `scene` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`active` boolean NULL DEFAULT 0,
`categories` boolean NULL DEFAULT 0,
`id_sup` int(11) NOT NULL,
`id_stu` int(11) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`id_sup`) REFERENCES `supervisor` (`id`),
FOREIGN KEY (`id_stu`) REFERENCES `student` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1
COMMENT="Scene table information. Every scene is related to some stu_pictos";
-- CREATE INDEX ix_ws_begin ON working_session (`begin`);
--
-- Restricciones para tablas volcadas
......@@ -518,7 +539,8 @@ ALTER TABLE `picto_tag`
--
ALTER TABLE `stu_picto`
ADD CONSTRAINT `fk_picto` FOREIGN KEY (`id_pic`) REFERENCES `picto` (`id`),
ADD CONSTRAINT `stu_picto_ibfk_1` FOREIGN KEY (`id_stu`) REFERENCES `student` (`id`);
ADD CONSTRAINT `stu_picto_ibfk_1` FOREIGN KEY (`id_stu`) REFERENCES `student` (`id`),
ADD CONSTRAINT `stu_picto_scene_fk` FOREIGN KEY (`id_scene`) REFERENCES `scene` (`id`);
--
-- Filtros para la tabla `stu_sup`
......
/*
ALTER TABLE stu_picto DROP FOREIGN KEY stu_picto_scene_fk;
ALTER TABLE stu_picto DROP id_scene;
DROP TABLE scene;
*/
DELIMITER $$
DROP PROCEDURE IF EXISTS scene_adapt $$
CREATE PROCEDURE scene_adapt()
BEGIN
DECLARE _id_stu INT;
DECLARE _id_sup INT;
DECLARE done INT DEFAULT FALSE;
DECLARE LID INT;
DECLARE student CURSOR FOR SELECT id FROM pictodb.student;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
/*SET FOREIGN_KEY_CHECKS = 0;*/
OPEN student;
read_loop: LOOP
FETCH student INTO _id_stu;
IF done THEN
LEAVE read_loop;
END IF;
SELECT `id_sup` INTO _id_sup FROM `stu_sup` WHERE `id_stu` = _id_stu LIMIT 1;
/* FIRST SCENE, ACTIVE, WITH CATEGORIES */
INSERT INTO `scene` (name, active, categories, id_sup, id_stu)
VALUES ('with_categories', 1, 1, _id_sup, _id_stu);
SET LID = LAST_INSERT_ID();
UPDATE `stu_picto`
SET `id_scene` = LID
WHERE `id_stu` = _id_stu
AND attributes->"$.free_category_coord_x" IS NULL
AND attributes->"$.free_category_coord_y" IS NULL;
/* SECOND SCENE, NOT ACTIVE, NO CATEGORIES */
INSERT INTO `scene` (name, active, categories, id_sup, id_stu)
VALUES ('no_categories', 0, 0, _id_sup, _id_stu);
SET LID = LAST_INSERT_ID();
UPDATE `stu_picto`
SET `id_scene` = LID
WHERE `id_stu` = _id_stu
AND attributes->"$.free_category_coord_x" IS NOT NULL
AND attributes->"$.free_category_coord_y" IS NOT NULL;
END LOOP;
CLOSE student;
/*SET FOREIGN_KEY_CHECKS = 1;*/
END $$
DELIMITER ;
CALL scene_adapt();
......@@ -15,11 +15,37 @@ Changes to be performed manually in servers to upgrade
- update arasaac uri
`source update_arasaac_color_uri.sql`
- add B&W arasaac pictos
`source arasaac_byn.sql`
- create scene table
`CREATE TABLE IF NOT EXISTS `scene` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`active` boolean NULL DEFAULT 0,
`categories` boolean NULL DEFAULT 0,
`id_sup` int(11) NOT NULL,
`id_stu` int(11) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`id_sup`) REFERENCES `supervisor` (`id`),
FOREIGN KEY (`id_stu`) REFERENCES `student` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1
COMMENT="Scene table information. Every scene is related to some stu_pictos";`
- alter table stu_picto to add new reference to scene
`ALTER TABLE `stu_picto` ADD id_scene int(11) NOT NULL;`
`ALTER TABLE `stu_picto` ADD CONSTRAINT `stu_picto_scene_fk` FOREIGN KEY (`id_scene`) REFERENCES `scene` (`id`);`
(si hay problema al añadir la foreign key, hacer SET FOREIGN_KEY_CHECKS = 0; antes de añadirla y SET FOREIGN_KEY_CHECKS = 1; después)
- load default scenes procedure
`source /vagrant/roles/database/files/scene_adapt.sql`
(already done in dev & pre)
- add new column to license:
`alter table license add column creator varchar(40) DEFAULT NULL;`
- add arasaac to source table
`INSERT INTO `source` (`id`, `name`, `description`) VALUES (3, 'Arasaac', 'Arasaac pictograms collection');`
- alter table supervisor add arasaac license:
......
......@@ -2,39 +2,44 @@
* AdminController which manages the administration login and login
* @type {Object}
*/
const bcrypt = require('bcrypt-nodejs');
module.exports = {
// @TODO ¿?¿?
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'});
return res.badRequest('No credentials sent');
// Check email
if (email != sails.config.pictogram.admin.email)
return res.json(401, {error: 'User not found'});
// if found, check password in encrypted form
bcrypt.compare(password, sails.config.pictogram.admin.password, function (err, match) {
if (err)
return res.json(500, { error: 'Server error' });
if (!match) // password do not match
return res.json(401, { error: 'Invalid password' });
// credentials are valid, return token with max life span
return res.json(200, {
token: sailsTokenAuth.issueToken({'isAdmin': true}, sails.config.jwt.expiresInMinutes)});
var admin = sails.config.pictogram.admins.find((a) => a.email == email);
if (typeof admin == 'undefined')
return res.badRequest('User not found');
// if found, check password in encrypted form
bcrypt.compare(password, admin.password, function (err, match) {
if (err)
return res.serverError('Server error' + err);
if (!match) // password do not match
return res.unauthorized('Invalid password');
// credentials are valid, return token with max life span
var returned_admin = (JSON.parse(JSON.stringify(admin)));
delete returned_admin.password;
return res.ok({
token: sailsTokenAuth.issueToken({'isAdmin': true, 'email': email}, sails.config.jwt.expiresInMinutes),
user: returned_admin
});
});
},
// @TODO 357
logout: function (req, res) {
delete req.token;
res.json(200, {notice: 'Session closed'});
res.ok('Session closed');
}
};
......@@ -8,6 +8,24 @@
module.exports = {
/**
* Get all the licenses for the given creator
* @param {request} req
* {
* email (creator email)
* }
* @param {response} res
* [list of licenses]
*/
getByEmail: function (req, res) {
if (!req.params.email)
return res.badRequest("No email provided");
License.find({creator: req.params.email})
.then((l) => res.ok(l))
.catch((e) => res.serverError(e));
},
/**
* Create a new Instruction, which is associated to the given method
* @param {request} req
* {
......@@ -67,7 +85,8 @@ module.exports = {
get_new_random_license(function (license) {
License.create({
number: license,
duration: params.duration
duration: params.duration,
creator: req.token.email
})
.then((l) => {
if (l) {
......
......@@ -58,7 +58,7 @@ module.exports = {
Office.findOne({ id: id }).populate('admin').then(function (office) {
if (office) {
res.ok({logo_url: office.logo_url, name: office.name});
res.ok({logo_url: office.logo_url || '/app/img/logo_pictogram.png', name: office.name});
} else {
res.notFound();
}
......
/* global Student, PictoCore, VStuLastInstruction, StuPicto, StuSup, sailsTokenAuth, sails,
Picto */
/**
/* StudentController
*
* @description :: Server-side logic for managing students
* @help :: See http://links.sailsjs.org/docs/controllers
*/
module.exports = {
//
// Adds a new scene into the database
//
create: function (req, res) {
var params = req.params.all();
Scene.create({
name: params.name,
active: false,
categories: params.categories,
supervisor: supervisor.id,
student: student.id
}).then(scene=>{
return res.ok(scene);
}).catch(function (err){
return res.serverError("Error creating scene: " + err);
});
},
//
// Update a scene data
//
update: function (req, res) {
Scene.findOne({ id: req.params.id }).then(function (scene) {
if (!scene) {
res.badRequest();
throw new Error('Scene not found');
}
delete scene.categories;//To avoid update these fields
delete scene.supervisor;
delete scene.student;
scene.name = req.param('name') || instruction.name;
scene.active = req.param('active') || instruction.active;
scene.save(function (error) {
if (error) {
res.serverError();
} else {
res.ok(scene);
}
});
})
.catch(function () {
res.serverError();
});
},
/**
* Delete a scene by its ID
* @param {request} req {} (with sceneId as url parameter)
* @param {response} res {}
*/
destroy: function (req, res) {
Scene.destroy({ id: req.params.id }).exec(function (error) {
if (error)
return res.badRequest();
else
return res.ok();
});
},
/**
* Return a scene with all StuPicto
* @param {request} req {} (with sceneId as url parameter)
* @param {response} res {}
*/
getScene: function(req, res){
Scene.findOne({id: req.params.id})
.populate('stuPictos').then(function(scene){
if(!scene)
return res.badRequest();
else
console.log(scene);
return res.ok(scene);
}).catch(function (err){
return res.serverError("Error finding scene "+err);
});
},
/**
* Return all the scenes of a student
* @param {request} req {} (studentId)
* @param {response} res {}
*/
getScene: function(req, res){
Scene.find({ student: req.params.id_stu})
.populate('stuPictos').then(function(scenes){
if(!scenes)
return res.badRequest();
else
return res.ok(scenes);
}).catch(function (err){
return res.serverError("Error retrieving scenes "+err);
});
}
};
......@@ -442,7 +442,7 @@ module.exports = {
// update supervisor office if it is linked as therapist
Supervisor.findOne({id: req.param('id_sup')})
.then((sup) => {
if (sup) {
if (sup && !sup.office) {
Student.findOne({id: req.param('id_stu')})
.then((stu) => {
if (stu) {
......@@ -818,6 +818,33 @@ module.exports = {
});
},
/**
* Return the active scene of a student with all StuPicto
* @param {request} req {} (with studentId as url parameter)
* @param {response} res {}
*/
getActiveScene: function(req, res){
Scene.findOne({ student: req.params.id_stu, active: true})
.populate('stuPictos').then(function(scene){
if(!scene)
return res.badRequest();
else
console.log(scene);
Scene.pictos(scene.id, function(err, pictos){
if (err){
return res.serverError("Error obtaining pictos: "+ err);
}
scene.pictos=pictos;
console.log(scene);
return res.ok(scene);
});
}).catch(function (err){
return res.serverError("Error finding scene "+err);
});
},
//
// Returns all working sessions for the given student
//
......@@ -1168,7 +1195,8 @@ module.exports = {
fs.unlinkSync(path.join(newAvatarDirectory, student.pic));
}
student.pic = newAvatarFileName;
student.pic = newAvatarFileName;
delete student.password;
student.save(function (updateStudentError) {
if (updateStudentError) {
throw updateStudentError;
......
......@@ -48,6 +48,10 @@ module.exports = {
size: 16,
unique: true
},
creator: {
columnName: "creator",
type: "string",
},
toJSON: function () {
var l = this.toObject();
delete l.id;
......
......@@ -87,7 +87,13 @@ module.exports = {
students: {
collection: "Student",
via: 'office'
}
},
toJSON: function() {
var office = this.toObject();
if (!office.logoUrl)
office.logoUrl = '/app/img/logo_pictogram.png';
return office;
}
},
beforeCreate: function (attrs, next) {
......
/**
* scene.js
*
* @description :: TODO: Write a short summary of how this model works and what it represents here.
* @docs :: http://sailsjs.org/#!documentation/models
*/
module.exports = {
tableName : 'scene',
migrate : 'safe',
schema : true,
autoPK : false,
autoCreatedAt : false,
autoUpdatedAt : false,
attributes: {
id: {
type: "integer",
autoIncrement: true,
primaryKey: true,
unique: true
},
name: {
required: true,
type: "string",
size: 100
},
active: {
type: "boolean"
},
categories: {
type: "boolean"
},
supervisor: { //FK de supervisor 1 a NN
columnName: "id_sup",
required: true,
type: "integer",
model: "Supervisor"
},
student: { // FK de student 1 a N
columnName: "id_stu",
required: true,
type: "integer",
model: "Student"
},
// Relacion con Stu_picto
stuPictos:{
collection: "StuPicto",
via: "scene"
}
},
//
// Class method for getting the list of pictos associated to a given
// scene
pictos: function(id_scene, callback) {
var l = [];
var fs = require('fs');
console.log("metodo pictos scene");
Scene.findOne(id_scene)
.then((scene) => {
if (!scene)
throw new Error("No scene found");
var stuPictos = StuPicto.find({scene: id_scene})
.populate('picto')
.then((stuPictos) => {
if (!stuPictos || stuPictos.length == 0)
return [];
console.log(stuPictos);
return stuPictos;
})
.catch((err) => {
throw err;
});
var student = Student.findOne(scene.student).then(student=>{
if (!student)
return [];
return student;
}).catch( (err) => {
throw err;
});
return [scene, stuPictos, student];
})
.spread((scene, stuPictos, student) => {
console.log(stuPictos);
async.eachSeries(stuPictos, function(stuPicto, next_cb) {
// Populate expressions to get it with the picto
Picto.findOne(stuPicto.picto.id)
.populate('expressions', {lang: student.lang})
.populate('tags', {lang: student.lang})
.then((picto) => {
// check picto has expressions associated in student language
if (picto.expressions.length == 0 || picto.expressions[0].text.length == 0)
return next_cb();
// check picto image is available
picto.imageFileExists(function(found) {
if (found) {
// Now we have everything, so we add the picto to the list
stuPicto.attributes.expression = stuPicto.attributes.expression ? stuPicto.attributes.expression : picto.expressions[0].text;
var stuPictoToAdd = {
"id": stuPicto.id,
"picto": stuPicto.picto,
"attributes": stuPicto.attributes,
"tags": picto.tags ? picto.tags : []
};
l.push(stuPictoToAdd);
next_cb();
} else {
next_cb();
}
});
})
.catch((err) => {
next_cb(err);
});
},
function (err) { // loop has end
callback(err, l);
}); // end async.eachSeries
})
.catch((err) => {
callback(err, l);
}); // end Student.findOne
}
};
......@@ -32,6 +32,11 @@ module.exports = {
type: 'integer',
model: 'Picto'
},
scene:{ //FK de Scene 1 a N
columName: "id_scene",
type: "integer",
model: "Scene"
},
/**
* This "extra" property allow us to adapt the server to the student needs
......
......@@ -2,7 +2,7 @@ var mailerService = require('sails-service-mailer');
module.exports.mailer = function() {
return mailerService('sendmail', {
from: 'no-reply@yottacode.com',
from: 'no-reply@pictogramweb.com',
subject: sails.__('notification_from_pictogram'),
provider: {
path: '/usr/sbin/sendmail'
......
......@@ -11,6 +11,7 @@
"action-tryend": "Try end",
"action-tryinit": "Try begin",
"action-unshow": "Writing mode",
"activation": "Activation",
"add": "Add",
"add_expression": "Add expression",
"add_instruction": "Add instruction",
......@@ -30,7 +31,7 @@
"apply":"Apply",
"annual": "Annual",
"April": "April",
"arasaac_license": "Los símbolos pictográficos utilizados en esta sección son propiedad de CATEDU (http://arasaac.org/) bajo licencia Creative Commons y han sido creados por Sergio Palao.",
"arasaac_license": "The pictographic symbols used in this section are the property of CATEDU (http://arasaac.org/) under a Creative Commons license and have been created by Sergio Palau.",
"arasaac_license_import": "The pictographic symbols used in this section are the property of CATEDU (http://arasaac.org/) under a Creative Commons license and have been created by Sergio Palau. By importing you accept the ",
"arasaac_license_import2": "terms of use",
"arasaac_license_link": "http://www.arasaac.org/condiciones_uso.php",
......@@ -43,6 +44,7 @@
"background": "Background",
"beep": "Beep",
"birthdate": "Birthdate",
"black_and_white": "B&W",
"cancel": "Cancel",
"cannot_delete_method": "Method could not be deleted, maybe due to existing recorded sessions.",
"cannot_delete_instruction": "Instruction could not be deleted, maybe due to existing recorded sessions.",
......@@ -76,6 +78,7 @@
"country_office_requested": "Country for office/center is mandatory",
"create_account": "Create account",
"create_an_account": "Create an account",
"creation": "Creation",
"credentials": "Credentials",
"crop_image": "Crop image",
"data_no_saved": "Data can't be saved",
......@@ -177,6 +180,7 @@
"legend_normal":"Normal legend",
"legend_full":"Only legend",
"legend_size": "Legend size",
"license": "License",
"licenses": "Licenses",
"license_already_activated": "License already activated",
"license_created": "License created",
......
......@@ -11,6 +11,7 @@
"action-tryend": "Fin de ensayo",
"action-tryinit": "Inicio de ensayo",
"action-unshow": "Modo escritura",
"activation": "Activación",
"add": "Añadir",
"add_expression": "Añadir expresión",
"add_instruction": "Añadir instrucción",
......@@ -43,6 +44,7 @@
"background": "Fondo",
"beep": "Pitido",
"birthdate": "Fecha de nacimiento",
"black_and_white": "ByN",
"cancel": "Cancelar",
"cannot_delete_method": "No se pudo eliminar el método, tal vez porque existen sesiones asociadas.",
"cannot_delete_instruction": "No se pudo eliminar la instrucción, tal vez porque existen sesiones asociadas.",
......@@ -76,6 +78,7 @@
"country_office_requested": "Debe especificar el país del gabinete/centro",
"create_account": "Crear cuenta",
"create_an_account": "Crear una cuenta",
"creation": "Creación",
"credentials": "Credenciales",
"crop_image": "Recortar imagen",
"data_no_saved": "Los datos no se han podido guardar",
......@@ -177,6 +180,7 @@
"legend_full":"Sólo leyenda",
"legend_apply_all":"Aplicar a todos los pictogramas",
"legend_size": "Tamaño de la leyenda",
"license": "Licencia",
"licenses": "Licencias",
"licenses_left": "{{number}} licencias disponibles",
"license_already_activated": "Licencia ya activada previamente",
......
/vagrant/arasaac
\ No newline at end of file
/* global angular */
angular.module('dashboardConfig', []).constant('config', {
backend: 'https://' + window.location.host,
});
......@@ -3,11 +3,10 @@
//-------------------
// Admin Controller
//-------------------
dashboardControllers.controller('AdminCtrl', function AdminCtrl($scope) {
dashboardControllers.controller('AdminCtrl', function AdminCtrl($scope, $window) {
// The last parameter, config, is injected from config.js (defined in dashboardConfig module)
// Assign values this way (like an object) to ensure it's the parent scope $scope.user.name ='Super';
$scope.user.surname = 'Admin';
$scope.user.pic ='img/arturo.jpg';
$scope.user = JSON.parse($window.sessionStorage.user);
});
\ No newline at end of file
});
......@@ -10,9 +10,20 @@ dashboardControllers.controller('AdminLicensesCtrl', function AdminLicensesCtrl(
$scope.showmessagesupervisor = false;
$scope.new_numbers = [];
$scope.formdatalicense = {
duration: 1,
duration: 3,
repeat: 1
};
$scope.licenses = [];
// Get all licenses for the user
$http
.get(config.backend + '/license/' + $scope.user.email)
.success(function(data, status, headers, config) {
$scope.licenses = data;
})
.error(function(data, status, headers, config) {
ngToast.danger({content: $translate.instant('error_general')});
});
// This generates a new license and registers it in the database
$scope.create_licenses = function(supervisor) {
......@@ -21,14 +32,15 @@ dashboardControllers.controller('AdminLicensesCtrl', function AdminLicensesCtrl(
$http
.post(config.backend+'/license', $scope.formdatalicense)
.success(function(data, status, headers, config) {
$scope.new_numbers.push(data.number.substr(0,4) +
/*$scope.new_numbers.push(data.number.substr(0,4) +
"-" + data.number.substr(4,4) +
"-" + data.number.substr(8,4) +
"-" + data.number.substr(12,4));
*/
$scope.new_numbers.push(data.number);
})
.error(function(data, status, headers, config) {
ngToast.danger({content: $translate.instant('error_general')});
console.log("Error from API: " + data.error);
});
}
$scope.duration_registered = $scope.formdatalicense.duration;
......
......@@ -15,8 +15,9 @@ dashboardControllers.controller('LoginAdminCtrl', function LoginAdminCtrl($scope
$http
.post(config.backend+'/admin/login', $scope.credentials)
.success(function(data, status, headers, config) {
// Save token and user data y sessionStorage
// Save token, user data in sessionStorage
$window.sessionStorage.token = data.token;
$window.sessionStorage.user = JSON.stringify(data.user);
// Redirect to admin panel
$location.path('/admin/licenses');
......@@ -28,6 +29,10 @@ dashboardControllers.controller('LoginAdminCtrl', function LoginAdminCtrl($scope
.error(function(data, status, headers, config) {
// Delete token if user fails to login
delete $window.sessionStorage.token;
delete $window.sessionStorage.user;
// Redirect to admin panel
$location.path('/admin/login');
$translate('login_fail').then(function(translation) {
ngToast.danger({ content: translation });
......
......@@ -12,7 +12,7 @@
<a class="navbar-brand" href="/app/#/admin"><img src="img/logo_pictogram.png" alt="Pictogram" title="Pictogram" style="height: 70px;" /></a>
</div>
<!-- Agrupar los enlaces de navegación, los formularios y cualquier otro elemento que se pueda ocultar al minimizar la barra -->
<div class="collapse navbar-collapse navbar-ex6-collapse">
......@@ -21,9 +21,9 @@
</div>
<div class="nav navbar-nav navbar-right navbar-text">
<h4 class="text-right">{{user.getFullName()}}</h4>
<h4 class="text-right">{{ user.email }}</h4>
<p class="text-right">
<!-- Botón salir -->
<a ng-click="logout()" class="btn btn-default btn-sm" role="button">
<span class="glyphicon glyphicon-log-out" aria-hidden="true"></span> {{ 'logout' | translate }}
......@@ -33,4 +33,4 @@
</div>
</div>
</nav>
\ No newline at end of file
</nav>
......@@ -2,13 +2,10 @@
<div class="row">
<div class="col-md-2">
</div>
<div class="col-md-4">
<h3 translate>licenses</h3>
<div class="col-md-5">
<form role="form" ng-submit="create_licenses()">
<div class="form-group">
......@@ -27,18 +24,40 @@
</form>
<div ng-show="new_numbers.length > 0" class="alert alert-info">
<p>{{ 'licenses_created' | translate }}:</p><p>&nbsp;</p>
<div ng-repeat="number in new_numbers track by $index">
<p> <strong>{{ number }}</strong></p>
</div>
<p>{{ 'duration_in_months' | translate }}: {{ duration_registered }}</p>
</div>
<div class="col-md-4">
<div ng-show="new_numbers.length > 0" class="alert alert-info">
<p>{{ 'licenses_created' | translate }}:</p><p>&nbsp;</p>
<div ng-repeat="number in new_numbers track by $index">
<p> <strong>{{ number }}</strong></p>
</div>
<p>{{ 'duration_in_months' | translate }}: {{ duration_registered }}</p>
</div>
</div>
<div class="col-md-2">
</div>
<div class="col-md-5">
<p> {{ 'licenses_created' | translate }}: {{ licenses.length }}</p>
<div class="pre-scrollable">
<table class="table table-striped">
<thead>
<tr>
<th>{{ 'license' | translate }}</th>
<th>{{ 'creation' | translate }}</th>
<th>{{ 'activation' | translate }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="l in licenses | orderBy : 'creation_ts' : true" ng-class-odd="'striped'">
<td><tt>{{ l.number }}</tt></a></td>
<td>{{ l.creation_ts | date : "dd/MM/y" }}</td>
<td>{{ l.activation_ts | date : "dd/MM/y" }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
......@@ -94,10 +94,9 @@ dashboardControllers.controller('AddPictoCtrl', function (
if(!$scope.showArasaacLicense){
$scope.loadingCatPictos = true;
var request = "";
//Request page X from all pictos (symbolstx)
request = config.backend + '/picto/' + student.lang +
'/pic_fromArasaac/page/'+$scope.page+'/limit/'+$scope.limit;
'/pic_fromArasaac/page/'+$scope.page+'/limit/'+$scope.limit+'/source/3';
$http.get(request)
.success(function (data) {
......@@ -116,6 +115,34 @@ dashboardControllers.controller('AddPictoCtrl', function (
}
};
$scope.load_arasaac_byn_pictos = function () {
$scope.pictos = [];
$scope.page = 1;
if(!$scope.showArasaacLicense){
$scope.loadingCatPictos = true;
var request = "";
//Request page X from all pictos (symbolstx)
request = config.backend + '/picto/' + student.lang +
'/pic_fromArasaac/page/'+$scope.page+'/limit/'+$scope.limit+'/source/4';
$http.get(request)
.success(function (data) {
if (data && $scope.source == 'arasaac_byn'){
$scope.pictos = data;
}
$scope.loadingCatPictos = false;
setTimeout(function () { $scope.$apply(); });
})
.error(function () {
$translate('error_loading_pictos').then(function (translation) {
ngToast.danger({ content: translation });
});
$scope.loadingCatPictos = false;
});
}
};
//
// Load pictos owned by the actual supervisor
//
......
......@@ -16,7 +16,7 @@
<div class="row">
<div class="col-xs-6">
<div class="col-xs-8">
<div class="btn-group" ng-show="!onlyOwn">
<button class="btn btn-default" btn-radio="'symbolstx'" ng-model="source" ng-click="open_category_from_bc('0')">
<span class="glyphicon glyphicon-th"></span> SymbolStix
......@@ -24,6 +24,9 @@
<button class="btn btn-default" btn-radio="'arasaac'" ng-model="source" ng-click="load_arasaac_pictos()">
<i class="fa fa-th" aria-hidden="true"></i> ARASAAC
</button>
<button class="btn btn-default" btn-radio="'arasaac_byn'" ng-model="source" ng-click="load_arasaac_byn_pictos()">
<i class="fa fa-th" aria-hidden="true"></i> ARASAAC {{ 'black_and_white' | translate}}
</button>
<button class="btn btn-default" btn-radio="'ownpictos'" ng-model="source" ng-click="load_own_pictos()">
<span class="glyphicon glyphicon-picture"></span> {{ 'own_pictos' | translate }}
</button>
......@@ -31,7 +34,7 @@
</div>
<!-- Filter form -->
<div class="col-xs-6">
<div class="col-xs-4">
<form ng-submit="search()">
<div class="input-group" id="search_pictos_box">
<input type="text" class="form-control" placeholder="{{ 'filter' | translate }}"
......@@ -46,41 +49,6 @@
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="btn-group btn-group-justified" role="group">
<div class="btn-group" role="group">
<button type="button" btn-radio="'color'" ng-model="color" ng-click="update_color()" class="btn btn-default">{{'color' | translate}}</button>
</div>
<div class="btn-group" role="group">
<button type="button" btn-radio="'byn'" ng-model="color" ng-click="update_color()" class="btn btn-default">{{'black_and_white' | translate}}</button>
</div>
</div>
</div>
<!-- <div class="col-xs-4">
<div class="input-group">
<span class="input-group-addon">
<input type="radio" id="arasaac_color" ng-model="color" ng-value="3" ng-change="update_color()" class="ng-pristine ng-valid" value="3" >
</span>
<span class="form-control ng-binding" for="arasaac_color">
{{'color' | translate}}
</span>
</div>
</div>
<div class="col-xs-4">
<div class="input-group">
<span class="input-group-addon">
<input type="radio" id="arasaac_byn" ng-model="color" ng-value="4" ng-change="update_color()" class="ng-pristine ng-valid" value="4">
</span>
<span class="form-control ng-binding" for="arasaac_color">
{{'black_and_white' | translate}}
</span>
</div>
</div> -->
</div>
<hr>
......@@ -110,7 +78,7 @@
<!-- Collections row -->
<div class="row">
<div id="arasaac_agreement" class="col-md-12" ng-show="showArasaacLicense && source == 'arasaac'">
<div id="arasaac_agreement" class="col-md-12" ng-show="showArasaacLicense && (source == 'arasaac' || source == 'arasaac_byn')">
<div class="panel panel-warning">
<div class="panel-heading">
<h3 class="panel-title">ARASAAC</h3>
......@@ -154,7 +122,7 @@
</div><!-- /modal-body -->
<div class="modal-footer">
<div class="arasaac_license" ng-show="source == 'arasaac'">
<div class="arasaac_license" ng-show="source == 'arasaac' || source == 'arasaac_byn'">
<p><small>{{'arasaac_license' | translate}}</small></p>
</div>
<button class="btn btn-success pull-left" ng-show="source == 'ownpictos'" ngf-select ng-model="picFile" accept="image/*" ngf-change="addOwnPicto()">
......
......@@ -49,7 +49,7 @@
</div>
<div class="form-group">
<label translate>license_number</label>
<input type="text" id="setup_license" class="form-control" mask="9999-9999-9999-9999" clean="true" placeholder="{{ 'license_number' | translate }}" ng-model="formUser.license_number" required>
<input type="text" id="setup_license" class="form-control" mask="wwww-wwww-wwww-wwww" clean="true" placeholder="{{ 'license_number' | translate }}" ng-model="formUser.license_number" required>
<div ng-show="studentData.license && !studentData.license_expired" class="alert alert-info" role="alert">
<i class="fa fa-info-circle" aria-hidden="true"></i> {{ 'license_expires' | translate }} {{ studentData.expiration_date }}
......
......@@ -26,7 +26,7 @@
</div>
<div class="form-group">
<input class="form-control" type="text" id="student_license" mask="9999-9999-9999-9999" clean="true" placeholder="{{ 'license_number' | translate }}" ng-model="formdatastudent.license_number" required>
<input class="form-control" type="text" id="student_license" mask="wwww-wwww-wwww-wwww" clean="true" placeholder="{{ 'license_number' | translate }}" ng-model="formdatastudent.license_number" required>
</div>
......
......@@ -11,12 +11,12 @@
</div>
<div class="col-xs-4">
<div class="languages-margin text-center text-muted">
Powered by &nbsp; <a href="http://www.yottacode.com"><img src="img/logo_pictogram.png" width="40px" alt="Pictogram" title="Pictogram" /></a>
Powered by &nbsp; <a href="http://www.pictogramweb.com"><img src="img/logo_pictogram.png" width="40px" alt="Pictogram" title="Pictogram" /></a>
</div>
</div>
<div class="col-xs-4">
<div class="languages-margin text-right text-muted">
<a href="mailto:soporte@yottacode.com" translate>support</a> |
<a href="mailto:soporte@pictogramweb.com" translate>support</a> |
<a href='/app/#/disclaimer' target='_blank' translate>disclaimer</a>
</div>
</div>
......
......@@ -59,6 +59,10 @@
margin-top: 7px;
}
.table .table-striped .striped {
background-color: lightgray !important;
}
/* Evitar scrolling horizontal */
body{
......
......@@ -6,10 +6,18 @@ var UPLOAD_PATH = path.join(__dirname, '..', '..', 'upload');
module.exports.pictogram = {
version: "1.1", // actual version of the server, to be checked by the client
admin: {
email: 'amontejo@ujaen.es',
password: '$2a$06$flEEOc15SerMeYWARrN9w.KSpJuM.jDsaTgrtD0ESzbxKHPl0f/zq' //y00ttaa!!
},
admins: [
{
email: 'amontejo@ujaen.es',
password: '$2a$06$flEEOc15SerMeYWARrN9w.KSpJuM.jDsaTgrtD0ESzbxKHPl0f/zq', //y00ttaa!!
lang: "es-es"
},
{
email: 'luisballesteros@editorialcepe.es',
password: '$2a$10$Re9LxejAm6GjYLvw7LCbQeLiNn4HDGxjQMbZxg0paP3SGIJI6Rn52', //ba!!3sTe;0s
lang: "es-es"
}
],
serialSize: 10, // number of characters in generated serial numbers
pageLimit: 10, // number of elements per "page"
......
......@@ -80,6 +80,14 @@ module.exports.policies = {
fromSearch: ['tokenAuth']
},
SceneController:{
create: ['tokenAuth', 'isSupervisorOfStudent'],
update: ['tokenAuth', 'isSupervisorOfStudent'],
destroy: ['tokenAuth', 'isSupervisorOfStudent'],
getScene: ['tokenAuth'],
getStudentScenes: ['tokenAuth']
},
ServerController: {
ping: true,
ping_session: ['tokenAuth']
......@@ -115,14 +123,16 @@ module.exports.policies = {
actions_batch: ['tokenAuth'],
delete: ['tokenAuth', 'isSupAdmin'],
unlink_supervisor: ['tokenAuth', 'isSupAdmin'],
delete_picto: ['tokenAuth', 'isSupervisorOfStudent']
delete_picto: ['tokenAuth', 'isSupervisorOfStudent'],
getActiveScene: ['tokenAuth']
},
LicenseController: {
// create: ['tokenAuth', 'isAdmin'],
// activate: ['tokenAuth']
create: true,
activate: true
create: ['tokenAuth'],
activate: ['tokenAuth'],
getByEmail: ['tokenAuth']
},
SupervisorController: {
......
......@@ -39,6 +39,7 @@ module.exports.routes = {
'POST /instruction/template': 'MetaInstructionController.create',
'DELETE /instruction/template/:id': 'MetaInstructionController.destroy',
'GET /license/:email': 'LicenseController.getByEmail',
'POST /license': 'LicenseController.create',
'PUT /license/:number': 'LicenseController.activate',
......@@ -62,7 +63,7 @@ module.exports.routes = {
'GET /picto/:lang/pic_categories/:id_cat': 'PictoController.categories',
'GET /picto/:lang/pic_fromcategory/:id_cat': 'PictoController.fromcategory',
'GET /picto/:lang/pic_fromSymbolStx/page/:page/limit/:limit': 'PictoController.fromSymbolStx',
'GET /picto/:lang/pic_fromArasaac/page/:page/limit/:limit': 'PictoController.fromArasaac',
'GET /picto/:lang/pic_fromArasaac/page/:page/limit/:limit/source/:source': 'PictoController.fromArasaac',
'GET /picto/:lang/pic_fromCatSubcat/category/:id_cat/page/:page/limit/:limit': 'PictoController.fromCatSubcat',
'GET /picto/:lang/:id_sup/pic_fromSearch/:text/category/:id_cat/source/:source': 'PictoController.fromSearch',
......@@ -72,6 +73,12 @@ module.exports.routes = {
'DELETE /picto/:id': 'PictoController.destroy',
'DELETE /picto/:id_sup/tag/:id_tag': 'PictoController.del_tag',
'GET /scene/:id': 'SceneController.getScene',
'GET /stu/:id_stu/scenes': 'SceneController.getStudentScenes',
'POST /scene/:id': 'SceneController.create',
'PUT /scene/:id': 'SceneController.update',
'DELETE /scene/:id': 'SceneController.destroy',
'GET /server/ping': 'ServerController.ping',
'GET /server/ping_session': 'ServerController.ping_session',
......@@ -81,6 +88,7 @@ module.exports.routes = {
'GET /stu/:id_stu/tutors': 'StudentController.tutors',
'POST /stu/:id_stu/sup/:id_sup': 'StudentController.link_supervisor',
'GET /stu/:id_stu/pictos': 'StudentController.pictos',
'GET /stu/:id_stu/activeScene': 'StudentController.getActiveScene',
'GET /stu/:id_stu/methods': 'StudentController.methods',
'GET /stu/:id_stu/lasttries': 'StudentController.lasttries',
......
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