diff --git a/Makefile b/Makefile
index 00f26e45..74fb67da 100644
--- a/Makefile
+++ b/Makefile
@@ -50,7 +50,8 @@ OBJS = swad_account.o swad_action.o swad_agenda.o swad_alert.o \
swad_message.o swad_MFU.o \
swad_network.o swad_nickname.o swad_notice.o swad_notification.o \
swad_pagination.o swad_parameter.o swad_password.o swad_photo.o \
- swad_place.o swad_plugin.o swad_privacy.o swad_profile.o swad_project.o \
+ swad_place.o swad_plugin.o swad_privacy.o swad_profile.o \
+ swad_program.o swad_project.o \
swad_QR.o \
swad_record.o swad_report.o swad_role.o swad_RSS.o \
swad_scope.o swad_search.o swad_session.o swad_setting.o \
diff --git a/sql/swad.sql b/sql/swad.sql
index fc0a7766..33261a4c 100644
--- a/sql/swad.sql
+++ b/sql/swad.sql
@@ -1025,6 +1025,27 @@ CREATE TABLE IF NOT EXISTS plugins (
IP CHAR(15) NOT NULL,
UNIQUE INDEX(PlgCod));
--
+-- Table prg_grp: stores the groups associated to each program item
+--
+CREATE TABLE IF NOT EXISTS prg_grp (
+ PrgIteCod INT NOT NULL,
+ GrpCod INT NOT NULL,
+ UNIQUE INDEX(PrgIteCod,GrpCod));
+--
+-- Table prg_items: stores the items of the course program
+--
+CREATE TABLE IF NOT EXISTS prg_items (
+ PrgIteCod INT NOT NULL AUTO_INCREMENT,
+ CrsCod INT NOT NULL DEFAULT -1,
+ Hidden ENUM('N','Y') NOT NULL DEFAULT 'N',
+ UsrCod INT NOT NULL,
+ StartTime DATETIME NOT NULL,
+ EndTime DATETIME NOT NULL,
+ Title VARCHAR(2047) NOT NULL,
+ Txt TEXT NOT NULL,
+ UNIQUE INDEX(PrgIteCod),
+ INDEX(CrsCod,Hidden));
+--
-- Table prj_config: stores the configuration of projects for each course
--
CREATE TABLE IF NOT EXISTS prj_config (
diff --git a/swad_action.c b/swad_action.c
index 95497a8f..f098d51c 100644
--- a/swad_action.c
+++ b/swad_action.c
@@ -73,6 +73,7 @@
#include "swad_photo.h"
#include "swad_privacy.h"
#include "swad_profile.h"
+#include "swad_program.h"
#include "swad_project.h"
#include "swad_QR.h"
#include "swad_report.h"
@@ -380,7 +381,7 @@ const struct Act_Actions Act_Actions[Act_NUM_ACTIONS] =
// TabCrs ******************************************************************
// Actions in menu:
[ActSeeCrsInf ] = { 847, 0,TabCrs,ActSeeCrsInf ,0x3F8,0x3C7, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Crs_ShowIntroduction ,"info" },
- [ActSeeScd ] = {1821, 1,TabCrs,ActSeeScd ,0x200,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Crs_ShowIntroduction ,"clipboard-list" },
+ [ActSeePrg ] = {1821, 1,TabCrs,ActSeePrg ,0x200,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Prg_SeeCourseProgram ,"clipboard-list" },
[ActSeeTchGui ] = { 784, 2,TabCrs,ActSeeTchGui ,0x3F8,0x3C7, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Inf_ShowInfo ,"book-open" },
[ActSeeSyl ] = {1242, 3,TabCrs,ActSeeSyl ,0x3F8,0x3C7, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Inf_ShowInfo ,"list-ol" },
[ActSeeBib ] = { 32, 4,TabCrs,ActSeeBib ,0x3F8,0x3C7, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Inf_ShowInfo ,"book" },
@@ -397,6 +398,16 @@ const struct Act_Actions Act_Actions[Act_NUM_ACTIONS] =
[ActChgCrsYeaCfg ] = {1573,-1,TabUnk,ActSeeCrsInf ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,CrsCfg_ChangeCrsYear ,CrsCfg_ContEditAfterChgCrs ,NULL},
[ActEdiCrsInf ] = { 848,-1,TabUnk,ActSeeCrsInf ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Inf_FormsToSelSendInfo ,NULL},
+ [ActFrmNewPrgIte ] = {1822,-1,TabUnk,ActSeePrg ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Prg_RequestCreatOrEditPrgItem ,NULL},
+ [ActEdiOnePrgIte ] = {1823,-1,TabUnk,ActSeePrg ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Prg_RequestCreatOrEditPrgItem ,NULL},
+ [ActPrnOnePrgIte ] = {1824,-1,TabUnk,ActSeePrg ,0x3F8,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_NEW_TAB,NULL ,Prg_PrintOnePrgItem ,NULL},
+ [ActNewPrgIte ] = {1825,-1,TabUnk,ActSeePrg ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Prg_RecFormPrgItem ,NULL},
+ [ActChgPrgIte ] = {1826,-1,TabUnk,ActSeePrg ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Prg_RecFormPrgItem ,NULL},
+ [ActReqRemPrgIte ] = {1827,-1,TabUnk,ActSeePrg ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Prg_ReqRemPrgItem ,NULL},
+ [ActRemPrgIte ] = {1828,-1,TabUnk,ActSeePrg ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Prg_RemovePrgItem ,NULL},
+ [ActHidPrgIte ] = {1829,-1,TabUnk,ActSeePrg ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Prg_HidePrgItem ,NULL},
+ [ActShoPrgIte ] = {1830,-1,TabUnk,ActSeePrg ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Prg_ShowPrgItem ,NULL},
+
[ActEdiTchGui ] = { 785,-1,TabUnk,ActSeeTchGui ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Inf_FormsToSelSendInfo ,NULL},
[ActSeeSylLec ] = { 28,-1,TabUnk,ActSeeSyl ,0x3F8,0x3C7, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Inf_ShowInfo ,NULL},
@@ -3536,7 +3547,16 @@ Act_Action_t Act_FromActCodToAction[1 + Act_MAX_ACTION_COD] = // Do not reuse un
ActSeeSysInf, // #1818
ActPrnSysInf, // #1819
ActMtn, // #1820
- ActSeeScd, // #1821
+ ActSeePrg, // #1821
+ ActFrmNewPrgIte, // #1822
+ ActEdiOnePrgIte, // #1823
+ ActPrnOnePrgIte, // #1824
+ ActNewPrgIte, // #1825
+ ActChgPrgIte, // #1826
+ ActReqRemPrgIte, // #1827
+ ActRemPrgIte, // #1828
+ ActHidPrgIte, // #1829
+ ActShoPrgIte, // #1830
};
/*****************************************************************************/
diff --git a/swad_action.h b/swad_action.h
index b85b876c..3468353e 100644
--- a/swad_action.h
+++ b/swad_action.h
@@ -64,7 +64,7 @@ typedef enum
typedef signed int Act_Action_t; // Must be a signed type, because -1 is used to indicate obsolete action
-#define Act_MAX_ACTION_COD 1821
+#define Act_MAX_ACTION_COD 1830
#define Act_MAX_OPTIONS_IN_MENU_PER_TAB 13
@@ -368,7 +368,7 @@ typedef signed int Act_Action_t; // Must be a signed type, because -1 is used to
/*****************************************************************************/
// Actions in menu
#define ActSeeCrsInf (ActChgCrsSta + 1)
-#define ActSeeScd (ActChgCrsSta + 2)
+#define ActSeePrg (ActChgCrsSta + 2)
#define ActSeeTchGui (ActChgCrsSta + 3)
#define ActSeeSyl (ActChgCrsSta + 4)
#define ActSeeBib (ActChgCrsSta + 5)
@@ -384,107 +384,116 @@ typedef signed int Act_Action_t; // Must be a signed type, because -1 is used to
#define ActChgInsCrsCodCfg (ActChgCrsSta + 13)
#define ActChgCrsYeaCfg (ActChgCrsSta + 14)
#define ActEdiCrsInf (ActChgCrsSta + 15)
-#define ActEdiTchGui (ActChgCrsSta + 16)
-#define ActSeeSylLec (ActChgCrsSta + 17)
-#define ActSeeSylPra (ActChgCrsSta + 18)
-#define ActEdiSylLec (ActChgCrsSta + 19)
-#define ActEdiSylPra (ActChgCrsSta + 20)
-#define ActDelItmSylLec (ActChgCrsSta + 21)
-#define ActDelItmSylPra (ActChgCrsSta + 22)
-#define ActUp_IteSylLec (ActChgCrsSta + 23)
-#define ActUp_IteSylPra (ActChgCrsSta + 24)
-#define ActDwnIteSylLec (ActChgCrsSta + 25)
-#define ActDwnIteSylPra (ActChgCrsSta + 26)
-#define ActRgtIteSylLec (ActChgCrsSta + 27)
-#define ActRgtIteSylPra (ActChgCrsSta + 28)
-#define ActLftIteSylLec (ActChgCrsSta + 29)
-#define ActLftIteSylPra (ActChgCrsSta + 30)
-#define ActInsIteSylLec (ActChgCrsSta + 31)
-#define ActInsIteSylPra (ActChgCrsSta + 32)
-#define ActModIteSylLec (ActChgCrsSta + 33)
-#define ActModIteSylPra (ActChgCrsSta + 34)
+#define ActFrmNewPrgIte (ActChgCrsSta + 16)
+#define ActEdiOnePrgIte (ActChgCrsSta + 17)
+#define ActPrnOnePrgIte (ActChgCrsSta + 18)
+#define ActNewPrgIte (ActChgCrsSta + 19)
+#define ActChgPrgIte (ActChgCrsSta + 20)
+#define ActReqRemPrgIte (ActChgCrsSta + 21)
+#define ActRemPrgIte (ActChgCrsSta + 22)
+#define ActHidPrgIte (ActChgCrsSta + 23)
+#define ActShoPrgIte (ActChgCrsSta + 24)
+#define ActEdiTchGui (ActChgCrsSta + 25)
+#define ActSeeSylLec (ActChgCrsSta + 26)
+#define ActSeeSylPra (ActChgCrsSta + 27)
+#define ActEdiSylLec (ActChgCrsSta + 28)
+#define ActEdiSylPra (ActChgCrsSta + 29)
+#define ActDelItmSylLec (ActChgCrsSta + 30)
+#define ActDelItmSylPra (ActChgCrsSta + 31)
+#define ActUp_IteSylLec (ActChgCrsSta + 32)
+#define ActUp_IteSylPra (ActChgCrsSta + 33)
+#define ActDwnIteSylLec (ActChgCrsSta + 34)
+#define ActDwnIteSylPra (ActChgCrsSta + 35)
+#define ActRgtIteSylLec (ActChgCrsSta + 36)
+#define ActRgtIteSylPra (ActChgCrsSta + 37)
+#define ActLftIteSylLec (ActChgCrsSta + 38)
+#define ActLftIteSylPra (ActChgCrsSta + 39)
+#define ActInsIteSylLec (ActChgCrsSta + 40)
+#define ActInsIteSylPra (ActChgCrsSta + 41)
+#define ActModIteSylLec (ActChgCrsSta + 42)
+#define ActModIteSylPra (ActChgCrsSta + 43)
-#define ActEdiBib (ActChgCrsSta + 35)
-#define ActEdiFAQ (ActChgCrsSta + 36)
-#define ActEdiCrsLnk (ActChgCrsSta + 37)
+#define ActEdiBib (ActChgCrsSta + 44)
+#define ActEdiFAQ (ActChgCrsSta + 45)
+#define ActEdiCrsLnk (ActChgCrsSta + 46)
-#define ActChgFrcReaCrsInf (ActChgCrsSta + 38)
-#define ActChgFrcReaTchGui (ActChgCrsSta + 39)
-#define ActChgFrcReaSylLec (ActChgCrsSta + 40)
-#define ActChgFrcReaSylPra (ActChgCrsSta + 41)
-#define ActChgFrcReaBib (ActChgCrsSta + 42)
-#define ActChgFrcReaFAQ (ActChgCrsSta + 43)
-#define ActChgFrcReaCrsLnk (ActChgCrsSta + 44)
+#define ActChgFrcReaCrsInf (ActChgCrsSta + 47)
+#define ActChgFrcReaTchGui (ActChgCrsSta + 48)
+#define ActChgFrcReaSylLec (ActChgCrsSta + 49)
+#define ActChgFrcReaSylPra (ActChgCrsSta + 50)
+#define ActChgFrcReaBib (ActChgCrsSta + 51)
+#define ActChgFrcReaFAQ (ActChgCrsSta + 52)
+#define ActChgFrcReaCrsLnk (ActChgCrsSta + 53)
-#define ActChgHavReaCrsInf (ActChgCrsSta + 45)
-#define ActChgHavReaTchGui (ActChgCrsSta + 46)
-#define ActChgHavReaSylLec (ActChgCrsSta + 47)
-#define ActChgHavReaSylPra (ActChgCrsSta + 48)
-#define ActChgHavReaBib (ActChgCrsSta + 49)
-#define ActChgHavReaFAQ (ActChgCrsSta + 50)
-#define ActChgHavReaCrsLnk (ActChgCrsSta + 51)
+#define ActChgHavReaCrsInf (ActChgCrsSta + 54)
+#define ActChgHavReaTchGui (ActChgCrsSta + 55)
+#define ActChgHavReaSylLec (ActChgCrsSta + 56)
+#define ActChgHavReaSylPra (ActChgCrsSta + 57)
+#define ActChgHavReaBib (ActChgCrsSta + 58)
+#define ActChgHavReaFAQ (ActChgCrsSta + 59)
+#define ActChgHavReaCrsLnk (ActChgCrsSta + 60)
-#define ActSelInfSrcCrsInf (ActChgCrsSta + 52)
-#define ActSelInfSrcTchGui (ActChgCrsSta + 53)
-#define ActSelInfSrcSylLec (ActChgCrsSta + 54)
-#define ActSelInfSrcSylPra (ActChgCrsSta + 55)
-#define ActSelInfSrcBib (ActChgCrsSta + 56)
-#define ActSelInfSrcFAQ (ActChgCrsSta + 57)
-#define ActSelInfSrcCrsLnk (ActChgCrsSta + 58)
-#define ActRcvURLCrsInf (ActChgCrsSta + 59)
-#define ActRcvURLTchGui (ActChgCrsSta + 60)
-#define ActRcvURLSylLec (ActChgCrsSta + 61)
-#define ActRcvURLSylPra (ActChgCrsSta + 62)
-#define ActRcvURLBib (ActChgCrsSta + 63)
-#define ActRcvURLFAQ (ActChgCrsSta + 64)
-#define ActRcvURLCrsLnk (ActChgCrsSta + 65)
-#define ActRcvPagCrsInf (ActChgCrsSta + 66)
-#define ActRcvPagTchGui (ActChgCrsSta + 67)
-#define ActRcvPagSylLec (ActChgCrsSta + 68)
-#define ActRcvPagSylPra (ActChgCrsSta + 69)
-#define ActRcvPagBib (ActChgCrsSta + 70)
-#define ActRcvPagFAQ (ActChgCrsSta + 71)
-#define ActRcvPagCrsLnk (ActChgCrsSta + 72)
-#define ActEditorCrsInf (ActChgCrsSta + 73)
-#define ActEditorTchGui (ActChgCrsSta + 74)
-#define ActEditorSylLec (ActChgCrsSta + 75)
-#define ActEditorSylPra (ActChgCrsSta + 76)
-#define ActEditorBib (ActChgCrsSta + 77)
-#define ActEditorFAQ (ActChgCrsSta + 78)
-#define ActEditorCrsLnk (ActChgCrsSta + 79)
-#define ActPlaTxtEdiCrsInf (ActChgCrsSta + 80)
-#define ActPlaTxtEdiTchGui (ActChgCrsSta + 81)
-#define ActPlaTxtEdiSylLec (ActChgCrsSta + 82)
-#define ActPlaTxtEdiSylPra (ActChgCrsSta + 83)
-#define ActPlaTxtEdiBib (ActChgCrsSta + 84)
-#define ActPlaTxtEdiFAQ (ActChgCrsSta + 85)
-#define ActPlaTxtEdiCrsLnk (ActChgCrsSta + 86)
-#define ActRchTxtEdiCrsInf (ActChgCrsSta + 87)
-#define ActRchTxtEdiTchGui (ActChgCrsSta + 88)
-#define ActRchTxtEdiSylLec (ActChgCrsSta + 89)
-#define ActRchTxtEdiSylPra (ActChgCrsSta + 90)
-#define ActRchTxtEdiBib (ActChgCrsSta + 91)
-#define ActRchTxtEdiFAQ (ActChgCrsSta + 92)
-#define ActRchTxtEdiCrsLnk (ActChgCrsSta + 93)
-#define ActRcvPlaTxtCrsInf (ActChgCrsSta + 94)
-#define ActRcvPlaTxtTchGui (ActChgCrsSta + 95)
-#define ActRcvPlaTxtSylLec (ActChgCrsSta + 96)
-#define ActRcvPlaTxtSylPra (ActChgCrsSta + 97)
-#define ActRcvPlaTxtBib (ActChgCrsSta + 98)
-#define ActRcvPlaTxtFAQ (ActChgCrsSta + 99)
-#define ActRcvPlaTxtCrsLnk (ActChgCrsSta + 100)
-#define ActRcvRchTxtCrsInf (ActChgCrsSta + 101)
-#define ActRcvRchTxtTchGui (ActChgCrsSta + 102)
-#define ActRcvRchTxtSylLec (ActChgCrsSta + 103)
-#define ActRcvRchTxtSylPra (ActChgCrsSta + 104)
-#define ActRcvRchTxtBib (ActChgCrsSta + 105)
-#define ActRcvRchTxtFAQ (ActChgCrsSta + 106)
-#define ActRcvRchTxtCrsLnk (ActChgCrsSta + 107)
+#define ActSelInfSrcCrsInf (ActChgCrsSta + 61)
+#define ActSelInfSrcTchGui (ActChgCrsSta + 62)
+#define ActSelInfSrcSylLec (ActChgCrsSta + 63)
+#define ActSelInfSrcSylPra (ActChgCrsSta + 64)
+#define ActSelInfSrcBib (ActChgCrsSta + 65)
+#define ActSelInfSrcFAQ (ActChgCrsSta + 66)
+#define ActSelInfSrcCrsLnk (ActChgCrsSta + 67)
+#define ActRcvURLCrsInf (ActChgCrsSta + 68)
+#define ActRcvURLTchGui (ActChgCrsSta + 69)
+#define ActRcvURLSylLec (ActChgCrsSta + 70)
+#define ActRcvURLSylPra (ActChgCrsSta + 71)
+#define ActRcvURLBib (ActChgCrsSta + 72)
+#define ActRcvURLFAQ (ActChgCrsSta + 73)
+#define ActRcvURLCrsLnk (ActChgCrsSta + 74)
+#define ActRcvPagCrsInf (ActChgCrsSta + 75)
+#define ActRcvPagTchGui (ActChgCrsSta + 76)
+#define ActRcvPagSylLec (ActChgCrsSta + 77)
+#define ActRcvPagSylPra (ActChgCrsSta + 78)
+#define ActRcvPagBib (ActChgCrsSta + 79)
+#define ActRcvPagFAQ (ActChgCrsSta + 80)
+#define ActRcvPagCrsLnk (ActChgCrsSta + 81)
+#define ActEditorCrsInf (ActChgCrsSta + 82)
+#define ActEditorTchGui (ActChgCrsSta + 83)
+#define ActEditorSylLec (ActChgCrsSta + 84)
+#define ActEditorSylPra (ActChgCrsSta + 85)
+#define ActEditorBib (ActChgCrsSta + 86)
+#define ActEditorFAQ (ActChgCrsSta + 87)
+#define ActEditorCrsLnk (ActChgCrsSta + 88)
+#define ActPlaTxtEdiCrsInf (ActChgCrsSta + 89)
+#define ActPlaTxtEdiTchGui (ActChgCrsSta + 90)
+#define ActPlaTxtEdiSylLec (ActChgCrsSta + 91)
+#define ActPlaTxtEdiSylPra (ActChgCrsSta + 92)
+#define ActPlaTxtEdiBib (ActChgCrsSta + 93)
+#define ActPlaTxtEdiFAQ (ActChgCrsSta + 94)
+#define ActPlaTxtEdiCrsLnk (ActChgCrsSta + 95)
+#define ActRchTxtEdiCrsInf (ActChgCrsSta + 96)
+#define ActRchTxtEdiTchGui (ActChgCrsSta + 97)
+#define ActRchTxtEdiSylLec (ActChgCrsSta + 98)
+#define ActRchTxtEdiSylPra (ActChgCrsSta + 99)
+#define ActRchTxtEdiBib (ActChgCrsSta + 100)
+#define ActRchTxtEdiFAQ (ActChgCrsSta + 101)
+#define ActRchTxtEdiCrsLnk (ActChgCrsSta + 102)
+#define ActRcvPlaTxtCrsInf (ActChgCrsSta + 103)
+#define ActRcvPlaTxtTchGui (ActChgCrsSta + 104)
+#define ActRcvPlaTxtSylLec (ActChgCrsSta + 105)
+#define ActRcvPlaTxtSylPra (ActChgCrsSta + 106)
+#define ActRcvPlaTxtBib (ActChgCrsSta + 107)
+#define ActRcvPlaTxtFAQ (ActChgCrsSta + 108)
+#define ActRcvPlaTxtCrsLnk (ActChgCrsSta + 109)
+#define ActRcvRchTxtCrsInf (ActChgCrsSta + 110)
+#define ActRcvRchTxtTchGui (ActChgCrsSta + 111)
+#define ActRcvRchTxtSylLec (ActChgCrsSta + 112)
+#define ActRcvRchTxtSylPra (ActChgCrsSta + 113)
+#define ActRcvRchTxtBib (ActChgCrsSta + 114)
+#define ActRcvRchTxtFAQ (ActChgCrsSta + 115)
+#define ActRcvRchTxtCrsLnk (ActChgCrsSta + 116)
-#define ActPrnCrsTT (ActChgCrsSta + 108)
-#define ActEdiCrsTT (ActChgCrsSta + 109)
-#define ActChgCrsTT (ActChgCrsSta + 110)
-#define ActChgCrsTT1stDay (ActChgCrsSta + 111)
+#define ActPrnCrsTT (ActChgCrsSta + 117)
+#define ActEdiCrsTT (ActChgCrsSta + 118)
+#define ActChgCrsTT (ActChgCrsSta + 119)
+#define ActChgCrsTT1stDay (ActChgCrsSta + 120)
/*****************************************************************************/
/***************************** Assessment tab ********************************/
diff --git a/swad_changelog.h b/swad_changelog.h
index 9bac175b..a357088d 100644
--- a/swad_changelog.h
+++ b/swad_changelog.h
@@ -520,6 +520,15 @@ Param
// TODO: Impedir la creación y edición de proyectos si no son editables.
// TODO: No se puede entrar con DNI '1' suponiendo que no tenga password ¿por qué?
// TODO: En la lista de conectados central, poner el logo de la institución a la que pertenece el usuario
+// TODO: Miguel Damas: por defecto, marcar "Permitir que los profesores..." en los test (que ya esté marcado en lugar de desmarcado)
+// TODO: Si el alumno ha marcado "Permitir que los profesores...", entonces pedir confirmación al pulsar el botón azul, para evitar que se envíe por error antes de tiempo
+// TODO: Análisis > Informe: "Informe de uso de SWAD" sale descentrado
+// TODO: Fig_GetAndShowCourseProgramStats
+
+ Version 19.130: Feb 20, 2020 New module swad_program. (281273 lines)
+ 2 changes necessary in database:
+CREATE TABLE IF NOT EXISTS prg_grp (PrgIteCod INT NOT NULL,GrpCod INT NOT NULL,UNIQUE INDEX(PrgIteCod,GrpCod));
+CREATE TABLE IF NOT EXISTS prg_items (PrgIteCod INT NOT NULL AUTO_INCREMENT,CrsCod INT NOT NULL DEFAULT -1,Hidden ENUM('N','Y') NOT NULL DEFAULT 'N',UsrCod INT NOT NULL,StartTime DATETIME NOT NULL,EndTime DATETIME NOT NULL,Title VARCHAR(2047) NOT NULL,Txt TEXT NOT NULL,UNIQUE INDEX(PrgIteCod),INDEX(CrsCod,Hidden));
Version 19.129: Feb 20, 2020 New option for schedule in course. (279240 lines)
Copy the following icons to icon public directory:
diff --git a/swad_database.c b/swad_database.c
index bd969c03..f443fc39 100644
--- a/swad_database.c
+++ b/swad_database.c
@@ -2188,6 +2188,51 @@ mysql> DESCRIBE plugins;
"IP CHAR(15) NOT NULL," // Cns_MAX_BYTES_IP
"UNIQUE INDEX(PlgCod))");
+ /***** Table prg_grp *****/
+/*
+mysql> DESCRIBE prg_grp;
++-----------+---------+------+-----+---------+-------+
+| Field | Type | Null | Key | Default | Extra |
++-----------+---------+------+-----+---------+-------+
+| PrgIteCod | int(11) | NO | PRI | NULL | |
+| GrpCod | int(11) | NO | PRI | NULL | |
++-----------+---------+------+-----+---------+-------+
+2 rows in set (0.06 sec)
+*/
+ DB_CreateTable ("CREATE TABLE IF NOT EXISTS prg_grp ("
+ "PrgIteCod INT NOT NULL,"
+ "GrpCod INT NOT NULL,"
+ "UNIQUE INDEX(PrgIteCod,GrpCod))");
+
+ /***** Table prg_items *****/
+/*
+mysql> DESCRIBE prg_items;
++-----------+---------------+------+-----+---------+----------------+
+| Field | Type | Null | Key | Default | Extra |
++-----------+---------------+------+-----+---------+----------------+
+| PrgIteCod | int(11) | NO | PRI | NULL | auto_increment |
+| CrsCod | int(11) | NO | MUL | -1 | |
+| Hidden | enum('N','Y') | NO | | N | |
+| UsrCod | int(11) | NO | | NULL | |
+| StartTime | datetime | NO | | NULL | |
+| EndTime | datetime | NO | | NULL | |
+| Title | varchar(2047) | NO | | NULL | |
+| Txt | text | NO | | NULL | |
++-----------+---------------+------+-----+---------+----------------+
+8 rows in set (0.01 sec)
+*/
+ DB_CreateTable ("CREATE TABLE IF NOT EXISTS prg_items ("
+ "PrgIteCod INT NOT NULL AUTO_INCREMENT,"
+ "CrsCod INT NOT NULL DEFAULT -1,"
+ "Hidden ENUM('N','Y') NOT NULL DEFAULT 'N',"
+ "UsrCod INT NOT NULL,"
+ "StartTime DATETIME NOT NULL,"
+ "EndTime DATETIME NOT NULL,"
+ "Title VARCHAR(2047) NOT NULL," // Prg_MAX_BYTES_PROGRAM_ITEM_TITLE
+ "Txt TEXT NOT NULL," // Cns_MAX_BYTES_TEXT
+ "UNIQUE INDEX(PrgIteCod),"
+ "INDEX(CrsCod,Hidden))");
+
/***** Table prj_config *****/
/*
mysql> DESCRIBE prj_config;
diff --git a/swad_figure.c b/swad_figure.c
index 3347892a..2527b852 100644
--- a/swad_figure.c
+++ b/swad_figure.c
@@ -145,6 +145,8 @@ static void Fig_WriteRowStatsFileBrowsers3 (const char *NameOfFileZones,
static void Fig_GetAndShowOERsStats (void);
static void Fig_GetNumberOfOERsFromDB (Hie_Level_t Scope,Brw_License_t License,unsigned long NumFiles[2]);
+static void Fig_GetAndShowCourseProgramStats (void); // TODO: Change function from assignments to schedule
+
static void Fig_GetAndShowAssignmentsStats (void);
static void Fig_GetAndShowProjectsStats (void);
static void Fig_GetAndShowTestsStats (void);
@@ -315,6 +317,7 @@ void Fig_ShowFigures (void)
[Fig_DEGREE_TYPES ] = Fig_GetAndShowDegreeTypesStats,
[Fig_FOLDERS_AND_FILES] = Fig_GetAndShowFileBrowsersStats,
[Fig_OER ] = Fig_GetAndShowOERsStats,
+ [Fig_COURSE_PROGRAM ] = Fig_GetAndShowCourseProgramStats,
[Fig_ASSIGNMENTS ] = Fig_GetAndShowAssignmentsStats,
[Fig_PROJECTS ] = Fig_GetAndShowProjectsStats,
[Fig_TESTS ] = Fig_GetAndShowTestsStats,
@@ -2936,6 +2939,68 @@ static void Fig_GetNumberOfOERsFromDB (Hie_Level_t Scope,Brw_License_t License,u
DB_FreeMySQLResult (&mysql_res);
}
+/*****************************************************************************/
+/********************** Show stats about schedule items **********************/
+/*****************************************************************************/
+
+static void Fig_GetAndShowCourseProgramStats (void) // TODO: Change function from assignments to course program items
+ {
+ extern const char *Hlp_ANALYTICS_Figures_assignments;
+ extern const char *Txt_FIGURE_TYPES[Fig_NUM_FIGURES];
+ extern const char *Txt_Number_of_BR_assignments;
+ extern const char *Txt_Number_of_BR_courses_with_BR_assignments;
+ extern const char *Txt_Average_number_BR_of_ASSIG_BR_per_course;
+ extern const char *Txt_Number_of_BR_notifications;
+ unsigned NumAssignments;
+ unsigned NumNotif;
+ unsigned NumCoursesWithAssignments = 0;
+ double NumAssignmentsPerCourse = 0.0;
+
+ /***** Get the number of assignments from this location *****/
+ if ((NumAssignments = Asg_GetNumAssignments (Gbl.Scope.Current,&NumNotif)))
+ if ((NumCoursesWithAssignments = Asg_GetNumCoursesWithAssignments (Gbl.Scope.Current)) != 0)
+ NumAssignmentsPerCourse = (double) NumAssignments /
+ (double) NumCoursesWithAssignments;
+
+ /***** Begin box and table *****/
+ Box_BoxTableBegin (NULL,Txt_FIGURE_TYPES[Fig_ASSIGNMENTS],NULL,
+ Hlp_ANALYTICS_Figures_assignments,Box_NOT_CLOSABLE,2);
+
+ /***** Write table heading *****/
+ HTM_TR_Begin (NULL);
+
+ HTM_TH (1,1,"RM",Txt_Number_of_BR_assignments);
+ HTM_TH (1,1,"RM",Txt_Number_of_BR_courses_with_BR_assignments);
+ HTM_TH (1,1,"RM",Txt_Average_number_BR_of_ASSIG_BR_per_course);
+ HTM_TH (1,1,"RM",Txt_Number_of_BR_notifications);
+
+ HTM_TR_End ();
+
+ /***** Write number of assignments *****/
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"DAT RM\"");
+ HTM_Unsigned (NumAssignments);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT RM\"");
+ HTM_Unsigned (NumCoursesWithAssignments);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT RM\"");
+ HTM_Double2Decimals (NumAssignmentsPerCourse);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"DAT RM\"");
+ HTM_Unsigned (NumNotif);
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** End table and box *****/
+ Box_BoxTableEnd ();
+ }
+
/*****************************************************************************/
/************************ Show stats about assignments ***********************/
/*****************************************************************************/
diff --git a/swad_figure.h b/swad_figure.h
index e6e15325..73aeadd8 100644
--- a/swad_figure.h
+++ b/swad_figure.h
@@ -31,7 +31,7 @@
/************************** Public types and constants ***********************/
/*****************************************************************************/
-#define Fig_NUM_FIGURES 28
+#define Fig_NUM_FIGURES 29
typedef enum
{
Fig_USERS, // Number of users
@@ -41,6 +41,7 @@ typedef enum
Fig_DEGREE_TYPES, // Number of degrees in each type of degree
Fig_FOLDERS_AND_FILES, // Number of folders and files
Fig_OER, // Number of OERs (Open Educational Resources)
+ Fig_COURSE_PROGRAM, // Number of program items
Fig_ASSIGNMENTS, // Number of assignments
Fig_PROJECTS, // Number of projects
Fig_TESTS, // Number of test questions
diff --git a/swad_global.c b/swad_global.c
index b7879a08..0d9488e0 100644
--- a/swad_global.c
+++ b/swad_global.c
@@ -44,6 +44,7 @@
#include "swad_global.h"
#include "swad_icon.h"
#include "swad_parameter.h"
+#include "swad_program.h"
#include "swad_project.h"
#include "swad_role.h"
#include "swad_setting.h"
@@ -296,6 +297,11 @@ void Gbl_InitializeGlobals (void)
Gbl.Search.Str[0] = '\0';
Gbl.Search.LogSearch = false;
+ Gbl.Prg.LstIsRead = false; // List is not read
+ Gbl.Prg.Num = 0;
+ Gbl.Prg.LstPrgIteCods = NULL;
+ Gbl.Prg.SelectedOrder = Prg_ORDER_DEFAULT;
+
Gbl.Asgs.LstIsRead = false; // List is not read
Gbl.Asgs.Num = 0;
Gbl.Asgs.LstAsgCods = NULL;
diff --git a/swad_global.h b/swad_global.h
index 3854864a..362c6801 100644
--- a/swad_global.h
+++ b/swad_global.h
@@ -540,6 +540,15 @@ struct Globals
char TmpDir[NAME_MAX + 1];
} ZIP;
} FileBrowser; // Struct used for a file browser
+ struct
+ {
+ bool LstIsRead; // Is the list already read from database, or it needs to be read?
+ unsigned Num; // Number of schedule items
+ long *LstPrgIteCods; // List of schedule items codes
+ Dat_StartEndTime_t SelectedOrder;
+ long PrgIteCodToEdit; // Used as parameter in contextual links
+ unsigned CurrentPage;
+ } Prg;
struct
{
bool LstIsRead; // Is the list already read from database, or it needs to be read?
diff --git a/swad_help_URL.c b/swad_help_URL.c
index b6f2dbea..ba1f904d 100644
--- a/swad_help_URL.c
+++ b/swad_help_URL.c
@@ -778,6 +778,69 @@ const char *Hlp_COURSE_Information_edit =
"COURSE.Information.en#edit";
#endif
+const char *Hlp_COURSE_Program =
+#if L==1
+ "COURSE.Program.es";
+#elif L==2
+ "COURSE.Program.en";
+#elif L==3
+ "COURSE.Program.en";
+#elif L==4
+ "COURSE.Program.es";
+#elif L==5
+ "COURSE.Program.en";
+#elif L==6
+ "COURSE.Program.es";
+#elif L==7
+ "COURSE.Program.en";
+#elif L==8
+ "COURSE.Program.en";
+#elif L==9
+ "COURSE.Program.en";
+#endif
+
+const char *Hlp_COURSE_Program_new_item =
+#if L==1
+ "COURSE.Program.es#nuevo-item";
+#elif L==2
+ "COURSE.Program.en#new-item";
+#elif L==3
+ "COURSE.Program.en#new-item";
+#elif L==4
+ "COURSE.Program.es#nuevo-item";
+#elif L==5
+ "COURSE.Program.en#new-item";
+#elif L==6
+ "COURSE.Program.es#nuevo-item";
+#elif L==7
+ "COURSE.Program.en#new-item";
+#elif L==8
+ "COURSE.Program.en#new-item";
+#elif L==9
+ "COURSE.Program.en#new-item";
+#endif
+
+const char *Hlp_COURSE_Program_edit_item =
+#if L==1
+ "COURSE.Program.es#editar-item";
+#elif L==2
+ "COURSE.Program.en#edit-item";
+#elif L==3
+ "COURSE.Program.en#edit-item";
+#elif L==4
+ "COURSE.Program.es#editar-item";
+#elif L==5
+ "COURSE.Program.en#edit-item";
+#elif L==6
+ "COURSE.Program.es#editar-item";
+#elif L==7
+ "COURSE.Program.en#edit-item";
+#elif L==8
+ "COURSE.Program.en#edit-item";
+#elif L==9
+ "COURSE.Program.en#edit-item";
+#endif
+
const char *Hlp_COURSE_Guide =
#if L==1
"COURSE.Guide.es";
diff --git a/swad_menu.c b/swad_menu.c
index c66b9510..afb38064 100644
--- a/swad_menu.c
+++ b/swad_menu.c
@@ -101,7 +101,7 @@ static const Act_Action_t Mnu_MenuActions[Tab_NUM_TABS][Act_MAX_OPTIONS_IN_MENU_
},
[TabCrs] = {
[ 0] = ActSeeCrsInf,
- [ 1] = ActSeeScd,
+ [ 1] = ActSeePrg,
[ 2] = ActSeeTchGui,
[ 3] = ActSeeSyl,
[ 4] = ActSeeBib,
diff --git a/swad_pagination.c b/swad_pagination.c
index 45fe8896..b31b68f4 100644
--- a/swad_pagination.c
+++ b/swad_pagination.c
@@ -37,6 +37,7 @@
#include "swad_global.h"
#include "swad_HTML.h"
#include "swad_parameter.h"
+#include "swad_program.h"
#include "swad_project.h"
/*****************************************************************************/
@@ -53,6 +54,7 @@ extern const Act_Action_t For_ActionsSeePstFor[For_NUM_TYPES_FORUM];
static const char *Pag_ParamNumPag[Pag_NUM_WHAT_PAGINATE] =
{
+ [Pag_COURSE_PROGRAM ] = "NumPagPrg",
[Pag_ASSIGNMENTS ] = "NumPagAsg",
[Pag_PROJECTS ] = "NumPagPrj",
[Pag_GAMES ] = "NumPagGam",
@@ -165,6 +167,12 @@ void Pag_WriteLinksToPages (Pag_WhatPaginate_t WhatPaginate,
{
switch (WhatPaginate)
{
+ case Pag_COURSE_PROGRAM:
+ Frm_StartFormAnchor (ActSeePrg,Pagination->Anchor);
+ Pag_PutHiddenParamPagNum (WhatPaginate,1);
+ Prg_PutHiddenParamPrgOrder ();
+ Grp_PutParamWhichGrps ();
+ break;
case Pag_ASSIGNMENTS:
Frm_StartFormAnchor (ActSeeAsg,Pagination->Anchor);
Pag_PutHiddenParamPagNum (WhatPaginate,1);
@@ -279,6 +287,12 @@ void Pag_WriteLinksToPages (Pag_WhatPaginate_t WhatPaginate,
{
switch (WhatPaginate)
{
+ case Pag_COURSE_PROGRAM:
+ Frm_StartFormAnchor (ActSeePrg,Pagination->Anchor);
+ Pag_PutHiddenParamPagNum (WhatPaginate,1);
+ Prg_PutHiddenParamPrgOrder ();
+ Grp_PutParamWhichGrps ();
+ break;
case Pag_ASSIGNMENTS:
Frm_StartFormAnchor (ActSeeAsg,Pagination->Anchor);
Pag_PutHiddenParamPagNum (WhatPaginate,1);
@@ -379,6 +393,12 @@ void Pag_WriteLinksToPages (Pag_WhatPaginate_t WhatPaginate,
{
switch (WhatPaginate)
{
+ case Pag_COURSE_PROGRAM:
+ Frm_StartFormAnchor (ActSeePrg,Pagination->Anchor);
+ Pag_PutHiddenParamPagNum (WhatPaginate,Pagination->LeftPage);
+ Prg_PutHiddenParamPrgOrder ();
+ Grp_PutParamWhichGrps ();
+ break;
case Pag_ASSIGNMENTS:
Frm_StartFormAnchor (ActSeeAsg,Pagination->Anchor);
Pag_PutHiddenParamPagNum (WhatPaginate,Pagination->LeftPage);
@@ -491,6 +511,12 @@ void Pag_WriteLinksToPages (Pag_WhatPaginate_t WhatPaginate,
{
switch (WhatPaginate)
{
+ case Pag_COURSE_PROGRAM:
+ Frm_StartFormAnchor (ActSeePrg,Pagination->Anchor);
+ Pag_PutHiddenParamPagNum (WhatPaginate,NumPage);
+ Prg_PutHiddenParamPrgOrder ();
+ Grp_PutParamWhichGrps ();
+ break;
case Pag_ASSIGNMENTS:
Frm_StartFormAnchor (ActSeeAsg,Pagination->Anchor);
Pag_PutHiddenParamPagNum (WhatPaginate,NumPage);
@@ -590,6 +616,12 @@ void Pag_WriteLinksToPages (Pag_WhatPaginate_t WhatPaginate,
}
switch (WhatPaginate)
{
+ case Pag_COURSE_PROGRAM:
+ Frm_StartFormAnchor (ActSeePrg,Pagination->Anchor);
+ Pag_PutHiddenParamPagNum (WhatPaginate,Pagination->RightPage);
+ Prg_PutHiddenParamPrgOrder ();
+ Grp_PutParamWhichGrps ();
+ break;
case Pag_ASSIGNMENTS:
Frm_StartFormAnchor (ActSeeAsg,Pagination->Anchor);
Pag_PutHiddenParamPagNum (WhatPaginate,Pagination->RightPage);
@@ -690,6 +722,12 @@ void Pag_WriteLinksToPages (Pag_WhatPaginate_t WhatPaginate,
}
switch (WhatPaginate)
{
+ case Pag_COURSE_PROGRAM:
+ Frm_StartFormAnchor (ActSeePrg,Pagination->Anchor);
+ Pag_PutHiddenParamPagNum (WhatPaginate,Pagination->NumPags);
+ Prg_PutHiddenParamPrgOrder ();
+ Grp_PutParamWhichGrps ();
+ break;
case Pag_ASSIGNMENTS:
Frm_StartFormAnchor (ActSeeAsg,Pagination->Anchor);
Pag_PutHiddenParamPagNum (WhatPaginate,Pagination->NumPags);
diff --git a/swad_pagination.h b/swad_pagination.h
index 18e99487..a7493d1a 100644
--- a/swad_pagination.h
+++ b/swad_pagination.h
@@ -37,20 +37,21 @@
/******************************** Public types *******************************/
/*****************************************************************************/
-#define Pag_NUM_WHAT_PAGINATE 11
+#define Pag_NUM_WHAT_PAGINATE 12
typedef enum
{
- Pag_ASSIGNMENTS = 0,
- Pag_PROJECTS = 1,
- Pag_GAMES = 2,
- Pag_SURVEYS = 3,
- Pag_ATT_EVENTS = 4,
- Pag_THREADS_FORUM = 5,
- Pag_POSTS_FORUM = 6,
- Pag_MESSAGES_RECEIVED = 7,
- Pag_MESSAGES_SENT = 8,
- Pag_MY_AGENDA = 9,
- Pag_ANOTHER_AGENDA = 10,
+ Pag_COURSE_PROGRAM = 0,
+ Pag_ASSIGNMENTS = 1,
+ Pag_PROJECTS = 2,
+ Pag_GAMES = 3,
+ Pag_SURVEYS = 4,
+ Pag_ATT_EVENTS = 5,
+ Pag_THREADS_FORUM = 6,
+ Pag_POSTS_FORUM = 7,
+ Pag_MESSAGES_RECEIVED = 8,
+ Pag_MESSAGES_SENT = 9,
+ Pag_MY_AGENDA = 10,
+ Pag_ANOTHER_AGENDA = 11,
} Pag_WhatPaginate_t;
struct Pagination // Used for threads and messages pagination
diff --git a/swad_program.c b/swad_program.c
new file mode 100644
index 00000000..2a123a1e
--- /dev/null
+++ b/swad_program.c
@@ -0,0 +1,1701 @@
+// swad_program.c: course program
+
+/*
+ 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-2020 Antonio Cañas Vargas
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+/*****************************************************************************/
+/********************************* Headers ***********************************/
+/*****************************************************************************/
+
+#define _GNU_SOURCE // For asprintf
+#include // For PATH_MAX
+#include // For NULL
+#include // For asprintf
+#include // For calloc
+#include // For string functions
+
+#include "swad_box.h"
+#include "swad_database.h"
+#include "swad_figure.h"
+#include "swad_form.h"
+#include "swad_global.h"
+#include "swad_group.h"
+#include "swad_HTML.h"
+#include "swad_notification.h"
+#include "swad_pagination.h"
+#include "swad_parameter.h"
+#include "swad_photo.h"
+#include "swad_program.h"
+#include "swad_role.h"
+#include "swad_setting.h"
+#include "swad_string.h"
+
+/*****************************************************************************/
+/************** External global variables from others modules ****************/
+/*****************************************************************************/
+
+extern struct Globals Gbl;
+
+/*****************************************************************************/
+/***************************** Private constants *****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/******************************* Private types *******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Private variables *****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Private prototypes ****************************/
+/*****************************************************************************/
+
+static void Prg_ShowAllPrgItems (void);
+static void Prg_PutHeadForSeeing (bool PrintView);
+static bool Prg_CheckIfICanCreatePrgItems (void);
+static void Prg_PutIconsListPrgItems (void);
+static void Prg_PutIconToCreateNewPrgItem (void);
+static void Prg_PutButtonToCreateNewPrgItem (void);
+static void Prg_ParamsWhichGroupsToShow (void);
+static void Prg_ShowOnePrgItem (long PrgIteCod,bool PrintView);
+static void Prg_WritePrgItemAuthor (struct ProgramItem *PrgItem);
+static void Prg_GetParamPrgOrder (void);
+
+static void Prg_PutFormsToRemEditOnePrgItem (const struct ProgramItem *PrgItem,
+ const char *Anchor);
+static void Prg_PutParams (void);
+static void Prg_GetDataOfPrgItem (struct ProgramItem *PrgItem,
+ MYSQL_RES **mysql_res,
+ unsigned long NumRows);
+static void Prg_ResetPrgItem (struct ProgramItem *PrgItem);
+static void Prg_GetPrgItemTxtFromDB (long PrgIteCod,char Txt[Cns_MAX_BYTES_TEXT + 1]);
+static void Prg_PutParamPrgItemCod (long PrgIteCod);
+static bool Prg_CheckIfSimilarPrgItemExists (const char *Field,const char *Value,long PrgIteCod);
+static void Prg_ShowLstGrpsToEditPrgItem (long PrgIteCod);
+static void Prg_CreatePrgItem (struct ProgramItem *PrgItem,const char *Txt);
+static void Prg_UpdatePrgItem (struct ProgramItem *PrgItem,const char *Txt);
+static bool Prg_CheckIfPrgItemIsAssociatedToGrps (long PrgIteCod);
+static void Prg_RemoveAllTheGrpsAssociatedToAPrgItem (long PrgIteCod);
+static void Prg_CreateGrps (long PrgIteCod);
+static void Prg_GetAndWriteNamesOfGrpsAssociatedToPrgItem (struct ProgramItem *PrgItem);
+static bool Prg_CheckIfIBelongToCrsOrGrpsThisPrgItem (long PrgIteCod);
+
+/*****************************************************************************/
+/************************ List all the program items *************************/
+/*****************************************************************************/
+
+void Prg_SeeCourseProgram (void)
+ {
+ /***** Get parameters *****/
+ Prg_GetParamPrgOrder ();
+ Grp_GetParamWhichGrps ();
+ Gbl.Prg.CurrentPage = Pag_GetParamPagNum (Pag_COURSE_PROGRAM);
+
+ /***** Show all the program items *****/
+ Prg_ShowAllPrgItems ();
+ }
+
+/*****************************************************************************/
+/*********************** Show all the program items **************************/
+/*****************************************************************************/
+
+static void Prg_ShowAllPrgItems (void)
+ {
+ extern const char *Hlp_COURSE_Program;
+ extern const char *Txt_Course_program;
+ extern const char *Txt_No_items;
+ struct Pagination Pagination;
+ unsigned NumAsg;
+
+ /***** Get list of program items *****/
+ Prg_GetListPrgItems ();
+
+ /***** Compute variables related to pagination *****/
+ Pagination.NumItems = Gbl.Prg.Num;
+ Pagination.CurrentPage = (int) Gbl.Prg.CurrentPage;
+ Pag_CalculatePagination (&Pagination);
+ Gbl.Prg.CurrentPage = (unsigned) Pagination.CurrentPage;
+
+ /***** Begin box *****/
+ Box_BoxBegin ("100%",Txt_Course_program,Prg_PutIconsListPrgItems,
+ Hlp_COURSE_Program,Box_NOT_CLOSABLE);
+
+ /***** Select whether show only my groups or all groups *****/
+ if (Gbl.Crs.Grps.NumGrps)
+ {
+ Set_StartSettingsHead ();
+ Grp_ShowFormToSelWhichGrps (ActSeePrg,Prg_ParamsWhichGroupsToShow);
+ Set_EndSettingsHead ();
+ }
+
+ /***** Write links to pages *****/
+ Pag_WriteLinksToPagesCentered (Pag_COURSE_PROGRAM,
+ &Pagination,
+ 0);
+
+ if (Gbl.Prg.Num)
+ {
+ /***** Table head *****/
+ HTM_TABLE_BeginWideMarginPadding (2);
+ Prg_PutHeadForSeeing (false); // Not print view
+
+ /***** Write all the program items *****/
+ for (NumAsg = Pagination.FirstItemVisible;
+ NumAsg <= Pagination.LastItemVisible;
+ NumAsg++)
+ Prg_ShowOnePrgItem (Gbl.Prg.LstPrgIteCods[NumAsg - 1],
+ false); // Not print view
+
+ /***** End table *****/
+ HTM_TABLE_End ();
+ }
+ else // No program items created
+ Ale_ShowAlert (Ale_INFO,Txt_No_items);
+
+ /***** Write again links to pages *****/
+ Pag_WriteLinksToPagesCentered (Pag_COURSE_PROGRAM,
+ &Pagination,
+ 0);
+
+ /***** Button to create a new program item *****/
+ if (Prg_CheckIfICanCreatePrgItems ())
+ Prg_PutButtonToCreateNewPrgItem ();
+
+ /***** End box *****/
+ Box_BoxEnd ();
+
+ /***** Free list of program items *****/
+ Prg_FreeListPrgItems ();
+ }
+
+/*****************************************************************************/
+/***************** Write header with fields of a program item ****************/
+/*****************************************************************************/
+
+static void Prg_PutHeadForSeeing (bool PrintView)
+ {
+ extern const char *Txt_START_END_TIME_HELP[Dat_NUM_START_END_TIME];
+ extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
+ extern const char *Txt_Item;
+ Dat_StartEndTime_t Order;
+
+ HTM_TR_Begin (NULL);
+
+ HTM_TH (1,1,"CONTEXT_COL",NULL); // Column for contextual icons
+ for (Order = Dat_START_TIME;
+ Order <= Dat_END_TIME;
+ Order++)
+ {
+ HTM_TH_Begin (1,1,"LM");
+
+ if (!PrintView)
+ {
+ Frm_StartForm (ActSeePrg);
+ Grp_PutParamWhichGrps ();
+ Pag_PutHiddenParamPagNum (Pag_COURSE_PROGRAM,Gbl.Prg.CurrentPage);
+ Par_PutHiddenParamUnsigned (NULL,"Order",(unsigned) Order);
+ HTM_BUTTON_SUBMIT_Begin (Txt_START_END_TIME_HELP[Order],"BT_LINK TIT_TBL",NULL);
+ if (Order == Gbl.Prg.SelectedOrder)
+ HTM_U_Begin ();
+ }
+ HTM_Txt (Txt_START_END_TIME[Order]);
+ if (!PrintView)
+ {
+ if (Order == Gbl.Prg.SelectedOrder)
+ HTM_U_End ();
+ HTM_BUTTON_End ();
+ Frm_EndForm ();
+ }
+
+ HTM_TH_End ();
+ }
+ HTM_TH (1,1,"LM",Txt_Item);
+
+ HTM_TR_End ();
+ }
+
+/*****************************************************************************/
+/******************* Check if I can create program items *********************/
+/*****************************************************************************/
+
+static bool Prg_CheckIfICanCreatePrgItems (void)
+ {
+ return (bool) (Gbl.Usrs.Me.Role.Logged == Rol_TCH ||
+ Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM);
+ }
+
+/*****************************************************************************/
+/************** Put contextual icons in list of program items ****************/
+/*****************************************************************************/
+
+static void Prg_PutIconsListPrgItems (void)
+ {
+ /***** Put icon to create a new program item *****/
+ if (Prg_CheckIfICanCreatePrgItems ())
+ Prg_PutIconToCreateNewPrgItem ();
+
+ /***** Put icon to show a figure *****/
+ Gbl.Figures.FigureType = Fig_COURSE_PROGRAM;
+ Fig_PutIconToShowFigure ();
+ }
+
+/*****************************************************************************/
+/****************** Put icon to create a new program item ********************/
+/*****************************************************************************/
+
+static void Prg_PutIconToCreateNewPrgItem (void)
+ {
+ extern const char *Txt_New_item;
+
+ /***** Put form to create a new program item *****/
+ Gbl.Prg.PrgIteCodToEdit = -1L;
+ Ico_PutContextualIconToAdd (ActFrmNewPrgIte,NULL,Prg_PutParams,
+ Txt_New_item);
+ }
+
+/*****************************************************************************/
+/***************** Put button to create a new program item *******************/
+/*****************************************************************************/
+
+static void Prg_PutButtonToCreateNewPrgItem (void)
+ {
+ extern const char *Txt_New_item;
+
+ Gbl.Prg.PrgIteCodToEdit = -1L;
+ Frm_StartForm (ActFrmNewPrgIte);
+ Prg_PutParams ();
+ Btn_PutConfirmButton (Txt_New_item);
+ Frm_EndForm ();
+ }
+
+/*****************************************************************************/
+/**************** Put params to select which groups to show ******************/
+/*****************************************************************************/
+
+static void Prg_ParamsWhichGroupsToShow (void)
+ {
+ Prg_PutHiddenParamPrgOrder ();
+ Pag_PutHiddenParamPagNum (Pag_COURSE_PROGRAM,Gbl.Prg.CurrentPage);
+ }
+
+/*****************************************************************************/
+/******************* Show print view of one program item *********************/
+/*****************************************************************************/
+
+void Prg_PrintOnePrgItem (void)
+ {
+ long PrgIteCod;
+
+ /***** Get the code of the program item *****/
+ PrgIteCod = Prg_GetParamPrgItemCod ();
+
+ /***** Write header *****/
+ Lay_WriteHeaderClassPhoto (true,false,
+ Gbl.Hierarchy.Ins.InsCod,
+ Gbl.Hierarchy.Deg.DegCod,
+ Gbl.Hierarchy.Crs.CrsCod);
+
+ /***** Table head *****/
+ HTM_TABLE_BeginWideMarginPadding (2);
+ Prg_PutHeadForSeeing (true); // Print view
+
+ /***** Write program item *****/
+ Prg_ShowOnePrgItem (PrgIteCod,
+ true); // Print view
+
+ /***** End table *****/
+ HTM_TABLE_End ();
+ }
+
+/*****************************************************************************/
+/************************** Show one program item ****************************/
+/*****************************************************************************/
+
+static void Prg_ShowOnePrgItem (long PrgIteCod,bool PrintView)
+ {
+ char *Anchor = NULL;
+ static unsigned UniqueId = 0;
+ char *Id;
+ struct ProgramItem PrgItem;
+ Dat_StartEndTime_t StartEndTime;
+ char Txt[Cns_MAX_BYTES_TEXT + 1];
+
+ /***** Get data of this program item *****/
+ PrgItem.PrgIteCod = PrgIteCod;
+ Prg_GetDataOfPrgItemByCod (&PrgItem);
+
+ /***** Set anchor string *****/
+ Frm_SetAnchorStr (PrgItem.PrgIteCod,&Anchor);
+
+ /***** Write first row of data of this program item *****/
+ HTM_TR_Begin (NULL);
+
+ /* Forms to remove/edit this program item */
+ if (PrintView)
+ HTM_TD_Begin ("rowspan=\"2\" class=\"CONTEXT_COL\"");
+ else
+ {
+ HTM_TD_Begin ("rowspan=\"2\" class=\"CONTEXT_COL COLOR%u\"",Gbl.RowEvenOdd);
+ Prg_PutFormsToRemEditOnePrgItem (&PrgItem,Anchor);
+ }
+ HTM_TD_End ();
+
+ /* Start/end date/time */
+ UniqueId++;
+
+ for (StartEndTime = (Dat_StartEndTime_t) 0;
+ StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
+ StartEndTime++)
+ {
+ if (asprintf (&Id,"scd_date_%u_%u",(unsigned) StartEndTime,UniqueId) < 0)
+ Lay_NotEnoughMemoryExit ();
+ if (PrintView)
+ HTM_TD_Begin ("id=\"%s\" class=\"%s LB\"",
+ Id,
+ PrgItem.Hidden ? (PrgItem.Open ? "DATE_GREEN_LIGHT" :
+ "DATE_RED_LIGHT") :
+ (PrgItem.Open ? "DATE_GREEN" :
+ "DATE_RED"));
+ else
+ HTM_TD_Begin ("id=\"%s\" class=\"%s LB COLOR%u\"",
+ Id,
+ PrgItem.Hidden ? (PrgItem.Open ? "DATE_GREEN_LIGHT" :
+ "DATE_RED_LIGHT") :
+ (PrgItem.Open ? "DATE_GREEN" :
+ "DATE_RED"),
+ Gbl.RowEvenOdd);
+ Dat_WriteLocalDateHMSFromUTC (Id,PrgItem.TimeUTC[StartEndTime],
+ Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK,
+ true,true,true,0x7);
+ HTM_TD_End ();
+ free (Id);
+ }
+
+ /* Schedule item title */
+ if (PrintView)
+ HTM_TD_Begin ("class=\"%s LT\"",
+ PrgItem.Hidden ? "ASG_TITLE_LIGHT" :
+ "ASG_TITLE");
+ else
+ HTM_TD_Begin ("class=\"%s LT COLOR%u\"",
+ PrgItem.Hidden ? "ASG_TITLE_LIGHT" :
+ "ASG_TITLE",
+ Gbl.RowEvenOdd);
+ HTM_ARTICLE_Begin (Anchor);
+ HTM_Txt (PrgItem.Title);
+ HTM_ARTICLE_End ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Write second row of data of this program item *****/
+ HTM_TR_Begin (NULL);
+
+ /* Author of the program item */
+ if (PrintView)
+ HTM_TD_Begin ("colspan=\"2\" class=\"LT\"");
+ else
+ HTM_TD_Begin ("colspan=\"2\" class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
+ Prg_WritePrgItemAuthor (&PrgItem);
+ HTM_TD_End ();
+
+ /* Text of the program item */
+ Prg_GetPrgItemTxtFromDB (PrgItem.PrgIteCod,Txt);
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Txt,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to recpectful HTML
+ Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
+ if (PrintView)
+ HTM_TD_Begin ("colspan=\"2\" class=\"LT\"");
+ else
+ HTM_TD_Begin ("colspan=\"2\" class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
+ if (Gbl.Crs.Grps.NumGrps)
+ Prg_GetAndWriteNamesOfGrpsAssociatedToPrgItem (&PrgItem);
+ HTM_DIV_Begin ("class=\"PAR %s\"",PrgItem.Hidden ? "DAT_LIGHT" :
+ "DAT");
+ HTM_Txt (Txt);
+ HTM_DIV_End ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Free anchor string *****/
+ Frm_FreeAnchorStr (Anchor);
+
+ Gbl.RowEvenOdd = 1 - Gbl.RowEvenOdd;
+
+ /***** Mark possible notification as seen *****/
+ Ntf_MarkNotifAsSeen (Ntf_EVENT_ASSIGNMENT,
+ PrgIteCod,Gbl.Hierarchy.Crs.CrsCod,
+ Gbl.Usrs.Me.UsrDat.UsrCod);
+ }
+
+/*****************************************************************************/
+/********************* Write the author of a program item ********************/
+/*****************************************************************************/
+
+static void Prg_WritePrgItemAuthor (struct ProgramItem *PrgItem)
+ {
+ Usr_WriteAuthor1Line (PrgItem->UsrCod,PrgItem->Hidden);
+ }
+
+/*****************************************************************************/
+/******* Get parameter with the type or order in list of program items *******/
+/*****************************************************************************/
+
+static void Prg_GetParamPrgOrder (void)
+ {
+ Gbl.Prg.SelectedOrder = (Dat_StartEndTime_t)
+ Par_GetParToUnsignedLong ("Order",
+ 0,
+ Dat_NUM_START_END_TIME - 1,
+ (unsigned long) Prg_ORDER_DEFAULT);
+ }
+
+/*****************************************************************************/
+/** Put a hidden parameter with the type of order in list of program items ***/
+/*****************************************************************************/
+
+void Prg_PutHiddenParamPrgOrder (void)
+ {
+ Par_PutHiddenParamUnsigned (NULL,"Order",(unsigned) Gbl.Prg.SelectedOrder);
+ }
+
+/*****************************************************************************/
+/**************** Put a link (form) to edit one program item *****************/
+/*****************************************************************************/
+
+static void Prg_PutFormsToRemEditOnePrgItem (const struct ProgramItem *PrgItem,
+ const char *Anchor)
+ {
+ Gbl.Prg.PrgIteCodToEdit = PrgItem->PrgIteCod; // Used as parameter in contextual links
+
+ switch (Gbl.Usrs.Me.Role.Logged)
+ {
+ case Rol_TCH:
+ case Rol_SYS_ADM:
+ /***** Put form to remove program item *****/
+ Ico_PutContextualIconToRemove (ActReqRemAsg,Prg_PutParams);
+
+ /***** Put form to hide/show program item *****/
+ if (PrgItem->Hidden)
+ Ico_PutContextualIconToUnhide (ActShoAsg,Anchor,Prg_PutParams);
+ else
+ Ico_PutContextualIconToHide (ActHidAsg,Anchor,Prg_PutParams);
+
+ /***** Put form to edit program item *****/
+ Ico_PutContextualIconToEdit (ActEdiOneAsg,Prg_PutParams);
+ /* falls through */
+ /* no break */
+ case Rol_STD:
+ case Rol_NET:
+ /***** Put form to print program item *****/
+ Ico_PutContextualIconToPrint (ActPrnOneAsg,Prg_PutParams);
+ break;
+ default:
+ break;
+ }
+ }
+
+/*****************************************************************************/
+/******************** Params used to edit a program item *********************/
+/*****************************************************************************/
+
+static void Prg_PutParams (void)
+ {
+ if (Gbl.Prg.PrgIteCodToEdit > 0)
+ Prg_PutParamPrgItemCod (Gbl.Prg.PrgIteCodToEdit);
+ Prg_PutHiddenParamPrgOrder ();
+ Grp_PutParamWhichGrps ();
+ Pag_PutHiddenParamPagNum (Pag_COURSE_PROGRAM,Gbl.Prg.CurrentPage);
+ }
+
+/*****************************************************************************/
+/*********************** List all the program items **************************/
+/*****************************************************************************/
+
+void Prg_GetListPrgItems (void)
+ {
+ static const char *HiddenSubQuery[Rol_NUM_ROLES] =
+ {
+ [Rol_UNK ] = " AND Hidden='N'",
+ [Rol_GST ] = " AND Hidden='N'",
+ [Rol_USR ] = " AND Hidden='N'",
+ [Rol_STD ] = " AND Hidden='N'",
+ [Rol_NET ] = " AND Hidden='N'",
+ [Rol_TCH ] = "",
+ [Rol_DEG_ADM] = " AND Hidden='N'",
+ [Rol_CTR_ADM] = " AND Hidden='N'",
+ [Rol_INS_ADM] = " AND Hidden='N'",
+ [Rol_SYS_ADM] = "",
+ };
+ static const char *OrderBySubQuery[Dat_NUM_START_END_TIME] =
+ {
+ [Dat_START_TIME] = "StartTime DESC,EndTime DESC,Title DESC",
+ [Dat_END_TIME ] = "EndTime DESC,StartTime DESC,Title DESC",
+ };
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned long NumRows;
+ unsigned NumAsg;
+
+ if (Gbl.Prg.LstIsRead)
+ Prg_FreeListPrgItems ();
+
+ /***** Get list of program items from database *****/
+ if (Gbl.Crs.Grps.WhichGrps == Grp_MY_GROUPS)
+ NumRows = DB_QuerySELECT (&mysql_res,"can not get program items",
+ "SELECT PrgIteCod"
+ " FROM prg_items"
+ " WHERE CrsCod=%ld%s"
+ " AND (PrgIteCod NOT IN (SELECT PrgIteCod FROM prg_grp) OR"
+ " PrgIteCod IN (SELECT prg_grp.PrgIteCod FROM prg_grp,crs_grp_usr"
+ " WHERE crs_grp_usr.UsrCod=%ld AND prg_grp.GrpCod=crs_grp_usr.GrpCod))"
+ " ORDER BY %s",
+ Gbl.Hierarchy.Crs.CrsCod,
+ HiddenSubQuery[Gbl.Usrs.Me.Role.Logged],
+ Gbl.Usrs.Me.UsrDat.UsrCod,
+ OrderBySubQuery[Gbl.Prg.SelectedOrder]);
+ else // Gbl.Crs.Grps.WhichGrps == Grp_ALL_GROUPS
+ NumRows = DB_QuerySELECT (&mysql_res,"can not get program items",
+ "SELECT PrgIteCod"
+ " FROM prg_items"
+ " WHERE CrsCod=%ld%s"
+ " ORDER BY %s",
+ Gbl.Hierarchy.Crs.CrsCod,
+ HiddenSubQuery[Gbl.Usrs.Me.Role.Logged],
+ OrderBySubQuery[Gbl.Prg.SelectedOrder]);
+
+ if (NumRows) // Assignments found...
+ {
+ Gbl.Prg.Num = (unsigned) NumRows;
+
+ /***** Create list of program items *****/
+ if ((Gbl.Prg.LstPrgIteCods = (long *) calloc (NumRows,sizeof (long))) == NULL)
+ Lay_NotEnoughMemoryExit ();
+
+ /***** Get the program items codes *****/
+ for (NumAsg = 0;
+ NumAsg < Gbl.Prg.Num;
+ NumAsg++)
+ {
+ /* Get next program item code */
+ row = mysql_fetch_row (mysql_res);
+ if ((Gbl.Prg.LstPrgIteCods[NumAsg] = Str_ConvertStrCodToLongCod (row[0])) < 0)
+ Lay_ShowErrorAndExit ("Error: wrong program item code.");
+ }
+ }
+ else
+ Gbl.Prg.Num = 0;
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+
+ Gbl.Prg.LstIsRead = true;
+ }
+
+/*****************************************************************************/
+/****************** Get program item data using its code *********************/
+/*****************************************************************************/
+
+void Prg_GetDataOfPrgItemByCod (struct ProgramItem *PrgItem)
+ {
+ MYSQL_RES *mysql_res;
+ unsigned long NumRows;
+
+ if (PrgItem->PrgIteCod > 0)
+ {
+ /***** Build query *****/
+ NumRows = DB_QuerySELECT (&mysql_res,"can not get program item data",
+ "SELECT PrgIteCod,Hidden,UsrCod,"
+ "UNIX_TIMESTAMP(StartTime),"
+ "UNIX_TIMESTAMP(EndTime),"
+ "NOW() BETWEEN StartTime AND EndTime,"
+ "Title"
+ " FROM prg_items"
+ " WHERE PrgIteCod=%ld AND CrsCod=%ld",
+ PrgItem->PrgIteCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /***** Get data of program item *****/
+ Prg_GetDataOfPrgItem (PrgItem,&mysql_res,NumRows);
+ }
+ else
+ {
+ /***** Clear all program item data *****/
+ PrgItem->PrgIteCod = -1L;
+ Prg_ResetPrgItem (PrgItem);
+ }
+ }
+
+/*****************************************************************************/
+/************************* Get program item data *****************************/
+/*****************************************************************************/
+
+static void Prg_GetDataOfPrgItem (struct ProgramItem *PrgItem,
+ MYSQL_RES **mysql_res,
+ unsigned long NumRows)
+ {
+ MYSQL_ROW row;
+
+ /***** Clear all program item data *****/
+ Prg_ResetPrgItem (PrgItem);
+
+ /***** Get data of program item from database *****/
+ if (NumRows) // Schedule item found...
+ {
+ /* Get row */
+ row = mysql_fetch_row (*mysql_res);
+
+ /* Get code of the program item (row[0]) */
+ PrgItem->PrgIteCod = Str_ConvertStrCodToLongCod (row[0]);
+
+ /* Get whether the program item is hidden or not (row[1]) */
+ PrgItem->Hidden = (row[1][0] == 'Y');
+
+ /* Get author of the program item (row[2]) */
+ PrgItem->UsrCod = Str_ConvertStrCodToLongCod (row[2]);
+
+ /* Get start date (row[3] holds the start UTC time) */
+ PrgItem->TimeUTC[Dat_START_TIME] = Dat_GetUNIXTimeFromStr (row[3]);
+
+ /* Get end date (row[4] holds the end UTC time) */
+ PrgItem->TimeUTC[Dat_END_TIME ] = Dat_GetUNIXTimeFromStr (row[4]);
+
+ /* Get whether the program item is open or closed (row(5)) */
+ PrgItem->Open = (row[5][0] == '1');
+
+ /* Get the title of the program item (row[6]) */
+ Str_Copy (PrgItem->Title,row[6],
+ Prg_MAX_BYTES_PROGRAM_ITEM_TITLE);
+
+ /* Can I do this program item? */
+ PrgItem->IBelongToCrsOrGrps = Prg_CheckIfIBelongToCrsOrGrpsThisPrgItem (PrgItem->PrgIteCod);
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (mysql_res);
+ }
+
+/*****************************************************************************/
+/************************ Clear all program item data ************************/
+/*****************************************************************************/
+
+static void Prg_ResetPrgItem (struct ProgramItem *PrgItem)
+ {
+ if (PrgItem->PrgIteCod <= 0) // If > 0 ==> keep value
+ PrgItem->PrgIteCod = -1L;
+ PrgItem->PrgIteCod = -1L;
+ PrgItem->Hidden = false;
+ PrgItem->UsrCod = -1L;
+ PrgItem->TimeUTC[Dat_START_TIME] =
+ PrgItem->TimeUTC[Dat_END_TIME ] = (time_t) 0;
+ PrgItem->Open = false;
+ PrgItem->Title[0] = '\0';
+ PrgItem->IBelongToCrsOrGrps = false;
+ }
+
+/*****************************************************************************/
+/************************ Free list of program items *************************/
+/*****************************************************************************/
+
+void Prg_FreeListPrgItems (void)
+ {
+ if (Gbl.Prg.LstIsRead && Gbl.Prg.LstPrgIteCods)
+ {
+ /***** Free memory used by the list of program items *****/
+ free (Gbl.Prg.LstPrgIteCods);
+ Gbl.Prg.LstPrgIteCods = NULL;
+ Gbl.Prg.Num = 0;
+ Gbl.Prg.LstIsRead = false;
+ }
+ }
+
+/*****************************************************************************/
+/******************* Get program item text from database *********************/
+/*****************************************************************************/
+
+static void Prg_GetPrgItemTxtFromDB (long PrgIteCod,char Txt[Cns_MAX_BYTES_TEXT + 1])
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned long NumRows;
+
+ /***** Get text of program item from database *****/
+ NumRows = DB_QuerySELECT (&mysql_res,"can not get program item text",
+ "SELECT Txt FROM prg_items"
+ " WHERE PrgIteCod=%ld AND CrsCod=%ld",
+ PrgIteCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /***** The result of the query must have one row or none *****/
+ if (NumRows == 1)
+ {
+ /* Get info text */
+ row = mysql_fetch_row (mysql_res);
+ Str_Copy (Txt,row[0],
+ Cns_MAX_BYTES_TEXT);
+ }
+ else
+ Txt[0] = '\0';
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+
+ if (NumRows > 1)
+ Lay_ShowErrorAndExit ("Error when getting program item text.");
+ }
+
+/*****************************************************************************/
+/***************** Get summary and content of a program item ****************/
+/*****************************************************************************/
+// This function may be called inside a web service
+
+void Prg_GetNotifPrgItem (char SummaryStr[Ntf_MAX_BYTES_SUMMARY + 1],
+ char **ContentStr,
+ long PrgIteCod,bool GetContent)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned long NumRows;
+ size_t Length;
+
+ SummaryStr[0] = '\0'; // Return nothing on error
+
+ /***** Build query *****/
+ NumRows = DB_QuerySELECT (&mysql_res,"can not get program item title and text",
+ "SELECT Title,Txt FROM prg_items"
+ " WHERE PrgIteCod=%ld",
+ PrgIteCod);
+
+ /***** Result should have a unique row *****/
+ if (NumRows == 1)
+ {
+ /***** Get row *****/
+ row = mysql_fetch_row (mysql_res);
+
+ /***** Get summary *****/
+ Str_Copy (SummaryStr,row[0],
+ Ntf_MAX_BYTES_SUMMARY);
+
+ /***** Get content *****/
+ if (GetContent)
+ {
+ Length = strlen (row[1]);
+ if ((*ContentStr = (char *) malloc (Length + 1)) == NULL)
+ Lay_ShowErrorAndExit ("Error allocating memory for notification content.");
+ Str_Copy (*ContentStr,row[1],
+ Length);
+ }
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/**************** Write parameter with code of program item ******************/
+/*****************************************************************************/
+
+static void Prg_PutParamPrgItemCod (long PrgIteCod)
+ {
+ Par_PutHiddenParamLong (NULL,"PrgIteCod",PrgIteCod);
+ }
+
+/*****************************************************************************/
+/***************** Get parameter with code of program item *******************/
+/*****************************************************************************/
+
+long Prg_GetParamPrgItemCod (void)
+ {
+ /***** Get code of program item *****/
+ return Par_GetParToLong ("PrgIteCod");
+ }
+
+/*****************************************************************************/
+/************* Ask for confirmation of removing a program item ***************/
+/*****************************************************************************/
+
+void Prg_ReqRemPrgItem (void)
+ {
+ extern const char *Txt_Do_you_really_want_to_remove_the_item_X;
+ extern const char *Txt_Remove_item;
+ struct ProgramItem PrgItem;
+
+ /***** Get parameters *****/
+ Prg_GetParamPrgOrder ();
+ Grp_GetParamWhichGrps ();
+ Gbl.Prg.CurrentPage = Pag_GetParamPagNum (Pag_COURSE_PROGRAM);
+
+ /***** Get program item code *****/
+ if ((PrgItem.PrgIteCod = Prg_GetParamPrgItemCod ()) == -1L)
+ Lay_ShowErrorAndExit ("Code of program item is missing.");
+
+ /***** Get data of the program item from database *****/
+ Prg_GetDataOfPrgItemByCod (&PrgItem);
+
+ /***** Show question and button to remove the program item *****/
+ Gbl.Prg.PrgIteCodToEdit = PrgItem.PrgIteCod;
+ Ale_ShowAlertAndButton (ActRemAsg,NULL,NULL,Prg_PutParams,
+ Btn_REMOVE_BUTTON,Txt_Remove_item,
+ Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_item_X,
+ PrgItem.Title);
+
+ /***** Show program items again *****/
+ Prg_SeeCourseProgram ();
+ }
+
+/*****************************************************************************/
+/*************************** Remove a program item ***************************/
+/*****************************************************************************/
+
+void Prg_RemovePrgItem (void)
+ {
+ extern const char *Txt_Assignment_X_removed;
+ struct ProgramItem PrgItem;
+
+ /***** Get program item code *****/
+ if ((PrgItem.PrgIteCod = Prg_GetParamPrgItemCod ()) == -1L)
+ Lay_ShowErrorAndExit ("Code of program item is missing.");
+
+ /***** Get data of the program item from database *****/
+ Prg_GetDataOfPrgItemByCod (&PrgItem); // Inside this function, the course is checked to be the current one
+
+ /***** Remove all the groups of this program item *****/
+ Prg_RemoveAllTheGrpsAssociatedToAPrgItem (PrgItem.PrgIteCod);
+
+ /***** Remove program item *****/
+ DB_QueryDELETE ("can not remove program item",
+ "DELETE FROM prg_items WHERE PrgIteCod=%ld AND CrsCod=%ld",
+ PrgItem.PrgIteCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /***** Mark possible notifications as removed *****/
+ Ntf_MarkNotifAsRemoved (Ntf_EVENT_ASSIGNMENT,PrgItem.PrgIteCod);
+
+ /***** Write message to show the change made *****/
+ Ale_ShowAlert (Ale_SUCCESS,Txt_Assignment_X_removed,
+ PrgItem.Title);
+
+ /***** Show program items again *****/
+ Prg_SeeCourseProgram ();
+ }
+
+/*****************************************************************************/
+/***************************** Hide a program item ***************************/
+/*****************************************************************************/
+
+void Prg_HidePrgItem (void)
+ {
+ struct ProgramItem PrgItem;
+
+ /***** Get program item code *****/
+ if ((PrgItem.PrgIteCod = Prg_GetParamPrgItemCod ()) == -1L)
+ Lay_ShowErrorAndExit ("Code of program item is missing.");
+
+ /***** Get data of the program item from database *****/
+ Prg_GetDataOfPrgItemByCod (&PrgItem);
+
+ /***** Hide program item *****/
+ DB_QueryUPDATE ("can not hide program item",
+ "UPDATE prg_items SET Hidden='Y'"
+ " WHERE PrgIteCod=%ld AND CrsCod=%ld",
+ PrgItem.PrgIteCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /***** Show program items again *****/
+ Prg_SeeCourseProgram ();
+ }
+
+/*****************************************************************************/
+/***************************** Show a program item ***************************/
+/*****************************************************************************/
+
+void Prg_ShowPrgItem (void)
+ {
+ struct ProgramItem PrgItem;
+
+ /***** Get program item code *****/
+ if ((PrgItem.PrgIteCod = Prg_GetParamPrgItemCod ()) == -1L)
+ Lay_ShowErrorAndExit ("Code of program item is missing.");
+
+ /***** Get data of the program item from database *****/
+ Prg_GetDataOfPrgItemByCod (&PrgItem);
+
+ /***** Hide program item *****/
+ DB_QueryUPDATE ("can not show program item",
+ "UPDATE prg_items SET Hidden='N'"
+ " WHERE PrgIteCod=%ld AND CrsCod=%ld",
+ PrgItem.PrgIteCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /***** Show program items again *****/
+ Prg_SeeCourseProgram ();
+ }
+
+/*****************************************************************************/
+/*************** Check if the title of a program item exists *****************/
+/*****************************************************************************/
+
+static bool Prg_CheckIfSimilarPrgItemExists (const char *Field,const char *Value,long PrgIteCod)
+ {
+ /***** Get number of program items with a field value from database *****/
+ return (DB_QueryCOUNT ("can not get similar program items",
+ "SELECT COUNT(*) FROM prg_items"
+ " WHERE CrsCod=%ld"
+ " AND %s='%s' AND PrgIteCod<>%ld",
+ Gbl.Hierarchy.Crs.CrsCod,
+ Field,Value,PrgIteCod) != 0);
+ }
+
+/*****************************************************************************/
+/***************** Put a form to create a new program item *******************/
+/*****************************************************************************/
+
+void Prg_RequestCreatOrEditPrgItem (void)
+ {
+ extern const char *Hlp_COURSE_Program_new_item;
+ extern const char *Hlp_COURSE_Program_edit_item;
+ extern const char *Txt_New_item;
+ extern const char *Txt_Edit_item;
+ extern const char *Txt_Title;
+ extern const char *Txt_Description;
+ extern const char *Txt_Create_item;
+ extern const char *Txt_Save_changes;
+ struct ProgramItem PrgItem;
+ bool ItsANewPrgItem;
+ char Txt[Cns_MAX_BYTES_TEXT + 1];
+
+ /***** Get parameters *****/
+ Prg_GetParamPrgOrder ();
+ Grp_GetParamWhichGrps ();
+ Gbl.Prg.CurrentPage = Pag_GetParamPagNum (Pag_COURSE_PROGRAM);
+
+ /***** Get the code of the program item *****/
+ ItsANewPrgItem = ((PrgItem.PrgIteCod = Prg_GetParamPrgItemCod ()) == -1L);
+
+ /***** Get from the database the data of the program item *****/
+ if (ItsANewPrgItem)
+ {
+ /* Initialize to empty program item */
+ PrgItem.PrgIteCod = -1L;
+ PrgItem.TimeUTC[Dat_START_TIME] = Gbl.StartExecutionTimeUTC;
+ PrgItem.TimeUTC[Dat_END_TIME ] = Gbl.StartExecutionTimeUTC + (2 * 60 * 60); // +2 hours
+ PrgItem.Open = true;
+ PrgItem.Title[0] = '\0';
+ PrgItem.IBelongToCrsOrGrps = false;
+ }
+ else
+ {
+ /* Get data of the program item from database */
+ Prg_GetDataOfPrgItemByCod (&PrgItem);
+
+ /* Get text of the program item from database */
+ Prg_GetPrgItemTxtFromDB (PrgItem.PrgIteCod,Txt);
+ }
+
+ /***** Begin form *****/
+ if (ItsANewPrgItem)
+ {
+ Frm_StartForm (ActNewPrgIte);
+ Gbl.Prg.PrgIteCodToEdit = -1L;
+ }
+ else
+ {
+ Frm_StartForm (ActChgPrgIte);
+ Gbl.Prg.PrgIteCodToEdit = PrgItem.PrgIteCod;
+ }
+ Prg_PutParams ();
+
+ /***** Begin box and table *****/
+ if (ItsANewPrgItem)
+ Box_BoxTableBegin (NULL,Txt_New_item,NULL,
+ Hlp_COURSE_Program_new_item,Box_NOT_CLOSABLE,2);
+ else
+ Box_BoxTableBegin (NULL,
+ PrgItem.Title[0] ? PrgItem.Title :
+ Txt_Edit_item,
+ NULL,
+ Hlp_COURSE_Program_edit_item,Box_NOT_CLOSABLE,2);
+
+
+ /***** Schedule item title *****/
+ HTM_TR_Begin (NULL);
+
+ /* Label */
+ Frm_LabelColumn ("RM","Title",Txt_Title);
+
+ /* Data */
+ HTM_TD_Begin ("class=\"LM\"");
+ HTM_INPUT_TEXT ("Title",Prg_MAX_CHARS_PROGRAM_ITEM_TITLE,PrgItem.Title,false,
+ "id=\"Title\" required=\"required\""
+ " class=\"TITLE_DESCRIPTION_WIDTH\"");
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Schedule item start and end dates *****/
+ Dat_PutFormStartEndClientLocalDateTimes (PrgItem.TimeUTC,Dat_FORM_SECONDS_ON);
+
+ /***** Schedule item text *****/
+ HTM_TR_Begin (NULL);
+
+ /* Label */
+ Frm_LabelColumn ("RT","Txt",Txt_Description);
+
+ /* Data */
+ HTM_TD_Begin ("class=\"LT\"");
+ HTM_TEXTAREA_Begin ("id=\"Txt\" name=\"Txt\" rows=\"10\""
+ " class=\"TITLE_DESCRIPTION_WIDTH\"");
+ if (!ItsANewPrgItem)
+ HTM_Txt (Txt);
+ HTM_TEXTAREA_End ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Groups *****/
+ Prg_ShowLstGrpsToEditPrgItem (PrgItem.PrgIteCod);
+
+ /***** End table, send button and end box *****/
+ if (ItsANewPrgItem)
+ Box_BoxTableWithButtonEnd (Btn_CREATE_BUTTON,Txt_Create_item);
+ else
+ Box_BoxTableWithButtonEnd (Btn_CONFIRM_BUTTON,Txt_Save_changes);
+
+ /***** End form *****/
+ Frm_EndForm ();
+
+ /***** Show current program items, if any *****/
+ Prg_ShowAllPrgItems ();
+ }
+
+/*****************************************************************************/
+/*************** Show list of groups to edit and program item ****************/
+/*****************************************************************************/
+
+static void Prg_ShowLstGrpsToEditPrgItem (long PrgIteCod)
+ {
+ extern const char *Hlp_USERS_Groups;
+ extern const char *The_ClassFormInBox[The_NUM_THEMES];
+ extern const char *Txt_Groups;
+ extern const char *Txt_The_whole_course;
+ unsigned NumGrpTyp;
+
+ /***** Get list of groups types and groups in this course *****/
+ Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS);
+
+ if (Gbl.Crs.Grps.GrpTypes.Num)
+ {
+ /***** Begin box and table *****/
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"%s RT\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
+ HTM_TxtF ("%s:",Txt_Groups);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"LT\"");
+ Box_BoxTableBegin ("100%",NULL,NULL,
+ Hlp_USERS_Groups,Box_NOT_CLOSABLE,0);
+
+ /***** First row: checkbox to select the whole course *****/
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("colspan=\"7\" class=\"DAT LM\"");
+ HTM_LABEL_Begin (NULL);
+ HTM_INPUT_CHECKBOX ("WholeCrs",false,
+ "id=\"WholeCrs\" value=\"Y\"%s"
+ " onclick=\"uncheckChildren(this,'GrpCods')\"",
+ Prg_CheckIfPrgItemIsAssociatedToGrps (PrgIteCod) ? "" :
+ " checked=\"checked\"");
+ HTM_TxtF ("%s %s",Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
+ HTM_LABEL_End ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** List the groups for each group type *****/
+ for (NumGrpTyp = 0;
+ NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num;
+ NumGrpTyp++)
+ if (Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].NumGrps)
+ Grp_ListGrpsToEditAsgAttSvyMch (&Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp],
+ PrgIteCod,Grp_ASSIGNMENT);
+
+ /***** End table and box *****/
+ Box_BoxTableEnd ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+ }
+
+ /***** Free list of groups types and groups in this course *****/
+ Grp_FreeListGrpTypesAndGrps ();
+ }
+
+/*****************************************************************************/
+/***************** Receive form to create a new program item *****************/
+/*****************************************************************************/
+
+void Prg_RecFormPrgItem (void)
+ {
+ extern const char *Txt_Already_existed_an_item_with_the_title_X;
+ extern const char *Txt_You_must_specify_the_title_of_the_item;
+ extern const char *Txt_Created_new_item_X;
+ extern const char *Txt_The_item_has_been_modified;
+ struct ProgramItem OldPrgItem; // Current program item data in database
+ struct ProgramItem NewPrgItem; // Schedule item data received from form
+ bool ItsANewPrgItem;
+ bool NewPrgItemIsCorrect = true;
+ char Description[Cns_MAX_BYTES_TEXT + 1];
+
+ /***** Get the code of the program item *****/
+ NewPrgItem.PrgIteCod = Prg_GetParamPrgItemCod ();
+ ItsANewPrgItem = (NewPrgItem.PrgIteCod < 0);
+
+ if (ItsANewPrgItem)
+ {
+ /***** Reset old (current, not existing) program item data *****/
+ OldPrgItem.PrgIteCod = -1L;
+ Prg_ResetPrgItem (&OldPrgItem);
+ }
+ else
+ {
+ /***** Get data of the old (current) program item from database *****/
+ OldPrgItem.PrgIteCod = NewPrgItem.PrgIteCod;
+ Prg_GetDataOfPrgItemByCod (&OldPrgItem);
+ }
+
+ /***** Get start/end date-times *****/
+ NewPrgItem.TimeUTC[Dat_START_TIME] = Dat_GetTimeUTCFromForm ("StartTimeUTC");
+ NewPrgItem.TimeUTC[Dat_END_TIME ] = Dat_GetTimeUTCFromForm ("EndTimeUTC" );
+
+ /***** Get program item title *****/
+ Par_GetParToText ("Title",NewPrgItem.Title,Prg_MAX_BYTES_PROGRAM_ITEM_TITLE);
+
+ /***** Get program item text *****/
+ Par_GetParToHTML ("Txt",Description,Cns_MAX_BYTES_TEXT); // Store in HTML format (not rigorous)
+
+ /***** Adjust dates *****/
+ if (NewPrgItem.TimeUTC[Dat_START_TIME] == 0)
+ NewPrgItem.TimeUTC[Dat_START_TIME] = Gbl.StartExecutionTimeUTC;
+ if (NewPrgItem.TimeUTC[Dat_END_TIME] == 0)
+ NewPrgItem.TimeUTC[Dat_END_TIME] = NewPrgItem.TimeUTC[Dat_START_TIME] + 2 * 60 * 60; // +2 hours
+
+ /***** Check if title is correct *****/
+ if (NewPrgItem.Title[0]) // If there's a program item title
+ {
+ /* If title of program item was in database... */
+ if (Prg_CheckIfSimilarPrgItemExists ("Title",NewPrgItem.Title,NewPrgItem.PrgIteCod))
+ {
+ NewPrgItemIsCorrect = false;
+
+ Ale_ShowAlert (Ale_WARNING,Txt_Already_existed_an_item_with_the_title_X,
+ NewPrgItem.Title);
+ }
+ }
+ else // If there is not a program item title
+ {
+ NewPrgItemIsCorrect = false;
+ Ale_ShowAlert (Ale_WARNING,Txt_You_must_specify_the_title_of_the_item);
+ }
+
+ /***** Create a new program item or update an existing one *****/
+ if (NewPrgItemIsCorrect)
+ {
+ /* Get groups for this program items */
+ Grp_GetParCodsSeveralGrps ();
+
+ if (ItsANewPrgItem)
+ {
+ Prg_CreatePrgItem (&NewPrgItem,Description); // Add new program item to database
+
+ /***** Write success message *****/
+ Ale_ShowAlert (Ale_SUCCESS,Txt_Created_new_item_X,
+ NewPrgItem.Title);
+ }
+ else
+ {
+ Prg_UpdatePrgItem (&NewPrgItem,Description);
+
+ /***** Write success message *****/
+ Ale_ShowAlert (Ale_SUCCESS,Txt_The_item_has_been_modified);
+ }
+
+ /* Free memory for list of selected groups */
+ Grp_FreeListCodSelectedGrps ();
+
+ /***** Show program items again *****/
+ Prg_SeeCourseProgram ();
+ }
+ else
+ // TODO: The form should be filled with partial data, now is always empty
+ Prg_RequestCreatOrEditPrgItem ();
+ }
+
+/*****************************************************************************/
+/************************ Create a new program item **************************/
+/*****************************************************************************/
+
+static void Prg_CreatePrgItem (struct ProgramItem *PrgItem,const char *Txt)
+ {
+ /***** Create a new program item *****/
+ PrgItem->PrgIteCod =
+ DB_QueryINSERTandReturnCode ("can not create new program item",
+ "INSERT INTO prg_items"
+ " (CrsCod,UsrCod,StartTime,EndTime,Title,Txt)"
+ " VALUES"
+ " (%ld,%ld,FROM_UNIXTIME(%ld),FROM_UNIXTIME(%ld),"
+ "'%s','%s')",
+ Gbl.Hierarchy.Crs.CrsCod,
+ Gbl.Usrs.Me.UsrDat.UsrCod,
+ PrgItem->TimeUTC[Dat_START_TIME],
+ PrgItem->TimeUTC[Dat_END_TIME ],
+ PrgItem->Title,
+ Txt);
+
+ /***** Create groups *****/
+ if (Gbl.Crs.Grps.LstGrpsSel.NumGrps)
+ Prg_CreateGrps (PrgItem->PrgIteCod);
+ }
+
+/*****************************************************************************/
+/******************** Update an existing program item ************************/
+/*****************************************************************************/
+
+static void Prg_UpdatePrgItem (struct ProgramItem *PrgItem,const char *Txt)
+ {
+ /***** Update the data of the program item *****/
+ DB_QueryUPDATE ("can not update program item",
+ "UPDATE prg_items SET "
+ "StartTime=FROM_UNIXTIME(%ld),"
+ "EndTime=FROM_UNIXTIME(%ld),"
+ "Title='%s',Txt='%s'"
+ " WHERE PrgIteCod=%ld AND CrsCod=%ld",
+ PrgItem->TimeUTC[Dat_START_TIME],
+ PrgItem->TimeUTC[Dat_END_TIME ],
+ PrgItem->Title,
+ Txt,
+ PrgItem->PrgIteCod,Gbl.Hierarchy.Crs.CrsCod);
+
+ /***** Update groups *****/
+ /* Remove old groups */
+ Prg_RemoveAllTheGrpsAssociatedToAPrgItem (PrgItem->PrgIteCod);
+
+ /* Create new groups */
+ if (Gbl.Crs.Grps.LstGrpsSel.NumGrps)
+ Prg_CreateGrps (PrgItem->PrgIteCod);
+ }
+
+/*****************************************************************************/
+/*********** Check if a program item is associated to any group **************/
+/*****************************************************************************/
+
+static bool Prg_CheckIfPrgItemIsAssociatedToGrps (long PrgIteCod)
+ {
+ /***** Get if a program item is associated to a group from database *****/
+ return (DB_QueryCOUNT ("can not check if a program item"
+ " is associated to groups",
+ "SELECT COUNT(*) FROM prg_grp WHERE PrgIteCod=%ld",
+ PrgIteCod) != 0);
+ }
+
+/*****************************************************************************/
+/************ Check if a program item is associated to a group ***************/
+/*****************************************************************************/
+
+bool Prg_CheckIfPrgItemIsAssociatedToGrp (long PrgIteCod,long GrpCod)
+ {
+ /***** Get if a program item is associated to a group from database *****/
+ return (DB_QueryCOUNT ("can not check if a program item"
+ " is associated to a group",
+ "SELECT COUNT(*) FROM prg_grp"
+ " WHERE PrgIteCod=%ld AND GrpCod=%ld",
+ PrgIteCod,GrpCod) != 0);
+ }
+
+/*****************************************************************************/
+/********************* Remove groups of a program item ***********************/
+/*****************************************************************************/
+
+static void Prg_RemoveAllTheGrpsAssociatedToAPrgItem (long PrgIteCod)
+ {
+ /***** Remove groups of the program item *****/
+ DB_QueryDELETE ("can not remove the groups associated to a program item",
+ "DELETE FROM prg_grp WHERE PrgIteCod=%ld",
+ PrgIteCod);
+ }
+
+/*****************************************************************************/
+/*************** Remove one group from all the program items *****************/
+/*****************************************************************************/
+
+void Prg_RemoveGroup (long GrpCod)
+ {
+ /***** Remove group from all the program items *****/
+ DB_QueryDELETE ("can not remove group from the associations"
+ " between program items and groups",
+ "DELETE FROM prg_grp WHERE GrpCod=%ld",
+ GrpCod);
+ }
+
+/*****************************************************************************/
+/********** Remove groups of one type from all the program items *************/
+/*****************************************************************************/
+
+void Prg_RemoveGroupsOfType (long GrpTypCod)
+ {
+ /***** Remove group from all the program items *****/
+ DB_QueryDELETE ("can not remove groups of a type from the associations"
+ " between program items and groups",
+ "DELETE FROM prg_grp USING crs_grp,prg_grp"
+ " WHERE crs_grp.GrpTypCod=%ld"
+ " AND crs_grp.GrpCod=prg_grp.GrpCod",
+ GrpTypCod);
+ }
+
+/*****************************************************************************/
+/********************* Create groups of a program item ***********************/
+/*****************************************************************************/
+
+static void Prg_CreateGrps (long PrgIteCod)
+ {
+ unsigned NumGrpSel;
+
+ /***** Create groups of the program item *****/
+ for (NumGrpSel = 0;
+ NumGrpSel < Gbl.Crs.Grps.LstGrpsSel.NumGrps;
+ NumGrpSel++)
+ /* Create group */
+ DB_QueryINSERT ("can not associate a group to a program item",
+ "INSERT INTO prg_grp"
+ " (PrgIteCod,GrpCod)"
+ " VALUES"
+ " (%ld,%ld)",
+ PrgIteCod,
+ Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrpSel]);
+ }
+
+/*****************************************************************************/
+/********* Get and write the names of the groups of a program item ***********/
+/*****************************************************************************/
+
+static void Prg_GetAndWriteNamesOfGrpsAssociatedToPrgItem (struct ProgramItem *PrgItem)
+ {
+ extern const char *Txt_Group;
+ extern const char *Txt_Groups;
+ extern const char *Txt_and;
+ extern const char *Txt_The_whole_course;
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned long NumRow;
+ unsigned long NumRows;
+
+ /***** Get groups associated to a program item from database *****/
+ NumRows = DB_QuerySELECT (&mysql_res,"can not get groups of a program item",
+ "SELECT crs_grp_types.GrpTypName,crs_grp.GrpName"
+ " FROM prg_grp,crs_grp,crs_grp_types"
+ " WHERE prg_grp.PrgIteCod=%ld"
+ " AND prg_grp.GrpCod=crs_grp.GrpCod"
+ " AND crs_grp.GrpTypCod=crs_grp_types.GrpTypCod"
+ " ORDER BY crs_grp_types.GrpTypName,crs_grp.GrpName",
+ PrgItem->PrgIteCod);
+
+ /***** Write heading *****/
+ HTM_DIV_Begin ("class=\"%s\"",PrgItem->Hidden ? "ASG_GRP_LIGHT" :
+ "ASG_GRP");
+ HTM_TxtColonNBSP (NumRows == 1 ? Txt_Group :
+ Txt_Groups);
+
+ /***** Write groups *****/
+ if (NumRows) // Groups found...
+ {
+ /* Get and write the group types and names */
+ for (NumRow = 0;
+ NumRow < NumRows;
+ NumRow++)
+ {
+ /* Get next group */
+ row = mysql_fetch_row (mysql_res);
+
+ /* Write group type name and group name */
+ HTM_TxtF ("%s %s",row[0],row[1]);
+
+ if (NumRows >= 2)
+ {
+ if (NumRow == NumRows-2)
+ HTM_TxtF (" %s ",Txt_and);
+ if (NumRows >= 3)
+ if (NumRow < NumRows-2)
+ HTM_Txt (", ");
+ }
+ }
+ }
+ else
+ HTM_TxtF ("%s %s",Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
+
+ HTM_DIV_End ();
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/***************** Remove all the program items of a course ******************/
+/*****************************************************************************/
+
+void Prg_RemoveCrsPrgItems (long CrsCod)
+ {
+ /***** Remove groups *****/
+ DB_QueryDELETE ("can not remove all the groups associated"
+ " to program items of a course",
+ "DELETE FROM prg_grp USING prg_items,prg_grp"
+ " WHERE prg_items.CrsCod=%ld"
+ " AND prg_items.PrgIteCod=prg_grp.PrgIteCod",
+ CrsCod);
+
+ /***** Remove program items *****/
+ DB_QueryDELETE ("can not remove all the program items of a course",
+ "DELETE FROM prg_items WHERE CrsCod=%ld",
+ CrsCod);
+ }
+
+/*****************************************************************************/
+/******** Check if I belong to any of the groups of a program item ***********/
+/*****************************************************************************/
+
+static bool Prg_CheckIfIBelongToCrsOrGrpsThisPrgItem (long PrgIteCod)
+ {
+ switch (Gbl.Usrs.Me.Role.Logged)
+ {
+ case Rol_STD:
+ case Rol_NET:
+ case Rol_TCH:
+ // Students and teachers can do program items depending on groups
+ /***** Get if I can do a program item from database *****/
+ return (DB_QueryCOUNT ("can not check if I can do a program item",
+ "SELECT COUNT(*) FROM prg_items"
+ " WHERE PrgIteCod=%ld"
+ " AND "
+ "("
+ // Schedule item is for the whole course
+ "PrgIteCod NOT IN (SELECT PrgIteCod FROM prg_grp)"
+ " OR "
+ // Schedule item is for specific groups
+ "PrgIteCod IN"
+ " (SELECT prg_grp.PrgIteCod FROM prg_grp,crs_grp_usr"
+ " WHERE crs_grp_usr.UsrCod=%ld"
+ " AND prg_grp.GrpCod=crs_grp_usr.GrpCod)"
+ ")",
+ PrgIteCod,Gbl.Usrs.Me.UsrDat.UsrCod) != 0);
+ case Rol_SYS_ADM:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+/*****************************************************************************/
+/***************** Get number of program items in a course *******************/
+/*****************************************************************************/
+
+unsigned Prg_GetNumPrgItemsInCrs (long CrsCod)
+ {
+ /***** Get number of program items in a course from database *****/
+ return
+ (unsigned) DB_QueryCOUNT ("can not get number of program items in course",
+ "SELECT COUNT(*) FROM prg_items"
+ " WHERE CrsCod=%ld",
+ CrsCod);
+ }
+
+/*****************************************************************************/
+/****************** Get number of courses with program items *****************/
+/*****************************************************************************/
+// Returns the number of courses with program items
+// in this location (all the platform, current degree or current course)
+
+unsigned Prg_GetNumCoursesWithPrgItems (Hie_Level_t Scope)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumCourses;
+
+ /***** Get number of courses with program items from database *****/
+ switch (Scope)
+ {
+ case Hie_SYS:
+ DB_QuerySELECT (&mysql_res,"can not get number of courses with program items",
+ "SELECT COUNT(DISTINCT CrsCod)"
+ " FROM prg_items"
+ " WHERE CrsCod>0");
+ break;
+ case Hie_CTY:
+ DB_QuerySELECT (&mysql_res,"can not get number of courses with program items",
+ "SELECT COUNT(DISTINCT prg_items.CrsCod)"
+ " FROM institutions,centres,degrees,courses,prg_items"
+ " WHERE institutions.CtyCod=%ld"
+ " AND institutions.InsCod=centres.InsCod"
+ " AND centres.CtrCod=degrees.CtrCod"
+ " AND degrees.DegCod=courses.DegCod"
+ " AND courses.Status=0"
+ " AND courses.CrsCod=prg_items.CrsCod",
+ Gbl.Hierarchy.Cty.CtyCod);
+ break;
+ case Hie_INS:
+ DB_QuerySELECT (&mysql_res,"can not get number of courses with program items",
+ "SELECT COUNT(DISTINCT prg_items.CrsCod)"
+ " FROM centres,degrees,courses,prg_items"
+ " WHERE centres.InsCod=%ld"
+ " AND centres.CtrCod=degrees.CtrCod"
+ " AND degrees.DegCod=courses.DegCod"
+ " AND courses.Status=0"
+ " AND courses.CrsCod=prg_items.CrsCod",
+ Gbl.Hierarchy.Ins.InsCod);
+ break;
+ case Hie_CTR:
+ DB_QuerySELECT (&mysql_res,"can not get number of courses with program items",
+ "SELECT COUNT(DISTINCT prg_items.CrsCod)"
+ " FROM degrees,courses,prg_items"
+ " WHERE degrees.CtrCod=%ld"
+ " AND degrees.DegCod=courses.DegCod"
+ " AND courses.Status=0"
+ " AND courses.CrsCod=prg_items.CrsCod",
+ Gbl.Hierarchy.Ctr.CtrCod);
+ break;
+ case Hie_DEG:
+ DB_QuerySELECT (&mysql_res,"can not get number of courses with program items",
+ "SELECT COUNT(DISTINCT prg_items.CrsCod)"
+ " FROM courses,prg_items"
+ " WHERE courses.DegCod=%ld"
+ " AND courses.Status=0"
+ " AND courses.CrsCod=prg_items.CrsCod",
+ Gbl.Hierarchy.Deg.DegCod);
+ break;
+ case Hie_CRS:
+ DB_QuerySELECT (&mysql_res,"can not get number of courses with program items",
+ "SELECT COUNT(DISTINCT CrsCod)"
+ " FROM prg_items"
+ " WHERE CrsCod=%ld",
+ Gbl.Hierarchy.Crs.CrsCod);
+ break;
+ default:
+ Lay_WrongScopeExit ();
+ break;
+ }
+
+ /***** Get number of courses *****/
+ row = mysql_fetch_row (mysql_res);
+ if (sscanf (row[0],"%u",&NumCourses) != 1)
+ Lay_ShowErrorAndExit ("Error when getting number of courses with program items.");
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+
+ return NumCourses;
+ }
+
+/*****************************************************************************/
+/************************ Get number of program items ************************/
+/*****************************************************************************/
+// Returns the number of program items
+// in this location (all the platform, current degree or current course)
+
+unsigned Prg_GetNumPrgItems (Hie_Level_t Scope,unsigned *NumNotif)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumPrgItems;
+
+ /***** Get number of program items from database *****/
+ switch (Scope)
+ {
+ case Hie_SYS:
+ DB_QuerySELECT (&mysql_res,"can not get number of program items",
+ "SELECT COUNT(*),SUM(NumNotif)"
+ " FROM prg_items"
+ " WHERE CrsCod>0");
+ break;
+ case Hie_CTY:
+ DB_QuerySELECT (&mysql_res,"can not get number of program items",
+ "SELECT COUNT(*),SUM(prg_items.NumNotif)"
+ " FROM institutions,centres,degrees,courses,prg_items"
+ " WHERE institutions.CtyCod=%ld"
+ " AND institutions.InsCod=centres.InsCod"
+ " AND centres.CtrCod=degrees.CtrCod"
+ " AND degrees.DegCod=courses.DegCod"
+ " AND courses.CrsCod=prg_items.CrsCod",
+ Gbl.Hierarchy.Cty.CtyCod);
+ break;
+ case Hie_INS:
+ DB_QuerySELECT (&mysql_res,"can not get number of program items",
+ "SELECT COUNT(*),SUM(prg_items.NumNotif)"
+ " FROM centres,degrees,courses,prg_items"
+ " WHERE centres.InsCod=%ld"
+ " AND centres.CtrCod=degrees.CtrCod"
+ " AND degrees.DegCod=courses.DegCod"
+ " AND courses.CrsCod=prg_items.CrsCod",
+ Gbl.Hierarchy.Ins.InsCod);
+ break;
+ case Hie_CTR:
+ DB_QuerySELECT (&mysql_res,"can not get number of program items",
+ "SELECT COUNT(*),SUM(prg_items.NumNotif)"
+ " FROM degrees,courses,prg_items"
+ " WHERE degrees.CtrCod=%ld"
+ " AND degrees.DegCod=courses.DegCod"
+ " AND courses.CrsCod=prg_items.CrsCod",
+ Gbl.Hierarchy.Ctr.CtrCod);
+ break;
+ case Hie_DEG:
+ DB_QuerySELECT (&mysql_res,"can not get number of program items",
+ "SELECT COUNT(*),SUM(prg_items.NumNotif)"
+ " FROM courses,prg_items"
+ " WHERE courses.DegCod=%ld"
+ " AND courses.CrsCod=prg_items.CrsCod",
+ Gbl.Hierarchy.Deg.DegCod);
+ break;
+ case Hie_CRS:
+ DB_QuerySELECT (&mysql_res,"can not get number of program items",
+ "SELECT COUNT(*),SUM(NumNotif)"
+ " FROM prg_items"
+ " WHERE CrsCod=%ld",
+ Gbl.Hierarchy.Crs.CrsCod);
+ break;
+ default:
+ Lay_WrongScopeExit ();
+ break;
+ }
+
+ /***** Get number of program items *****/
+ row = mysql_fetch_row (mysql_res);
+ if (sscanf (row[0],"%u",&NumPrgItems) != 1)
+ Lay_ShowErrorAndExit ("Error when getting number of program items.");
+
+ /***** Get number of notifications by email *****/
+ if (row[1])
+ {
+ if (sscanf (row[1],"%u",NumNotif) != 1)
+ Lay_ShowErrorAndExit ("Error when getting number of notifications of program items.");
+ }
+ else
+ *NumNotif = 0;
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+
+ return NumPrgItems;
+ }
diff --git a/swad_program.h b/swad_program.h
new file mode 100644
index 00000000..02999fd8
--- /dev/null
+++ b/swad_program.h
@@ -0,0 +1,90 @@
+// swad_program.h: course program
+
+#ifndef _SWAD_PRG
+#define _SWAD_PRG
+/*
+ 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-2020 Antonio Cañas Vargas
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+/*****************************************************************************/
+/********************************* Headers ***********************************/
+/*****************************************************************************/
+
+#include "swad_date.h"
+#include "swad_file_browser.h"
+#include "swad_notification.h"
+#include "swad_user.h"
+
+/*****************************************************************************/
+/************************** Public types and constants ***********************/
+/*****************************************************************************/
+
+#define Prg_MAX_CHARS_PROGRAM_ITEM_TITLE (128 - 1) // 127
+#define Prg_MAX_BYTES_PROGRAM_ITEM_TITLE ((Prg_MAX_CHARS_PROGRAM_ITEM_TITLE + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
+
+struct ProgramItem
+ {
+ long PrgIteCod;
+ bool Hidden;
+ long UsrCod;
+ time_t TimeUTC[Dat_NUM_START_END_TIME];
+ bool Open;
+ char Title[Prg_MAX_BYTES_PROGRAM_ITEM_TITLE + 1];
+ bool IBelongToCrsOrGrps; // I can do this program item
+ // (it is associated to no groups
+ // or, if associated to groups,
+ // I belong to any of the groups)
+ };
+
+#define Prg_ORDER_DEFAULT Dat_START_TIME
+
+/*****************************************************************************/
+/***************************** Public prototypes *****************************/
+/*****************************************************************************/
+
+void Prg_SeeCourseProgram (void);
+void Prg_PrintOnePrgItem (void);
+
+void Prg_PutHiddenParamPrgOrder (void);
+void Prg_RequestCreatOrEditPrgItem (void);
+void Prg_GetListPrgItems (void);
+void Prg_GetDataOfPrgItemByCod (struct ProgramItem *PrgItem);
+void Prg_FreeListPrgItems (void);
+
+void Prg_GetNotifPrgItem (char SummaryStr[Ntf_MAX_BYTES_SUMMARY + 1],
+ char **ContentStr,
+ long PrgIteCod,bool GetContent);
+
+long Prg_GetParamPrgItemCod (void);
+void Prg_ReqRemPrgItem (void);
+void Prg_RemovePrgItem (void);
+void Prg_HidePrgItem (void);
+void Prg_ShowPrgItem (void);
+void Prg_RecFormPrgItem (void);
+bool Prg_CheckIfPrgItemIsAssociatedToGrp (long PrgIteCod,long GrpCod);
+void Prg_RemoveGroup (long GrpCod);
+void Prg_RemoveGroupsOfType (long GrpTypCod);
+void Prg_RemoveCrsPrgItems (long CrsCod);
+unsigned Prg_GetNumPrgItemsInCrs(long CrsCod);
+
+unsigned Prg_GetNumCoursesWithPrgItems (Hie_Level_t Scope);
+unsigned Prg_GetNumPrgItems (Hie_Level_t Scope,unsigned *NumNotif);
+
+#endif
diff --git a/swad_text.c b/swad_text.c
index e2a82e50..c8b8ceae 100644
--- a/swad_text.c
+++ b/swad_text.c
@@ -1506,6 +1506,27 @@ const char *Txt_Already_existed_an_event_with_the_title_X = // Warning: it is ve
"Já existe um evento com o título %s.";
#endif
+const char *Txt_Already_existed_an_item_with_the_title_X = // Warning: it is very important to include %s in the following sentences
+#if L==1 // ca
+ "Ja existia un ítem amb el títol %s.";
+#elif L==2 // de
+ "Es gibt bereits einen Artikel mit dem Namen %s.";
+#elif L==3 // en
+ "Already existed an item with the title %s.";
+#elif L==4 // es
+ "Ya existía un ítem con el título %s.";
+#elif L==5 // fr
+ "Il existe déjà un article du titre %s.";
+#elif L==6 // gn
+ "Ya existía un ítem con el título %s."; // Okoteve traducción
+#elif L==7 // it
+ "Esiste già un articolo con il titolo %s.";
+#elif L==8 // pl
+ "Istniala juz przedmiot z tytulu %s.";
+#elif L==9 // pt
+ "Já existe um item com o título %s.";
+#endif
+
const char *Txt_Altitude =
#if L==1 // ca
"Altitud";
@@ -5182,6 +5203,27 @@ const char *Txt_course =
"disciplina";
#endif
+const char *Txt_Course_program =
+#if L==1 // ca
+ "Programa de l'assignatura";
+#elif L==2 // de
+ "Kursprogramm";
+#elif L==3 // en
+ "Course program";
+#elif L==4 // es
+ "Programa de la asignatura";
+#elif L==5 // fr
+ "Programme de la matière";
+#elif L==6 // gn
+ "Programa de la asignatura"; // Okoteve traducción
+#elif L==7 // it
+ "Programma del corso";
+#elif L==8 // pl
+ "Program kursu";
+#elif L==9 // pt
+ "Programa da disciplina";
+#endif
+
const char *Txt_COURSE_STATUS[Crs_NUM_STATUS_TXT] =
{
[Crs_STATUS_UNKNOWN] =
@@ -5941,6 +5983,27 @@ const char *Txt_Create_institution =
"Criar institução";
#endif
+const char *Txt_Create_item =
+#if L==1 // ca
+ "Crear ítem";
+#elif L==2 // de
+ "Artikel eingeben";
+#elif L==3 // en
+ "Create item";
+#elif L==4 // es
+ "Crear ítem";
+#elif L==5 // fr
+ "Créer article";
+#elif L==6 // gn
+ "Crear ítem"; // Okoteve traducción
+#elif L==7 // it
+ "Crea articolo";
+#elif L==8 // pl
+ "Utwórz przedmiot";
+#elif L==9 // pt
+ "Criar item";
+#endif
+
const char *Txt_Create_link =
#if L==1 // ca
"Crear enllaç";
@@ -6529,6 +6592,27 @@ const char *Txt_Created_new_institution_X = // Warning: it is very important to
"Criada nova institução %s.";
#endif
+const char *Txt_Created_new_item_X = // Warning: it is very important to include %s in the following sentences
+#if L==1 // ca
+ "Creado nuevo ítem %s."; // Necessita traduccio
+#elif L==2 // de
+ "Neue Artikel %s eingegeben.";
+#elif L==3 // en
+ "Created new item %s.";
+#elif L==4 // es
+ "Creado nuevo ítem %s.";
+#elif L==5 // fr
+ "Créée nouvel article %s.";
+#elif L==6 // gn
+ "Creado nuevo ítem %s."; // Okoteve traducción
+#elif L==7 // it
+ "Creato nuovo articolo %s.";
+#elif L==8 // pl
+ "Utworzono nowa przedmiot %s.";
+#elif L==9 // pt
+ "Criado novo item %s.";
+#endif
+
const char *Txt_Created_new_link_X = // Warning: it is very important to include %s in the following sentences
#if L==1 // ca
"Creado nuevo enlace %s."; // Necessita traduccio
@@ -9080,6 +9164,27 @@ const char *Txt_Do_you_really_want_to_remove_the_group_X_Y_students_ = // Warnin
"Ao fazer isso, você removerá %u estudantes desse grupo.";
#endif
+const char *Txt_Do_you_really_want_to_remove_the_item_X = // Warning: it is very important to include %s in the following sentences
+#if L==1 // ca
+ "De veres voleu eliminar l'ítem %s?";
+#elif L==2 // de
+ "Wollen Sie die Artikel %s wirklich entfernen?";
+#elif L==3 // en
+ "Do you really want to remove the item %s?";
+#elif L==4 // es
+ "¿Realmente desea eliminar el ítem %s?";
+#elif L==5 // fr
+ "Voulez-vous vraiment supprimer l'article %s?";
+#elif L==6 // gn
+ "¿Realmente desea eliminar el ítem %s?"; // Okoteve traducción
+#elif L==7 // it
+ "Vuoi realmente rimuovere il articolo %s?";
+#elif L==8 // pl
+ "Czy na pewno chcesz usunac przedmiot %s?";
+#elif L==9 // pt
+ "Você realmente deseja remover o item %s?";
+#endif
+
const char *Txt_Do_you_really_want_to_remove_the_match_X = // Warning: it is very important to include %s in the following sentences
#if L==1 // ca
"De veres voleu eliminar la partida %s?";
@@ -9145,7 +9250,7 @@ const char *Txt_Do_you_really_want_to_remove_the_project_X = // Warning: it is v
const char *Txt_Do_you_really_want_to_remove_the_question_X = // Warning: it is very important to include %ld in the following sentences
#if L==1 // ca
- "De veres voleu eliminar pregunta %ld?";
+ "De veres voleu eliminar la pregunta %ld?";
#elif L==2 // de
"Wollen Sie die Frage %ld wirklich entfernen?";
#elif L==3 // en
@@ -9896,6 +10001,27 @@ const char *Txt_Edit_game =
"Editar jogo";
#endif
+const char *Txt_Edit_item =
+#if L==1 // ca
+ "Editar ítem";
+#elif L==2 // de
+ "Artikel bearbeiten";
+#elif L==3 // en
+ "Edit item";
+#elif L==4 // es
+ "Editar ítem";
+#elif L==5 // fr
+ "Éditer article";
+#elif L==6 // gn
+ "Editar ítem"; // Okoteve traducción
+#elif L==7 // it
+ "Editare articolo";
+#elif L==8 // pl
+ "Edycja przedmiot";
+#elif L==9 // pt
+ "Editar item";
+#endif
+
const char *Txt_Edit_my_personal_data =
#if L==1 // ca
"Edita meves dades personals";
@@ -15912,7 +16038,7 @@ const char *Txt_INFO_TITLE[Inf_NUM_INFO_TYPES] =
#elif L==2 // de
"Studienplan (Vorlesungen)"
#elif L==3 // en
- "Syllabus (lectures)"
+ "Topics (lectures)"
#elif L==4 // es
"Temario teoría"
#elif L==5 // fr
@@ -15933,7 +16059,7 @@ const char *Txt_INFO_TITLE[Inf_NUM_INFO_TYPES] =
#elif L==2 // de
"Studienplan (Übungen)"
#elif L==3 // en
- "Syllabus (practicals)"
+ "Topics (practicals)"
#elif L==4 // es
"Temario prácticas"
#elif L==5 // fr
@@ -17083,6 +17209,27 @@ const char *Txt_It_is_optional_to_choose_a_group =
"Escolher um grupo é voluntário";
#endif
+const char *Txt_Item =
+#if L==1 // ca
+ "Ítem";
+#elif L==2 // de
+ "Artikel";
+#elif L==3 // en
+ "Item";
+#elif L==4 // es
+ "Ítem";
+#elif L==5 // fr
+ "Article";
+#elif L==6 // gn
+ "Ítem"; // Okoteve traducción
+#elif L==7 // it
+ "Articolo";
+#elif L==8 // pl
+ "Pozycja";
+#elif L==9 // pt
+ "Item";
+#endif
+
const char *Txt_Its_me =
#if L==1 // ca
"Sóc jo!";
@@ -19223,13 +19370,13 @@ const char *Txt_MENU_TITLE[Tab_NUM_TABS][Act_MAX_OPTIONS_IN_MENU_PER_TAB] =
"Informação"
#endif
,
- // 1: ActSeeScd
+ // 1: ActSeePrg
#if L==1 // ca
"Programa"
#elif L==2 // de
"Programm"
#elif L==3 // en
- "Schedule"
+ "Program"
#elif L==4 // es
"Programa"
#elif L==5 // fr
@@ -19271,7 +19418,7 @@ const char *Txt_MENU_TITLE[Tab_NUM_TABS][Act_MAX_OPTIONS_IN_MENU_PER_TAB] =
#elif L==2 // de
"Studienplan"
#elif L==3 // en
- "Syllabus"
+ "Topics"
#elif L==4 // es
"Temario"
#elif L==5 // fr
@@ -21079,7 +21226,7 @@ const char *Txt_MENU_SUBTITLE[Tab_NUM_TABS][Act_MAX_OPTIONS_IN_MENU_PER_TAB] =
"Informação desta disciplina"
#endif
,
- // 1: ActSeeScd
+ // 1: ActSeePrg
#if L==1 // ca
"Programació didàctica de l'assignatura"
#elif L==2 // de
@@ -21127,7 +21274,7 @@ const char *Txt_MENU_SUBTITLE[Tab_NUM_TABS][Act_MAX_OPTIONS_IN_MENU_PER_TAB] =
#elif L==2 // de
"Studienplan"
#elif L==3 // en
- "Syllabus"
+ "Topics"
#elif L==4 // es
"Temario de la asignatura"
#elif L==5 // fr
@@ -24463,6 +24610,27 @@ const char *Txt_New_institution =
"Nova institução (universidade, faculdade, escola, academia, organização, empresa...)";
#endif
+const char *Txt_New_item =
+#if L==1 // ca
+ "Nou ítem";
+#elif L==2 // de
+ "Neuer Artikel";
+#elif L==3 // en
+ "New item";
+#elif L==4 // es
+ "Nuevo ítem";
+#elif L==5 // fr
+ "Nouvel article";
+#elif L==6 // gn
+ "Nuevo ítem"; // Okoteve traducción
+#elif L==7 // it
+ "Nuovo articolo";
+#elif L==8 // pl
+ "Nowa pozycja";
+#elif L==9 // pt
+ "Novo item";
+#endif
+
const char *Txt_New_link =
#if L==1 // ca
"Nuevo enlace"; // Necessita traduccio
@@ -25510,6 +25678,27 @@ const char *Txt_No_institutions =
"Não há instituções.";
#endif
+const char *Txt_No_items =
+#if L==1 // ca
+ "No hi ha items";
+#elif L==2 // de
+ "Keine Artikel";
+#elif L==3 // en
+ "No items";
+#elif L==4 // es
+ "No hay items";
+#elif L==5 // fr
+ "Il n'y a pas d'articles";
+#elif L==6 // gn
+ "No hay items"; // Okoteve traducción
+#elif L==7 // it
+ "Non ci sono articoli";
+#elif L==8 // pl
+ "Brak przedmiotów";
+#elif L==9 // pt
+ "Não há itens";
+#endif
+
const char *Txt_No_links =
#if L==1 // ca
"No hi ha enllaços.";
@@ -32859,6 +33048,27 @@ const char *Txt_Remove_group =
"Remover grupo";
#endif
+const char *Txt_Remove_item =
+#if L==1 // ca
+ "Eliminar ítem";
+#elif L==2 // de
+ "Entfernen Artikel";
+#elif L==3 // en
+ "Remove item";
+#elif L==4 // es
+ "Eliminar ítem";
+#elif L==5 // fr
+ "Supprimer article";
+#elif L==6 // gn
+ "Eliminar ítem"; // Okoteve traducción
+#elif L==7 // it
+ "Rimuovere articolo";
+#elif L==8 // pl
+ "Usuń przedmiot";
+#elif L==9 // pt
+ "Remover item";
+#endif
+
const char *Txt_Remove_link =
#if L==1 // ca
"Eliminar enllaç";
@@ -41681,6 +41891,27 @@ const char *Txt_FIGURE_TYPES[Fig_NUM_FIGURES] =
"Otwarte Zasoby Edukacyjne (OER)"
#elif L==9 // pt
"Recursos Educacionais Abertos (OER)"
+#endif
+ ,
+ [Fig_COURSE_PROGRAM] =
+#if L==1 // ca
+ "Programa"
+#elif L==2 // de
+ "Programm"
+#elif L==3 // en
+ "Program"
+#elif L==4 // es
+ "Programa"
+#elif L==5 // fr
+ "Programme"
+#elif L==6 // gn
+ "Programa" // Okoteve traducción
+#elif L==7 // it
+ "Programma"
+#elif L==8 // pl
+ "Program"
+#elif L==9 // pt
+ "Programa"
#endif
,
[Fig_ASSIGNMENTS] =
@@ -45894,6 +46125,27 @@ const char *Txt_The_integrated_editor_is_not_yet_available =
"O editor integrado ainda não está disponível.";
#endif
+const char *Txt_The_item_has_been_modified =
+#if L==1 // ca
+ "El ítem ha estat modificat.";
+#elif L==2 // de
+ "Das Artikel wurde geändert.";
+#elif L==3 // en
+ "The item has been modified.";
+#elif L==4 // es
+ "El ítem ha sido modificado.";
+#elif L==5 // fr
+ "L'article a été modifié.";
+#elif L==6 // gn
+ "El ítem ha sido modificado."; // Okoteve traducción
+#elif L==7 // it
+ "Il articolo è stato modificato.";
+#elif L==8 // pl
+ "Gra przedmiot zmodyfikowane.";
+#elif L==9 // pt
+ "O item foi modificado.";
+#endif
+
const char *Txt_The_link_X_already_exists = // Warning: it is very important to include %s in the following sentences
#if L==1 // ca
"El enlace %s ya existe."; // Necessita traduccio
@@ -56821,6 +57073,27 @@ const char *Txt_You_must_specify_the_title_of_the_game =
"Você deve especificar o título do jogo.";
#endif
+const char *Txt_You_must_specify_the_title_of_the_item =
+#if L==1 // ca
+ "Cal especificar el títol del ítem.";
+#elif L==2 // de
+ "Die Artikel muss angegeben werden.";
+#elif L==3 // en
+ "You must specify the title of the item.";
+#elif L==4 // es
+ "Debe especificar el título del ítem.";
+#elif L==5 // fr
+ "Vous devez spécifier le titre du article.";
+#elif L==6 // gn
+ "Debe especificar el título del ítem."; // Okoteve traducción
+#elif L==7 // it
+ "È necessario specificare il titolo del articolo.";
+#elif L==8 // pl
+ "Musisz podać tytuł przedmiot.";
+#elif L==9 // pt
+ "Você deve especificar o título do item.";
+#endif
+
const char *Txt_You_must_specify_the_title_of_the_project =
#if L==1 // ca
"Cal especificar el títol del projecte.";
diff --git a/swad_text_action.c b/swad_text_action.c
index b5926e30..1a16ee7f 100644
--- a/swad_text_action.c
+++ b/swad_text_action.c
@@ -4766,7 +4766,7 @@ const char *Txt_Actions[Act_NUM_ACTIONS] =
"" // Precisa de tradução
#endif
,
- [ActSeeScd] =
+ [ActSeePrg] =
#if L==1 // ca
"" // Necessita traducció
#elif L==2 // de