// swad_game.c: games using remote control /* SWAD (Shared Workspace At a Distance), is a web platform developed at the University of Granada (Spain), and used to support university teaching. This file is part of SWAD core. Copyright (C) 1999-2019 Antonio Caņas Vargas This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /*****************************************************************************/ /********************************* Headers ***********************************/ /*****************************************************************************/ #define _GNU_SOURCE // For asprintf #include // For PATH_MAX #include // For NULL #include // For asprintf #include // For calloc #include // For string functions #include "swad_database.h" #include "swad_form.h" #include "swad_game.h" #include "swad_global.h" #include "swad_HTML.h" #include "swad_match.h" #include "swad_match_result.h" #include "swad_pagination.h" #include "swad_role.h" #include "swad_test.h" /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /***************************** Private constants *****************************/ /*****************************************************************************/ #define Gam_MAX_CHARS_ANSWER (1024 - 1) // 1023 #define Gam_MAX_BYTES_ANSWER ((Gam_MAX_CHARS_ANSWER + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 16383 #define Gam_MAX_BYTES_LIST_ANSWER_TYPES (10 + (Gam_NUM_ANS_TYPES - 1) * (1 + 10)) const char *Gam_StrAnswerTypesDB[Gam_NUM_ANS_TYPES] = { "unique_choice", "multiple_choice", }; #define Gam_MAX_ANSWERS_PER_QUESTION 10 #define Gam_MAX_SELECTED_QUESTIONS 1000 #define Gam_MAX_BYTES_LIST_SELECTED_QUESTIONS (Gam_MAX_SELECTED_QUESTIONS * (1 + 10 + 1)) /*****************************************************************************/ /******************************* Private types *******************************/ /*****************************************************************************/ /*****************************************************************************/ /***************************** Private variables *****************************/ /*****************************************************************************/ long Gam_CurrentGamCod = -1L; // Used as parameter in contextual links unsigned Gam_CurrentQstInd = 0; // Used as parameter in contextual links /*****************************************************************************/ /***************************** Private prototypes ****************************/ /*****************************************************************************/ static void Gam_ListAllGames (void); static bool Gam_CheckIfICanEditGames (void); static void Gam_PutIconsListGames (void); static void Gam_PutIconToCreateNewGame (void); static void Gam_PutButtonToCreateNewGame (void); static void Gam_PutParamsToCreateNewGame (void); static void Gam_WriteAuthor (struct Game *Game); static void Gam_PutFormsToRemEditOneGame (const struct Game *Game, const char *Anchor); static void Gam_PutHiddenParamOrder (void); static void Gam_GetParamOrder (void); static void Gam_ResetGame (struct Game *Game); static void Gam_GetGameTxtFromDB (long GamCod,char Txt[Cns_MAX_BYTES_TEXT + 1]); static void Gam_RemoveGameFromAllTables (long GamCod); static bool Gam_CheckIfSimilarGameExists (struct Game *Game); static void Gam_PutFormsEditionGame (struct Game *Game,bool ItsANewGame); static void Gam_CreateGame (struct Game *Game,const char *Txt); static void Gam_UpdateGame (struct Game *Game,const char *Txt); static void Gam_RemAnswersOfAQuestion (long GamCod,unsigned QstInd); static unsigned Gam_GetMaxQuestionIndexInGame (long GamCod); static void Gam_ListGameQuestions (struct Game *Game); static void Gam_ListOneOrMoreQuestionsForEdition (long GamCod,unsigned NumQsts, MYSQL_RES *mysql_res, bool ICanEditQuestions); static void Gam_PutIconToAddNewQuestions (void); static void Gam_PutButtonToAddNewQuestions (void); static void Gam_AllocateListSelectedQuestions (void); static void Gam_FreeListsSelectedQuestions (void); static unsigned Gam_CountNumQuestionsInList (void); static void Gam_PutParamsOneQst (void); static void Gam_ExchangeQuestions (long GamCod, unsigned QstIndTop,unsigned QstIndBottom); static bool Gam_GetNumMchsGameAndCheckIfEditable (struct Game *Game); static long Gam_GetParamCurrentGamCod (void); /*****************************************************************************/ /***************************** List all games ********************************/ /*****************************************************************************/ void Gam_SeeAllGames (void) { /***** Get parameters *****/ Gam_GetParams (); // Return value ignored /***** Show all games *****/ Gam_ListAllGames (); } /*****************************************************************************/ /**************************** Show all the games *****************************/ /*****************************************************************************/ static void Gam_ListAllGames (void) { extern const char *Hlp_ASSESSMENT_Games; extern const char *Txt_Games; extern const char *Txt_GAMES_ORDER_HELP[Gam_NUM_ORDERS]; extern const char *Txt_GAMES_ORDER[Gam_NUM_ORDERS]; extern const char *Txt_Matches; extern const char *Txt_No_games; Gam_Order_t Order; struct Pagination Pagination; unsigned NumGame; /***** Put link to view matches results *****/ switch (Gbl.Usrs.Me.Role.Logged) { case Rol_STD: McR_PutFormToViewMchResults (ActReqSeeMyMchRes); break; case Rol_NET: case Rol_TCH: case Rol_SYS_ADM: McR_PutFormToViewMchResults (ActReqSeeUsrMchRes); break; case Rol_DEG_ADM: case Rol_CTR_ADM: case Rol_INS_ADM: break; default: Rol_WrongRoleExit (); break; } /***** Get number of groups in current course *****/ if (!Gbl.Crs.Grps.NumGrps) Gbl.Crs.Grps.WhichGrps = Grp_ALL_GROUPS; /***** Get list of games *****/ Gam_GetListGames (); /***** Compute variables related to pagination *****/ Pagination.NumItems = Gbl.Games.Num; Pagination.CurrentPage = (int) Gbl.Games.CurrentPage; Pag_CalculatePagination (&Pagination); Gbl.Games.CurrentPage = (unsigned) Pagination.CurrentPage; /***** Write links to pages *****/ if (Pagination.MoreThanOnePage) Pag_WriteLinksToPagesCentered (Pag_GAMES, 0, &Pagination); /***** Begin box *****/ Box_BoxBegin ("100%",Txt_Games,Gam_PutIconsListGames, Hlp_ASSESSMENT_Games,Box_NOT_CLOSABLE); if (Gbl.Games.Num) { /***** Table head *****/ HTM_TABLE_BeginWideMarginPadding (2); HTM_TR_Begin (NULL); if (Gam_CheckIfICanEditGames ()) HTM_TH (1,1,"CONTEXT_COL",NULL); // Column for contextual icons for (Order = (Gam_Order_t) 0; Order <= (Gam_Order_t) (Gam_NUM_ORDERS - 1); Order++) { HTM_TH_Begin (1,1,"LM"); /* Form to change order */ Frm_StartForm (ActSeeAllGam); Pag_PutHiddenParamPagNum (Pag_GAMES,Gbl.Games.CurrentPage); Par_PutHiddenParamUnsigned ("Order",(unsigned) Order); Frm_LinkFormSubmit (Txt_GAMES_ORDER_HELP[Order],"TIT_TBL",NULL); if (Order == Gbl.Games.SelectedOrder) fprintf (Gbl.F.Out,""); fprintf (Gbl.F.Out,"%s",Txt_GAMES_ORDER[Order]); if (Order == Gbl.Games.SelectedOrder) fprintf (Gbl.F.Out,""); Frm_LinkFormEnd (); Frm_EndForm (); HTM_TH_End (); } HTM_TH (1,1,"RM",Txt_Matches); HTM_TR_End (); /***** Write all the games *****/ for (NumGame = Pagination.FirstItemVisible; NumGame <= Pagination.LastItemVisible; NumGame++) Gam_ShowOneGame (Gbl.Games.LstGamCods[NumGame - 1], false, false, // Do not list game questions false); // Do not put form to start new match /***** End table *****/ HTM_TABLE_End (); } else // No games created Ale_ShowAlert (Ale_INFO,Txt_No_games); /***** Button to create a new game *****/ if (Gam_CheckIfICanEditGames ()) Gam_PutButtonToCreateNewGame (); /***** End box *****/ Box_BoxEnd (); /***** Write again links to pages *****/ if (Pagination.MoreThanOnePage) Pag_WriteLinksToPagesCentered (Pag_GAMES, 0, &Pagination); /***** Free list of games *****/ Gam_FreeListGames (); } /*****************************************************************************/ /************************ Check if I can edit games **************************/ /*****************************************************************************/ static bool Gam_CheckIfICanEditGames (void) { switch (Gbl.Usrs.Me.Role.Logged) { case Rol_TCH: case Rol_SYS_ADM: return true; default: return false; } return false; } /*****************************************************************************/ /***************** Put contextual icons in list of games *******************/ /*****************************************************************************/ static void Gam_PutIconsListGames (void) { /***** Put icon to create a new game *****/ if (Gam_CheckIfICanEditGames ()) Gam_PutIconToCreateNewGame (); /***** Put icon to show a figure *****/ Gbl.Figures.FigureType = Fig_GAMES; Fig_PutIconToShowFigure (); } /*****************************************************************************/ /********************** Put icon to create a new game **********************/ /*****************************************************************************/ static void Gam_PutIconToCreateNewGame (void) { extern const char *Txt_New_game; Ico_PutContextualIconToAdd (ActFrmNewGam,NULL,Gam_PutParamsToCreateNewGame, Txt_New_game); } /*****************************************************************************/ /********************* Put button to create a new game *********************/ /*****************************************************************************/ static void Gam_PutButtonToCreateNewGame (void) { extern const char *Txt_New_game; Frm_StartForm (ActFrmNewGam); Gam_PutParamsToCreateNewGame (); Btn_PutConfirmButton (Txt_New_game); Frm_EndForm (); } /*****************************************************************************/ /******************* Put parameters to create a new game *******************/ /*****************************************************************************/ static void Gam_PutParamsToCreateNewGame (void) { Gam_PutHiddenParamGameOrder (); Pag_PutHiddenParamPagNum (Pag_GAMES,Gbl.Games.CurrentPage); } /*****************************************************************************/ /****************************** Show one game ******************************/ /*****************************************************************************/ void Gam_SeeOneGame (void) { struct Game Game; /***** Get parameters *****/ if ((Game.GamCod = Gam_GetParams ()) == -1L) Lay_ShowErrorAndExit ("Code of game is missing."); /***** Show game *****/ Gam_ShowOneGame (Game.GamCod, true, // Show only this game false, // Do not list game questions false); // Do not put form to start new match } /*****************************************************************************/ /******************************* Show one game *******************************/ /*****************************************************************************/ void Gam_ShowOneGame (long GamCod, bool ShowOnlyThisGame, bool ListGameQuestions, bool PutFormNewMatch) { extern const char *Hlp_ASSESSMENT_Games; extern const char *Txt_Game; extern const char *Txt_View_game; extern const char *Txt_No_of_questions; extern const char *Txt_Matches; char *Anchor = NULL; static unsigned UniqueId = 0; char *Id; struct Game Game; Dat_StartEndTime_t StartEndTime; char Txt[Cns_MAX_BYTES_TEXT + 1]; /***** Begin box *****/ if (ShowOnlyThisGame) Box_BoxBegin (NULL,Txt_Game,NULL, Hlp_ASSESSMENT_Games,Box_NOT_CLOSABLE); /***** Get data of this game *****/ Game.GamCod = GamCod; Gam_GetDataOfGameByCod (&Game); /***** Set anchor string *****/ Frm_SetAnchorStr (Game.GamCod,&Anchor); /***** Begin table *****/ if (ShowOnlyThisGame) HTM_TABLE_BeginWidePadding (2); /***** Start first row of this game *****/ HTM_TR_Begin (NULL); /***** Icons related to this game *****/ if (Gam_CheckIfICanEditGames ()) { if (ShowOnlyThisGame) HTM_TD_Begin ("rowspan=\"2\" class=\"CONTEXT_COL\""); else HTM_TD_Begin ("rowspan=\"2\" class=\"CONTEXT_COL COLOR%u\"",Gbl.RowEvenOdd); /* Icons to remove/edit this game */ Gam_PutFormsToRemEditOneGame (&Game,Anchor); HTM_TD_End (); } /***** Start/end date/time *****/ UniqueId++; for (StartEndTime = (Dat_StartEndTime_t) 0; StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1); StartEndTime++) { if (asprintf (&Id,"gam_date_%u_%u",(unsigned) StartEndTime,UniqueId) < 0) Lay_NotEnoughMemoryExit (); if (ShowOnlyThisGame) HTM_TD_Begin ("id=\"%s\" class=\"%s LT\"", Id,Game.Hidden ? "DATE_GREEN_LIGHT": "DATE_GREEN"); else HTM_TD_Begin ("id=\"%s\" class=\"%s LT COLOR%u\"", Id,Game.Hidden ? "DATE_GREEN_LIGHT": "DATE_GREEN", Gbl.RowEvenOdd); if (Game.TimeUTC[Dat_START_TIME]) Dat_WriteLocalDateHMSFromUTC (Id,Game.TimeUTC[StartEndTime], Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK, true,true,true,0x7); HTM_TD_End (); free ((void *) Id); } /***** Game title and main data *****/ if (ShowOnlyThisGame) HTM_TD_Begin ("class=\"LT\""); else HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd); /* Game title */ Gam_SetParamCurrentGamCod (GamCod); // Used to pass parameter HTM_ARTICLE_Begin (Anchor); Frm_StartForm (ActSeeGam); Gam_PutParams (); Frm_LinkFormSubmit (Txt_View_game, Game.Hidden ? "ASG_TITLE_LIGHT": "ASG_TITLE",NULL); fprintf (Gbl.F.Out,"%s",Game.Title); Frm_LinkFormEnd (); Frm_EndForm (); HTM_ARTICLE_End (); /* Number of questions */ HTM_DIV_Begin ("class=\"%s\"",Game.Hidden ? "ASG_GRP_LIGHT" : "ASG_GRP"); fprintf (Gbl.F.Out,"%s: %u",Txt_No_of_questions,Game.NumQsts); HTM_DIV_End (); HTM_TD_End (); /***** Number of matches in game *****/ if (ShowOnlyThisGame) HTM_TD_Begin ("class=\"RT\""); else HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd); Gam_SetParamCurrentGamCod (GamCod); // Used to pass parameter Frm_StartForm (ActSeeGam); Gam_PutParams (); Frm_LinkFormSubmit (Txt_Matches, Game.Hidden ? "ASG_TITLE_LIGHT" : "ASG_TITLE",NULL); if (ShowOnlyThisGame) fprintf (Gbl.F.Out,"%s: ",Txt_Matches); fprintf (Gbl.F.Out,"%u",Game.NumMchs); Frm_LinkFormEnd (); Frm_EndForm (); HTM_TD_End (); /***** End 1st row of this game *****/ HTM_TR_End (); /***** Start 2nd row of this game *****/ HTM_TR_Begin (NULL); /***** Author of the game *****/ if (ShowOnlyThisGame) HTM_TD_Begin ("colspan=\"2\" class=\"LT\""); else HTM_TD_Begin ("colspan=\"2\" class=\"LT COLOR%u\"",Gbl.RowEvenOdd); Gam_WriteAuthor (&Game); HTM_TD_End (); /***** Text of the game *****/ if (ShowOnlyThisGame) HTM_TD_Begin ("colspan=\"2\" class=\"LT\""); else HTM_TD_Begin ("colspan=\"2\" class=\"LT COLOR%u\"",Gbl.RowEvenOdd); Gam_GetGameTxtFromDB (Game.GamCod,Txt); Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML, Txt,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to rigorous HTML Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links HTM_DIV_Begin ("class=\"PAR %s\"",Game.Hidden ? "DAT_LIGHT" : "DAT"); fprintf (Gbl.F.Out,"%s",Txt); HTM_DIV_End (); HTM_TD_End (); /***** End 2nd row of this game *****/ HTM_TR_End (); /***** End table *****/ if (ShowOnlyThisGame) HTM_TABLE_End (); else Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd; /***** Free anchor string *****/ Frm_FreeAnchorStr (Anchor); if (ShowOnlyThisGame) { if (ListGameQuestions) /***** Write questions of this game *****/ Gam_ListGameQuestions (&Game); else /***** List matches *****/ Mch_ListMatches (&Game,PutFormNewMatch); /***** End box *****/ Box_BoxEnd (); } } /*****************************************************************************/ /*********************** Write the author of a game ************************/ /*****************************************************************************/ static void Gam_WriteAuthor (struct Game *Game) { Usr_WriteAuthor1Line (Game->UsrCod,Game->Hidden); } /*****************************************************************************/ /****** Put a hidden parameter with the type of order in list of games *******/ /*****************************************************************************/ void Gam_PutHiddenParamGameOrder (void) { Par_PutHiddenParamUnsigned ("Order",(unsigned) Gbl.Games.SelectedOrder); } /*****************************************************************************/ /******************** Put a link (form) to edit one game *********************/ /*****************************************************************************/ static void Gam_PutFormsToRemEditOneGame (const struct Game *Game, const char *Anchor) { Gam_SetParamCurrentGamCod (Game->GamCod); // Used to pass parameter /***** Put icon to remove game *****/ Ico_PutContextualIconToRemove (ActReqRemGam,Gam_PutParams); /***** Put icon to unhide/hide game *****/ if (Game->Hidden) Ico_PutContextualIconToUnhide (ActShoGam,Anchor,Gam_PutParams); else Ico_PutContextualIconToHide (ActHidGam,Anchor,Gam_PutParams); /***** Put icon to edit game *****/ Ico_PutContextualIconToEdit (ActEdiOneGam,Gam_PutParams); } /*****************************************************************************/ /*********************** Params used to edit a game **************************/ /*****************************************************************************/ void Gam_PutParams (void) { long CurrentGamCod = Gam_GetParamCurrentGamCod (); if (CurrentGamCod > 0) Gam_PutParamGameCod (CurrentGamCod); Gam_PutHiddenParamOrder (); Grp_PutParamWhichGrps (); Pag_PutHiddenParamPagNum (Pag_GAMES,Gbl.Games.CurrentPage); } /*****************************************************************************/ /******************* Get parameters used to edit a game **********************/ /*****************************************************************************/ long Gam_GetParams (void) { /***** Get other parameters *****/ Gam_GetParamOrder (); Grp_GetParamWhichGrps (); Gbl.Games.CurrentPage = Pag_GetParamPagNum (Pag_GAMES); /***** Get game code *****/ return Gam_GetParamGameCod (); } /*****************************************************************************/ /****** Put a hidden parameter with the type of order in list of games *******/ /*****************************************************************************/ static void Gam_PutHiddenParamOrder (void) { Par_PutHiddenParamUnsigned ("Order",(unsigned) Gbl.Games.SelectedOrder); } /*****************************************************************************/ /********** Get parameter with the type or order in list of games ************/ /*****************************************************************************/ static void Gam_GetParamOrder (void) { Gbl.Games.SelectedOrder = (Gam_Order_t) Par_GetParToUnsignedLong ("Order", 0, Gam_NUM_ORDERS - 1, (unsigned long) Gam_ORDER_DEFAULT); } /*****************************************************************************/ /*********************** Get list of all the games *************************/ /*****************************************************************************/ void Gam_GetListGames (void) { static const char *OrderBySubQuery[Gam_NUM_ORDERS] = { "StartTime DESC,EndTime DESC,gam_games.Title DESC", // Gam_ORDER_BY_START_DATE "EndTime DESC,StartTime DESC,gam_games.Title DESC", // Gam_ORDER_BY_END_DATE "gam_games.Title DESC", // Gam_ORDER_BY_TITLE }; MYSQL_RES *mysql_res; MYSQL_ROW row; char *HiddenSubQuery; unsigned long NumRows = 0; // Initialized to avoid warning unsigned NumGame; /***** Free list of games *****/ if (Gbl.Games.LstIsRead) Gam_FreeListGames (); /***** Subquery: get hidden games depending on user's role *****/ switch (Gbl.Usrs.Me.Role.Logged) { case Rol_STD: if (asprintf (&HiddenSubQuery," AND Hidden='N'") < 0) Lay_NotEnoughMemoryExit (); break; case Rol_NET: case Rol_TCH: case Rol_DEG_ADM: case Rol_CTR_ADM: case Rol_INS_ADM: case Rol_SYS_ADM: if (asprintf (&HiddenSubQuery,"%s","") < 0) Lay_NotEnoughMemoryExit (); break; default: Rol_WrongRoleExit (); break; } /***** Get list of games from database *****/ NumRows = DB_QuerySELECT (&mysql_res,"can not get games", "SELECT gam_games.GamCod," // row[0] "MIN(mch_matches.StartTime) AS StartTime," // row[1] "MAX(mch_matches.EndTime) AS EndTime" // row[2] " FROM gam_games" " LEFT JOIN mch_matches" " ON gam_games.GamCod=mch_matches.GamCod" " WHERE gam_games.CrsCod=%ld" "%s" " GROUP BY gam_games.GamCod" " ORDER BY %s", Gbl.Hierarchy.Crs.CrsCod, HiddenSubQuery, OrderBySubQuery[Gbl.Games.SelectedOrder]); /***** Free allocated memory for subquery *****/ free ((void *) HiddenSubQuery); if (NumRows) // Games found... { Gbl.Games.Num = (unsigned) NumRows; /***** Create list of games *****/ if ((Gbl.Games.LstGamCods = (long *) calloc (NumRows,sizeof (long))) == NULL) Lay_NotEnoughMemoryExit (); /***** Get the games codes *****/ for (NumGame = 0; NumGame < Gbl.Games.Num; NumGame++) { /* Get next game code (row[0]) */ row = mysql_fetch_row (mysql_res); if ((Gbl.Games.LstGamCods[NumGame] = Str_ConvertStrCodToLongCod (row[0])) <= 0) Lay_ShowErrorAndExit ("Error: wrong game code."); } } else Gbl.Games.Num = 0; /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); Gbl.Games.LstIsRead = true; } /*****************************************************************************/ /********************** Get game data using its code *************************/ /*****************************************************************************/ void Gam_GetDataOfGameByCod (struct Game *Game) { MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned long NumRows; /***** Get data of game from database *****/ NumRows = DB_QuerySELECT (&mysql_res,"can not get game data", "SELECT gam_games.GamCod," // row[0] "gam_games.CrsCod," // row[1] "gam_games.Hidden," // row[2] "gam_games.UsrCod," // row[3] "gam_games.Title" // row[4] " FROM gam_games" " LEFT JOIN mch_matches" " ON gam_games.GamCod=mch_matches.GamCod" " WHERE gam_games.GamCod=%ld", Game->GamCod); if (NumRows) // Game found... { /* Get row */ row = mysql_fetch_row (mysql_res); /* Get code of the game (row[0]) */ Game->GamCod = Str_ConvertStrCodToLongCod (row[0]); /* Get code of the course (row[1]) */ Game->CrsCod = Str_ConvertStrCodToLongCod (row[1]); /* Get whether the game is hidden (row[2]) */ Game->Hidden = (row[2][0] == 'Y'); /* Get author of the game (row[3]) */ Game->UsrCod = Str_ConvertStrCodToLongCod (row[3]); /* Get the title of the game (row[4]) */ Str_Copy (Game->Title,row[4], Gam_MAX_BYTES_TITLE); /* Get number of questions */ Game->NumQsts = Gam_GetNumQstsGame (Game->GamCod); /* Get number of matches */ Game->NumMchs = Mch_GetNumMchsInGame (Game->GamCod); } else /* Initialize to empty game */ Gam_ResetGame (Game); /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_res); if (Game->GamCod > 0) { /***** Get start and end times from database *****/ NumRows = DB_QuerySELECT (&mysql_res,"can not get game data", "SELECT UNIX_TIMESTAMP(MIN(StartTime))," // row[0] "UNIX_TIMESTAMP(MAX(EndTime))" // row[1] " FROM mch_matches" " WHERE GamCod=%ld", Game->GamCod); if (NumRows) { /* Get row */ row = mysql_fetch_row (mysql_res); /* Get start date (row[0] holds the start UTC time) */ Game->TimeUTC[Dat_START_TIME] = Dat_GetUNIXTimeFromStr (row[0]); /* Get end date (row[1] holds the end UTC time) */ Game->TimeUTC[Dat_END_TIME ] = Dat_GetUNIXTimeFromStr (row[1]); } /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_res); } else { Game->TimeUTC[Dat_START_TIME] = Game->TimeUTC[Dat_END_TIME ] = (time_t) 0; } } /*****************************************************************************/ /*************************** Initialize game to empty ************************/ /*****************************************************************************/ static void Gam_ResetGame (struct Game *Game) { /***** Initialize to empty game *****/ Game->GamCod = -1L; Game->CrsCod = -1L; Game->UsrCod = -1L; Game->TimeUTC[Dat_START_TIME] = (time_t) 0; Game->TimeUTC[Dat_END_TIME ] = (time_t) 0; Game->Title[0] = '\0'; Game->NumQsts = 0; Game->NumMchs = 0; Game->Hidden = false; } /*****************************************************************************/ /***************************** Free list of games ****************************/ /*****************************************************************************/ void Gam_FreeListGames (void) { if (Gbl.Games.LstIsRead && Gbl.Games.LstGamCods) { /***** Free memory used by the list of games *****/ free ((void *) Gbl.Games.LstGamCods); Gbl.Games.LstGamCods = NULL; Gbl.Games.Num = 0; Gbl.Games.LstIsRead = false; } } /*****************************************************************************/ /********************** Get game text from database ************************/ /*****************************************************************************/ static void Gam_GetGameTxtFromDB (long GamCod,char Txt[Cns_MAX_BYTES_TEXT + 1]) { MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned long NumRows; /***** Get text of game from database *****/ NumRows = DB_QuerySELECT (&mysql_res,"can not get game text", "SELECT Txt FROM gam_games WHERE GamCod=%ld", GamCod); /***** The result of the query must have one row or none *****/ if (NumRows == 1) { /* Get info text */ row = mysql_fetch_row (mysql_res); Str_Copy (Txt,row[0], Cns_MAX_BYTES_TEXT); } else Txt[0] = '\0'; /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); if (NumRows > 1) Lay_ShowErrorAndExit ("Error when getting game text."); } /*****************************************************************************/ /******************** Write parameter with code of game **********************/ /*****************************************************************************/ void Gam_PutParamGameCod (long GamCod) { Par_PutHiddenParamLong ("GamCod",GamCod); } /*****************************************************************************/ /********************* Get parameter with code of game ***********************/ /*****************************************************************************/ long Gam_GetParamGameCod (void) { /***** Get code of game *****/ return Par_GetParToLong ("GamCod"); } /*****************************************************************************/ /*************** Ask for confirmation of removing of a game ******************/ /*****************************************************************************/ void Gam_AskRemGame (void) { extern const char *Txt_Do_you_really_want_to_remove_the_game_X; extern const char *Txt_Remove_game; struct Game Game; /***** Get parameters *****/ if ((Game.GamCod = Gam_GetParams ()) == -1L) Lay_ShowErrorAndExit ("Code of game is missing."); /***** Get data of the game from database *****/ Gam_GetDataOfGameByCod (&Game); if (!Gam_CheckIfICanEditGames ()) Lay_NoPermissionExit (); /***** Show question and button to remove game *****/ Gam_SetParamCurrentGamCod (Game.GamCod); // Used to pass parameter Ale_ShowAlertAndButton (ActRemGam,NULL,NULL,Gam_PutParams, Btn_REMOVE_BUTTON,Txt_Remove_game, Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_game_X, Game.Title); /***** Show games again *****/ Gam_ListAllGames (); } /*****************************************************************************/ /******************************* Remove a game *******************************/ /*****************************************************************************/ void Gam_RemoveGame (void) { extern const char *Txt_Game_X_removed; struct Game Game; /***** Get game code *****/ if ((Game.GamCod = Gam_GetParamGameCod ()) == -1L) Lay_ShowErrorAndExit ("Code of game is missing."); /***** Get data of the game from database *****/ Gam_GetDataOfGameByCod (&Game); if (!Gam_CheckIfICanEditGames ()) Lay_NoPermissionExit (); /***** Remove game from all tables *****/ Gam_RemoveGameFromAllTables (Game.GamCod); /***** Write message to show the change made *****/ Ale_ShowAlert (Ale_SUCCESS,Txt_Game_X_removed, Game.Title); /***** Show games again *****/ Gam_ListAllGames (); } /*****************************************************************************/ /*********************** Remove game from all tables *************************/ /*****************************************************************************/ static void Gam_RemoveGameFromAllTables (long GamCod) { /***** Remove all matches in this game *****/ Mch_RemoveMatchesInGameFromAllTables (GamCod); /***** Remove game question *****/ DB_QueryDELETE ("can not remove game questions", "DELETE FROM gam_questions WHERE GamCod=%ld", GamCod); /***** Remove game *****/ DB_QueryDELETE ("can not remove game", "DELETE FROM gam_games WHERE GamCod=%ld", GamCod); } /*****************************************************************************/ /******************** Remove all the games of a course ***********************/ /*****************************************************************************/ void Gam_RemoveGamesCrs (long CrsCod) { /***** Remove all matches in this course *****/ Mch_RemoveMatchInCourseFromAllTables (CrsCod); /***** Remove the questions in games *****/ DB_QueryDELETE ("can not remove questions in course games", "DELETE FROM gam_questions" " USING gam_games,gam_questions" " WHERE gam_games.CrsCod=%ld" " AND gam_games.GamCod=gam_questions.GamCod", CrsCod); /***** Remove the games *****/ DB_QueryDELETE ("can not remove course games", "DELETE FROM gam_games" " WHERE CrsCod=%ld", CrsCod); } /*****************************************************************************/ /******************************** Hide a game ******************************/ /*****************************************************************************/ void Gam_HideGame (void) { struct Game Game; /***** Get parameters *****/ if ((Game.GamCod = Gam_GetParams ()) == -1L) Lay_ShowErrorAndExit ("Code of game is missing."); /***** Get data of the game from database *****/ Gam_GetDataOfGameByCod (&Game); if (!Gam_CheckIfICanEditGames ()) Lay_NoPermissionExit (); /***** Hide game *****/ DB_QueryUPDATE ("can not hide game", "UPDATE gam_games SET Hidden='Y' WHERE GamCod=%ld", Game.GamCod); /***** Show games again *****/ Gam_ListAllGames (); } /*****************************************************************************/ /******************************** Show a game ******************************/ /*****************************************************************************/ void Gam_UnhideGame (void) { struct Game Game; /***** Get parameters *****/ if ((Game.GamCod = Gam_GetParams ()) == -1L) Lay_ShowErrorAndExit ("Code of game is missing."); /***** Get data of the game from database *****/ Gam_GetDataOfGameByCod (&Game); if (!Gam_CheckIfICanEditGames ()) Lay_NoPermissionExit (); /***** Show game *****/ DB_QueryUPDATE ("can not show game", "UPDATE gam_games SET Hidden='N' WHERE GamCod=%ld", Game.GamCod); /***** Show games again *****/ Gam_ListAllGames (); } /*****************************************************************************/ /******************* Check if the title of a game exists *******************/ /*****************************************************************************/ static bool Gam_CheckIfSimilarGameExists (struct Game *Game) { /***** Get number of games with a field value from database *****/ return (DB_QueryCOUNT ("can not get similar games", "SELECT COUNT(*) FROM gam_games" " WHERE CrsCod=%ld AND Title='%s'" " AND GamCod<>%ld", Gbl.Hierarchy.Crs.CrsCod,Game->Title, Game->GamCod) != 0); } /*****************************************************************************/ /**************** Request the creation or edition of a game ******************/ /*****************************************************************************/ void Gam_RequestCreatOrEditGame (void) { struct Game Game; bool ItsANewGame; /***** Check if I can create new games *****/ if (!Gam_CheckIfICanEditGames ()) Lay_NoPermissionExit (); /***** Get parameters *****/ Game.GamCod = Gam_GetParams (); ItsANewGame = (Game.GamCod < 0); /***** Put forms to create/edit a game *****/ Gam_PutFormsEditionGame (&Game,ItsANewGame); /***** Show games again *****/ Gam_ListAllGames (); } /*****************************************************************************/ /********************* Put a form to create/edit a game **********************/ /*****************************************************************************/ static void Gam_PutFormsEditionGame (struct Game *Game,bool ItsANewGame) { extern const char *Hlp_ASSESSMENT_Games_new_game; extern const char *Hlp_ASSESSMENT_Games_edit_game; extern const char *The_ClassFormInBox[The_NUM_THEMES]; extern const char *Txt_New_game; extern const char *Txt_Edit_game; extern const char *Txt_Title; extern const char *Txt_Description; extern const char *Txt_Create_game; extern const char *Txt_Save_changes; char Txt[Cns_MAX_BYTES_TEXT + 1]; /***** Get game data *****/ if (ItsANewGame) /* Initialize to empty game */ Gam_ResetGame (Game); else { /* Get game data from database */ Gam_GetDataOfGameByCod (Game); Gam_GetGameTxtFromDB (Game->GamCod,Txt); } /***** Begin form *****/ Gam_SetParamCurrentGamCod (Game->GamCod); // Used to pass parameter Frm_StartForm (ItsANewGame ? ActNewGam : ActChgGam); Gam_PutParams (); /***** Begin box and table *****/ if (ItsANewGame) Box_StartBoxTable (NULL,Txt_New_game,NULL, Hlp_ASSESSMENT_Games_new_game,Box_NOT_CLOSABLE,2); else Box_StartBoxTable (NULL, Game->Title[0] ? Game->Title : Txt_Edit_game, NULL, Hlp_ASSESSMENT_Games_edit_game,Box_NOT_CLOSABLE,2); /***** Game title *****/ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"RM\""); fprintf (Gbl.F.Out,"