// swad_department.c: departments
/*
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 ***********************************/
/*****************************************************************************/
#include // For NULL
#include // For boolean type
#include // For calloc
#include // For string functions
#include "swad_box.h"
#include "swad_constant.h"
#include "swad_database.h"
#include "swad_department.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_HTML.h"
#include "swad_institution.h"
#include "swad_language.h"
#include "swad_parameter.h"
#include "swad_string.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Private variables *****************************/
/*****************************************************************************/
static struct Department *Dpt_EditingDpt = NULL; // Static variable to keep the department being edited
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
static void Dpt_GetParamDptOrder (void);
static void Dpt_PutIconToEditDpts (void);
static void Dpt_EditDepartmentsInternal (void);
static void Dpt_ListDepartmentsForEdition (void);
static void Dpt_PutParamDptCod (long DptCod);
static void Dpt_RenameDepartment (Cns_ShrtOrFullName_t ShrtOrFullName);
static bool Dpt_CheckIfDepartmentNameExists (const char *FieldName,const char *Name,long DptCod);
static void Dpt_UpdateDegNameDB (long DptCod,const char *FieldName,const char *NewDptName);
static void Dpt_PutFormToCreateDepartment (void);
static void Dpt_PutHeadDepartments (void);
static void Dpt_CreateDepartment (struct Department *Dpt);
static void Dpt_EditingDepartmentConstructor (void);
static void Dpt_EditingDepartmentDestructor (void);
/*****************************************************************************/
/************************* List all the departments **************************/
/*****************************************************************************/
void Dpt_SeeDepts (void)
{
extern const char *Hlp_INSTITUTION_Departments;
extern const char *Txt_Departments_of_INSTITUTION_X;
extern const char *Txt_DEPARTMENTS_HELP_ORDER[2];
extern const char *Txt_DEPARTMENTS_ORDER[2];
extern const char *Txt_Other_departments;
extern const char *Txt_Department_unspecified;
Dpt_Order_t Order;
unsigned NumDpt;
unsigned NumTchsInsWithDpt = 0; // Number of teachers from the current institution with department
unsigned NumTchsInOtherDpts;
/***** Trivial check *****/
if (Gbl.Hierarchy.Ins.InsCod <= 0) // No institution selected
return;
/***** Get parameter with the type of order in the list of departments *****/
Dpt_GetParamDptOrder ();
/***** Get list of departments *****/
Dpt_GetListDepartments (Gbl.Hierarchy.Ins.InsCod);
/***** Begin box and table *****/
snprintf (Gbl.Title,sizeof (Gbl.Title),
Txt_Departments_of_INSTITUTION_X,
Gbl.Hierarchy.Ins.FullName);
Box_StartBoxTable (NULL,Gbl.Title,
Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM ? Dpt_PutIconToEditDpts :
NULL,
Hlp_INSTITUTION_Departments,Box_NOT_CLOSABLE,2);
/***** Write heading *****/
HTM_TR_Begin (NULL);
for (Order = Dpt_ORDER_BY_DEPARTMENT;
Order <= Dpt_ORDER_BY_NUM_TCHS;
Order++)
{
HTM_TH_Begin (1,1,"LM");
Frm_StartForm (ActSeeDpt);
Par_PutHiddenParamUnsigned (NULL,"Order",(unsigned) Order);
Frm_LinkFormSubmit (Txt_DEPARTMENTS_HELP_ORDER[Order],"TIT_TBL",NULL);
if (Order == Gbl.Dpts.SelectedOrder)
fprintf (Gbl.F.Out,"");
fprintf (Gbl.F.Out,"%s",Txt_DEPARTMENTS_ORDER[Order]);
if (Order == Gbl.Dpts.SelectedOrder)
fprintf (Gbl.F.Out,"");
Frm_LinkFormEnd ();
Frm_EndForm ();
HTM_TH_End ();
}
HTM_TR_End ();
/***** Write all the departments and their nuber of teachers *****/
for (NumDpt = 0;
NumDpt < Gbl.Dpts.Num;
NumDpt++)
{
/* Write data of this department */
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"LM\"");
HTM_A_Begin ("href=\"%s\" target=\"_blank\" class=\"DAT\"",
Gbl.Dpts.Lst[NumDpt].WWW);
fprintf (Gbl.F.Out,"%s",Gbl.Dpts.Lst[NumDpt].FullName);
HTM_A_End ();
HTM_TD_End ();
HTM_TD_Begin ("class=\"DAT RM\"");
fprintf (Gbl.F.Out,"%u",Gbl.Dpts.Lst[NumDpt].NumTchs);
HTM_TD_End ();
HTM_TR_End ();
/* Update number of teachers from the current institution
with department */
NumTchsInsWithDpt += Gbl.Dpts.Lst[NumDpt].NumTchs;
}
/***** Separation row *****/
HTM_TR_Begin (NULL);
HTM_TD_Begin ("colspan=\"3\" class=\"DAT\"");
fprintf (Gbl.F.Out," ");
HTM_TD_End ();
HTM_TR_End ();
/***** Write teachers with other department *****/
NumTchsInOtherDpts = Usr_GetNumTchsCurrentInsInDepartment (0);
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"DAT LM\"");
fprintf (Gbl.F.Out,"%s",Txt_Other_departments);
HTM_TD_End ();
HTM_TD_Begin ("class=\"DAT RM\"");
fprintf (Gbl.F.Out,"%u",NumTchsInOtherDpts);
HTM_TD_End ();
HTM_TR_End ();
NumTchsInsWithDpt += NumTchsInOtherDpts;
/***** Write teachers with no department *****/
HTM_TR_Begin (NULL);
HTM_TD_Begin ("class=\"DAT LM\"");
fprintf (Gbl.F.Out,"%s",Txt_Department_unspecified);
HTM_TD_End ();
HTM_TD_Begin ("class=\"DAT RM\"");
fprintf (Gbl.F.Out,"%u",
Usr_GetTotalNumberOfUsersInCourses (Hie_INS,
1 << Rol_NET |
1 << Rol_TCH) -
NumTchsInsWithDpt);
HTM_TD_End ();
HTM_TR_End ();
/***** End table and box *****/
Box_EndBoxTable ();
/***** Free list of departments *****/
Dpt_FreeListDepartments ();
}
/*****************************************************************************/
/******** Get parameter with the type or order in list of departments ********/
/*****************************************************************************/
static void Dpt_GetParamDptOrder (void)
{
Gbl.Dpts.SelectedOrder = (Dpt_Order_t)
Par_GetParToUnsignedLong ("Order",
0,
Dpt_NUM_ORDERS - 1,
(unsigned long) Dpt_ORDER_DEFAULT);
}
/*****************************************************************************/
/************************ Put icon to edit departments ***********************/
/*****************************************************************************/
static void Dpt_PutIconToEditDpts (void)
{
Ico_PutContextualIconToEdit (ActEdiDpt,NULL);
}
/*****************************************************************************/
/******* Put forms to edit the departments of the current institution ********/
/*****************************************************************************/
void Dpt_EditDepartments (void)
{
/***** Department constructor *****/
Dpt_EditingDepartmentConstructor ();
/***** Edit departments *****/
Dpt_EditDepartmentsInternal ();
/***** Department destructor *****/
Dpt_EditingDepartmentDestructor ();
}
static void Dpt_EditDepartmentsInternal (void)
{
extern const char *Hlp_INSTITUTION_Departments_edit;
extern const char *Txt_Departments_of_INSTITUTION_X;
/***** Trivial check *****/
if (Gbl.Hierarchy.Ins.InsCod <= 0) // An institution must be selected
return;
/***** Get list of institutions *****/
Ins_GetListInstitutions (Gbl.Hierarchy.Cty.CtyCod,Ins_GET_BASIC_DATA);
/***** Get list of departments *****/
Dpt_GetListDepartments (Gbl.Hierarchy.Ins.InsCod);
/***** Begin box *****/
snprintf (Gbl.Title,sizeof (Gbl.Title),
Txt_Departments_of_INSTITUTION_X,
Gbl.Hierarchy.Ins.FullName);
Box_BoxBegin (NULL,Gbl.Title,NULL,
Hlp_INSTITUTION_Departments_edit,Box_NOT_CLOSABLE);
/***** Put a form to create a new department *****/
Dpt_PutFormToCreateDepartment ();
/***** Forms to edit current departments *****/
if (Gbl.Dpts.Num)
Dpt_ListDepartmentsForEdition ();
/***** End box *****/
Box_BoxEnd ();
/***** Free list of departments *****/
Dpt_FreeListDepartments ();
/***** Free list of institutions *****/
Ins_FreeListInstitutions ();
}
/*****************************************************************************/
/************************** Get list of departments **************************/
/*****************************************************************************/
// If InsCod > 0 ==> get departments of an institution
// If InsCod <= 0 ==> an empty list is returned
void Dpt_GetListDepartments (long InsCod)
{
static const char *OrderBySubQuery[Dpt_NUM_ORDERS] =
{
"FullName", // Dpt_ORDER_BY_DEPARTMENT
"NumTchs DESC,FullName", // Dpt_ORDER_BY_NUM_TCHS
};
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumDpt;
struct Department *Dpt;
/***** Free list of departments *****/
Dpt_FreeListDepartments (); // List is initialized to empty
if (InsCod > 0) // Institution specified
{
/***** Get departments from database *****/
Gbl.Dpts.Num = (unsigned) DB_QuerySELECT (&mysql_res,"can not get departments",
"(SELECT departments.DptCod,departments.InsCod,"
"departments.ShortName,departments.FullName,departments.WWW,"
"COUNT(DISTINCT usr_data.UsrCod) AS NumTchs"
" FROM departments,usr_data,crs_usr"
" WHERE departments.InsCod=%ld"
" AND departments.DptCod=usr_data.DptCod"
" AND usr_data.UsrCod=crs_usr.UsrCod"
" AND crs_usr.Role IN (%u,%u)"
" GROUP BY departments.DptCod)"
" UNION "
"(SELECT DptCod,InsCod,ShortName,FullName,WWW,0 AS NumTchs"
" FROM departments"
" WHERE InsCod=%ld AND DptCod NOT IN"
" (SELECT DISTINCT usr_data.DptCod FROM usr_data,crs_usr"
" WHERE crs_usr.Role IN (%u,%u) AND crs_usr.UsrCod=usr_data.UsrCod))"
" ORDER BY %s",
InsCod,(unsigned) Rol_NET,(unsigned) Rol_TCH,
InsCod,(unsigned) Rol_NET,(unsigned) Rol_TCH,
OrderBySubQuery[Gbl.Dpts.SelectedOrder]);
if (Gbl.Dpts.Num) // Departments found...
{
/***** Create list with courses in degree *****/
if ((Gbl.Dpts.Lst = (struct Department *) calloc ((size_t) Gbl.Dpts.Num,
sizeof (struct Department))) == NULL)
Lay_NotEnoughMemoryExit ();
/***** Get the departments *****/
for (NumDpt = 0;
NumDpt < Gbl.Dpts.Num;
NumDpt++)
{
Dpt = &(Gbl.Dpts.Lst[NumDpt]);
/* Get next department */
row = mysql_fetch_row (mysql_res);
/* Get department code (row[0]) */
if ((Dpt->DptCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of department.");
/* Get institution code (row[1]) */
if ((Dpt->InsCod = Str_ConvertStrCodToLongCod (row[1])) < 0)
Lay_ShowErrorAndExit ("Wrong code of institution.");
/* Get the short name of the department (row[2]) */
Str_Copy (Dpt->ShrtName,row[2],
Hie_MAX_BYTES_SHRT_NAME);
/* Get the full name of the department (row[3]) */
Str_Copy (Dpt->FullName,row[3],
Hie_MAX_BYTES_FULL_NAME);
/* Get the URL of the department (row[4]) */
Str_Copy (Dpt->WWW,row[4],
Cns_MAX_BYTES_WWW);
/* Get number of non-editing teachers and teachers in this department (row[5]) */
if (sscanf (row[5],"%u",&Dpt->NumTchs) != 1)
Dpt->NumTchs = 0;
}
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
}
/*****************************************************************************/
/************************** Get department full name *************************/
/*****************************************************************************/
void Dpt_GetDataOfDepartmentByCod (struct Department *Dpt)
{
extern const char *Txt_Another_department;
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumRows;
/***** Clear data *****/
Dpt->InsCod = -1L;
Dpt->ShrtName[0] = Dpt->FullName[0] = Dpt->WWW[0] = '\0';
Dpt->NumTchs = 0;
/***** Check if department code is correct *****/
if (Dpt->DptCod == 0)
{
Str_Copy (Dpt->ShrtName,Txt_Another_department,
Hie_MAX_BYTES_SHRT_NAME);
Str_Copy (Dpt->FullName,Txt_Another_department,
Hie_MAX_BYTES_FULL_NAME);
}
else if (Dpt->DptCod > 0)
{
/***** Get data of a department from database *****/
NumRows = DB_QuerySELECT (&mysql_res,"can not get data of a department",
"(SELECT departments.InsCod,departments.ShortName,departments.FullName,departments.WWW,"
"COUNT(DISTINCT usr_data.UsrCod) AS NumTchs"
" FROM departments,usr_data,crs_usr"
" WHERE departments.DptCod=%ld"
" AND departments.DptCod=usr_data.DptCod"
" AND usr_data.UsrCod=crs_usr.UsrCod"
" AND crs_usr.Role=%u"
" GROUP BY departments.DptCod)"
" UNION "
"(SELECT InsCod,ShortName,FullName,WWW,0"
" FROM departments"
" WHERE DptCod=%ld AND DptCod NOT IN"
" (SELECT DISTINCT usr_data.DptCod FROM usr_data,crs_usr"
" WHERE crs_usr.Role=%u AND crs_usr.UsrCod=usr_data.UsrCod))",
Dpt->DptCod,(unsigned) Rol_TCH,
Dpt->DptCod,(unsigned) Rol_TCH);
if (NumRows) // Department found...
{
/* Get row */
row = mysql_fetch_row (mysql_res);
/* Get the code of the institution (row[0]) */
Dpt->InsCod = Str_ConvertStrCodToLongCod (row[0]);
/* Get the short name of the department (row[1]) */
Str_Copy (Dpt->ShrtName,row[1],
Hie_MAX_BYTES_SHRT_NAME);
/* Get the full name of the department (row[2]) */
Str_Copy (Dpt->FullName,row[2],
Hie_MAX_BYTES_FULL_NAME);
/* Get the URL of the department (row[3]) */
Str_Copy (Dpt->WWW,row[3],
Cns_MAX_BYTES_WWW);
/* Get number of teachers in this department (row[4]) */
if (sscanf (row[4],"%u",&Dpt->NumTchs) != 1)
Dpt->NumTchs = 0;
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
}
/*****************************************************************************/
/************************** Free list of departments *************************/
/*****************************************************************************/
void Dpt_FreeListDepartments (void)
{
if (Gbl.Dpts.Lst)
/***** Free memory used by the list of departments *****/
free ((void *) Gbl.Dpts.Lst);
Gbl.Dpts.Lst = NULL;
Gbl.Dpts.Num = 0;
}
/*****************************************************************************/
/************** Get number of departments in an institution ******************/
/*****************************************************************************/
unsigned Dpt_GetNumDepartmentsInInstitution (long InsCod)
{
/***** Get number of departments in an institution from database *****/
return
(unsigned) DB_QueryCOUNT ("can not get number of departments"
" in an institution",
"SELECT COUNT(*) FROM departments"
" WHERE InsCod=%ld",
InsCod);
}
/*****************************************************************************/
/************************** List all the departments *************************/
/*****************************************************************************/
static void Dpt_ListDepartmentsForEdition (void)
{
extern const char *Txt_Another_institution;
unsigned NumDpt;
struct Department *Dpt;
struct Instit Ins;
unsigned NumIns;
/***** Begin table *****/
HTM_TABLE_BeginPadding (2);
/***** Write heading *****/
Dpt_PutHeadDepartments ();
/***** Write all the departments *****/
for (NumDpt = 0;
NumDpt < Gbl.Dpts.Num;
NumDpt++)
{
Dpt = &Gbl.Dpts.Lst[NumDpt];
/* Get data of institution of this department */
Ins.InsCod = Dpt->InsCod;
Ins_GetDataOfInstitutionByCod (&Ins,Ins_GET_BASIC_DATA);
HTM_TR_Begin (NULL);
/* Put icon to remove department */
HTM_TD_Begin ("class=\"BM\"");
if (Dpt->NumTchs) // Department has teachers ==> deletion forbidden
Ico_PutIconRemovalNotAllowed ();
else
{
Frm_StartForm (ActRemDpt);
Dpt_PutParamDptCod (Dpt->DptCod);
Ico_PutIconRemove ();
Frm_EndForm ();
}
HTM_TD_End ();
/* Department code */
HTM_TD_Begin ("class=\"DAT RM\"");
fprintf (Gbl.F.Out,"%ld ",Dpt->DptCod);
HTM_TD_End ();
/* Institution */
HTM_TD_Begin ("class=\"CM\"");
Frm_StartForm (ActChgDptIns);
Dpt_PutParamDptCod (Dpt->DptCod);
fprintf (Gbl.F.Out,"