Version 15.175

This commit is contained in:
Antonio Cañas Vargas 2016-04-03 01:24:20 +02:00
parent 0648ccefe6
commit 093c17c456
9 changed files with 271 additions and 310 deletions

View File

@ -26,23 +26,29 @@
# #
###############################################################################
OBJS = swad_account.o swad_action.o swad_announcement.o swad_assignment.o swad_attendance.o \
OBJS = swad_account.o swad_action.o swad_announcement.o swad_assignment.o \
swad_attendance.o \
swad_banner.o \
swad_calendar.o swad_centre.o swad_chat.o swad_config.o swad_connected.o swad_country.o swad_course.o swad_cryptography.o \
swad_database.o swad_date.o swad_degree.o swad_degree_type.o swad_department.o \
swad_calendar.o swad_centre.o swad_chat.o swad_config.o \
swad_connected.o swad_country.o swad_course.o swad_cryptography.o \
swad_database.o swad_date.o swad_degree.o swad_degree_type.o \
swad_department.o \
swad_enrollment.o swad_exam.o \
swad_file.o swad_file_browser.o swad_follow.o swad_forum.o \
swad_global.o swad_group.o \
swad_help.o swad_holiday.o \
swad_icon.o swad_ID.o swad_import.o swad_indicator.o swad_info.o swad_institution.o \
swad_icon.o swad_ID.o swad_image.o swad_import.o swad_indicator.o \
swad_info.o swad_institution.o \
swad_layout.o swad_link.o swad_logo.o \
swad_mail.o swad_main.o swad_mark.o swad_menu.o swad_message.o \
swad_network.o swad_nickname.o swad_notice.o swad_notification.o \
swad_pagination.o swad_parameter.o swad_password.o swad_photo.o \
swad_place.o swad_plugin.o swad_preference.o swad_profile.o swad_privacy.o \
swad_place.o swad_plugin.o swad_preference.o swad_profile.o \
swad_privacy.o \
swad_QR.o \
swad_record.o swad_role.o swad_RSS.o \
swad_scope.o swad_search.o swad_session.o swad_setup.o swad_social.o swad_statistic.o swad_string.o swad_survey.o swad_syllabus.o \
swad_scope.o swad_search.o swad_session.o swad_setup.o swad_social.o \
swad_statistic.o swad_string.o swad_survey.o swad_syllabus.o \
swad_tab.o swad_test.o swad_test_import.o swad_theme.o swad_timetable.o \
swad_user.o \
swad_web_service.o \

View File

@ -144,13 +144,16 @@
/****************************** Public constants *****************************/
/*****************************************************************************/
#define Log_PLATFORM_VERSION "SWAD 15.174 (2016-04-01)"
#define Log_PLATFORM_VERSION "SWAD 15.175 (2016-04-03)"
#define CSS_FILE "swad15.173.1.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.175: Apr 03, 2016 Fixed bug when editing a test question.
Lot of code refactoring related to edition of questions.
New module swad_image for edition of images. (197754 lines)
Version 15.174: Apr 01, 2016 Image in a test question is removed when the test question is removed.
All images in test questions of a course are removed when course is removed. (197504 lines)
Version 15.173.4: Apr 01, 2016 Old image in test question is removed when replaced by a new one. (197481 lines)

View File

@ -374,9 +374,7 @@ bool Fil_RenameFileOrDir (const char *PathOld,const char *PathNew)
extern const char *Txt_There_is_already_a_file_named_X;
/* Rename the file or directory */
if (rename (PathOld,PathNew) == 0)
return true;
else
if (rename (PathOld,PathNew)) // Fail
{
switch (errno)
{
@ -398,6 +396,8 @@ bool Fil_RenameFileOrDir (const char *PathOld,const char *PathNew)
}
return false;
}
else // Success
return true;
}
/*****************************************************************************/

View File

@ -29,6 +29,7 @@
#include <stdbool.h> // For boolean type
#include <stdio.h> // For FILE
#include <time.h> // For time_t
/*****************************************************************************/
/************************** Public types and constants ***********************/

View File

@ -2723,12 +2723,12 @@ bool Brw_UpdateFoldersAssigmentsIfExistForAllUsrs (const char *OldFolderName,con
UsrCod, // User's code
Brw_INTERNAL_NAME_ROOT_FOLDER_ASSIGNMENTS,
NewFolderName);
if (rename (PathOldFolder,PathNewFolder))
if (rename (PathOldFolder,PathNewFolder)) // Fail
{
Lay_ShowAlert (Lay_ERROR,Txt_Can_not_rename_a_folder_of_assignment);
NumUsrsError++;
}
else
else // Success
{
/* Remove affected clipboards */
Brw_RemoveAffectedClipboards (Brw_ADMI_ASSIG_USR,UsrCod,-1L);
@ -8259,7 +8259,26 @@ void Brw_RenFolderFileBrowser (void)
but we leave this work to the system */
/* Rename the directory. If a empty folder existed with the name new, overwrite it! */
if (rename (OldPath,NewPath) == 0)
if (rename (OldPath,NewPath)) // Fail
{
switch (errno)
{
case ENOTEMPTY:
case EEXIST:
case ENOTDIR:
sprintf (Gbl.Message,Txt_The_folder_name_X_has_not_changed_because_there_is_already_a_folder_or_a_file_with_the_name_Y,
Gbl.FileBrowser.FilFolLnkName,Gbl.FileBrowser.NewFilFolLnkName);
break;
case EACCES:
Lay_ShowErrorAndExit ("Write forbidden.");
break;
default:
Lay_ShowErrorAndExit ("Can not rename folder.");
break;
}
Lay_ShowAlert (Lay_WARNING,Gbl.Message);
}
else // Success
{
/* If a folder is renamed,
it is necessary to rename all the entries in the tables of files
@ -8287,25 +8306,7 @@ void Brw_RenFolderFileBrowser (void)
Gbl.FileBrowser.NewFilFolLnkName);
Lay_ShowAlert (Lay_SUCCESS,Gbl.Message);
}
else
{
switch (errno)
{
case ENOTEMPTY:
case EEXIST:
case ENOTDIR:
sprintf (Gbl.Message,Txt_The_folder_name_X_has_not_changed_because_there_is_already_a_folder_or_a_file_with_the_name_Y,
Gbl.FileBrowser.FilFolLnkName,Gbl.FileBrowser.NewFilFolLnkName);
break;
case EACCES:
Lay_ShowErrorAndExit ("Write forbidden.");
break;
default:
Lay_ShowErrorAndExit ("Can not rename folder.");
break;
}
Lay_ShowAlert (Lay_WARNING,Gbl.Message);
}
}
else // Names are equal. This may happens if we have press INTRO without changing the name
{
@ -8449,13 +8450,13 @@ static bool Brw_RcvFileInFileBrw (Brw_UploadType_t UploadType)
if (FileIsValid)
{
/* Rename the temporary */
if (rename (PathTmp,Path))
if (rename (PathTmp,Path)) // Fail
{
Brw_RemoveTree (PathTmp);
sprintf (Gbl.Message,Txt_UPLOAD_FILE_could_not_create_file_NO_HTML,
Gbl.FileBrowser.NewFilFolLnkName);
}
else
else // Success
{
/* Check if quota has been exceeded */
Brw_CalcSizeOfDir (Gbl.FileBrowser.Priv.PathRootFolder);

View File

@ -430,6 +430,9 @@ void Gbl_InitializeGlobals (void)
Gbl.Imported.ExternalSesId[0] = '\0';
Gbl.Imported.ExternalRole = Rol_UNKNOWN;
/* Related to images uploaded in a form */
Gbl.Image.Status = Img_NONE;
Gbl.WebService.Function = Svc_unknown;
}

View File

@ -47,8 +47,9 @@
#include "swad_file_browser.h"
#include "swad_forum.h"
#include "swad_holiday.h"
#include "swad_icon.h"
#include "swad_image.h"
#include "swad_import.h"
#include "swad_icon.h"
#include "swad_institution.h"
#include "swad_layout.h"
#include "swad_link.h"
@ -659,7 +660,6 @@ struct Globals
size_t Length;
} Stem, Feedback;
char Image[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64+1];
bool ChangeImage; // Change image only when teacher uploads a new image
bool Shuffle;
struct
{
@ -719,6 +719,10 @@ struct Globals
float MaxPercent;
} DegPhotos;
} Stat;
struct
{
Img_Status_t Status;
} Image;
};
/*****************************************************************************/

View File

@ -42,6 +42,7 @@
#include "swad_database.h"
#include "swad_global.h"
#include "swad_ID.h"
#include "swad_image.h"
#include "swad_parameter.h"
#include "swad_theme.h"
#include "swad_test.h"
@ -154,7 +155,6 @@ 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_WriteQstImage (const char *Image,const char *ClassImg);
static void Tst_PutFormToUploadNewQstImage (void);
static void Tst_UpdateScoreQst (long QstCod,float ScoreThisQst,bool AnswerIsNotBlank);
static void Tst_UpdateMyNumAccessTst (unsigned NumAccessesTst);
@ -213,13 +213,16 @@ static bool Tst_GetCreateXMLFromForm (void);
static int Tst_CountNumTagsInList (void);
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 Tst_AnswerType_t Tst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr);
static void Tst_GetQstFromForm (char *Stem,char *Feedback);
static bool Tst_GetImageFromForm (void);
static long Tst_GetTagCodFromTagTxt (const char *TagTxt);
static long Tst_CreateNewTag (long CrsCod,const char *TagTxt);
static void Tst_EnableOrDisableTag (long TagCod,bool TagHidden);
static int Tst_GetQstCod (void);
static bool Tst_GetQstCod (void);
static void Tst_InsertOrUpdateQstIntoDB (void);
static void Tst_InsertTagsIntoDB (void);
@ -975,7 +978,11 @@ static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row,
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP COLOR%u\">",
Gbl.RowEvenOdd);
Tst_WriteQstStem (row[4],"TEST_EXA");
Tst_WriteQstImage (row[5],"TEST_IMG_SHOW");
if (row[5][0])
{
Gbl.Image.Status = Img_NAME_STORED_IN_DB;
Img_ShowImage (row[5],"TEST_IMG_SHOW");
}
if (Gbl.Action.Act == ActSeeTst)
Tst_WriteAnswersOfAQstSeeExam (NumQst,QstCod,(Str_ConvertToUpperLetter (row[3][0]) == 'Y'));
else // Assessing exam / Viewing old exam
@ -1016,48 +1023,6 @@ void Tst_WriteQstStem (const char *Stem,const char *ClassStem)
free ((void *) StemRigorousHTML);
}
/*****************************************************************************/
/******************** Write the image of a test question *********************/
/*****************************************************************************/
static void Tst_WriteQstImage (const char *Image,const char *ClassImg)
{
char FileNameImgPriv[PATH_MAX+1];
char FullPathImgPriv[PATH_MAX+1];
char URL[PATH_MAX+1];
if (!Image)
return;
if (!Image[0])
return;
/***** Create a temporary public directory
used to download the zip file *****/
Brw_CreateDirDownloadTmp ();
/***** Create symbolic link from temporary public directory to private file in order to gain access to it for downloading *****/
/* Private path to image */
sprintf (FileNameImgPriv,"%s.jpg",
Image);
sprintf (FullPathImgPriv,"%s/%s/%c%c/%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG,
Image[0],
Image[1],
FileNameImgPriv);
Brw_CreateTmpPublicLinkToPrivateFile (FullPathImgPriv,FileNameImgPriv);
/***** Create URL pointing to symbolic link *****/
sprintf (URL,"%s/%s/%s/%s",
Cfg_HTTPS_URL_SWAD_PUBLIC,Cfg_FOLDER_FILE_BROWSER_TMP,
Gbl.FileBrowser.TmpPubDir,
FileNameImgPriv);
/***** Show image *****/
fprintf (Gbl.F.Out,"<img src=\"%s\" alt=\"\" class=\"%s\"/>"
"<br />",
URL,ClassImg);
}
/*****************************************************************************/
/************* Put form to upload a new image for a test question ************/
/*****************************************************************************/
@ -2707,7 +2672,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");
Tst_WriteQstImage (row[5],"TEST_IMG_EDIT_LIST");
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");
}
Tst_WriteQstFeedback (row[6],"TEST_EDI_LIGHT");
Tst_WriteAnswersOfAQstEdit (QstCod);
fprintf (Gbl.F.Out,"</td>");
@ -4202,125 +4172,24 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
extern const char *Txt_Save;
extern const char *Txt_Create_question;
char Title[512];
char Query[512];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumRow,NumRows;
unsigned long NumRows;
unsigned long NumRow;
unsigned NumOpt;
Tst_AnswerType_t AnsType;
bool Shuffle = false;
unsigned NumTag;
bool TagNotFound;
bool OptionsDisabled;
if (Gbl.Action.Act == ActEdiOneTstQst) // If no receiving the question, but editing a new or existing question
/***** 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
{
/***** Get the type of answer and the stem from the database *****/
/* Get the question from database */
sprintf (Query,"SELECT AnsType,Shuffle,Stem,Image,Feedback"
" 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");
row = mysql_fetch_row (mysql_res);
/* Get the type of answer */
Gbl.Test.AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[0]);
/* Get shuffle (row[1]) */
Shuffle = (Str_ConvertToUpperLetter (row[1][0]) == 'Y');
/* Get the stem of the question from the database (row[2]) */
strncpy (Stem,row[2],Cns_MAX_BYTES_TEXT);
Stem[Cns_MAX_BYTES_TEXT] = '\0';
/* Get the image of the question from the database (row[3]) */
strncpy (Gbl.Test.Image,row[3],Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64);
Gbl.Test.Image[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64] = '\0';
/* Get the feedback of the question from the database (row[4]) */
Feedback[0] = '\0';
if (row[4])
if (row[4][0])
{
strncpy (Feedback,row[4],Cns_MAX_BYTES_TEXT);
Feedback[Cns_MAX_BYTES_TEXT] = '\0';
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
/***** Get the tags from the database *****/
NumRows = Tst_GetTagsQst (Gbl.Test.QstCod,&mysql_res);
for (NumRow = 0;
NumRow < NumRows;
NumRow++)
{
row = mysql_fetch_row (mysql_res);
strncpy (Gbl.Test.TagText[NumRow],row[0],Tst_MAX_BYTES_TAG);
Gbl.Test.TagText[NumRow][Tst_MAX_BYTES_TAG] = '\0';
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
/***** Get the answers from the database *****/
Gbl.Test.Answer.NumOptions = Tst_GetAnswersQst (Gbl.Test.QstCod,&mysql_res,false); // Result: AnsInd,Answer,Correct,Feedback
for (NumOpt = 0;
NumOpt < Gbl.Test.Answer.NumOptions;
NumOpt++)
{
row = mysql_fetch_row (mysql_res);
switch (Gbl.Test.AnswerType)
{
case Tst_ANS_INT:
if (Gbl.Test.Answer.NumOptions != 1)
Lay_ShowErrorAndExit ("Wrong answer.");
Gbl.Test.Answer.Integer = Tst_GetIntAnsFromStr (row[1]);
break;
case Tst_ANS_FLOAT:
if (Gbl.Test.Answer.NumOptions != 2)
Lay_ShowErrorAndExit ("Wrong answer.");
Gbl.Test.Answer.FloatingPoint[NumOpt] = Tst_GetFloatAnsFromStr (row[1]);
break;
case Tst_ANS_TRUE_FALSE:
if (Gbl.Test.Answer.NumOptions != 1)
Lay_ShowErrorAndExit ("Wrong answer.");
Gbl.Test.Answer.TF = row[1][0];
break;
case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE:
case Tst_ANS_TEXT:
if (Gbl.Test.Answer.NumOptions > Tst_MAX_OPTIONS_PER_QUESTION)
Lay_ShowErrorAndExit ("Wrong answer.");
if (!Tst_AllocateTextChoiceAnswer (NumOpt))
Lay_ShowErrorAndExit (Gbl.Message);
strncpy (Gbl.Test.Answer.Options[NumOpt].Text,row[1],Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
Gbl.Test.Answer.Options[NumOpt].Text[Tst_MAX_BYTES_ANSWER_OR_FEEDBACK] = '\0';
// Feedback is initialized to empty string
if (row[3])
if (row[3][0])
{
strncpy (Gbl.Test.Answer.Options[NumOpt].Feedback,row[3],Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
Gbl.Test.Answer.Options[NumOpt].Feedback[Tst_MAX_BYTES_ANSWER_OR_FEEDBACK] = '\0';
}
Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[2][0]) == 'Y');
break;
default:
break;
}
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
}
if (Tst_GetQstCod ()) // If parameter QstCod received ==>
// ==> question already exists in the database
Tst_GetQstDataFromDB (Stem,Feedback);
}
/***** Start form and table *****/
@ -4438,7 +4307,8 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
"<td class=\"LEFT_TOP\">",
The_ClassForm[Gbl.Prefs.Theme],
Txt_Image);
Tst_WriteQstImage (Gbl.Test.Image,"TEST_IMG_EDIT_ONE");
if (Gbl.Test.Image[0])
Img_ShowImage (Gbl.Test.Image,"TEST_IMG_EDIT_ONE");
Tst_PutFormToUploadNewQstImage ();
fprintf (Gbl.F.Out,"</td>"
"</tr>");
@ -4560,7 +4430,7 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
"<td class=\"%s LEFT_TOP\">"
"<input type=\"checkbox\" name=\"Shuffle\" value=\"Y\"",
The_ClassForm[Gbl.Prefs.Theme]);
if (Shuffle)
if (Gbl.Test.Shuffle)
fprintf (Gbl.F.Out," checked=\"checked\"");
if (Gbl.Test.AnswerType != Tst_ANS_UNIQUE_CHOICE &&
Gbl.Test.AnswerType != Tst_ANS_MULTIPLE_CHOICE)
@ -4676,7 +4546,7 @@ void Tst_InitQst (void)
Gbl.Test.Feedback.Text = NULL;
Gbl.Test.Feedback.Length = 0;
Gbl.Test.Image[0] = '\0';
Gbl.Test.ChangeImage = false;
Gbl.Test.Shuffle = false;
Gbl.Test.AnswerType = Tst_ANS_UNIQUE_CHOICE;
Gbl.Test.Answer.NumOptions = 0;
Gbl.Test.Answer.TF = ' ';
@ -4692,6 +4562,159 @@ void Tst_InitQst (void)
Gbl.Test.Answer.FloatingPoint[1] = 0.0;
}
/*****************************************************************************/
/****************** Get data of a question from database *********************/
/*****************************************************************************/
static void Tst_GetQstDataFromDB (char *Stem,char *Feedback)
{
char Query[512];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumRows;
unsigned long NumRow;
unsigned NumOpt;
/***** Get the type of answer and the stem from the database *****/
/* Get the question from database */
sprintf (Query,"SELECT AnsType,Shuffle,Stem,Image,Feedback"
" 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");
row = mysql_fetch_row (mysql_res);
/* Get the type of answer */
Gbl.Test.AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[0]);
/* Get shuffle (row[1]) */
Gbl.Test.Shuffle = (Str_ConvertToUpperLetter (row[1][0]) == 'Y');
/* Get the stem of the question from the database (row[2]) */
strncpy (Stem,row[2],Cns_MAX_BYTES_TEXT);
Stem[Cns_MAX_BYTES_TEXT] = '\0';
/* 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';
}
else
Gbl.Image.Status = Img_NONE;
/* Get the feedback of the question from the database (row[4]) */
Feedback[0] = '\0';
if (row[4])
if (row[4][0])
{
strncpy (Feedback,row[4],Cns_MAX_BYTES_TEXT);
Feedback[Cns_MAX_BYTES_TEXT] = '\0';
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
/***** Get the tags from the database *****/
NumRows = Tst_GetTagsQst (Gbl.Test.QstCod,&mysql_res);
for (NumRow = 0;
NumRow < NumRows;
NumRow++)
{
row = mysql_fetch_row (mysql_res);
strncpy (Gbl.Test.TagText[NumRow],row[0],Tst_MAX_BYTES_TAG);
Gbl.Test.TagText[NumRow][Tst_MAX_BYTES_TAG] = '\0';
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
/***** Get the answers from the database *****/
Gbl.Test.Answer.NumOptions = Tst_GetAnswersQst (Gbl.Test.QstCod,&mysql_res,false); // Result: AnsInd,Answer,Correct,Feedback
for (NumOpt = 0;
NumOpt < Gbl.Test.Answer.NumOptions;
NumOpt++)
{
row = mysql_fetch_row (mysql_res);
switch (Gbl.Test.AnswerType)
{
case Tst_ANS_INT:
if (Gbl.Test.Answer.NumOptions != 1)
Lay_ShowErrorAndExit ("Wrong answer.");
Gbl.Test.Answer.Integer = Tst_GetIntAnsFromStr (row[1]);
break;
case Tst_ANS_FLOAT:
if (Gbl.Test.Answer.NumOptions != 2)
Lay_ShowErrorAndExit ("Wrong answer.");
Gbl.Test.Answer.FloatingPoint[NumOpt] = Tst_GetFloatAnsFromStr (row[1]);
break;
case Tst_ANS_TRUE_FALSE:
if (Gbl.Test.Answer.NumOptions != 1)
Lay_ShowErrorAndExit ("Wrong answer.");
Gbl.Test.Answer.TF = row[1][0];
break;
case Tst_ANS_UNIQUE_CHOICE:
case Tst_ANS_MULTIPLE_CHOICE:
case Tst_ANS_TEXT:
if (Gbl.Test.Answer.NumOptions > Tst_MAX_OPTIONS_PER_QUESTION)
Lay_ShowErrorAndExit ("Wrong answer.");
if (!Tst_AllocateTextChoiceAnswer (NumOpt))
Lay_ShowErrorAndExit (Gbl.Message);
strncpy (Gbl.Test.Answer.Options[NumOpt].Text,row[1],Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
Gbl.Test.Answer.Options[NumOpt].Text[Tst_MAX_BYTES_ANSWER_OR_FEEDBACK] = '\0';
// Feedback is initialized to empty string
if (row[3])
if (row[3][0])
{
strncpy (Gbl.Test.Answer.Options[NumOpt].Feedback,row[3],Tst_MAX_BYTES_ANSWER_OR_FEEDBACK);
Gbl.Test.Answer.Options[NumOpt].Feedback[Tst_MAX_BYTES_ANSWER_OR_FEEDBACK] = '\0';
}
Gbl.Test.Answer.Options[NumOpt].Correct = (Str_ConvertToUpperLetter (row[2][0]) == 'Y');
break;
default:
break;
}
}
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/***** Get possible image associated with a test question from database ******/
/*****************************************************************************/
static void Tst_GetImageNameFromDB (void)
{
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");
row = mysql_fetch_row (mysql_res);
/* 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';
}
else // No image in this question
Gbl.Image.Status = Img_NONE;
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/** Convert a string with the type of answer in database to type of answer ***/
/*****************************************************************************/
@ -4746,6 +4769,10 @@ void Tst_ReceiveQst (void)
if (Tst_CheckIfQstFormatIsCorrectAndCountNumOptions ())
{
/***** Form is received OK ==> insert or update question and answer in the database *****/
if (Gbl.Image.Status == Img_FILE_PROCESSED)
/* Move processed image to definitive directory */
Img_MoveImageToDefinitiveDirectory ();
/* Insert or update question, tags and answer in the database */
Tst_InsertOrUpdateQstTagsAnsIntoDB ();
@ -4753,6 +4780,7 @@ void Tst_ReceiveQst (void)
Tst_ListOneQstToEdit ();
}
else // Question is wrong
/***** Put form to edit question again *****/
Tst_PutFormEditOneQst (Stem,Feedback);
/***** Free answers *****/
@ -4815,7 +4843,17 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback)
Par_GetParToHTML ("Feedback",Feedback,Cns_MAX_BYTES_TEXT);
/***** Get new image (if present ==> create file) *****/
Gbl.Test.ChangeImage = Tst_GetImageFromForm ();
Img_GetImageFromForm (Tst_PHOTO_SAVED_MAX_WIDTH,
Tst_PHOTO_SAVED_MAX_HEIGHT,
Tst_PHOTO_SAVED_QUALITY);
if (Gbl.Image.Status != Img_FILE_PROCESSED && // No new image received-processed successfully
Gbl.Test.QstCod > 0) // Question exists
{
/***** Get possible image from database *****/
Tst_GetImageNameFromDB ();
Gbl.Image.Status = (Gbl.Test.Image[0] ? Img_NAME_STORED_IN_DB :
Img_NONE);
}
/***** Get answers *****/
Gbl.Test.Shuffle = false;
@ -4915,109 +4953,6 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback)
Gbl.Test.Feedback.Length = strlen (Gbl.Test.Feedback.Text);
}
/*****************************************************************************/
/****************** Get image of a test question from form *******************/
/*****************************************************************************/
// Return true if image is created
static bool Tst_GetImageFromForm (void)
{
struct Param *Param;
char FileNameImgSrc[PATH_MAX+1];
char *PtrExtension;
size_t LengthExtension;
char MIMEType[Brw_MAX_BYTES_MIME_TYPE+1];
char PathImgPriv[PATH_MAX+1];
char FileNameImgTmp[PATH_MAX+1]; // Full name (including path and .jpg) of the destination temporary file
char FileNameImg[PATH_MAX+1]; // Full name (including path and .jpg) of the destination file
bool WrongType = false;
char Command[1024+PATH_MAX*2];
int ReturnCode;
/***** Get filename and MIME type *****/
Param = Fil_StartReceptionOfFile (FileNameImgSrc,MIMEType);
if (!FileNameImgSrc[0]) // No file present
return false;
/* Get filename extension */
if ((PtrExtension = strrchr (FileNameImgSrc,(int) '.')) == NULL)
return false;
LengthExtension = strlen (PtrExtension);
if (LengthExtension < Fil_MIN_LENGTH_FILE_EXTENSION ||
LengthExtension > Fil_MAX_LENGTH_FILE_EXTENSION)
return false;
/* Check if the file type is image/ or application/octet-stream */
if (strncmp (MIMEType,"image/",strlen ("image/")))
if (strcmp (MIMEType,"application/octet-stream"))
if (strcmp (MIMEType,"application/octetstream"))
if (strcmp (MIMEType,"application/octet"))
WrongType = true;
if (WrongType)
return false;
/***** Assign a unique name for the image *****/
strcpy (Gbl.Test.Image,Gbl.UniqueNameEncrypted);
/***** Create private directories if not exist *****/
/* Create private directory for images if it does not exist */
sprintf (PathImgPriv,"%s/%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG);
Fil_CreateDirIfNotExists (PathImgPriv);
/* Create temporary private directory for images if it does not exist */
sprintf (PathImgPriv,"%s/%s/%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG,Cfg_FOLDER_IMG_TMP);
Fil_CreateDirIfNotExists (PathImgPriv);
/* 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]);
Fil_CreateDirIfNotExists (PathImgPriv);
/***** End the reception of image in a temporary file *****/
sprintf (FileNameImgTmp,"%s/%s/%s/%s.%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_IMG,Cfg_FOLDER_IMG_TMP,
Gbl.Test.Image,PtrExtension);
if (!Fil_EndReceptionOfFile (FileNameImgTmp,Param))
return false;
/***** Convert temporary file to public JPEG 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);
/* Call to program that makes the conversion */
sprintf (Command,"convert %s -resize '%ux%u>' -quality %u %s",
FileNameImgTmp,
Tst_PHOTO_SAVED_MAX_WIDTH,
Tst_PHOTO_SAVED_MAX_HEIGHT,
Tst_PHOTO_SAVED_QUALITY,
FileNameImg);
ReturnCode = system (Command);
if (ReturnCode == -1)
Lay_ShowErrorAndExit ("Error when running command to process image.");
/***** Write message depending on return code *****/
ReturnCode = WEXITSTATUS(ReturnCode);
if (ReturnCode != 0)
{
sprintf (Gbl.Message,"Image could not be processed successfully.<br />"
"Error code returned by the program of processing: %d",
ReturnCode);
Lay_ShowErrorAndExit (Gbl.Message);
}
/***** Remove temporary file *****/
unlink (FileNameImgTmp);
return true;
}
/*****************************************************************************/
/*********************** Check if a question is correct **********************/
/*****************************************************************************/
@ -5419,16 +5354,15 @@ void Tst_ChangeShuffleQst (void)
/*****************************************************************************/
/************ Get the parameter with the code of a test question *************/
/*****************************************************************************/
// Return 1 if parameter exists; 0 if not found
static int Tst_GetQstCod (void)
static bool Tst_GetQstCod (void)
{
char LongStr[1+10+1];
Par_GetParToText ("QstCod",LongStr,1+10);
if ((Gbl.Test.QstCod = Str_ConvertStrCodToLongCod (LongStr)) < 0)
return 0;
return 1;
return false;
return true;
}
/*****************************************************************************/
@ -5478,14 +5412,19 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
Gbl.Test.Image,
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;
}
else // It's an existing question
{
/***** Update existing question *****/
if (Gbl.Test.ChangeImage)
if (Gbl.Image.Status == Img_FILE_MOVED)
{
/* Remove file with the previous image
(the file with the new image is already created) */
/* 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);
/* Update question in database */
@ -5500,6 +5439,10 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
Gbl.Test.Image,
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;
}
else // Do not change image
sprintf (Query,"UPDATE tst_questions"

View File

@ -41499,21 +41499,21 @@ const char *Txt_The_file_is_not_X = // Warning: it is very important to include
#if L==1
"El archivo no es <em>%s</em>."; // Necessita traduccio
#elif L==2
"The file is not <em>%s</em>."; // Need Übersetzung
"The file is not <em>%s</em>."; // Need Übersetzung
#elif L==3
"The file is not <em>%s</em>.";
#elif L==4
"El archivo no es <em>%s</em>.";
#elif L==5
"The file is not <em>%s</em>."; // Besoin de traduction
"The file is not <em>%s</em>."; // Besoin de traduction
#elif L==6
"El archivo no es <em>%s</em>."; // Okoteve traducción
#elif L==7
"IL file non &egrave; <em>%s</em>.";
#elif L==8
"The file is not <em>%s</em>."; // Potrzebujesz tlumaczenie
"The file is not <em>%s</em>."; // Potrzebujesz tlumaczenie
#elif L==9
"The file is not <em>%s</em>."; // Necessita de tradução
"The file is not <em>%s</em>."; // Necessita de tradução
#endif
const char *Txt_The_file_of_folder_no_longer_exists_or_is_now_hidden =