mirror of
https://github.com/acanas/swad-core.git
synced 2024-09-23 00:00:50 +02:00
3044 lines
105 KiB
C
3044 lines
105 KiB
C
// 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-2021 Antonio Cañas Vargas
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/*****************************************************************************/
|
|
/********************************* Headers ***********************************/
|
|
/*****************************************************************************/
|
|
|
|
#define _GNU_SOURCE // For asprintf
|
|
#include <stddef.h> // For NULL
|
|
#include <stdio.h> // For asprintf
|
|
#include <stdlib.h> // For free
|
|
#include <string.h> // For string functions
|
|
|
|
#include "swad_attendance.h"
|
|
#include "swad_course.h"
|
|
#include "swad_course_config.h"
|
|
#include "swad_database.h"
|
|
#include "swad_exam_announcement.h"
|
|
#include "swad_figure.h"
|
|
#include "swad_figure_cache.h"
|
|
#include "swad_form.h"
|
|
#include "swad_forum.h"
|
|
#include "swad_game.h"
|
|
#include "swad_global.h"
|
|
#include "swad_help.h"
|
|
#include "swad_hierarchy.h"
|
|
#include "swad_HTML.h"
|
|
#include "swad_info.h"
|
|
#include "swad_logo.h"
|
|
#include "swad_message.h"
|
|
#include "swad_project.h"
|
|
#include "swad_search.h"
|
|
#include "swad_survey.h"
|
|
#include "swad_test.h"
|
|
|
|
/*****************************************************************************/
|
|
/************** External global variables from others modules ****************/
|
|
/*****************************************************************************/
|
|
|
|
extern struct Globals Gbl;
|
|
|
|
/*****************************************************************************/
|
|
/*************************** Public constants ********************************/
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/***************************** Private types *********************************/
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/**************************** Private variables ******************************/
|
|
/*****************************************************************************/
|
|
|
|
static struct Crs_Course *Crs_EditingCrs = NULL; // Static variable to keep the course being edited
|
|
|
|
/*****************************************************************************/
|
|
/**************************** Private prototypes *****************************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_WriteListMyCoursesToSelectOne (void);
|
|
|
|
static void Crs_GetListCrssInCurrentDeg (Crs_WhatCourses_t WhatCourses);
|
|
static void Crs_ListCourses (void);
|
|
static bool Crs_CheckIfICanCreateCourses (void);
|
|
static void Crs_PutIconsListCourses (__attribute__((unused)) void *Args);
|
|
static void Crs_PutIconToEditCourses (void);
|
|
static bool Crs_ListCoursesOfAYearForSeeing (unsigned Year);
|
|
|
|
static void Crs_EditCoursesInternal (void);
|
|
static void Crs_PutIconsEditingCourses (__attribute__((unused)) void *Args);
|
|
static void Crs_PutIconToViewCourses (void);
|
|
static void Crs_ListCoursesForEdition (void);
|
|
static void Crs_ListCoursesOfAYearForEdition (unsigned Year);
|
|
static bool Crs_CheckIfICanEdit (struct Crs_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_ReceiveFormRequestOrCreateCrs (unsigned Status);
|
|
static void Crs_GetParamsNewCourse (struct Crs_Course *Crs);
|
|
|
|
static void Crs_CreateCourse (unsigned Status);
|
|
static void Crs_GetDataOfCourseFromRow (struct Crs_Course *Crs,MYSQL_ROW row);
|
|
|
|
static void Crs_GetShortNamesByCod (long CrsCod,
|
|
char CrsShortName[Cns_HIERARCHY_MAX_BYTES_SHRT_NAME + 1],
|
|
char DegShortName[Cns_HIERARCHY_MAX_BYTES_SHRT_NAME + 1]);
|
|
|
|
static void Crs_EmptyCourseCompletely (long CrsCod);
|
|
|
|
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 (__attribute__((unused)) void *Args);
|
|
|
|
static void Crs_PutParamOtherCrsCod (void *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\"");
|
|
CrsCfg_Configuration (false);
|
|
HTM_DIV_End ();
|
|
|
|
/***** Course introduction *****/
|
|
Inf_ShowInfo ();
|
|
|
|
/***** Show help to enrol me *****/
|
|
Hlp_ShowHelpWhatWouldYouLikeToDo ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************ 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_ClassFormLinkInBox[The_NUM_THEMES];
|
|
extern const char *The_ClassFormLinkInBoxBold[The_NUM_THEMES];
|
|
extern const char *Txt_My_courses;
|
|
extern const char *Txt_System;
|
|
struct Hie_Hierarchy Hie;
|
|
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;
|
|
const char *ClassNormal;
|
|
char ClassHighlight[64];
|
|
|
|
ClassNormal = The_ClassFormLinkInBox[Gbl.Prefs.Theme];
|
|
snprintf (ClassHighlight,sizeof (ClassHighlight),"%s LIGHT_BLUE",
|
|
The_ClassFormLinkInBoxBold[Gbl.Prefs.Theme]);
|
|
|
|
/***** Begin box *****/
|
|
Box_BoxBegin (NULL,Txt_My_courses,
|
|
Crs_PutIconToSearchCourses,NULL,
|
|
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_BeginForm (ActMyCrs);
|
|
Cty_PutParamCtyCod (-1L);
|
|
HTM_BUTTON_SUBMIT_Begin (Txt_System,
|
|
Highlight ? ClassHighlight :
|
|
ClassNormal,
|
|
NULL);
|
|
Ico_PutIcon ("sitemap.svg",Txt_System,"ICO16x16");
|
|
HTM_TxtF (" %s",Txt_System);
|
|
HTM_BUTTON_End ();
|
|
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 *****/
|
|
Hie.Cty.CtyCod = Str_ConvertStrCodToLongCod (row[0]);
|
|
if (!Cty_GetDataOfCountryByCod (&Hie.Cty))
|
|
Lay_ShowErrorAndExit ("Country not found.");
|
|
|
|
/***** Write link to country *****/
|
|
Highlight = (Gbl.Hierarchy.Ins.InsCod <= 0 &&
|
|
Gbl.Hierarchy.Cty.CtyCod == Hie.Cty.CtyCod);
|
|
HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight :
|
|
ClassNormal);
|
|
IsLastItemInLevel[1] = (NumCty == NumCtys - 1);
|
|
Lay_IndentDependingOnLevel (1,IsLastItemInLevel);
|
|
Frm_BeginForm (ActMyCrs);
|
|
Cty_PutParamCtyCod (Hie.Cty.CtyCod);
|
|
HTM_BUTTON_SUBMIT_Begin (Act_GetActionText (ActSeeCtyInf),
|
|
Highlight ? ClassHighlight :
|
|
ClassNormal,
|
|
NULL);
|
|
Cty_DrawCountryMap (&Hie.Cty,"ICO16x16");
|
|
HTM_TxtF (" %s",Hie.Cty.Name[Gbl.Prefs.Language]);
|
|
HTM_BUTTON_End ();
|
|
Frm_EndForm ();
|
|
HTM_LI_End ();
|
|
|
|
/***** Get my institutions in this country *****/
|
|
NumInss = (unsigned) Usr_GetInssFromUsr (Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
Hie.Cty.CtyCod,&mysql_resIns);
|
|
for (NumIns = 0;
|
|
NumIns < NumInss;
|
|
NumIns++)
|
|
{
|
|
/***** Get next institution *****/
|
|
row = mysql_fetch_row (mysql_resIns);
|
|
|
|
/***** Get data of this institution *****/
|
|
Hie.Ins.InsCod = Str_ConvertStrCodToLongCod (row[0]);
|
|
if (!Ins_GetDataOfInstitutionByCod (&Hie.Ins))
|
|
Lay_ShowErrorAndExit ("Institution not found.");
|
|
|
|
/***** Write link to institution *****/
|
|
Highlight = (Gbl.Hierarchy.Ctr.CtrCod <= 0 &&
|
|
Gbl.Hierarchy.Ins.InsCod == Hie.Ins.InsCod);
|
|
HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight :
|
|
ClassNormal);
|
|
IsLastItemInLevel[2] = (NumIns == NumInss - 1);
|
|
Lay_IndentDependingOnLevel (2,IsLastItemInLevel);
|
|
Frm_BeginForm (ActMyCrs);
|
|
Ins_PutParamInsCod (Hie.Ins.InsCod);
|
|
HTM_BUTTON_SUBMIT_Begin (Act_GetActionText (ActSeeInsInf),
|
|
Highlight ? ClassHighlight :
|
|
ClassNormal,
|
|
NULL);
|
|
Lgo_DrawLogo (Hie_Lvl_INS,Hie.Ins.InsCod,Hie.Ins.ShrtName,16,NULL,true);
|
|
HTM_TxtF (" %s",Hie.Ins.ShrtName);
|
|
HTM_BUTTON_End ();
|
|
Frm_EndForm ();
|
|
HTM_LI_End ();
|
|
|
|
/***** Get my centers in this institution *****/
|
|
NumCtrs = (unsigned) Usr_GetCtrsFromUsr (Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
Hie.Ins.InsCod,&mysql_resCtr);
|
|
for (NumCtr = 0;
|
|
NumCtr < NumCtrs;
|
|
NumCtr++)
|
|
{
|
|
/***** Get next center *****/
|
|
row = mysql_fetch_row (mysql_resCtr);
|
|
|
|
/***** Get data of this center *****/
|
|
Hie.Ctr.CtrCod = Str_ConvertStrCodToLongCod (row[0]);
|
|
if (!Ctr_GetDataOfCenterByCod (&Hie.Ctr))
|
|
Lay_ShowErrorAndExit ("Center not found.");
|
|
|
|
/***** Write link to center *****/
|
|
Highlight = (Gbl.Hierarchy.Level == Hie_Lvl_CTR &&
|
|
Gbl.Hierarchy.Ctr.CtrCod == Hie.Ctr.CtrCod);
|
|
HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight :
|
|
ClassNormal);
|
|
IsLastItemInLevel[3] = (NumCtr == NumCtrs - 1);
|
|
Lay_IndentDependingOnLevel (3,IsLastItemInLevel);
|
|
Frm_BeginForm (ActMyCrs);
|
|
Ctr_PutParamCtrCod (Hie.Ctr.CtrCod);
|
|
HTM_BUTTON_SUBMIT_Begin (Act_GetActionText (ActSeeCtrInf),
|
|
Highlight ? ClassHighlight :
|
|
ClassNormal,
|
|
NULL);
|
|
Lgo_DrawLogo (Hie_Lvl_CTR,Hie.Ctr.CtrCod,Hie.Ctr.ShrtName,16,NULL,true);
|
|
HTM_TxtF (" %s",Hie.Ctr.ShrtName);
|
|
HTM_BUTTON_End ();
|
|
Frm_EndForm ();
|
|
HTM_LI_End ();
|
|
|
|
/***** Get my degrees in this center *****/
|
|
NumDegs = (unsigned) Usr_GetDegsFromUsr (Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
Hie.Ctr.CtrCod,&mysql_resDeg);
|
|
for (NumDeg = 0;
|
|
NumDeg < NumDegs;
|
|
NumDeg++)
|
|
{
|
|
/***** Get next degree *****/
|
|
row = mysql_fetch_row (mysql_resDeg);
|
|
|
|
/***** Get data of this degree *****/
|
|
Hie.Deg.DegCod = Str_ConvertStrCodToLongCod (row[0]);
|
|
if (!Deg_GetDataOfDegreeByCod (&Hie.Deg))
|
|
Lay_ShowErrorAndExit ("Degree not found.");
|
|
|
|
/***** Write link to degree *****/
|
|
Highlight = (Gbl.Hierarchy.Level == Hie_Lvl_DEG &&
|
|
Gbl.Hierarchy.Deg.DegCod == Hie.Deg.DegCod);
|
|
HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight :
|
|
ClassNormal);
|
|
IsLastItemInLevel[4] = (NumDeg == NumDegs - 1);
|
|
Lay_IndentDependingOnLevel (4,IsLastItemInLevel);
|
|
Frm_BeginForm (ActMyCrs);
|
|
Deg_PutParamDegCod (Hie.Deg.DegCod);
|
|
HTM_BUTTON_SUBMIT_Begin (Act_GetActionText (ActSeeDegInf),
|
|
Highlight ? ClassHighlight :
|
|
ClassNormal,
|
|
NULL);
|
|
Lgo_DrawLogo (Hie_Lvl_DEG,Hie.Deg.DegCod,Hie.Deg.ShrtName,16,NULL,true);
|
|
HTM_TxtF (" %s",Hie.Deg.ShrtName);
|
|
HTM_BUTTON_End ();
|
|
Frm_EndForm ();
|
|
HTM_LI_End ();
|
|
|
|
/***** Get my courses in this degree *****/
|
|
NumCrss = (unsigned) Usr_GetCrssFromUsr (Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
Hie.Deg.DegCod,&mysql_resCrs);
|
|
for (NumCrs = 0;
|
|
NumCrs < NumCrss;
|
|
NumCrs++)
|
|
{
|
|
/***** Get next course *****/
|
|
row = mysql_fetch_row (mysql_resCrs);
|
|
|
|
/***** Get data of this course *****/
|
|
Hie.Crs.CrsCod = Str_ConvertStrCodToLongCod (row[0]);
|
|
if (!Crs_GetDataOfCourseByCod (&Hie.Crs))
|
|
Lay_ShowErrorAndExit ("Course not found.");
|
|
|
|
/***** Write link to course *****/
|
|
Highlight = (Gbl.Hierarchy.Level == Hie_Lvl_CRS &&
|
|
Gbl.Hierarchy.Crs.CrsCod == Hie.Crs.CrsCod);
|
|
HTM_LI_Begin ("class=\"%s\"",Highlight ? ClassHighlight :
|
|
ClassNormal);
|
|
IsLastItemInLevel[5] = (NumCrs == NumCrss - 1);
|
|
Lay_IndentDependingOnLevel (5,IsLastItemInLevel);
|
|
Frm_BeginForm (ActMyCrs);
|
|
Crs_PutParamCrsCod (Hie.Crs.CrsCod);
|
|
HTM_BUTTON_SUBMIT_Begin (Hie_BuildGoToMsg (Hie.Crs.ShrtName),
|
|
Highlight ? ClassHighlight :
|
|
ClassNormal,
|
|
NULL);
|
|
Hie_FreeGoToMsg ();
|
|
Ico_PutIcon ("chalkboard-teacher.svg",Hie.Crs.FullName,"ICO16x16");
|
|
HTM_TxtF (" %s",Hie.Crs.ShrtName);
|
|
HTM_BUTTON_End ();
|
|
Frm_EndForm ();
|
|
|
|
/***** Put link to register students *****/
|
|
Enr_PutButtonInlineToRegisterStds (Hie.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_GetCachedNumCrssInSys (void)
|
|
{
|
|
unsigned NumCrss;
|
|
|
|
/***** Get number of courses from cache *****/
|
|
if (!FigCch_GetFigureFromCache (FigCch_NUM_CRSS,Hie_Lvl_SYS,-1L,
|
|
FigCch_UNSIGNED,&NumCrss))
|
|
{
|
|
/***** Get current number of courses from database and update cache *****/
|
|
NumCrss = (unsigned) DB_GetNumRowsTable ("crs_courses");
|
|
FigCch_UpdateFigureIntoCache (FigCch_NUM_CRSS,Hie_Lvl_SYS,-1L,
|
|
FigCch_UNSIGNED,&NumCrss);
|
|
}
|
|
|
|
return NumCrss;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/****************** Get number of courses in a country ***********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_FlushCacheNumCrssInCty (void)
|
|
{
|
|
Gbl.Cache.NumCrssInCty.CtyCod = -1L;
|
|
Gbl.Cache.NumCrssInCty.NumCrss = 0;
|
|
}
|
|
|
|
unsigned Crs_GetNumCrssInCty (long CtyCod)
|
|
{
|
|
/***** 1. Fast check: Trivial case *****/
|
|
if (CtyCod <= 0)
|
|
return 0;
|
|
|
|
/***** 2. Fast check: If cached... *****/
|
|
if (CtyCod == Gbl.Cache.NumCrssInCty.CtyCod)
|
|
return Gbl.Cache.NumCrssInCty.NumCrss;
|
|
|
|
/***** 3. Slow: number of courses in a country from database *****/
|
|
Gbl.Cache.NumCrssInCty.CtyCod = CtyCod;
|
|
Gbl.Cache.NumCrssInCty.NumCrss = (unsigned)
|
|
DB_QueryCOUNT ("can not get the number of courses in a country",
|
|
"SELECT COUNT(*)"
|
|
" FROM ins_instits,"
|
|
"ctr_centers,"
|
|
"deg_degrees,"
|
|
"crs_courses"
|
|
" WHERE ins_instits.CtyCod=%ld"
|
|
" AND ins_instits.InsCod=ctr_centers.InsCod"
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod",
|
|
CtyCod);
|
|
FigCch_UpdateFigureIntoCache (FigCch_NUM_CRSS,Hie_Lvl_CTY,Gbl.Cache.NumCrssInCty.CtyCod,
|
|
FigCch_UNSIGNED,&Gbl.Cache.NumCrssInCty.NumCrss);
|
|
return Gbl.Cache.NumCrssInCty.NumCrss;
|
|
}
|
|
|
|
unsigned Crs_GetCachedNumCrssInCty (long CtyCod)
|
|
{
|
|
unsigned NumCrss;
|
|
|
|
/***** Get number of courses from cache *****/
|
|
if (!FigCch_GetFigureFromCache (FigCch_NUM_CRSS,Hie_Lvl_CTY,CtyCod,
|
|
FigCch_UNSIGNED,&NumCrss))
|
|
/***** Get current number of courses from database and update cache *****/
|
|
NumCrss = Crs_GetNumCrssInCty (CtyCod);
|
|
|
|
return NumCrss;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**************** Get number of courses in an institution ********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_FlushCacheNumCrssInIns (void)
|
|
{
|
|
Gbl.Cache.NumCrssInIns.InsCod = -1L;
|
|
Gbl.Cache.NumCrssInIns.NumCrss = 0;
|
|
}
|
|
|
|
unsigned Crs_GetNumCrssInIns (long InsCod)
|
|
{
|
|
/***** 1. Fast check: Trivial case *****/
|
|
if (InsCod <= 0)
|
|
return 0;
|
|
|
|
/***** 2. Fast check: If cached... *****/
|
|
if (InsCod == Gbl.Cache.NumCrssInIns.InsCod)
|
|
return Gbl.Cache.NumCrssInIns.NumCrss;
|
|
|
|
/***** 3. Slow: number of courses in an institution from database *****/
|
|
Gbl.Cache.NumCrssInIns.InsCod = InsCod;
|
|
Gbl.Cache.NumCrssInIns.NumCrss = (unsigned)
|
|
DB_QueryCOUNT ("can not get the number of courses in an institution",
|
|
"SELECT COUNT(*)"
|
|
" FROM ctr_centers,"
|
|
"deg_degrees,"
|
|
"crs_courses"
|
|
" WHERE ctr_centers.InsCod=%ld"
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod",
|
|
InsCod);
|
|
FigCch_UpdateFigureIntoCache (FigCch_NUM_CRSS,Hie_Lvl_INS,Gbl.Cache.NumCrssInIns.InsCod,
|
|
FigCch_UNSIGNED,&Gbl.Cache.NumCrssInIns.NumCrss);
|
|
return Gbl.Cache.NumCrssInIns.NumCrss;
|
|
}
|
|
|
|
unsigned Crs_GetCachedNumCrssInIns (long InsCod)
|
|
{
|
|
unsigned NumCrss;
|
|
|
|
/***** Get number of courses from cache *****/
|
|
if (!FigCch_GetFigureFromCache (FigCch_NUM_CRSS,Hie_Lvl_INS,InsCod,
|
|
FigCch_UNSIGNED,&NumCrss))
|
|
/***** Get current number of courses from database and update cache *****/
|
|
NumCrss = Crs_GetNumCrssInIns (InsCod);
|
|
|
|
return NumCrss;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/******************** Get number of courses in a center **********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_FlushCacheNumCrssInCtr (void)
|
|
{
|
|
Gbl.Cache.NumCrssInCtr.CtrCod = -1L;
|
|
Gbl.Cache.NumCrssInCtr.NumCrss = 0;
|
|
}
|
|
|
|
unsigned Crs_GetNumCrssInCtr (long CtrCod)
|
|
{
|
|
/***** 1. Fast check: Trivial case *****/
|
|
if (CtrCod <= 0)
|
|
return 0;
|
|
|
|
/***** 2. Fast check: If cached... *****/
|
|
if (CtrCod == Gbl.Cache.NumCrssInCtr.CtrCod)
|
|
return Gbl.Cache.NumCrssInCtr.NumCrss;
|
|
|
|
/***** 3. Slow: number of courses in a center from database *****/
|
|
Gbl.Cache.NumCrssInCtr.CtrCod = CtrCod;
|
|
Gbl.Cache.NumCrssInCtr.NumCrss = (unsigned)
|
|
DB_QueryCOUNT ("can not get the number of courses in a center",
|
|
"SELECT COUNT(*)"
|
|
" FROM deg_degrees,"
|
|
"crs_courses"
|
|
" WHERE deg_degrees.CtrCod=%ld"
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod",
|
|
CtrCod);
|
|
return Gbl.Cache.NumCrssInCtr.NumCrss;
|
|
}
|
|
|
|
unsigned Crs_GetCachedNumCrssInCtr (long CtrCod)
|
|
{
|
|
unsigned NumCrss;
|
|
|
|
/***** Get number of courses from cache *****/
|
|
if (!FigCch_GetFigureFromCache (FigCch_NUM_CRSS,Hie_Lvl_CTR,CtrCod,
|
|
FigCch_UNSIGNED,&NumCrss))
|
|
{
|
|
/***** Get current number of courses from database and update cache *****/
|
|
NumCrss = Crs_GetNumCrssInCtr (CtrCod);
|
|
FigCch_UpdateFigureIntoCache (FigCch_NUM_CRSS,Hie_Lvl_CTR,CtrCod,
|
|
FigCch_UNSIGNED,&NumCrss);
|
|
}
|
|
|
|
return NumCrss;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/******************** Get number of courses in a degree **********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_FlushCacheNumCrssInDeg (void)
|
|
{
|
|
Gbl.Cache.NumCrssInDeg.DegCod = -1L;
|
|
Gbl.Cache.NumCrssInDeg.NumCrss = 0;
|
|
}
|
|
|
|
unsigned Crs_GetNumCrssInDeg (long DegCod)
|
|
{
|
|
/***** 1. Fast check: Trivial case *****/
|
|
if (DegCod <= 0)
|
|
return 0;
|
|
|
|
/***** 2. Fast check: If cached... *****/
|
|
if (DegCod == Gbl.Cache.NumCrssInDeg.DegCod)
|
|
return Gbl.Cache.NumCrssInDeg.NumCrss;
|
|
|
|
/***** 3. Slow: number of courses in a degree from database *****/
|
|
Gbl.Cache.NumCrssInDeg.DegCod = DegCod;
|
|
Gbl.Cache.NumCrssInDeg.NumCrss = (unsigned)
|
|
DB_QueryCOUNT ("can not get the number of courses in a degree",
|
|
"SELECT COUNT(*) FROM crs_courses WHERE DegCod=%ld",
|
|
DegCod);
|
|
FigCch_UpdateFigureIntoCache (FigCch_NUM_CRSS,Hie_Lvl_DEG,Gbl.Cache.NumCrssInDeg.DegCod,
|
|
FigCch_UNSIGNED,&Gbl.Cache.NumCrssInDeg.NumCrss);
|
|
return Gbl.Cache.NumCrssInDeg.NumCrss;
|
|
}
|
|
|
|
unsigned Crs_GetCachedNumCrssInDeg (long DegCod)
|
|
{
|
|
unsigned NumCrss;
|
|
|
|
/***** Get number of courses from cache *****/
|
|
if (!FigCch_GetFigureFromCache (FigCch_NUM_CRSS,Hie_Lvl_DEG,DegCod,
|
|
FigCch_UNSIGNED,&NumCrss))
|
|
/***** Get current number of courses from database and update cache *****/
|
|
NumCrss = Crs_GetNumCrssInDeg (DegCod);
|
|
|
|
return NumCrss;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********************* Get number of courses with users **********************/
|
|
/*****************************************************************************/
|
|
|
|
unsigned Crs_GetCachedNumCrssWithUsrs (Rol_Role_t Role,const char *SubQuery,
|
|
Hie_Lvl_Level_t Scope,long Cod)
|
|
{
|
|
static const FigCch_FigureCached_t FigureCrss[Rol_NUM_ROLES] =
|
|
{
|
|
[Rol_STD] = FigCch_NUM_CRSS_WITH_STDS, // Students
|
|
[Rol_NET] = FigCch_NUM_CRSS_WITH_NETS, // Non-editing teachers
|
|
[Rol_TCH] = FigCch_NUM_CRSS_WITH_TCHS, // Teachers
|
|
};
|
|
unsigned NumCrssWithUsrs;
|
|
|
|
/***** Get number of courses with users from cache *****/
|
|
if (!FigCch_GetFigureFromCache (FigureCrss[Role],Scope,Cod,
|
|
FigCch_UNSIGNED,&NumCrssWithUsrs))
|
|
{
|
|
/***** Get current number of courses with users from database and update cache *****/
|
|
NumCrssWithUsrs = (unsigned)
|
|
DB_QueryCOUNT ("can not get number of courses with users",
|
|
"SELECT COUNT(DISTINCT crs_courses.CrsCod)"
|
|
" FROM ins_instits,"
|
|
"ctr_centers,"
|
|
"deg_degrees,"
|
|
"crs_courses,"
|
|
"crs_usr"
|
|
" WHERE %sinstitutions.InsCod=ctr_centers.InsCod"
|
|
" AND ctr_centers.CtrCod=deg_degrees.CtrCod"
|
|
" AND deg_degrees.DegCod=crs_courses.DegCod"
|
|
" AND crs_courses.CrsCod=crs_usr.CrsCod"
|
|
" AND crs_usr.Role=%u",
|
|
SubQuery,(unsigned) Role);
|
|
FigCch_UpdateFigureIntoCache (FigureCrss[Role],Scope,Cod,
|
|
FigCch_UNSIGNED,&NumCrssWithUsrs);
|
|
}
|
|
|
|
return NumCrssWithUsrs;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*************************** 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_BeginFormGoTo (ActSeeCrsInf);
|
|
if (Gbl.Hierarchy.Deg.DegCod > 0)
|
|
HTM_SELECT_Begin (HTM_SUBMIT_ON_CHANGE,
|
|
"id=\"crs\" name=\"crs\" class=\"HIE_SEL\"");
|
|
else
|
|
HTM_SELECT_Begin (HTM_DONT_SUBMIT_ON_CHANGE,
|
|
"id=\"crs\" name=\"crs\" class=\"HIE_SEL\""
|
|
" disabled=\"disabled\"");
|
|
HTM_OPTION (HTM_Type_STRING,"",Gbl.Hierarchy.Crs.CrsCod < 0,true,
|
|
"[%s]",Txt_Course);
|
|
|
|
if (Gbl.Hierarchy.Deg.DegCod > 0)
|
|
{
|
|
/***** Get courses belonging to the current degree from database *****/
|
|
NumCrss = (unsigned)
|
|
DB_QuerySELECT (&mysql_res,"can not get courses of a degree",
|
|
"SELECT CrsCod,"
|
|
"ShortName"
|
|
" FROM crs_courses"
|
|
" WHERE DegCod=%ld"
|
|
" ORDER BY ShortName",
|
|
Gbl.Hierarchy.Deg.DegCod);
|
|
|
|
/***** Get courses of this degree *****/
|
|
for (NumCrs = 0;
|
|
NumCrs < NumCrss;
|
|
NumCrs++)
|
|
{
|
|
/* Get next course */
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
/* Get course code (row[0]) */
|
|
if ((CrsCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
|
|
Lay_ShowErrorAndExit ("Wrong course.");
|
|
|
|
/* Write option */
|
|
HTM_OPTION (HTM_Type_LONG,&CrsCod,
|
|
Gbl.Hierarchy.Level == Hie_Lvl_CRS && // Course selected
|
|
CrsCod == Gbl.Hierarchy.Crs.CrsCod,false,
|
|
"%s",row[1]);
|
|
}
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
}
|
|
|
|
/***** End form *****/
|
|
HTM_SELECT_End ();
|
|
Frm_EndForm ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************** Show courses of a degree *************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_ShowCrssOfCurrentDeg (void)
|
|
{
|
|
/***** Trivial check *****/
|
|
if (Gbl.Hierarchy.Deg.DegCod <= 0) // No degree selected
|
|
return;
|
|
|
|
/***** Get list of courses in this degree *****/
|
|
Crs_GetListCrssInCurrentDeg (Crs_ALL_COURSES_EXCEPT_REMOVED);
|
|
|
|
/***** Write menu to select country, institution, center and degree *****/
|
|
Hie_WriteMenuHierarchy ();
|
|
|
|
/***** Show list of courses *****/
|
|
Crs_ListCourses ();
|
|
|
|
/***** Free list of courses in this degree *****/
|
|
Crs_FreeListCoursesInCurrentDegree ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*************** Create a list with courses in current degree ****************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_GetListCrssInCurrentDeg (Crs_WhatCourses_t WhatCourses)
|
|
{
|
|
MYSQL_RES *mysql_res;
|
|
MYSQL_ROW row;
|
|
unsigned NumCrss;
|
|
unsigned NumCrs;
|
|
struct Crs_Course *Crs;
|
|
|
|
/***** Get courses of a degree from database *****/
|
|
switch (WhatCourses)
|
|
{
|
|
case Crs_ACTIVE_COURSES:
|
|
NumCrss = (unsigned)
|
|
DB_QuerySELECT (&mysql_res,"can not get courses of a degree",
|
|
"SELECT CrsCod,"
|
|
"DegCod,"
|
|
"Year,"
|
|
"InsCrsCod,"
|
|
"Status,"
|
|
"RequesterUsrCod,"
|
|
"ShortName,"
|
|
"FullName"
|
|
" FROM crs_courses"
|
|
" WHERE DegCod=%ld AND Status=0"
|
|
" ORDER BY Year,ShortName",
|
|
Gbl.Hierarchy.Deg.DegCod);
|
|
break;
|
|
case Crs_ALL_COURSES_EXCEPT_REMOVED:
|
|
NumCrss = (unsigned)
|
|
DB_QuerySELECT (&mysql_res,"can not get courses of a degree",
|
|
"SELECT CrsCod,"
|
|
"DegCod,"
|
|
"Year,"
|
|
"InsCrsCod,"
|
|
"Status,"
|
|
"RequesterUsrCod,"
|
|
"ShortName,"
|
|
"FullName"
|
|
" FROM crs_courses"
|
|
" WHERE DegCod=%ld AND (Status & %u)=0"
|
|
" ORDER BY Year,ShortName",
|
|
Gbl.Hierarchy.Deg.DegCod,
|
|
(unsigned) Crs_STATUS_BIT_REMOVED);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (NumCrss) // Courses found...
|
|
{
|
|
/***** Create list with courses in degree *****/
|
|
if ((Gbl.Hierarchy.Crss.Lst = calloc (NumCrss,
|
|
sizeof (*Gbl.Hierarchy.Ctys.Lst))) == NULL)
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
/***** Get the courses in degree *****/
|
|
for (NumCrs = 0;
|
|
NumCrs < NumCrss;
|
|
NumCrs++)
|
|
{
|
|
Crs = &Gbl.Hierarchy.Crss.Lst[NumCrs];
|
|
|
|
/* Get next course */
|
|
row = mysql_fetch_row (mysql_res);
|
|
Crs_GetDataOfCourseFromRow (Crs,row);
|
|
}
|
|
}
|
|
|
|
Gbl.Hierarchy.Crss.Num = NumCrss;
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********************* Free list of courses in this degree *******************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_FreeListCoursesInCurrentDegree (void)
|
|
{
|
|
if (Gbl.Hierarchy.Crss.Lst)
|
|
{
|
|
/***** Free memory used by the list of courses in degree *****/
|
|
free (Gbl.Hierarchy.Crss.Lst);
|
|
Gbl.Hierarchy.Crss.Lst = NULL;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********************** Write selector of my coursess ************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_WriteSelectorMyCoursesInBreadcrumb (void)
|
|
{
|
|
extern const char *Txt_Course;
|
|
unsigned NumMyCrs;
|
|
long CrsCod;
|
|
long DegCod;
|
|
long LastDegCod;
|
|
char CrsShortName[Cns_HIERARCHY_MAX_BYTES_SHRT_NAME + 1];
|
|
char DegShortName[Cns_HIERARCHY_MAX_BYTES_SHRT_NAME + 1];
|
|
|
|
/***** Fill the list with the courses I belong to, if not filled *****/
|
|
if (Gbl.Usrs.Me.Logged)
|
|
Usr_GetMyCourses ();
|
|
|
|
/***** Begin form *****/
|
|
Frm_BeginFormGoTo (Gbl.Usrs.Me.MyCrss.Num ? ActSeeCrsInf :
|
|
ActReqSch);
|
|
|
|
/***** Start selector of courses *****/
|
|
HTM_SELECT_Begin (HTM_SUBMIT_ON_CHANGE,
|
|
"id=\"my_courses\" name=\"crs\"");
|
|
|
|
/***** Write an option when no course selected *****/
|
|
if (Gbl.Hierarchy.Crs.CrsCod <= 0) // No course selected
|
|
HTM_OPTION (HTM_Type_STRING,"-1",true,true,
|
|
"%s",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)
|
|
HTM_OPTGROUP_End ();
|
|
HTM_OPTGROUP_Begin (DegShortName);
|
|
LastDegCod = DegCod;
|
|
}
|
|
|
|
HTM_OPTION (HTM_Type_LONG,&Gbl.Usrs.Me.MyCrss.Crss[NumMyCrs].CrsCod,
|
|
CrsCod == Gbl.Hierarchy.Crs.CrsCod,false, // Course selected
|
|
"%s",CrsShortName);
|
|
}
|
|
|
|
if (LastDegCod > 0)
|
|
HTM_OPTGROUP_End ();
|
|
}
|
|
|
|
/***** Write an option with the current course
|
|
when I don't belong to it *****/
|
|
if (Gbl.Hierarchy.Level == Hie_Lvl_CRS && // Course selected
|
|
!Gbl.Usrs.Me.IBelongToCurrentCrs) // I do not belong to it
|
|
HTM_OPTION (HTM_Type_LONG,&Gbl.Hierarchy.Crs.CrsCod,true,true,
|
|
"%s",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 *****/
|
|
Box_BoxBegin (NULL,Str_BuildStringStr (Txt_Courses_of_DEGREE_X,
|
|
Gbl.Hierarchy.Deg.ShrtName),
|
|
Crs_PutIconsListCourses,NULL,
|
|
Hlp_DEGREE_Courses,Box_NOT_CLOSABLE);
|
|
Str_FreeString ();
|
|
|
|
if (Gbl.Hierarchy.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_BeginForm (ActEdiCrs);
|
|
Btn_PutConfirmButton (Gbl.Hierarchy.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 (__attribute__((unused)) void *Args)
|
|
{
|
|
/***** Put icon to edit courses *****/
|
|
if (Crs_CheckIfICanCreateCourses ())
|
|
Crs_PutIconToEditCourses ();
|
|
|
|
/***** Put icon to show a figure *****/
|
|
Fig_PutIconToShowFigure (Fig_HIERARCHY);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************* Put icon to edit courses **************************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_PutIconToEditCourses (void)
|
|
{
|
|
Ico_PutContextualIconToEdit (ActEdiCrs,NULL,
|
|
NULL,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_COURSE_STATUS[Crs_NUM_STATUS_TXT];
|
|
unsigned NumCrs;
|
|
struct Crs_Course *Crs;
|
|
const char *TxtClassNormal;
|
|
const char *TxtClassStrong;
|
|
const char *BgColor;
|
|
Crs_StatusTxt_t StatusTxt;
|
|
bool ThisYearHasCourses = false;
|
|
unsigned NumUsrs[Rol_NUM_ROLES];
|
|
|
|
/***** Write all the courses of this year *****/
|
|
for (NumCrs = 0;
|
|
NumCrs < Gbl.Hierarchy.Crss.Num;
|
|
NumCrs++)
|
|
{
|
|
Crs = &(Gbl.Hierarchy.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 = "BT_LINK LT DAT_LIGHT";
|
|
}
|
|
else
|
|
{
|
|
TxtClassNormal = "DAT";
|
|
TxtClassStrong = "BT_LINK LT 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);
|
|
|
|
/* Get number of users */
|
|
NumUsrs[Rol_STD] = Usr_GetCachedNumUsrsInCrss (Hie_Lvl_CRS,Crs->CrsCod,1 << Rol_STD);
|
|
NumUsrs[Rol_NET] = Usr_GetCachedNumUsrsInCrss (Hie_Lvl_CRS,Crs->CrsCod,1 << Rol_NET);
|
|
NumUsrs[Rol_TCH] = Usr_GetCachedNumUsrsInCrss (Hie_Lvl_CRS,Crs->CrsCod,1 << Rol_TCH);
|
|
NumUsrs[Rol_UNK] = NumUsrs[Rol_STD] +
|
|
NumUsrs[Rol_NET] +
|
|
NumUsrs[Rol_TCH];
|
|
|
|
/* Put green tip if course has users */
|
|
HTM_TD_Begin ("class=\"%s CM %s\" title=\"%s\"",
|
|
TxtClassNormal,BgColor,
|
|
NumUsrs[Rol_UNK] ? Txt_COURSE_With_users :
|
|
Txt_COURSE_Without_users);
|
|
HTM_Txt (NumUsrs[Rol_UNK] ? "✓" :
|
|
" ");
|
|
HTM_TD_End ();
|
|
|
|
/* Institutional code of the course */
|
|
HTM_TD_Begin ("class=\"%s CM %s\"",TxtClassNormal,BgColor);
|
|
HTM_Txt (Crs->InstitutionalCrsCod);
|
|
HTM_TD_End ();
|
|
|
|
/* Course year */
|
|
HTM_TD_Begin ("class=\"%s CM %s\"",TxtClassNormal,BgColor);
|
|
HTM_Txt (Txt_YEAR_OF_DEGREE[Crs->Year]);
|
|
HTM_TD_End ();
|
|
|
|
/* Course full name */
|
|
HTM_TD_Begin ("class=\"%s LM %s\"",TxtClassStrong,BgColor);
|
|
Frm_BeginFormGoTo (ActSeeCrsInf);
|
|
Crs_PutParamCrsCod (Crs->CrsCod);
|
|
HTM_BUTTON_SUBMIT_Begin (Hie_BuildGoToMsg (Crs->FullName),
|
|
TxtClassStrong,NULL);
|
|
Hie_FreeGoToMsg ();
|
|
HTM_Txt (Crs->FullName);
|
|
HTM_BUTTON_End ();
|
|
Frm_EndForm ();
|
|
HTM_TD_End ();
|
|
|
|
/* Number of teachers in this course */
|
|
HTM_TD_Begin ("class=\"%s RM %s\"",TxtClassNormal,BgColor);
|
|
HTM_Unsigned (NumUsrs[Rol_TCH] +
|
|
NumUsrs[Rol_NET]);
|
|
HTM_TD_End ();
|
|
|
|
/* Number of students in this course */
|
|
HTM_TD_Begin ("class=\"%s RM %s\"",TxtClassNormal,BgColor);
|
|
HTM_Unsigned (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
|
|
HTM_Txt (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 center *****/
|
|
Deg_GetListDegsInCurrentCtr ();
|
|
|
|
/***** Get list of courses in this degree *****/
|
|
Crs_GetListCrssInCurrentDeg (Crs_ALL_COURSES_EXCEPT_REMOVED);
|
|
|
|
/***** Write menu to select country, institution, center and degree *****/
|
|
Hie_WriteMenuHierarchy ();
|
|
|
|
/***** Begin box *****/
|
|
Box_BoxBegin (NULL,Str_BuildStringStr (Txt_Courses_of_DEGREE_X,
|
|
Gbl.Hierarchy.Deg.ShrtName),
|
|
Crs_PutIconsEditingCourses,NULL,
|
|
Hlp_DEGREE_Courses,Box_NOT_CLOSABLE);
|
|
Str_FreeString ();
|
|
|
|
/***** Put a form to create or request a new course *****/
|
|
Crs_PutFormToCreateCourse ();
|
|
|
|
/***** Forms to edit current courses *****/
|
|
if (Gbl.Hierarchy.Crss.Num)
|
|
Crs_ListCoursesForEdition ();
|
|
|
|
/***** End box *****/
|
|
Box_BoxEnd ();
|
|
|
|
/***** Free list of courses in this degree *****/
|
|
Crs_FreeListCoursesInCurrentDegree ();
|
|
|
|
/***** Free list of degrees in this center *****/
|
|
Deg_FreeListDegs (&Gbl.Hierarchy.Degs);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**************** Put contextual icons in edition of courses *****************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_PutIconsEditingCourses (__attribute__((unused)) void *Args)
|
|
{
|
|
/***** Put icon to view degrees *****/
|
|
Crs_PutIconToViewCourses ();
|
|
|
|
/***** Put icon to show a figure *****/
|
|
Fig_PutIconToShowFigure (Fig_HIERARCHY);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************* Put icon to view courses **************************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_PutIconToViewCourses (void)
|
|
{
|
|
extern const char *Txt_Courses;
|
|
|
|
Lay_PutContextualLinkOnlyIcon (ActSeeCrs,NULL,
|
|
NULL,NULL,
|
|
"chalkboard-teacher.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 Crs_Course *Crs;
|
|
unsigned YearAux;
|
|
unsigned NumCrs;
|
|
struct UsrData UsrDat;
|
|
bool ICanEdit;
|
|
unsigned NumUsrs[Rol_NUM_ROLES];
|
|
Crs_StatusTxt_t StatusTxt;
|
|
unsigned StatusUnsigned;
|
|
|
|
/***** Initialize structure with user's data *****/
|
|
Usr_UsrDataConstructor (&UsrDat);
|
|
|
|
/***** List courses of a given year *****/
|
|
for (NumCrs = 0;
|
|
NumCrs < Gbl.Hierarchy.Crss.Num;
|
|
NumCrs++)
|
|
{
|
|
Crs = &(Gbl.Hierarchy.Crss.Lst[NumCrs]);
|
|
if (Crs->Year == Year)
|
|
{
|
|
ICanEdit = Crs_CheckIfICanEdit (Crs);
|
|
|
|
/* Get number of users */
|
|
NumUsrs[Rol_STD] = Usr_GetNumUsrsInCrss (Hie_Lvl_CRS,Crs->CrsCod,1 << Rol_STD);
|
|
NumUsrs[Rol_NET] = Usr_GetNumUsrsInCrss (Hie_Lvl_CRS,Crs->CrsCod,1 << Rol_NET);
|
|
NumUsrs[Rol_TCH] = Usr_GetNumUsrsInCrss (Hie_Lvl_CRS,Crs->CrsCod,1 << Rol_TCH);
|
|
NumUsrs[Rol_UNK] = NumUsrs[Rol_STD] +
|
|
NumUsrs[Rol_NET] +
|
|
NumUsrs[Rol_TCH];
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
/* Put icon to remove course */
|
|
HTM_TD_Begin ("class=\"BM\"");
|
|
if (NumUsrs[Rol_UNK] || // Course has users ==> deletion forbidden
|
|
!ICanEdit)
|
|
Ico_PutIconRemovalNotAllowed ();
|
|
else // Crs->NumUsrs == 0 && ICanEdit
|
|
Ico_PutContextualIconToRemove (ActRemCrs,NULL,
|
|
Crs_PutParamOtherCrsCod,&Crs->CrsCod);
|
|
HTM_TD_End ();
|
|
|
|
/* Course code */
|
|
HTM_TD_Begin ("class=\"DAT CODE\"");
|
|
HTM_Long (Crs->CrsCod);
|
|
HTM_TD_End ();
|
|
|
|
/* Institutional code of the course */
|
|
HTM_TD_Begin ("class=\"DAT CM\"");
|
|
if (ICanEdit)
|
|
{
|
|
Frm_BeginForm (ActChgInsCrsCod);
|
|
Crs_PutParamOtherCrsCod (&Crs->CrsCod);
|
|
HTM_INPUT_TEXT ("InsCrsCod",Crs_MAX_CHARS_INSTITUTIONAL_CRS_COD,
|
|
Crs->InstitutionalCrsCod,HTM_SUBMIT_ON_CHANGE,
|
|
"class=\"INPUT_INS_CODE\"");
|
|
Frm_EndForm ();
|
|
}
|
|
else
|
|
HTM_Txt (Crs->InstitutionalCrsCod);
|
|
HTM_TD_End ();
|
|
|
|
/* Course year */
|
|
HTM_TD_Begin ("class=\"DAT CM\"");
|
|
if (ICanEdit)
|
|
{
|
|
Frm_BeginForm (ActChgCrsYea);
|
|
Crs_PutParamOtherCrsCod (&Crs->CrsCod);
|
|
HTM_SELECT_Begin (HTM_SUBMIT_ON_CHANGE,
|
|
"name=\"OthCrsYear\" class=\"HIE_SEL_NARROW\"");
|
|
for (YearAux = 0;
|
|
YearAux <= Deg_MAX_YEARS_PER_DEGREE;
|
|
YearAux++) // All the years are permitted because it's possible to move this course to another degree (with other active years)
|
|
HTM_OPTION (HTM_Type_UNSIGNED,&YearAux,
|
|
YearAux == Crs->Year,false,
|
|
"%s",Txt_YEAR_OF_DEGREE[YearAux]);
|
|
HTM_SELECT_End ();
|
|
Frm_EndForm ();
|
|
}
|
|
else
|
|
HTM_Txt (Txt_YEAR_OF_DEGREE[Crs->Year]);
|
|
HTM_TD_End ();
|
|
|
|
/* Course short name */
|
|
HTM_TD_Begin ("class=\"DAT LM\"");
|
|
if (ICanEdit)
|
|
{
|
|
Frm_BeginForm (ActRenCrsSho);
|
|
Crs_PutParamOtherCrsCod (&Crs->CrsCod);
|
|
HTM_INPUT_TEXT ("ShortName",Cns_HIERARCHY_MAX_CHARS_SHRT_NAME,Crs->ShrtName,
|
|
HTM_SUBMIT_ON_CHANGE,
|
|
"class=\"INPUT_SHORT_NAME\"");
|
|
Frm_EndForm ();
|
|
}
|
|
else
|
|
HTM_Txt (Crs->ShrtName);
|
|
HTM_TD_End ();
|
|
|
|
/* Course full name */
|
|
HTM_TD_Begin ("class=\"DAT LM\"");
|
|
if (ICanEdit)
|
|
{
|
|
Frm_BeginForm (ActRenCrsFul);
|
|
Crs_PutParamOtherCrsCod (&Crs->CrsCod);
|
|
HTM_INPUT_TEXT ("FullName",Cns_HIERARCHY_MAX_CHARS_FULL_NAME,Crs->FullName,
|
|
HTM_SUBMIT_ON_CHANGE,
|
|
"class=\"INPUT_FULL_NAME\"");
|
|
Frm_EndForm ();
|
|
}
|
|
else
|
|
HTM_Txt (Crs->FullName);
|
|
HTM_TD_End ();
|
|
|
|
/* Current number of teachers in this course */
|
|
HTM_TD_Begin ("class=\"DAT RM\"");
|
|
HTM_Unsigned (NumUsrs[Rol_TCH] +
|
|
NumUsrs[Rol_NET]);
|
|
HTM_TD_End ();
|
|
|
|
/* Current number of students in this course */
|
|
HTM_TD_Begin ("class=\"DAT RM\"");
|
|
HTM_Unsigned (NumUsrs[Rol_STD]);
|
|
HTM_TD_End ();
|
|
|
|
/* Course requester */
|
|
UsrDat.UsrCod = Crs->RequesterUsrCod;
|
|
Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat,Usr_DONT_GET_PREFS);
|
|
HTM_TD_Begin ("class=\"DAT INPUT_REQUESTER LT\"");
|
|
Msg_WriteMsgAuthor (&UsrDat,true,NULL);
|
|
HTM_TD_End ();
|
|
|
|
/* Course status */
|
|
StatusTxt = Crs_GetStatusTxtFromStatusBits (Crs->Status);
|
|
HTM_TD_Begin ("class=\"DAT LM\"");
|
|
if (Gbl.Usrs.Me.Role.Logged >= Rol_DEG_ADM &&
|
|
StatusTxt == Crs_STATUS_PENDING)
|
|
{
|
|
Frm_BeginForm (ActChgCrsSta);
|
|
Crs_PutParamOtherCrsCod (&Crs->CrsCod);
|
|
HTM_SELECT_Begin (HTM_SUBMIT_ON_CHANGE,
|
|
"name=\"Status\" class=\"INPUT_STATUS\"");
|
|
|
|
StatusUnsigned = (unsigned) Crs_GetStatusBitsFromStatusTxt (Crs_STATUS_PENDING);
|
|
HTM_OPTION (HTM_Type_UNSIGNED,&StatusUnsigned,true,false,
|
|
"%s",Txt_COURSE_STATUS[Crs_STATUS_PENDING]);
|
|
|
|
StatusUnsigned = (unsigned) Crs_GetStatusBitsFromStatusTxt (Crs_STATUS_ACTIVE);
|
|
HTM_OPTION (HTM_Type_UNSIGNED,&StatusUnsigned,false,false,
|
|
"%s",Txt_COURSE_STATUS[Crs_STATUS_ACTIVE]);
|
|
|
|
HTM_SELECT_End ();
|
|
Frm_EndForm ();
|
|
}
|
|
else if (StatusTxt != Crs_STATUS_ACTIVE) // If active ==> do not show anything
|
|
HTM_Txt (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 Crs_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_BeginForm (ActNewCrs);
|
|
else if (Gbl.Usrs.Me.Role.Max >= Rol_GST)
|
|
Frm_BeginForm (ActReqCrs);
|
|
else
|
|
Lay_NoPermissionExit ();
|
|
|
|
/***** Begin box and table *****/
|
|
Box_BoxTableBegin (NULL,Txt_New_course,
|
|
NULL,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,
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
|
"class=\"INPUT_INS_CODE\"");
|
|
HTM_TD_End ();
|
|
|
|
/***** Year *****/
|
|
HTM_TD_Begin ("class=\"CM\"");
|
|
HTM_SELECT_Begin (HTM_DONT_SUBMIT_ON_CHANGE,
|
|
"name=\"OthCrsYear\" class=\"HIE_SEL_NARROW\"");
|
|
for (Year = 0;
|
|
Year <= Deg_MAX_YEARS_PER_DEGREE;
|
|
Year++)
|
|
HTM_OPTION (HTM_Type_UNSIGNED,&Year,
|
|
Year == Crs_EditingCrs->Year,false,
|
|
"%s",Txt_YEAR_OF_DEGREE[Year]);
|
|
HTM_SELECT_End ();
|
|
HTM_TD_End ();
|
|
|
|
/***** Course short name *****/
|
|
HTM_TD_Begin ("class=\"LM\"");
|
|
HTM_INPUT_TEXT ("ShortName",Cns_HIERARCHY_MAX_CHARS_SHRT_NAME,Crs_EditingCrs->ShrtName,
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
|
"class=\"INPUT_SHORT_NAME\" required=\"required\"");
|
|
HTM_TD_End ();
|
|
|
|
/***** Course full name *****/
|
|
HTM_TD_Begin ("class=\"LM\"");
|
|
HTM_INPUT_TEXT ("FullName",Cns_HIERARCHY_MAX_CHARS_FULL_NAME,Crs_EditingCrs->FullName,
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
|
"class=\"INPUT_FULL_NAME\" required=\"required\"");
|
|
HTM_TD_End ();
|
|
|
|
/***** Current number of teachers in this course *****/
|
|
HTM_TD_Begin ("class=\"DAT RM\"");
|
|
HTM_Unsigned (0);
|
|
HTM_TD_End ();
|
|
|
|
/***** Current number of students in this course *****/
|
|
HTM_TD_Begin ("class=\"DAT RM\"");
|
|
HTM_Unsigned (0);
|
|
HTM_TD_End ();
|
|
|
|
/***** Course requester *****/
|
|
HTM_TD_Begin ("class=\"DAT INPUT_REQUESTER LT\"");
|
|
Msg_WriteMsgAuthor (&Gbl.Usrs.Me.UsrDat,true,NULL);
|
|
HTM_TD_End ();
|
|
|
|
/***** Course status *****/
|
|
HTM_TD_Begin ("class=\"DAT LM\"");
|
|
HTM_TD_End ();
|
|
|
|
HTM_TR_End ();
|
|
|
|
/***** End table, send button and end box *****/
|
|
Box_BoxTableWithButtonEnd (Btn_CREATE_BUTTON,Txt_Create_course);
|
|
|
|
/***** End form *****/
|
|
Frm_EndForm ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/******************** Write header with fields of a course *******************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_PutHeadCoursesForSeeing (void)
|
|
{
|
|
extern const char *Txt_Institutional_BR_code;
|
|
extern const char *Txt_Year_OF_A_DEGREE;
|
|
extern const char *Txt_Course;
|
|
extern const char *Txt_ROLES_PLURAL_BRIEF_Abc[Rol_NUM_ROLES];
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
HTM_TH (1,1,"BM",NULL);
|
|
HTM_TH (1,1,"CM",Txt_Institutional_BR_code);
|
|
HTM_TH (1,1,"CM",Txt_Year_OF_A_DEGREE);
|
|
HTM_TH (1,1,"LM",Txt_Course);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_TCH]);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_STD]);
|
|
HTM_TH_Empty (1);
|
|
|
|
HTM_TR_End ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/******************** Write header with fields of a course *******************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_PutHeadCoursesForEdition (void)
|
|
{
|
|
extern const char *Txt_Code;
|
|
extern const char *Txt_Institutional_code;
|
|
extern const char *Txt_optional;
|
|
extern const char *Txt_Year_OF_A_DEGREE;
|
|
extern const char *Txt_Short_name_of_the_course;
|
|
extern const char *Txt_Full_name_of_the_course;
|
|
extern const char *Txt_ROLES_PLURAL_BRIEF_Abc[Rol_NUM_ROLES];
|
|
extern const char *Txt_Requester;
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
HTM_TH (1,1,"BM",NULL);
|
|
HTM_TH (1,1,"RM",Txt_Code);
|
|
HTM_TH_Begin (1,1,"CM");
|
|
HTM_TxtF ("%s (%s)",Txt_Institutional_code,Txt_optional);
|
|
HTM_TH_End ();
|
|
HTM_TH (1,1,"CM",Txt_Year_OF_A_DEGREE);
|
|
HTM_TH (1,1,"LM",Txt_Short_name_of_the_course);
|
|
HTM_TH (1,1,"LM",Txt_Full_name_of_the_course);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_TCH]);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_STD]);
|
|
HTM_TH (1,1,"LM",Txt_Requester);
|
|
HTM_TH_Empty (1);
|
|
|
|
HTM_TR_End ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/****************** Receive form to request a new course *********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_ReceiveFormReqCrs (void)
|
|
{
|
|
/***** Course constructor *****/
|
|
Crs_EditingCourseConstructor ();
|
|
|
|
/***** Receive form to request a new course *****/
|
|
Crs_ReceiveFormRequestOrCreateCrs ((unsigned) Crs_STATUS_BIT_PENDING);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/******************* Receive form to create a new course *********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_ReceiveFormNewCrs (void)
|
|
{
|
|
/***** Course constructor *****/
|
|
Crs_EditingCourseConstructor ();
|
|
|
|
/***** Receive form to create a new course *****/
|
|
Crs_ReceiveFormRequestOrCreateCrs (0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************* Receive form to request or create a new course ****************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_ReceiveFormRequestOrCreateCrs (unsigned Status)
|
|
{
|
|
extern const char *Txt_The_course_X_already_exists;
|
|
extern const char *Txt_Created_new_course_X;
|
|
extern const char *Txt_You_must_specify_the_short_name_and_the_full_name_of_the_new_course;
|
|
extern const char *Txt_The_year_X_is_not_allowed;
|
|
extern const char *Txt_YEAR_OF_DEGREE[1 + Deg_MAX_YEARS_PER_DEGREE];
|
|
|
|
/***** Get parameters from form *****/
|
|
/* Set course degree */
|
|
// Deg.DegCod =
|
|
Crs_EditingCrs->DegCod = Gbl.Hierarchy.Deg.DegCod;
|
|
|
|
/* Get parameters of the new course */
|
|
Crs_GetParamsNewCourse (Crs_EditingCrs);
|
|
|
|
/***** Check if year is correct *****/
|
|
if (Crs_EditingCrs->Year <= Deg_MAX_YEARS_PER_DEGREE) // If year is valid
|
|
{
|
|
if (Crs_EditingCrs->ShrtName[0] &&
|
|
Crs_EditingCrs->FullName[0]) // If there's a course name
|
|
{
|
|
/***** If name of course was in database... *****/
|
|
if (Crs_CheckIfCrsNameExistsInYearOfDeg ("ShortName",Crs_EditingCrs->ShrtName,
|
|
-1L,Crs_EditingCrs->DegCod,Crs_EditingCrs->Year))
|
|
Ale_CreateAlert (Ale_WARNING,NULL,
|
|
Txt_The_course_X_already_exists,
|
|
Crs_EditingCrs->ShrtName);
|
|
else if (Crs_CheckIfCrsNameExistsInYearOfDeg ("FullName",Crs_EditingCrs->FullName,
|
|
-1L,Crs_EditingCrs->DegCod,Crs_EditingCrs->Year))
|
|
Ale_CreateAlert (Ale_WARNING,NULL,
|
|
Txt_The_course_X_already_exists,
|
|
Crs_EditingCrs->FullName);
|
|
else // Add new requested course to database
|
|
{
|
|
Crs_CreateCourse (Status);
|
|
Ale_CreateAlert (Ale_SUCCESS,NULL,
|
|
Txt_Created_new_course_X,
|
|
Crs_EditingCrs->FullName);
|
|
}
|
|
}
|
|
else // If there is not a course name
|
|
Ale_CreateAlert (Ale_WARNING,NULL,
|
|
Txt_You_must_specify_the_short_name_and_the_full_name_of_the_new_course);
|
|
}
|
|
else // Year not valid
|
|
Ale_CreateAlert (Ale_WARNING,NULL,
|
|
Txt_The_year_X_is_not_allowed,
|
|
Crs_EditingCrs->Year);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************** Get the parameters of a new course from form *****************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_GetParamsNewCourse (struct Crs_Course *Crs)
|
|
{
|
|
char YearStr[2 + 1];
|
|
|
|
/***** Get parameters of the course from form *****/
|
|
/* Get institutional code */
|
|
Par_GetParToText ("InsCrsCod",Crs->InstitutionalCrsCod,Crs_MAX_BYTES_INSTITUTIONAL_CRS_COD);
|
|
|
|
/* Get year */
|
|
Par_GetParToText ("OthCrsYear",YearStr,2);
|
|
Crs->Year = Deg_ConvStrToYear (YearStr);
|
|
|
|
/* Get course short name */
|
|
Par_GetParToText ("ShortName",Crs->ShrtName,Cns_HIERARCHY_MAX_BYTES_SHRT_NAME);
|
|
|
|
/* Get course full name */
|
|
Par_GetParToText ("FullName",Crs->FullName,Cns_HIERARCHY_MAX_BYTES_FULL_NAME);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************* Add a new requested course to pending requests ****************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_CreateCourse (unsigned Status)
|
|
{
|
|
/***** Insert new course into pending requests *****/
|
|
Crs_EditingCrs->CrsCod =
|
|
DB_QueryINSERTandReturnCode ("can not create a new course",
|
|
"INSERT INTO crs_courses"
|
|
" (DegCod,Year,InsCrsCod,Status,RequesterUsrCod,"
|
|
"ShortName,FullName)"
|
|
" VALUES"
|
|
" (%ld,%u,'%s',%u,%ld,"
|
|
"'%s','%s')",
|
|
Crs_EditingCrs->DegCod,Crs_EditingCrs->Year,
|
|
Crs_EditingCrs->InstitutionalCrsCod,
|
|
Status,
|
|
Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
Crs_EditingCrs->ShrtName,
|
|
Crs_EditingCrs->FullName);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/****************************** Remove a course ******************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_RemoveCourse (void)
|
|
{
|
|
extern const char *Txt_To_remove_a_course_you_must_first_remove_all_users_in_the_course;
|
|
extern const char *Txt_Course_X_removed;
|
|
|
|
/***** Course constructor *****/
|
|
Crs_EditingCourseConstructor ();
|
|
|
|
/***** Get course code *****/
|
|
Crs_EditingCrs->CrsCod = Crs_GetAndCheckParamOtherCrsCod (1);
|
|
|
|
/***** Get data of the course from database *****/
|
|
Crs_GetDataOfCourseByCod (Crs_EditingCrs);
|
|
|
|
if (Crs_CheckIfICanEdit (Crs_EditingCrs))
|
|
{
|
|
/***** Check if this course has users *****/
|
|
if (Usr_GetNumUsrsInCrss (Hie_Lvl_CRS,Crs_EditingCrs->CrsCod,
|
|
1 << Rol_STD |
|
|
1 << Rol_NET |
|
|
1 << Rol_TCH)) // Course has users ==> don't remove
|
|
Ale_ShowAlert (Ale_WARNING,
|
|
Txt_To_remove_a_course_you_must_first_remove_all_users_in_the_course);
|
|
else // Course has no users ==> remove it
|
|
{
|
|
/***** Remove course *****/
|
|
Crs_RemoveCourseCompletely (Crs_EditingCrs->CrsCod);
|
|
|
|
/***** Write message to show the change made *****/
|
|
Ale_ShowAlert (Ale_SUCCESS,Txt_Course_X_removed,
|
|
Crs_EditingCrs->FullName);
|
|
|
|
Crs_EditingCrs->CrsCod = -1L; // To not showing button to go to course
|
|
}
|
|
}
|
|
else
|
|
Lay_NoPermissionExit ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********************* Get data of a course from its code ********************/
|
|
/*****************************************************************************/
|
|
|
|
bool Crs_GetDataOfCourseByCod (struct Crs_Course *Crs)
|
|
{
|
|
MYSQL_RES *mysql_res;
|
|
MYSQL_ROW row;
|
|
bool CrsFound = false;
|
|
|
|
/***** Clear data *****/
|
|
Crs->DegCod = -1L;
|
|
Crs->Year = 0;
|
|
Crs->Status = (Crs_Status_t) 0;
|
|
Crs->RequesterUsrCod = -1L;
|
|
Crs->ShrtName[0] = '\0';
|
|
Crs->FullName[0] = '\0';
|
|
|
|
/***** Check if course code is correct *****/
|
|
if (Crs->CrsCod > 0)
|
|
{
|
|
/***** Get data of a course from database *****/
|
|
if (DB_QuerySELECT (&mysql_res,"can not get data of a course",
|
|
"SELECT CrsCod," // row[0]
|
|
"DegCod," // row[1]
|
|
"Year," // row[2]
|
|
"InsCrsCod," // row[3]
|
|
"Status," // row[4]
|
|
"RequesterUsrCod," // row[5]
|
|
"ShortName," // row[6]
|
|
"FullName" // row[7]
|
|
" FROM crs_courses"
|
|
" WHERE CrsCod=%ld",
|
|
Crs->CrsCod)) // Course found...
|
|
{
|
|
/***** Get data of the course *****/
|
|
row = mysql_fetch_row (mysql_res);
|
|
Crs_GetDataOfCourseFromRow (Crs,row);
|
|
|
|
/* Set return value */
|
|
CrsFound = true;
|
|
}
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
}
|
|
|
|
return CrsFound;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********** Get data of a course from a row resulting of a query *************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_GetDataOfCourseFromRow (struct Crs_Course *Crs,MYSQL_ROW row)
|
|
{
|
|
/***** Get course code (row[0]) *****/
|
|
if ((Crs->CrsCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
|
|
Lay_ShowErrorAndExit ("Wrong code of course.");
|
|
|
|
/***** Get code of degree (row[1]) *****/
|
|
Crs->DegCod = Str_ConvertStrCodToLongCod (row[1]);
|
|
|
|
/***** Get year (row[2]) *****/
|
|
Crs->Year = Deg_ConvStrToYear (row[2]);
|
|
|
|
/***** Get institutional course code (row[3]) *****/
|
|
Str_Copy (Crs->InstitutionalCrsCod,row[3],sizeof (Crs->InstitutionalCrsCod) - 1);
|
|
|
|
/***** Get course status (row[4]) *****/
|
|
if (sscanf (row[4],"%u",&(Crs->Status)) != 1)
|
|
Lay_ShowErrorAndExit ("Wrong course status.");
|
|
|
|
/***** Get requester user'code (row[5]) *****/
|
|
Crs->RequesterUsrCod = Str_ConvertStrCodToLongCod (row[5]);
|
|
|
|
/***** Get short name (row[6]) and full name (row[7]) of the course *****/
|
|
Str_Copy (Crs->ShrtName,row[6],sizeof (Crs->ShrtName) - 1);
|
|
Str_Copy (Crs->FullName,row[7],sizeof (Crs->FullName) - 1);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/******* Get the short names of degree and course from a course code *********/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_GetShortNamesByCod (long CrsCod,
|
|
char CrsShortName[Cns_HIERARCHY_MAX_BYTES_SHRT_NAME + 1],
|
|
char DegShortName[Cns_HIERARCHY_MAX_BYTES_SHRT_NAME + 1])
|
|
{
|
|
MYSQL_RES *mysql_res;
|
|
MYSQL_ROW row;
|
|
|
|
DegShortName[0] = CrsShortName[0] = '\0';
|
|
|
|
if (CrsCod > 0)
|
|
{
|
|
/***** Get the short name of a degree from database *****/
|
|
if (DB_QuerySELECT (&mysql_res,"can not get the short name of a course",
|
|
"SELECT crs_courses.ShortName,"
|
|
"deg_degrees.ShortName"
|
|
" FROM crs_courses,"
|
|
"deg_degrees"
|
|
" WHERE crs_courses.CrsCod=%ld"
|
|
" AND crs_courses.DegCod=deg_degrees.DegCod",
|
|
CrsCod) == 1)
|
|
{
|
|
/***** Get the course short name and degree short name *****/
|
|
row = mysql_fetch_row (mysql_res);
|
|
Str_Copy (CrsShortName,row[0],Cns_HIERARCHY_MAX_BYTES_SHRT_NAME);
|
|
Str_Copy (DegShortName,row[1],Cns_HIERARCHY_MAX_BYTES_SHRT_NAME);
|
|
}
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/****************************** Remove a course ******************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_RemoveCourseCompletely (long CrsCod)
|
|
{
|
|
if (CrsCod > 0)
|
|
{
|
|
/***** Empty course *****/
|
|
Crs_EmptyCourseCompletely (CrsCod);
|
|
|
|
/***** Remove course from table of last accesses to courses in database *****/
|
|
DB_QueryDELETE ("can not remove a course",
|
|
"DELETE FROM crs_last WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
/***** Remove course from table of courses in database *****/
|
|
DB_QueryDELETE ("can not remove a course",
|
|
"DELETE FROM crs_courses WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********** Empty a course (remove all its information and users) ************/
|
|
/*****************************************************************************/
|
|
// Start removing less important things to more important things;
|
|
// so, in case of failure, important things can been removed in the future
|
|
|
|
static void Crs_EmptyCourseCompletely (long CrsCod)
|
|
{
|
|
struct Crs_Course Crs;
|
|
char PathRelCrs[PATH_MAX + 1];
|
|
|
|
if (CrsCod > 0)
|
|
{
|
|
/***** Get course data *****/
|
|
Crs.CrsCod = CrsCod;
|
|
Crs_GetDataOfCourseByCod (&Crs);
|
|
|
|
/***** Remove all the students in the course *****/
|
|
Enr_RemAllStdsInCrs (&Crs);
|
|
|
|
/***** Set all the notifications from the course as removed,
|
|
except notifications about new messages *****/
|
|
Ntf_MarkNotifInCrsAsRemoved (-1L,CrsCod);
|
|
|
|
/***** Remove information of the course ****/
|
|
/* Remove timetable of the course */
|
|
DB_QueryDELETE ("can not remove the timetable of a course",
|
|
"DELETE FROM timetable_crs WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
/* Remove other information of the course */
|
|
DB_QueryDELETE ("can not remove info sources of a course",
|
|
"DELETE FROM crs_info_src WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
DB_QueryDELETE ("can not remove info of a course",
|
|
"DELETE FROM crs_info_txt WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
/***** Remove exam announcements in the course *****/
|
|
/* Mark all exam announcements in the course as deleted */
|
|
DB_QueryUPDATE ("can not remove exam announcements of a course",
|
|
"UPDATE exam_announcements SET Status=%u"
|
|
" WHERE CrsCod=%ld",
|
|
(unsigned) ExaAnn_DELETED_EXAM_ANNOUNCEMENT,CrsCod);
|
|
|
|
/***** Remove course cards of the course *****/
|
|
/* Remove content of course cards */
|
|
DB_QueryDELETE ("can not remove content of cards in a course",
|
|
"DELETE FROM crs_records"
|
|
" USING crs_record_fields,crs_records"
|
|
" WHERE crs_record_fields.CrsCod=%ld"
|
|
" AND crs_record_fields.FieldCod=crs_records.FieldCod",
|
|
CrsCod);
|
|
|
|
/* Remove definition of fields in course cards */
|
|
DB_QueryDELETE ("can not remove fields of cards in a course",
|
|
"DELETE FROM crs_record_fields WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
/***** Remove information related to files in course,
|
|
including groups and projects,
|
|
so this function must be called
|
|
before removing groups and projects *****/
|
|
Brw_RemoveCrsFilesFromDB (CrsCod);
|
|
|
|
/***** Remove assignments of the course *****/
|
|
Asg_RemoveCrsAssignments (CrsCod);
|
|
|
|
/***** Remove projects of the course *****/
|
|
Prj_RemoveCrsProjects (CrsCod);
|
|
|
|
/***** Remove attendance events of the course *****/
|
|
Att_RemoveCrsAttEvents (CrsCod);
|
|
|
|
/***** Remove notices in the course *****/
|
|
/* Copy all notices from the course to table of deleted notices */
|
|
DB_QueryINSERT ("can not remove notices in a course",
|
|
"INSERT INTO notices_deleted"
|
|
" (NotCod,CrsCod,UsrCod,CreatTime,Content,NumNotif)"
|
|
" SELECT NotCod,CrsCod,UsrCod,CreatTime,Content,NumNotif"
|
|
" FROM notices"
|
|
" WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
/* Remove all notices from the course */
|
|
DB_QueryDELETE ("can not remove notices in a course",
|
|
"DELETE FROM notices WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
/***** Remove all the threads and posts in forums of the course *****/
|
|
For_RemoveForums (Hie_Lvl_CRS,CrsCod);
|
|
|
|
/***** Remove all surveys in the course *****/
|
|
Svy_RemoveSurveys (Hie_Lvl_CRS,CrsCod);
|
|
|
|
/***** Remove all games in the course *****/
|
|
Gam_RemoveCrsGames (CrsCod);
|
|
|
|
/***** Remove all exams in the course *****/
|
|
Exa_RemoveCrsExams (CrsCod);
|
|
|
|
/***** Remove all tests in the course *****/
|
|
Tst_RemoveCrsTests (CrsCod);
|
|
|
|
/***** Remove groups in the course *****/
|
|
/* Remove all the users in groups in the course */
|
|
DB_QueryDELETE ("can not remove users from groups of a course",
|
|
"DELETE FROM crs_grp_usr"
|
|
" USING crs_grp_types,crs_grp,crs_grp_usr"
|
|
" WHERE crs_grp_types.CrsCod=%ld"
|
|
" AND crs_grp_types.GrpTypCod=crs_grp.GrpTypCod"
|
|
" AND crs_grp.GrpCod=crs_grp_usr.GrpCod",
|
|
CrsCod);
|
|
|
|
/* Remove all the groups in the course */
|
|
DB_QueryDELETE ("can not remove groups of a course",
|
|
"DELETE FROM crs_grp"
|
|
" USING crs_grp_types,crs_grp"
|
|
" WHERE crs_grp_types.CrsCod=%ld"
|
|
" AND crs_grp_types.GrpTypCod=crs_grp.GrpTypCod",
|
|
CrsCod);
|
|
|
|
/* Remove all the group types in the course */
|
|
DB_QueryDELETE ("can not remove types of group of a course",
|
|
"DELETE FROM crs_grp_types WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
/***** Remove users' requests for inscription in the course *****/
|
|
DB_QueryDELETE ("can not remove requests for inscription to a course",
|
|
"DELETE FROM crs_usr_requests WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
/***** Remove possible users remaining in the course (teachers) *****/
|
|
DB_QueryDELETE ("can not remove users from a course",
|
|
"DELETE FROM crs_usr_last WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
DB_QueryDELETE ("can not remove users from a course",
|
|
"DELETE FROM crs_usr WHERE CrsCod=%ld",
|
|
CrsCod);
|
|
|
|
/***** Remove directories of the course *****/
|
|
snprintf (PathRelCrs,sizeof (PathRelCrs),"%s/%ld",
|
|
Cfg_PATH_CRS_PRIVATE,CrsCod);
|
|
Fil_RemoveTree (PathRelCrs);
|
|
snprintf (PathRelCrs,sizeof (PathRelCrs),"%s/%ld",
|
|
Cfg_PATH_CRS_PUBLIC,CrsCod);
|
|
Fil_RemoveTree (PathRelCrs);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************** Change the institutional code of a course ********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_ChangeInsCrsCod (void)
|
|
{
|
|
extern const char *Txt_The_institutional_code_of_the_course_X_has_changed_to_Y;
|
|
extern const char *Txt_The_institutional_code_of_the_course_X_has_not_changed;
|
|
char NewInstitutionalCrsCod[Crs_MAX_BYTES_INSTITUTIONAL_CRS_COD + 1];
|
|
|
|
/***** Course constructor *****/
|
|
Crs_EditingCourseConstructor ();
|
|
|
|
/***** Get parameters from form *****/
|
|
/* Get course code */
|
|
Crs_EditingCrs->CrsCod = Crs_GetAndCheckParamOtherCrsCod (1);
|
|
|
|
/* Get institutional code */
|
|
Par_GetParToText ("InsCrsCod",NewInstitutionalCrsCod,Crs_MAX_BYTES_INSTITUTIONAL_CRS_COD);
|
|
|
|
/* Get data of the course */
|
|
Crs_GetDataOfCourseByCod (Crs_EditingCrs);
|
|
|
|
if (Crs_CheckIfICanEdit (Crs_EditingCrs))
|
|
{
|
|
/***** Change the institutional course code *****/
|
|
if (strcmp (NewInstitutionalCrsCod,Crs_EditingCrs->InstitutionalCrsCod))
|
|
{
|
|
Crs_UpdateInstitutionalCrsCod (Crs_EditingCrs,NewInstitutionalCrsCod);
|
|
Ale_CreateAlert (Ale_SUCCESS,NULL,
|
|
Txt_The_institutional_code_of_the_course_X_has_changed_to_Y,
|
|
Crs_EditingCrs->ShrtName,
|
|
NewInstitutionalCrsCod);
|
|
}
|
|
else // The same institutional code
|
|
Ale_CreateAlert (Ale_INFO,NULL,
|
|
Txt_The_institutional_code_of_the_course_X_has_not_changed,
|
|
Crs_EditingCrs->ShrtName);
|
|
}
|
|
else
|
|
Lay_NoPermissionExit ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************ Change the year of a course ************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_ChangeCrsYear (void)
|
|
{
|
|
extern const char *Txt_The_course_X_already_exists_in_year_Y;
|
|
extern const char *Txt_YEAR_OF_DEGREE[1 + Deg_MAX_YEARS_PER_DEGREE];
|
|
extern const char *Txt_The_year_of_the_course_X_has_changed;
|
|
extern const char *Txt_The_year_X_is_not_allowed;
|
|
char YearStr[2 + 1];
|
|
unsigned NewYear;
|
|
|
|
/***** Course constructor *****/
|
|
Crs_EditingCourseConstructor ();
|
|
|
|
/***** Get parameters from form *****/
|
|
/* Get course code */
|
|
Crs_EditingCrs->CrsCod = Crs_GetAndCheckParamOtherCrsCod (1);
|
|
|
|
/* Get parameter with year */
|
|
Par_GetParToText ("OthCrsYear",YearStr,2);
|
|
NewYear = Deg_ConvStrToYear (YearStr);
|
|
|
|
Crs_GetDataOfCourseByCod (Crs_EditingCrs);
|
|
|
|
if (Crs_CheckIfICanEdit (Crs_EditingCrs))
|
|
{
|
|
if (NewYear <= Deg_MAX_YEARS_PER_DEGREE) // If year is valid
|
|
{
|
|
/***** If name of course was in database in the new year... *****/
|
|
if (Crs_CheckIfCrsNameExistsInYearOfDeg ("ShortName",Crs_EditingCrs->ShrtName,
|
|
-1L,Crs_EditingCrs->DegCod,NewYear))
|
|
Ale_CreateAlert (Ale_WARNING,NULL,
|
|
Txt_The_course_X_already_exists_in_year_Y,
|
|
Crs_EditingCrs->ShrtName,
|
|
Txt_YEAR_OF_DEGREE[NewYear]);
|
|
else if (Crs_CheckIfCrsNameExistsInYearOfDeg ("FullName",Crs_EditingCrs->FullName,
|
|
-1L,Crs_EditingCrs->DegCod,NewYear))
|
|
Ale_CreateAlert (Ale_WARNING,NULL,
|
|
Txt_The_course_X_already_exists_in_year_Y,
|
|
Crs_EditingCrs->FullName,
|
|
Txt_YEAR_OF_DEGREE[NewYear]);
|
|
else // Update year in database
|
|
{
|
|
/***** Update year in table of courses *****/
|
|
Crs_UpdateCrsYear (Crs_EditingCrs,NewYear);
|
|
|
|
/***** Create message to show the change made *****/
|
|
Ale_CreateAlert (Ale_SUCCESS,NULL,
|
|
Txt_The_year_of_the_course_X_has_changed,
|
|
Crs_EditingCrs->ShrtName);
|
|
}
|
|
}
|
|
else // Year not valid
|
|
Ale_CreateAlert (Ale_WARNING,NULL,
|
|
Txt_The_year_X_is_not_allowed,
|
|
NewYear);
|
|
}
|
|
else
|
|
Lay_NoPermissionExit ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/****************** Change the year/semester of a course *********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_UpdateCrsYear (struct Crs_Course *Crs,unsigned NewYear)
|
|
{
|
|
/***** Update year/semester in table of courses *****/
|
|
DB_QueryUPDATE ("can not update the year of a course",
|
|
"UPDATE crs_courses SET Year=%u WHERE CrsCod=%ld",
|
|
NewYear,Crs->CrsCod);
|
|
|
|
/***** Copy course year/semester *****/
|
|
Crs->Year = NewYear;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************* Change the institutional course code of a course **************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_UpdateInstitutionalCrsCod (struct Crs_Course *Crs,const char *NewInstitutionalCrsCod)
|
|
{
|
|
/***** Update institutional course code in table of courses *****/
|
|
DB_QueryUPDATE ("can not update the institutional code"
|
|
" of the current course",
|
|
"UPDATE crs_courses SET InsCrsCod='%s' WHERE CrsCod=%ld",
|
|
NewInstitutionalCrsCod,Crs->CrsCod);
|
|
|
|
/***** Copy institutional course code *****/
|
|
Str_Copy (Crs->InstitutionalCrsCod,NewInstitutionalCrsCod,
|
|
sizeof (Crs->InstitutionalCrsCod) - 1);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************ Change the name of a course ************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_RenameCourseShort (void)
|
|
{
|
|
/***** Course constructor *****/
|
|
Crs_EditingCourseConstructor ();
|
|
|
|
/***** Rename course *****/
|
|
Crs_EditingCrs->CrsCod = Crs_GetAndCheckParamOtherCrsCod (1);
|
|
Crs_RenameCourse (Crs_EditingCrs,Cns_SHRT_NAME);
|
|
}
|
|
|
|
void Crs_RenameCourseFull (void)
|
|
{
|
|
/***** Course constructor *****/
|
|
Crs_EditingCourseConstructor ();
|
|
|
|
/***** Rename course *****/
|
|
Crs_EditingCrs->CrsCod = Crs_GetAndCheckParamOtherCrsCod (1);
|
|
Crs_RenameCourse (Crs_EditingCrs,Cns_FULL_NAME);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************ Change the name of a course ************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_RenameCourse (struct Crs_Course *Crs,Cns_ShrtOrFullName_t ShrtOrFullName)
|
|
{
|
|
extern const char *Txt_The_course_X_already_exists;
|
|
extern const char *Txt_The_name_of_the_course_X_has_changed_to_Y;
|
|
extern const char *Txt_The_name_of_the_course_X_has_not_changed;
|
|
const char *ParamName = NULL; // Initialized to avoid warning
|
|
const char *FieldName = NULL; // Initialized to avoid warning
|
|
unsigned MaxBytes = 0; // Initialized to avoid warning
|
|
char *CurrentCrsName = NULL; // Initialized to avoid warning
|
|
char NewCrsName[Cns_HIERARCHY_MAX_BYTES_FULL_NAME + 1];
|
|
|
|
switch (ShrtOrFullName)
|
|
{
|
|
case Cns_SHRT_NAME:
|
|
ParamName = "ShortName";
|
|
FieldName = "ShortName";
|
|
MaxBytes = Cns_HIERARCHY_MAX_BYTES_SHRT_NAME;
|
|
CurrentCrsName = Crs->ShrtName;
|
|
break;
|
|
case Cns_FULL_NAME:
|
|
ParamName = "FullName";
|
|
FieldName = "FullName";
|
|
MaxBytes = Cns_HIERARCHY_MAX_BYTES_FULL_NAME;
|
|
CurrentCrsName = Crs->FullName;
|
|
break;
|
|
}
|
|
|
|
/***** Get parameters from form *****/
|
|
/* Get the new name for the course */
|
|
Par_GetParToText (ParamName,NewCrsName,MaxBytes);
|
|
|
|
/***** Get from the database the data of the degree *****/
|
|
Crs_GetDataOfCourseByCod (Crs);
|
|
|
|
if (Crs_CheckIfICanEdit (Crs))
|
|
{
|
|
/***** Check if new name is empty *****/
|
|
if (NewCrsName[0])
|
|
{
|
|
/***** Check if old and new names are the same
|
|
(this happens when return is pressed without changes) *****/
|
|
if (strcmp (CurrentCrsName,NewCrsName)) // Different names
|
|
{
|
|
/***** If course was in database... *****/
|
|
if (Crs_CheckIfCrsNameExistsInYearOfDeg (ParamName,NewCrsName,Crs->CrsCod,
|
|
Crs->DegCod,Crs->Year))
|
|
Ale_CreateAlert (Ale_WARNING,NULL,
|
|
Txt_The_course_X_already_exists,
|
|
NewCrsName);
|
|
else
|
|
{
|
|
/* Update the table changing old name by new name */
|
|
Crs_UpdateCrsNameDB (Crs->CrsCod,FieldName,NewCrsName);
|
|
|
|
/* Create alert to show the change made */
|
|
Ale_CreateAlert (Ale_SUCCESS,NULL,
|
|
Txt_The_name_of_the_course_X_has_changed_to_Y,
|
|
CurrentCrsName,NewCrsName);
|
|
|
|
/* Change current course name in order to display it properly */
|
|
Str_Copy (CurrentCrsName,NewCrsName,MaxBytes);
|
|
}
|
|
}
|
|
else // The same name
|
|
Ale_CreateAlert (Ale_INFO,NULL,
|
|
Txt_The_name_of_the_course_X_has_not_changed,
|
|
CurrentCrsName);
|
|
}
|
|
else
|
|
Ale_CreateAlertYouCanNotLeaveFieldEmpty ();
|
|
}
|
|
else
|
|
Lay_NoPermissionExit ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********** Check if the name of course exists in existing courses ***********/
|
|
/*****************************************************************************/
|
|
|
|
bool Crs_CheckIfCrsNameExistsInYearOfDeg (const char *FieldName,const char *Name,long CrsCod,
|
|
long DegCod,unsigned Year)
|
|
{
|
|
/***** Get number of courses in a year of a degree and with a name from database *****/
|
|
return (DB_QueryCOUNT ("can not check if the name"
|
|
" of a course already existed",
|
|
"SELECT COUNT(*) FROM crs_courses"
|
|
" WHERE DegCod=%ld AND Year=%u"
|
|
" AND %s='%s' AND CrsCod<>%ld",
|
|
DegCod,Year,FieldName,Name,CrsCod) != 0);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/***************** Update course name in table of courses ********************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_UpdateCrsNameDB (long CrsCod,const char *FieldName,const char *NewCrsName)
|
|
{
|
|
/***** Update course changing old name by new name *****/
|
|
DB_QueryUPDATE ("can not update the name of a course",
|
|
"UPDATE crs_courses SET %s='%s' WHERE CrsCod=%ld",
|
|
FieldName,NewCrsName,CrsCod);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*********************** Change the status of a course ***********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_ChangeCrsStatus (void)
|
|
{
|
|
extern const char *Txt_The_status_of_the_course_X_has_changed;
|
|
Crs_Status_t Status;
|
|
Crs_StatusTxt_t StatusTxt;
|
|
|
|
/***** Course constructor *****/
|
|
Crs_EditingCourseConstructor ();
|
|
|
|
/***** Get parameters from form *****/
|
|
/* Get course code */
|
|
Crs_EditingCrs->CrsCod = Crs_GetAndCheckParamOtherCrsCod (1);
|
|
|
|
/* Get parameter with status */
|
|
Status = (Crs_Status_t)
|
|
Par_GetParToUnsignedLong ("Status",
|
|
0,
|
|
(unsigned long) Crs_MAX_STATUS,
|
|
(unsigned long) Crs_WRONG_STATUS);
|
|
if (Status == Crs_WRONG_STATUS)
|
|
Lay_ShowErrorAndExit ("Wrong status.");
|
|
StatusTxt = Crs_GetStatusTxtFromStatusBits (Status);
|
|
Status = Crs_GetStatusBitsFromStatusTxt (StatusTxt); // New status
|
|
|
|
/***** Get data of course *****/
|
|
Crs_GetDataOfCourseByCod (Crs_EditingCrs);
|
|
|
|
/***** Update status in table of courses *****/
|
|
DB_QueryUPDATE ("can not update the status of a course",
|
|
"UPDATE crs_courses SET Status=%u WHERE CrsCod=%ld",
|
|
(unsigned) Status,Crs_EditingCrs->CrsCod);
|
|
Crs_EditingCrs->Status = Status;
|
|
|
|
/***** Create alert to show the change made *****/
|
|
Ale_CreateAlert (Ale_SUCCESS,NULL,
|
|
Txt_The_status_of_the_course_X_has_changed,
|
|
Crs_EditingCrs->ShrtName);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********* Show alerts after changing a course and continue editing **********/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_ContEditAfterChgCrs (void)
|
|
{
|
|
bool PutButtonToRequestRegistration;
|
|
|
|
if (Ale_GetTypeOfLastAlert () == Ale_SUCCESS)
|
|
{
|
|
/***** Begin alert *****/
|
|
Ale_ShowLastAlertAndButton1 ();
|
|
|
|
/***** Put button to go to course changed *****/
|
|
Crs_PutButtonToGoToCrs ();
|
|
|
|
/***** Put button to request my registration in course *****/
|
|
PutButtonToRequestRegistration = false;
|
|
switch (Gbl.Usrs.Me.Role.Logged)
|
|
{
|
|
case Rol_GST: // I do not belong to any course
|
|
PutButtonToRequestRegistration = true;
|
|
break;
|
|
case Rol_USR:
|
|
PutButtonToRequestRegistration = !Usr_CheckIfUsrBelongsToCrs (Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
Crs_EditingCrs->CrsCod,
|
|
false);
|
|
break;
|
|
case Rol_STD:
|
|
case Rol_NET:
|
|
case Rol_TCH:
|
|
if (Crs_EditingCrs->CrsCod != Gbl.Hierarchy.Crs.CrsCod)
|
|
PutButtonToRequestRegistration = !Usr_CheckIfUsrBelongsToCrs (Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
Crs_EditingCrs->CrsCod,
|
|
false);
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
if (PutButtonToRequestRegistration)
|
|
Crs_PutButtonToRegisterInCrs ();
|
|
|
|
/***** End alert *****/
|
|
Ale_ShowAlertAndButton2 (ActUnk,NULL,NULL,
|
|
NULL,NULL,
|
|
Btn_NO_BUTTON,NULL);
|
|
}
|
|
|
|
/***** Show possible delayed alerts *****/
|
|
Ale_ShowAlerts (NULL);
|
|
|
|
/***** Show the form again *****/
|
|
Crs_EditCoursesInternal ();
|
|
|
|
/***** Course destructor *****/
|
|
Crs_EditingCourseDestructor ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************ Put button to go to course *************************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_PutButtonToGoToCrs (void)
|
|
{
|
|
// If the course being edited is different to the current one...
|
|
if (Crs_EditingCrs->CrsCod != Gbl.Hierarchy.Crs.CrsCod)
|
|
{
|
|
Frm_BeginForm (ActSeeCrsInf);
|
|
Crs_PutParamCrsCod (Crs_EditingCrs->CrsCod);
|
|
Btn_PutConfirmButton (Hie_BuildGoToMsg (Crs_EditingCrs->ShrtName));
|
|
Hie_FreeGoToMsg ();
|
|
Frm_EndForm ();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************ Put button to go to course *************************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_PutButtonToRegisterInCrs (void)
|
|
{
|
|
extern const char *Txt_Register_me_in_X;
|
|
|
|
Frm_BeginForm (ActReqSignUp);
|
|
// If the course being edited is different to the current one...
|
|
if (Crs_EditingCrs->CrsCod != Gbl.Hierarchy.Crs.CrsCod)
|
|
Crs_PutParamCrsCod (Crs_EditingCrs->CrsCod);
|
|
Btn_PutCreateButton (Str_BuildStringStr (Txt_Register_me_in_X,
|
|
Crs_EditingCrs->ShrtName));
|
|
Str_FreeString ();
|
|
Frm_EndForm ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************* Select one of my courses **************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_ReqSelectOneOfMyCourses (void)
|
|
{
|
|
/***** Fill the list with the courses I belong to, if not filled *****/
|
|
Usr_GetMyCourses ();
|
|
|
|
/***** Select one of my courses *****/
|
|
if (Gbl.Usrs.Me.MyCrss.Num)
|
|
/* Show my courses */
|
|
Crs_WriteListMyCoursesToSelectOne ();
|
|
else // I am not enroled in any course
|
|
/* Show help to enrol me */
|
|
Hlp_ShowHelpWhatWouldYouLikeToDo ();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/******************* Put an icon (form) to search courses ********************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_PutIconToSearchCourses (__attribute__((unused)) void *Args)
|
|
{
|
|
extern const char *Txt_Search_courses;
|
|
|
|
/***** Put form to search / select courses *****/
|
|
Lay_PutContextualLinkOnlyIcon (ActReqSch,NULL,
|
|
Sch_PutLinkToSearchCoursesParams,NULL,
|
|
"search.svg",
|
|
Txt_Search_courses);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********** Put an icon (form) to select my courses in breadcrumb ************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_PutIconToSelectMyCoursesInBreadcrumb (void)
|
|
{
|
|
extern const char *Txt_My_courses;
|
|
|
|
if (Gbl.Usrs.Me.Logged) // I am logged
|
|
{
|
|
/***** Begin form *****/
|
|
Frm_BeginForm (ActMyCrs);
|
|
|
|
/***** Put icon with link *****/
|
|
HTM_INPUT_IMAGE (Gbl.Prefs.URLTheme,"sitemap.svg",Txt_My_courses,
|
|
"BC_ICON ICO_HIGHLIGHT");
|
|
/*
|
|
HTM_BUTTON_Begin (Txt_My_courses,NULL,NULL);
|
|
HTM_IMG (Gbl.Prefs.URLTheme,"sitemap.svg",Txt_My_courses,
|
|
"class=\"BC_ICON ICO_HIGHLIGHT\"");
|
|
HTM_BUTTON_End ();
|
|
*/
|
|
|
|
/***** End form *****/
|
|
Frm_EndForm ();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/****************** Put an icon (form) to select my courses ******************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_PutIconToSelectMyCourses (__attribute__((unused)) void *Args)
|
|
{
|
|
extern const char *Txt_My_courses;
|
|
|
|
if (Gbl.Usrs.Me.Logged) // I am logged
|
|
/***** Put icon with link *****/
|
|
Lay_PutContextualLinkOnlyIcon (ActMyCrs,NULL,
|
|
NULL,NULL,
|
|
"sitemap.svg",
|
|
Txt_My_courses);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/******************** Write parameter with code of course ********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_PutParamCrsCod (long CrsCod)
|
|
{
|
|
Par_PutHiddenParamLong (NULL,"crs",CrsCod);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/******************** Write parameter with code of course ********************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_PutParamOtherCrsCod (void *CrsCod)
|
|
{
|
|
if (CrsCod)
|
|
Par_PutHiddenParamLong (NULL,"OthCrsCod",*((long *) CrsCod));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********************* Get parameter with code of course *********************/
|
|
/*****************************************************************************/
|
|
|
|
static long Crs_GetAndCheckParamOtherCrsCod (long MinCodAllowed)
|
|
{
|
|
long CrsCod;
|
|
|
|
/***** Get and check parameter with code of course *****/
|
|
if ((CrsCod = Par_GetParToLong ("OthCrsCod")) < MinCodAllowed)
|
|
Lay_ShowErrorAndExit ("Code of course is missing or invalid.");
|
|
|
|
return CrsCod;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************** Write courses of a user **************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_GetAndWriteCrssOfAUsr (const struct UsrData *UsrDat,Rol_Role_t Role)
|
|
{
|
|
extern const char *Txt_USER_in_COURSE;
|
|
extern const char *Txt_User[Usr_NUM_SEXS];
|
|
extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
|
|
extern const char *Txt_Degree;
|
|
extern const char *Txt_Year_OF_A_DEGREE;
|
|
extern const char *Txt_Course;
|
|
extern const char *Txt_ROLES_PLURAL_BRIEF_Abc[Rol_NUM_ROLES];
|
|
char *SubQuery;
|
|
MYSQL_RES *mysql_res;
|
|
MYSQL_ROW row;
|
|
unsigned NumCrss;
|
|
unsigned NumCrs;
|
|
|
|
/***** Get courses of a user from database *****/
|
|
if (Role == Rol_UNK) // Role == Rol_UNK ==> any role
|
|
{
|
|
if (asprintf (&SubQuery,"%s","") < 0)
|
|
Lay_NotEnoughMemoryExit ();
|
|
}
|
|
else
|
|
{
|
|
if (asprintf (&SubQuery," AND crs_usr.Role=%u",(unsigned) Role) < 0)
|
|
Lay_NotEnoughMemoryExit ();
|
|
}
|
|
NumCrss = (unsigned)
|
|
DB_QuerySELECT (&mysql_res,"can not get courses of a user",
|
|
"SELECT deg_degrees.DegCod," // row[0]
|
|
"crs_courses.CrsCod," // row[1]
|
|
"deg_degrees.ShortName," // row[2]
|
|
"deg_degrees.FullName," // row[3]
|
|
"crs_courses.Year," // row[4]
|
|
"crs_courses.FullName," // row[5]
|
|
"ctr_centers.ShortName," // row[6]
|
|
"crs_usr.Accepted" // row[7]
|
|
" FROM crs_usr,"
|
|
"crs_courses,"
|
|
"deg_degrees,"
|
|
"ctr_centers"
|
|
" WHERE crs_usr.UsrCod=%ld%s"
|
|
" AND crs_usr.CrsCod=crs_courses.CrsCod"
|
|
" AND crs_courses.DegCod=deg_degrees.DegCod"
|
|
" AND deg_degrees.CtrCod=ctr_centers.CtrCod"
|
|
" ORDER BY deg_degrees.FullName,"
|
|
"crs_courses.Year,"
|
|
"crs_courses.FullName",
|
|
UsrDat->UsrCod,SubQuery);
|
|
|
|
/***** Free allocated memory for subquery *****/
|
|
free (SubQuery);
|
|
|
|
/***** List the courses (one row per course) *****/
|
|
if (NumCrss)
|
|
{
|
|
/* Begin box and table */
|
|
Box_BoxTableBegin ("100%",NULL,
|
|
NULL,NULL,
|
|
NULL,Box_NOT_CLOSABLE,2);
|
|
|
|
/* Heading row */
|
|
HTM_TR_Begin (NULL);
|
|
|
|
HTM_TH_Begin (1,7,"LM");
|
|
HTM_TxtColon (Str_BuildStringStr (Txt_USER_in_COURSE,
|
|
Role == Rol_UNK ? Txt_User[Usr_SEX_UNKNOWN] : // Role == Rol_UNK ==> any role
|
|
Txt_ROLES_SINGUL_Abc[Role][UsrDat->Sex]));
|
|
Str_FreeString ();
|
|
HTM_TH_End ();
|
|
|
|
HTM_TR_End ();
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
HTM_TH (1,1,"BM",NULL);
|
|
HTM_TH (1,1,"BM",NULL);
|
|
HTM_TH (1,1,"LM",Txt_Degree);
|
|
HTM_TH (1,1,"CM",Txt_Year_OF_A_DEGREE);
|
|
HTM_TH (1,1,"LM",Txt_Course);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_TCH]);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_NET]);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_STD]);
|
|
|
|
HTM_TR_End ();
|
|
|
|
/* Write courses */
|
|
for (NumCrs = 1;
|
|
NumCrs <= NumCrss;
|
|
NumCrs++)
|
|
{
|
|
/* Get next course */
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
/* Write data of this course */
|
|
Crs_WriteRowCrsData (NumCrs,row,true);
|
|
}
|
|
|
|
/* End table and box */
|
|
Box_BoxTableEnd ();
|
|
}
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**************************** List courses found *****************************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_ListCrssFound (MYSQL_RES **mysql_res,unsigned NumCrss)
|
|
{
|
|
extern const char *Txt_course;
|
|
extern const char *Txt_courses;
|
|
extern const char *Txt_Degree;
|
|
extern const char *Txt_Year_OF_A_DEGREE;
|
|
extern const char *Txt_Course;
|
|
extern const char *Txt_ROLES_PLURAL_BRIEF_Abc[Rol_NUM_ROLES];
|
|
MYSQL_ROW row;
|
|
unsigned NumCrs;
|
|
|
|
/***** List the courses (one row per course) *****/
|
|
if (NumCrss)
|
|
{
|
|
/***** Begin box and table *****/
|
|
/* Number of courses found */
|
|
Box_BoxTableBegin (NULL,Str_BuildStringLongStr ((long) NumCrss,
|
|
(NumCrss == 1) ? Txt_course :
|
|
Txt_courses),
|
|
NULL,NULL,
|
|
NULL,Box_NOT_CLOSABLE,2);
|
|
Str_FreeString ();
|
|
|
|
/***** Heading row *****/
|
|
HTM_TR_Begin (NULL);
|
|
|
|
HTM_TH (1,1,"BM",NULL);
|
|
HTM_TH (1,1,"LM",Txt_Degree);
|
|
HTM_TH (1,1,"CM",Txt_Year_OF_A_DEGREE);
|
|
HTM_TH (1,1,"LM",Txt_Course);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_TCH]);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_NET]);
|
|
HTM_TH (1,1,"RM",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_STD]);
|
|
|
|
HTM_TR_End ();
|
|
|
|
/***** Write courses *****/
|
|
for (NumCrs = 1;
|
|
NumCrs <= NumCrss;
|
|
NumCrs++)
|
|
{
|
|
/* Get next course */
|
|
row = mysql_fetch_row (*mysql_res);
|
|
|
|
/* Write data of this course */
|
|
Crs_WriteRowCrsData (NumCrs,row,false);
|
|
}
|
|
|
|
/***** End table and box *****/
|
|
Box_BoxTableEnd ();
|
|
}
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
DB_FreeMySQLResult (mysql_res);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************** Write the data of a course (result of a query) ***************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_WriteRowCrsData (unsigned NumCrs,MYSQL_ROW row,bool WriteColumnAccepted)
|
|
{
|
|
extern const char *Txt_Enrolment_confirmed;
|
|
extern const char *Txt_Enrolment_not_confirmed;
|
|
extern const char *Txt_YEAR_OF_DEGREE[1 + Deg_MAX_YEARS_PER_DEGREE];
|
|
struct Deg_Degree Deg;
|
|
long CrsCod;
|
|
unsigned NumStds;
|
|
unsigned NumNETs;
|
|
unsigned NumTchs;
|
|
unsigned NumUsrs;
|
|
const char *ClassTxt;
|
|
const char *ClassLink;
|
|
const char *BgColor;
|
|
bool Accepted;
|
|
static unsigned RowEvenOdd = 1;
|
|
/*
|
|
SELECT deg_degrees.DegCod row[0]
|
|
crs_courses.CrsCod row[1]
|
|
deg_degrees.ShortName row[2]
|
|
deg_degrees.FullName row[3]
|
|
crs_courses.Year row[4]
|
|
crs_courses.FullName row[5]
|
|
ctr_centers.ShortName row[6]
|
|
crs_usr.Accepted row[7] (only if WriteColumnAccepted == true)
|
|
*/
|
|
|
|
/***** Get degree code (row[0]) *****/
|
|
if ((Deg.DegCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
|
|
Lay_ShowErrorAndExit ("Wrong code of degree.");
|
|
if (!Deg_GetDataOfDegreeByCod (&Deg))
|
|
Lay_ShowErrorAndExit ("Degree not found.");
|
|
|
|
/***** Get course code (row[1]) *****/
|
|
if ((CrsCod = Str_ConvertStrCodToLongCod (row[1])) < 0)
|
|
Lay_ShowErrorAndExit ("Wrong code of course.");
|
|
|
|
/***** Get number of teachers and students in this course *****/
|
|
NumStds = Usr_GetNumUsrsInCrss (Hie_Lvl_CRS,CrsCod,1 << Rol_STD);
|
|
NumNETs = Usr_GetNumUsrsInCrss (Hie_Lvl_CRS,CrsCod,1 << Rol_NET);
|
|
NumTchs = Usr_GetNumUsrsInCrss (Hie_Lvl_CRS,CrsCod,1 << Rol_TCH);
|
|
NumUsrs = NumStds + NumNETs + NumTchs;
|
|
if (NumUsrs)
|
|
{
|
|
ClassTxt = "DAT_N";
|
|
ClassLink = "BT_LINK LT DAT_N";
|
|
}
|
|
else
|
|
{
|
|
ClassTxt = "DAT";
|
|
ClassLink = "BT_LINK LT DAT";
|
|
}
|
|
BgColor = (CrsCod == Gbl.Hierarchy.Crs.CrsCod) ? "LIGHT_BLUE" :
|
|
Gbl.ColorRows[RowEvenOdd];
|
|
|
|
/***** Start row *****/
|
|
HTM_TR_Begin (NULL);
|
|
|
|
/***** User has accepted joining to this course/to any course in degree/to any course? *****/
|
|
if (WriteColumnAccepted)
|
|
{
|
|
Accepted = (row[7][0] == 'Y');
|
|
HTM_TD_Begin ("class=\"BT %s\" title=\"%s\"",
|
|
BgColor,
|
|
Accepted ? Txt_Enrolment_confirmed :
|
|
Txt_Enrolment_not_confirmed);
|
|
HTM_Txt (Accepted ? "✓" :
|
|
"✗");
|
|
HTM_TD_End ();
|
|
}
|
|
|
|
/***** Write number of course in this search *****/
|
|
HTM_TD_Begin ("class=\"%s RT %s\"",ClassTxt,BgColor);
|
|
HTM_Unsigned (NumCrs);
|
|
HTM_TD_End ();
|
|
|
|
/***** Write degree logo, degree short name (row[2])
|
|
and center short name (row[6]) *****/
|
|
HTM_TD_Begin ("class=\"LT %s\"",BgColor);
|
|
Frm_BeginFormGoTo (ActSeeDegInf);
|
|
Deg_PutParamDegCod (Deg.DegCod);
|
|
HTM_BUTTON_SUBMIT_Begin (Hie_BuildGoToMsg (row[2]),ClassLink,NULL);
|
|
Hie_FreeGoToMsg ();
|
|
Lgo_DrawLogo (Hie_Lvl_DEG,Deg.DegCod,Deg.ShrtName,20,"CT",true);
|
|
HTM_TxtF (" %s (%s)",row[2],row[6]);
|
|
HTM_BUTTON_End ();
|
|
Frm_EndForm ();
|
|
HTM_TD_End ();
|
|
|
|
/***** Write year (row[4]) *****/
|
|
HTM_TD_Begin ("class=\"%s CT %s\"",ClassTxt,BgColor);
|
|
HTM_Txt (Txt_YEAR_OF_DEGREE[Deg_ConvStrToYear (row[4])]);
|
|
HTM_TD_End ();
|
|
|
|
/***** Write course full name (row[5]) *****/
|
|
HTM_TD_Begin ("class=\"LT %s\"",BgColor);
|
|
Frm_BeginFormGoTo (ActSeeCrsInf);
|
|
Crs_PutParamCrsCod (CrsCod);
|
|
HTM_BUTTON_SUBMIT_Begin (Hie_BuildGoToMsg (row[5]),ClassLink,NULL);
|
|
Hie_FreeGoToMsg ();
|
|
HTM_Txt (row[5]);
|
|
HTM_BUTTON_End ();
|
|
Frm_EndForm ();
|
|
HTM_TD_End ();
|
|
|
|
/***** Write number of teachers in course *****/
|
|
HTM_TD_Begin ("class=\"%s RT %s\"",ClassTxt,BgColor);
|
|
HTM_Unsigned (NumTchs);
|
|
HTM_TD_End ();
|
|
|
|
/***** Write number of non-editing teachers in course *****/
|
|
HTM_TD_Begin ("class=\"%s RT %s\"",ClassTxt,BgColor);
|
|
HTM_Unsigned (NumNETs);
|
|
HTM_TD_End ();
|
|
|
|
/***** Write number of students in course *****/
|
|
HTM_TD_Begin ("class=\"%s RT %s\"",ClassTxt,BgColor);
|
|
HTM_Unsigned (NumStds);
|
|
HTM_TD_End ();
|
|
|
|
HTM_TR_End ();
|
|
|
|
RowEvenOdd = 1 - RowEvenOdd;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/***************** Update my last click in current course ********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_UpdateCrsLast (void)
|
|
{
|
|
if (Gbl.Hierarchy.Level == Hie_Lvl_CRS && // Course selected
|
|
Gbl.Usrs.Me.Role.Logged >= Rol_STD)
|
|
/***** Update my last access to current course *****/
|
|
DB_QueryUPDATE ("can not update last access to current course",
|
|
"REPLACE INTO crs_last (CrsCod,LastTime)"
|
|
" VALUES (%ld,NOW())",
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********************** Put link to remove old courses ***********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_PutLinkToRemoveOldCrss (void)
|
|
{
|
|
extern const char *Txt_Eliminate_old_courses;
|
|
|
|
/***** Put form to remove old courses *****/
|
|
Lay_PutContextualLinkIconText (ActReqRemOldCrs,NULL,
|
|
NULL,NULL,
|
|
"trash.svg",
|
|
Txt_Eliminate_old_courses);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/********************** Write form to remove old courses *********************/
|
|
/*****************************************************************************/
|
|
|
|
void Crs_AskRemoveOldCrss (void)
|
|
{
|
|
extern const char *Hlp_SYSTEM_Maintenance_eliminate_old_courses;
|
|
extern const char *The_ClassFormInBox[The_NUM_THEMES];
|
|
extern const char *Txt_Eliminate_old_courses;
|
|
extern const char *Txt_Eliminate_all_courses_whithout_users_PART_1_OF_2;
|
|
extern const char *Txt_Eliminate_all_courses_whithout_users_PART_2_OF_2;
|
|
extern const char *Txt_Eliminate;
|
|
unsigned MonthsWithoutAccess = Crs_DEF_MONTHS_WITHOUT_ACCESS_TO_REMOVE_OLD_CRSS;
|
|
unsigned i;
|
|
|
|
/***** Begin form *****/
|
|
Frm_BeginForm (ActRemOldCrs);
|
|
|
|
/***** Begin box *****/
|
|
Box_BoxBegin (NULL,Txt_Eliminate_old_courses,
|
|
NULL,NULL,
|
|
Hlp_SYSTEM_Maintenance_eliminate_old_courses,Box_NOT_CLOSABLE);
|
|
|
|
/***** Form to request number of months without clicks *****/
|
|
HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
|
HTM_TxtF ("%s ",Txt_Eliminate_all_courses_whithout_users_PART_1_OF_2);
|
|
HTM_SELECT_Begin (HTM_DONT_SUBMIT_ON_CHANGE,
|
|
"name=\"Months\"");
|
|
for (i = Crs_MIN_MONTHS_WITHOUT_ACCESS_TO_REMOVE_OLD_CRSS;
|
|
i <= Crs_MAX_MONTHS_WITHOUT_ACCESS_TO_REMOVE_OLD_CRSS;
|
|
i++)
|
|
HTM_OPTION (HTM_Type_UNSIGNED,&i,
|
|
i == MonthsWithoutAccess,false,
|
|
"%u",i);
|
|
HTM_SELECT_End ();
|
|
HTM_NBSP ();
|
|
HTM_TxtF (Txt_Eliminate_all_courses_whithout_users_PART_2_OF_2,
|
|
Cfg_PLATFORM_SHORT_NAME);
|
|
HTM_LABEL_End ();
|
|
|
|
/***** Send button and end box *****/
|
|
Box_BoxWithButtonEnd (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 LastTime<FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu)"
|
|
" AND CrsCod NOT IN (SELECT DISTINCT CrsCod FROM crs_usr)",
|
|
SecondsWithoutAccess);
|
|
if (NumCrss)
|
|
{
|
|
Ale_ShowAlert (Ale_INFO,Txt_Eliminating_X_courses_whithout_users_and_with_more_than_Y_months_without_access,
|
|
NumCrss,
|
|
MonthsWithoutAccess,
|
|
Cfg_PLATFORM_SHORT_NAME);
|
|
|
|
/***** Remove courses *****/
|
|
for (NumCrs = 0;
|
|
NumCrs < NumCrss;
|
|
NumCrs++)
|
|
{
|
|
row = mysql_fetch_row (mysql_res);
|
|
CrsCod = Str_ConvertStrCodToLongCod (row[0]);
|
|
Crs_RemoveCourseCompletely (CrsCod);
|
|
NumCrssRemoved++;
|
|
}
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
}
|
|
|
|
/***** Write end message *****/
|
|
Ale_ShowAlert (Ale_SUCCESS,Txt_X_courses_have_been_eliminated,
|
|
NumCrssRemoved);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/************************ Course constructor/destructor **********************/
|
|
/*****************************************************************************/
|
|
|
|
static void Crs_EditingCourseConstructor (void)
|
|
{
|
|
/***** Pointer must be NULL *****/
|
|
if (Crs_EditingCrs != NULL)
|
|
Lay_ShowErrorAndExit ("Error initializing course.");
|
|
|
|
/***** Allocate memory for course *****/
|
|
if ((Crs_EditingCrs = malloc (sizeof (*Crs_EditingCrs))) == NULL)
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
/***** Reset course *****/
|
|
Crs_EditingCrs->CrsCod = -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';
|
|
}
|
|
|
|
static void Crs_EditingCourseDestructor (void)
|
|
{
|
|
/***** Free memory used for course *****/
|
|
if (Crs_EditingCrs != NULL)
|
|
{
|
|
free (Crs_EditingCrs);
|
|
Crs_EditingCrs = NULL;
|
|
}
|
|
}
|