From 093c17c456c1323e1a347055945a0b39497ad4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Ca=C3=B1as=20Vargas?= Date: Sun, 3 Apr 2016 01:24:20 +0200 Subject: [PATCH] Version 15.175 --- Makefile | 18 +- swad_changelog.h | 5 +- swad_file.c | 6 +- swad_file.h | 1 + swad_file_browser.c | 49 ++--- swad_global.c | 3 + swad_global.h | 8 +- swad_test.c | 483 +++++++++++++++++++------------------------- swad_text.c | 8 +- 9 files changed, 271 insertions(+), 310 deletions(-) diff --git a/Makefile b/Makefile index de86fdcb6..ee7bf9bf1 100644 --- a/Makefile +++ b/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_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 \ diff --git a/swad_changelog.h b/swad_changelog.h index efaf389b8..f807bef59 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -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) diff --git a/swad_file.c b/swad_file.c index 5daff8826..9ac5d2524 100644 --- a/swad_file.c +++ b/swad_file.c @@ -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; } /*****************************************************************************/ diff --git a/swad_file.h b/swad_file.h index 3ba616499..25063471a 100644 --- a/swad_file.h +++ b/swad_file.h @@ -29,6 +29,7 @@ #include // For boolean type #include // For FILE +#include // For time_t /*****************************************************************************/ /************************** Public types and constants ***********************/ diff --git a/swad_file_browser.c b/swad_file_browser.c index 64ef65b4f..2ba9535a1 100644 --- a/swad_file_browser.c +++ b/swad_file_browser.c @@ -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); diff --git a/swad_global.c b/swad_global.c index b129849f5..be0576e0a 100644 --- a/swad_global.c +++ b/swad_global.c @@ -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; } diff --git a/swad_global.h b/swad_global.h index 2306a9498..2ffa19cc7 100644 --- a/swad_global.h +++ b/swad_global.h @@ -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; }; /*****************************************************************************/ diff --git a/swad_test.c b/swad_test.c index 903b18488..5af292a8e 100644 --- a/swad_test.c +++ b/swad_test.c @@ -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,"", 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,"\"\"" - "
", - 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,"", 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,""); @@ -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) "", 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,"" ""); @@ -4560,7 +4430,7 @@ static void Tst_PutFormEditOneQst (char *Stem,char *Feedback) "" " 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.
" - "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" diff --git a/swad_text.c b/swad_text.c index dcca90920..33b03b4c9 100644 --- a/swad_text.c +++ b/swad_text.c @@ -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 %s."; // Necessita traduccio #elif L==2 - "The file is not %s."; // Need Übersetzung + "The file is not %s."; // Need Übersetzung #elif L==3 "The file is not %s."; #elif L==4 "El archivo no es %s."; #elif L==5 - "The file is not %s."; // Besoin de traduction + "The file is not %s."; // Besoin de traduction #elif L==6 "El archivo no es %s."; // Okoteve traducción #elif L==7 "IL file non è %s."; #elif L==8 - "The file is not %s."; // Potrzebujesz tlumaczenie + "The file is not %s."; // Potrzebujesz tlumaczenie #elif L==9 - "The file is not %s."; // Necessita de tradução + "The file is not %s."; // Necessita de tradução #endif const char *Txt_The_file_of_folder_no_longer_exists_or_is_now_hidden =