diff --git a/Makefile b/Makefile
index c0312172..0fff1c0c 100644
--- a/Makefile
+++ b/Makefile
@@ -49,7 +49,7 @@ OBJS = swad_account.o swad_account_database.o swad_action.o swad_admin.o \
swad_figure.o swad_figure_cache.o swad_file.o swad_file_browser.o \
swad_file_extension.o swad_file_MIME.o swad_firewall.o swad_follow.o \
swad_follow_database.o swad_form.o swad_forum.o \
- swad_game.o swad_global.o swad_group.o \
+ swad_game.o swad_global.o swad_group.o swad_group_database.o \
swad_help.o swad_hierarchy.o swad_hierarchy_config.o swad_holiday.o \
swad_HTML.o \
swad_icon.o swad_ID.o swad_indicator.o swad_info.o swad_institution.o \
diff --git a/swad_API.c b/swad_API.c
index 42a73db8..1ff9b390 100644
--- a/swad_API.c
+++ b/swad_API.c
@@ -109,6 +109,7 @@ cp -f /home/acanas/swad/swad/swad /var/www/cgi-bin/
#include "swad_file_browser.h"
#include "swad_forum.h"
#include "swad_global.h"
+#include "swad_group_database.h"
#include "swad_hierarchy.h"
#include "swad_hierarchy_level.h"
#include "swad_ID.h"
diff --git a/swad_assignment.c b/swad_assignment.c
index 922c9959..7dc04c7b 100644
--- a/swad_assignment.c
+++ b/swad_assignment.c
@@ -41,6 +41,7 @@
#include "swad_form.h"
#include "swad_global.h"
#include "swad_group.h"
+#include "swad_group_database.h"
#include "swad_HTML.h"
#include "swad_notification.h"
#include "swad_pagination.h"
diff --git a/swad_attendance.c b/swad_attendance.c
index d7c7de05..5c76c6d4 100644
--- a/swad_attendance.c
+++ b/swad_attendance.c
@@ -41,6 +41,7 @@
#include "swad_form.h"
#include "swad_global.h"
#include "swad_group.h"
+#include "swad_group_database.h"
#include "swad_hierarchy_level.h"
#include "swad_HTML.h"
#include "swad_ID.h"
diff --git a/swad_changelog.h b/swad_changelog.h
index 87ef1a2a..c351d6dc 100644
--- a/swad_changelog.h
+++ b/swad_changelog.h
@@ -602,13 +602,14 @@ TODO: FIX BUG, URGENT! En las fechas como par
TODO: En las encuestas, que los estudiantes no puedan ver los resultados hasta que no finalice el plazo.
*/
-#define Log_PLATFORM_VERSION "SWAD 20.93 (2021-06-17)"
+#define Log_PLATFORM_VERSION "SWAD 20.94 (2021-06-17)"
#define CSS_FILE "swad20.45.css"
#define JS_FILE "swad20.69.1.js"
/*
TODO: Rename CENTRE to CENTER in help wiki.
TODO: Rename ASSESSMENT.Announcements to ASSESSMENT.Calls_for_exams
+ Version 20.94: Jun 17, 2021 New module swad_group_database for database queries related to groups. (313489 lines)
Version 20.93: Jun 17, 2021 Code refactoring. (313382 lines)
Version 20.92: Jun 11, 2021 New modules swad_admin and swad_admin_database for administrators. (313381 lines)
Version 20.91.3: Jun 11, 2021 Queries moved to module swad_enrolment_database. (313173 lines)
diff --git a/swad_exam_session.c b/swad_exam_session.c
index afea27cd..ee890814 100644
--- a/swad_exam_session.c
+++ b/swad_exam_session.c
@@ -43,6 +43,7 @@
#include "swad_exam_type.h"
#include "swad_form.h"
#include "swad_global.h"
+#include "swad_group_database.h"
#include "swad_HTML.h"
#include "swad_role.h"
#include "swad_setting.h"
diff --git a/swad_file_browser.c b/swad_file_browser.c
index 32635a6f..84ef977a 100644
--- a/swad_file_browser.c
+++ b/swad_file_browser.c
@@ -48,6 +48,7 @@
#include "swad_file_MIME.h"
#include "swad_form.h"
#include "swad_global.h"
+#include "swad_group_database.h"
#include "swad_hierarchy.h"
#include "swad_hierarchy_level.h"
#include "swad_HTML.h"
@@ -11724,7 +11725,7 @@ void Brw_RemoveZonesOfGroupsOfType (long GrpTypCod)
long GrpCod;
/***** Query database *****/
- NumGrps = Grp_DB_GetGrpsOfType (GrpTypCod,&mysql_res);
+ NumGrps = Grp_DB_GetGrpsOfType (&mysql_res,GrpTypCod);
for (NumGrp = 0;
NumGrp < NumGrps;
NumGrp++)
diff --git a/swad_group.c b/swad_group.c
index 50a4d877..71d4e323 100644
--- a/swad_group.c
+++ b/swad_group.c
@@ -42,6 +42,7 @@
#include "swad_game.h"
#include "swad_global.h"
#include "swad_group.h"
+#include "swad_group_database.h"
#include "swad_HTML.h"
#include "swad_match.h"
#include "swad_notification.h"
@@ -107,9 +108,6 @@ static void Grp_PutIconToCreateNewGroup (void);
static void Grp_PutCheckboxAllGrps (Grp_WhichGroups_t GroupsSelectableByStdsOrNETs);
-static void Grp_DB_LockTables (void);
-static void Grp_DB_UnlockTables (void);
-
static void Grp_ConstructorListGrpAlreadySelec (struct ListGrpsAlreadySelec **AlreadyExistsGroupOfType);
static void Grp_DestructorListGrpAlreadySelec (struct ListGrpsAlreadySelec **AlreadyExistsGroupOfType);
static void Grp_RemoveUsrFromGroup (long UsrCod,long GrpCod);
@@ -123,8 +121,7 @@ static void Grp_WriteHeadingGroupTypes (void);
static void Grp_ListGroupsForEdition (const struct Roo_Rooms *Rooms);
static void Grp_WriteHeadingGroups (void);
-static bool Grp_DB_CheckIfAssociatedToGrp (const char *Table,const char *Field,
- long Cod,long GrpCod);
+
static void Grp_PutIconToEditGroups (__attribute__((unused)) void *Args);
static void Grp_ShowWarningToStdsToChangeGrps (void);
@@ -137,7 +134,6 @@ static void Grp_WriteGrpHead (struct GroupType *GrpTyp);
static void Grp_WriteRowGrp (struct Group *Grp,bool Highlight);
static void Grp_PutFormToCreateGroupType (void);
static void Grp_PutFormToCreateGroup (const struct Roo_Rooms *Rooms);
-static unsigned Grp_DB_CountNumGrpsInThisCrsOfType (long GrpTypCod);
static void Grp_GetDataOfGroupTypeByCod (struct GroupType *GrpTyp);
static bool Grp_GetMultipleEnrolmentOfAGroupType (long GrpTypCod);
static long Grp_DB_GetTypeOfGroupOfAGroup (long GrpCod);
@@ -972,35 +968,6 @@ void Grp_ChangeGrpsOtherUsrAtomically (struct ListCodGrps *LstGrpsUsrWants)
Grp_FreeListGrpTypesAndGrps ();
}
-/*****************************************************************************/
-/*********** Lock tables to make the registration in groups atomic ***********/
-/*****************************************************************************/
-
-static void Grp_DB_LockTables (void)
- {
- DB_Query ("can not lock tables to change user's groups",
- "LOCK TABLES "
- "grp_types WRITE,"
- "grp_groups WRITE,"
- "grp_users WRITE,"
- "crs_users READ,"
- "crs_user_settings READ,"
- "roo_rooms READ");
- Gbl.DB.LockedTables = true;
- }
-
-/*****************************************************************************/
-/*********** Unlock tables after changes in registration in groups ***********/
-/*****************************************************************************/
-
-static void Grp_DB_UnlockTables (void)
- {
- Gbl.DB.LockedTables = false; // Set to false before the following unlock...
- // ...to not retry the unlock if error in unlocking
- DB_Query ("can not unlock tables after changing user's groups",
- "UNLOCK TABLES");
- }
-
/*****************************************************************************/
/******* Check if not selected more than a group of single enrolment *********/
/*****************************************************************************/
@@ -1767,47 +1734,6 @@ void Grp_ListGrpsToEditAsgAttSvyEvtMch (struct GroupType *GrpTyp,
Grp_FreeListCodGrp (&LstGrpsIBelong);
}
-/*****************************************************************************/
-/************ Check if an assignment is associated to a group ****************/
-/*****************************************************************************/
-
-static bool Grp_DB_CheckIfAssociatedToGrp (const char *Table,const char *Field,
- long Cod,long GrpCod)
- {
- /***** Get if an assignment, attendance event, survey, exam event or match
- is associated to a given group from database *****/
- return (DB_QueryCOUNT ("can not check if associated to a group",
- "SELECT COUNT(*)"
- " FROM %s"
- " WHERE %s=%ld"
- " AND GrpCod=%ld",
- Table,
- Field,Cod,
- GrpCod) != 0);
- }
-
-
-/*****************************************************************************/
-/*** Check if an assignment, attendance event, survey, exam event or match ***/
-/*** is associated to any group ***/
-/*****************************************************************************/
-
-bool Grp_DB_CheckIfAssociatedToGrps (const char *Table,const char *Field,long Cod)
- {
- /***** Trivial check *****/
- if (Cod <= 0) // Assignment, attendance event, survey, exam event or match code
- return false;
-
- /***** Get if an assignment, attendance event, survey, exam event or match
- is associated to any group from database *****/
- return (DB_QueryCOUNT ("can not check if associated to groups",
- "SELECT COUNT(*)"
- " FROM %s"
- " WHERE %s=%ld",
- Table,
- Field,Cod) != 0);
- }
-
/*****************************************************************************/
/***************** Show list of groups to register/remove me *****************/
/*****************************************************************************/
@@ -2955,7 +2881,7 @@ void Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_WhichGroupTypes_t WhichGroupTypes)
if (GrpTyp->NumGrps) // If there are groups of this type...
{
/***** Query database *****/
- GrpTyp->NumGrps = Grp_DB_GetGrpsOfType (GrpTyp->GrpTypCod,&mysql_res);
+ GrpTyp->NumGrps = Grp_DB_GetGrpsOfType (&mysql_res,GrpTyp->GrpTypCod);
if (GrpTyp->NumGrps > 0) // Groups found...
{
/***** Create list with groups of this type *****/
@@ -3047,63 +2973,6 @@ void Grp_FreeListGrpTypesAndGrps (void)
}
}
-/*****************************************************************************/
-/*********** Query the number of groups that hay in this course **************/
-/*****************************************************************************/
-
-unsigned Grp_DB_CountNumGrpsInCurrentCrs (void)
- {
- /***** Get number of group in current course from database *****/
- return (unsigned)
- DB_QueryCOUNT ("can not get number of groups in this course",
- "SELECT COUNT(*)"
- " FROM grp_types,"
- "grp_groups"
- " WHERE grp_types.CrsCod=%ld"
- " AND grp_types.GrpTypCod=grp_groups.GrpTypCod",
- Gbl.Hierarchy.Crs.CrsCod);
- }
-
-/*****************************************************************************/
-/****************** Count number of groups in a group type *******************/
-/*****************************************************************************/
-
-static unsigned Grp_DB_CountNumGrpsInThisCrsOfType (long GrpTypCod)
- {
- /***** Get number of groups of a type from database *****/
- return (unsigned)
- DB_QueryCOUNT ("can not get number of groups of a type",
- "SELECT COUNT(*)"
- " FROM grp_groups"
- " WHERE GrpTypCod=%ld",
- GrpTypCod);
- }
-
-/*****************************************************************************/
-/******************** Get groups of a type in this course ********************/
-/*****************************************************************************/
-
-unsigned Grp_DB_GetGrpsOfType (long GrpTypCod,MYSQL_RES **mysql_res)
- {
- /***** Get groups of a type from database *****/
- // Don't use INNER JOIN because there are groups without assigned room
- return (unsigned)
- DB_QuerySELECT (mysql_res,"can not get groups of a type",
- "SELECT grp_groups.GrpCod," // row[0]
- "grp_groups.GrpName," // row[1]
- "grp_groups.RooCod," // row[2]
- "roo_rooms.ShortName," // row[3]
- "grp_groups.MaxStudents," // row[4]
- "grp_groups.Open," // row[5]
- "grp_groups.FileZones" // row[6]
- " FROM grp_groups"
- " LEFT JOIN roo_rooms"
- " ON grp_groups.RooCod=roo_rooms.RooCod"
- " WHERE grp_groups.GrpTypCod=%ld"
- " ORDER BY grp_groups.GrpName",
- GrpTypCod);
- }
-
/*****************************************************************************/
/******************* Get data of a group type from its code ******************/
/*****************************************************************************/
diff --git a/swad_group.h b/swad_group.h
index 5e502648..f96d5a2f 100644
--- a/swad_group.h
+++ b/swad_group.h
@@ -1,4 +1,4 @@
-// swad_group.h: groups
+// swad_group.h: types of groups and groups
#ifndef _SWAD_GRP
#define _SWAD_GRP
@@ -184,7 +184,6 @@ void Grp_ListGrpsToEditAsgAttSvyEvtMch (struct GroupType *GrpTyp,
Grp_WhichIsAssociatedToGrp_t WhichIsAssociatedToGrp,
long Cod);
-bool Grp_DB_CheckIfAssociatedToGrps (const char *Table,const char *Field,long Cod);
void Grp_ReqRegisterInGrps (void);
void Grp_ShowLstGrpsToChgMyGrps (void);
void Grp_ShowLstGrpsToChgOtherUsrsGrps (long UsrCod);
@@ -193,8 +192,6 @@ void Grp_GetListGrpTypesInThisCrs (Grp_WhichGroupTypes_t WhichGroupTypes);
void Grp_FreeListGrpTypesAndGrps (void);
void Grp_OpenGroupsAutomatically (void);
void Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_WhichGroupTypes_t WhichGroupTypes);
-unsigned Grp_DB_CountNumGrpsInCurrentCrs (void);
-unsigned Grp_DB_GetGrpsOfType (long GrpTypCod,MYSQL_RES **mysql_res);
void Grp_GetDataOfGroupByCod (struct GroupData *GrpDat);
bool Grp_DB_CheckIfGroupExists (long GrpCod);
bool Grp_DB_CheckIfGrpBelongsToCrs (long GrpCod,long CrsCod);
diff --git a/swad_group_database.c b/swad_group_database.c
new file mode 100644
index 00000000..ae377704
--- /dev/null
+++ b/swad_group_database.c
@@ -0,0 +1,200 @@
+// swad_group_database.c: types of groups and groups operations with database
+
+/*
+ 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-2021 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 NULL
+// #include // For asprintf
+// #include // For exit, system, malloc, free, rand, etc.
+// #include // For string functions
+
+// #include "swad_action.h"
+// #include "swad_assignment_database.h"
+// #include "swad_attendance_database.h"
+// #include "swad_box.h"
+#include "swad_database.h"
+// #include "swad_error.h"
+// #include "swad_exam_session.h"
+// #include "swad_form.h"
+// #include "swad_game.h"
+#include "swad_global.h"
+#include "swad_group.h"
+// #include "swad_HTML.h"
+// #include "swad_match.h"
+// #include "swad_notification.h"
+// #include "swad_parameter.h"
+// #include "swad_program.h"
+// #include "swad_project.h"
+// #include "swad_setting.h"
+// #include "swad_survey.h"
+
+/*****************************************************************************/
+/*************************** Private constants *******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/****************************** Private types ********************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/************* External global variables from others modules *****************/
+/*****************************************************************************/
+
+extern struct Globals Gbl;
+
+/*****************************************************************************/
+/************************** Private global variables *************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Private prototypes ****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*********** Lock tables to make the registration in groups atomic ***********/
+/*****************************************************************************/
+
+void Grp_DB_LockTables (void)
+ {
+ DB_Query ("can not lock tables to change user's groups",
+ "LOCK TABLES "
+ "grp_types WRITE,"
+ "grp_groups WRITE,"
+ "grp_users WRITE,"
+ "crs_users READ,"
+ "crs_user_settings READ,"
+ "roo_rooms READ");
+ Gbl.DB.LockedTables = true;
+ }
+
+/*****************************************************************************/
+/*********** Unlock tables after changes in registration in groups ***********/
+/*****************************************************************************/
+
+void Grp_DB_UnlockTables (void)
+ {
+ Gbl.DB.LockedTables = false; // Set to false before the following unlock...
+ // ...to not retry the unlock if error in unlocking
+ DB_Query ("can not unlock tables after changing user's groups",
+ "UNLOCK TABLES");
+ }
+
+/*****************************************************************************/
+/************ Check if an assignment is associated to a group ****************/
+/*****************************************************************************/
+
+bool Grp_DB_CheckIfAssociatedToGrp (const char *Table,const char *Field,
+ long Cod,long GrpCod)
+ {
+ /***** Get if an assignment, attendance event, survey, exam event or match
+ is associated to a given group from database *****/
+ return (DB_QueryCOUNT ("can not check if associated to a group",
+ "SELECT COUNT(*)"
+ " FROM %s"
+ " WHERE %s=%ld"
+ " AND GrpCod=%ld",
+ Table,
+ Field,Cod,
+ GrpCod) != 0);
+ }
+
+/*****************************************************************************/
+/*** Check if an assignment, attendance event, survey, exam event or match ***/
+/*** is associated to any group ***/
+/*****************************************************************************/
+
+bool Grp_DB_CheckIfAssociatedToGrps (const char *Table,const char *Field,long Cod)
+ {
+ /***** Trivial check *****/
+ if (Cod <= 0) // Assignment, attendance event, survey, exam event or match code
+ return false;
+
+ /***** Get if an assignment, attendance event, survey, exam event or match
+ is associated to any group from database *****/
+ return (DB_QueryCOUNT ("can not check if associated to groups",
+ "SELECT COUNT(*)"
+ " FROM %s"
+ " WHERE %s=%ld",
+ Table,
+ Field,Cod) != 0);
+ }
+
+/*****************************************************************************/
+/*********** Query the number of groups that hay in this course **************/
+/*****************************************************************************/
+
+unsigned Grp_DB_CountNumGrpsInCurrentCrs (void)
+ {
+ /***** Get number of group in current course from database *****/
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of groups in this course",
+ "SELECT COUNT(*)"
+ " FROM grp_types,"
+ "grp_groups"
+ " WHERE grp_types.CrsCod=%ld"
+ " AND grp_types.GrpTypCod=grp_groups.GrpTypCod",
+ Gbl.Hierarchy.Crs.CrsCod);
+ }
+
+/*****************************************************************************/
+/****************** Count number of groups in a group type *******************/
+/*****************************************************************************/
+
+unsigned Grp_DB_CountNumGrpsInThisCrsOfType (long GrpTypCod)
+ {
+ /***** Get number of groups of a type from database *****/
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of groups of a type",
+ "SELECT COUNT(*)"
+ " FROM grp_groups"
+ " WHERE GrpTypCod=%ld",
+ GrpTypCod);
+ }
+
+/*****************************************************************************/
+/******************** Get groups of a type in this course ********************/
+/*****************************************************************************/
+
+unsigned Grp_DB_GetGrpsOfType (MYSQL_RES **mysql_res,long GrpTypCod)
+ {
+ /***** Get groups of a type from database *****/
+ // Don't use INNER JOIN because there are groups without assigned room
+ return (unsigned)
+ DB_QuerySELECT (mysql_res,"can not get groups of a type",
+ "SELECT grp_groups.GrpCod," // row[0]
+ "grp_groups.GrpName," // row[1]
+ "grp_groups.RooCod," // row[2]
+ "roo_rooms.ShortName," // row[3]
+ "grp_groups.MaxStudents," // row[4]
+ "grp_groups.Open," // row[5]
+ "grp_groups.FileZones" // row[6]
+ " FROM grp_groups"
+ " LEFT JOIN roo_rooms"
+ " ON grp_groups.RooCod=roo_rooms.RooCod"
+ " WHERE grp_groups.GrpTypCod=%ld"
+ " ORDER BY grp_groups.GrpName",
+ GrpTypCod);
+ }
diff --git a/swad_group_database.h b/swad_group_database.h
new file mode 100644
index 00000000..f1c9fc81
--- /dev/null
+++ b/swad_group_database.h
@@ -0,0 +1,57 @@
+// swad_group_database.h: types of groups and groups operations with database
+
+#ifndef _SWAD_GRP_DB
+#define _SWAD_GRP_DB
+/*
+ SWAD (Shared Workspace At a Distance in Spanish),
+ 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-2021 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 ***********************************/
+/*****************************************************************************/
+
+// #include "swad_info.h"
+// #include "swad_room.h"
+// #include "swad_user.h"
+
+/*****************************************************************************/
+/***************************** Public constants ******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/******************************* Public types ********************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/****************************** Public prototypes ****************************/
+/*****************************************************************************/
+
+void Grp_DB_LockTables (void);
+void Grp_DB_UnlockTables (void);
+
+bool Grp_DB_CheckIfAssociatedToGrp (const char *Table,const char *Field,
+ long Cod,long GrpCod);
+bool Grp_DB_CheckIfAssociatedToGrps (const char *Table,const char *Field,long Cod);
+
+unsigned Grp_DB_CountNumGrpsInCurrentCrs (void);
+unsigned Grp_DB_CountNumGrpsInThisCrsOfType (long GrpTypCod);
+unsigned Grp_DB_GetGrpsOfType (MYSQL_RES **mysql_res,long GrpTypCod);
+
+#endif
diff --git a/swad_hierarchy.c b/swad_hierarchy.c
index 9d781e79..7db71008 100644
--- a/swad_hierarchy.c
+++ b/swad_hierarchy.c
@@ -34,6 +34,7 @@
#include "swad_error.h"
#include "swad_form.h"
#include "swad_global.h"
+#include "swad_group_database.h"
#include "swad_hierarchy.h"
#include "swad_hierarchy_level.h"
#include "swad_HTML.h"
diff --git a/swad_match.c b/swad_match.c
index 0a485e52..bd1abde8 100644
--- a/swad_match.c
+++ b/swad_match.c
@@ -38,6 +38,7 @@
#include "swad_form.h"
#include "swad_game.h"
#include "swad_global.h"
+#include "swad_group_database.h"
#include "swad_HTML.h"
#include "swad_match.h"
#include "swad_match_result.h"
diff --git a/swad_survey.c b/swad_survey.c
index 7c0c0f15..2d8398c2 100644
--- a/swad_survey.c
+++ b/swad_survey.c
@@ -40,6 +40,7 @@
#include "swad_form.h"
#include "swad_global.h"
#include "swad_group.h"
+#include "swad_group_database.h"
#include "swad_HTML.h"
#include "swad_notification.h"
#include "swad_pagination.h"