Version 15.177

This commit is contained in:
Antonio Cañas Vargas 2016-04-04 14:48:12 +02:00
parent 4c2189921f
commit 2b59faa4ef
8 changed files with 214 additions and 142 deletions

View File

@ -139,13 +139,14 @@
/****************************** Public constants *****************************/
/*****************************************************************************/
#define Log_PLATFORM_VERSION "SWAD 15.176 (2016-04-04)"
#define Log_PLATFORM_VERSION "SWAD 15.177 (2016-04-04)"
#define CSS_FILE "swad15.175.10.css"
#define JS_FILE "swad15.131.3.js"
// Number of lines (includes comments but not blank lines) has been got with the following command:
// nl swad*.c swad*.h css/swad*.css py/swad*.py js/swad*.js soap/swad*.h sql/swad*.sql | tail -1
/*
Version 15.177: Apr 04, 2016 Code refactoring related to images. (198083 lines)
Version 15.176: Apr 04, 2016 Code refactoring related to images. (198019 lines)
Version 15.175.11:Apr 04, 2016 Code refactoring related to image associated to a test question.
Forms to edit image in every unique/multiple answer. (197968 lines)

View File

@ -30,12 +30,27 @@
#include <unistd.h> // For access, lstat, getpid, chdir, symlink
#include "sha2/sha2.h" // For sha-256 and sha-512 algorithms
#include "swad_constant.h"
#include "swad_cryptography.h"
#include "swad_global.h"
/*****************************************************************************/
/****************************** Public constants *****************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Internal constants ****************************/
/*****************************************************************************/
/*****************************************************************************/
/****************************** Internal types *******************************/
/*****************************************************************************/
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
extern const char Str_BIN_TO_BASE64URL[64];
/*****************************************************************************/
@ -120,3 +135,18 @@ void Cry_EncryptSHA512Base64 (const char *PlainText,char EncryptedText[Cry_LENGT
}
EncryptedText[Cry_LENGTH_ENCRYPTED_STR_SHA512_BASE64] = '\0';
}
/*****************************************************************************/
/*** Create a unique name encrypted, different each time function is called **/
/*****************************************************************************/
void Cry_CreateUniqueNameEncrypted (char UniqueNameEncrypted[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64+1])
{
static unsigned NumCall = 0; // When this function is called several times in the same execution of the program, each time a new name is created
char UniqueNamePlain[Cns_MAX_LENGTH_IP+1+10+1+10+1+10+1];
NumCall++;
sprintf (UniqueNamePlain,"%s-%lx-%x-%x",
Gbl.IP,Gbl.StartExecutionTimeUTC,Gbl.PID,NumCall);
Cry_EncryptSHA256Base64 (UniqueNamePlain,UniqueNameEncrypted); // Make difficult to guess a unique name
}

View File

@ -45,4 +45,6 @@
void Cry_EncryptSHA256Base64 (const char *PlainText,char EncryptedText[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64+1]);
void Cry_EncryptSHA512Base64 (const char *PlainText,char EncryptedText[Cry_LENGTH_ENCRYPTED_STR_SHA512_BASE64+1]);
void Cry_CreateUniqueNameEncrypted (char UniqueNameEncrypted[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64+1]);
#endif

View File

@ -83,7 +83,6 @@ void Gbl_InitializeGlobals (void)
extern const char *The_ThemeId[The_NUM_THEMES];
extern const char *Ico_IconSetId[Ico_NUM_ICON_SETS];
extern const unsigned Txt_Current_CGI_SWAD_Language;
char UniqueNamePlain[Cns_MAX_LENGTH_IP+1+10+1+10+1];
Txt_Language_t Lan;
setlocale (LC_ALL,"es_ES.utf8");
@ -98,8 +97,7 @@ void Gbl_InitializeGlobals (void)
Gbl.TimeGenerationInMicroseconds = Gbl.TimeSendInMicroseconds = 0L;
Gbl.PID = getpid ();
Sta_GetRemoteAddr ();
sprintf (UniqueNamePlain,"%s-%lx-%x",Gbl.IP,Gbl.StartExecutionTimeUTC,Gbl.PID);
Cry_EncryptSHA256Base64 (UniqueNamePlain,Gbl.UniqueNameEncrypted); // Make difficult to guess a unique name
Cry_CreateUniqueNameEncrypted (Gbl.UniqueNameEncrypted);
srand ((unsigned int) Gbl.StartExecutionTimeUTC); // Initialize seed for rand()
@ -430,10 +428,6 @@ void Gbl_InitializeGlobals (void)
Gbl.Imported.ExternalSesId[0] = '\0';
Gbl.Imported.ExternalRole = Rol_UNKNOWN;
/* Related to images uploaded in a form */
Gbl.Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Image.Status = Img_FILE_NONE;
Gbl.WebService.Function = Svc_unknown;
}

View File

@ -659,7 +659,7 @@ struct Globals
char *Text;
size_t Length;
} Stem, Feedback;
char Image[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64+1];
struct Image Image;
bool Shuffle;
struct
{
@ -670,7 +670,7 @@ struct Globals
bool Correct;
char *Text;
char *Feedback;
char Image[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64+1];
struct Image Image;
} Options[Tst_MAX_OPTIONS_PER_QUESTION];
long Integer;
double FloatingPoint[2];
@ -720,11 +720,13 @@ struct Globals
float MaxPercent;
} DegPhotos;
} Stat;
/*
struct
{
Img_Action_t Action;
Img_FileStatus_t Status;
} Image;
*/
};
/*****************************************************************************/

View File

@ -71,43 +71,44 @@ static void Img_ProcessImage (const char *FileNameImgOriginal,
/***************************** Get image from form ***************************/
/*****************************************************************************/
void Img_GetImageFromForm (char *ImageName,void (*GetImageName) (void),
const char *ParamFile,
void Img_GetImageFromForm (unsigned NumOpt,struct Image *Image,
void (*GetImageName) (unsigned NumOpt,char *ImageName),
const char *ParamAction,const char *ParamFile,
unsigned Width,unsigned Height,unsigned Quality)
{
Gbl.Image.Action = Img_GetImageActionFromForm ("ImgAct");
switch (Gbl.Image.Action)
Image->Action = Img_GetImageActionFromForm (ParamAction);
Image->Status = Img_FILE_NONE;
switch (Image->Action)
{
case Img_ACTION_NO_IMAGE: // Do not use image (remove current image if exists)
/***** Reset image name *****/
ImageName[0] = '\0';
Gbl.Image.Status = Img_FILE_NONE;
Image->Name[0] = '\0';
break;
case Img_ACTION_KEEP_IMAGE: // Keep current image unchanged
/***** Get image name *****/
GetImageName ();
Gbl.Image.Status = (ImageName[0] ? Img_NAME_STORED_IN_DB :
Img_FILE_NONE);
GetImageName (NumOpt,Image->Name);
if (Image->Name[0])
Image->Status = Img_NAME_STORED_IN_DB;
break;
case Img_ACTION_NEW_IMAGE: // Upload new image
/***** Get new image (if present ==> process and create temporary file) *****/
Img_GetAndProcessImageFileFromForm (ParamFile,Width,Height,Quality);
if (Gbl.Image.Status != Img_FILE_PROCESSED) // No new image received-processed successfully
Img_GetAndProcessImageFileFromForm (Image,ParamFile,Width,Height,Quality);
if (Image->Status != Img_FILE_PROCESSED) // No new image received-processed successfully
{
/* Reset image name */
ImageName[0] = '\0';
Gbl.Image.Status = Img_FILE_NONE;
Image->Name[0] = '\0';
Image->Status = Img_FILE_NONE;
}
break;
case Img_ACTION_CHANGE_IMAGE: // Replace old image by new image
/***** Get new image (if present ==> process and create temporary file) *****/
Img_GetAndProcessImageFileFromForm (ParamFile,Width,Height,Quality);
if (Gbl.Image.Status != Img_FILE_PROCESSED) // No new image received-processed successfully
Img_GetAndProcessImageFileFromForm (Image,ParamFile,Width,Height,Quality);
if (Image->Status != Img_FILE_PROCESSED) // No new image received-processed successfully
{
/* Get image name */
GetImageName ();
Gbl.Image.Status = (ImageName[0] ? Img_NAME_STORED_IN_DB :
Img_FILE_NONE);
GetImageName (NumOpt,Image->Name);
Image->Status = (Image->Name[0] ? Img_NAME_STORED_IN_DB :
Img_FILE_NONE);
}
break;
}
@ -117,12 +118,12 @@ void Img_GetImageFromForm (char *ImageName,void (*GetImageName) (void),
/************************* Get image action from form ************************/
/*****************************************************************************/
Img_Action_t Img_GetImageActionFromForm (const char *ParamRadio)
Img_Action_t Img_GetImageActionFromForm (const char *ParamAction)
{
char UnsignedStr[10+1];
unsigned UnsignedNum;
Par_GetParToText (ParamRadio,UnsignedStr,10);
Par_GetParToText (ParamAction,UnsignedStr,10);
if (sscanf (UnsignedStr,"%u",&UnsignedNum) != 1)
Lay_ShowErrorAndExit ("Wrong action to perform on image.");
if (UnsignedNum >= Img_NUM_ACTIONS)
@ -135,7 +136,8 @@ Img_Action_t Img_GetImageActionFromForm (const char *ParamRadio)
/*****************************************************************************/
// Return true if image is created
void Img_GetAndProcessImageFileFromForm (const char *ParamFile,
void Img_GetAndProcessImageFileFromForm (struct Image *Image,
const char *ParamFile,
unsigned Width,unsigned Height,
unsigned Quality)
{
@ -149,8 +151,8 @@ void Img_GetAndProcessImageFileFromForm (const char *ParamFile,
char FileNameImgTmp[PATH_MAX+1]; // Full name of temporary processed file
bool WrongType = false;
/***** Reset image status *****/
Gbl.Image.Status = Img_FILE_NONE;
/***** Rest image file status *****/
Image->Status = Img_FILE_NONE;
/***** Get filename and MIME type *****/
Param = Fil_StartReceptionOfFile (ParamFile,FileNameImgSrc,MIMEType);
@ -176,7 +178,7 @@ void Img_GetAndProcessImageFileFromForm (const char *ParamFile,
return;
/***** Assign a unique name for the image *****/
strcpy (Gbl.Test.Image,Gbl.UniqueNameEncrypted);
Cry_CreateUniqueNameEncrypted (Image->Name);
/***** Create private directories if not exist *****/
/* Create private directory for images if it does not exist */
@ -191,23 +193,23 @@ void Img_GetAndProcessImageFileFromForm (const char *ParamFile,
/***** End the reception of original not processed image
(it can be very big) into a temporary file *****/
Image->Status = Img_FILE_NONE;
sprintf (FileNameImgOrig,"%s/%s/%s/%s_original.%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG,Cfg_FOLDER_IMG_TMP,
Gbl.Test.Image,PtrExtension);
Image->Name,PtrExtension);
if (Fil_EndReceptionOfFile (FileNameImgOrig,Param)) // Success
{
Gbl.Image.Status = Img_FILE_RECEIVED;
Image->Status = Img_FILE_RECEIVED;
/***** Convert original image to temporary JPEG processed file *****/
/***** Convert original image to temporary JPEG processed file
by calling to program that makes the conversion *****/
sprintf (FileNameImgTmp,"%s/%s/%s/%s.jpg",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG,Cfg_FOLDER_IMG_TMP,
Gbl.Test.Image);
/* Call to program that makes the conversion */
Image->Name);
Img_ProcessImage (FileNameImgOrig,FileNameImgTmp,Width,Height,Quality);
Gbl.Image.Status = Img_FILE_PROCESSED;
Image->Status = Img_FILE_PROCESSED;
/***** Remove temporary file *****/
/***** Remove temporary original file *****/
unlink (FileNameImgOrig);
}
}
@ -246,7 +248,7 @@ static void Img_ProcessImage (const char *FileNameImgOriginal,
/**** Move temporary processed image file to definitive private directory ****/
/*****************************************************************************/
void Img_MoveImageToDefinitiveDirectory (void)
void Img_MoveImageToDefinitiveDirectory (struct Image *Image)
{
char PathImgPriv[PATH_MAX+1];
char FileNameImgTmp[PATH_MAX+1]; // Full name of temporary processed file
@ -255,45 +257,45 @@ void Img_MoveImageToDefinitiveDirectory (void)
/***** Create subdirectory if it does not exist *****/
sprintf (PathImgPriv,"%s/%s/%c%c",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG,
Gbl.Test.Image[0],
Gbl.Test.Image[1]);
Image->Name[0],
Image->Name[1]);
Fil_CreateDirIfNotExists (PathImgPriv);
/***** Temporary processed file *****/
sprintf (FileNameImgTmp,"%s/%s/%s/%s.jpg",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG,Cfg_FOLDER_IMG_TMP,
Gbl.Test.Image);
Image->Name);
/***** Definitive processed file *****/
sprintf (FileNameImg,"%s/%s/%c%c/%s.jpg",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG,
Gbl.Test.Image[0],
Gbl.Test.Image[1],
Gbl.Test.Image);
Image->Name[0],
Image->Name[1],
Image->Name);
/***** Move file *****/
if (rename (FileNameImgTmp,FileNameImg)) // Fail
Lay_ShowAlert (Lay_ERROR,"Can not move file.");
else // Success
Gbl.Image.Status = Img_FILE_MOVED;
Image->Status = Img_FILE_MOVED;
}
/*****************************************************************************/
/******************** Write the image of a test question *********************/
/*****************************************************************************/
void Img_ShowImage (const char *ImageName,const char *ClassImg)
void Img_ShowImage (struct Image *Image,const char *ClassImg)
{
char FileNameImgPriv[PATH_MAX+1];
char FullPathImgPriv[PATH_MAX+1];
char URL[PATH_MAX+1];
/***** If no image to show ==> nothing to do *****/
if (!ImageName)
if (!Image->Name)
return;
if (!ImageName[0])
if (!Image->Name[0])
return;
if (Gbl.Image.Status != Img_NAME_STORED_IN_DB)
if (Image->Status != Img_NAME_STORED_IN_DB)
return;
/***** Create a temporary public directory
@ -301,11 +303,11 @@ void Img_ShowImage (const char *ImageName,const char *ClassImg)
Brw_CreateDirDownloadTmp ();
/***** Build private path to image *****/
sprintf (FileNameImgPriv,"%s.jpg",ImageName);
sprintf (FileNameImgPriv,"%s.jpg",Image->Name);
sprintf (FullPathImgPriv,"%s/%s/%c%c/%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG,
ImageName[0],
ImageName[1],
Image->Name[0],
Image->Name[1],
FileNameImgPriv);
/***** Create symbolic link from temporary public directory to private file

View File

@ -34,11 +34,23 @@
/*****************************************************************************/
/******************************* Public types ********************************/
/*****************************************************************************/
/***** Action to perform when editing a form with an image *****/
#define Img_NUM_ACTIONS 4
typedef enum
{
Img_ACTION_NO_IMAGE, // Do not use image (remove current image if exists)
Img_ACTION_KEEP_IMAGE, // Keep current image unchanged
Img_ACTION_NEW_IMAGE, // Upload new image
Img_ACTION_CHANGE_IMAGE, // Change existing image by a new image
} Img_Action_t;
/***** Status of an image file *****/
/*
No image Original file Temporary Definitive Name of the image
uploaded uploaded by user processed image processed image stored in database
--------- --------------------------- ------------------ ------------------ ---------------------
Img_NONE Img_NONE Img_FILE_PROCESSED Img_FILE_MOVED Img_NAME_STORED_IN_DB
Img_NONE Img_FILE_RECEIVED Img_FILE_PROCESSED Img_FILE_MOVED Img_NAME_STORED_IN_DB
--------- --------------------------- ------------------ ------------------ ---------------------
-> upload-file -> -> process-file -> b -> move-file -> -> insert-name ->
--------- --------------------------- ------------------ ------------------ ---------------------
@ -67,30 +79,30 @@ typedef enum
Img_NAME_STORED_IN_DB,
} Img_FileStatus_t;
/***** Action to perform when editing a form with an image *****/
#define Img_NUM_ACTIONS 4
typedef enum
/***** Struct used to get images from forms *****/
struct Image
{
Img_ACTION_NO_IMAGE, // Do not use image (remove current image if exists)
Img_ACTION_KEEP_IMAGE, // Keep current image unchanged
Img_ACTION_NEW_IMAGE, // Upload new image
Img_ACTION_CHANGE_IMAGE, // Change existing image by a new image
} Img_Action_t;
Img_Action_t Action;
Img_FileStatus_t Status;
char Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64+1];
};
/*****************************************************************************/
/***************************** Public prototypes *****************************/
/*****************************************************************************/
void Img_GetImageFromForm (char *ImageName,void (*GetImageName) (void),
const char *ParamFile,
void Img_GetImageFromForm (unsigned NumOpt,struct Image *Image,
void (*GetImageName) (unsigned NumOpt,char *ImageName),
const char *ParamAction,const char *ParamFile,
unsigned Width,unsigned Height,unsigned Quality);
Img_Action_t Img_GetImageActionFromForm (const char *ParamRadio);
void Img_GetAndProcessImageFileFromForm (const char *ParamFile,
Img_Action_t Img_GetImageActionFromForm (const char *ParamAction);
void Img_GetAndProcessImageFileFromForm (struct Image *Image,
const char *ParamFile,
unsigned Width,unsigned Height,
unsigned Quality);
void Img_MoveImageToDefinitiveDirectory (void);
void Img_ShowImage (const char *ImageName,const char *ClassImg);
void Img_MoveImageToDefinitiveDirectory (struct Image *Image);
void Img_ShowImage (struct Image *Image,const char *ClassImg);
void Img_RemoveImageFile (const char *ImageName);
#endif

View File

@ -155,8 +155,8 @@ static void Tst_ShowTestQuestionsWhenSeeing (MYSQL_RES *mysql_res);
static void Tst_ShowTstResultAfterAssess (long TstCod,unsigned *NumQstsNotBlank,double *TotalScore);
static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row,
double *ScoreThisQst,bool *AnswerIsNotBlank);
static void Tst_PutFormToEditQstImage (const char *ImageName,
const char *ParamRadio,
static void Tst_PutFormToEditQstImage (struct Image *Image,
const char *ParamAction,
const char *ParamFile);
static void Tst_UpdateScoreQst (long QstCod,float ScoreThisQst,bool AnswerIsNotBlank);
static void Tst_UpdateMyNumAccessTst (unsigned NumAccessesTst);
@ -217,7 +217,7 @@ static int Tst_CountNumAnswerTypesInList (void);
static void Tst_PutFormEditOneQst (char *Stem,char *Feedback);
static void Tst_GetQstDataFromDB (char *Stem,char *Feedback);
static void Tst_GetImageNameFromDB (void);
static void Tst_GetImageNameFromDB (unsigned NumOpt,char *ImageName);
static Tst_AnswerType_t Tst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr);
static void Tst_GetQstFromForm (char *Stem,char *Feedback);
@ -982,8 +982,10 @@ static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row,
Tst_WriteQstStem (row[4],"TEST_EXA");
if (row[5][0])
{
Gbl.Image.Status = Img_NAME_STORED_IN_DB;
Img_ShowImage (row[5],"TEST_IMG_SHOW");
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
strncpy (Gbl.Test.Image.Name,row[5],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Image.Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_SHOW");
}
if (Gbl.Action.Act == ActSeeTst)
Tst_WriteAnswersOfAQstSeeExam (NumQst,QstCod,(Str_ConvertToUpperLetter (row[3][0]) == 'Y'));
@ -1029,8 +1031,8 @@ void Tst_WriteQstStem (const char *Stem,const char *ClassStem)
/************* Put form to upload a new image for a test question ************/
/*****************************************************************************/
static void Tst_PutFormToEditQstImage (const char *ImageName,
const char *ParamRadio,
static void Tst_PutFormToEditQstImage (struct Image *Image,
const char *ParamAction,
const char *ParamFile)
{
extern const char *The_ClassForm[The_NUM_THEMES];
@ -1042,8 +1044,8 @@ static void Tst_PutFormToEditQstImage (const char *ImageName,
/***** No image *****/
fprintf (Gbl.F.Out,"<input type=\"radio\" name=\"%s\" value=\"%u\"",
ParamRadio,Img_ACTION_NO_IMAGE);
if (!ImageName[0])
ParamAction,Img_ACTION_NO_IMAGE);
if (!Image->Name[0])
fprintf (Gbl.F.Out," checked=\"checked\"");
fprintf (Gbl.F.Out," />"
"<label class=\"%s\">"
@ -1053,43 +1055,38 @@ static void Tst_PutFormToEditQstImage (const char *ImageName,
Txt_No_image);
/***** Current image *****/
if (ImageName[0])
if (Image->Name[0])
{
fprintf (Gbl.F.Out,"<input type=\"radio\" name=\"%s\" value=\"%u\" checked=\"checked\" />"
"<label class=\"%s\">"
"%s"
"</label><br />",
ParamRadio,Img_ACTION_KEEP_IMAGE,
ParamAction,Img_ACTION_KEEP_IMAGE,
The_ClassForm[Gbl.Prefs.Theme],
Txt_Current_image);
Img_ShowImage (ImageName,"TEST_IMG_EDIT_ONE");
Img_ShowImage (Image,"TEST_IMG_EDIT_ONE");
}
/***** Change/new image *****/
UniqueId++;
if (ImageName[0]) // Image exists
{
if (Image->Name[0]) // Image exists
/***** Change image *****/
fprintf (Gbl.F.Out,"<input type=\"radio\" id=\"chg_img_%u\" name=\"%s\""
" value=\"%u\">",
UniqueId,ParamRadio,Img_ACTION_CHANGE_IMAGE); // Replace existing image by new image
fprintf (Gbl.F.Out,"<label class=\"%s\">"
" value=\"%u\">"
"<label class=\"%s\">"
"%s: "
"</label>",
UniqueId,ParamAction,Img_ACTION_CHANGE_IMAGE, // Replace existing image by new image
The_ClassForm[Gbl.Prefs.Theme],Txt_Change_image);
}
else // Image does not exist
{
/***** New image *****/
fprintf (Gbl.F.Out,"<input type=\"radio\" id=\"chg_img_%u\" name=\"%s\""
" value=\"%u\">",
UniqueId,ParamRadio,Img_ACTION_NEW_IMAGE); // Upload new image
fprintf (Gbl.F.Out,"<label class=\"%s\">"
" value=\"%u\">"
"<label class=\"%s\">"
"%s: "
"</label>",
UniqueId,ParamAction,Img_ACTION_NEW_IMAGE, // Upload new image
The_ClassForm[Gbl.Prefs.Theme],Txt_New_image);
}
fprintf (Gbl.F.Out,"<input type=\"file\" name=\"%s\""
" size=\"40\" maxlength=\"100\" value=\"\""
" onchange=\"document.getElementById('chg_img_%u').checked = true;\" />",
@ -2735,11 +2732,12 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP COLOR%u\">",
Gbl.RowEvenOdd);
Tst_WriteQstStem (row[4],"TEST_EDI");
Gbl.Image.Status = Img_NAME_STORED_IN_DB;
if (row[5][0])
{
Gbl.Image.Status = Img_NAME_STORED_IN_DB;
Img_ShowImage (row[5],"TEST_IMG_EDIT_LIST");
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
strncpy (Gbl.Test.Image.Name,row[5],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Image.Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_EDIT_LIST");
}
Tst_WriteQstFeedback (row[6],"TEST_EDI_LIGHT");
Tst_WriteAnswersOfAQstEdit (QstCod);
@ -4243,7 +4241,7 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
unsigned NumTag;
bool TagNotFound;
bool OptionsDisabled;
char ParamRadio[32];
char ParamAction[32];
char ParamFile[32];
/***** If no receiving the question, but editing a new or existing question
@ -4356,8 +4354,8 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
The_ClassForm[Gbl.Prefs.Theme],
Txt_Stem,
Stem);
Tst_PutFormToEditQstImage (Gbl.Test.Image,
"ImgAct",Fil_NAME_OF_PARAM_FILENAME_ORG);
Tst_PutFormToEditQstImage (&Gbl.Test.Image,
"ImgAct","FileImg");
fprintf (Gbl.F.Out,"</td>"
"</tr>");
@ -4558,9 +4556,9 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
/* Image */
fprintf (Gbl.F.Out,"<tr>"
"<td colspan=\"2\" class=\"LEFT_TOP\">");
sprintf (ParamRadio,"ImgAct%u",NumOpt);
sprintf (ParamAction,"ImgAct%u",NumOpt);
sprintf (ParamFile,"FileImg%u",NumOpt);
Tst_PutFormToEditQstImage (Gbl.Test.Answer.Options[NumOpt].Image,ParamRadio,ParamFile);
Tst_PutFormToEditQstImage (&Gbl.Test.Answer.Options[NumOpt].Image,ParamAction,ParamFile);
// if (OptionsDisabled)
// fprintf (Gbl.F.Out," disabled=\"disabled\"");
fprintf (Gbl.F.Out,"</td>"
@ -4603,7 +4601,9 @@ void Tst_InitQst (void)
Gbl.Test.Stem.Length = 0;
Gbl.Test.Feedback.Text = NULL;
Gbl.Test.Feedback.Length = 0;
Gbl.Test.Image[0] = '\0';
Gbl.Test.Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Test.Image.Status = Img_FILE_NONE;
Gbl.Test.Image.Name[0] = '\0';
Gbl.Test.Shuffle = false;
Gbl.Test.AnswerType = Tst_ANS_UNIQUE_CHOICE;
Gbl.Test.Answer.NumOptions = 0;
@ -4615,7 +4615,9 @@ void Tst_InitQst (void)
Gbl.Test.Answer.Options[NumOpt].Correct = false;
Gbl.Test.Answer.Options[NumOpt].Text = NULL;
Gbl.Test.Answer.Options[NumOpt].Feedback = NULL;
Gbl.Test.Answer.Options[NumOpt].Image[0] = '\0';
Gbl.Test.Answer.Options[NumOpt].Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_FILE_NONE;
Gbl.Test.Answer.Options[NumOpt].Image.Name[0] = '\0';
}
Gbl.Test.Answer.Integer = 0;
Gbl.Test.Answer.FloatingPoint[0] =
@ -4658,12 +4660,12 @@ static void Tst_GetQstDataFromDB (char *Stem,char *Feedback)
/* Get the image of the question from the database (row[3]) */
if (row[3][0])
{
Gbl.Image.Status = Img_NAME_STORED_IN_DB;
strncpy (Gbl.Test.Image,row[3],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Image[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
strncpy (Gbl.Test.Image.Name,row[3],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Image.Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
}
else
Gbl.Image.Status = Img_FILE_NONE;
Gbl.Test.Image.Status = Img_FILE_NONE;
/* Get the feedback of the question from the database (row[4]) */
Feedback[0] = '\0';
@ -4747,31 +4749,41 @@ static void Tst_GetQstDataFromDB (char *Stem,char *Feedback)
/*****************************************************************************/
/***** Get possible image associated with a test question from database ******/
/*****************************************************************************/
// NumOpt >= Tst_MAX_OPTIONS_PER_QUESTION ==> image associated to stem
// 0 <= NumOpt < Tst_MAX_OPTIONS_PER_QUESTION ==> image associated to answer
static void Tst_GetImageNameFromDB (void)
static void Tst_GetImageNameFromDB (unsigned NumOpt,char *ImageName)
{
char Query[256];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
/***** Get possible image associated with a test question from database *****/
sprintf (Query,"SELECT Image FROM tst_questions"
" WHERE QstCod='%ld' AND CrsCod='%ld'",
Gbl.Test.QstCod,Gbl.CurrentCrs.Crs.CrsCod);
DB_QuerySELECT (Query,&mysql_res,"can not get a question");
/***** Build query depending on NumOpt *****/
if (NumOpt < Tst_MAX_OPTIONS_PER_QUESTION)
// Get image associated to answer
sprintf (Query,"SELECT Image FROM tst_answers"
" WHERE QstCod='%ld' AND AnsInd='%u'",
Gbl.Test.QstCod,NumOpt);
else
// Get image associated to stem
sprintf (Query,"SELECT Image FROM tst_questions"
" WHERE QstCod='%ld' AND CrsCod='%ld'",
Gbl.Test.QstCod,Gbl.CurrentCrs.Crs.CrsCod);
/***** Query database *****/
DB_QuerySELECT (Query,&mysql_res,"can not get image name");
row = mysql_fetch_row (mysql_res);
/* Get the image of the question from the database (row[0]) */
/***** Get the image of the question from the database (row[0]) *****/
if (row[0][0]) // Image name stored in database
{
Gbl.Image.Status = Img_NAME_STORED_IN_DB;
strncpy (Gbl.Test.Image,row[0],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Image[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
strncpy (ImageName,row[0],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
ImageName[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
}
else // No image in this question
Gbl.Image.Status = Img_FILE_NONE;
ImageName[0] = '\0';
/* Free structure that stores the query result */
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
@ -4828,17 +4840,17 @@ void Tst_ReceiveQst (void)
/***** Make sure that tags, text and answer are not empty *****/
if (Tst_CheckIfQstFormatIsCorrectAndCountNumOptions ())
{
if (Gbl.Image.Action != Img_ACTION_KEEP_IMAGE) // Don't keep the current image
if (Gbl.Test.Image.Action != Img_ACTION_KEEP_IMAGE) // Don't keep the current image
/* Remove possible file with the old image
(the new image file is already processed
and moved to the definitive directory) */
Tst_RemoveImageFilesFromQstsInCrs (Gbl.CurrentCrs.Crs.CrsCod,Gbl.Test.QstCod);
if ((Gbl.Image.Action == Img_ACTION_NEW_IMAGE || // Upload new image
Gbl.Image.Action == Img_ACTION_CHANGE_IMAGE) && // Replace existing image by new image
Gbl.Image.Status == Img_FILE_PROCESSED) // The new image received has been processed
if ((Gbl.Test.Image.Action == Img_ACTION_NEW_IMAGE || // Upload new image
Gbl.Test.Image.Action == Img_ACTION_CHANGE_IMAGE) && // Replace existing image by new image
Gbl.Test.Image.Status == Img_FILE_PROCESSED) // The new image received has been processed
/* Move processed image to definitive directory */
Img_MoveImageToDefinitiveDirectory ();
Img_MoveImageToDefinitiveDirectory (&Gbl.Test.Image);
/***** Insert or update question, tags and answer in the database *****/
Tst_InsertOrUpdateQstTagsAnsIntoDB ();
@ -4850,9 +4862,9 @@ void Tst_ReceiveQst (void)
{
/***** Whether an image has been received or not,
reset status and image *****/
Gbl.Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Image.Status = Img_FILE_NONE;
Gbl.Test.Image[0] = '\0';
Gbl.Test.Image.Action = Img_ACTION_NO_IMAGE;
Gbl.Test.Image.Status = Img_FILE_NONE;
Gbl.Test.Image.Name[0] = '\0';
/***** Put form to edit question again *****/
Tst_PutFormEditOneQst (Stem,Feedback);
@ -4879,6 +4891,8 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback)
char StrMultiAns[Tst_MAX_SIZE_ANSWERS_ONE_QST+1];
const char *Ptr;
unsigned NumCorrectAns;
char ParamAction[32];
char ParamFile[32];
/***** Get question code *****/
Tst_GetQstCod ();
@ -4914,9 +4928,10 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback)
/***** Get question stem *****/
Par_GetParToHTML ("Stem",Stem,Cns_MAX_BYTES_TEXT);
/***** Get image associated to stem *****/
Img_GetImageFromForm (Gbl.Test.Image,Tst_GetImageNameFromDB,
Fil_NAME_OF_PARAM_FILENAME_ORG,
/***** Get image associated to the stem *****/
Img_GetImageFromForm (Tst_MAX_OPTIONS_PER_QUESTION,&Gbl.Test.Image,
Tst_GetImageNameFromDB,
"ImgAct","FileImg",
Tst_PHOTO_SAVED_MAX_WIDTH,
Tst_PHOTO_SAVED_MAX_HEIGHT,
Tst_PHOTO_SAVED_QUALITY);
@ -4970,6 +4985,20 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback)
/* Get feedback */
sprintf (FbStr,"FbStr%u",NumOpt);
Par_GetParToHTML (FbStr,Gbl.Test.Answer.Options[NumOpt].Feedback,Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
/* Get image associated to the answer */
if (Gbl.Test.AnswerType == Tst_ANS_UNIQUE_CHOICE ||
Gbl.Test.AnswerType == Tst_ANS_MULTIPLE_CHOICE)
{
sprintf (ParamAction,"ImgAct%u",NumOpt);
sprintf (ParamFile,"FileImg%u",NumOpt);
Img_GetImageFromForm (NumOpt,&Gbl.Test.Answer.Options[NumOpt].Image,
Tst_GetImageNameFromDB,
ParamAction,ParamFile,
Tst_PHOTO_SAVED_MAX_WIDTH,
Tst_PHOTO_SAVED_MAX_HEIGHT,
Tst_PHOTO_SAVED_QUALITY);
}
}
/* Get the numbers of correct answers */
@ -5047,9 +5076,9 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void)
bool ThereIsEndOfAnswers;
unsigned i;
if ((Gbl.Image.Action == Img_ACTION_NEW_IMAGE || // Upload new image
Gbl.Image.Action == Img_ACTION_CHANGE_IMAGE) && // Replace existing image by new image
Gbl.Image.Status != Img_FILE_PROCESSED)
if ((Gbl.Test.Image.Action == Img_ACTION_NEW_IMAGE || // Upload new image
Gbl.Test.Image.Action == Img_ACTION_CHANGE_IMAGE) && // Replace existing image by new image
Gbl.Test.Image.Status != Img_FILE_PROCESSED)
{
Lay_ShowAlert (Lay_WARNING,Txt_Error_receiving_or_processing_image);
return false;
@ -5487,13 +5516,13 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
Gbl.Test.Shuffle ? 'Y' :
'N',
Gbl.Test.Stem.Text,
Gbl.Test.Image,
Gbl.Test.Image.Name,
Gbl.Test.Feedback.Text ? Gbl.Test.Feedback.Text : "");
Gbl.Test.QstCod = DB_QueryINSERTandReturnCode (Query,"can not create question");
/* Update image status */
if (Gbl.Test.Image[0])
Gbl.Image.Status = Img_NAME_STORED_IN_DB;
if (Gbl.Test.Image.Name[0])
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
}
else // It's an existing question
{
@ -5507,13 +5536,13 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
Gbl.Test.Shuffle ? 'Y' :
'N',
Gbl.Test.Stem.Text,
Gbl.Test.Image,
Gbl.Test.Image.Name,
Gbl.Test.Feedback.Text ? Gbl.Test.Feedback.Text : "",
Gbl.Test.QstCod,Gbl.CurrentCrs.Crs.CrsCod);
/* Update image status */
if (Gbl.Test.Image[0])
Gbl.Image.Status = Img_NAME_STORED_IN_DB;
if (Gbl.Test.Image.Name[0])
Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB;
DB_QueryUPDATE (Query,"can not update question");
/* Remove answers and tags from this test question */