// swad_group.c: types of groups and groups /* 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 NULL #include // For asprintf #include // For exit, system, malloc, free, rand, etc. #include // For string functions #include "swad_action.h" #include "swad_box.h" #include "swad_database.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_project.h" #include "swad_setting.h" /*****************************************************************************/ /*************************** Internal constants ******************************/ /*****************************************************************************/ #define Grp_GROUP_TYPES_SECTION_ID "grp_types" #define Grp_NEW_GROUP_TYPE_SECTION_ID "new_grp_type" #define Grp_GROUPS_SECTION_ID "grps" #define Grp_NEW_GROUP_SECTION_ID "new_grp" static const bool Grp_ICanChangeGrps[Rol_NUM_ROLES] = { false, // Rol_UNK false, // Rol_GST false, // Rol_USR true, // Rol_STD false, // Rol_NET true, // Rol_TCH false, // Rol_DEG_ADM false, // Rol_CTR_ADM false, // Rol_INS_ADM true, // Rol_SYS_ADM }; /*****************************************************************************/ /***************************** Internal types ********************************/ /*****************************************************************************/ /*****************************************************************************/ /************* External global variables from others modules *****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /************************* Internal global variables *************************/ /*****************************************************************************/ /*****************************************************************************/ /***************************** Internal prototypes ***************************/ /*****************************************************************************/ static void Grp_ReqEditGroupsInternal (Ale_AlertType_t AlertTypeGroupTypes,const char *AlertTextGroupTypes, Ale_AlertType_t AlertTypeGroups,const char *AlertTextGroups); static void Grp_ReqEditGroupsInternal0 (void); static void Grp_ReqEditGroupsInternal1 (Ale_AlertType_t AlertTypeGroupTypes, const char *AlertTextGroupTypes); static void Grp_ReqEditGroupsInternal2 (Ale_AlertType_t AlertTypeGroups, const char *AlertTextGroups); static void Grp_EditGroupTypes (void); static void Grp_EditGroups (void); static void Grp_PutIconsEditingGroups (void); static void Grp_PutIconToCreateNewGroup (void); static void Grp_PutCheckboxAllGrps (Grp_WhichGroups_t GroupsSelectableByStdsOrNETs); static void Grp_LockTables (void); static void Grp_UnlockTables (void); static void Grp_ConstructorListGrpAlreadySelec (struct ListGrpsAlreadySelec **AlreadyExistsGroupOfType); static void Grp_DestructorListGrpAlreadySelec (struct ListGrpsAlreadySelec **AlreadyExistsGroupOfType); static void Grp_RemoveUsrFromGroup (long UsrCod,long GrpCod); static void Grp_AddUsrToGroup (struct UsrData *UsrDat,long GrpCod); static void Grp_ListGroupTypesForEdition (void); static void Grp_PutIconsEditingGroupTypes (void); static void Grp_PutIconToViewGroups (void); static void Grp_PutIconToCreateNewGroupType (void); static void Grp_WriteHeadingGroupTypes (void); static void Grp_ListGroupsForEdition (void); static void Grp_WriteHeadingGroups (void); static void Grp_PutIconToEditGroups (void); static void Grp_ShowWarningToStdsToChangeGrps (void); static bool Grp_ListGrpsForChangeMySelection (struct GroupType *GrpTyp, unsigned *NumGrpsThisTypeIBelong); static void Grp_ListGrpsToAddOrRemUsrs (struct GroupType *GrpTyp,long UsrCod); static void Grp_ListGrpsForMultipleSelection (struct GroupType *GrpTyp, Grp_WhichGroups_t GroupsSelectableByStdsOrNETs); 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 (void); static unsigned Grp_CountNumGrpsInThisCrsOfType (long GrpTypCod); static void Grp_GetDataOfGroupTypeByCod (struct GroupType *GrpTyp); static bool Grp_GetMultipleEnrolmentOfAGroupType (long GrpTypCod); static long Grp_GetTypeOfGroupOfAGroup (long GrpCod); static unsigned long Grp_CountNumUsrsInNoGrpsOfType (Rol_Role_t Role,long GrpTypCod); static bool Grp_CheckIfIBelongToGrpsOfType (long GrpTypCod); static void Grp_GetLstCodGrpsUsrBelongs (long CrsCod,long GrpTypCod,long UsrCod, struct ListCodGrps *LstGrps); static bool Grp_CheckIfGrpIsInList (long GrpCod,struct ListCodGrps *LstGrps); static bool Grp_CheckIfOpenTimeInTheFuture (time_t OpenTimeUTC); static bool Grp_CheckIfGroupTypeNameExists (const char *GrpTypName,long GrpTypCod); static bool Grp_CheckIfGroupNameExists (long GrpTypCod,const char *GrpName,long GrpCod); static void Grp_CreateGroupType (void); static void Grp_CreateGroup (void); static void Grp_AskConfirmRemGrpTypWithGrps (unsigned NumGrps); static void Grp_PutParamRemGrpTyp (void); static void Grp_AskConfirmRemGrp (void); static void Grp_PutParamRemGrp (void); static void Grp_RemoveGroupTypeCompletely (void); static void Grp_RemoveGroupCompletely (void); static void Grp_WriteMaxStds (unsigned MaxStudents); static long Grp_GetParamGrpTypCod (void); static long Grp_GetParamGrpCod (void); static void Grp_PutParamGrpTypCod (long GrpTypCod); /*****************************************************************************/ /******************* Write the names of the selected groups ******************/ /*****************************************************************************/ void Grp_WriteNamesOfSelectedGrps (void) { extern const char *Txt_Group; extern const char *Txt_Groups; extern const char *Txt_users_with_no_group; extern const char *Txt_and; long GrpCod; unsigned NumGrpSel; struct GroupData GrpDat; /***** Show the selected groups *****/ fprintf (Gbl.F.Out,"%s: ", (Gbl.Crs.Grps.LstGrpsSel.NumGrps == 1) ? Txt_Group : Txt_Groups); for (NumGrpSel = 0; NumGrpSel < Gbl.Crs.Grps.LstGrpsSel.NumGrps; NumGrpSel++) { if ((GrpCod = Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrpSel]) >= 0) { GrpDat.GrpCod = GrpCod; Grp_GetDataOfGroupByCod (&GrpDat); fprintf (Gbl.F.Out,"%s %s", GrpDat.GrpTypName,GrpDat.GrpName); } else // GrpCod < 0 ==> students not belonging to any group of type (-GrpCod) { Gbl.Crs.Grps.GrpTyp.GrpTypCod = -GrpCod; Grp_GetDataOfGroupTypeByCod (&Gbl.Crs.Grps.GrpTyp); fprintf (Gbl.F.Out,"%s (%s)", Gbl.Crs.Grps.GrpTyp.GrpTypName, Txt_users_with_no_group); } if (Gbl.Crs.Grps.LstGrpsSel.NumGrps >= 2) { if (NumGrpSel == Gbl.Crs.Grps.LstGrpsSel.NumGrps-2) fprintf (Gbl.F.Out," %s ",Txt_and); if (Gbl.Crs.Grps.LstGrpsSel.NumGrps >= 3) if (NumGrpSel < Gbl.Crs.Grps.LstGrpsSel.NumGrps-2) fprintf (Gbl.F.Out,", "); } } } /*****************************************************************************/ /************************** Put forms to edit groups *************************/ /*****************************************************************************/ void Grp_ReqEditGroups (void) { Grp_ReqEditGroupsInternal (Ale_INFO,NULL, Ale_INFO,NULL); } static void Grp_ReqEditGroupsInternal (Ale_AlertType_t AlertTypeGroupTypes, const char *AlertTextGroupTypes, Ale_AlertType_t AlertTypeGroups, const char *AlertTextGroups) { Grp_ReqEditGroupsInternal0 (); Grp_ReqEditGroupsInternal1 (AlertTypeGroupTypes,AlertTextGroupTypes); Grp_ReqEditGroupsInternal2 (AlertTypeGroups,AlertTextGroups); } static void Grp_ReqEditGroupsInternal0 (void) { /***** Start groups types section *****/ HTM_SECTION_Begin (Grp_GROUP_TYPES_SECTION_ID); } static void Grp_ReqEditGroupsInternal1 (Ale_AlertType_t AlertTypeGroupTypes, const char *AlertTextGroupTypes) { /***** Get list of groups types and groups in this course *****/ Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ALL_GROUP_TYPES); /***** Get list of classrooms in this centre *****/ Cla_GetListClassrooms (Cla_ONLY_SHRT_NAME); /***** Show optional alert *****/ if (AlertTextGroupTypes) if (AlertTextGroupTypes[0]) Ale_ShowAlert (AlertTypeGroupTypes,AlertTextGroupTypes); /***** Put form to edit group types *****/ Grp_EditGroupTypes (); /***** End groups types section *****/ HTM_SECTION_End (); /***** Start groups section *****/ HTM_SECTION_Begin (Grp_GROUPS_SECTION_ID); } static void Grp_ReqEditGroupsInternal2 (Ale_AlertType_t AlertTypeGroups, const char *AlertTextGroups) { /***** Show optional alert *****/ if (AlertTextGroups) if (AlertTextGroups[0]) Ale_ShowAlert (AlertTypeGroups,AlertTextGroups); /***** Put form to edit groups *****/ if (Gbl.Crs.Grps.GrpTypes.Num) // If there are group types... Grp_EditGroups (); /***** End groups section *****/ HTM_SECTION_End (); /***** Free list of classrooms in this centre *****/ Cla_FreeListClassrooms (); /***** Free list of groups types and groups in this course *****/ Grp_FreeListGrpTypesAndGrps (); } /*****************************************************************************/ /************************* Put forms to edit group types *********************/ /*****************************************************************************/ static void Grp_EditGroupTypes (void) { extern const char *Hlp_USERS_Groups; extern const char *Txt_Types_of_group; extern const char *Txt_There_are_no_types_of_group_in_the_course_X; /***** Begin box *****/ Box_BoxBegin (NULL,Txt_Types_of_group,Grp_PutIconsEditingGroupTypes, Hlp_USERS_Groups,Box_NOT_CLOSABLE); /***** Put a form to create a new group type *****/ Grp_PutFormToCreateGroupType (); /***** Forms to edit current group types *****/ if (Gbl.Crs.Grps.GrpTypes.Num) // Group types found... Grp_ListGroupTypesForEdition (); else // No group types found in this course Ale_ShowAlert (Ale_INFO,Txt_There_are_no_types_of_group_in_the_course_X, Gbl.Hierarchy.Crs.ShrtName); /***** End box *****/ Box_BoxEnd (); } /*****************************************************************************/ /**************************** Put forms to edit groups ***********************/ /*****************************************************************************/ static void Grp_EditGroups (void) { extern const char *Hlp_USERS_Groups; extern const char *Txt_Groups; extern const char *Txt_No_groups_have_been_created_in_the_course_X; /***** Begin box *****/ Box_BoxBegin (NULL,Txt_Groups,Grp_PutIconsEditingGroups, Hlp_USERS_Groups,Box_NOT_CLOSABLE); /***** Put a form to create a new group *****/ Grp_PutFormToCreateGroup (); /***** Forms to edit current groups *****/ if (Gbl.Crs.Grps.GrpTypes.NumGrpsTotal) // If there are groups... Grp_ListGroupsForEdition (); else // There are group types, but there aren't groups Ale_ShowAlert (Ale_INFO,Txt_No_groups_have_been_created_in_the_course_X, Gbl.Hierarchy.Crs.ShrtName); /***** End box *****/ Box_BoxEnd (); } /*****************************************************************************/ /**************** Put contextual icons in edition of groups ******************/ /*****************************************************************************/ static void Grp_PutIconsEditingGroups (void) { /***** Put icon to view groups *****/ Grp_PutIconToViewGroups (); /***** Put icon to create a new group *****/ Grp_PutIconToCreateNewGroup (); } static void Grp_PutIconToCreateNewGroup (void) { extern const char *Txt_New_group; /***** Put form to create a new group *****/ Ico_PutContextualIconToAdd (ActReqEdiGrp,Grp_NEW_GROUP_SECTION_ID,NULL, Txt_New_group); } /*****************************************************************************/ /*************** Show form to select one or several groups *******************/ /*****************************************************************************/ void Grp_ShowFormToSelectSeveralGroups (void (*FuncParams) (void), Grp_WhichGroups_t GroupsSelectableByStdsOrNETs) { extern const char *Hlp_USERS_Groups; extern const char *The_ClassFormInBoxBold[The_NUM_THEMES]; extern const char *Txt_Groups; extern const char *Txt_Update_users; unsigned NumGrpTyp; bool ICanEdit; /***** Trivial check: if no groups ==> nothing to do *****/ if (!Gbl.Crs.Grps.NumGrps) return; /***** Begin box *****/ ICanEdit = !Gbl.Form.Inside && (Gbl.Usrs.Me.Role.Logged == Rol_TCH || Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM); Box_BoxBegin (NULL,Txt_Groups,ICanEdit ? Grp_PutIconToEditGroups : NULL, Hlp_USERS_Groups,Box_CLOSABLE); /***** Begin form to update the students listed depending on the groups selected *****/ Frm_StartFormAnchor (Gbl.Action.Act, // Repeat current action Usr_USER_LIST_SECTION_ID); Usr_PutParamsPrefsAboutUsrList (); if (FuncParams) FuncParams (); /***** Select all groups *****/ Grp_PutCheckboxAllGrps (GroupsSelectableByStdsOrNETs); /***** Get list of groups types and groups in this course *****/ Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS); /***** List the groups for each group type *****/ HTM_TABLE_BeginWidePadding (2); for (NumGrpTyp = 0; NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num; NumGrpTyp++) if (Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].NumGrps) Grp_ListGrpsForMultipleSelection (&Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp], GroupsSelectableByStdsOrNETs); HTM_TABLE_End (); /***** Free list of groups types and groups in this course *****/ Grp_FreeListGrpTypesAndGrps (); /***** Submit button *****/ HTM_DIV_Begin ("class=\"CM\" style=\"padding-top:12px;\""); Frm_LinkFormSubmitAnimated (Txt_Update_users, The_ClassFormInBoxBold[Gbl.Prefs.Theme], "CopyMessageToHiddenFields();"); Ico_PutCalculateIconWithText (Txt_Update_users); Frm_LinkFormEnd (); HTM_DIV_End (); /***** End form *****/ Frm_EndForm (); /***** End box *****/ Box_BoxEnd (); } /*****************************************************************************/ /******************* Put checkbox to select all groups ***********************/ /*****************************************************************************/ static void Grp_PutCheckboxAllGrps (Grp_WhichGroups_t GroupsSelectableByStdsOrNETs) { extern const char *The_ClassFormInBox[The_NUM_THEMES]; extern const char *Txt_All_groups; bool ICanSelUnselGroup; switch (Gbl.Usrs.Me.Role.Logged) { case Rol_STD: case Rol_NET: ICanSelUnselGroup = (GroupsSelectableByStdsOrNETs == Grp_ALL_GROUPS); break; case Rol_TCH: case Rol_DEG_ADM: case Rol_CTR_ADM: case Rol_INS_ADM: case Rol_SYS_ADM: ICanSelUnselGroup = true; break; default: ICanSelUnselGroup = false; break; } HTM_DIV_Begin ("class=\"CONTEXT_OPT\""); fprintf (Gbl.F.Out,""); fprintf (Gbl.F.Out,"