// swad_course.c: edition of courses /* SWAD (Shared Workspace At a Distance), is a web platform developed at the University of Granada (Spain), and used to support university teaching. This file is part of SWAD core. Copyright (C) 1999-2019 Antonio Caņas Vargas This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /*****************************************************************************/ /********************************* Headers ***********************************/ /*****************************************************************************/ #define _GNU_SOURCE // For asprintf #include // For PATH_MAX #include // For NULL #include // For maximum values #include // For asprintf #include // For getenv, etc. #include // For string functions #include "swad_box.h" #include "swad_course.h" #include "swad_constant.h" #include "swad_database.h" #include "swad_degree.h" #include "swad_enrolment.h" #include "swad_exam.h" #include "swad_form.h" #include "swad_global.h" #include "swad_help.h" #include "swad_hierarchy.h" #include "swad_HTML.h" #include "swad_indicator.h" #include "swad_logo.h" #include "swad_notification.h" #include "swad_parameter.h" #include "swad_QR.h" #include "swad_role.h" #include "swad_RSS.h" #include "swad_tab.h" #include "swad_theme.h" /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /*************************** Public constants ********************************/ /*****************************************************************************/ /*****************************************************************************/ /***************************** Private types *********************************/ /*****************************************************************************/ /*****************************************************************************/ /**************************** Private variables ******************************/ /*****************************************************************************/ static struct Course *Crs_EditingCrs = NULL; // Static variable to keep the course being edited /*****************************************************************************/ /**************************** Private prototypes *****************************/ /*****************************************************************************/ static void Crs_Configuration (bool PrintView); static void Crs_PutIconToPrint (void); static void Crs_ShowNumUsrsInCrs (Rol_Role_t Role); static void Crs_WriteListMyCoursesToSelectOne (void); static void Crs_GetListCoursesInCurrentDegree (Crs_WhatCourses_t WhatCourses); static void Crs_ListCourses (void); static bool Crs_CheckIfICanCreateCourses (void); static void Crs_PutIconsListCourses (void); static void Crs_PutIconToEditCourses (void); static bool Crs_ListCoursesOfAYearForSeeing (unsigned Year); static void Crs_EditCoursesInternal (void); static void Crs_PutIconsEditingCourses (void); static void Crs_PutIconToViewCourses (void); static void Crs_ListCoursesForEdition (void); static void Crs_ListCoursesOfAYearForEdition (unsigned Year); static bool Crs_CheckIfICanEdit (struct Course *Crs); static Crs_StatusTxt_t Crs_GetStatusTxtFromStatusBits (Crs_Status_t Status); static Crs_Status_t Crs_GetStatusBitsFromStatusTxt (Crs_StatusTxt_t StatusTxt); static void Crs_PutFormToCreateCourse (void); static void Crs_PutHeadCoursesForSeeing (void); static void Crs_PutHeadCoursesForEdition (void); static void Crs_RecFormRequestOrCreateCrs (unsigned Status); static void Crs_GetParamsNewCourse (struct Course *Crs); static void Crs_CreateCourse (unsigned Status); static void Crs_GetDataOfCourseFromRow (struct Course *Crs,MYSQL_ROW row); static void Crs_UpdateCrsDegDB (long CrsCod,long DegCod); static void Crs_UpdateCrsYear (struct Course *Crs,unsigned NewYear); static void Crs_GetShortNamesByCod (long CrsCod, char CrsShortName[Hie_MAX_BYTES_SHRT_NAME + 1], char DegShortName[Hie_MAX_BYTES_SHRT_NAME + 1]); static void Crs_EmptyCourseCompletely (long CrsCod); static void Crs_RenameCourse (struct Course *Crs,Cns_ShrtOrFullName_t ShrtOrFullName); static bool Crs_CheckIfCrsNameExistsInYearOfDeg (const char *FieldName,const char *Name,long CrsCod, long DegCod,unsigned Year); static void Crs_UpdateCrsNameDB (long CrsCod,const char *FieldName,const char *NewCrsName); static void Crs_PutButtonToGoToCrs (void); static void Crs_PutButtonToRegisterInCrs (void); static void Crs_PutIconToSearchCourses (void); static void Sch_PutLinkToSearchCoursesParams (void); static void Crs_PutParamOtherCrsCod (long CrsCod); static long Crs_GetAndCheckParamOtherCrsCod (long MinCodAllowed); static void Crs_WriteRowCrsData (unsigned NumCrs,MYSQL_ROW row,bool WriteColumnAccepted); static void Crs_EditingCourseConstructor (void); static void Crs_EditingCourseDestructor (void); /*****************************************************************************/ /***************** Show introduction to the current course *******************/ /*****************************************************************************/ void Crs_ShowIntroduction (void) { /***** Course configuration *****/ HTM_DIV_Begin ("class=\"CM\""); Crs_Configuration (false); HTM_DIV_End (); /***** Course introduction *****/ Inf_ShowInfo (); /***** Show help to enrol me *****/ Hlp_ShowHelpWhatWouldYouLikeToDo (); } /*****************************************************************************/ /***************** Print configuration of the current course *****************/ /*****************************************************************************/ void Crs_PrintConfiguration (void) { Crs_Configuration (true); } /*****************************************************************************/ /***************** Configuration of the current course ***********************/ /*****************************************************************************/ static void Crs_Configuration (bool PrintView) { extern const char *Hlp_COURSE_Information; extern const char *The_ClassFormInBox[The_NUM_THEMES]; extern const char *Txt_Degree; extern const char *Txt_Course; extern const char *Txt_Short_name; extern const char *Txt_Year_OF_A_DEGREE; extern const char *Txt_YEAR_OF_DEGREE[1 + Deg_MAX_YEARS_PER_DEGREE]; extern const char *Txt_Not_applicable; extern const char *Txt_Institutional_code; extern const char *Txt_Internal_code; extern const char *Txt_Shortcut; extern const char *Lan_STR_LANG_ID[1 + Lan_NUM_LANGUAGES]; extern const char *Txt_QR_code; extern const char *Txt_Indicators; extern const char *Txt_of_PART_OF_A_TOTAL; unsigned NumDeg; unsigned Year; int NumIndicatorsFromDB; struct Ind_IndicatorsCrs Indicators; bool IsForm; bool PutLink; /***** Trivial check *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) // No course selected return; /***** Contextual menu *****/ if (!PrintView) if (Gbl.Usrs.Me.Role.Logged == Rol_GST || Gbl.Usrs.Me.Role.Logged == Rol_USR) { Mnu_ContextMenuBegin (); Enr_PutLinkToRequestSignUp (); // Request enrolment in the current course Mnu_ContextMenuEnd (); } /***** Begin box *****/ if (PrintView) Box_BoxBegin (NULL,NULL,NULL, NULL,Box_NOT_CLOSABLE); else Box_BoxBegin (NULL,NULL,Crs_PutIconToPrint, Hlp_COURSE_Information,Box_NOT_CLOSABLE); /***** Title *****/ PutLink = !PrintView && Gbl.Hierarchy.Deg.WWW[0]; HTM_DIV_Begin ("class=\"FRAME_TITLE FRAME_TITLE_BIG\""); if (PutLink) HTM_A_Begin ("href=\"%s\" target=\"_blank\"" " class=\"FRAME_TITLE_BIG\" title=\"%s\"", Gbl.Hierarchy.Deg.WWW, Gbl.Hierarchy.Deg.FullName); Log_DrawLogo (Hie_DEG,Gbl.Hierarchy.Deg.DegCod, Gbl.Hierarchy.Deg.ShrtName,64,NULL,true); if (PutLink) HTM_A_End (); fprintf (Gbl.F.Out,"
%s",Gbl.Hierarchy.Crs.FullName); HTM_DIV_End (); /***** Begin table *****/ HTM_TABLE_BeginWidePadding (2); /***** Degree *****/ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"RM\""); HTM_LABEL_Begin ("for=\"OthDegCod\" class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]); fprintf (Gbl.F.Out,"%s:",Txt_Degree); HTM_LABEL_End (); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LM\""); if (!PrintView && Gbl.Usrs.Me.Role.Logged >= Rol_CTR_ADM) // Only centre admins, institution admins and system admin can move a course to another degree { /* Get list of degrees of the current centre */ Deg_GetListDegsOfCurrentCtr (); /* Put form to select degree */ Frm_StartForm (ActChgCrsDegCfg); fprintf (Gbl.F.Out,"", Gbl.Form.Id); for (Year = 0; Year <= Deg_MAX_YEARS_PER_DEGREE; Year++) fprintf (Gbl.F.Out,"", Year, Year == Gbl.Hierarchy.Crs.Year ? " selected=\"selected\"" : "", Txt_YEAR_OF_DEGREE[Year]); HTM_SELECT_End (); Frm_EndForm (); } else fprintf (Gbl.F.Out,"%s", Gbl.Hierarchy.Crs.Year ? Txt_YEAR_OF_DEGREE[Gbl.Hierarchy.Crs.Year] : Txt_Not_applicable); HTM_TD_End (); HTM_TR_End (); if (!PrintView) { /***** Institutional code of the course *****/ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"RM\""); HTM_LABEL_Begin ("for=\"InsCrsCod\" class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]); fprintf (Gbl.F.Out,"%s:",Txt_Institutional_code); HTM_LABEL_End (); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LM\""); if (IsForm) { Frm_StartForm (ActChgInsCrsCodCfg); HTM_INPUT_TEXT ("InsCrsCod",Crs_MAX_CHARS_INSTITUTIONAL_CRS_COD, Gbl.Hierarchy.Crs.InstitutionalCrsCod,true, "size=\"%u\"", Crs_MAX_CHARS_INSTITUTIONAL_CRS_COD); Frm_EndForm (); } else fprintf (Gbl.F.Out,"%s",Gbl.Hierarchy.Crs.InstitutionalCrsCod); HTM_TD_End (); HTM_TR_End (); /***** Internal code of the course *****/ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"%s RM\"",The_ClassFormInBox[Gbl.Prefs.Theme]); fprintf (Gbl.F.Out,"%s:",Txt_Internal_code); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LM\""); fprintf (Gbl.F.Out,"%ld",Gbl.Hierarchy.Crs.CrsCod); HTM_TD_End (); HTM_TR_End (); } /***** Link to the course *****/ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"%s RM\"",The_ClassFormInBox[Gbl.Prefs.Theme]); fprintf (Gbl.F.Out,"%s:",Txt_Shortcut); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LM\""); HTM_A_Begin ("href=\"%s/%s?crs=%ld\" class=\"DAT\" target=\"_blank\"", Cfg_URL_SWAD_CGI, Lan_STR_LANG_ID[Gbl.Prefs.Language], Gbl.Hierarchy.Crs.CrsCod); fprintf (Gbl.F.Out,"%s/%s?crs=%ld", Cfg_URL_SWAD_CGI, Lan_STR_LANG_ID[Gbl.Prefs.Language], Gbl.Hierarchy.Crs.CrsCod); HTM_A_End (); HTM_TD_End (); HTM_TR_End (); if (PrintView) { /***** QR code with link to the course *****/ HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"%s RM\"",The_ClassFormInBox[Gbl.Prefs.Theme]); fprintf (Gbl.F.Out,"%s:",Txt_QR_code); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LM\""); QR_LinkTo (250,"crs",Gbl.Hierarchy.Crs.CrsCod); HTM_TD_End (); HTM_TR_End (); } else { /***** Number of users *****/ Crs_ShowNumUsrsInCrs (Rol_TCH); Crs_ShowNumUsrsInCrs (Rol_NET); Crs_ShowNumUsrsInCrs (Rol_STD); /***** Indicators *****/ NumIndicatorsFromDB = Ind_GetNumIndicatorsCrsFromDB (Gbl.Hierarchy.Crs.CrsCod); Ind_ComputeAndStoreIndicatorsCrs (Gbl.Hierarchy.Crs.CrsCod, NumIndicatorsFromDB,&Indicators); HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"%s RM\"",The_ClassFormInBox[Gbl.Prefs.Theme]); fprintf (Gbl.F.Out,"%s:",Txt_Indicators); HTM_TD_End (); HTM_TD_Begin ("class=\"LM\""); Frm_StartForm (ActReqStaCrs); snprintf (Gbl.Title,sizeof (Gbl.Title), "%u %s %u", Indicators.NumIndicators, Txt_of_PART_OF_A_TOTAL,Ind_NUM_INDICATORS); Frm_LinkFormSubmit (Gbl.Title,"DAT",NULL); fprintf (Gbl.F.Out,"%s ",Gbl.Title); Ico_PutIcon ((Indicators.NumIndicators == Ind_NUM_INDICATORS) ? "check-circle.svg" : "exclamation-triangle.svg", Gbl.Title,"ICO16x16"); Frm_EndForm (); HTM_TD_End (); HTM_TR_End (); } /***** End table *****/ HTM_TABLE_End (); /***** End box *****/ Box_BoxEnd (); } /*****************************************************************************/ /************* Put icon to print the configuration of a course ***************/ /*****************************************************************************/ static void Crs_PutIconToPrint (void) { Ico_PutContextualIconToPrint (ActPrnCrsInf,NULL); } /*****************************************************************************/ /**************** Number of users in courses of this country *****************/ /*****************************************************************************/ static void Crs_ShowNumUsrsInCrs (Rol_Role_t Role) { extern const char *The_ClassFormInBox[The_NUM_THEMES]; extern const char *Txt_ROLES_PLURAL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS]; HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"%s RM\"",The_ClassFormInBox[Gbl.Prefs.Theme]); fprintf (Gbl.F.Out,"%s:",Txt_ROLES_PLURAL_Abc[Role][Usr_SEX_UNKNOWN]); HTM_TD_End (); HTM_TD_Begin ("class=\"DAT LM\""); fprintf (Gbl.F.Out,"%u",Gbl.Hierarchy.Crs.NumUsrs[Role]); HTM_TD_End (); HTM_TR_End (); } /*****************************************************************************/ /************************ Write menu with my courses *************************/ /*****************************************************************************/ #define Crs_MAX_BYTES_TXT_LINK 40 static void Crs_WriteListMyCoursesToSelectOne (void) { extern const char *Hlp_PROFILE_Courses; extern const char *The_ClassFormInBox[The_NUM_THEMES]; extern const char *The_ClassFormInBoxBold[The_NUM_THEMES]; extern const char *Txt_My_courses; extern const char *Txt_System; extern const char *Txt_Go_to_X; struct Country Cty; struct Instit Ins; struct Centre Ctr; struct Degree Deg; struct Course Crs; bool IsLastItemInLevel[1 + 5]; bool Highlight; // Highlight because degree, course, etc. is selected MYSQL_RES *mysql_resCty; MYSQL_RES *mysql_resIns; MYSQL_RES *mysql_resCtr; MYSQL_RES *mysql_resDeg; MYSQL_RES *mysql_resCrs; MYSQL_ROW row; unsigned NumCty; unsigned NumCtys; unsigned NumIns; unsigned NumInss; unsigned NumCtr; unsigned NumCtrs; unsigned NumDeg; unsigned NumDegs; unsigned NumCrs; unsigned NumCrss; char ActTxt[Act_MAX_BYTES_ACTION_TXT + 1]; const char *ClassNormal; char ClassHighlight[64]; ClassNormal = The_ClassFormInBox[Gbl.Prefs.Theme]; snprintf (ClassHighlight,sizeof (ClassHighlight), "%s LIGHT_BLUE", The_ClassFormInBoxBold[Gbl.Prefs.Theme]); /***** Begin box *****/ Box_BoxBegin (NULL,Txt_My_courses,Crs_PutIconToSearchCourses, Hlp_PROFILE_Courses,Box_NOT_CLOSABLE); HTM_UL_Begin ("class=\"LIST_TREE\""); /***** Write link to platform *****/ Highlight = (Gbl.Hierarchy.Cty.CtyCod <= 0); HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight : ClassNormal); Frm_StartForm (ActMyCrs); Cty_PutParamCtyCod (-1L); Frm_LinkFormSubmit (Txt_System, Highlight ? ClassHighlight : ClassNormal, NULL); Ico_PutIcon ("sitemap.svg",Txt_System,"ICO16x16"); fprintf (Gbl.F.Out," %s",Txt_System); Frm_LinkFormEnd (); Frm_EndForm (); HTM_LI_End (); /***** Get my countries *****/ NumCtys = Usr_GetCtysFromUsr (Gbl.Usrs.Me.UsrDat.UsrCod,&mysql_resCty); for (NumCty = 0; NumCty < NumCtys; NumCty++) { /***** Get next institution *****/ row = mysql_fetch_row (mysql_resCty); /***** Get data of this institution *****/ Cty.CtyCod = Str_ConvertStrCodToLongCod (row[0]); if (!Cty_GetDataOfCountryByCod (&Cty,Cty_GET_BASIC_DATA)) Lay_ShowErrorAndExit ("Country not found."); /***** Write link to country *****/ Highlight = (Gbl.Hierarchy.Ins.InsCod <= 0 && Gbl.Hierarchy.Cty.CtyCod == Cty.CtyCod); HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight : ClassNormal); IsLastItemInLevel[1] = (NumCty == NumCtys - 1); Lay_IndentDependingOnLevel (1,IsLastItemInLevel); Frm_StartForm (ActMyCrs); Cty_PutParamCtyCod (Cty.CtyCod); Frm_LinkFormSubmit (Act_GetActionTextFromDB (Act_GetActCod (ActSeeCtyInf),ActTxt), Highlight ? ClassHighlight : ClassNormal,NULL); Cty_DrawCountryMap (&Cty,"ICO16x16"); fprintf (Gbl.F.Out," %s",Cty.Name[Gbl.Prefs.Language]); Frm_LinkFormEnd (); Frm_EndForm (); HTM_LI_End (); /***** Get my institutions in this country *****/ NumInss = (unsigned) Usr_GetInssFromUsr (Gbl.Usrs.Me.UsrDat.UsrCod, Cty.CtyCod,&mysql_resIns); for (NumIns = 0; NumIns < NumInss; NumIns++) { /***** Get next institution *****/ row = mysql_fetch_row (mysql_resIns); /***** Get data of this institution *****/ Ins.InsCod = Str_ConvertStrCodToLongCod (row[0]); if (!Ins_GetDataOfInstitutionByCod (&Ins,Ins_GET_BASIC_DATA)) Lay_ShowErrorAndExit ("Institution not found."); /***** Write link to institution *****/ Highlight = (Gbl.Hierarchy.Ctr.CtrCod <= 0 && Gbl.Hierarchy.Ins.InsCod == Ins.InsCod); HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight : ClassNormal); IsLastItemInLevel[2] = (NumIns == NumInss - 1); Lay_IndentDependingOnLevel (2,IsLastItemInLevel); Frm_StartForm (ActMyCrs); Ins_PutParamInsCod (Ins.InsCod); Frm_LinkFormSubmit (Act_GetActionTextFromDB (Act_GetActCod (ActSeeInsInf),ActTxt), Highlight ? ClassHighlight : ClassNormal,NULL); Log_DrawLogo (Hie_INS,Ins.InsCod,Ins.ShrtName,16,NULL,true); fprintf (Gbl.F.Out," %s",Ins.FullName); Frm_LinkFormEnd (); Frm_EndForm (); HTM_LI_End (); /***** Get my centres in this institution *****/ NumCtrs = (unsigned) Usr_GetCtrsFromUsr (Gbl.Usrs.Me.UsrDat.UsrCod, Ins.InsCod,&mysql_resCtr); for (NumCtr = 0; NumCtr < NumCtrs; NumCtr++) { /***** Get next centre *****/ row = mysql_fetch_row (mysql_resCtr); /***** Get data of this centre *****/ Ctr.CtrCod = Str_ConvertStrCodToLongCod (row[0]); if (!Ctr_GetDataOfCentreByCod (&Ctr)) Lay_ShowErrorAndExit ("Centre not found."); /***** Write link to centre *****/ Highlight = (Gbl.Hierarchy.Level == Hie_CTR && Gbl.Hierarchy.Ctr.CtrCod == Ctr.CtrCod); HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight : ClassNormal); IsLastItemInLevel[3] = (NumCtr == NumCtrs - 1); Lay_IndentDependingOnLevel (3,IsLastItemInLevel); Frm_StartForm (ActMyCrs); Ctr_PutParamCtrCod (Ctr.CtrCod); Frm_LinkFormSubmit (Act_GetActionTextFromDB (Act_GetActCod (ActSeeCtrInf),ActTxt), Highlight ? ClassHighlight : ClassNormal,NULL); Log_DrawLogo (Hie_CTR,Ctr.CtrCod,Ctr.ShrtName,16,NULL,true); fprintf (Gbl.F.Out," %s",Ctr.FullName); Frm_LinkFormEnd (); Frm_EndForm (); HTM_LI_End (); /***** Get my degrees in this centre *****/ NumDegs = (unsigned) Usr_GetDegsFromUsr (Gbl.Usrs.Me.UsrDat.UsrCod, Ctr.CtrCod,&mysql_resDeg); for (NumDeg = 0; NumDeg < NumDegs; NumDeg++) { /***** Get next degree *****/ row = mysql_fetch_row (mysql_resDeg); /***** Get data of this degree *****/ Deg.DegCod = Str_ConvertStrCodToLongCod (row[0]); if (!Deg_GetDataOfDegreeByCod (&Deg)) Lay_ShowErrorAndExit ("Degree not found."); /***** Write link to degree *****/ Highlight = (Gbl.Hierarchy.Level == Hie_DEG && Gbl.Hierarchy.Deg.DegCod == Deg.DegCod); HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight : ClassNormal); IsLastItemInLevel[4] = (NumDeg == NumDegs - 1); Lay_IndentDependingOnLevel (4,IsLastItemInLevel); Frm_StartForm (ActMyCrs); Deg_PutParamDegCod (Deg.DegCod); Frm_LinkFormSubmit (Act_GetActionTextFromDB (Act_GetActCod (ActSeeDegInf),ActTxt), Highlight ? ClassHighlight : ClassNormal,NULL); Log_DrawLogo (Hie_DEG,Deg.DegCod,Deg.ShrtName,16,NULL,true); fprintf (Gbl.F.Out," %s",Deg.FullName); Frm_LinkFormEnd (); Frm_EndForm (); HTM_LI_End (); /***** Get my courses in this degree *****/ NumCrss = (unsigned) Usr_GetCrssFromUsr (Gbl.Usrs.Me.UsrDat.UsrCod, Deg.DegCod,&mysql_resCrs); for (NumCrs = 0; NumCrs < NumCrss; NumCrs++) { /***** Get next course *****/ row = mysql_fetch_row (mysql_resCrs); /***** Get data of this course *****/ Crs.CrsCod = Str_ConvertStrCodToLongCod (row[0]); if (!Crs_GetDataOfCourseByCod (&Crs)) Lay_ShowErrorAndExit ("Course not found."); /***** Write link to course *****/ Highlight = (Gbl.Hierarchy.Level == Hie_CRS && Gbl.Hierarchy.Crs.CrsCod == Crs.CrsCod); HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight : ClassNormal); IsLastItemInLevel[5] = (NumCrs == NumCrss - 1); Lay_IndentDependingOnLevel (5,IsLastItemInLevel); Frm_StartForm (ActMyCrs); Crs_PutParamCrsCod (Crs.CrsCod); snprintf (Gbl.Title,sizeof (Gbl.Title), Txt_Go_to_X, Crs.ShrtName); Frm_LinkFormSubmit (Gbl.Title, Highlight ? ClassHighlight : ClassNormal,NULL); Ico_PutIcon ("list-ol.svg",Crs.FullName,"ICO16x16"); fprintf (Gbl.F.Out," %s",Crs.FullName); Frm_LinkFormEnd (); Frm_EndForm (); /***** Put link to register students *****/ Enr_PutButtonInlineToRegisterStds (Crs.CrsCod); HTM_LI_End (); } /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_resCrs); } /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_resDeg); } /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_resCtr); } /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_resIns); } /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_resCty); /***** End box *****/ HTM_UL_End (); Box_BoxEnd (); } /*****************************************************************************/ /*********************** Get total number of courses *************************/ /*****************************************************************************/ unsigned Crs_GetNumCrssTotal (void) { /***** Get total number of courses from database *****/ return (unsigned) DB_GetNumRowsTable ("courses"); } /*****************************************************************************/ /****************** Get number of courses in a country ***********************/ /*****************************************************************************/ unsigned Crs_GetNumCrssInCty (long CtyCod) { /***** Get number of courses in a country from database *****/ return (unsigned) DB_QueryCOUNT ("can not get the number of courses in a country", "SELECT COUNT(*)" " FROM institutions,centres,degrees,courses" " WHERE institutions.CtyCod=%ld" " AND institutions.InsCod=centres.InsCod" " AND centres.CtrCod=degrees.CtrCod" " AND degrees.DegCod=courses.DegCod", CtyCod); } /*****************************************************************************/ /**************** Get number of courses in an institution ********************/ /*****************************************************************************/ unsigned Crs_GetNumCrssInIns (long InsCod) { /***** Get number of courses in a degree from database *****/ return (unsigned) DB_QueryCOUNT ("can not get the number of courses" " in an institution", "SELECT COUNT(*) FROM centres,degrees,courses" " WHERE centres.InsCod=%ld" " AND centres.CtrCod=degrees.CtrCod" " AND degrees.DegCod=courses.DegCod", InsCod); } /*****************************************************************************/ /******************** Get number of courses in a centre **********************/ /*****************************************************************************/ unsigned Crs_GetNumCrssInCtr (long CtrCod) { /***** Get number of courses in a degree from database *****/ return (unsigned) DB_QueryCOUNT ("can not get the number of courses in a centre", "SELECT COUNT(*) FROM degrees,courses" " WHERE degrees.CtrCod=%ld" " AND degrees.DegCod=courses.DegCod", CtrCod); } /*****************************************************************************/ /******************** Get number of courses in a degree **********************/ /*****************************************************************************/ unsigned Crs_GetNumCrssInDeg (long DegCod) { /***** Get number of courses in a degree from database *****/ return (unsigned) DB_QueryCOUNT ("can not get the number of courses in a degree", "SELECT COUNT(*) FROM courses" " WHERE DegCod=%ld", DegCod); } /*****************************************************************************/ /********************* Get number of courses with users **********************/ /*****************************************************************************/ unsigned Crs_GetNumCrssWithUsrs (Rol_Role_t Role,const char *SubQuery) { /***** Get number of degrees with users from database *****/ return (unsigned) DB_QueryCOUNT ("can not get number of courses with users", "SELECT COUNT(DISTINCT courses.CrsCod)" " FROM institutions,centres,degrees,courses,crs_usr" " WHERE %sinstitutions.InsCod=centres.InsCod" " AND centres.CtrCod=degrees.CtrCod" " AND degrees.DegCod=courses.DegCod" " AND courses.CrsCod=crs_usr.CrsCod" " AND crs_usr.Role=%u", SubQuery,(unsigned) Role); } /*****************************************************************************/ /*************************** Write selector of course ************************/ /*****************************************************************************/ void Crs_WriteSelectorOfCourse (void) { extern const char *Txt_Course; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumCrss; unsigned NumCrs; long CrsCod; /***** Begin form *****/ Frm_StartFormGoTo (ActSeeCrsInf); fprintf (Gbl.F.Out,"", Gbl.Form.Id); /***** Write an option when no course selected *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) // No course selected fprintf (Gbl.F.Out,"", Txt_Course); if (Gbl.Usrs.Me.MyCrss.Num) { /***** Write an option for each of my courses *****/ for (NumMyCrs = 0, LastDegCod = -1L; NumMyCrs < Gbl.Usrs.Me.MyCrss.Num; NumMyCrs++) { CrsCod = Gbl.Usrs.Me.MyCrss.Crss[NumMyCrs].CrsCod; DegCod = Gbl.Usrs.Me.MyCrss.Crss[NumMyCrs].DegCod; Crs_GetShortNamesByCod (CrsCod,CrsShortName,DegShortName); if (DegCod != LastDegCod) { if (LastDegCod > 0) fprintf (Gbl.F.Out,""); fprintf (Gbl.F.Out,"",DegShortName); LastDegCod = DegCod; } fprintf (Gbl.F.Out,"",CrsShortName); } if (LastDegCod > 0) fprintf (Gbl.F.Out,""); } /***** Write an option with the current course when I don't belong to it *****/ if (Gbl.Hierarchy.Level == Hie_CRS && // Course selected !Gbl.Usrs.Me.IBelongToCurrentCrs) // I do not belong to it fprintf (Gbl.F.Out,"", Gbl.Hierarchy.Crs.CrsCod, Gbl.Hierarchy.Crs.ShrtName); /***** End form *****/ HTM_SELECT_End (); Frm_EndForm (); } /*****************************************************************************/ /************************* List courses in this degree ***********************/ /*****************************************************************************/ static void Crs_ListCourses (void) { extern const char *Hlp_DEGREE_Courses; extern const char *Txt_Courses_of_DEGREE_X; extern const char *Txt_No_courses; extern const char *Txt_Create_another_course; extern const char *Txt_Create_course; unsigned Year; /***** Begin box *****/ snprintf (Gbl.Title,sizeof (Gbl.Title), Txt_Courses_of_DEGREE_X, Gbl.Hierarchy.Deg.ShrtName); Box_BoxBegin (NULL,Gbl.Title,Crs_PutIconsListCourses, Hlp_DEGREE_Courses,Box_NOT_CLOSABLE); if (Gbl.Hierarchy.Deg.Crss.Num) // There are courses in the current degree { /***** Begin table *****/ HTM_TABLE_BeginWideMarginPadding (2); Crs_PutHeadCoursesForSeeing (); /***** List the courses *****/ for (Year = 1; Year <= Deg_MAX_YEARS_PER_DEGREE; Year++) if (Crs_ListCoursesOfAYearForSeeing (Year)) // If this year has courses ==> Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd; // ==> change color for the next year Crs_ListCoursesOfAYearForSeeing (0); // Courses without a year selected /***** End table *****/ HTM_TABLE_End (); } else // No courses created in the current degree Ale_ShowAlert (Ale_INFO,Txt_No_courses); /***** Button to create course *****/ if (Crs_CheckIfICanCreateCourses ()) { Frm_StartForm (ActEdiCrs); Btn_PutConfirmButton (Gbl.Hierarchy.Deg.Crss.Num ? Txt_Create_another_course : Txt_Create_course); Frm_EndForm (); } /***** End box *****/ Box_BoxEnd (); } /*****************************************************************************/ /********************** Check if I can create courses ************************/ /*****************************************************************************/ static bool Crs_CheckIfICanCreateCourses (void) { return (bool) (Gbl.Usrs.Me.Role.Logged >= Rol_GST); } /*****************************************************************************/ /***************** Put contextual icons in list of courses *******************/ /*****************************************************************************/ static void Crs_PutIconsListCourses (void) { /***** Put icon to edit courses *****/ if (Crs_CheckIfICanCreateCourses ()) Crs_PutIconToEditCourses (); /***** Put icon to show a figure *****/ Gbl.Figures.FigureType = Fig_HIERARCHY; Fig_PutIconToShowFigure (); } /*****************************************************************************/ /************************* Put icon to edit courses **************************/ /*****************************************************************************/ static void Crs_PutIconToEditCourses (void) { Ico_PutContextualIconToEdit (ActEdiCrs,NULL); } /*****************************************************************************/ /********************* List courses of a year for seeing *********************/ /*****************************************************************************/ // Return true if this year has courses static bool Crs_ListCoursesOfAYearForSeeing (unsigned Year) { extern const char *Txt_COURSE_With_users; extern const char *Txt_COURSE_Without_users; extern const char *Txt_YEAR_OF_DEGREE[1 + Deg_MAX_YEARS_PER_DEGREE]; extern const char *Txt_Go_to_X; extern const char *Txt_COURSE_STATUS[Crs_NUM_STATUS_TXT]; unsigned NumCrs; struct Course *Crs; const char *TxtClassNormal; const char *TxtClassStrong; const char *BgColor; Crs_StatusTxt_t StatusTxt; bool ThisYearHasCourses = false; /***** Write all the courses of this year *****/ for (NumCrs = 0; NumCrs < Gbl.Hierarchy.Deg.Crss.Num; NumCrs++) { Crs = &(Gbl.Hierarchy.Deg.Crss.Lst[NumCrs]); if (Crs->Year == Year) // The year of the course is this? { ThisYearHasCourses = true; if (Crs->Status & Crs_STATUS_BIT_PENDING) { TxtClassNormal = "DAT_LIGHT"; TxtClassStrong = "DAT_LIGHT"; } else { TxtClassNormal = "DAT"; TxtClassStrong = "DAT_N"; } /* Check if this course is one of my courses */ BgColor = (Usr_CheckIfIBelongToCrs (Crs->CrsCod)) ? "LIGHT_BLUE" : Gbl.ColorRows[Gbl.RowEvenOdd]; HTM_TR_Begin (NULL); /* Put green tip if course has users */ HTM_TD_Begin ("class=\"%s CM %s\" title=\"%s\"", TxtClassNormal,BgColor, Crs->NumUsrs[Rol_UNK] ? Txt_COURSE_With_users : Txt_COURSE_Without_users); fprintf (Gbl.F.Out,"%s",Crs->NumUsrs[Rol_UNK] ? "✓" : " "); HTM_TD_End (); /* Institutional code of the course */ HTM_TD_Begin ("class=\"%s CM %s\"", TxtClassNormal,BgColor); fprintf (Gbl.F.Out,"%s",Crs->InstitutionalCrsCod); HTM_TD_End (); /* Course year */ HTM_TD_Begin ("class=\"%s CM %s\"", TxtClassNormal,BgColor); fprintf (Gbl.F.Out,"%s",Txt_YEAR_OF_DEGREE[Crs->Year]); HTM_TD_End (); /* Course full name */ HTM_TD_Begin ("class=\"%s LM %s\"",TxtClassStrong,BgColor); Frm_StartFormGoTo (ActSeeCrsInf); Crs_PutParamCrsCod (Crs->CrsCod); snprintf (Gbl.Title,sizeof (Gbl.Title), Txt_Go_to_X, Crs->FullName); Frm_LinkFormSubmit (Gbl.Title,TxtClassStrong,NULL); fprintf (Gbl.F.Out,"%s",Crs->FullName); Frm_LinkFormEnd (); Frm_EndForm (); HTM_TD_End (); /* Current number of teachers in this course */ HTM_TD_Begin ("class=\"%s RM %s\"",TxtClassNormal,BgColor); fprintf (Gbl.F.Out,"%u",Crs->NumUsrs[Rol_TCH] + Crs->NumUsrs[Rol_NET]); HTM_TD_End (); /* Current number of students in this course */ HTM_TD_Begin ("class=\"%s RM %s\"",TxtClassNormal,BgColor); fprintf (Gbl.F.Out,"%u",Crs->NumUsrs[Rol_STD]); HTM_TD_End (); /* Course status */ StatusTxt = Crs_GetStatusTxtFromStatusBits (Crs->Status); HTM_TD_Begin ("class=\"%s LM %s\"",TxtClassNormal,BgColor); if (StatusTxt != Crs_STATUS_ACTIVE) // If active ==> do not show anything fprintf (Gbl.F.Out,"%s",Txt_COURSE_STATUS[StatusTxt]); HTM_TD_End (); HTM_TR_End (); } } return ThisYearHasCourses; } /*****************************************************************************/ /****************** Put forms to edit courses in this degree *****************/ /*****************************************************************************/ void Crs_EditCourses (void) { /***** Course constructor *****/ Crs_EditingCourseConstructor (); /***** Edit courses *****/ Crs_EditCoursesInternal (); /***** Course destructor *****/ Crs_EditingCourseDestructor (); } static void Crs_EditCoursesInternal (void) { extern const char *Hlp_DEGREE_Courses; extern const char *Txt_Courses_of_DEGREE_X; /***** Get list of degrees in this centre *****/ Deg_GetListDegsOfCurrentCtr (); /***** Get list of courses in this degree *****/ Crs_GetListCoursesInCurrentDegree (Crs_ALL_COURSES_EXCEPT_REMOVED); /***** Write menu to select country, institution, centre and degree *****/ Hie_WriteMenuHierarchy (); /***** Begin box *****/ snprintf (Gbl.Title,sizeof (Gbl.Title), Txt_Courses_of_DEGREE_X, Gbl.Hierarchy.Deg.ShrtName); Box_BoxBegin (NULL,Gbl.Title,Crs_PutIconsEditingCourses, Hlp_DEGREE_Courses,Box_NOT_CLOSABLE); /***** Put a form to create or request a new course *****/ Crs_PutFormToCreateCourse (); /***** Forms to edit current courses *****/ if (Gbl.Hierarchy.Deg.Crss.Num) Crs_ListCoursesForEdition (); /***** End box *****/ Box_BoxEnd (); /***** Free list of courses in this degree *****/ Crs_FreeListCoursesInCurrentDegree (); /***** Free list of degrees in this centre *****/ Deg_FreeListDegs (&Gbl.Hierarchy.Ctr.Degs); } /*****************************************************************************/ /**************** Put contextual icons in edition of courses *****************/ /*****************************************************************************/ static void Crs_PutIconsEditingCourses (void) { /***** Put icon to view degrees *****/ Crs_PutIconToViewCourses (); /***** Put icon to show a figure *****/ Gbl.Figures.FigureType = Fig_HIERARCHY; Fig_PutIconToShowFigure (); } /*****************************************************************************/ /************************* Put icon to view courses **************************/ /*****************************************************************************/ static void Crs_PutIconToViewCourses (void) { extern const char *Txt_Courses; Lay_PutContextualLinkOnlyIcon (ActSeeCrs,NULL,NULL, "list-ol.svg", Txt_Courses); } /*****************************************************************************/ /********************* List current courses for edition **********************/ /*****************************************************************************/ static void Crs_ListCoursesForEdition (void) { unsigned Year; /***** Write heading *****/ HTM_TABLE_BeginWidePadding (2); Crs_PutHeadCoursesForEdition (); /***** List the courses *****/ for (Year = 1; Year <= Deg_MAX_YEARS_PER_DEGREE; Year++) Crs_ListCoursesOfAYearForEdition (Year); Crs_ListCoursesOfAYearForEdition (0); /***** End table *****/ HTM_TABLE_End (); } /*****************************************************************************/ /******************** List courses of a year for edition *********************/ /*****************************************************************************/ static void Crs_ListCoursesOfAYearForEdition (unsigned Year) { extern const char *Txt_YEAR_OF_DEGREE[1 + Deg_MAX_YEARS_PER_DEGREE]; extern const char *Txt_COURSE_STATUS[Crs_NUM_STATUS_TXT]; struct Course *Crs; unsigned YearAux; unsigned NumCrs; struct UsrData UsrDat; bool ICanEdit; Crs_StatusTxt_t StatusTxt; /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&UsrDat); /***** List courses of a given year *****/ for (NumCrs = 0; NumCrs < Gbl.Hierarchy.Deg.Crss.Num; NumCrs++) { Crs = &(Gbl.Hierarchy.Deg.Crss.Lst[NumCrs]); if (Crs->Year == Year) { ICanEdit = Crs_CheckIfICanEdit (Crs); HTM_TR_Begin (NULL); /* Put icon to remove course */ HTM_TD_Begin ("class=\"BM\""); if (Crs->NumUsrs[Rol_UNK] || // Course has users ==> deletion forbidden !ICanEdit) Ico_PutIconRemovalNotAllowed (); else // Crs->NumUsrs == 0 && ICanEdit { Frm_StartForm (ActRemCrs); Crs_PutParamOtherCrsCod (Crs->CrsCod); Ico_PutIconRemove (); Frm_EndForm (); } HTM_TD_End (); /* Course code */ HTM_TD_Begin ("class=\"DAT CODE\""); fprintf (Gbl.F.Out,"%ld",Crs->CrsCod); HTM_TD_End (); /* Institutional code of the course */ HTM_TD_Begin ("class=\"DAT CM\""); if (ICanEdit) { Frm_StartForm (ActChgInsCrsCod); Crs_PutParamOtherCrsCod (Crs->CrsCod); HTM_INPUT_TEXT ("InsCrsCod",Crs_MAX_CHARS_INSTITUTIONAL_CRS_COD, Crs->InstitutionalCrsCod,true, "class=\"INPUT_INS_CODE\""); Frm_EndForm (); } else fprintf (Gbl.F.Out,"%s",Crs->InstitutionalCrsCod); HTM_TD_End (); /* Course year */ HTM_TD_Begin ("class=\"DAT CM\""); if (ICanEdit) { Frm_StartForm (ActChgCrsYea); Crs_PutParamOtherCrsCod (Crs->CrsCod); fprintf (Gbl.F.Out,"", Gbl.Form.Id); fprintf (Gbl.F.Out,"" "", (unsigned) Crs_GetStatusBitsFromStatusTxt (Crs_STATUS_PENDING), Txt_COURSE_STATUS[Crs_STATUS_PENDING], (unsigned) Crs_GetStatusBitsFromStatusTxt (Crs_STATUS_ACTIVE), Txt_COURSE_STATUS[Crs_STATUS_ACTIVE]); HTM_SELECT_End (); Frm_EndForm (); } else if (StatusTxt != Crs_STATUS_ACTIVE) // If active ==> do not show anything fprintf (Gbl.F.Out,"%s",Txt_COURSE_STATUS[StatusTxt]); HTM_TD_End (); HTM_TR_End (); } } /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&UsrDat); } /*****************************************************************************/ /************** Check if I can edit, remove, etc. a course *******************/ /*****************************************************************************/ static bool Crs_CheckIfICanEdit (struct Course *Crs) { return (bool) (Gbl.Usrs.Me.Role.Logged >= Rol_DEG_ADM || // I am a degree administrator or higher ((Crs->Status & Crs_STATUS_BIT_PENDING) != 0 && // Course is not yet activated Gbl.Usrs.Me.UsrDat.UsrCod == Crs->RequesterUsrCod)); // I am the requester } /*****************************************************************************/ /******************* Set StatusTxt depending on status bits ******************/ /*****************************************************************************/ // Crs_STATUS_UNKNOWN = 0 // Other // Crs_STATUS_ACTIVE = 1 // 00 (Status == 0) // Crs_STATUS_PENDING = 2 // 01 (Status == Crs_STATUS_BIT_PENDING) // Crs_STATUS_REMOVED = 3 // 1- (Status & Crs_STATUS_BIT_REMOVED) static Crs_StatusTxt_t Crs_GetStatusTxtFromStatusBits (Crs_Status_t Status) { if (Status == 0) return Crs_STATUS_ACTIVE; if (Status == Crs_STATUS_BIT_PENDING) return Crs_STATUS_PENDING; if (Status & Crs_STATUS_BIT_REMOVED) return Crs_STATUS_REMOVED; return Crs_STATUS_UNKNOWN; } /*****************************************************************************/ /******************* Set status bits depending on StatusTxt ******************/ /*****************************************************************************/ // Crs_STATUS_UNKNOWN = 0 // Other // Crs_STATUS_ACTIVE = 1 // 00 (Status == 0) // Crs_STATUS_PENDING = 2 // 01 (Status == Crs_STATUS_BIT_PENDING) // Crs_STATUS_REMOVED = 3 // 1- (Status & Crs_STATUS_BIT_REMOVED) static Crs_Status_t Crs_GetStatusBitsFromStatusTxt (Crs_StatusTxt_t StatusTxt) { switch (StatusTxt) { case Crs_STATUS_UNKNOWN: case Crs_STATUS_ACTIVE: return (Crs_Status_t) 0; case Crs_STATUS_PENDING: return Crs_STATUS_BIT_PENDING; case Crs_STATUS_REMOVED: return Crs_STATUS_BIT_REMOVED; } return (Crs_Status_t) 0; } /*****************************************************************************/ /*********************** Put a form to create a new course *******************/ /*****************************************************************************/ static void Crs_PutFormToCreateCourse (void) { extern const char *Txt_New_course; extern const char *Txt_YEAR_OF_DEGREE[1 + Deg_MAX_YEARS_PER_DEGREE]; extern const char *Txt_Create_course; unsigned Year; /***** Begin form *****/ if (Gbl.Usrs.Me.Role.Logged >= Rol_DEG_ADM) Frm_StartForm (ActNewCrs); else if (Gbl.Usrs.Me.Role.Max >= Rol_GST) Frm_StartForm (ActReqCrs); else Lay_NoPermissionExit (); /***** Begin box and table *****/ Box_StartBoxTable (NULL,Txt_New_course,NULL, NULL,Box_NOT_CLOSABLE,2); /***** Write heading *****/ Crs_PutHeadCoursesForEdition (); HTM_TR_Begin (NULL); /***** Column to remove course, disabled here *****/ HTM_TD_Begin ("class=\"BM\""); HTM_TD_End (); /***** Course code *****/ HTM_TD_Begin ("class=\"CODE\""); HTM_TD_End (); /***** Institutional code of the course *****/ HTM_TD_Begin ("class=\"CM\""); HTM_INPUT_TEXT ("InsCrsCod",Crs_MAX_CHARS_INSTITUTIONAL_CRS_COD, Crs_EditingCrs->InstitutionalCrsCod,false, "class=\"INPUT_INS_CODE\""); HTM_TD_End (); /***** Year *****/ HTM_TD_Begin ("class=\"CM\""); fprintf (Gbl.F.Out,""); for (i = Crs_MIN_MONTHS_WITHOUT_ACCESS_TO_REMOVE_OLD_CRSS; i <= Crs_MAX_MONTHS_WITHOUT_ACCESS_TO_REMOVE_OLD_CRSS; i++) { fprintf (Gbl.F.Out,"%u",i); } HTM_SELECT_End (); fprintf (Gbl.F.Out," "); fprintf (Gbl.F.Out,Txt_Eliminate_all_courses_whithout_users_PART_2_OF_2, Cfg_PLATFORM_SHORT_NAME); HTM_LABEL_End (); /***** Send button and end box *****/ Box_EndBoxWithButton (Btn_REMOVE_BUTTON,Txt_Eliminate); /***** End form *****/ Frm_EndForm (); } /*****************************************************************************/ /**************************** Remove old courses *****************************/ /*****************************************************************************/ void Crs_RemoveOldCrss (void) { extern const char *Txt_Eliminating_X_courses_whithout_users_and_with_more_than_Y_months_without_access; extern const char *Txt_X_courses_have_been_eliminated; unsigned MonthsWithoutAccess; unsigned long SecondsWithoutAccess; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned long NumCrs; unsigned long NumCrss; unsigned NumCrssRemoved = 0; long CrsCod; /***** Get parameter with number of months without access *****/ MonthsWithoutAccess = (unsigned) Par_GetParToUnsignedLong ("Months", Crs_MIN_MONTHS_WITHOUT_ACCESS_TO_REMOVE_OLD_CRSS, Crs_MAX_MONTHS_WITHOUT_ACCESS_TO_REMOVE_OLD_CRSS, UINT_MAX); if (MonthsWithoutAccess == UINT_MAX) Lay_ShowErrorAndExit ("Wrong number of months without clicks."); SecondsWithoutAccess = (unsigned long) MonthsWithoutAccess * Dat_SECONDS_IN_ONE_MONTH; /***** Get old courses from database *****/ NumCrss = DB_QuerySELECT (&mysql_res,"can not get old courses", "SELECT CrsCod FROM crs_last WHERE" " LastTimeCrsCod = -1L; Crs_EditingCrs->InstitutionalCrsCod[0] = '\0'; Crs_EditingCrs->DegCod = -1L; Crs_EditingCrs->Year = 0; Crs_EditingCrs->Status = 0; Crs_EditingCrs->ShrtName[0] = '\0'; Crs_EditingCrs->FullName[0] = '\0'; for (Role = (Rol_Role_t) 0; Role < Rol_NUM_ROLES; Role++) Crs_EditingCrs->NumUsrs[Role] = 0; } static void Crs_EditingCourseDestructor (void) { /***** Free memory used for course *****/ if (Crs_EditingCrs != NULL) { free ((void *) Crs_EditingCrs); Crs_EditingCrs = NULL; } }