mirror of https://github.com/acanas/swad-core.git
Version 22.78.1: Mar 22, 2023 Code refactoring in exam sets and announcements.
This commit is contained in:
parent
b4caf9e0f8
commit
0d76ca2af1
|
@ -52,9 +52,11 @@ extern struct Globals Gbl;
|
|||
|
||||
static void Ann_PutIconToAddNewAnnouncement (__attribute__((unused)) void *Args);
|
||||
static void Ann_PutButtonToAddNewAnnouncement (void);
|
||||
static void Ann_DrawAnAnnouncement (long AnnCod,Ann_Status_t Status,
|
||||
const char *Subject,const char *Content,
|
||||
unsigned Roles,
|
||||
|
||||
static void Ann_GetAnnouncementDataFromRow (MYSQL_RES *mysql_res,
|
||||
struct Ann_Announcement *Announcement);
|
||||
|
||||
static void Ann_DrawAnAnnouncement (struct Ann_Announcement *Announcement,
|
||||
bool ShowAllAnnouncements,
|
||||
bool ICanEdit);
|
||||
static void Ann_PutParAnnCod (void *AnnCod);
|
||||
|
@ -71,15 +73,9 @@ void Ann_ShowAllAnnouncements (void)
|
|||
extern const char *Txt_Announcements;
|
||||
extern const char *Txt_No_announcements;
|
||||
MYSQL_RES *mysql_res;
|
||||
MYSQL_ROW row;
|
||||
struct Ann_Announcement Announcement;
|
||||
unsigned NumAnnouncements;
|
||||
unsigned NumAnn;
|
||||
long AnnCod;
|
||||
unsigned Roles;
|
||||
char Subject[Cns_MAX_BYTES_SUBJECT + 1];
|
||||
char Content[Cns_MAX_BYTES_TEXT + 1];
|
||||
unsigned UnsignedNum;
|
||||
Ann_Status_t Status;
|
||||
bool ICanEdit = (Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM);
|
||||
|
||||
/***** Get announcements from database *****/
|
||||
|
@ -107,30 +103,13 @@ void Ann_ShowAllAnnouncements (void)
|
|||
NumAnn < NumAnnouncements;
|
||||
NumAnn++)
|
||||
{
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
|
||||
/* Get announcement code (row[0]) */
|
||||
if (sscanf (row[0],"%ld",&AnnCod) != 1)
|
||||
Err_WrongAnnouncementExit ();
|
||||
|
||||
/* Get status of the announcement (row[1]) */
|
||||
Status = Ann_OBSOLETE_ANNOUNCEMENT;
|
||||
if (sscanf (row[1],"%u",&UnsignedNum) == 1)
|
||||
if (UnsignedNum < Ann_NUM_STATUS)
|
||||
Status = (Ann_Status_t) UnsignedNum;
|
||||
|
||||
/* Get roles (row[2]) */
|
||||
if (sscanf (row[2],"%u",&Roles) != 1)
|
||||
Err_ShowErrorAndExit ("Error when reading roles of announcement.");
|
||||
|
||||
/* Get the subject (row[3]), the content (row[4]), and insert links */
|
||||
Str_Copy (Subject,row[3],sizeof (Subject) - 1);
|
||||
Str_Copy (Content,row[4],sizeof (Content) - 1);
|
||||
ALn_InsertLinks (Content,Cns_MAX_BYTES_TEXT,50);
|
||||
/* Get announcement data */
|
||||
Ann_GetAnnouncementDataFromRow (mysql_res,&Announcement);
|
||||
|
||||
/* Show the announcement */
|
||||
Ann_DrawAnAnnouncement (AnnCod,Status,Subject,Content,
|
||||
Roles,true,ICanEdit);
|
||||
Ann_DrawAnAnnouncement (&Announcement,
|
||||
true, // Show all announcements
|
||||
ICanEdit);
|
||||
}
|
||||
|
||||
/***** Button to add new announcement *****/
|
||||
|
@ -173,12 +152,9 @@ static void Ann_PutButtonToAddNewAnnouncement (void)
|
|||
void Ann_ShowMyAnnouncementsNotMarkedAsSeen (void)
|
||||
{
|
||||
MYSQL_RES *mysql_res;
|
||||
MYSQL_ROW row;
|
||||
unsigned NumAnnouncements;
|
||||
unsigned NumAnn;
|
||||
long AnnCod;
|
||||
char Subject[Cns_MAX_BYTES_SUBJECT + 1];
|
||||
char Content[Cns_MAX_BYTES_TEXT + 1];
|
||||
struct Ann_Announcement Announcement;
|
||||
|
||||
/***** Select announcements not seen *****/
|
||||
Rol_GetRolesInAllCrss (&Gbl.Usrs.Me.UsrDat);
|
||||
|
@ -193,33 +169,60 @@ void Ann_ShowMyAnnouncementsNotMarkedAsSeen (void)
|
|||
NumAnn < NumAnnouncements;
|
||||
NumAnn++)
|
||||
{
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
|
||||
/* Get announcement code (row[0]) */
|
||||
if (sscanf (row[0],"%ld",&AnnCod) != 1)
|
||||
Err_WrongAnnouncementExit ();
|
||||
|
||||
/* Get the subject (row[1]), the content (row[2]), and insert links */
|
||||
Str_Copy (Subject,row[1],sizeof (Subject) - 1);
|
||||
Str_Copy (Content,row[2],sizeof (Content) - 1);
|
||||
ALn_InsertLinks (Content,Cns_MAX_BYTES_TEXT,50);
|
||||
/* Get announcement data */
|
||||
Ann_GetAnnouncementDataFromRow (mysql_res,&Announcement);
|
||||
|
||||
/* Show the announcement */
|
||||
Ann_DrawAnAnnouncement (AnnCod,Ann_ACTIVE_ANNOUNCEMENT,Subject,Content,
|
||||
0,false,false);
|
||||
Ann_DrawAnAnnouncement (&Announcement,
|
||||
false, // Don't show all announcements
|
||||
false); // I can not edit
|
||||
}
|
||||
|
||||
HTM_DIV_End ();
|
||||
}
|
||||
|
||||
/***** Free structure that stores the query result *****/
|
||||
DB_FreeMySQLResult (&mysql_res);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/********************** Get announcement data from row ***********************/
|
||||
/*****************************************************************************/
|
||||
|
||||
static void Ann_GetAnnouncementDataFromRow (MYSQL_RES *mysql_res,
|
||||
struct Ann_Announcement *Announcement)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
unsigned UnsignedNum;
|
||||
|
||||
/***** Get row *****/
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
|
||||
/***** Get announcement code (row[0]) *****/
|
||||
if (sscanf (row[0],"%ld",&Announcement->AnnCod) != 1)
|
||||
Err_WrongAnnouncementExit ();
|
||||
|
||||
/***** Get status of the announcement (row[1]) *****/
|
||||
Announcement->Status = Ann_OBSOLETE_ANNOUNCEMENT;
|
||||
if (sscanf (row[1],"%u",&UnsignedNum) == 1)
|
||||
if (UnsignedNum < Ann_NUM_STATUS)
|
||||
Announcement->Status = (Ann_Status_t) UnsignedNum;
|
||||
|
||||
/***** Get roles (row[2]) *****/
|
||||
if (sscanf (row[2],"%u",&Announcement->Roles) != 1)
|
||||
Err_ShowErrorAndExit ("Error when reading roles of announcement.");
|
||||
|
||||
/***** Get the subject (row[3]), the content (row[4]), and insert links *****/
|
||||
Str_Copy (Announcement->Subject,row[3],sizeof (Announcement->Subject) - 1);
|
||||
Str_Copy (Announcement->Content,row[4],sizeof (Announcement->Content) - 1);
|
||||
ALn_InsertLinks (Announcement->Content,Cns_MAX_BYTES_TEXT,50);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/****************** Draw an announcement as a yellow note ********************/
|
||||
/*****************************************************************************/
|
||||
|
||||
static void Ann_DrawAnAnnouncement (long AnnCod,Ann_Status_t Status,
|
||||
const char *Subject,const char *Content,
|
||||
unsigned Roles,
|
||||
static void Ann_DrawAnAnnouncement (struct Ann_Announcement *Announcement,
|
||||
bool ShowAllAnnouncements,
|
||||
bool ICanEdit)
|
||||
{
|
||||
|
@ -240,30 +243,30 @@ static void Ann_DrawAnAnnouncement (long AnnCod,Ann_Status_t Status,
|
|||
bool SomeRolesAreSelected;
|
||||
|
||||
/***** Begin yellow note *****/
|
||||
HTM_DIV_Begin ("class=\"%s\"",ContainerClass[Status]);
|
||||
HTM_DIV_Begin ("class=\"%s\"",ContainerClass[Announcement->Status]);
|
||||
|
||||
if (ICanEdit)
|
||||
{
|
||||
/***** Icon to remove announcement *****/
|
||||
Ico_PutContextualIconToRemove (ActRemAnn,NULL,
|
||||
Ann_PutParAnnCod,&AnnCod);
|
||||
Ann_PutParAnnCod,&Announcement->AnnCod);
|
||||
|
||||
/***** Icon to hide/unhide the announcement *****/
|
||||
Ico_PutContextualIconToHideUnhide (ActionHideUnhide,NULL, // TODO: Put anchor
|
||||
Ann_PutParAnnCod,&AnnCod,
|
||||
Status == Ann_OBSOLETE_ANNOUNCEMENT);
|
||||
Ann_PutParAnnCod,&Announcement->AnnCod,
|
||||
Announcement->Status == Ann_OBSOLETE_ANNOUNCEMENT);
|
||||
}
|
||||
|
||||
/***** Write the subject of the announcement *****/
|
||||
HTM_DIV_Begin ("class=\"NOTICE_SUBJECT NOTICE_SUBJECT_%s\"",
|
||||
The_GetSuffix ());
|
||||
HTM_Txt (Subject);
|
||||
HTM_Txt (Announcement->Subject);
|
||||
HTM_DIV_End ();
|
||||
|
||||
/***** Write the content of the announcement *****/
|
||||
HTM_DIV_Begin ("class=\"NOTICE_TEXT NOTICE_TEXT_%s\"",
|
||||
The_GetSuffix ());
|
||||
HTM_Txt (Content);
|
||||
HTM_Txt (Announcement->Content);
|
||||
HTM_DIV_End ();
|
||||
|
||||
/***** Write announcement foot *****/
|
||||
|
@ -277,7 +280,7 @@ static void Ann_DrawAnAnnouncement (long AnnCod,Ann_Status_t Status,
|
|||
for (Role = Rol_UNK, SomeRolesAreSelected = false;
|
||||
Role <= Rol_TCH;
|
||||
Role++)
|
||||
if (Roles & (1 << Role))
|
||||
if (Announcement->Roles & (1 << Role))
|
||||
{
|
||||
if (SomeRolesAreSelected)
|
||||
HTM_Comma ();
|
||||
|
@ -288,7 +291,7 @@ static void Ann_DrawAnAnnouncement (long AnnCod,Ann_Status_t Status,
|
|||
else
|
||||
/***** Put form to mark announcement as seen *****/
|
||||
Lay_PutContextualLinkIconText (ActAnnSee,NULL,
|
||||
Ann_PutParAnnCod,&AnnCod,
|
||||
Ann_PutParAnnCod,&Announcement->AnnCod,
|
||||
"times.svg",Ico_BLACK,
|
||||
Txt_Do_not_show_again,NULL);
|
||||
|
||||
|
|
|
@ -23,6 +23,12 @@
|
|||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*****************************************************************************/
|
||||
/*********************************** Headers *********************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
#include "swad_constant.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************************* Public types ********************************/
|
||||
/*****************************************************************************/
|
||||
|
@ -34,6 +40,15 @@ typedef enum
|
|||
Ann_OBSOLETE_ANNOUNCEMENT = 1, // Hidden
|
||||
} Ann_Status_t; // Don't change these numbers because they are used in database
|
||||
|
||||
struct Ann_Announcement
|
||||
{
|
||||
long AnnCod;
|
||||
Ann_Status_t Status;
|
||||
unsigned Roles;
|
||||
char Subject[Cns_MAX_BYTES_SUBJECT + 1];
|
||||
char Content[Cns_MAX_BYTES_TEXT + 1];
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/***************************** Public prototypes *****************************/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -108,8 +108,10 @@ unsigned Ann_DB_GetAnnouncementsNotSeen (MYSQL_RES **mysql_res)
|
|||
return (unsigned)
|
||||
DB_QuerySELECT (mysql_res,"can not get announcements",
|
||||
"SELECT AnnCod," // row[0]
|
||||
"Subject," // row[1]
|
||||
"Content" // row[2]
|
||||
"Status," // row[1]
|
||||
"Roles," // row[2]
|
||||
"Subject," // row[3]
|
||||
"Content" // row[4]
|
||||
" FROM ann_announcements"
|
||||
" WHERE Status=%u"
|
||||
" AND (Roles&%u)<>0 " // All my roles in different courses
|
||||
|
|
|
@ -84,8 +84,8 @@ static void Asg_PutIconsToRemEditOneAsg (struct Asg_Assignments *Assignments,
|
|||
const char *Anchor);
|
||||
static void Asg_PutPars (void *Assignments);
|
||||
static void Asg_GetListAssignments (struct Asg_Assignments *Assignments);
|
||||
static void Asg_GetDataOfAssignment (struct Asg_Assignment *Asg,
|
||||
MYSQL_RES **mysql_res,
|
||||
static void Asg_GetDataOfAssignmentFromRow (MYSQL_RES **mysql_res,
|
||||
struct Asg_Assignment *Asg,
|
||||
unsigned NumAsgs);
|
||||
static void Asg_ResetAssignment (struct Asg_Assignment *Asg);
|
||||
static void Asg_FreeListAssignments (struct Asg_Assignments *Assignments);
|
||||
|
@ -835,7 +835,7 @@ void Asg_GetDataOfAssignmentByCod (struct Asg_Assignment *Asg)
|
|||
NumAsgs = Asg_DB_GetDataOfAssignmentByCod (&mysql_res,Asg->AsgCod);
|
||||
|
||||
/***** Get data of assignment *****/
|
||||
Asg_GetDataOfAssignment (Asg,&mysql_res,NumAsgs);
|
||||
Asg_GetDataOfAssignmentFromRow (&mysql_res,Asg,NumAsgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -860,7 +860,7 @@ void Asg_GetDataOfAssignmentByFolder (struct Asg_Assignment *Asg)
|
|||
NumAsgs = Asg_DB_GetDataOfAssignmentByFolder (&mysql_res,Asg->Folder);
|
||||
|
||||
/***** Get data of assignment *****/
|
||||
Asg_GetDataOfAssignment (Asg,&mysql_res,NumAsgs);
|
||||
Asg_GetDataOfAssignmentFromRow (&mysql_res,Asg,NumAsgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -874,8 +874,8 @@ void Asg_GetDataOfAssignmentByFolder (struct Asg_Assignment *Asg)
|
|||
/************************* Get assignment data *******************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
static void Asg_GetDataOfAssignment (struct Asg_Assignment *Asg,
|
||||
MYSQL_RES **mysql_res,
|
||||
static void Asg_GetDataOfAssignmentFromRow (MYSQL_RES **mysql_res,
|
||||
struct Asg_Assignment *Asg,
|
||||
unsigned NumAsgs)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
|
|
|
@ -629,10 +629,11 @@ TODO: Emilce Barrera Mesa: Podr
|
|||
TODO: Emilce Barrera Mesa: Mis estudiantes presentan muchas dificultades a la hora de poner la foto porque la plataforma es muy exigente respecto al fondo de la imagen.
|
||||
|
||||
*/
|
||||
#define Log_PLATFORM_VERSION "SWAD 22.78 (2023-03-22)"
|
||||
#define Log_PLATFORM_VERSION "SWAD 22.78.1 (2023-03-22)"
|
||||
#define CSS_FILE "swad22.57.1.css"
|
||||
#define JS_FILE "swad22.49.js"
|
||||
/*
|
||||
Version 22.78.1: Mar 22, 2023 Code refactoring in exam sets and announcements. (337781 lines)
|
||||
Version 22.78: Mar 22, 2023 New fields Source and Cod in rubric criteria. (337770 lines)
|
||||
3 changes necessary in database:
|
||||
ALTER TABLE rub_criteria ADD COLUMN Source ENUM('teacher','rubric','exam','game') NOT NULL DEFAULT 'teacher' AFTER CriInd;
|
||||
|
|
|
@ -649,9 +649,10 @@ unsigned Exa_DB_GetExamSets (MYSQL_RES **mysql_res,long ExaCod)
|
|||
return (unsigned)
|
||||
DB_QuerySELECT (mysql_res,"can not get sets of questions",
|
||||
"SELECT SetCod," // row[0]
|
||||
"SetInd," // row[1]
|
||||
"NumQstsToPrint," // row[2]
|
||||
"Title" // row[3]
|
||||
"ExaCod," // row[1]
|
||||
"SetInd," // row[2]
|
||||
"NumQstsToPrint," // row[3]
|
||||
"Title" // row[4]
|
||||
" FROM exa_sets"
|
||||
" WHERE ExaCod=%ld"
|
||||
" ORDER BY SetInd",
|
||||
|
|
|
@ -321,7 +321,6 @@ static void ExaPrn_GetDataOfPrint (struct ExaPrn_Print *Print,
|
|||
static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print,long ExaCod)
|
||||
{
|
||||
MYSQL_RES *mysql_res;
|
||||
MYSQL_ROW row;
|
||||
unsigned NumSets;
|
||||
unsigned NumSet;
|
||||
struct ExaSet_Set Set;
|
||||
|
@ -341,22 +340,7 @@ static void ExaPrn_GetQuestionsForNewPrintFromDB (struct ExaPrn_Print *Print,lon
|
|||
ExaSet_ResetSet (&Set);
|
||||
|
||||
/***** Get set data *****/
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
/*
|
||||
row[0] SetCod
|
||||
row[1] SetInd
|
||||
row[2] NumQstsToPrint
|
||||
row[3] Title
|
||||
*/
|
||||
/* Get set code (row[0]),
|
||||
set index (row[1]),
|
||||
and number of questions to print (row[2]) */
|
||||
Set.SetCod = Str_ConvertStrCodToLongCod (row[0]);
|
||||
Set.SetInd = Str_ConvertStrToUnsigned (row[1]);
|
||||
Set.NumQstsToPrint = Str_ConvertStrToUnsigned (row[2]);
|
||||
|
||||
/* Get the title of the set (row[3]) */
|
||||
Str_Copy (Set.Title,row[3],sizeof (Set.Title) - 1);
|
||||
ExaSet_GetSetDataFromRow (mysql_res,&Set);
|
||||
|
||||
/***** Questions in this set *****/
|
||||
NumQstsFromSet = ExaPrn_GetSomeQstsFromSetToPrint (Print,&Set,&NumQstsInPrint);
|
||||
|
|
|
@ -178,7 +178,7 @@ void ExaSet_GetDataOfSetByCod (struct ExaSet_Set *Set)
|
|||
/* Get set code (row[0]) */
|
||||
Set->SetCod = Str_ConvertStrCodToLongCod (row[0]);
|
||||
|
||||
/* Get exam code (row[0]) */
|
||||
/* Get exam code (row[1]) */
|
||||
Set->ExaCod = Str_ConvertStrCodToLongCod (row[1]);
|
||||
|
||||
/* Get set index (row[2]) */
|
||||
|
@ -649,7 +649,6 @@ static void ExaSet_ListOneOrMoreSetsForEdition (struct Exa_Exams *Exams,
|
|||
extern const char *Txt_Movement_not_allowed;
|
||||
unsigned NumSet;
|
||||
struct ExaSet_Set Set;
|
||||
MYSQL_ROW row;
|
||||
char *Anchor;
|
||||
|
||||
/***** Trivial check *****/
|
||||
|
@ -669,27 +668,9 @@ static void ExaSet_ListOneOrMoreSetsForEdition (struct Exa_Exams *Exams,
|
|||
{
|
||||
/***** Create set of questions *****/
|
||||
ExaSet_ResetSet (&Set);
|
||||
Set.ExaCod = Exams->Exam.ExaCod;
|
||||
|
||||
/***** Get set data *****/
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
/*
|
||||
row[0] SetCod
|
||||
row[1] SetInd
|
||||
row[2] NumQstsToPrint
|
||||
row[3] Title
|
||||
*/
|
||||
/* Get set code (row[0]) */
|
||||
Set.SetCod = Str_ConvertStrCodToLongCod (row[0]);
|
||||
|
||||
/* Get set index (row[1]) */
|
||||
Set.SetInd = Str_ConvertStrToUnsigned (row[1]);
|
||||
|
||||
/* Get number of questions to exam (row[2]) */
|
||||
Set.NumQstsToPrint = Str_ConvertStrToUnsigned (row[2]);
|
||||
|
||||
/* Get the title of the set (row[3]) */
|
||||
Str_Copy (Set.Title,row[3],sizeof (Set.Title) - 1);
|
||||
ExaSet_GetSetDataFromRow (mysql_res,&Set);
|
||||
|
||||
/* Initialize context */
|
||||
Exams->SetCod = Set.SetCod;
|
||||
|
@ -852,6 +833,39 @@ void ExaSet_ResetSet (struct ExaSet_Set *Set)
|
|||
Set->NumQstsToPrint = 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/***************************** Get exam set data ****************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
void ExaSet_GetSetDataFromRow (MYSQL_RES *mysql_res,struct ExaSet_Set *Set)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
|
||||
/***** Get row *****/
|
||||
row = mysql_fetch_row (mysql_res);
|
||||
/*
|
||||
row[0] SetCod
|
||||
row[1] ExaCod
|
||||
row[2] SetInd
|
||||
row[3] NumQstsToPrint
|
||||
row[4] Title
|
||||
*/
|
||||
/***** Get set code (row[0]) *****/
|
||||
Set->SetCod = Str_ConvertStrCodToLongCod (row[0]);
|
||||
|
||||
/***** Get exam code (row[1]) *****/
|
||||
Set->ExaCod = Str_ConvertStrCodToLongCod (row[1]);
|
||||
|
||||
/***** Get set index (row[2]) *****/
|
||||
Set->SetInd = Str_ConvertStrToUnsigned (row[2]);
|
||||
|
||||
/***** Get set index (row[3]) *****/
|
||||
Set->NumQstsToPrint = Str_ConvertStrToUnsigned (row[3]);
|
||||
|
||||
/***** Get the title of the set (row[4]) *****/
|
||||
Str_Copy (Set->Title,row[4],sizeof (Set->Title) - 1);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/********************* List exam questions for edition ***********************/
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -52,6 +52,7 @@ void ExaSet_ListExamSets (struct Exa_Exams *Exams,
|
|||
struct ExaSet_Set *Set);
|
||||
|
||||
void ExaSet_ResetSet (struct ExaSet_Set *Set);
|
||||
void ExaSet_GetSetDataFromRow (MYSQL_RES *mysql_res,struct ExaSet_Set *Set);
|
||||
|
||||
Qst_AnswerType_t ExaSet_GetAnswerType (long QstCod);
|
||||
void ExaSet_GetQstDataFromDB (struct Qst_Question *Question);
|
||||
|
|
Loading…
Reference in New Issue