From 1af2a3277e96f48a70fce7c8324d0b4146da870b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Ca=C3=B1as=20Vargas?= Date: Sun, 17 Mar 2019 14:47:58 +0100 Subject: [PATCH] Version18.78 --- swad_changelog.h | 9 ++- swad_forum.c | 14 ++-- swad_media.c | 196 +++++++++++++++++++++++------------------------ swad_media.h | 50 ++++++------ swad_message.c | 7 +- swad_test.c | 79 ++++++++----------- swad_text.c | 20 ++--- swad_timeline.c | 18 +++-- 8 files changed, 191 insertions(+), 202 deletions(-) diff --git a/swad_changelog.h b/swad_changelog.h index b762b82cd..8395d56bf 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -427,6 +427,8 @@ Lo de mutear anuncios, en principio prefiero hacer una opci // Para bloquear/desbloquear se usará un icono candado // Para preasignado/no preasignado usar otro icono (usuario/usuario tachado, por ej.) +// TODO: Check if Feedback is set to "(null)" when importing test questions. Change columns Feedback "(null)" to "" in test questions + // TODO: Select users individually in action to send email // TODO: Actualizar ayuda en GitHub, por ejemplo Preferencias ahora es Ajustes. @@ -457,7 +459,7 @@ En OpenSWAD: ps2pdf source.ps destination.pdf */ -#define Log_PLATFORM_VERSION "SWAD 18.77.5 (2019-03-17)" +#define Log_PLATFORM_VERSION "SWAD 18.77.6 (2019-03-17)" #define CSS_FILE "swad18.77.css" #define JS_FILE "swad18.77.js" /* @@ -473,7 +475,10 @@ Si el usuario acepta, a partir de ese momento se le mostrar Ese bloqueo o aceptación sería una opción en Perfil > Ajustes que el usuario podría cambiar en cualquier momento. - Version 18.77.5: Mar 16, 2019 Fixed bugs in forms to upload media. (240272 lines) + Version 18.79: Mar 17, 2019 YouTube videos are not shown if user doesn't accept third party cookies. (? lines) + Version 18.78: Mar 17, 2019 Fixed bugs and code refactoring in media. + Code refactoring in tests. (240252 lines) + Version 18.77.5: Mar 17, 2019 Fixed bugs in forms to upload media. (240272 lines) Version 18.77.4: Mar 16, 2019 Changes in form to upload media. (240256 lines) Version 18.77.3: Mar 16, 2019 Embedded YouTube videos. Not finished. (240252 lines) Version 18.77.2: Mar 14, 2019 Embedded YouTube videos. Not finished. (240267 lines) diff --git a/swad_forum.c b/swad_forum.c index 9c0635a5b..095e2013f 100644 --- a/swad_forum.c +++ b/swad_forum.c @@ -501,9 +501,9 @@ static long For_InsertForumPst (long ThrCod,long UsrCod, long PstCod; /***** Check if image is received and processed *****/ - if (Media->Action == Med_ACTION_NEW_MEDIA && // Upload new image - Media->Status == Med_PROCESSED) // The new image received has been processed - /* Move processed image to definitive directory */ + if (Media->Action == Med_ACTION_NEW_MEDIA && // New media + Media->Status == Med_PROCESSED) // The new media received has been processed + /* Move processed media to definitive directory */ Med_MoveMediaToDefinitiveDir (Media); /***** Insert forum post in the database *****/ @@ -973,6 +973,7 @@ static void For_ShowPostsOfAThread (Ale_AlertType_t AlertType,const char *Messag /***** Show alert after action *****/ Lay_StartSection (For_FORUM_POSTS_SECTION_ID); + Ale_ShowAlerts (For_FORUM_POSTS_SECTION_ID); // Possible pending alerts if (Message) if (Message[0]) Ale_ShowAlert (AlertType,Message); @@ -4053,14 +4054,15 @@ void For_ReceiveForumPost (void) Par_GetParAndChangeFormat ("Content",Content,Cns_MAX_BYTES_LONG_TEXT, Str_TO_RIGOROUS_HTML,false); - /***** Initialize image *****/ + /***** Initialize media *****/ Med_MediaConstructor (&Media); - /***** Get attached image (action, file and title) *****/ + /***** Get attached media *****/ Media.Width = For_IMAGE_SAVED_MAX_WIDTH; Media.Height = For_IMAGE_SAVED_MAX_HEIGHT; Media.Quality = For_IMAGE_SAVED_QUALITY; - Med_GetMediaFromForm (-1,&Media,NULL); + Med_GetMediaFromForm (-1,&Media,NULL, + For_FORUM_POSTS_SECTION_ID); // Alerts will be shown later in posts section /***** Create a new message *****/ if (IsReply) // This post is a reply to another posts in the thread diff --git a/swad_media.c b/swad_media.c index 58812a8e0..123cb0d80 100644 --- a/swad_media.c +++ b/swad_media.c @@ -103,6 +103,10 @@ extern struct Globals Gbl; /***************************** Internal prototypes ***************************/ /*****************************************************************************/ +static void Med_ResetMediaExceptURLAndTitle (struct Media *Media); +static void Med_FreeMediaURL (struct Media *Media); +static void Med_FreeMediaTitle (struct Media *Media); + static Med_Action_t Med_GetMediaActionFromForm (const char *ParamAction); static Med_FormType_t Usr_GetFormTypeFromForm (struct ParamUploadMedia *ParamUploadMedia); static void Usr_GetURLFromForm (const char *ParamName,struct Media *Media); @@ -153,62 +157,59 @@ static Med_Type_t Med_GetTypeFromExtAndMIME (const char *Extension, const char *MIMEType); /*****************************************************************************/ -/********************* Reset media (image/video) fields **********************/ +/********************** Media (image/video) constructor **********************/ /*****************************************************************************/ // Every struct Media must be initialized with this constructor function after it is declared // Every call to constructor must have a call to destructor void Med_MediaConstructor (struct Media *Media) { - Med_ResetMediaExceptTitleAndURL (Media); - Media->Title = NULL; + Med_ResetMediaExceptURLAndTitle (Media); Media->URL = NULL; + Media->Title = NULL; } /*****************************************************************************/ -/***************** Reset image fields except title and URL *******************/ -/*****************************************************************************/ - -void Med_ResetMediaExceptTitleAndURL (struct Media *Media) - { - Media->Action = Med_ACTION_NO_MEDIA; - Media->Status = Med_STATUS_NONE; - Media->Name[0] = '\0'; - Media->Type = Med_TYPE_NONE; - } - -/*****************************************************************************/ -/************************ Free media (image/video) ***************************/ +/********************** Media (image/video) destructor ***********************/ /*****************************************************************************/ // Every call to constructor must have a call to destructor void Med_MediaDestructor (struct Media *Media) { - Med_FreeMediaTitle (Media); - Med_FreeMediaURL (Media); + Med_ResetMedia (Media); } /*****************************************************************************/ -/****************************** Free image title *****************************/ +/********************* Reset media (image/video) fields **********************/ +/*****************************************************************************/ +// Media must be constructed before calling this function + +void Med_ResetMedia (struct Media *Media) + { + Med_ResetMediaExceptURLAndTitle (Media); + Med_FreeMediaURL (Media); + Med_FreeMediaTitle (Media); + } + +/*****************************************************************************/ +/***************** Reset media fields except URL and title *******************/ /*****************************************************************************/ -void Med_FreeMediaTitle (struct Media *Media) +static void Med_ResetMediaExceptURLAndTitle (struct Media *Media) { - // Media->Title must be initialized to NULL after declaration - if (Media->Title) - { - free ((void *) Media->Title); - Media->Title = NULL; - } + Media->Action = Med_ACTION_NO_MEDIA; + Media->Status = Med_STATUS_NONE; + Media->Name[0] = '\0'; + Media->Type = Med_TYPE_NONE; } /*****************************************************************************/ /******************************* Free image URL ******************************/ /*****************************************************************************/ -void Med_FreeMediaURL (struct Media *Media) +static void Med_FreeMediaURL (struct Media *Media) { - // Media->URL must be initialized to NULL after declaration + // Media->URL is initialized to NULL in constructor if (Media->URL) { free ((void *) Media->URL); @@ -216,6 +217,20 @@ void Med_FreeMediaURL (struct Media *Media) } } +/*****************************************************************************/ +/****************************** Free image title *****************************/ +/*****************************************************************************/ + +static void Med_FreeMediaTitle (struct Media *Media) + { + // Media->Title is initialized to NULL in constructor + if (Media->Title) + { + free ((void *) Media->Title); + Media->Title = NULL; + } + } + /*****************************************************************************/ /**** Get media name, title and URL from a query result and copy to struct ***/ /*****************************************************************************/ @@ -391,32 +406,28 @@ void Med_PutMediaUploader (int NumMediaInForm,const char *ClassInput) /*****************************************************************************/ /******************** Get media (image/video) from form **********************/ /*****************************************************************************/ +// Media constructor must be called before calling this function // If NumMediaInForm < 0, params have no suffix // If NumMediaInForm >= 0, the number is a suffix of the params void Med_GetMediaFromForm (int NumMediaInForm,struct Media *Media, - void (*GetMediaFromDB) (int NumMediaInForm,struct Media *Media)) + void (*GetMediaFromDB) (int NumMediaInForm,struct Media *Media), + const char *SectionForAlerts) { + extern const char *Txt_Error_sending_or_processing_image_video; struct ParamUploadMedia ParamUploadMedia; + Med_Action_t Action; Med_FormType_t FormType; - /***** Initialize media *****/ - Media->Action = Med_ACTION_NO_MEDIA; - Media->Status = Med_STATUS_NONE; - Media->Name[0] = '\0'; - Media->Type = Med_TYPE_NONE; - Media->Title = NULL; - Media->URL = NULL; - /***** Set names of parameters depending on number of media in form *****/ Med_SetParamNames (&ParamUploadMedia,NumMediaInForm); /***** Get action and initialize media (image/video) (except title, that will be get after the media file) *****/ - Media->Action = Med_GetMediaActionFromForm (ParamUploadMedia.Action); + Action = Med_GetMediaActionFromForm (ParamUploadMedia.Action); /***** Get the media (image/video) name and the file *****/ - switch (Media->Action) + switch (Action) { case Med_ACTION_NEW_MEDIA: // Upload new image/video /***** Get form type *****/ @@ -426,76 +437,59 @@ void Med_GetMediaFromForm (int NumMediaInForm,struct Media *Media, switch (FormType) { case Med_FORM_FILE: + Media->Action = Med_ACTION_NEW_MEDIA; + /* Get image/video (if present ==> process and create temporary file) */ Med_GetAndProcessFileFromForm (ParamUploadMedia.File,Media); - switch (Media->Status) - { - case Med_STATUS_NONE: // No new image/video received - Media->Action = Med_ACTION_NO_MEDIA; - Media->Name[0] = '\0'; - Media->Type = Med_TYPE_NONE; - break; - case Med_RECEIVED: // New image/video received, but not processed - Media->Status = Med_STATUS_NONE; - Media->Name[0] = '\0'; - Media->Type = Med_TYPE_NONE; - break; - case Med_PROCESSED: - Usr_GetURLFromForm (ParamUploadMedia.URL,Media); - Usr_GetTitleFromForm (ParamUploadMedia.Title,Media); - break; - default: - break; + + /* Check status of media after getting and processing it */ + if (Media->Status == Med_PROCESSED) + { + Usr_GetURLFromForm (ParamUploadMedia.URL,Media); + Usr_GetTitleFromForm (ParamUploadMedia.Title,Media); + } + else + { + /* Create alert with warning */ + Ale_CreateAlert (Ale_WARNING,SectionForAlerts, + Txt_Error_sending_or_processing_image_video); + + /* Reset media (no media will be saved into database) */ + Med_ResetMedia (Media); } break; case Med_FORM_EMBED: + Media->Action = Med_ACTION_NEW_MEDIA; + /* Get and process embed URL from form */ Med_GetAndProcessEmbedFromForm (ParamUploadMedia.URL,Media); + + /* Check status of media after getting and processing it */ + if (Media->Status != Med_PROCESSED) + { + /* Create alert with warning */ + Ale_CreateAlert (Ale_WARNING,SectionForAlerts, + Txt_Error_sending_or_processing_image_video); + + /* Reset media (no media will be saved into database) */ + Med_ResetMedia (Media); + } break; - default: + default: // No media form selected + Media->Action = Med_ACTION_NO_MEDIA; break; } break; case Med_ACTION_KEEP_MEDIA: // Keep current image/video unchanged + Media->Action = Med_ACTION_KEEP_MEDIA; + /***** Get media name *****/ if (GetMediaFromDB != NULL) GetMediaFromDB (NumMediaInForm,Media); break; - case Med_ACTION_CHANGE_MEDIA: // Replace old image/video by new one - /***** Get form type *****/ - FormType = Usr_GetFormTypeFromForm (&ParamUploadMedia); - - /***** Get new media *****/ - switch (FormType) - { - case Med_FORM_FILE: - /* Get image/video (if present ==> - process and create temporary file) */ - Med_GetAndProcessFileFromForm (ParamUploadMedia.File,Media); - switch (Media->Status) - { - case Med_PROCESSED: - Usr_GetURLFromForm (ParamUploadMedia.URL,Media); - Usr_GetTitleFromForm (ParamUploadMedia.Title,Media); - break; - default: - break; - } - break; - case Med_FORM_EMBED: - /* Get and process embed URL from form */ - Med_GetAndProcessEmbedFromForm (ParamUploadMedia.URL,Media); - break; - default: - break; - } - if (Media->Status != Med_PROCESSED && // No new media received-processed successfully - GetMediaFromDB != NULL) - /* Get media (image/video) name */ - GetMediaFromDB (NumMediaInForm,Media); - break; - default: + default: // Unknown action + Media->Action = Med_ACTION_NO_MEDIA; break; } } @@ -687,8 +681,6 @@ static void Med_GetAndProcessFileFromForm (const char *ParamFile, if (Fil_EndReceptionOfFile (PathFileOrg,Param)) // Success { - Media->Status = Med_RECEIVED; - /***** Detect if animated GIF *****/ if (Media->Type == Med_GIF) if (!Med_DetectIfAnimated (Media,PathMedPrivTmp,PathFileOrg)) @@ -853,8 +845,9 @@ static void Med_ProcessGIF (struct Media *Media, { /* Show warning alert */ Fil_WriteFileSizeBrief ((double) Med_MAX_SIZE_GIF,FileSizeStr); - Ale_ShowAlert (Ale_WARNING,Txt_The_size_of_the_file_exceeds_the_maximum_allowed_X, - FileSizeStr); + Ale_CreateAlert (Ale_WARNING,NULL, + Txt_The_size_of_the_file_exceeds_the_maximum_allowed_X, + FileSizeStr); } } else // Error getting file data @@ -897,8 +890,9 @@ static void Med_ProcessVideo (struct Media *Media, { /* Show warning alert */ Fil_WriteFileSizeBrief ((double) Med_MAX_SIZE_MP4,FileSizeStr); - Ale_ShowAlert (Ale_WARNING,Txt_The_size_of_the_file_exceeds_the_maximum_allowed_X, - FileSizeStr); + Ale_CreateAlert (Ale_WARNING,NULL, + Txt_The_size_of_the_file_exceeds_the_maximum_allowed_X, + FileSizeStr); } } else // Error getting file data @@ -1133,8 +1127,8 @@ void Med_MoveMediaToDefinitiveDir (struct Media *Media) char PathMedPriv[PATH_MAX + 1]; /***** Check trivial cases *****/ - if (Media->Type == Med_TYPE_NONE) - Lay_ShowErrorAndExit ("Med_MoveMediaToDefinitiveDir: Wrong media type."); + if (Media->Status != Med_PROCESSED) + return; if (Media->Type == Med_YOUTUBE) return; // Nothing to do with files @@ -1182,8 +1176,6 @@ void Med_MoveMediaToDefinitiveDir (struct Media *Media) Lay_ShowErrorAndExit ("Wrong media type."); break; } - - Media->Status = Med_FILE_MOVED; // Success } /*****************************************************************************/ diff --git a/swad_media.h b/swad_media.h index 65d36e3f8..f0a7ed5d4 100644 --- a/swad_media.h +++ b/swad_media.h @@ -44,13 +44,12 @@ /*****************************************************************************/ /***** Action to perform when editing a form with an image/video *****/ -#define Med_NUM_ACTIONS 4 +#define Med_NUM_ACTIONS 3 typedef enum { - Med_ACTION_NEW_MEDIA, // Upload new media - Med_ACTION_KEEP_MEDIA, // Keep current media unchanged - Med_ACTION_CHANGE_MEDIA, // Change existing media by a new one Med_ACTION_NO_MEDIA, // Do not use media (remove current media if exists) + Med_ACTION_KEEP_MEDIA, // Keep current media unchanged + Med_ACTION_NEW_MEDIA, // Upload new media (if current media exists, remove and change by the new one) } Med_Action_t; #define Med_ACTION_DEFAULT Med_ACTION_NO_MEDIA @@ -59,22 +58,22 @@ typedef enum No file Original file Temporary Definitive Name of the image/video uploaded uploaded by user processed file processed file stored in database --------- --------------------------- ------------------ ------------------ --------------------- -Med_NONE Med_FILE_RECEIVED Med_FILE_PROCESSED Med_FILE_MOVED Med_NAME_STORED_IN_DB +Med_STATUS_NONE Med_PROCESSED Med_NAME_STORED_IN_DB --------- --------------------------- ------------------ ------------------ --------------------- --> upload-file -> -> process-file -> b -> move-file -> -> insert-name -> + upload-file process file move file insert in database --------- --------------------------- ------------------ ------------------ --------------------- -file.ext / / / xx-unique-name - | | | - var var var - | | | - www www www - | | | - swad swad swad - | | | - med med med - | | | - tmp tmp xx (2 first chars) - | | | +file.ext / / / xx-unique-name + | | | + var var var + | | | + www www www + | | | + swad swad swad + | | | + med med med + | | | + tmp tmp xx (2 first chars) + | | | xx-unique-name_original.ext xx-unique-name.jpg xx-unique-name.jpg xx-unique-name: a unique name encrypted starting by two random chars xx @@ -82,9 +81,7 @@ xx-unique-name: a unique name encrypted starting by two random chars xx typedef enum { Med_STATUS_NONE, - Med_RECEIVED, Med_PROCESSED, - Med_FILE_MOVED, Med_STORED_IN_DB, } Med_Status_t; @@ -107,11 +104,13 @@ struct Media Med_Status_t Status; char Name[Med_BYTES_NAME + 1]; Med_Type_t Type; - char *Title; // Title/attribution (it must be initialized to NULL - // in order to not trying to free it when no memory allocated) + bool URLIsAllocated; + bool TitleIsAllocated; char *URL; // URL, i.e. link to original big photo or video // (it must be initialized to NULL // in order to not trying to free it when no memory allocated) + char *Title; // Title/attribution (it must be initialized to NULL + // in order to not trying to free it when no memory allocated) unsigned Width; unsigned Height; unsigned Quality; @@ -133,10 +132,8 @@ struct ParamUploadMedia /*****************************************************************************/ void Med_MediaConstructor (struct Media *Media); -void Med_ResetMediaExceptTitleAndURL (struct Media *Media); void Med_MediaDestructor (struct Media *Media); -void Med_FreeMediaTitle (struct Media *Media); -void Med_FreeMediaURL (struct Media *Media); +void Med_ResetMedia (struct Media *Media); void Med_GetMediaDataFromRow (const char *Name, const char *TypeStr, @@ -146,7 +143,8 @@ void Med_GetMediaDataFromRow (const char *Name, void Med_PutMediaUploader (int NumMediaInForm,const char *ClassInput); void Med_GetMediaFromForm (int NumMediaInForm,struct Media *Media, - void (*GetMediaFromDB) (int NumMediaInForm,struct Media *Media)); + void (*GetMediaFromDB) (int NumMediaInForm,struct Media *Media), + const char *SectionForAlerts); void Med_SetParamNames (struct ParamUploadMedia *ParamUploadMedia,int NumMediaInForm); void Med_MoveMediaToDefinitiveDir (struct Media *Media); diff --git a/swad_message.c b/swad_message.c index 29658cedb..0b348aeb9 100644 --- a/swad_message.c +++ b/swad_message.c @@ -732,7 +732,8 @@ void Msg_RecMsgFromUsr (void) Media.Width = Msg_IMAGE_SAVED_MAX_WIDTH; Media.Height = Msg_IMAGE_SAVED_MAX_HEIGHT; Media.Quality = Msg_IMAGE_SAVED_QUALITY; - Med_GetMediaFromForm (-1,&Media,NULL); + Med_GetMediaFromForm (-1,&Media,NULL,NULL); + Ale_ShowAlerts (NULL); /***** Loop over the list Gbl.Usrs.Selected.List[Rol_UNK], that holds the list of the recipients, creating a received message for each recipient *****/ @@ -1281,8 +1282,8 @@ static long Msg_InsertNewMsg (const char *Subject,const char *Content, long MsgCod; /***** Check if image is received and processed *****/ - if (Media->Action == Med_ACTION_NEW_MEDIA && // Upload new image - Media->Status == Med_PROCESSED) // The new image received has been processed + if (Media->Action == Med_ACTION_NEW_MEDIA && // New media + Media->Status == Med_PROCESSED) // The new media received has been processed /* Move processed image to definitive directory */ Med_MoveMediaToDefinitiveDir (Media); diff --git a/swad_test.c b/swad_test.c index 2c8abc3c1..56abb7295 100644 --- a/swad_test.c +++ b/swad_test.c @@ -247,7 +247,7 @@ static void Tst_PutTFInputField (const char *Label,char Value); static void Tst_FreeTextChoiceAnswers (void); static void Tst_FreeTextChoiceAnswer (unsigned NumOpt); -static void Tst_InitMediaOfQuestion (void); +static void Tst_ResetMediaOfQuestion (void); static void Tst_FreeMediaOfQuestion (void); static void Tst_GetQstDataFromDB (char Stem[Cns_MAX_BYTES_TEXT + 1], @@ -1162,7 +1162,7 @@ static void Tst_PutFormToEditQstMedia (struct Media *Media,int NumMediaInForm, " value=\"%u\"", The_ClassFormInBox[Gbl.Prefs.Theme], UniqueId,ParamUploadMedia.Action, - Med_ACTION_CHANGE_MEDIA); // Replace existing image by new image + Med_ACTION_NEW_MEDIA); // Replace existing image by new image if (OptionsDisabled) fprintf (Gbl.F.Out," disabled=\"disabled\""); fprintf (Gbl.F.Out," />" @@ -5476,24 +5476,18 @@ static void Tst_FreeTextChoiceAnswer (unsigned NumOpt) /***************** Initialize images of a question to zero *******************/ /*****************************************************************************/ -static void Tst_InitMediaOfQuestion (void) +static void Tst_ResetMediaOfQuestion (void) { unsigned NumOpt; - /***** Initialize image *****/ - Med_ResetMediaExceptTitleAndURL (&Gbl.Test.Media); - Med_FreeMediaTitle (&Gbl.Test.Media); - Med_FreeMediaURL (&Gbl.Test.Media); + /***** Reset media for stem *****/ + Med_ResetMedia (&Gbl.Test.Media); + /***** Reset media for every answer option *****/ for (NumOpt = 0; NumOpt < Tst_MAX_OPTIONS_PER_QUESTION; NumOpt++) - { - /***** Initialize image *****/ - Med_ResetMediaExceptTitleAndURL (&Gbl.Test.Answer.Options[NumOpt].Media); - Med_FreeMediaTitle (&Gbl.Test.Media); - Med_FreeMediaURL (&Gbl.Test.Media); - } + Med_ResetMedia (&Gbl.Test.Answer.Options[NumOpt].Media); } /*****************************************************************************/ @@ -5751,7 +5745,7 @@ void Tst_ReceiveQst (void) else // Question is wrong { /***** Whether images has been received or not, reset images *****/ - Tst_InitMediaOfQuestion (); + Tst_ResetMediaOfQuestion (); /***** Put form to edit question again *****/ Tst_PutFormEditOneQst (Stem,Feedback); @@ -5828,7 +5822,9 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback) Gbl.Test.Media.Height = Tst_IMAGE_SAVED_MAX_HEIGHT; Gbl.Test.Media.Quality = Tst_IMAGE_SAVED_QUALITY; Med_GetMediaFromForm (-1, // < 0 ==> the image associated to the stem - &Gbl.Test.Media,Tst_GetMediaFromDB); + &Gbl.Test.Media,Tst_GetMediaFromDB, + NULL); + Ale_ShowAlerts (NULL); /***** Get answers *****/ Gbl.Test.Shuffle = false; @@ -5904,7 +5900,9 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback) Gbl.Test.Answer.Options[NumOpt].Media.Quality = Tst_IMAGE_SAVED_QUALITY; Med_GetMediaFromForm ((int) NumOpt, // >= 0 ==> the image associated to an answer &Gbl.Test.Answer.Options[NumOpt].Media, - Tst_GetMediaFromDB); + Tst_GetMediaFromDB, + NULL); + Ale_ShowAlerts (NULL); } } @@ -5963,7 +5961,6 @@ static void Tst_GetQstFromForm (char *Stem,char *Feedback) bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void) { - extern const char *Txt_Error_receiving_or_processing_image; extern const char *Txt_You_must_type_at_least_one_tag_for_the_question; extern const char *Txt_You_must_type_the_stem_of_the_question; extern const char *Txt_You_must_select_a_T_F_answer; @@ -5979,14 +5976,6 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void) bool ThereIsEndOfAnswers; unsigned i; - if ((Gbl.Test.Media.Action == Med_ACTION_NEW_MEDIA || // Upload new image - Gbl.Test.Media.Action == Med_ACTION_CHANGE_MEDIA) && // Replace existing image by new image - Gbl.Test.Media.Status != Med_PROCESSED) - { - Ale_ShowAlert (Ale_WARNING,Txt_Error_receiving_or_processing_image); - return false; - } - /***** This function also counts the number of options. Initialize this number to 0. *****/ Gbl.Test.Answer.NumOptions = 0; @@ -6008,6 +5997,7 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void) switch (Gbl.Test.AnswerType) { case Tst_ANS_INT: + /* First option should be filled */ if (!Gbl.Test.Answer.Options[0].Text) { Ale_ShowAlert (Ale_WARNING,Txt_You_must_enter_an_integer_value_as_the_correct_answer); @@ -6018,10 +6008,12 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void) Ale_ShowAlert (Ale_WARNING,Txt_You_must_enter_an_integer_value_as_the_correct_answer); return false; } + Gbl.Test.Answer.Integer = Tst_GetIntAnsFromStr (Gbl.Test.Answer.Options[0].Text); Gbl.Test.Answer.NumOptions = 1; break; case Tst_ANS_FLOAT: + /* First two options should be filled */ if (!Gbl.Test.Answer.Options[0].Text || !Gbl.Test.Answer.Options[1].Text) { @@ -6034,6 +6026,8 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void) Ale_ShowAlert (Ale_WARNING,Txt_You_must_enter_the_range_of_floating_point_values_allowed_as_answer); return false; } + + /* Lower limit should be <= upper limit */ for (i = 0; i < 2; i++) @@ -6044,30 +6038,23 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void) Ale_ShowAlert (Ale_WARNING,Txt_The_lower_limit_of_correct_answers_must_be_less_than_or_equal_to_the_upper_limit); return false; } + Gbl.Test.Answer.NumOptions = 2; break; case Tst_ANS_TRUE_FALSE: + /* Answer should be 'T' or 'F' */ if (Gbl.Test.Answer.TF != 'T' && Gbl.Test.Answer.TF != 'F') { Ale_ShowAlert (Ale_WARNING,Txt_You_must_select_a_T_F_answer); return false; } + Gbl.Test.Answer.NumOptions = 1; break; case Tst_ANS_UNIQUE_CHOICE: case Tst_ANS_MULTIPLE_CHOICE: - if (!Gbl.Test.Answer.Options[0].Text) // If the first answer is empty - { - Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_two_answers); - return false; - } - if (!Gbl.Test.Answer.Options[0].Text[0]) // If the first answer is empty - { - Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_two_answers); - return false; - } - + /* No option should be empty before a non-empty option */ for (NumOpt = 0, NumLastOpt = 0, ThereIsEndOfAnswers = false; NumOpt < Tst_MAX_OPTIONS_PER_QUESTION; NumOpt++) @@ -6089,12 +6076,14 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void) else ThereIsEndOfAnswers = true; + /* The two first options must be filled */ if (NumLastOpt < 1) { Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_two_answers); return false; } + /* Its mandatory to mark at least one option as correct */ for (NumOpt = 0; NumOpt <= NumLastOpt; NumOpt++) @@ -6107,17 +6096,19 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void) } break; case Tst_ANS_TEXT: + /* First option should be filled */ if (!Gbl.Test.Answer.Options[0].Text) // If the first answer is empty { - Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_two_answers); + Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_answer); return false; } - if (!Gbl.Test.Answer.Options[0].Text[0]) // If the first answer is empty + if (!Gbl.Test.Answer.Options[0].Text[0]) // If the first answer is empty { Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_answer); return false; } + /* No option should be empty before a non-empty option */ for (NumOpt=0, ThereIsEndOfAnswers=false; NumOpt