mirror of
https://github.com/acanas/swad-core.git
synced 2024-06-09 02:05:24 +02:00
Version 15.175
This commit is contained in:
parent
0648ccefe6
commit
093c17c456
18
Makefile
18
Makefile
|
@ -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_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_calendar.o swad_centre.o swad_chat.o swad_config.o \
|
||||||
swad_database.o swad_date.o swad_degree.o swad_degree_type.o swad_department.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_enrollment.o swad_exam.o \
|
||||||
swad_file.o swad_file_browser.o swad_follow.o swad_forum.o \
|
swad_file.o swad_file_browser.o swad_follow.o swad_forum.o \
|
||||||
swad_global.o swad_group.o \
|
swad_global.o swad_group.o \
|
||||||
swad_help.o swad_holiday.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_layout.o swad_link.o swad_logo.o \
|
||||||
swad_mail.o swad_main.o swad_mark.o swad_menu.o swad_message.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_network.o swad_nickname.o swad_notice.o swad_notification.o \
|
||||||
swad_pagination.o swad_parameter.o swad_password.o swad_photo.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_QR.o \
|
||||||
swad_record.o swad_role.o swad_RSS.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_tab.o swad_test.o swad_test_import.o swad_theme.o swad_timetable.o \
|
||||||
swad_user.o \
|
swad_user.o \
|
||||||
swad_web_service.o \
|
swad_web_service.o \
|
||||||
|
|
|
@ -144,13 +144,16 @@
|
||||||
/****************************** Public constants *****************************/
|
/****************************** 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 CSS_FILE "swad15.173.1.css"
|
||||||
#define JS_FILE "swad15.131.3.js"
|
#define JS_FILE "swad15.131.3.js"
|
||||||
|
|
||||||
// Number of lines (includes comments but not blank lines) has been got with the following command:
|
// 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
|
// 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.
|
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)
|
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)
|
Version 15.173.4: Apr 01, 2016 Old image in test question is removed when replaced by a new one. (197481 lines)
|
||||||
|
|
|
@ -374,9 +374,7 @@ bool Fil_RenameFileOrDir (const char *PathOld,const char *PathNew)
|
||||||
extern const char *Txt_There_is_already_a_file_named_X;
|
extern const char *Txt_There_is_already_a_file_named_X;
|
||||||
|
|
||||||
/* Rename the file or directory */
|
/* Rename the file or directory */
|
||||||
if (rename (PathOld,PathNew) == 0)
|
if (rename (PathOld,PathNew)) // Fail
|
||||||
return true;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
switch (errno)
|
switch (errno)
|
||||||
{
|
{
|
||||||
|
@ -398,6 +396,8 @@ bool Fil_RenameFileOrDir (const char *PathOld,const char *PathNew)
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else // Success
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include <stdbool.h> // For boolean type
|
#include <stdbool.h> // For boolean type
|
||||||
#include <stdio.h> // For FILE
|
#include <stdio.h> // For FILE
|
||||||
|
#include <time.h> // For time_t
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/************************** Public types and constants ***********************/
|
/************************** Public types and constants ***********************/
|
||||||
|
|
|
@ -2723,12 +2723,12 @@ bool Brw_UpdateFoldersAssigmentsIfExistForAllUsrs (const char *OldFolderName,con
|
||||||
UsrCod, // User's code
|
UsrCod, // User's code
|
||||||
Brw_INTERNAL_NAME_ROOT_FOLDER_ASSIGNMENTS,
|
Brw_INTERNAL_NAME_ROOT_FOLDER_ASSIGNMENTS,
|
||||||
NewFolderName);
|
NewFolderName);
|
||||||
if (rename (PathOldFolder,PathNewFolder))
|
if (rename (PathOldFolder,PathNewFolder)) // Fail
|
||||||
{
|
{
|
||||||
Lay_ShowAlert (Lay_ERROR,Txt_Can_not_rename_a_folder_of_assignment);
|
Lay_ShowAlert (Lay_ERROR,Txt_Can_not_rename_a_folder_of_assignment);
|
||||||
NumUsrsError++;
|
NumUsrsError++;
|
||||||
}
|
}
|
||||||
else
|
else // Success
|
||||||
{
|
{
|
||||||
/* Remove affected clipboards */
|
/* Remove affected clipboards */
|
||||||
Brw_RemoveAffectedClipboards (Brw_ADMI_ASSIG_USR,UsrCod,-1L);
|
Brw_RemoveAffectedClipboards (Brw_ADMI_ASSIG_USR,UsrCod,-1L);
|
||||||
|
@ -8259,7 +8259,26 @@ void Brw_RenFolderFileBrowser (void)
|
||||||
but we leave this work to the system */
|
but we leave this work to the system */
|
||||||
|
|
||||||
/* Rename the directory. If a empty folder existed with the name new, overwrite it! */
|
/* 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,
|
/* If a folder is renamed,
|
||||||
it is necessary to rename all the entries in the tables of files
|
it is necessary to rename all the entries in the tables of files
|
||||||
|
@ -8287,25 +8306,7 @@ void Brw_RenFolderFileBrowser (void)
|
||||||
Gbl.FileBrowser.NewFilFolLnkName);
|
Gbl.FileBrowser.NewFilFolLnkName);
|
||||||
Lay_ShowAlert (Lay_SUCCESS,Gbl.Message);
|
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
|
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)
|
if (FileIsValid)
|
||||||
{
|
{
|
||||||
/* Rename the temporary */
|
/* Rename the temporary */
|
||||||
if (rename (PathTmp,Path))
|
if (rename (PathTmp,Path)) // Fail
|
||||||
{
|
{
|
||||||
Brw_RemoveTree (PathTmp);
|
Brw_RemoveTree (PathTmp);
|
||||||
sprintf (Gbl.Message,Txt_UPLOAD_FILE_could_not_create_file_NO_HTML,
|
sprintf (Gbl.Message,Txt_UPLOAD_FILE_could_not_create_file_NO_HTML,
|
||||||
Gbl.FileBrowser.NewFilFolLnkName);
|
Gbl.FileBrowser.NewFilFolLnkName);
|
||||||
}
|
}
|
||||||
else
|
else // Success
|
||||||
{
|
{
|
||||||
/* Check if quota has been exceeded */
|
/* Check if quota has been exceeded */
|
||||||
Brw_CalcSizeOfDir (Gbl.FileBrowser.Priv.PathRootFolder);
|
Brw_CalcSizeOfDir (Gbl.FileBrowser.Priv.PathRootFolder);
|
||||||
|
|
|
@ -430,6 +430,9 @@ void Gbl_InitializeGlobals (void)
|
||||||
Gbl.Imported.ExternalSesId[0] = '\0';
|
Gbl.Imported.ExternalSesId[0] = '\0';
|
||||||
Gbl.Imported.ExternalRole = Rol_UNKNOWN;
|
Gbl.Imported.ExternalRole = Rol_UNKNOWN;
|
||||||
|
|
||||||
|
/* Related to images uploaded in a form */
|
||||||
|
Gbl.Image.Status = Img_NONE;
|
||||||
|
|
||||||
Gbl.WebService.Function = Svc_unknown;
|
Gbl.WebService.Function = Svc_unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,9 @@
|
||||||
#include "swad_file_browser.h"
|
#include "swad_file_browser.h"
|
||||||
#include "swad_forum.h"
|
#include "swad_forum.h"
|
||||||
#include "swad_holiday.h"
|
#include "swad_holiday.h"
|
||||||
#include "swad_icon.h"
|
#include "swad_image.h"
|
||||||
#include "swad_import.h"
|
#include "swad_import.h"
|
||||||
|
#include "swad_icon.h"
|
||||||
#include "swad_institution.h"
|
#include "swad_institution.h"
|
||||||
#include "swad_layout.h"
|
#include "swad_layout.h"
|
||||||
#include "swad_link.h"
|
#include "swad_link.h"
|
||||||
|
@ -659,7 +660,6 @@ struct Globals
|
||||||
size_t Length;
|
size_t Length;
|
||||||
} Stem, Feedback;
|
} Stem, Feedback;
|
||||||
char Image[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64+1];
|
char Image[Cry_LENGTH_ENCRYPTED_STR_SHA256_BASE64+1];
|
||||||
bool ChangeImage; // Change image only when teacher uploads a new image
|
|
||||||
bool Shuffle;
|
bool Shuffle;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
@ -719,6 +719,10 @@ struct Globals
|
||||||
float MaxPercent;
|
float MaxPercent;
|
||||||
} DegPhotos;
|
} DegPhotos;
|
||||||
} Stat;
|
} Stat;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
Img_Status_t Status;
|
||||||
|
} Image;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
483
swad_test.c
483
swad_test.c
|
@ -42,6 +42,7 @@
|
||||||
#include "swad_database.h"
|
#include "swad_database.h"
|
||||||
#include "swad_global.h"
|
#include "swad_global.h"
|
||||||
#include "swad_ID.h"
|
#include "swad_ID.h"
|
||||||
|
#include "swad_image.h"
|
||||||
#include "swad_parameter.h"
|
#include "swad_parameter.h"
|
||||||
#include "swad_theme.h"
|
#include "swad_theme.h"
|
||||||
#include "swad_test.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_ShowTstResultAfterAssess (long TstCod,unsigned *NumQstsNotBlank,double *TotalScore);
|
||||||
static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row,
|
static void Tst_WriteQstAndAnsExam (unsigned NumQst,long QstCod,MYSQL_ROW row,
|
||||||
double *ScoreThisQst,bool *AnswerIsNotBlank);
|
double *ScoreThisQst,bool *AnswerIsNotBlank);
|
||||||
static void Tst_WriteQstImage (const char *Image,const char *ClassImg);
|
|
||||||
static void Tst_PutFormToUploadNewQstImage (void);
|
static void Tst_PutFormToUploadNewQstImage (void);
|
||||||
static void Tst_UpdateScoreQst (long QstCod,float ScoreThisQst,bool AnswerIsNotBlank);
|
static void Tst_UpdateScoreQst (long QstCod,float ScoreThisQst,bool AnswerIsNotBlank);
|
||||||
static void Tst_UpdateMyNumAccessTst (unsigned NumAccessesTst);
|
static void Tst_UpdateMyNumAccessTst (unsigned NumAccessesTst);
|
||||||
|
@ -213,13 +213,16 @@ static bool Tst_GetCreateXMLFromForm (void);
|
||||||
static int Tst_CountNumTagsInList (void);
|
static int Tst_CountNumTagsInList (void);
|
||||||
static int Tst_CountNumAnswerTypesInList (void);
|
static int Tst_CountNumAnswerTypesInList (void);
|
||||||
static void Tst_PutFormEditOneQst (char *Stem,char *Feedback);
|
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 Tst_AnswerType_t Tst_ConvertFromUnsignedStrToAnsTyp (const char *UnsignedStr);
|
||||||
static void Tst_GetQstFromForm (char *Stem,char *Feedback);
|
static void Tst_GetQstFromForm (char *Stem,char *Feedback);
|
||||||
static bool Tst_GetImageFromForm (void);
|
|
||||||
static long Tst_GetTagCodFromTagTxt (const char *TagTxt);
|
static long Tst_GetTagCodFromTagTxt (const char *TagTxt);
|
||||||
static long Tst_CreateNewTag (long CrsCod,const char *TagTxt);
|
static long Tst_CreateNewTag (long CrsCod,const char *TagTxt);
|
||||||
static void Tst_EnableOrDisableTag (long TagCod,bool TagHidden);
|
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_InsertOrUpdateQstIntoDB (void);
|
||||||
static void Tst_InsertTagsIntoDB (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\">",
|
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP COLOR%u\">",
|
||||||
Gbl.RowEvenOdd);
|
Gbl.RowEvenOdd);
|
||||||
Tst_WriteQstStem (row[4],"TEST_EXA");
|
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)
|
if (Gbl.Action.Act == ActSeeTst)
|
||||||
Tst_WriteAnswersOfAQstSeeExam (NumQst,QstCod,(Str_ConvertToUpperLetter (row[3][0]) == 'Y'));
|
Tst_WriteAnswersOfAQstSeeExam (NumQst,QstCod,(Str_ConvertToUpperLetter (row[3][0]) == 'Y'));
|
||||||
else // Assessing exam / Viewing old exam
|
else // Assessing exam / Viewing old exam
|
||||||
|
@ -1016,48 +1023,6 @@ void Tst_WriteQstStem (const char *Stem,const char *ClassStem)
|
||||||
free ((void *) StemRigorousHTML);
|
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 ************/
|
/************* 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\">",
|
fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP COLOR%u\">",
|
||||||
Gbl.RowEvenOdd);
|
Gbl.RowEvenOdd);
|
||||||
Tst_WriteQstStem (row[4],"TEST_EDI");
|
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_WriteQstFeedback (row[6],"TEST_EDI_LIGHT");
|
||||||
Tst_WriteAnswersOfAQstEdit (QstCod);
|
Tst_WriteAnswersOfAQstEdit (QstCod);
|
||||||
fprintf (Gbl.F.Out,"</td>");
|
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_Save;
|
||||||
extern const char *Txt_Create_question;
|
extern const char *Txt_Create_question;
|
||||||
char Title[512];
|
char Title[512];
|
||||||
char Query[512];
|
|
||||||
MYSQL_RES *mysql_res;
|
MYSQL_RES *mysql_res;
|
||||||
MYSQL_ROW row;
|
MYSQL_ROW row;
|
||||||
unsigned long NumRow,NumRows;
|
unsigned long NumRows;
|
||||||
|
unsigned long NumRow;
|
||||||
unsigned NumOpt;
|
unsigned NumOpt;
|
||||||
Tst_AnswerType_t AnsType;
|
Tst_AnswerType_t AnsType;
|
||||||
bool Shuffle = false;
|
|
||||||
unsigned NumTag;
|
unsigned NumTag;
|
||||||
bool TagNotFound;
|
bool TagNotFound;
|
||||||
bool OptionsDisabled;
|
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 ();
|
Tst_InitQst ();
|
||||||
if (Tst_GetQstCod ()) // If parameter QstCod received ==> question already exists in the database
|
if (Tst_GetQstCod ()) // If parameter QstCod received ==>
|
||||||
{
|
// ==> question already exists in the database
|
||||||
/***** Get the type of answer and the stem from the database *****/
|
Tst_GetQstDataFromDB (Stem,Feedback);
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***** Start form and table *****/
|
/***** Start form and table *****/
|
||||||
|
@ -4438,7 +4307,8 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
|
||||||
"<td class=\"LEFT_TOP\">",
|
"<td class=\"LEFT_TOP\">",
|
||||||
The_ClassForm[Gbl.Prefs.Theme],
|
The_ClassForm[Gbl.Prefs.Theme],
|
||||||
Txt_Image);
|
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 ();
|
Tst_PutFormToUploadNewQstImage ();
|
||||||
fprintf (Gbl.F.Out,"</td>"
|
fprintf (Gbl.F.Out,"</td>"
|
||||||
"</tr>");
|
"</tr>");
|
||||||
|
@ -4560,7 +4430,7 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback)
|
||||||
"<td class=\"%s LEFT_TOP\">"
|
"<td class=\"%s LEFT_TOP\">"
|
||||||
"<input type=\"checkbox\" name=\"Shuffle\" value=\"Y\"",
|
"<input type=\"checkbox\" name=\"Shuffle\" value=\"Y\"",
|
||||||
The_ClassForm[Gbl.Prefs.Theme]);
|
The_ClassForm[Gbl.Prefs.Theme]);
|
||||||
if (Shuffle)
|
if (Gbl.Test.Shuffle)
|
||||||
fprintf (Gbl.F.Out," checked=\"checked\"");
|
fprintf (Gbl.F.Out," checked=\"checked\"");
|
||||||
if (Gbl.Test.AnswerType != Tst_ANS_UNIQUE_CHOICE &&
|
if (Gbl.Test.AnswerType != Tst_ANS_UNIQUE_CHOICE &&
|
||||||
Gbl.Test.AnswerType != Tst_ANS_MULTIPLE_CHOICE)
|
Gbl.Test.AnswerType != Tst_ANS_MULTIPLE_CHOICE)
|
||||||
|
@ -4676,7 +4546,7 @@ void Tst_InitQst (void)
|
||||||
Gbl.Test.Feedback.Text = NULL;
|
Gbl.Test.Feedback.Text = NULL;
|
||||||
Gbl.Test.Feedback.Length = 0;
|
Gbl.Test.Feedback.Length = 0;
|
||||||
Gbl.Test.Image[0] = '\0';
|
Gbl.Test.Image[0] = '\0';
|
||||||
Gbl.Test.ChangeImage = false;
|
Gbl.Test.Shuffle = false;
|
||||||
Gbl.Test.AnswerType = Tst_ANS_UNIQUE_CHOICE;
|
Gbl.Test.AnswerType = Tst_ANS_UNIQUE_CHOICE;
|
||||||
Gbl.Test.Answer.NumOptions = 0;
|
Gbl.Test.Answer.NumOptions = 0;
|
||||||
Gbl.Test.Answer.TF = ' ';
|
Gbl.Test.Answer.TF = ' ';
|
||||||
|
@ -4692,6 +4562,159 @@ void Tst_InitQst (void)
|
||||||
Gbl.Test.Answer.FloatingPoint[1] = 0.0;
|
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 ***/
|
/** 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 ())
|
if (Tst_CheckIfQstFormatIsCorrectAndCountNumOptions ())
|
||||||
{
|
{
|
||||||
/***** Form is received OK ==> insert or update question and answer in the database *****/
|
/***** 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 */
|
/* Insert or update question, tags and answer in the database */
|
||||||
Tst_InsertOrUpdateQstTagsAnsIntoDB ();
|
Tst_InsertOrUpdateQstTagsAnsIntoDB ();
|
||||||
|
|
||||||
|
@ -4753,6 +4780,7 @@ void Tst_ReceiveQst (void)
|
||||||
Tst_ListOneQstToEdit ();
|
Tst_ListOneQstToEdit ();
|
||||||
}
|
}
|
||||||
else // Question is wrong
|
else // Question is wrong
|
||||||
|
/***** Put form to edit question again *****/
|
||||||
Tst_PutFormEditOneQst (Stem,Feedback);
|
Tst_PutFormEditOneQst (Stem,Feedback);
|
||||||
|
|
||||||
/***** Free answers *****/
|
/***** Free answers *****/
|
||||||
|
@ -4815,7 +4843,17 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback)
|
||||||
Par_GetParToHTML ("Feedback",Feedback,Cns_MAX_BYTES_TEXT);
|
Par_GetParToHTML ("Feedback",Feedback,Cns_MAX_BYTES_TEXT);
|
||||||
|
|
||||||
/***** Get new image (if present ==> create file) *****/
|
/***** 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 *****/
|
/***** Get answers *****/
|
||||||
Gbl.Test.Shuffle = false;
|
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);
|
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 **********************/
|
/*********************** Check if a question is correct **********************/
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -5419,16 +5354,15 @@ void Tst_ChangeShuffleQst (void)
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/************ Get the parameter with the code of a test question *************/
|
/************ 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];
|
char LongStr[1+10+1];
|
||||||
|
|
||||||
Par_GetParToText ("QstCod",LongStr,1+10);
|
Par_GetParToText ("QstCod",LongStr,1+10);
|
||||||
if ((Gbl.Test.QstCod = Str_ConvertStrCodToLongCod (LongStr)) < 0)
|
if ((Gbl.Test.QstCod = Str_ConvertStrCodToLongCod (LongStr)) < 0)
|
||||||
return 0;
|
return false;
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -5478,14 +5412,19 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
|
||||||
Gbl.Test.Image,
|
Gbl.Test.Image,
|
||||||
Gbl.Test.Feedback.Text ? Gbl.Test.Feedback.Text : "");
|
Gbl.Test.Feedback.Text ? Gbl.Test.Feedback.Text : "");
|
||||||
Gbl.Test.QstCod = DB_QueryINSERTandReturnCode (Query,"can not create question");
|
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
|
else // It's an existing question
|
||||||
{
|
{
|
||||||
/***** Update existing question *****/
|
/***** Update existing question *****/
|
||||||
if (Gbl.Test.ChangeImage)
|
if (Gbl.Image.Status == Img_FILE_MOVED)
|
||||||
{
|
{
|
||||||
/* Remove file with the previous image
|
/* Remove possible file with the old image
|
||||||
(the file with the new image is already created) */
|
(the new image file is already processed
|
||||||
|
and moved to the definitive directory) */
|
||||||
Tst_RemoveImageFilesFromQstsInCrs (Gbl.CurrentCrs.Crs.CrsCod,Gbl.Test.QstCod);
|
Tst_RemoveImageFilesFromQstsInCrs (Gbl.CurrentCrs.Crs.CrsCod,Gbl.Test.QstCod);
|
||||||
|
|
||||||
/* Update question in database */
|
/* Update question in database */
|
||||||
|
@ -5500,6 +5439,10 @@ static void Tst_InsertOrUpdateQstIntoDB (void)
|
||||||
Gbl.Test.Image,
|
Gbl.Test.Image,
|
||||||
Gbl.Test.Feedback.Text ? Gbl.Test.Feedback.Text : "",
|
Gbl.Test.Feedback.Text ? Gbl.Test.Feedback.Text : "",
|
||||||
Gbl.Test.QstCod,Gbl.CurrentCrs.Crs.CrsCod);
|
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
|
else // Do not change image
|
||||||
sprintf (Query,"UPDATE tst_questions"
|
sprintf (Query,"UPDATE tst_questions"
|
||||||
|
|
|
@ -41499,21 +41499,21 @@ const char *Txt_The_file_is_not_X = // Warning: it is very important to include
|
||||||
#if L==1
|
#if L==1
|
||||||
"El archivo no es <em>%s</em>."; // Necessita traduccio
|
"El archivo no es <em>%s</em>."; // Necessita traduccio
|
||||||
#elif L==2
|
#elif L==2
|
||||||
"The file is not <em>%s</em>."; // Need Übersetzung
|
"The file is not <em>%s</em>."; // Need Übersetzung
|
||||||
#elif L==3
|
#elif L==3
|
||||||
"The file is not <em>%s</em>.";
|
"The file is not <em>%s</em>.";
|
||||||
#elif L==4
|
#elif L==4
|
||||||
"El archivo no es <em>%s</em>.";
|
"El archivo no es <em>%s</em>.";
|
||||||
#elif L==5
|
#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
|
#elif L==6
|
||||||
"El archivo no es <em>%s</em>."; // Okoteve traducción
|
"El archivo no es <em>%s</em>."; // Okoteve traducción
|
||||||
#elif L==7
|
#elif L==7
|
||||||
"IL file non è <em>%s</em>.";
|
"IL file non è <em>%s</em>.";
|
||||||
#elif L==8
|
#elif L==8
|
||||||
"The file is not <em>%s</em>."; // Potrzebujesz tlumaczenie
|
"The file is not <em>%s</em>."; // Potrzebujesz tlumaczenie
|
||||||
#elif L==9
|
#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
|
#endif
|
||||||
|
|
||||||
const char *Txt_The_file_of_folder_no_longer_exists_or_is_now_hidden =
|
const char *Txt_The_file_of_folder_no_longer_exists_or_is_now_hidden =
|
||||||
|
|
Loading…
Reference in New Issue
Block a user