diff --git a/css/swad15.178.2.css b/css/swad15.178.2.css index 85479350c..5fe62d52c 100644 --- a/css/swad15.178.2.css +++ b/css/swad15.178.2.css @@ -1743,12 +1743,12 @@ a:hover img.CENTRE_PHOTO_SHOW .TEST_EXA_LIGHT {color:#A0A0A0; font-size:12pt;} .TEST_EDI {color:#404040; font-size:12pt;} .TEST_EDI_LIGHT {color:#A0A0A0; font-size:12pt;} -.TEST_IMG_SHOW_STEM {width:600px; margin:10px 0;} -.TEST_IMG_SHOW_ANS {width:450px; margin:10px 0;} -.TEST_IMG_EDIT_LIST_STEM {width:300px; margin:5px 0;} -.TEST_IMG_EDIT_LIST_ANS {width:225px; margin:5px 0;} -.TEST_IMG_EDIT_ONE_STEM {width:600px;} -.TEST_IMG_EDIT_ONE_ANS {width:450px;} +.TEST_IMG_SHOW_STEM {width:600px; border-radius:4px; margin:10px 0;} +.TEST_IMG_SHOW_ANS {width:450px; border-radius:4px; margin:10px 0;} +.TEST_IMG_EDIT_LIST_STEM {width:300px; border-radius:2px; margin:5px 0;} +.TEST_IMG_EDIT_LIST_ANS {width:225px; border-radius:2px; margin:5px 0;} +.TEST_IMG_EDIT_ONE_STEM {width:600px; border-radius:4px;} +.TEST_IMG_EDIT_ONE_ANS {width:450px; border-radius:4px;} /******************************* Time table **********************************/ #timetable diff --git a/swad_changelog.h b/swad_changelog.h index 7874a7841..aa698afa3 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -132,13 +132,15 @@ /****************************** Public constants *****************************/ /*****************************************************************************/ -#define Log_PLATFORM_VERSION "SWAD 15.183.1 (2016-04-06)" +#define Log_PLATFORM_VERSION "SWAD 15.184 (2016-04-06)" #define CSS_FILE "swad15.178.2.css" #define JS_FILE "swad15.178.2.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.184: Apr 06, 2016 Code refactoring in tests and images. + Fixed bugs in test questions. (198682 lines) Version 15.183.1: Apr 06, 2016 Fix bug in feedback of test question. (198706 lines) Version 15.183: Apr 06, 2016 Change in length of title/attribution of images. (198704 lines) 2 changes necessary in database: diff --git a/swad_image.c b/swad_image.c index c09d8f91d..2b5370a40 100644 --- a/swad_image.c +++ b/swad_image.c @@ -71,45 +71,47 @@ static void Img_ProcessImage (const char *FileNameImgOriginal, /*************************** Reset image title *******************************/ /*****************************************************************************/ -void Img_ResetImageTitle (struct Image *Image) +void Img_FreeImageTitle (struct Image *Image) { + // Image->Title must be initialized to NULL if (Image->Title) + { free ((void *) Image->Title); - Image->Title = NULL; + Image->Title = NULL; + } } /*****************************************************************************/ -/********* Get image name and title from strings and copy to struct **********/ +/****** Get image name and title from a query result and copy to struct ******/ /*****************************************************************************/ -void Img_GetImageNameAndTitle (const char *Name,const char *Title, - struct Image *Image) +void Img_GetImageNameAndTitleFromRow (const char *Name,const char *Title, + struct Image *Image) { size_t Length; - Img_ResetImageTitle (Image); + /***** Copy image name to struct *****/ + strncpy (Image->Name,Name,Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64); + Image->Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0'; - if (Name[0]) + /***** Set status of image file *****/ + Image->Status = Image->Name[0] ? Img_NAME_STORED_IN_DB : + Img_FILE_NONE; + + /***** Copy image title to struct *****/ + Img_FreeImageTitle (Image); + if (Title[0]) { - strncpy (Image->Name,Name,Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64); - Image->Name[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0'; + /* Get and limit length of the title */ + Length = strlen (Title); + if (Length > Img_MAX_BYTES_TITLE) + Length = Img_MAX_BYTES_TITLE; - if (Image->Name[0]) // There is an image - if (Title[0]) - { - /* Get and limit length of the title */ - Length = strlen (Title); - if (Length > Img_MAX_BYTES_TITLE) - Length = Img_MAX_BYTES_TITLE; - - if ((Image->Title = (char *) malloc (Length+1)) == NULL) - Lay_ShowErrorAndExit ("Error allocating memory for image title."); - strncpy (Image->Title,Title,Length); - Image->Title[Length] = '\0'; - } + if ((Image->Title = (char *) malloc (Length+1)) == NULL) + Lay_ShowErrorAndExit ("Error allocating memory for image title."); + strncpy (Image->Title,Title,Length); + Image->Title[Length] = '\0'; } - else // No image in this question - Image->Name[0] = '\0'; } /*****************************************************************************/ @@ -117,50 +119,60 @@ void Img_GetImageNameAndTitle (const char *Name,const char *Title, /*****************************************************************************/ void Img_GetImageFromForm (unsigned NumOpt,struct Image *Image, - void (*GetImageNameFromDB) (unsigned NumOpt,struct Image *Image), + void (*GetImageFromDB) (unsigned NumOpt,struct Image *Image), const char *ParamAction,const char *ParamFile,const char *ParamTitle, unsigned Width,unsigned Height,unsigned Quality) { + char Title[Img_MAX_BYTES_TITLE+1]; + size_t Length; + Image->Action = Img_GetImageActionFromForm (ParamAction); Image->Status = Img_FILE_NONE; + Img_FreeImageTitle (Image); // Reset to NULL + switch (Image->Action) { case Img_ACTION_NO_IMAGE: // Do not use image (remove current image if exists) /***** Reset image name *****/ Image->Name[0] = '\0'; - Img_ResetImageTitle (Image); break; case Img_ACTION_KEEP_IMAGE: // Keep current image unchanged /***** Get image name *****/ - GetImageNameFromDB (NumOpt,Image); - if (Image->Name[0]) - Image->Status = Img_NAME_STORED_IN_DB; + GetImageFromDB (NumOpt,Image); break; case Img_ACTION_NEW_IMAGE: // Upload new image /***** Get new image (if present ==> process and create temporary file) *****/ - Img_GetAndProcessImageFileFromForm (Image,ParamFile,ParamTitle, + Img_GetAndProcessImageFileFromForm (Image,ParamFile, Width,Height,Quality); if (Image->Status != Img_FILE_PROCESSED) // No new image received-processed successfully { /* Reset image name */ Image->Status = Img_FILE_NONE; Image->Name[0] = '\0'; - Img_ResetImageTitle (Image); } break; case Img_ACTION_CHANGE_IMAGE: // Replace old image by new image /***** Get new image (if present ==> process and create temporary file) *****/ - Img_GetAndProcessImageFileFromForm (Image,ParamFile,ParamTitle, + Img_GetAndProcessImageFileFromForm (Image,ParamFile, Width,Height,Quality); if (Image->Status != Img_FILE_PROCESSED) // No new image received-processed successfully - { /* Get image name */ - GetImageNameFromDB (NumOpt,Image); - Image->Status = (Image->Name[0] ? Img_NAME_STORED_IN_DB : - Img_FILE_NONE); - } + GetImageFromDB (NumOpt,Image); break; } + + /***** Get image title from form *****/ + Par_GetParToHTML (ParamTitle,Title,Img_MAX_BYTES_TITLE); + if ((Length = strlen (Title)) > 0) // If title comming from the form is not empty + { + /* Overwrite current title (empty or coming from database) + with the title coming from the form */ + Img_FreeImageTitle (Image); + if ((Image->Title = (char *) malloc (Length + 1)) == NULL) + Lay_ShowErrorAndExit ("Error allocating memory for image title."); + strncpy (Image->Title,Title,Length); + Image->Title[Length] = '\0'; + } } /*****************************************************************************/ @@ -183,12 +195,9 @@ Img_Action_t Img_GetImageActionFromForm (const char *ParamAction) /*****************************************************************************/ /**************************** Get image from form ****************************/ /*****************************************************************************/ -// Return true if image is created -void Img_GetAndProcessImageFileFromForm (struct Image *Image, - const char *ParamFile,const char *ParamTitle, - unsigned Width,unsigned Height, - unsigned Quality) +void Img_GetAndProcessImageFileFromForm (struct Image *Image,const char *ParamFile, + unsigned Width,unsigned Height,unsigned Quality) { struct Param *Param; char FileNameImgSrc[PATH_MAX+1]; @@ -199,8 +208,6 @@ void Img_GetAndProcessImageFileFromForm (struct Image *Image, char FileNameImgOrig[PATH_MAX+1]; // Full name of original uploaded file char FileNameImgTmp[PATH_MAX+1]; // Full name of temporary processed file bool WrongType = false; - char Title[Img_MAX_BYTES_TITLE+1]; - size_t Length; /***** Rest image file status *****/ Image->Status = Img_FILE_NONE; @@ -265,17 +272,6 @@ void Img_GetAndProcessImageFileFromForm (struct Image *Image, /***** Remove temporary original file *****/ unlink (FileNameImgOrig); - - /***** Get image title from form *****/ - Par_GetParToHTML (ParamTitle,Title,Img_MAX_BYTES_TITLE); // TODO: Create a function to get only the length of a parameter - Length = strlen (Title); - if (Length > 0) - { - if ((Image->Title = (char *) malloc (Length+1)) == NULL) - Lay_ShowErrorAndExit ("Error allocating memory for image title."); - strncpy (Image->Title,Title,Length); - Image->Title[Length] = '\0'; - } } } diff --git a/swad_image.h b/swad_image.h index 257dfc3fd..987948425 100644 --- a/swad_image.h +++ b/swad_image.h @@ -94,19 +94,17 @@ struct Image /***************************** Public prototypes *****************************/ /*****************************************************************************/ -void Img_ResetImageTitle (struct Image *Image); -void Img_GetImageNameAndTitle (const char *Name,const char *Title, - struct Image *Image); +void Img_FreeImageTitle (struct Image *Image); +void Img_GetImageNameAndTitleFromRow (const char *Name,const char *Title, + struct Image *Image); void Img_GetImageFromForm (unsigned NumOpt,struct Image *Image, - void (*GetImageNameFromDB) (unsigned NumOpt,struct Image *Image), + void (*GetImageFromDB) (unsigned NumOpt,struct Image *Image), const char *ParamAction,const char *ParamFile,const char *ParamTitle, unsigned Width,unsigned Height,unsigned Quality); Img_Action_t Img_GetImageActionFromForm (const char *ParamAction); -void Img_GetAndProcessImageFileFromForm (struct Image *Image, - const char *ParamFile,const char *ParamTitle, - unsigned Width,unsigned Height, - unsigned Quality); +void Img_GetAndProcessImageFileFromForm (struct Image *Image,const char *ParamFile, + unsigned Width,unsigned Height,unsigned Quality); void Img_MoveImageToDefinitiveDirectory (struct Image *Image); void Img_ShowImage (struct Image *Image,const char *ClassImg); diff --git a/swad_survey.c b/swad_survey.c index 16dbaaef6..d06a24067 100644 --- a/swad_survey.c +++ b/swad_survey.c @@ -2527,7 +2527,7 @@ static unsigned Svy_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res) /***** Count number of rows of result *****/ if (NumRows == 0) - Lay_ShowErrorAndExit ("Error when getting answers of a question."); + Lay_ShowAlert (Lay_ERROR,"Error when getting answers of a question."); return (unsigned) NumRows; } @@ -3024,64 +3024,67 @@ static void Svy_WriteAnswersOfAQst (struct Survey *Svy,struct SurveyQuestion *Sv NumAnswers = Svy_GetAnswersQst (SvyQst->QstCod,&mysql_res); // Result: AnsInd,NumUsrs,Answer /***** Write the answers *****/ - fprintf (Gbl.F.Out,""); - for (NumAns = 0; - NumAns < NumAnswers; - NumAns++) + if (NumAnswers) { - row = mysql_fetch_row (mysql_res); + fprintf (Gbl.F.Out,"
"); + for (NumAns = 0; + NumAns < NumAnswers; + NumAns++) + { + row = mysql_fetch_row (mysql_res); - /* Get number of users who have marked this answer (row[1]) */ - if (sscanf (row[1],"%u",&NumUsrsThisAnswer) != 1) - Lay_ShowErrorAndExit ("Error when getting number of users who have marked an answer."); + /* Get number of users who have marked this answer (row[1]) */ + if (sscanf (row[1],"%u",&NumUsrsThisAnswer) != 1) + Lay_ShowErrorAndExit ("Error when getting number of users who have marked an answer."); - /* Convert the answer (row[2]), that is in HTML, to rigorous HTML */ - AnsLength = strlen (row[2]) * Str_MAX_LENGTH_SPEC_CHAR_HTML; - if ((Answer = malloc (AnsLength+1)) == NULL) - Lay_ShowErrorAndExit ("Not enough memory to store answer."); - strcpy (Answer,row[2]); - Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML, - Answer,AnsLength,false); + /* Convert the answer (row[2]), that is in HTML, to rigorous HTML */ + AnsLength = strlen (row[2]) * Str_MAX_LENGTH_SPEC_CHAR_HTML; + if ((Answer = malloc (AnsLength+1)) == NULL) + Lay_ShowErrorAndExit ("Not enough memory to store answer."); + strcpy (Answer,row[2]); + Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML, + Answer,AnsLength,false); - /* Selectors and label with the letter of the answer */ - fprintf (Gbl.F.Out,""); + /* Selectors and label with the letter of the answer */ + fprintf (Gbl.F.Out,""); - if (PutFormAnswerSurvey) - { - /* Write selector to choice this answer */ - fprintf (Gbl.F.Out,"", - (unsigned) SvyQst->QstCod,NumAns); - } + if (PutFormAnswerSurvey) + { + /* Write selector to choice this answer */ + fprintf (Gbl.F.Out,"", + (unsigned) SvyQst->QstCod,NumAns); + } - /* Write the number of option */ - fprintf (Gbl.F.Out,"", - NumAns + 1); + /* Write the number of option */ + fprintf (Gbl.F.Out,"", + NumAns + 1); - /* Write the text of the answer */ - fprintf (Gbl.F.Out,"", - Answer); + /* Write the text of the answer */ + fprintf (Gbl.F.Out,"", + Answer); - /* Show stats of this answer */ - if (Svy->Status.ICanViewResults) - Svy_DrawBarNumUsrs (NumUsrsThisAnswer,Svy->NumUsrs); + /* Show stats of this answer */ + if (Svy->Status.ICanViewResults) + Svy_DrawBarNumUsrs (NumUsrsThisAnswer,Svy->NumUsrs); - fprintf (Gbl.F.Out,""); + fprintf (Gbl.F.Out,""); - /* Free memory allocated for the answer */ - free ((void *) Answer); + /* Free memory allocated for the answer */ + free ((void *) Answer); + } + fprintf (Gbl.F.Out,"
" - "AnswerType == Svy_ANS_UNIQUE_CHOICE) - fprintf (Gbl.F.Out,"radio\"" - " onclick=\"selectUnselectRadio(this,this.form.Ans%010u,%u)\"", - (unsigned) SvyQst->QstCod,NumAnswers); - else // SvyQst->AnswerType == Svy_ANS_MULTIPLE_CHOICE - fprintf (Gbl.F.Out,"checkbox\""); - fprintf (Gbl.F.Out," name=\"Ans%010u\" value=\"%u\" />" - "" + "AnswerType == Svy_ANS_UNIQUE_CHOICE) + fprintf (Gbl.F.Out,"radio\"" + " onclick=\"selectUnselectRadio(this,this.form.Ans%010u,%u)\"", + (unsigned) SvyQst->QstCod,NumAnswers); + else // SvyQst->AnswerType == Svy_ANS_MULTIPLE_CHOICE + fprintf (Gbl.F.Out,"checkbox\""); + fprintf (Gbl.F.Out," name=\"Ans%010u\" value=\"%u\" />" + "" - "%u)" - "" + "%u)" + "%s%s
"); } - fprintf (Gbl.F.Out,""); /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); diff --git a/swad_test.c b/swad_test.c index af850deed..56d32596c 100644 --- a/swad_test.c +++ b/swad_test.c @@ -219,6 +219,9 @@ static int Tst_CountNumTagsInList (void); static int Tst_CountNumAnswerTypesInList (void); static void Tst_PutFormEditOneQst (char *Stem,char *Feedback); +static void Tst_FreeTextChoiceAnswers (void); +static void Tst_FreeTextChoiceAnswer (unsigned NumOpt); + static void Tst_InitImagesOfQuestion (void); static void Tst_FreeImagesOfQuestion (void); @@ -236,7 +239,7 @@ static void Tst_EnableOrDisableTag (long TagCod,bool TagHidden); static void Tst_PutIconToRemoveOneQst (void); static void Tst_PutParamsRemoveOneQst (void); -static bool Tst_GetQstCod (void); +static long Tst_GetQstCod (void); static void Tst_InsertOrUpdateQstIntoDB (void); static void Tst_InsertTagsIntoDB (void); @@ -249,8 +252,6 @@ static void Tst_RemoveUnusedTagsFromCurrentCrs (void); static void Tst_RemoveImgFilesFromStemOfQsts (long CrsCod,long QstCod); static void Tst_RemoveImgFilesFromAnsOfQsts (long CrsCod,long QstCod,unsigned AnsInd); -static void Tst_FreeTextChoiceAnswer (unsigned NumOpt); - static unsigned Tst_GetNumTstQuestions (Sco_Scope_t Scope,Tst_AnswerType_t AnsType,struct Tst_Stats *Stats); static unsigned Tst_GetNumCoursesWithTstQuestions (Sco_Scope_t Scope,Tst_AnswerType_t AnsType); static unsigned Tst_GetNumCoursesWithPluggableTstQuestions (Sco_Scope_t Scope,Tst_AnswerType_t AnsType); @@ -981,6 +982,10 @@ static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row, row[10] Score */ + /***** Create test question *****/ + Tst_QstConstructor (); + Gbl.Test.QstCod = QstCod; + /***** Write number of question *****/ fprintf (Gbl.F.Out,"" "" @@ -999,12 +1004,9 @@ static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row, fprintf (Gbl.F.Out,"", Gbl.RowEvenOdd); Tst_WriteQstStem (row[4],"TEST_EXA"); - if (row[6][0]) - { - Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB; - Img_GetImageNameAndTitle (row[6],row[7],&Gbl.Test.Image); - Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_SHOW_STEM"); - } + Img_GetImageNameAndTitleFromRow (row[6],row[7],&Gbl.Test.Image); + Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_SHOW_STEM"); + if (Gbl.Action.Act == ActSeeTst) Tst_WriteAnswersOfAQstSeeExam (NumQst,QstCod,(Str_ConvertToUpperLetter (row[3][0]) == 'Y')); else // Assessing exam / Viewing old exam @@ -1018,9 +1020,8 @@ static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row, fprintf (Gbl.F.Out,"" ""); - /***** Free answers and images of this test question *****/ - Tst_FreeTextChoiceAnswers (); - Tst_FreeImagesOfQuestion (); + /***** Destroy test question *****/ + Tst_QstDestructor (); } /*****************************************************************************/ @@ -2615,7 +2616,6 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m Tst_QuestionsOrder_t Order; unsigned long NumRow; MYSQL_ROW row; - long QstCod; unsigned UniqueId; time_t TimeUTC; unsigned long NumHitsThisQst; @@ -2706,8 +2706,11 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m row[ 9] NumHitsNotBlank row[10] Score */ + /***** Create test question *****/ + Tst_QstConstructor (); + /* row[0] holds the code of the question */ - if ((QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0) + if ((Gbl.Test.QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0) Lay_ShowErrorAndExit ("Wrong code of question."); /* Write icon to remove the question */ @@ -2716,7 +2719,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m Act_FormStart (ActReqRemTstQst); Sta_WriteParamsDatesSeeAccesses (); Tst_WriteParamEditQst (); - Par_PutHiddenParamLong ("QstCod",QstCod); + Par_PutHiddenParamLong ("QstCod",Gbl.Test.QstCod); if (NumRows == 1) Par_PutHiddenParamChar ("OnlyThisQst",'Y'); // If there are only one row, don't list again after removing Lay_PutIconRemove (); @@ -2726,7 +2729,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m /* Write icon to edit the question */ fprintf (Gbl.F.Out,"",Gbl.RowEvenOdd); Act_FormStart (ActEdiOneTstQst); - Par_PutHiddenParamLong ("QstCod",QstCod); + Par_PutHiddenParamLong ("QstCod",Gbl.Test.QstCod); fprintf (Gbl.F.Out,"", @@ -2746,7 +2749,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m fprintf (Gbl.F.Out,"" "%ld " "", - Gbl.RowEvenOdd,QstCod); + Gbl.RowEvenOdd,Gbl.Test.QstCod); /* Write the date (row[1] has the UTC date-time) */ TimeUTC = Dat_GetUNIXTimeFromStr (row[1]); @@ -2762,7 +2765,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m /* Write the question tags */ fprintf (Gbl.F.Out,"", Gbl.RowEvenOdd); - Tst_GetAndWriteTagsQst (QstCod); + Tst_GetAndWriteTagsQst (Gbl.Test.QstCod); fprintf (Gbl.F.Out,""); /* Write the question type (row[2]) */ @@ -2780,7 +2783,7 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m Gbl.Test.AnswerType == Tst_ANS_MULTIPLE_CHOICE) { Act_FormStart (ActShfTstQst); - Par_PutHiddenParamLong ("QstCod",QstCod); + Par_PutHiddenParamLong ("QstCod",Gbl.Test.QstCod); Sta_WriteParamsDatesSeeAccesses (); Tst_WriteParamEditQst (); if (NumRows == 1) @@ -2800,20 +2803,12 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m fprintf (Gbl.F.Out,"", Gbl.RowEvenOdd); Tst_WriteQstStem (row[4],"TEST_EDI"); - if (row[6][0]) - { - Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB; - Img_GetImageNameAndTitle (row[6],row[7],&Gbl.Test.Image); - Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_EDIT_LIST_STEM"); - } + Img_GetImageNameAndTitleFromRow (row[6],row[7],&Gbl.Test.Image); + Img_ShowImage (&Gbl.Test.Image,"TEST_IMG_EDIT_LIST_STEM"); Tst_WriteQstFeedback (row[5],"TEST_EDI_LIGHT"); - Tst_WriteAnswersOfAQstEdit (QstCod); + Tst_WriteAnswersOfAQstEdit (Gbl.Test.QstCod); fprintf (Gbl.F.Out,""); - /* Free answers and images of this test question */ - Tst_FreeTextChoiceAnswers (); - Tst_FreeImagesOfQuestion (); - /* Get number of hits (number of times that the question has been answered, including blank answers) (row[8]) */ @@ -2865,6 +2860,9 @@ static void Tst_ListOneOrMoreQuestionsToEdit (unsigned long NumRows,MYSQL_RES *m fprintf (Gbl.F.Out,"N.A."); fprintf (Gbl.F.Out,"" ""); + + /***** Destroy test question *****/ + Tst_QstDestructor (); } /***** End table *****/ @@ -2901,7 +2899,7 @@ void Tst_WriteParamEditQst (void) unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle) { - char Query[256]; + char Query[512]; unsigned long NumRows; /***** Get answers of a question from database *****/ @@ -2911,7 +2909,7 @@ unsigned Tst_GetAnswersQst (long QstCod,MYSQL_RES **mysql_res,bool Shuffle) Shuffle ? "RAND(NOW())" : "AnsInd"); if (!(NumRows = DB_QuerySELECT (Query,mysql_res,"can not get answers of a question"))) - Lay_ShowErrorAndExit ("Error when getting answers of a question."); + Lay_ShowAlert (Lay_ERROR,"Error when getting answers of a question."); return (unsigned) NumRows; } @@ -3007,11 +3005,7 @@ static void Tst_WriteAnswersOfAQstEdit (long QstCod) } /* Copy image */ - if (row[3][0]) - { - Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB; - Img_GetImageNameAndTitle (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image); - } + Img_GetImageNameAndTitleFromRow (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image); /* Put an icon that indicates whether the answer is correct or wrong */ fprintf (Gbl.F.Out,"" @@ -3037,8 +3031,7 @@ static void Tst_WriteAnswersOfAQstEdit (long QstCod) "
" "%s", Answer); - if (Gbl.Test.Answer.Options[NumOpt].Image.Name[0]) - Img_ShowImage (&Gbl.Test.Answer.Options[NumOpt].Image,"TEST_IMG_EDIT_LIST_ANS"); + Img_ShowImage (&Gbl.Test.Answer.Options[NumOpt].Image,"TEST_IMG_EDIT_LIST_ANS"); fprintf (Gbl.F.Out,"
"); /* Write the text of the feedback */ @@ -3106,6 +3099,10 @@ static void Tst_WriteAnswersOfAQstAssessExam (unsigned NumQst,long QstCod, { MYSQL_RES *mysql_res; + /***** Create test question *****/ + Tst_QstConstructor (); + Gbl.Test.QstCod = QstCod; + /***** Get answers of a question from database *****/ Gbl.Test.Answer.NumOptions = Tst_GetAnswersQst (QstCod,&mysql_res,false); // Result: AnsInd,Answer,Correct /* @@ -3141,6 +3138,9 @@ static void Tst_WriteAnswersOfAQstAssessExam (unsigned NumQst,long QstCod, /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); + + /***** Destroy test question *****/ + Tst_QstDestructor (); } /*****************************************************************************/ @@ -3281,8 +3281,9 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle) bool ErrorInIndex = false; char ParamName[3+6+1]; - /***** Start of table *****/ - fprintf (Gbl.F.Out,""); + /***** Create test question *****/ + Tst_QstConstructor (); + Gbl.Test.QstCod = QstCod; /***** Get answers of a question from database *****/ Gbl.Test.Answer.NumOptions = Tst_GetAnswersQst (QstCod,&mysql_res,Shuffle); // Result: AnsInd,Answer,Correct @@ -3294,6 +3295,10 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle) row[ 4] ImageTitle row[ 5] Correct */ + + /***** Start of table *****/ + fprintf (Gbl.F.Out,"
"); + for (NumOpt = 0; NumOpt < Gbl.Test.Answer.NumOptions; NumOpt++) @@ -3324,11 +3329,7 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle) Gbl.Test.Answer.Options[NumOpt].Text,Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false); /***** Copy image *****/ - if (row[3][0]) - { - Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB; - Img_GetImageNameAndTitle (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image); - } + Img_GetImageNameAndTitleFromRow (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image); /***** Write selectors and letter of this option *****/ fprintf (Gbl.F.Out,"" @@ -3351,8 +3352,7 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle) fprintf (Gbl.F.Out,"" ""); } @@ -3360,12 +3360,11 @@ static void Tst_WriteChoiceAnsSeeExam (unsigned NumQst,long QstCod,bool Shuffle) /***** End of table *****/ fprintf (Gbl.F.Out,"
" "%s", Gbl.Test.Answer.Options[NumOpt].Text); - if (Gbl.Test.Answer.Options[NumOpt].Image.Name[0]) - Img_ShowImage (&Gbl.Test.Answer.Options[NumOpt].Image,"TEST_IMG_SHOW_ANS"); + Img_ShowImage (&Gbl.Test.Answer.Options[NumOpt].Image,"TEST_IMG_SHOW_ANS"); fprintf (Gbl.F.Out,"
"); - /***** Free answers and images of this test question *****/ - Tst_FreeTextChoiceAnswers (); - Tst_FreeImagesOfQuestion (); - /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); + + /***** Destroy test question *****/ + Tst_QstDestructor (); } /*****************************************************************************/ @@ -3430,11 +3429,7 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res, } /***** Copy image *****/ - if (row[3][0]) - { - Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB; - Img_GetImageNameAndTitle (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image); - } + Img_GetImageNameAndTitleFromRow (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image); /***** Assign correctness (row[5]) of this answer (this option) *****/ Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[5][0]) == 'Y'); @@ -3526,8 +3521,7 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res, "
" "%s", Gbl.Test.Answer.Options[Indexes[NumOpt]].Text); - if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Image.Name[0]) - Img_ShowImage (&Gbl.Test.Answer.Options[Indexes[NumOpt]].Image,"TEST_IMG_SHOW_ANS"); + Img_ShowImage (&Gbl.Test.Answer.Options[Indexes[NumOpt]].Image,"TEST_IMG_SHOW_ANS"); fprintf (Gbl.F.Out,"
"); if (Gbl.Test.Config.FeedbackType == Tst_FEEDBACK_FULL_FEEDBACK) if (Gbl.Test.Answer.Options[Indexes[NumOpt]].Feedback) @@ -3606,10 +3600,6 @@ static void Tst_WriteChoiceAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res, /***** End of table *****/ fprintf (Gbl.F.Out,""); - - /***** Free answers and images of this test question *****/ - Tst_FreeTextChoiceAnswers (); - Tst_FreeImagesOfQuestion (); } /*****************************************************************************/ @@ -3791,9 +3781,6 @@ static void Tst_WriteTextAnsAssessExam (unsigned NumQst,MYSQL_RES *mysql_res, } fprintf (Gbl.F.Out,""); - - /***** Free answers *****/ - Tst_FreeTextChoiceAnswers (); } /*****************************************************************************/ @@ -4351,8 +4338,18 @@ void Tst_ShowFormEditOneQst (void) char Stem[Cns_MAX_BYTES_TEXT+1]; char Feedback[Cns_MAX_BYTES_TEXT+1]; + /***** Create test question *****/ + Tst_QstConstructor (); + Gbl.Test.QstCod = Tst_GetQstCod (); Stem[0] = Feedback[0] = '\0'; + if (Gbl.Test.QstCod > 0) // If question already exists in the database + Tst_GetQstDataFromDB (Stem,Feedback); + + /***** Put form to edit question *****/ Tst_PutFormEditOneQst (Stem,Feedback); + + /***** Destroy test question *****/ + Tst_QstDestructor (); } /*****************************************************************************/ @@ -4398,16 +4395,6 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback) char ParamFile[32]; char ParamTitle[32]; - /***** If no receiving the question, but editing a new or existing question - ==> init or edit data of question *****/ - if (Gbl.Action.Act == ActEdiOneTstQst) - { - Tst_InitQst (); - if (Tst_GetQstCod ()) // If parameter QstCod received ==> - // ==> question already exists in the database - Tst_GetQstDataFromDB (Stem,Feedback); - } - /***** Start frame *****/ if (Gbl.Test.QstCod > 0) // The question already has assigned a code { @@ -4737,21 +4724,18 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback) /***** End frame *****/ Lay_EndRoundFrame (); - - /***** Free memory for answers *****/ - Tst_FreeTextChoiceAnswers (); } /*****************************************************************************/ /********************* Initialize a new question to zero *********************/ /*****************************************************************************/ -void Tst_InitQst (void) +void Tst_QstConstructor (void) { unsigned NumOpt; unsigned NumTag; - Gbl.Test.QstCod = -1; + Gbl.Test.QstCod = -1L; for (NumTag = 0; NumTag < Tst_MAX_TAGS_PER_QUESTION; NumTag++) @@ -4781,6 +4765,74 @@ void Tst_InitQst (void) Tst_InitImagesOfQuestion (); } +/*****************************************************************************/ +/***************** Free memory allocated for test question *******************/ +/*****************************************************************************/ + +void Tst_QstDestructor (void) + { + Tst_FreeTextChoiceAnswers (); + Tst_FreeImagesOfQuestion (); + } + +/*****************************************************************************/ +/******************* Allocate memory for a choice answer *********************/ +/*****************************************************************************/ + +int Tst_AllocateTextChoiceAnswer (unsigned NumOpt) + { + Tst_FreeTextChoiceAnswer (NumOpt); + + if ((Gbl.Test.Answer.Options[NumOpt].Text = + malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL) + { + sprintf (Gbl.Message,"Not enough memory to store answer."); + return 0; + } + if ((Gbl.Test.Answer.Options[NumOpt].Feedback = + malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL) + { + sprintf (Gbl.Message,"Not enough memory to store feedback."); + return 0; + } + + Gbl.Test.Answer.Options[NumOpt].Text[0] = + Gbl.Test.Answer.Options[NumOpt].Feedback[0] = '\0'; + return 1; + } + +/*****************************************************************************/ +/******************** Free memory of all choice answers **********************/ +/*****************************************************************************/ + +static void Tst_FreeTextChoiceAnswers (void) + { + unsigned NumOpt; + + for (NumOpt = 0; + NumOpt < Tst_MAX_OPTIONS_PER_QUESTION; + NumOpt++) + Tst_FreeTextChoiceAnswer (NumOpt); + } + +/*****************************************************************************/ +/********************** Free memory of a choice answer ***********************/ +/*****************************************************************************/ + +static void Tst_FreeTextChoiceAnswer (unsigned NumOpt) + { + if (Gbl.Test.Answer.Options[NumOpt].Text) + { + free ((void *) Gbl.Test.Answer.Options[NumOpt].Text); + Gbl.Test.Answer.Options[NumOpt].Text = NULL; + } + if (Gbl.Test.Answer.Options[NumOpt].Feedback) + { + free ((void *) Gbl.Test.Answer.Options[NumOpt].Feedback); + Gbl.Test.Answer.Options[NumOpt].Feedback = NULL; + } + } + /*****************************************************************************/ /***************** Initialize images of a question to zero *******************/ /*****************************************************************************/ @@ -4812,11 +4864,11 @@ static void Tst_FreeImagesOfQuestion (void) { unsigned NumOpt; - Img_ResetImageTitle (&Gbl.Test.Image); + Img_FreeImageTitle (&Gbl.Test.Image); for (NumOpt = 0; NumOpt < Tst_MAX_OPTIONS_PER_QUESTION; NumOpt++) - Img_ResetImageTitle (&Gbl.Test.Answer.Options[NumOpt].Image); + Img_FreeImageTitle (&Gbl.Test.Answer.Options[NumOpt].Image); } /*****************************************************************************/ @@ -4869,13 +4921,7 @@ static void Tst_GetQstDataFromDB (char *Stem,char *Feedback) } /* Get the image name of the question from the database (row[4]) */ - if (row[4][0]) - { - Gbl.Test.Image.Status = Img_NAME_STORED_IN_DB; - Img_GetImageNameAndTitle (row[4],row[5],&Gbl.Test.Image); - } - else - Gbl.Test.Image.Status = Img_FILE_NONE; + Img_GetImageNameAndTitleFromRow (row[4],row[5],&Gbl.Test.Image); /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_res); @@ -4946,11 +4992,7 @@ static void Tst_GetQstDataFromDB (char *Stem,char *Feedback) } /* Copy image */ - if (row[3][0]) - { - Gbl.Test.Answer.Options[NumOpt].Image.Status = Img_NAME_STORED_IN_DB; - Img_GetImageNameAndTitle (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image); - } + Img_GetImageNameAndTitleFromRow (row[3],row[4],&Gbl.Test.Answer.Options[NumOpt].Image); Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[5][0]) == 'Y'); break; @@ -4991,7 +5033,7 @@ static void Tst_GetImageFromDB (unsigned NumOpt,struct Image *Image) row = mysql_fetch_row (mysql_res); /***** Get the image name (row[0]) *****/ - Img_GetImageNameAndTitle (row[0],row[1],Image); + Img_GetImageNameAndTitleFromRow (row[0],row[1],Image); /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); @@ -5040,11 +5082,11 @@ void Tst_ReceiveQst (void) char Stem[Cns_MAX_BYTES_TEXT+1]; char Feedback[Cns_MAX_BYTES_TEXT+1]; - /***** Initialize new question to zero *****/ - Tst_InitQst (); - Stem[0] = Feedback[0] = '\0'; + /***** Create test question *****/ + Tst_QstConstructor (); /***** Get parameters of the question from form *****/ + Stem[0] = Feedback[0] = '\0'; Tst_GetQstFromForm (Stem,Feedback); /***** Make sure that tags, text and answer are not empty *****/ @@ -5068,8 +5110,8 @@ void Tst_ReceiveQst (void) Tst_PutFormEditOneQst (Stem,Feedback); } - /***** Free answers *****/ - Tst_FreeTextChoiceAnswers (); + /***** Destroy test question *****/ + Tst_QstDestructor (); } /*****************************************************************************/ @@ -5094,7 +5136,7 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback) char ParamTitle[32]; /***** Get question code *****/ - Tst_GetQstCod (); + Gbl.Test.QstCod = Tst_GetQstCod (); /***** Get answer type *****/ Par_GetParToText ("AnswerType",UnsignedStr,10); @@ -5645,7 +5687,8 @@ void Tst_RequestRemoveQst (void) /***** Get main parameters from form *****/ /* Get the question code */ - if (!Tst_GetQstCod ()) + Gbl.Test.QstCod = Tst_GetQstCod (); + if (Gbl.Test.QstCod <= 0) Lay_ShowErrorAndExit ("Wrong code of question."); /* Get a parameter that indicates whether it's necessary @@ -5697,7 +5740,8 @@ void Tst_RemoveQst (void) bool EditingOnlyThisQst; /***** Get the question code *****/ - if (!Tst_GetQstCod ()) + Gbl.Test.QstCod = Tst_GetQstCod (); + if (Gbl.Test.QstCod <= 0) Lay_ShowErrorAndExit ("Wrong code of question."); /***** Get a parameter that indicates whether it's necessary @@ -5749,7 +5793,8 @@ void Tst_ChangeShuffleQst (void) bool Shuffle; /***** Get the question code *****/ - if (!Tst_GetQstCod ()) + Gbl.Test.QstCod = Tst_GetQstCod (); + if (Gbl.Test.QstCod <= 0) Lay_ShowErrorAndExit ("Wrong code of question."); /***** Get a parameter that indicates whether it's necessary to continue listing the rest of questions ******/ @@ -5787,14 +5832,12 @@ void Tst_ChangeShuffleQst (void) /************ Get the parameter with the code of a test question *************/ /*****************************************************************************/ -static bool Tst_GetQstCod (void) +static long Tst_GetQstCod (void) { char LongStr[1+10+1]; Par_GetParToText ("QstCod",LongStr,1+10); - if ((Gbl.Test.QstCod = Str_ConvertStrCodToLongCod (LongStr)) < 0) - return false; - return true; + return Str_ConvertStrCodToLongCod (LongStr); } /*****************************************************************************/ @@ -5825,9 +5868,10 @@ static void Tst_InsertOrUpdateQstIntoDB (void) char *Query; /***** Allocate space for query *****/ - if ((Query = malloc (256 + + if ((Query = malloc (512 + Gbl.Test.Stem.Length + - Gbl.Test.Feedback.Length)) == NULL) + Gbl.Test.Feedback.Length + + Img_MAX_BYTES_TITLE)) == NULL) Lay_ShowErrorAndExit ("Not enough memory to store database query."); if (Gbl.Test.QstCod < 0) // It's a new question @@ -6144,64 +6188,6 @@ static void Tst_RemoveImgFilesFromAnsOfQsts (long CrsCod, DB_FreeMySQLResult (&mysql_res); } -/*****************************************************************************/ -/******************* Allocate memory for a choice answer *********************/ -/*****************************************************************************/ - -int Tst_AllocateTextChoiceAnswer (unsigned NumOpt) - { - Tst_FreeTextChoiceAnswer (NumOpt); - - if ((Gbl.Test.Answer.Options[NumOpt].Text = - malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL) - { - sprintf (Gbl.Message,"Not enough memory to store answer."); - return 0; - } - if ((Gbl.Test.Answer.Options[NumOpt].Feedback = - malloc (Tst_MAX_BYTES_ANSWER_OR_FEEDBACK + 1)) == NULL) - { - sprintf (Gbl.Message,"Not enough memory to store feedback."); - return 0; - } - - Gbl.Test.Answer.Options[NumOpt].Text[0] = - Gbl.Test.Answer.Options[NumOpt].Feedback[0] = '\0'; - return 1; - } - -/*****************************************************************************/ -/******************** Free memory of all choice answers **********************/ -/*****************************************************************************/ - -void Tst_FreeTextChoiceAnswers (void) - { - unsigned NumOpt; - - for (NumOpt = 0; - NumOpt < Tst_MAX_OPTIONS_PER_QUESTION; - NumOpt++) - Tst_FreeTextChoiceAnswer (NumOpt); - } - -/*****************************************************************************/ -/********************** Free memory of a choice answer ***********************/ -/*****************************************************************************/ - -static void Tst_FreeTextChoiceAnswer (unsigned NumOpt) - { - if (Gbl.Test.Answer.Options[NumOpt].Text) - { - free ((void *) Gbl.Test.Answer.Options[NumOpt].Text); - Gbl.Test.Answer.Options[NumOpt].Text = NULL; - } - if (Gbl.Test.Answer.Options[NumOpt].Feedback) - { - free ((void *) Gbl.Test.Answer.Options[NumOpt].Feedback); - Gbl.Test.Answer.Options[NumOpt].Feedback = NULL; - } - } - /*****************************************************************************/ /*********************** Get stats about test questions **********************/ /*****************************************************************************/ diff --git a/swad_test.h b/swad_test.h index 09b2ea653..99cce6bff 100644 --- a/swad_test.h +++ b/swad_test.h @@ -141,7 +141,12 @@ void Tst_GetConfigFromRow (MYSQL_ROW row); bool Tst_CheckIfCourseHaveTestsAndPluggableIsUnknown (void); void Tst_ReceiveConfigTst (void); void Tst_ShowFormEditOneQst (void); -void Tst_InitQst (void); + +void Tst_QstConstructor (void); +void Tst_QstDestructor (void); + +int Tst_AllocateTextChoiceAnswer (unsigned NumOpt); + Tst_AnswerType_t Tst_ConvertFromStrAnsTypDBToAnsTyp (const char *StrAnsTypeBD); void Tst_ReceiveQst (void); bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void); @@ -152,8 +157,6 @@ void Tst_RemoveQst (void); void Tst_ChangeShuffleQst (void); void Tst_InsertOrUpdateQstTagsAnsIntoDB (void); -int Tst_AllocateTextChoiceAnswer (unsigned NumOpt); -void Tst_FreeTextChoiceAnswers (void); void Tst_FreeTagsList (void); void Tst_GetTestStats (Tst_AnswerType_t AnsType,struct Tst_Stats *Stats); diff --git a/swad_test_import.c b/swad_test_import.c index 61cec29f6..d0d350dc0 100644 --- a/swad_test_import.c +++ b/swad_test_import.c @@ -533,8 +533,8 @@ static void TsI_ImportQuestionsFromXMLBuffer (const char *XMLBuffer) { if (!strcmp (QuestionElem->TagName,"question")) { - /***** Initialize new question to zero *****/ - Tst_InitQst (); + /***** Create test question *****/ + Tst_QstConstructor (); /* Get type of questions (in mandatory attribute "type") */ AnswerTypeFound = false; @@ -653,8 +653,8 @@ static void TsI_ImportQuestionsFromXMLBuffer (const char *XMLBuffer) } } - /***** Free answers *****/ - Tst_FreeTextChoiceAnswers (); + /***** Destroy test question *****/ + Tst_QstDestructor (); } }