// 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 (char Str[10 + 1],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\"");
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
HTM_INPUT_CHECKBOX ("AllGroups",false,
"value=\"Y\"%s",
ICanSelUnselGroup ? (Gbl.Usrs.ClassPhoto.AllGroups ? " checked=\"checked\""
" onclick=\"togglecheckChildren(this,'GrpCods')\"" :
" onclick=\"togglecheckChildren(this,'GrpCods')\"") :
" disabled=\"disabled\"");
fprintf (Gbl.F.Out," %s",Txt_All_groups);
HTM_LABEL_End ();
HTM_DIV_End ();
}
/*****************************************************************************/
/************ Put parameters with the groups of students selected ************/
/*****************************************************************************/
void Grp_PutParamsCodGrps (void)
{
extern const char *Par_SEPARATOR_PARAM_MULTIPLE;
unsigned NumGrpSel;
size_t MaxLengthGrpCods;
char *GrpCods;
char GrpCod[20 + 1];
/***** Write the boolean parameter that indicates if all the groups must be listed *****/
Par_PutHiddenParamChar ("AllGroups",
Gbl.Usrs.ClassPhoto.AllGroups ? 'Y' :
'N');
/***** Write the parameter with the list of group codes to show *****/
if (!Gbl.Usrs.ClassPhoto.AllGroups &&
Gbl.Crs.Grps.LstGrpsSel.NumGrps)
{
MaxLengthGrpCods = Gbl.Crs.Grps.LstGrpsSel.NumGrps * (1 + 20) - 1;
if ((GrpCods = (char *) malloc (MaxLengthGrpCods + 1)) == NULL)
Lay_NotEnoughMemoryExit ();
for (NumGrpSel = 0;
NumGrpSel < Gbl.Crs.Grps.LstGrpsSel.NumGrps;
NumGrpSel++)
{
/* Append group code to list */
if (NumGrpSel)
Str_Concat (GrpCods,Par_SEPARATOR_PARAM_MULTIPLE,MaxLengthGrpCods);
snprintf (GrpCod,sizeof (GrpCod),
"%ld",
Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrpSel]);
Str_Concat (GrpCods,GrpCod,MaxLengthGrpCods);
}
Par_PutHiddenParamString (NULL,"GrpCods",GrpCods);
free ((void *) GrpCods);
}
}
/*****************************************************************************/
/**************** Get parameters related to groups selected ******************/
/*****************************************************************************/
void Grp_GetParCodsSeveralGrpsToShowUsrs (void)
{
struct ListCodGrps LstGrpsIBelong;
unsigned NumGrp;
if (++Gbl.Crs.Grps.LstGrpsSel.NestedCalls > 1) // If list is created yet, there's nothing to do
return;
/***** Get boolean parameter that indicates if all groups must be listed *****/
Gbl.Usrs.ClassPhoto.AllGroups = Par_GetParToBool ("AllGroups");
/***** Get parameter with list of groups selected *****/
Grp_GetParCodsSeveralGrps ();
if (Gbl.Crs.Grps.NumGrps && // This course has groups and...
!Gbl.Crs.Grps.LstGrpsSel.NumGrps) // ...I haven't selected any group
{
/***** I I haven't selected any group, show by default the groups I belong to *****/
/* Get list of groups of all types in current course I belong to */
Grp_GetLstCodGrpsUsrBelongs (Gbl.Hierarchy.Crs.CrsCod,-1L,
Gbl.Usrs.Me.UsrDat.UsrCod,&LstGrpsIBelong);
if (LstGrpsIBelong.NumGrps)
{
/* Allocate space for list of selected groups */
if ((Gbl.Crs.Grps.LstGrpsSel.GrpCods = (long *) calloc (LstGrpsIBelong.NumGrps,sizeof (long))) == NULL)
Lay_NotEnoughMemoryExit ();
/* Fill list of selected groups with list of groups I belong to */
for (NumGrp = 0;
NumGrp < LstGrpsIBelong.NumGrps;
NumGrp++)
Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrp] = LstGrpsIBelong.GrpCods[NumGrp];
Gbl.Crs.Grps.LstGrpsSel.NumGrps = LstGrpsIBelong.NumGrps;
}
/* Free list of groups I belong to */
Grp_FreeListCodGrp (&LstGrpsIBelong);
}
/***** If no groups selected ==> show all groups *****/
if (!Gbl.Crs.Grps.LstGrpsSel.NumGrps)
Gbl.Usrs.ClassPhoto.AllGroups = true;
}
/*****************************************************************************/
/**************** Get parameter with list of groups selected *****************/
/*****************************************************************************/
void Grp_GetParCodsSeveralGrps (void)
{
char *ParamLstCodGrps;
const char *Ptr;
char LongStr[1 + 10 + 1];
unsigned NumGrp;
unsigned long MaxSizeLstGrpCods = ((1 + 10 + 1) * Gbl.Crs.Grps.NumGrps) - 1;
/***** Reset number of groups selected *****/
Gbl.Crs.Grps.LstGrpsSel.NumGrps = 0;
if (Gbl.Crs.Grps.NumGrps) // If course has groups
{
/***** Allocate memory for the list of group codes selected *****/
if ((ParamLstCodGrps = (char *) malloc (MaxSizeLstGrpCods + 1)) == NULL)
Lay_NotEnoughMemoryExit ();
/***** Get parameter with list of groups to list *****/
Par_GetParMultiToText ("GrpCods",ParamLstCodGrps,MaxSizeLstGrpCods);
if (ParamLstCodGrps[0])
{
/***** Count number of groups selected from LstCodGrps *****/
for (Ptr = ParamLstCodGrps, NumGrp = 0;
*Ptr;
NumGrp++)
Par_GetNextStrUntilSeparParamMult (&Ptr,LongStr,1 + 10);
Gbl.Crs.Grps.LstGrpsSel.NumGrps = NumGrp;
if (Gbl.Crs.Grps.LstGrpsSel.NumGrps) // If I have selected groups...
{
/***** Create a list of groups selected from LstCodGrps *****/
if ((Gbl.Crs.Grps.LstGrpsSel.GrpCods = (long *) calloc (Gbl.Crs.Grps.LstGrpsSel.NumGrps,sizeof (long))) == NULL)
Lay_NotEnoughMemoryExit ();
for (Ptr = ParamLstCodGrps, NumGrp = 0;
*Ptr;
NumGrp++)
{
Par_GetNextStrUntilSeparParamMult (&Ptr,LongStr,1 + 10);
Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrp] = Str_ConvertStrCodToLongCod (LongStr);
}
}
}
/***** Free memory used for the list of groups to show *****/
free ((void *) ParamLstCodGrps);
}
}
/*****************************************************************************/
/********* Free memory used for the list of group codes selected *************/
/*****************************************************************************/
void Grp_FreeListCodSelectedGrps (void)
{
if (Gbl.Crs.Grps.LstGrpsSel.NestedCalls > 0)
if (--Gbl.Crs.Grps.LstGrpsSel.NestedCalls == 0)
if (Gbl.Crs.Grps.LstGrpsSel.GrpCods)
{
free ((void *) Gbl.Crs.Grps.LstGrpsSel.GrpCods);
Gbl.Crs.Grps.LstGrpsSel.GrpCods = NULL;
Gbl.Crs.Grps.LstGrpsSel.NumGrps = 0;
}
}
/*****************************************************************************/
/******************* Change my groups and show form again ********************/
/*****************************************************************************/
void Grp_ChangeMyGrpsAndShowChanges (void)
{
/***** Change my groups *****/
Grp_ChangeMyGrps (Cns_VERBOSE);
/***** Show again the table of selection of groups with the changes already made *****/
Grp_ReqRegisterInGrps ();
}
/*****************************************************************************/
/****************************** Change my groups *****************************/
/*****************************************************************************/
void Grp_ChangeMyGrps (Cns_QuietOrVerbose_t QuietOrVerbose)
{
extern const char *Txt_The_requested_group_changes_were_successful;
extern const char *Txt_There_has_been_no_change_in_groups;
extern const char *Txt_In_a_type_of_group_with_single_enrolment_students_can_not_be_registered_in_more_than_one_group;
struct ListCodGrps LstGrpsIWant;
bool MySelectionIsValid;
bool ChangesMade;
/***** Can I change my groups? *****/
if (Grp_ICanChangeGrps[Gbl.Usrs.Me.Role.Logged])
{
/***** Get list of groups types and groups in this course *****/
Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS);
/***** Get the group codes which I want to join to *****/
LstGrpsIWant.GrpCods = NULL; // Initialized to avoid bug reported by Coverity
LstGrpsIWant.NumGrps = 0; // Initialized to avoid bug reported by Coverity
Grp_GetLstCodsGrpWanted (&LstGrpsIWant);
/***** A student can not be enroled in more than one group
if the type of group is of single enrolment *****/
// As the form to register in groups of single enrolment...
// ...is a radio-based form and not a checkbox-based form...
// ...this check is made only to avoid problems...
// ...if the student manipulates the form
MySelectionIsValid = Grp_CheckIfSelectionGrpsSingleEnrolmentIsValid (Gbl.Usrs.Me.Role.Logged,&LstGrpsIWant);
/***** Free list of groups types and groups in this course *****/
// The lists of group types and groups need to be freed here...
// ...in order to get them again when changing my groups atomically
Grp_FreeListGrpTypesAndGrps ();
/***** Change my groups *****/
if (MySelectionIsValid)
{
ChangesMade = Grp_ChangeMyGrpsAtomically (&LstGrpsIWant);
if (QuietOrVerbose == Cns_VERBOSE)
{
if (ChangesMade)
Ale_CreateAlert (Ale_SUCCESS,NULL,
Txt_The_requested_group_changes_were_successful);
else
Ale_CreateAlert (Ale_WARNING,NULL,
Txt_There_has_been_no_change_in_groups);
}
}
else if (QuietOrVerbose == Cns_VERBOSE)
Ale_CreateAlert (Ale_WARNING,NULL,
Txt_In_a_type_of_group_with_single_enrolment_students_can_not_be_registered_in_more_than_one_group);
/***** Free memory with the list of groups which I want to belong to *****/
Grp_FreeListCodGrp (&LstGrpsIWant);
}
}
/*****************************************************************************/
/********************** Change groups of another user ************************/
/*****************************************************************************/
void Grp_ChangeOtherUsrGrps (void)
{
extern const char *Txt_The_requested_group_changes_were_successful;
extern const char *Txt_There_has_been_no_change_in_groups;
extern const char *Txt_In_a_type_of_group_with_single_enrolment_students_can_not_be_registered_in_more_than_one_group;
struct ListCodGrps LstGrpsUsrWants;
bool SelectionIsValid;
/***** Can I change another user's groups? *****/
if (Grp_ICanChangeGrps[Gbl.Usrs.Me.Role.Logged])
{
/***** Get list of groups types and groups in current course *****/
Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS);
/***** Get the list of groups to which register this user *****/
LstGrpsUsrWants.GrpCods = NULL; // Initialized to avoid bug reported by Coverity
LstGrpsUsrWants.NumGrps = 0; // Initialized to avoid bug reported by Coverity
Grp_GetLstCodsGrpWanted (&LstGrpsUsrWants);
/***** A student can not be enroled in more than one group
if the type of group is of single enrolment *****/
SelectionIsValid = Grp_CheckIfSelectionGrpsSingleEnrolmentIsValid (Gbl.Usrs.Other.UsrDat.Roles.InCurrentCrs.Role,&LstGrpsUsrWants);
/***** Free list of groups types and groups in this course *****/
// The lists of group types and groups need to be freed here...
// ...in order to get them again when changing groups atomically
Grp_FreeListGrpTypesAndGrps ();
/***** Register user in the selected groups *****/
if (SelectionIsValid)
Grp_ChangeGrpsOtherUsrAtomically (&LstGrpsUsrWants);
else
Ale_CreateAlert (Ale_WARNING,NULL,
Txt_In_a_type_of_group_with_single_enrolment_students_can_not_be_registered_in_more_than_one_group);
/***** Free memory with the list of groups to/from which register/remove users *****/
Grp_FreeListCodGrp (&LstGrpsUsrWants);
}
}
/*****************************************************************************/
/********************** Change my groups atomically **************************/
/*****************************************************************************/
// Return true if desired changes are made
bool Grp_ChangeMyGrpsAtomically (struct ListCodGrps *LstGrpsIWant)
{
struct ListCodGrps LstGrpsIBelong;
unsigned NumGrpTyp;
unsigned NumGrpIBelong;
unsigned NumGrpIWant;
unsigned NumGrpThisType;
struct GroupType *GrpTyp;
bool ITryToLeaveAClosedGroup = false;
bool ITryToRegisterInAClosedGroup = false;
bool ITryToRegisterInFullGroup = false;
bool RemoveMeFromThisGrp;
bool RegisterMeInThisGrp;
bool ChangesMade = false;
/***** Lock tables to make the inscription atomic *****/
Grp_LockTables ();
/***** Get list of groups types and groups in this course *****/
Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS);
/***** Query in the database the group codes which I belong to *****/
Grp_GetLstCodGrpsUsrBelongs (Gbl.Hierarchy.Crs.CrsCod,-1L,
Gbl.Usrs.Me.UsrDat.UsrCod,&LstGrpsIBelong);
if (Gbl.Usrs.Me.Role.Logged == Rol_STD)
{
/***** Go across the list of groups which I belong to and check if I try to leave a closed group *****/
for (NumGrpIBelong = 0;
NumGrpIBelong < LstGrpsIBelong.NumGrps && !ITryToLeaveAClosedGroup;
NumGrpIBelong++)
{
for (NumGrpIWant = 0, RemoveMeFromThisGrp = true;
NumGrpIWant < LstGrpsIWant->NumGrps && RemoveMeFromThisGrp;
NumGrpIWant++)
if (LstGrpsIBelong.GrpCods[NumGrpIBelong] == LstGrpsIWant->GrpCods[NumGrpIWant])
RemoveMeFromThisGrp = false;
if (RemoveMeFromThisGrp)
/* Check if the group is closed */
for (NumGrpTyp = 0;
NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num && !ITryToLeaveAClosedGroup;
NumGrpTyp++)
{
GrpTyp = &Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp];
for (NumGrpThisType = 0;
NumGrpThisType < GrpTyp->NumGrps && !ITryToLeaveAClosedGroup;
NumGrpThisType++)
if ((GrpTyp->LstGrps[NumGrpThisType]).GrpCod == LstGrpsIBelong.GrpCods[NumGrpIBelong])
if (!((GrpTyp->LstGrps[NumGrpThisType]).Open))
ITryToLeaveAClosedGroup = true;
}
}
if (!ITryToLeaveAClosedGroup)
/***** Go across the list of groups which I want to belong
and check that they are not closed or full *****/
for (NumGrpIWant = 0;
NumGrpIWant < LstGrpsIWant->NumGrps &&
!ITryToRegisterInAClosedGroup &&
!ITryToRegisterInFullGroup;
NumGrpIWant++)
{
for (NumGrpIBelong = 0, RegisterMeInThisGrp = true;
NumGrpIBelong < LstGrpsIBelong.NumGrps && RegisterMeInThisGrp;
NumGrpIBelong++)
if (LstGrpsIWant->GrpCods[NumGrpIWant] == LstGrpsIBelong.GrpCods[NumGrpIBelong])
RegisterMeInThisGrp = false;
if (RegisterMeInThisGrp)
/* Check if the group is closed or full */
for (NumGrpTyp = 0;
NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num &&
!ITryToRegisterInAClosedGroup &&
!ITryToRegisterInFullGroup;
NumGrpTyp++)
{
GrpTyp = &Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp];
for (NumGrpThisType = 0;
NumGrpThisType < GrpTyp->NumGrps &&
!ITryToRegisterInAClosedGroup &&
!ITryToRegisterInFullGroup;
NumGrpThisType++)
if ((GrpTyp->LstGrps[NumGrpThisType]).GrpCod == LstGrpsIWant->GrpCods[NumGrpIWant])
{
/* Check if the group is closed */
if (!((GrpTyp->LstGrps[NumGrpThisType]).Open))
ITryToRegisterInAClosedGroup = true;
/* Check if the group is full */
else if ((GrpTyp->LstGrps[NumGrpThisType]).NumUsrs[Rol_STD] >=
(GrpTyp->LstGrps[NumGrpThisType]).MaxStudents)
ITryToRegisterInFullGroup = true;
}
}
}
}
if (!ITryToLeaveAClosedGroup &&
!ITryToRegisterInAClosedGroup &&
!ITryToRegisterInFullGroup)
{
/***** Go across the list of groups I belong to, removing those groups that are not present in the list of groups I want to belong to *****/
for (NumGrpIBelong = 0;
NumGrpIBelong < LstGrpsIBelong.NumGrps;
NumGrpIBelong++)
{
for (NumGrpIWant = 0, RemoveMeFromThisGrp = true;
NumGrpIWant < LstGrpsIWant->NumGrps && RemoveMeFromThisGrp;
NumGrpIWant++)
if (LstGrpsIBelong.GrpCods[NumGrpIBelong] == LstGrpsIWant->GrpCods[NumGrpIWant])
RemoveMeFromThisGrp = false;
if (RemoveMeFromThisGrp)
Grp_RemoveUsrFromGroup (Gbl.Usrs.Me.UsrDat.UsrCod,LstGrpsIBelong.GrpCods[NumGrpIBelong]);
}
/***** Go across the list of groups that I want to register in, adding those groups that are not present in the list of groups I belong to *****/
for (NumGrpIWant = 0;
NumGrpIWant < LstGrpsIWant->NumGrps;
NumGrpIWant++)
{
for (NumGrpIBelong = 0, RegisterMeInThisGrp = true;
NumGrpIBelong < LstGrpsIBelong.NumGrps && RegisterMeInThisGrp;
NumGrpIBelong++)
if (LstGrpsIWant->GrpCods[NumGrpIWant] == LstGrpsIBelong.GrpCods[NumGrpIBelong])
RegisterMeInThisGrp = false;
if (RegisterMeInThisGrp)
Grp_AddUsrToGroup (&Gbl.Usrs.Me.UsrDat,LstGrpsIWant->GrpCods[NumGrpIWant]);
}
ChangesMade = true;
}
/***** Free memory with the list of groups which I belonged to *****/
Grp_FreeListCodGrp (&LstGrpsIBelong);
/***** Unlock tables after changes in my groups *****/
Grp_UnlockTables ();
/***** Free list of groups types and groups in this course *****/
Grp_FreeListGrpTypesAndGrps ();
return ChangesMade;
}
/*****************************************************************************/
/********************** Change my groups atomically **************************/
/*****************************************************************************/
void Grp_ChangeGrpsOtherUsrAtomically (struct ListCodGrps *LstGrpsUsrWants)
{
struct ListCodGrps LstGrpsUsrBelongs;
unsigned NumGrpUsrBelongs;
unsigned NumGrpUsrWants;
bool RemoveUsrFromThisGrp;
bool RegisterUsrInThisGrp;
/***** Lock tables to make the inscription atomic *****/
if (Gbl.Usrs.Other.UsrDat.Roles.InCurrentCrs.Role == Rol_STD)
Grp_LockTables ();
/***** Get list of groups types and groups in this course *****/
Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS);
/***** Query in the database the group codes which user belongs to *****/
Grp_GetLstCodGrpsUsrBelongs (Gbl.Hierarchy.Crs.CrsCod,-1L,
Gbl.Usrs.Other.UsrDat.UsrCod,&LstGrpsUsrBelongs);
/***** Go across the list of groups user belongs to, removing those groups that are not present in the list of groups user wants to belong to *****/
for (NumGrpUsrBelongs = 0;
NumGrpUsrBelongs < LstGrpsUsrBelongs.NumGrps;
NumGrpUsrBelongs++)
{
for (NumGrpUsrWants = 0, RemoveUsrFromThisGrp = true;
NumGrpUsrWants < LstGrpsUsrWants->NumGrps && RemoveUsrFromThisGrp;
NumGrpUsrWants++)
if (LstGrpsUsrBelongs.GrpCods[NumGrpUsrBelongs] == LstGrpsUsrWants->GrpCods[NumGrpUsrWants])
RemoveUsrFromThisGrp = false;
if (RemoveUsrFromThisGrp)
Grp_RemoveUsrFromGroup (Gbl.Usrs.Other.UsrDat.UsrCod,LstGrpsUsrBelongs.GrpCods[NumGrpUsrBelongs]);
}
/***** Go across the list of groups that user wants to register in, adding those groups that are not present in the list of groups user belongs to *****/
for (NumGrpUsrWants = 0;
NumGrpUsrWants < LstGrpsUsrWants->NumGrps;
NumGrpUsrWants++)
{
for (NumGrpUsrBelongs = 0, RegisterUsrInThisGrp = true;
NumGrpUsrBelongs < LstGrpsUsrBelongs.NumGrps && RegisterUsrInThisGrp;
NumGrpUsrBelongs++)
if (LstGrpsUsrWants->GrpCods[NumGrpUsrWants] == LstGrpsUsrBelongs.GrpCods[NumGrpUsrBelongs])
RegisterUsrInThisGrp = false;
if (RegisterUsrInThisGrp)
Grp_AddUsrToGroup (&Gbl.Usrs.Other.UsrDat,LstGrpsUsrWants->GrpCods[NumGrpUsrWants]);
}
/***** Free memory with the list of groups which user belonged to *****/
Grp_FreeListCodGrp (&LstGrpsUsrBelongs);
/***** Unlock tables after changes in groups *****/
if (Gbl.Usrs.Other.UsrDat.Roles.InCurrentCrs.Role == Rol_STD)
Grp_UnlockTables ();
/***** Free list of groups types and groups in this course *****/
Grp_FreeListGrpTypesAndGrps ();
}
/*****************************************************************************/
/*********** Lock tables to make the registration in groups atomic ***********/
/*****************************************************************************/
static void Grp_LockTables (void)
{
DB_Query ("can not lock tables to change user's groups",
"LOCK TABLES "
"crs_grp_types WRITE,"
"crs_grp WRITE,"
"crs_grp_usr WRITE,"
"crs_usr READ,"
"classrooms READ");
Gbl.DB.LockedTables = true;
}
/*****************************************************************************/
/*********** Unlock tables after changes in registration in groups ***********/
/*****************************************************************************/
static void Grp_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 *********/
/*****************************************************************************/
bool Grp_CheckIfSelectionGrpsSingleEnrolmentIsValid (Rol_Role_t Role,struct ListCodGrps *LstGrps)
{
struct ListGrpsAlreadySelec *AlreadyExistsGroupOfType;
unsigned NumCodGrp;
unsigned NumGrpTyp;
long GrpTypCod;
bool MultipleEnrolment;
bool SelectionValid;
switch (Role)
{
case Rol_STD:
if (LstGrps->NumGrps <= 1)
return true;
/***** Create and initialize list of groups already selected *****/
Grp_ConstructorListGrpAlreadySelec (&AlreadyExistsGroupOfType);
/***** Go across the list of groups selected
checking if a group of the same type is already selected *****/
SelectionValid = true;
for (NumCodGrp = 0;
SelectionValid && NumCodGrp < LstGrps->NumGrps;
NumCodGrp++)
{
GrpTypCod = Grp_GetTypeOfGroupOfAGroup (LstGrps->GrpCods[NumCodGrp]);
MultipleEnrolment = Grp_GetMultipleEnrolmentOfAGroupType (GrpTypCod);
if (!MultipleEnrolment)
for (NumGrpTyp = 0;
NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num;
NumGrpTyp++)
if (GrpTypCod == AlreadyExistsGroupOfType[NumGrpTyp].GrpTypCod)
{
if (AlreadyExistsGroupOfType[NumGrpTyp].AlreadySelected)
SelectionValid = false;
else
AlreadyExistsGroupOfType[NumGrpTyp].AlreadySelected = true;
break;
}
}
/***** Free memory of the list of booleanos that indicates
if a group of each type has been selected *****/
Grp_DestructorListGrpAlreadySelec (&AlreadyExistsGroupOfType);
return SelectionValid; // Return true if the selection of groups is correct
case Rol_NET:
case Rol_TCH:
return true;
default:
return false;
}
}
/*****************************************************************************/
/*********** Create e inicializar list of groups already selected ************/
/*****************************************************************************/
static void Grp_ConstructorListGrpAlreadySelec (struct ListGrpsAlreadySelec **AlreadyExistsGroupOfType)
{
unsigned NumGrpTyp;
/***** Allocate memory to a list of booleanos that indica if already se ha selected a group of cada type *****/
if ((*AlreadyExistsGroupOfType = (struct ListGrpsAlreadySelec *) calloc (Gbl.Crs.Grps.GrpTypes.Num,sizeof (struct ListGrpsAlreadySelec))) == NULL)
Lay_NotEnoughMemoryExit ();
/***** Initialize the list *****/
for (NumGrpTyp = 0;
NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num;
NumGrpTyp++)
{
(*AlreadyExistsGroupOfType)[NumGrpTyp].GrpTypCod = Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].GrpTypCod;
(*AlreadyExistsGroupOfType)[NumGrpTyp].AlreadySelected = false;
}
}
/*****************************************************************************/
/***************** Liberar list of groups already selected *******************/
/*****************************************************************************/
static void Grp_DestructorListGrpAlreadySelec (struct ListGrpsAlreadySelec **AlreadyExistsGroupOfType)
{
free ((void *) *AlreadyExistsGroupOfType);
*AlreadyExistsGroupOfType = NULL;
}
/*****************************************************************************/
/******************* Register user in the groups of a list *******************/
/*****************************************************************************/
void Grp_RegisterUsrIntoGroups (struct UsrData *UsrDat,struct ListCodGrps *LstGrps)
{
extern const char *Txt_THE_USER_X_has_been_removed_from_the_group_of_type_Y_to_which_it_belonged;
extern const char *Txt_THE_USER_X_has_been_enroled_in_the_group_of_type_Y_Z;
struct ListCodGrps LstGrpsHeBelongs;
unsigned NumGrpTyp;
unsigned NumGrpSel;
unsigned NumGrpThisType;
unsigned NumGrpHeBelongs;
bool MultipleEnrolment;
bool AlreadyRegisteredInGrp;
/***** For each existing type of group in the course... *****/
for (NumGrpTyp = 0;
NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num;
NumGrpTyp++)
{
MultipleEnrolment = Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].MultipleEnrolment;
/***** Query in the database the group codes of any group of this type the student belongs to *****/
LstGrpsHeBelongs.NumGrps = 0; // Initialized to avoid bug reported by Coverity
LstGrpsHeBelongs.GrpCods = NULL; // Initialized to avoid bug reported by Coverity
Grp_GetLstCodGrpsUsrBelongs (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].GrpTypCod,
UsrDat->UsrCod,&LstGrpsHeBelongs);
/***** For each group selected by me... *****/
for (NumGrpSel = 0;
NumGrpSel < LstGrps->NumGrps;
NumGrpSel++)
{
/* Check if the selected group is of this type */
for (NumGrpThisType = 0;
NumGrpThisType < Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].NumGrps;
NumGrpThisType++)
if (LstGrps->GrpCods[NumGrpSel] == Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].LstGrps[NumGrpThisType].GrpCod)
{ // The selected group is of this type
AlreadyRegisteredInGrp = false;
/* For each group of this type to which the user belongs... */
for (NumGrpHeBelongs = 0;
NumGrpHeBelongs < LstGrpsHeBelongs.NumGrps;
NumGrpHeBelongs++)
if (LstGrps->GrpCods[NumGrpSel] == LstGrpsHeBelongs.GrpCods[NumGrpHeBelongs])
AlreadyRegisteredInGrp = true;
else if (!MultipleEnrolment) // If the type of group is of single enrolment
{
/* If the enrolment is single and the group to which the user belongs is different from the selected ==>
remove user from the group to which he belongs */
Grp_RemoveUsrFromGroup (UsrDat->UsrCod,LstGrpsHeBelongs.GrpCods[NumGrpHeBelongs]);
Ale_ShowAlert (Ale_SUCCESS,Txt_THE_USER_X_has_been_removed_from_the_group_of_type_Y_to_which_it_belonged,
UsrDat->FullName,Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].GrpTypName);
}
if (!AlreadyRegisteredInGrp) // If the user does not belong to the selected group
{
Grp_AddUsrToGroup (UsrDat,LstGrps->GrpCods[NumGrpSel]);
Ale_ShowAlert (Ale_SUCCESS,Txt_THE_USER_X_has_been_enroled_in_the_group_of_type_Y_Z,
UsrDat->FullName,Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].GrpTypName,
Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].LstGrps[NumGrpThisType].GrpName);
}
break; // Once we know the type of a selected group, it's not necessary to check the rest of types
}
}
/***** Free the list of groups of this type to which the user belonged *****/
Grp_FreeListCodGrp (&LstGrpsHeBelongs);
}
}
/*****************************************************************************/
/**************** Remove user of the groups indicados in a list **************/
/*****************************************************************************/
// Returns NumGrpsHeIsRemoved
unsigned Grp_RemoveUsrFromGroups (struct UsrData *UsrDat,struct ListCodGrps *LstGrps)
{
extern const char *Txt_THE_USER_X_has_not_been_removed_from_any_group;
extern const char *Txt_THE_USER_X_has_been_removed_from_one_group;
extern const char *Txt_THE_USER_X_has_been_removed_from_Y_groups;
struct ListCodGrps LstGrpsHeBelongs;
unsigned NumGrpSel;
unsigned NumGrpHeBelongs;
unsigned NumGrpsHeIsRemoved = 0;
/***** Query in the database the group codes of any group the user belongs to *****/
Grp_GetLstCodGrpsUsrBelongs (Gbl.Hierarchy.Crs.CrsCod,-1L,
UsrDat->UsrCod,&LstGrpsHeBelongs);
/***** For each group selected by me... *****/
for (NumGrpSel = 0;
NumGrpSel < LstGrps->NumGrps;
NumGrpSel++)
/* For each group to which the user belongs... */
for (NumGrpHeBelongs = 0;
NumGrpHeBelongs < LstGrpsHeBelongs.NumGrps;
NumGrpHeBelongs++)
/* If the user belongs to a selected group from which he must be removed */
if (LstGrpsHeBelongs.GrpCods[NumGrpHeBelongs] == LstGrps->GrpCods[NumGrpSel])
{
Grp_RemoveUsrFromGroup (UsrDat->UsrCod,LstGrpsHeBelongs.GrpCods[NumGrpHeBelongs]);
NumGrpsHeIsRemoved++;
}
/***** Write message to inform about how many groups the student has been removed from *****/
if (NumGrpsHeIsRemoved == 0)
Ale_ShowAlert (Ale_SUCCESS,Txt_THE_USER_X_has_not_been_removed_from_any_group,
UsrDat->FullName);
else if (NumGrpsHeIsRemoved == 1)
Ale_ShowAlert (Ale_SUCCESS,Txt_THE_USER_X_has_been_removed_from_one_group,
UsrDat->FullName);
else // NumGrpsHeIsRemoved > 1
Ale_ShowAlert (Ale_SUCCESS,Txt_THE_USER_X_has_been_removed_from_Y_groups,
UsrDat->FullName,NumGrpsHeIsRemoved);
/***** Free the list of groups of this type to which the user belonged *****/
Grp_FreeListCodGrp (&LstGrpsHeBelongs);
return NumGrpsHeIsRemoved;
}
/*****************************************************************************/
/*************** Remove a user of all the groups of a course *****************/
/*****************************************************************************/
void Grp_RemUsrFromAllGrpsInCrs (long UsrCod,long CrsCod)
{
bool ItsMe = Usr_ItsMe (UsrCod);
/***** Remove user from all the groups of the course *****/
DB_QueryDELETE ("can not remove a user from all groups of a course",
"DELETE FROM crs_grp_usr"
" WHERE UsrCod=%ld AND GrpCod IN"
" (SELECT crs_grp.GrpCod FROM crs_grp_types,crs_grp"
" WHERE crs_grp_types.CrsCod=%ld"
" AND crs_grp_types.GrpTypCod=crs_grp.GrpTypCod)",
UsrCod,CrsCod);
/***** Flush caches *****/
Grp_FlushCacheUsrSharesAnyOfMyGrpsInCurrentCrs ();
if (ItsMe)
Grp_FlushCacheIBelongToGrp ();
}
/*****************************************************************************/
/******* Remove a user from all the groups of all the user's courses *********/
/*****************************************************************************/
void Grp_RemUsrFromAllGrps (long UsrCod)
{
bool ItsMe = Usr_ItsMe (UsrCod);
/***** Remove user from all groups *****/
DB_QueryDELETE ("can not remove a user from the groups he/she belongs to",
"DELETE FROM crs_grp_usr WHERE UsrCod=%ld",
UsrCod);
/***** Flush caches *****/
Grp_FlushCacheUsrSharesAnyOfMyGrpsInCurrentCrs ();
if (ItsMe)
Grp_FlushCacheIBelongToGrp ();
}
/*****************************************************************************/
/************************* Remove a user from a group ************************/
/*****************************************************************************/
static void Grp_RemoveUsrFromGroup (long UsrCod,long GrpCod)
{
bool ItsMe = Usr_ItsMe (UsrCod);
/***** Remove user from group *****/
DB_QueryDELETE ("can not remove a user from a group",
"DELETE FROM crs_grp_usr"
" WHERE GrpCod=%ld AND UsrCod=%ld",
GrpCod,UsrCod);
/***** Flush caches *****/
Grp_FlushCacheUsrSharesAnyOfMyGrpsInCurrentCrs ();
if (ItsMe)
Grp_FlushCacheIBelongToGrp ();
}
/*****************************************************************************/
/*********************** Register a user in a group **************************/
/*****************************************************************************/
static void Grp_AddUsrToGroup (struct UsrData *UsrDat,long GrpCod)
{
/***** Register in group *****/
DB_QueryINSERT ("can not add a user to a group",
"INSERT INTO crs_grp_usr"
" (GrpCod,UsrCod)"
" VALUES"
" (%ld,%ld)",
GrpCod,UsrDat->UsrCod);
}
/*****************************************************************************/
/******************** List current group types for edition *******************/
/*****************************************************************************/
static void Grp_ListGroupTypesForEdition (void)
{
extern const char *Txt_It_is_optional_to_choose_a_group;
extern const char *Txt_It_is_mandatory_to_choose_a_group;
extern const char *Txt_A_student_can_belong_to_several_groups;
extern const char *Txt_A_student_can_only_belong_to_one_group;
extern const char *Txt_The_groups_will_automatically_open;
extern const char *Txt_The_groups_will_not_automatically_open;
unsigned NumGrpTyp;
unsigned UniqueId;
char Id[32];
/***** Write heading *****/
HTM_TABLE_BeginWidePadding (2);
Grp_WriteHeadingGroupTypes ();
/***** List group types with forms for edition *****/
for (NumGrpTyp = 0, UniqueId=1;
NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num;
NumGrpTyp++, UniqueId++)
{
HTM_TR_Begin (NULL);
/* Put icon to remove group type */
HTM_TD_Begin ("class=\"BM\"");
Frm_StartFormAnchor (ActReqRemGrpTyp,Grp_GROUP_TYPES_SECTION_ID);
Grp_PutParamGrpTypCod (Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].GrpTypCod);
Ico_PutIconRemove ();
Frm_EndForm ();
HTM_TD_End ();
/* Name of group type */
HTM_TD_Begin ("class=\"LM\"");
Frm_StartFormAnchor (ActRenGrpTyp,Grp_GROUP_TYPES_SECTION_ID);
Grp_PutParamGrpTypCod (Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].GrpTypCod);
HTM_INPUT_TEXT ("GrpTypName",Grp_MAX_CHARS_GROUP_TYPE_NAME,
Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].GrpTypName,true,
"size=\"12\"");
Frm_EndForm ();
HTM_TD_End ();
/* Is it mandatory to register in any group? */
HTM_TD_Begin ("class=\"CM\"");
Frm_StartFormAnchor (ActChgMdtGrpTyp,Grp_GROUP_TYPES_SECTION_ID);
Grp_PutParamGrpTypCod (Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].GrpTypCod);
fprintf (Gbl.F.Out,"