// swad_info.c: info about course

/*
    SWAD (Shared Workspace At a Distance),
    is a web platform developed at the University of Granada (Spain),
    and used to support university teaching.

    This file is part of SWAD core.
    Copyright (C) 1999-2019 Antonio Caņas Vargas

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/

#include <limits.h>		// For maximum values
#include <linux/limits.h>	// For PATH_MAX, NAME_MAX
#include <linux/stddef.h>	// For NULL
#include <stdlib.h>		// For getenv, etc
#include <stdsoap2.h>		// For SOAP_OK and soap functions
#include <string.h>		// For string functions
#include <unistd.h>		// For unlink

#include "swad_action.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_info.h"
#include "swad_parameter.h"
#include "swad_string.h"

/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/

extern struct Globals Gbl;

/*****************************************************************************/
/*************************** Public constants ********************************/
/*****************************************************************************/

const char *Inf_FileNamesForInfoType[Inf_NUM_INFO_TYPES] =
  {
   Cfg_CRS_INFO_INTRODUCTION,
   Cfg_CRS_INFO_TEACHING_GUIDE,
   Cfg_CRS_INFO_LECTURES,
   Cfg_CRS_INFO_PRACTICALS,
   Cfg_CRS_INFO_BIBLIOGRAPHY,
   Cfg_CRS_INFO_FAQ,
   Cfg_CRS_INFO_LINKS,
   Cfg_CRS_INFO_ASSESSMENT,
  };

/*****************************************************************************/
/****************************** Private types ********************************/
/*****************************************************************************/

/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/

/* Functions to write forms in course edition (FAQ, links, etc.) */
void (*Inf_FormsForEditionTypes[Inf_NUM_INFO_SOURCES])(Inf_InfoSrc_t InfoSrc) =
  {
   NULL,
   Inf_FormToEnterIntegratedEditor,
   Inf_FormToEnterPlainTextEditor,
   Inf_FormToEnterRichTextEditor,
   Inf_FormToSendPage,
   Inf_FormToSendURL,
  };

const char *Inf_NamesInDBForInfoSrc[Inf_NUM_INFO_SOURCES] =
  {
   "none",
   "editor",
   "plain_text",
   "rich_text",
   "page",
   "URL",
  };
const Act_Action_t Inf_ActionsSeeInfo[Inf_NUM_INFO_TYPES] =
  {
   ActSeeCrsInf,
   ActSeeTchGui,
   ActSeeSylLec,
   ActSeeSylPra,
   ActSeeBib,
   ActSeeFAQ,
   ActSeeCrsLnk,
   ActSeeAss,
  };
const Act_Action_t Inf_ActionsEditInfo[Inf_NUM_INFO_TYPES] =
  {
   ActEdiCrsInf,
   ActEdiTchGui,
   ActEdiSylLec,
   ActEdiSylPra,
   ActEdiBib,
   ActEdiFAQ,
   ActEdiCrsLnk,
   ActEdiAss,
  };
const Act_Action_t Inf_ActionsChangeForceReadInfo[Inf_NUM_INFO_TYPES] =
  {
   ActChgFrcReaCrsInf,
   ActChgFrcReaTchGui,
   ActChgFrcReaSylLec,
   ActChgFrcReaSylPra,
   ActChgFrcReaBib,
   ActChgFrcReaFAQ,
   ActChgFrcReaCrsLnk,
   ActChgFrcReaAss,
  };
const Act_Action_t Inf_ActionsIHaveReadInfo[Inf_NUM_INFO_TYPES] =
  {
   ActChgHavReaCrsInf,
   ActChgHavReaTchGui,
   ActChgHavReaSylLec,
   ActChgHavReaSylPra,
   ActChgHavReaBib,
   ActChgHavReaFAQ,
   ActChgHavReaCrsLnk,
   ActChgHavReaAss,
  };
const Act_Action_t Inf_ActionsSelecInfoSrc[Inf_NUM_INFO_TYPES] =
  {
   ActSelInfSrcCrsInf,
   ActSelInfSrcTchGui,
   ActSelInfSrcSylLec,
   ActSelInfSrcSylPra,
   ActSelInfSrcBib,
   ActSelInfSrcFAQ,
   ActSelInfSrcCrsLnk,
   ActSelInfSrcAss,
  };
const Act_Action_t Inf_ActionsInfo[Inf_NUM_INFO_SOURCES][Inf_NUM_INFO_TYPES] =
  {
   {
    ActUnk,
    ActUnk,
    ActUnk,
    ActUnk,
    ActUnk,
    ActUnk,
    ActUnk,
    ActUnk,
   },
   {
    ActEditorCrsInf,
    ActEditorTchGui,
    ActEditorSylLec,
    ActEditorSylPra,
    ActEditorBib,
    ActEditorFAQ,
    ActEditorCrsLnk,
    ActEditorAss,
   },
   {
    ActPlaTxtEdiCrsInf,
    ActPlaTxtEdiTchGui,
    ActPlaTxtEdiSylLec,
    ActPlaTxtEdiSylPra,
    ActPlaTxtEdiBib,
    ActPlaTxtEdiFAQ,
    ActPlaTxtEdiCrsLnk,
    ActPlaTxtEdiAss,
   },
   {
    ActRchTxtEdiCrsInf,
    ActRchTxtEdiTchGui,
    ActRchTxtEdiSylLec,
    ActRchTxtEdiSylPra,
    ActRchTxtEdiBib,
    ActRchTxtEdiFAQ,
    ActRchTxtEdiCrsLnk,
    ActRchTxtEdiAss,
   },
   {
    ActRcvPagCrsInf,
    ActRcvPagTchGui,
    ActRcvPagSylLec,
    ActRcvPagSylPra,
    ActRcvPagBib,
    ActRcvPagFAQ,
    ActRcvPagCrsLnk,
    ActRcvPagAss,
   },
   {
    ActRcvURLCrsInf,
    ActRcvURLTchGui,
    ActRcvURLSylLec,
    ActRcvURLSylPra,
    ActRcvURLBib,
    ActRcvURLFAQ,
    ActRcvURLCrsLnk,
    ActRcvURLAss,
   }
  };

const Act_Action_t Inf_ActionsEditorInfo[Inf_NUM_INFO_TYPES] =
  {
   ActEditorCrsInf,
   ActEditorTchGui,
   ActEditorSylLec,
   ActEditorSylPra,
   ActEditorBib,
   ActEditorFAQ,
   ActEditorCrsLnk,
   ActEditorAss,
  };
const Act_Action_t Inf_ActionsRcvPlaTxtInfo[Inf_NUM_INFO_TYPES] =
  {
   ActRcvPlaTxtCrsInf,
   ActRcvPlaTxtTchGui,
   ActRcvPlaTxtSylLec,
   ActRcvPlaTxtSylPra,
   ActRcvPlaTxtBib,
   ActRcvPlaTxtFAQ,
   ActRcvPlaTxtCrsLnk,
   ActRcvPlaTxtAss,
  };
const Act_Action_t Inf_ActionsRcvRchTxtInfo[Inf_NUM_INFO_TYPES] =
  {
   ActRcvRchTxtCrsInf,
   ActRcvRchTxtTchGui,
   ActRcvRchTxtSylLec,
   ActRcvRchTxtSylPra,
   ActRcvRchTxtBib,
   ActRcvRchTxtFAQ,
   ActRcvRchTxtCrsLnk,
   ActRcvRchTxtAss,
  };
const Act_Action_t Inf_ActionsRcvPagBibEvEtc[Inf_NUM_INFO_TYPES] =
  {
   ActRcvPagCrsInf,
   ActRcvPagTchGui,
   ActRcvPagSylLec,
   ActRcvPagSylPra,
   ActRcvPagBib,
   ActRcvPagFAQ,
   ActRcvPagCrsLnk,
   ActRcvPagAss,
  };
const Act_Action_t In_ActionsRcvURLBibEvEtc[Inf_NUM_INFO_TYPES] =
  {
   ActRcvURLCrsInf,
   ActRcvURLTchGui,
   ActRcvURLSylLec,
   ActRcvURLSylPra,
   ActRcvURLBib,
   ActRcvURLFAQ,
   ActRcvURLCrsLnk,
   ActRcvURLAss,
  };
const char *Inf_NamesInDBForInfoType[Inf_NUM_INFO_TYPES] =
  {
   "intro",		// TODO: Change this to "introduction"!
   "description",	// TODO: Change this to "guide"!
   "theory",		// TODO: Change this to "lectures"!
   "practices",		// TODO: Change this to "practicals"!
   "bibliography",
   "FAQ",
   "links",
   "assessment",
  };

/***** Help *****/
extern const char *Hlp_COURSE_Information_textual_information;
extern const char *Hlp_COURSE_Guide;
extern const char *Hlp_COURSE_Syllabus;
extern const char *Hlp_COURSE_Bibliography;
extern const char *Hlp_COURSE_FAQ;
extern const char *Hlp_COURSE_Links;
extern const char *Hlp_ASSESSMENT_System;

extern const char *Hlp_COURSE_Information_edit;
extern const char *Hlp_COURSE_Guide_edit;
extern const char *Hlp_COURSE_Syllabus_edit;
extern const char *Hlp_COURSE_Bibliography_edit;
extern const char *Hlp_COURSE_FAQ_edit;
extern const char *Hlp_COURSE_Links_edit;
extern const char *Hlp_ASSESSMENT_System_edit;

/*****************************************************************************/
/**************************** Private prototypes *****************************/
/*****************************************************************************/

static void Inf_PutButtonToEditInfo (void);
static void Inf_PutIconToViewInfo (void);
static void Inf_PutCheckboxForceStdsToReadInfo (bool MustBeRead,bool Disabled);
static void Inf_PutCheckboxConfirmIHaveReadInfo (void);
static bool Inf_CheckIfIHaveReadInfo (void);
static bool Inf_GetMustBeReadFromForm (void);
static bool Inf_GetIfIHaveReadFromForm (void);
static void Inf_SetForceReadIntoDB (bool MustBeRead);
static void Inf_SetIHaveReadIntoDB (bool IHaveRead);

static bool Inf_CheckPage (long CrsCod,Inf_InfoType_t InfoType);
static bool Inf_CheckAndShowPage (void);
static void Inf_BuildPathPage (long CrsCod,Inf_InfoType_t InfoType,char PathDir[PATH_MAX + 1]);

static bool Inf_CheckURL (long CrsCod,Inf_InfoType_t InfoType);
static bool Inf_CheckAndShowURL (void);
static void Inf_BuildPathURL (long CrsCod,Inf_InfoType_t InfoType,
                              char PathFile[PATH_MAX + 1]);

static void Inf_ShowPage (const char *URL);

static bool Inf_CheckIfInfoAvailable (Inf_InfoSrc_t InfoSrc);
static Inf_InfoType_t Inf_AsignInfoType (void);
static void Inf_SetInfoTxtIntoDB (const char *InfoTxtHTML,const char *InfoTxtMD);
static void Inf_GetInfoTxtFromDB (long CrsCod,Inf_InfoType_t InfoType,
                                  char InfoTxtHTML[Cns_MAX_BYTES_LONG_TEXT + 1],
                                  char InfoTxtMD[Cns_MAX_BYTES_LONG_TEXT + 1]);

static bool Inf_CheckPlainTxt (long CrsCod,Inf_InfoType_t InfoType);
static bool Inf_CheckAndShowPlainTxt (void);

static bool Inf_CheckRichTxt (long CrsCod,Inf_InfoType_t InfoType);
static bool Inf_CheckAndShowRichTxt (void);

/*****************************************************************************/
/******** Show course info (theory, practices, bibliography, etc.) ***********/
/*****************************************************************************/

void Inf_ShowInfo (void)
  {
   extern const char *Txt_INFO_TITLE[Inf_NUM_INFO_TYPES];
   extern const char *Txt_No_information;
   Inf_InfoSrc_t InfoSrc;
   bool MustBeRead;
   bool Disabled;
   bool ICanEdit = (Gbl.Usrs.Me.Role.Logged == Rol_TCH ||
                    Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM);
   bool ShowWarningNoInfo = false;
   const char *Help[Inf_NUM_INFO_TYPES] =
     {
      Hlp_COURSE_Information_textual_information,	// Inf_INTRODUCTION
      Hlp_COURSE_Guide,				// Inf_TEACHING_GUIDE
      Hlp_COURSE_Syllabus,				// Inf_LECTURES
      Hlp_COURSE_Syllabus,				// Inf_PRACTICALS
      Hlp_COURSE_Bibliography,			// Inf_BIBLIOGRAPHY
      Hlp_COURSE_FAQ,				// Inf_FAQ
      Hlp_COURSE_Links,				// Inf_LINKS
      Hlp_ASSESSMENT_System,			// Inf_ASSESSMENT
     };

   /***** Set info type *****/
   Gbl.Crs.Info.Type = Inf_AsignInfoType ();

   /***** Get info source from database *****/
   Inf_GetAndCheckInfoSrcFromDB (Gbl.Hierarchy.Crs.CrsCod,
                                 Gbl.Crs.Info.Type,
                                 &InfoSrc,&MustBeRead);

   switch (Gbl.Crs.Info.Type)
     {
      case Inf_LECTURES:
      case Inf_PRACTICALS:
         Syl_PutFormWhichSyllabus ();
         break;
      default:
	 break;
     }

   switch (Gbl.Usrs.Me.Role.Logged)
     {
      case Rol_STD:
         /* Put checkbox to force students to read this couse info */
         if (MustBeRead)
           {
            fprintf (Gbl.F.Out,"<div class=\"CONTEXT_MENU\">");
            Inf_PutCheckboxConfirmIHaveReadInfo ();
            fprintf (Gbl.F.Out,"</div>");
           }
         break;
      case Rol_NET:
      case Rol_TCH:
      case Rol_SYS_ADM:
         /* Put checkbox to force students to read this couse info */
         if (InfoSrc != Inf_INFO_SRC_NONE)
           {
            fprintf (Gbl.F.Out,"<div class=\"CONTEXT_MENU\">");
            Disabled = (Gbl.Usrs.Me.Role.Logged == Rol_NET);	// Non-editing teachers can not change the status of checkbox
            Inf_PutCheckboxForceStdsToReadInfo (MustBeRead,Disabled);
            fprintf (Gbl.F.Out,"</div>");
           }
         break;
      default:
         break;
     }

   switch (InfoSrc)
     {
      case Inf_INFO_SRC_NONE:
	 ShowWarningNoInfo = true;
         break;
      case Inf_INFO_SRC_EDITOR:
         switch (Gbl.Crs.Info.Type)
           {
            case Inf_LECTURES:
            case Inf_PRACTICALS:
               ShowWarningNoInfo = !Syl_CheckAndEditSyllabus ();
               break;
            case Inf_INTRODUCTION:
            case Inf_TEACHING_GUIDE:
            case Inf_BIBLIOGRAPHY:
            case Inf_FAQ:
            case Inf_LINKS:
            case Inf_ASSESSMENT:
               ShowWarningNoInfo = true;
	       break;
           }
         break;
      case Inf_INFO_SRC_PLAIN_TEXT:
         ShowWarningNoInfo = !Inf_CheckAndShowPlainTxt ();
         break;
      case Inf_INFO_SRC_RICH_TEXT:
         ShowWarningNoInfo = !Inf_CheckAndShowRichTxt ();
         break;
      case Inf_INFO_SRC_PAGE:
         /***** Open file with web page *****/
	 ShowWarningNoInfo = !Inf_CheckAndShowPage ();
         break;
      case Inf_INFO_SRC_URL:
         /***** Check if file with URL exists *****/
	 ShowWarningNoInfo = !Inf_CheckAndShowURL ();
         break;
     }

   if (ShowWarningNoInfo)
     {
      Box_StartBox ("100%",Txt_INFO_TITLE[Gbl.Crs.Info.Type],
		    ICanEdit ? Inf_PutIconToEditInfo :
			       NULL,
		    Help[Gbl.Crs.Info.Type],Box_NOT_CLOSABLE);
      Ale_ShowAlert (Ale_INFO,Txt_No_information);
      if (ICanEdit)
	 Inf_PutButtonToEditInfo ();
      Box_EndBox ();
     }
  }

/*****************************************************************************/
/*********************** Put button to edit course info **********************/
/*****************************************************************************/

static void Inf_PutButtonToEditInfo (void)
  {
   extern const char *Txt_Edit;

   Frm_StartForm (Inf_ActionsEditInfo[Gbl.Crs.Info.Type]);
   Btn_PutConfirmButton (Txt_Edit);
   Frm_EndForm ();
  }

/*****************************************************************************/
/************************ Put icon to edit course info ***********************/
/*****************************************************************************/

static void Inf_PutIconToViewInfo (void)
  {
   Ico_PutContextualIconToView (Inf_ActionsSeeInfo[Gbl.Crs.Info.Type],NULL);
  }

void Inf_PutIconToEditInfo (void)
  {
   Ico_PutContextualIconToEdit (Inf_ActionsEditInfo[Gbl.Crs.Info.Type],NULL);
  }

/*****************************************************************************/
/********** Put a form (checkbox) to force students to read info *************/
/*****************************************************************************/

static void Inf_PutCheckboxForceStdsToReadInfo (bool MustBeRead,bool Disabled)
  {
   extern const char *Txt_Force_students_to_read_this_information;

   Lay_PutContextualCheckbox (Inf_ActionsChangeForceReadInfo[Gbl.Crs.Info.Type],
                              NULL,
                              "MustBeRead",
                              MustBeRead,Disabled,
                              Txt_Force_students_to_read_this_information,
                              Txt_Force_students_to_read_this_information);
  }

/*****************************************************************************/
/********** Put a form (checkbox) to force students to read info *************/
/*****************************************************************************/

static void Inf_PutCheckboxConfirmIHaveReadInfo (void)
  {
   extern const char *Txt_I_have_read_this_information;
   bool IHaveRead = Inf_CheckIfIHaveReadInfo ();

   Lay_PutContextualCheckbox (Inf_ActionsIHaveReadInfo[Gbl.Crs.Info.Type],
                              NULL,
                              "IHaveRead",
                              IHaveRead,false,
                              Txt_I_have_read_this_information,
                              Txt_I_have_read_this_information);
  }

/*****************************************************************************/
/******************** Check I have read a course info ************************/
/*****************************************************************************/

static bool Inf_CheckIfIHaveReadInfo (void)
  {
   /***** Get if info source is already stored in database *****/
   return (DB_QueryCOUNT ("can not get if I have read course info",
			  "SELECT COUNT(*) FROM crs_info_read"
			  " WHERE UsrCod=%ld AND CrsCod=%ld AND InfoType='%s'",
			  Gbl.Usrs.Me.UsrDat.UsrCod,
			  Gbl.Hierarchy.Crs.CrsCod,
			  Inf_NamesInDBForInfoType[Gbl.Crs.Info.Type]) != 0);
  }

/*****************************************************************************/
/********* Get if students must read any info about current course ***********/
/*****************************************************************************/

bool Inf_GetIfIMustReadAnyCrsInfoInThisCrs (void)
  {
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   unsigned long NumRow;
   unsigned long NumRows;
   Inf_InfoType_t InfoType;

   /***** Reset must-be-read to false for all info types *****/
   for (InfoType = (Inf_InfoType_t) 0;
	InfoType < Inf_NUM_INFO_TYPES;
	InfoType++)
      Gbl.Crs.Info.MustBeRead[InfoType] = false;

   /***** Get info types where students must read info *****/
   NumRows = DB_QuerySELECT (&mysql_res,"can not get if you must read"
					" any course info",
			     "SELECT InfoType FROM crs_info_src"
			     " WHERE CrsCod=%ld AND MustBeRead='Y'"
			     " AND InfoType NOT IN"
			     " (SELECT InfoType FROM crs_info_read"
			     " WHERE UsrCod=%ld AND CrsCod=%ld)",
			     Gbl.Hierarchy.Crs.CrsCod,
			     Gbl.Usrs.Me.UsrDat.UsrCod,
			     Gbl.Hierarchy.Crs.CrsCod);

   /***** Set must-be-read to true for each rown in result *****/
   for (NumRow = 0;
	NumRow < NumRows;
	NumRow++)
     {
      row = mysql_fetch_row (mysql_res);

      /* Get info type (row[0]) */
      InfoType = Inf_ConvertFromStrDBToInfoType (row[0]);

      Gbl.Crs.Info.MustBeRead[InfoType] = true;
     }

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);

   return (NumRows != 0);
  }

/*****************************************************************************/
/***** Write message if students must read any info about current course *****/
/*****************************************************************************/

void Inf_WriteMsgYouMustReadInfo (void)
  {
   extern const char *The_ClassFormInBox[The_NUM_THEMES];
   extern const char *Txt_Required_reading;
   extern const char *Txt_You_should_read_the_following_information;
   Inf_InfoType_t InfoType;

   /***** Start box *****/
   Box_StartBox (NULL,Txt_Required_reading,NULL,
                 NULL,Box_NOT_CLOSABLE);

   /***** Write message *****/
   Ale_ShowAlert (Ale_WARNING,Txt_You_should_read_the_following_information);

   /***** Write every information I must read *****/
   fprintf (Gbl.F.Out,"<div class=\"CENTER_MIDDLE\">"
	              "<ul class=\"LIST_LEFT\""
	              " style=\"list-style-type:initial;\">");
   for (InfoType = (Inf_InfoType_t) 0;
	InfoType < Inf_NUM_INFO_TYPES;
	InfoType++)
      if (Gbl.Crs.Info.MustBeRead[InfoType])
        {
         fprintf (Gbl.F.Out,"<li>");
         Frm_StartForm (Inf_ActionsSeeInfo[InfoType]);
         Frm_LinkFormSubmit (Act_GetTitleAction (Inf_ActionsSeeInfo[InfoType]),
                             The_ClassFormInBox[Gbl.Prefs.Theme],NULL);
         fprintf (Gbl.F.Out,"%s"
                            "</a>",
                  Act_GetTitleAction (Inf_ActionsSeeInfo[InfoType]));
         Frm_EndForm ();
         fprintf (Gbl.F.Out,"</li>");
        }
   fprintf (Gbl.F.Out,"</ul>"
	              "</div>");

   /***** End box *****/
   Box_EndBox ();
  }

/*****************************************************************************/
/****** Change teacher's preference about force students to read info ********/
/*****************************************************************************/

void Inf_ChangeForceReadInfo (void)
  {
   extern const char *Txt_Students_now_are_required_to_read_this_information;
   extern const char *Txt_Students_are_no_longer_obliged_to_read_this_information;
   bool MustBeRead = Inf_GetMustBeReadFromForm ();

   /***** Set info type *****/
   Gbl.Crs.Info.Type = Inf_AsignInfoType ();

   /***** Set status (if info must be read or not) into database *****/
   Inf_SetForceReadIntoDB (MustBeRead);

   /***** Write message of success *****/
   Ale_ShowAlert (Ale_SUCCESS,
                  MustBeRead ? Txt_Students_now_are_required_to_read_this_information :
                               Txt_Students_are_no_longer_obliged_to_read_this_information);

   /***** Show the selected info *****/
   Inf_ShowInfo ();
  }

/*****************************************************************************/
/************** Change confirmation of I have read course info ***************/
/*****************************************************************************/

void Inf_ChangeIHaveReadInfo (void)
  {
   extern const char *Txt_You_have_confirmed_that_you_have_read_this_information;
   extern const char *Txt_You_have_eliminated_the_confirmation_that_you_have_read_this_information;
   bool IHaveRead = Inf_GetIfIHaveReadFromForm ();

   /***** Set info type *****/
   Gbl.Crs.Info.Type = Inf_AsignInfoType ();

   /***** Set status (if I have read or not a information) into database *****/
   Inf_SetIHaveReadIntoDB (IHaveRead);

   /***** Write message of success *****/
   Ale_ShowAlert (Ale_SUCCESS,
                  IHaveRead ? Txt_You_have_confirmed_that_you_have_read_this_information :
                              Txt_You_have_eliminated_the_confirmation_that_you_have_read_this_information);

   /***** Show the selected info *****/
   Inf_ShowInfo ();
  }

/*****************************************************************************/
/************* Get if info must be read by students from form ****************/
/*****************************************************************************/

static bool Inf_GetMustBeReadFromForm (void)
  {
   return Par_GetParToBool ("MustBeRead");
  }

/*****************************************************************************/
/************* Get if info must be read by students from form ****************/
/*****************************************************************************/

static bool Inf_GetIfIHaveReadFromForm (void)
  {
   return Par_GetParToBool ("IHaveRead");
  }

/*****************************************************************************/
/***************** Set if students must read course info *********************/
/*****************************************************************************/

static void Inf_SetForceReadIntoDB (bool MustBeRead)
  {
   /***** Insert or replace info source for a specific type of course information *****/
   DB_QueryUPDATE ("can not update if info must be read",
		   "UPDATE crs_info_src SET MustBeRead='%c'"
		   " WHERE CrsCod=%ld AND InfoType='%s'",
                   MustBeRead ? 'Y' :
        	                'N',
                   Gbl.Hierarchy.Crs.CrsCod,
		   Inf_NamesInDBForInfoType[Gbl.Crs.Info.Type]);
  }

/*****************************************************************************/
/********************* Set if I have read course info ************************/
/*****************************************************************************/

static void Inf_SetIHaveReadIntoDB (bool IHaveRead)
  {
   if (IHaveRead)
      /***** Insert I have read course information *****/
      DB_QueryREPLACE ("can not set that I have read course info",
		       "REPLACE INTO crs_info_read"
		       " (UsrCod,CrsCod,InfoType)"
		       " VALUES"
		       " (%ld,%ld,'%s')",
                       Gbl.Usrs.Me.UsrDat.UsrCod,
                       Gbl.Hierarchy.Crs.CrsCod,
                       Inf_NamesInDBForInfoType[Gbl.Crs.Info.Type]);
   else
      /***** Remove I have read course information *****/
      DB_QueryDELETE ("can not set that I have not read course info",
		      "DELETE FROM crs_info_read"
		      " WHERE UsrCod=%ld AND CrsCod=%ld AND InfoType='%s'",
		      Gbl.Usrs.Me.UsrDat.UsrCod,
		      Gbl.Hierarchy.Crs.CrsCod,
		      Inf_NamesInDBForInfoType[Gbl.Crs.Info.Type]);
  }

/*****************************************************************************/
/********* Remove user's status about reading of course information **********/
/*****************************************************************************/

void Inf_RemoveUsrFromCrsInfoRead (long UsrCod,long CrsCod)
  {
   /***** Remove user's status about reading of course information *****/
   DB_QueryDELETE ("can not set that I have not read course info",
		   "DELETE FROM crs_info_read"
		   " WHERE UsrCod=%ld AND CrsCod=%ld",
                   UsrCod,CrsCod);
  }

/*****************************************************************************/
/************************** Check if exists a page ***************************/
/*****************************************************************************/
// Return true if info available

static bool Inf_CheckPage (long CrsCod,Inf_InfoType_t InfoType)
  {
   char PathRelDirHTML[PATH_MAX + 1];
   char PathRelFileHTML[PATH_MAX + 1];

   // TODO !!!!!!!!!!!! If the page is hosted in server ==> it should be created a temporary public directory
   //                                                       and host the page in a private directory !!!!!!!!!!!!!!!!!

   /***** Build path of directory containing web page *****/
   Inf_BuildPathPage (CrsCod,InfoType,PathRelDirHTML);

   /***** Open file with web page *****/
   /* 1. Check if index.html exists */
   snprintf (PathRelFileHTML,sizeof (PathRelFileHTML),
	     "%s/index.html",
	     PathRelDirHTML);
   if (Fil_CheckIfPathExists (PathRelFileHTML))	// TODO: Check if not empty?
      return true;

   /* 2. If index.html does not exist, try index.htm */
   snprintf (PathRelFileHTML,sizeof (PathRelFileHTML),
	     "%s/index.htm",
	     PathRelDirHTML);
   if (Fil_CheckIfPathExists (PathRelFileHTML))	// TODO: Check if not empty?
      return true;

   return false;
  }

/*****************************************************************************/
/**************** Check if exists and show link to a page ********************/
/*****************************************************************************/
// Return true if info available

static bool Inf_CheckAndShowPage (void)
  {
   char PathRelDirHTML[PATH_MAX + 1];
   char PathRelFileHTML[PATH_MAX + 1];
   char URL[PATH_MAX + 1];

   // TODO !!!!!!!!!!!! If the page is hosted in server ==> it should be created a temporary public directory
   //                                                       and host the page in a private directory !!!!!!!!!!!!!!!!!

   /***** Build path of directory containing web page *****/
   Inf_BuildPathPage (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,PathRelDirHTML);

   /***** Open file with web page *****/
   /* 1. Check if index.html exists */
   snprintf (PathRelFileHTML,sizeof (PathRelFileHTML),
	     "%s/index.html",
	     PathRelDirHTML);
   if (Fil_CheckIfPathExists (PathRelFileHTML))	// TODO: Check if not empty?
     {
      snprintf (URL,sizeof (URL),
	        "%s/%ld/%s/index.html",
	        Cfg_URL_CRS_PUBLIC,Gbl.Hierarchy.Crs.CrsCod,
	        Inf_FileNamesForInfoType[Gbl.Crs.Info.Type]);
      Inf_ShowPage (URL);

      return true;
     }

   /* 2. If index.html does not exist, try index.htm */
   snprintf (PathRelFileHTML,sizeof (PathRelFileHTML),
	     "%s/index.htm",
	     PathRelDirHTML);
   if (Fil_CheckIfPathExists (PathRelFileHTML))	// TODO: Check if not empty?
     {
      snprintf (URL,sizeof (URL),
	        "%s/%ld/%s/index.htm",
	        Cfg_URL_CRS_PUBLIC,Gbl.Hierarchy.Crs.CrsCod,
	        Inf_FileNamesForInfoType[Gbl.Crs.Info.Type]);
      Inf_ShowPage (URL);

      return true;
     }

   return false;
  }

/*****************************************************************************/
/* Build path inside a course for a given a info type to store web page file */
/*****************************************************************************/

static void Inf_BuildPathPage (long CrsCod,Inf_InfoType_t InfoType,char PathDir[PATH_MAX + 1])
  {
   snprintf (PathDir,PATH_MAX + 1,
	     "%s/%ld/%s",
             Cfg_PATH_CRS_PUBLIC,CrsCod,
             Inf_FileNamesForInfoType[InfoType]);
  }

/*****************************************************************************/
/************* Check if exists and write page into HTML buffer ***************/
/*****************************************************************************/
// This function is called only from web service

int Inf_WritePageIntoHTMLBuffer (char **HTMLBuffer)
  {
   char PathRelDirHTML[PATH_MAX + 1];
   char PathRelFileHTML[PATH_MAX + 1];
   FILE *FileHTML;
   bool FileExists = false;
   size_t Length;

   /***** Initialize buffer *****/
   *HTMLBuffer = NULL;

   /***** Build path of directory containing web page *****/
   Inf_BuildPathPage (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,PathRelDirHTML);

   /***** Open file with web page *****/
   /* 1. Check if index.html exists */
   snprintf (PathRelFileHTML,sizeof (PathRelFileHTML),
	     "%s/index.html",
	     PathRelDirHTML);
   if (Fil_CheckIfPathExists (PathRelFileHTML))		// TODO: Check if not empty?
      FileExists = true;
   else
     {
      /* 2. If index.html not exists, try index.htm */
      snprintf (PathRelFileHTML,sizeof (PathRelFileHTML),
	        "%s/index.htm",
		PathRelDirHTML);
      if (Fil_CheckIfPathExists (PathRelFileHTML))	// TODO: Check if not empty?
         FileExists = true;
     }

   if (FileExists)
     {
      /***** Write page from file to text buffer *****/
      /* Open file */
      if ((FileHTML = fopen (PathRelFileHTML,"rb")) == NULL)
	 Lay_ShowErrorAndExit ("Can not open XML file.");

      /* Compute file size */
      fseek (FileHTML,0L,SEEK_END);
      Length = (size_t) ftell (FileHTML);
      fseek (FileHTML,0L,SEEK_SET);

      /* Allocate memory for buffer */
      if ((*HTMLBuffer = (char *) malloc (Length + 1)) == NULL)
	{
	 fclose (FileHTML);
	 Lay_NotEnoughMemoryExit ();
         return soap_receiver_fault (Gbl.soap,
                                     "Web page can not be copied into buffer",
                                     "Not enough memory for buffer");
	}

      /* Copy file content into buffer */
      if (fread ((void *) *HTMLBuffer,sizeof (char),Length,FileHTML) != Length)
	{
	 fclose (FileHTML);
         return soap_receiver_fault (Gbl.soap,
                                     "Web page can not be copied into buffer",
                                     "Error reading web page into buffer");
	}
      (*HTMLBuffer)[Length] = '\0';

      /***** Close HTML file *****/
      fclose (FileHTML);
     }

   return SOAP_OK;
  }

/*****************************************************************************/
/********************* Check if exists link to a page ************************/
/*****************************************************************************/
// Return true if info available

static bool Inf_CheckURL (long CrsCod,Inf_InfoType_t InfoType)
  {
   char PathFile[PATH_MAX + 1];
   FILE *FileURL;

   /***** Build path to file containing URL *****/
   Inf_BuildPathURL (CrsCod,InfoType,PathFile);

   /***** Check if file with URL exists *****/
   if ((FileURL = fopen (PathFile,"rb")))
     {
      if (fgets (Gbl.Crs.Info.URL,Cns_MAX_BYTES_WWW,FileURL) == NULL)
	 Gbl.Crs.Info.URL[0] = '\0';
      /* File is not longer needed  ==> close it */
      fclose (FileURL);

      if (Gbl.Crs.Info.URL[0])
         return true;
     }

   return false;
  }

/*****************************************************************************/
/**************** Check if exists and show link to a page ********************/
/*****************************************************************************/
// Return true if info available

static bool Inf_CheckAndShowURL (void)
  {
   char PathFile[PATH_MAX + 1];
   FILE *FileURL;

   /***** Build path to file containing URL *****/
   Inf_BuildPathURL (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,PathFile);

   /***** Check if file with URL exists *****/
   if ((FileURL = fopen (PathFile,"rb")))
     {
      if (fgets (Gbl.Crs.Info.URL,Cns_MAX_BYTES_WWW,FileURL) == NULL)
	 Gbl.Crs.Info.URL[0] = '\0';
      /* File is not longer needed  ==> close it */
      fclose (FileURL);

      if (Gbl.Crs.Info.URL[0])
	{
	 Inf_ShowPage (Gbl.Crs.Info.URL);
         return true;
	}
     }

   return false;
  }

/*****************************************************************************/
/*** Build path inside a course for a given a info type to store URL file ****/
/*****************************************************************************/

static void Inf_BuildPathURL (long CrsCod,Inf_InfoType_t InfoType,
                              char PathFile[PATH_MAX + 1])
  {
   snprintf (PathFile,PATH_MAX + 1,
	     "%s/%ld/%s.url",
	     Cfg_PATH_CRS_PRIVATE,CrsCod,
	     Inf_FileNamesForInfoType[InfoType]);
  }

/*****************************************************************************/
/************* Check if exists and write URL into text buffer ****************/
/*****************************************************************************/
// This function is called only from web service

void Inf_WriteURLIntoTxtBuffer (char TxtBuffer[Cns_MAX_BYTES_WWW + 1])
  {
   char PathFile[PATH_MAX + 1];
   FILE *FileURL;

   /***** Initialize buffer *****/
   TxtBuffer[0] = '\0';

   /***** Build path to file containing URL *****/
   Inf_BuildPathURL (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,PathFile);

   /***** Check if file with URL exists *****/
   if ((FileURL = fopen (PathFile,"rb")))
     {
      if (fgets (TxtBuffer,Cns_MAX_BYTES_WWW,FileURL) == NULL)
	 TxtBuffer[0] = '\0';
      /* File is not longer needed  ==> close it */
      fclose (FileURL);
     }
  }

/*****************************************************************************/
/*************** Show link to a internal or external a page ******************/
/*****************************************************************************/

static void Inf_ShowPage (const char *URL)
  {
   extern const char *The_ClassFormOutBoxBold[The_NUM_THEMES];
   extern const char *Txt_View_in_a_new_window;
   extern const char *Txt_INFO_TITLE[Inf_NUM_INFO_TYPES];
   bool ICanEdit = (Gbl.Usrs.Me.Role.Logged == Rol_TCH ||
                    Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM);
   const char *Help[Inf_NUM_INFO_TYPES] =
     {
      Hlp_COURSE_Information_textual_information,	// Inf_INTRODUCTION
      Hlp_COURSE_Guide,				// Inf_TEACHING_GUIDE
      Hlp_COURSE_Syllabus,				// Inf_LECTURES
      Hlp_COURSE_Syllabus,				// Inf_PRACTICALS
      Hlp_COURSE_Bibliography,			// Inf_BIBLIOGRAPHY
      Hlp_COURSE_FAQ,				// Inf_FAQ
      Hlp_COURSE_Links,				// Inf_LINKS
      Hlp_ASSESSMENT_System,			// Inf_ASSESSMENT
     };

   /***** Start box *****/
   Box_StartBox (NULL,Txt_INFO_TITLE[Gbl.Crs.Info.Type],
                 ICanEdit ? Inf_PutIconToEditInfo :
                            NULL,
                 Help[Gbl.Crs.Info.Type],Box_NOT_CLOSABLE);

   /***** Link to view in a new window *****/
   fprintf (Gbl.F.Out,"<a href=\"%s\" target=\"_blank\" class=\"%s\">",
	    URL,The_ClassFormOutBoxBold[Gbl.Prefs.Theme]);
   Ico_PutIconTextLink ("expand-arrows-alt.svg",
		        Txt_View_in_a_new_window);
   fprintf (Gbl.F.Out,"</a>");

   /***** End box *****/
   Box_EndBox ();
  }

/*****************************************************************************/
/************************** Set course info source ***************************/
/*****************************************************************************/

void Inf_SetInfoSrc (void)
  {
   Inf_InfoSrc_t InfoSrcSelected = Inf_GetInfoSrcFromForm ();

   /***** Set info type *****/
   Gbl.Crs.Info.Type = Inf_AsignInfoType ();

   /***** Set info source into database *****/
   Inf_SetInfoSrcIntoDB (InfoSrcSelected);

   /***** Show the selected info *****/
   Inf_ShowInfo ();
  }

/*****************************************************************************/
/************** Select course info source and send course info ***************/
/*****************************************************************************/

void Inf_FormsToSelSendInfo (void)
  {
   extern const char *The_ClassFormInBox[The_NUM_THEMES];
   extern const char *Txt_Source_of_information;
   extern const char *Txt_INFO_SRC_FULL_TEXT[Inf_NUM_INFO_SOURCES];
   extern const char *Txt_INFO_SRC_HELP[Inf_NUM_INFO_SOURCES];
   Inf_InfoSrc_t InfoSrc;
   Inf_InfoSrc_t InfoSrcSelected;
   bool InfoAvailable[Inf_NUM_INFO_SOURCES];
   bool MustBeRead;
   const char *HelpEdit[Inf_NUM_INFO_TYPES] =
     {
      Hlp_COURSE_Information_edit,	// Inf_INTRODUCTION
      Hlp_COURSE_Guide_edit,		// Inf_TEACHING_GUIDE
      Hlp_COURSE_Syllabus_edit,		// Inf_LECTURES
      Hlp_COURSE_Syllabus_edit,		// Inf_PRACTICALS
      Hlp_COURSE_Bibliography_edit,	// Inf_BIBLIOGRAPHY
      Hlp_COURSE_FAQ_edit,		// Inf_FAQ
      Hlp_COURSE_Links_edit,		// Inf_LINKS
      Hlp_ASSESSMENT_System_edit,	// Inf_ASSESSMENT
     };

   /***** Set info type *****/
   Gbl.Crs.Info.Type = Inf_AsignInfoType ();

   /***** Get current info source from database *****/
   Inf_GetAndCheckInfoSrcFromDB (Gbl.Hierarchy.Crs.CrsCod,
                                 Gbl.Crs.Info.Type,
                                 &InfoSrcSelected,&MustBeRead);

   /***** Check if info available *****/
   for (InfoSrc = (Inf_InfoSrc_t) 0;
	InfoSrc < Inf_NUM_INFO_SOURCES;
	InfoSrc++)
      InfoAvailable[InfoSrc] = Inf_CheckIfInfoAvailable (InfoSrc);

   /***** Set info source to none
          when no info available for the current source *****/
   if (InfoSrcSelected != Inf_INFO_SRC_NONE &&
       !InfoAvailable[InfoSrcSelected])
     {
      InfoSrcSelected = Inf_INFO_SRC_NONE;
      Inf_SetInfoSrcIntoDB (Inf_INFO_SRC_NONE);
     }

   /***** Form to choice between alternatives *****/
   /* Start box and table */
   Box_StartBoxTable (NULL,Txt_Source_of_information,Inf_PutIconToViewInfo,
                      HelpEdit[Gbl.Crs.Info.Type],Box_NOT_CLOSABLE,4);

   /* Options */
   for (InfoSrc = (Inf_InfoSrc_t) 0;
	InfoSrc < Inf_NUM_INFO_SOURCES;
	InfoSrc++)
     {
      /* Select info source */
      fprintf (Gbl.F.Out,"<tr>"
	                 "<td class=\"DAT LEFT_TOP");
      if (InfoSrc == InfoSrcSelected)
         fprintf (Gbl.F.Out," LIGHT_BLUE");
      fprintf (Gbl.F.Out,"\">");
      Frm_StartForm (Inf_ActionsSelecInfoSrc[Gbl.Crs.Info.Type]);
      fprintf (Gbl.F.Out,"<input type=\"radio\""
	                 " id=\"InfoSrc%u\" name=\"InfoSrc\" value=\"%u\"",
	       (unsigned) InfoSrc,(unsigned) InfoSrc);
      if (InfoSrc == InfoSrcSelected)
         fprintf (Gbl.F.Out," checked=\"checked\"");
      else
	{
	 if (InfoSrc == Inf_INFO_SRC_NONE ||
	     InfoAvailable[InfoSrc])	// If no info available for this source
	    fprintf (Gbl.F.Out," onclick=\"document.getElementById('%s').submit();\"",
		     Gbl.Form.Id);
	 else
	    fprintf (Gbl.F.Out," disabled=\"disabled\"");
	}
      fprintf (Gbl.F.Out," />");
      Frm_EndForm ();
      fprintf (Gbl.F.Out,"</td>");

      /* Form for this info source */
      fprintf (Gbl.F.Out,"<td class=\"LEFT_TOP");
      if (InfoSrc == InfoSrcSelected)
         fprintf (Gbl.F.Out," LIGHT_BLUE");
      fprintf (Gbl.F.Out,"\">"
	                 "<label for=\"InfoSrc%u\" class=\"%s\">%s</label>",
	       (unsigned) InfoSrc,The_ClassFormInBox[Gbl.Prefs.Theme],
               Txt_INFO_SRC_FULL_TEXT[InfoSrc]);
      if (Txt_INFO_SRC_HELP[InfoSrc])
         fprintf (Gbl.F.Out,"<span class=\"DAT\"><br />(%s)</span>",
                  Txt_INFO_SRC_HELP[InfoSrc]);
      if (Inf_FormsForEditionTypes[InfoSrc])
         Inf_FormsForEditionTypes[InfoSrc] (InfoSrc);
      fprintf (Gbl.F.Out,"</td>"
	                 "</tr>");
     }

   /***** End table and box *****/
   Box_EndBoxTable ();
  }

/*****************************************************************************/
/* Check if there is info available for current info type and a given source */
/*****************************************************************************/

static bool Inf_CheckIfInfoAvailable (Inf_InfoSrc_t InfoSrc)
  {
   switch (InfoSrc)
     {
      case Inf_INFO_SRC_NONE:
	 return false;
      case Inf_INFO_SRC_EDITOR:
         switch (Gbl.Crs.Info.Type)
           {
            case Inf_LECTURES:
            case Inf_PRACTICALS:
               return Syl_CheckSyllabus (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type);
            default:
               return false;
           }
         return false;	// Not reached
      case Inf_INFO_SRC_PLAIN_TEXT:
         return Inf_CheckPlainTxt (Gbl.Hierarchy.Crs.CrsCod,
                                   Gbl.Crs.Info.Type);
      case Inf_INFO_SRC_RICH_TEXT:
         return Inf_CheckRichTxt (Gbl.Hierarchy.Crs.CrsCod,
                                  Gbl.Crs.Info.Type);
      case Inf_INFO_SRC_PAGE:
	 return Inf_CheckPage (Gbl.Hierarchy.Crs.CrsCod,
                               Gbl.Crs.Info.Type);
      case Inf_INFO_SRC_URL:
	 return Inf_CheckURL (Gbl.Hierarchy.Crs.CrsCod,
                              Gbl.Crs.Info.Type);
     }

   return false;	// Not reached
  }

/*****************************************************************************/
/****************** Form to enter in integrated editor ***********************/
/*****************************************************************************/

void Inf_FormToEnterIntegratedEditor (Inf_InfoSrc_t InfoSrc)
  {
   extern const char *Txt_Edit;

   Frm_StartForm (Inf_ActionsInfo[InfoSrc][Gbl.Crs.Info.Type]);
   Btn_PutConfirmButton (Txt_Edit);
   Frm_EndForm ();
  }

/*****************************************************************************/
/****************** Form to enter in plain text editor ***********************/
/*****************************************************************************/

void Inf_FormToEnterPlainTextEditor (Inf_InfoSrc_t InfoSrc)
  {
   extern const char *Txt_Edit_plain_text;

   Frm_StartForm (Inf_ActionsInfo[InfoSrc][Gbl.Crs.Info.Type]);
   Btn_PutConfirmButton (Txt_Edit_plain_text);
   Frm_EndForm ();
  }

/*****************************************************************************/
/******************* Form to enter in rich text editor ***********************/
/*****************************************************************************/

void Inf_FormToEnterRichTextEditor (Inf_InfoSrc_t InfoSrc)
  {
   extern const char *Txt_Edit_rich_text;

   Frm_StartForm (Inf_ActionsInfo[InfoSrc][Gbl.Crs.Info.Type]);
   Btn_PutConfirmButton (Txt_Edit_rich_text);
   Frm_EndForm ();
  }

/*****************************************************************************/
/******************* Form to upload a file with a page ***********************/
/*****************************************************************************/

void Inf_FormToSendPage (Inf_InfoSrc_t InfoSrc)
  {
   extern const char *The_ClassFormInBox[The_NUM_THEMES];
   extern const char *Txt_File;
   extern const char *Txt_Upload_file;

   /***** Start form *****/
   Frm_StartForm (Inf_ActionsInfo[InfoSrc][Gbl.Crs.Info.Type]);

   /***** File *****/
   fprintf (Gbl.F.Out,"<div class=\"CENTER_MIDDLE\">"
	              "<label class=\"%s\">"
                      "%s:&nbsp;"
                      "<input type=\"file\" name=\"%s\" />"
                      "</label>"
                      "</div>",
            The_ClassFormInBox[Gbl.Prefs.Theme],Txt_File,
            Fil_NAME_OF_PARAM_FILENAME_ORG);

   /***** Send button *****/
   Btn_PutCreateButton (Txt_Upload_file);

   /***** End form *****/
   Frm_EndForm ();
  }

/*****************************************************************************/
/********************* Form to send a link to a web page *********************/
/*****************************************************************************/

void Inf_FormToSendURL (Inf_InfoSrc_t InfoSrc)
  {
   extern const char *The_ClassFormInBox[The_NUM_THEMES];
   extern const char *Txt_URL;
   extern const char *Txt_Send_URL;
   char PathFile[PATH_MAX + 1];
   FILE *FileURL;

   /***** Build path to file containing URL *****/
   Inf_BuildPathURL (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,PathFile);

   /***** Start form *****/
   Frm_StartForm (Inf_ActionsInfo[InfoSrc][Gbl.Crs.Info.Type]);

   /***** Link *****/
   fprintf (Gbl.F.Out,"<div class=\"CENTER_MIDDLE\">"
                      "<label class=\"%s\">"
                      "%s:&nbsp;"
                      "<input type=\"url\" name=\"InfoSrcURL\""
                      " size=\"50\" maxlength=\"256\" value=\"",
            The_ClassFormInBox[Gbl.Prefs.Theme],Txt_URL);
   if ((FileURL = fopen (PathFile,"rb")) == NULL)
      fprintf (Gbl.F.Out,"http://");
   else
     {
      if (fgets (Gbl.Crs.Info.URL,Cns_MAX_BYTES_WWW,FileURL) == NULL)
         Gbl.Crs.Info.URL[0] = '\0';
      /* File is not needed now. Close it */
      fclose (FileURL);
      fprintf (Gbl.F.Out,"%s",Gbl.Crs.Info.URL);
     }
   fprintf (Gbl.F.Out,"\" />"
                      "</label>"
	              "</div>");

   /***** Send button *****/
   Btn_PutCreateButton (Txt_Send_URL);

   /***** End form *****/
   Frm_EndForm ();
  }

/*****************************************************************************/
/******** Returns bibliography, assessment, etc. from Gbl.Action.Act *********/
/*****************************************************************************/

static Inf_InfoType_t Inf_AsignInfoType (void)
  {
   switch (Gbl.Action.Act)
     {
      case ActSeeCrsInf:
      case ActEdiCrsInf:
      case ActChgFrcReaCrsInf:
      case ActChgHavReaCrsInf:
      case ActSelInfSrcCrsInf:
      case ActEditorCrsInf:
      case ActRcvURLCrsInf:
      case ActRcvPagCrsInf:
      case ActPlaTxtEdiCrsInf:
      case ActRchTxtEdiCrsInf:
      case ActRcvPlaTxtCrsInf:
      case ActRcvRchTxtCrsInf:
         return Inf_INTRODUCTION;
      case ActSeeTchGui:
      case ActEdiTchGui:
      case ActChgFrcReaTchGui:
      case ActChgHavReaTchGui:
      case ActSelInfSrcTchGui:
      case ActEditorTchGui:
      case ActRcvURLTchGui:
      case ActRcvPagTchGui:
      case ActPlaTxtEdiTchGui:
      case ActRchTxtEdiTchGui:
      case ActRcvPlaTxtTchGui:
      case ActRcvRchTxtTchGui:
         return Inf_TEACHING_GUIDE;
      case ActSeeSyl:
	 Syl_GetParamWhichSyllabus ();
	 return (Gbl.Syllabus.WhichSyllabus == Syl_LECTURES ? Inf_LECTURES :
	                                                                 Inf_PRACTICALS);
      case ActSeeSylLec:
      case ActEdiSylLec:
      case ActChgFrcReaSylLec:
      case ActChgHavReaSylLec:
      case ActSelInfSrcSylLec:
      case ActEditorSylLec:
      case ActRcvURLSylLec:
      case ActRcvPagSylLec:
      case ActPlaTxtEdiSylLec:
      case ActRchTxtEdiSylLec:
      case ActRcvPlaTxtSylLec:
      case ActRcvRchTxtSylLec:
	 Gbl.Syllabus.WhichSyllabus = Syl_LECTURES;
         return Inf_LECTURES;
      case ActSeeSylPra:
      case ActEdiSylPra:
      case ActChgFrcReaSylPra:
      case ActChgHavReaSylPra:
      case ActSelInfSrcSylPra:
      case ActEditorSylPra:
      case ActRcvURLSylPra:
      case ActRcvPagSylPra:
      case ActPlaTxtEdiSylPra:
      case ActRchTxtEdiSylPra:
      case ActRcvPlaTxtSylPra:
      case ActRcvRchTxtSylPra:
	 Gbl.Syllabus.WhichSyllabus = Syl_PRACTICALS;
         return Inf_PRACTICALS;
      case ActSeeBib:
      case ActEdiBib:
      case ActChgFrcReaBib:
      case ActChgHavReaBib:
      case ActSelInfSrcBib:
      case ActEditorBib:
      case ActRcvURLBib:
      case ActRcvPagBib:
      case ActPlaTxtEdiBib:
      case ActRchTxtEdiBib:
      case ActRcvPlaTxtBib:
      case ActRcvRchTxtBib:
         return Inf_BIBLIOGRAPHY;
      case ActSeeFAQ:
      case ActEdiFAQ:
      case ActChgFrcReaFAQ:
      case ActChgHavReaFAQ:
      case ActSelInfSrcFAQ:
      case ActEditorFAQ:
      case ActRcvURLFAQ:
      case ActRcvPagFAQ:
      case ActPlaTxtEdiFAQ:
      case ActRchTxtEdiFAQ:
      case ActRcvPlaTxtFAQ:
      case ActRcvRchTxtFAQ:
         return Inf_FAQ;
      case ActSeeCrsLnk:
      case ActEdiCrsLnk:
      case ActChgFrcReaCrsLnk:
      case ActChgHavReaCrsLnk:
      case ActSelInfSrcCrsLnk:
      case ActEditorCrsLnk:
      case ActRcvURLCrsLnk:
      case ActRcvPagCrsLnk:
      case ActPlaTxtEdiCrsLnk:
      case ActRchTxtEdiCrsLnk:
      case ActRcvPlaTxtCrsLnk:
      case ActRcvRchTxtCrsLnk:
         return Inf_LINKS;
      case ActSeeAss:
      case ActEdiAss:
      case ActChgFrcReaAss:
      case ActChgHavReaAss:
      case ActSelInfSrcAss:
      case ActEditorAss:
      case ActRcvURLAss:
      case ActRcvPagAss:
      case ActPlaTxtEdiAss:
      case ActRchTxtEdiAss:
      case ActRcvPlaTxtAss:
      case ActRcvRchTxtAss:
         return Inf_ASSESSMENT;
     }
   return (Inf_InfoType_t) 0; // If action is not one of the former ones, the returned value is not important
  }

/*****************************************************************************/
/********** Get info source for bibliography, FAQ, etc. from form ************/
/*****************************************************************************/

Inf_InfoSrc_t Inf_GetInfoSrcFromForm (void)
  {
   /***** Get info source for a specific type of course information
          (introduction, teaching guide, bibliography, FAQ, links or evaluation) *****/
   return (Inf_InfoSrc_t)
	  Par_GetParToUnsignedLong ("InfoSrc",
                                    0,
                                    Inf_NUM_INFO_SOURCES - 1,
                                    (unsigned long) Inf_INFO_SRC_NONE);
  }

/*****************************************************************************/
/********* Set info source for a type of course info from database ***********/
/*****************************************************************************/

void Inf_SetInfoSrcIntoDB (Inf_InfoSrc_t InfoSrc)
  {
   /***** Get if info source is already stored in database *****/
   if (DB_QueryCOUNT ("can not get if info source is already stored in database",
		      "SELECT COUNT(*) FROM crs_info_src"
		      " WHERE CrsCod=%ld AND InfoType='%s'",
		      Gbl.Hierarchy.Crs.CrsCod,
		      Inf_NamesInDBForInfoType[Gbl.Crs.Info.Type]))
      // Info is already stored in database, so update it
     {	// Update info source
      if (InfoSrc == Inf_INFO_SRC_NONE)
         DB_QueryUPDATE ("can not update info source",
			 "UPDATE crs_info_src SET InfoSrc='%s',MustBeRead='N'"
			 " WHERE CrsCod=%ld AND InfoType='%s'",
                         Inf_NamesInDBForInfoSrc[Inf_INFO_SRC_NONE],
                         Gbl.Hierarchy.Crs.CrsCod,
                         Inf_NamesInDBForInfoType[Gbl.Crs.Info.Type]);
      else	// MustBeRead remains unchanged
         DB_QueryUPDATE ("can not update info source",
			 "UPDATE crs_info_src SET InfoSrc='%s'"
		         " WHERE CrsCod=%ld AND InfoType='%s'",
		         Inf_NamesInDBForInfoSrc[InfoSrc],
		         Gbl.Hierarchy.Crs.CrsCod,
		         Inf_NamesInDBForInfoType[Gbl.Crs.Info.Type]);
     }
   else		// Info is not stored in database, so insert it
      DB_QueryINSERT ("can not insert info source",
		      "INSERT INTO crs_info_src"
		      " (CrsCod,InfoType,InfoSrc,MustBeRead)"
		      " VALUES"
		      " (%ld,'%s','%s','N')",
		      Gbl.Hierarchy.Crs.CrsCod,
		      Inf_NamesInDBForInfoType[Gbl.Crs.Info.Type],
		      Inf_NamesInDBForInfoSrc[InfoSrc]);
  }


/*****************************************************************************/
/***** Get and check info source for a type of course info from database *****/
/*****************************************************************************/

Inf_InfoSrc_t Inf_GetInfoSrcFromDB (long CrsCod,Inf_InfoType_t InfoType)
  {
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   Inf_InfoSrc_t InfoSrc;

   /***** Get info source for a specific type of info from database *****/
   if (DB_QuerySELECT (&mysql_res,"can not get info source",
		       "SELECT InfoSrc FROM crs_info_src"
		       " WHERE CrsCod=%ld AND InfoType='%s'",
		       CrsCod,Inf_NamesInDBForInfoType[InfoType]))
     {
      /* Get row */
      row = mysql_fetch_row (mysql_res);

      /* Get info source (row[0]) */
      InfoSrc = Inf_ConvertFromStrDBToInfoSrc (row[0]);
     }
   else
      InfoSrc = Inf_INFO_SRC_NONE;

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);

   return InfoSrc;
  }

/*****************************************************************************/
/***** Get and check info source for a type of course info from database *****/
/*****************************************************************************/

void Inf_GetAndCheckInfoSrcFromDB (long CrsCod,
                                   Inf_InfoType_t InfoType,
                                   Inf_InfoSrc_t *InfoSrc,bool *MustBeRead)
  {
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   unsigned long NumRows;

   /***** Set default values *****/
   *InfoSrc = Inf_INFO_SRC_NONE;
   *MustBeRead = false;

   /***** Get info source for a specific type of info from database *****/
   NumRows = DB_QuerySELECT (&mysql_res,"can not get info source",
			     "SELECT InfoSrc,MustBeRead FROM crs_info_src"
			     " WHERE CrsCod=%ld AND InfoType='%s'",
			     CrsCod,Inf_NamesInDBForInfoType[InfoType]);

   /***** The result of the query must have one row or none *****/
   if (NumRows == 1)
     {
      /* Get row */
      row = mysql_fetch_row (mysql_res);

      /* Get info source (row[0]) */
      *InfoSrc = Inf_ConvertFromStrDBToInfoSrc (row[0]);

      /* Get if students must read info (row[1]) */
      *MustBeRead = (row[1][0] == 'Y');
     }

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);

   if (NumRows > 1)
      Lay_ShowErrorAndExit ("Error when getting info source.");

   /***** If info is empty, return Inf_INFO_SRC_NONE *****/
   switch (*InfoSrc)
     {
      case Inf_INFO_SRC_NONE:
         *MustBeRead = false;
         break;
      case Inf_INFO_SRC_EDITOR:
         switch (InfoType)
           {
            case Inf_LECTURES:
            case Inf_PRACTICALS:
               if (!Syl_CheckSyllabus (CrsCod,InfoType))
                 {
                  *InfoSrc = Inf_INFO_SRC_NONE;
                  *MustBeRead = false;
                 }
               break;
            case Inf_INTRODUCTION:
            case Inf_TEACHING_GUIDE:
            case Inf_BIBLIOGRAPHY:
            case Inf_FAQ:
            case Inf_LINKS:
            case Inf_ASSESSMENT:
               *InfoSrc = Inf_INFO_SRC_NONE;
               *MustBeRead = false;
	       break;	// Internal editor is not yet available
           }
         break;
      case Inf_INFO_SRC_PLAIN_TEXT:
	 if (!Inf_CheckPlainTxt (CrsCod,InfoType))
           {
            *InfoSrc = Inf_INFO_SRC_NONE;
            *MustBeRead = false;
           }
         break;
      case Inf_INFO_SRC_RICH_TEXT:
	 if (!Inf_CheckRichTxt (CrsCod,InfoType))
           {
            *InfoSrc = Inf_INFO_SRC_NONE;
            *MustBeRead = false;
           }
         break;
      case Inf_INFO_SRC_PAGE:
	 if (!Inf_CheckPage (CrsCod,InfoType))
	   {
	    *InfoSrc = Inf_INFO_SRC_NONE;
	    *MustBeRead = false;
	   }
         break;
      case Inf_INFO_SRC_URL:
	 if (!Inf_CheckURL (CrsCod,InfoType))
	   {
	    *InfoSrc = Inf_INFO_SRC_NONE;
	    *MustBeRead = false;
	   }
         break;
     }
  }

/*****************************************************************************/
/*** Convert a string with info type in database to a Inf_InfoType_t value ***/
/*****************************************************************************/

Inf_InfoType_t Inf_ConvertFromStrDBToInfoType (const char *StrInfoTypeDB)
  {
   Inf_InfoType_t InfoType;

   for (InfoType = (Inf_InfoType_t) 0;
	InfoType < Inf_NUM_INFO_TYPES;
	InfoType++)
      if (!strcmp (StrInfoTypeDB,Inf_NamesInDBForInfoType[InfoType]))
         return InfoType;

   return (Inf_InfoType_t) 0;
  }

/*****************************************************************************/
/** Convert a string with info source in database to a Inf_InfoSrc_t value ***/
/*****************************************************************************/

Inf_InfoSrc_t Inf_ConvertFromStrDBToInfoSrc (const char *StrInfoSrcDB)
  {
   Inf_InfoSrc_t InfoSrc;

   for (InfoSrc = (Inf_InfoSrc_t) 0;
	InfoSrc < Inf_NUM_INFO_SOURCES;
	InfoSrc++)
      if (!strcmp (StrInfoSrcDB,Inf_NamesInDBForInfoSrc[InfoSrc]))
         return InfoSrc;

   return Inf_INFO_SRC_NONE;
  }

/*****************************************************************************/
/********** Set info text for a type of course info from database ************/
/*****************************************************************************/

static void Inf_SetInfoTxtIntoDB (const char *InfoTxtHTML,const char *InfoTxtMD)
  {
   /***** Insert or replace info source for a specific type of course information *****/
   DB_QueryREPLACE ("can not update info text",
		    "REPLACE INTO crs_info_txt"
		    " (CrsCod,InfoType,InfoTxtHTML,InfoTxtMD)"
		    " VALUES"
		    " (%ld,'%s','%s','%s')",
		    Gbl.Hierarchy.Crs.CrsCod,
		    Inf_NamesInDBForInfoType[Gbl.Crs.Info.Type],
		    InfoTxtHTML,InfoTxtMD);
  }

/*****************************************************************************/
/********** Get info text for a type of course info from database ************/
/*****************************************************************************/

static void Inf_GetInfoTxtFromDB (long CrsCod,Inf_InfoType_t InfoType,
                                  char InfoTxtHTML[Cns_MAX_BYTES_LONG_TEXT + 1],
                                  char InfoTxtMD[Cns_MAX_BYTES_LONG_TEXT + 1])
  {
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   unsigned long NumRows;

   /***** Get info source for a specific type of course information
          (bibliography, FAQ, links or evaluation) from database *****/
   NumRows = DB_QuerySELECT (&mysql_res,"can not get info text",
			     "SELECT InfoTxtHTML,InfoTxtMD FROM crs_info_txt"
			     " WHERE CrsCod=%ld AND InfoType='%s'",
			     CrsCod,Inf_NamesInDBForInfoType[InfoType]);

   /***** The result of the query must have one row or none *****/
   if (NumRows == 1)
     {
      /* Get info text */
      row = mysql_fetch_row (mysql_res);

      /* Get text in HTML format (not rigorous) */
      if (InfoTxtHTML)
	 Str_Copy (InfoTxtHTML,row[0],
	           Cns_MAX_BYTES_LONG_TEXT);

      /* Get text in Markdown format */
      if (InfoTxtMD)
	 Str_Copy (InfoTxtMD,row[1],
	           Cns_MAX_BYTES_LONG_TEXT);
     }
   else
     {
      if (InfoTxtHTML)
         InfoTxtHTML[0] = '\0';
      if (InfoTxtMD)
         InfoTxtMD[0]   = '\0';
     }

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);

   if (NumRows > 1)
      Lay_ShowErrorAndExit ("Error when getting info text.");
  }

/*****************************************************************************/
/********************* Check information about the course ********************/
/*****************************************************************************/
// Return true if info available

static bool Inf_CheckPlainTxt (long CrsCod,Inf_InfoType_t InfoType)
  {
   char TxtHTML[Cns_MAX_BYTES_LONG_TEXT + 1];

   /***** Get info text from database *****/
   Inf_GetInfoTxtFromDB (CrsCod,InfoType,TxtHTML,NULL);

   return (TxtHTML[0] != '\0');
  }

/*****************************************************************************/
/********************* Show information about the course *********************/
/*****************************************************************************/
// Return true if info available

static bool Inf_CheckAndShowPlainTxt (void)
  {
   extern const char *Txt_INFO_TITLE[Inf_NUM_INFO_TYPES];
   char TxtHTML[Cns_MAX_BYTES_LONG_TEXT + 1];
   bool ICanEdit = (Gbl.Usrs.Me.Role.Logged == Rol_TCH ||
                    Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM);
   const char *Help[Inf_NUM_INFO_TYPES] =
     {
      Hlp_COURSE_Information_textual_information,	// Inf_INTRODUCTION
      Hlp_COURSE_Guide,					// Inf_TEACHING_GUIDE
      Hlp_COURSE_Syllabus,				// Inf_LECTURES
      Hlp_COURSE_Syllabus,				// Inf_PRACTICALS
      Hlp_COURSE_Bibliography,				// Inf_BIBLIOGRAPHY
      Hlp_COURSE_FAQ,					// Inf_FAQ
      Hlp_COURSE_Links,					// Inf_LINKS
      Hlp_ASSESSMENT_System,				// Inf_ASSESSMENT
     };

   /***** Get info text from database *****/
   Inf_GetInfoTxtFromDB (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,
                         TxtHTML,NULL);

   if (TxtHTML[0])
     {
      /***** Start box *****/
      Box_StartBox (NULL,Txt_INFO_TITLE[Gbl.Crs.Info.Type],
                    ICanEdit ? Inf_PutIconToEditInfo :
                               NULL,
                    Help[Gbl.Crs.Info.Type],Box_NOT_CLOSABLE);

      if (Gbl.Crs.Info.Type == Inf_INTRODUCTION ||
          Gbl.Crs.Info.Type == Inf_TEACHING_GUIDE)
         Lay_WriteHeaderClassPhoto (false,false,Gbl.Hierarchy.Ins.InsCod,Gbl.Hierarchy.Deg.DegCod,Gbl.Hierarchy.Crs.CrsCod);

      fprintf (Gbl.F.Out,"<div class=\"DAT LEFT_MIDDLE\">");

      /***** Convert to respectful HTML and insert links *****/
      Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
                        TxtHTML,Cns_MAX_BYTES_LONG_TEXT,false);	// Convert from HTML to recpectful HTML
      Str_InsertLinks (TxtHTML,Cns_MAX_BYTES_LONG_TEXT,60);	// Insert links

      /***** Write text *****/
      fprintf (Gbl.F.Out,"%s",TxtHTML);

      /***** End box *****/
      fprintf (Gbl.F.Out,"</div>");
      Box_EndBox ();

      return true;
     }

   return false;
  }

/*****************************************************************************/
/********************* Show information about the course *********************/
/*****************************************************************************/
// Return true if info available

static bool Inf_CheckRichTxt (long CrsCod,Inf_InfoType_t InfoType)
  {
   char TxtHTML[Cns_MAX_BYTES_LONG_TEXT + 1];
   char TxtMD[Cns_MAX_BYTES_LONG_TEXT + 1];

   /***** Get info text from database *****/
   Inf_GetInfoTxtFromDB (CrsCod,InfoType,
                         TxtHTML,TxtMD);

   return (TxtMD[0] != '\0');
  }

/*****************************************************************************/
/********************* Show information about the course *********************/
/*****************************************************************************/
// Return true if info available

static bool Inf_CheckAndShowRichTxt (void)
  {
   extern const char *Txt_INFO_TITLE[Inf_NUM_INFO_TYPES];
   char TxtHTML[Cns_MAX_BYTES_LONG_TEXT + 1];
   char TxtMD[Cns_MAX_BYTES_LONG_TEXT + 1];
   char PathFileMD[PATH_MAX + 1];
   char PathFileHTML[PATH_MAX + 1];
   FILE *FileMD;		// Temporary Markdown file
   FILE *FileHTML;		// Temporary HTML file
   char MathJaxURL[PATH_MAX + 1];
   char Command[512 + PATH_MAX * 3]; // Command to call the program of preprocessing of photos
   int ReturnCode;
   bool ICanEdit = (Gbl.Usrs.Me.Role.Logged == Rol_TCH ||
                    Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM);
   const char *Help[Inf_NUM_INFO_TYPES] =
     {
      Hlp_COURSE_Information_textual_information,	// Inf_INTRODUCTION
      Hlp_COURSE_Guide,				// Inf_TEACHING_GUIDE
      Hlp_COURSE_Syllabus,				// Inf_LECTURES
      Hlp_COURSE_Syllabus,				// Inf_PRACTICALS
      Hlp_COURSE_Bibliography,			// Inf_BIBLIOGRAPHY
      Hlp_COURSE_FAQ,				// Inf_FAQ
      Hlp_COURSE_Links,				// Inf_LINKS
      Hlp_ASSESSMENT_System,			// Inf_ASSESSMENT
     };

   /***** Get info text from database *****/
   Inf_GetInfoTxtFromDB (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,
                         TxtHTML,TxtMD);

   if (TxtMD[0])
     {
      /***** Start box *****/
      Box_StartBox (NULL,Txt_INFO_TITLE[Gbl.Crs.Info.Type],
                    ICanEdit ? Inf_PutIconToEditInfo :
                               NULL,
                    Help[Gbl.Crs.Info.Type],Box_NOT_CLOSABLE);

      if (Gbl.Crs.Info.Type == Inf_INTRODUCTION ||
          Gbl.Crs.Info.Type == Inf_TEACHING_GUIDE)
         Lay_WriteHeaderClassPhoto (false,false,Gbl.Hierarchy.Ins.InsCod,Gbl.Hierarchy.Deg.DegCod,Gbl.Hierarchy.Crs.CrsCod);

      fprintf (Gbl.F.Out,"<div id=\"crs_info\" class=\"LEFT_MIDDLE\">");

      /***** Store text into a temporary .md file in HTML output directory *****/
      // TODO: change to another directory?
      /* Create a unique name for the .md file */
      snprintf (PathFileMD,sizeof (PathFileMD),
	        "%s/%s.md",
	        Cfg_PATH_OUT_PRIVATE,Gbl.UniqueNameEncrypted);
      snprintf (PathFileHTML,sizeof (PathFileHTML),
	        "%s/%s.md.html",	// Do not use only .html because that is the output temporary file
	        Cfg_PATH_OUT_PRIVATE,Gbl.UniqueNameEncrypted);

      /* Open Markdown file for writing */
      if ((FileMD = fopen (PathFileMD,"wb")) == NULL)
	 Lay_ShowErrorAndExit ("Can not create temporary Markdown file.");

      /* Write text into Markdown file */
      fprintf (FileMD,"%s",TxtMD);

      /* Close Markdown file */
      fclose (FileMD);

      /***** Convert from Markdown to HTML *****/
#ifdef Cfg_MATHJAX_LOCAL
      // Use the local copy of MathJax
      snprintf (MathJaxURL,sizeof (MathJaxURL),
	        "=%s/MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML",
	        Cfg_URL_SWAD_PUBLIC);
#else
      // Use the MathJax Content Delivery Network (CDN)
      MathJaxURL[0] = '\0';
#endif
      // --ascii uses only ascii characters in output
      //         (uses numerical entities instead of UTF-8)
      //         is mandatory in order to convert (with iconv) the UTF-8 output of pandoc to WINDOWS-1252
      snprintf (Command,sizeof (Command),
	        "iconv -f WINDOWS-1252 -t UTF-8 %s"
	        " | "
	        "pandoc --ascii --mathjax%s -f markdown -t html5"
	        " | "
	        "iconv -f UTF-8 -t WINDOWS-1252 -o %s",
	        PathFileMD,
	        MathJaxURL,
	        PathFileHTML);
      ReturnCode = system (Command);
      if (ReturnCode == -1)
	 Lay_ShowErrorAndExit ("Error when running command to convert from Markdown to HTML.");

      /***** Remove Markdown file *****/
      unlink (PathFileMD);

      /***** Copy HTML file just created to HTML output *****/
      /* Open temporary HTML file for reading */
      if ((FileHTML = fopen (PathFileHTML,"rb")) == NULL)
	 Lay_ShowErrorAndExit ("Can not open temporary HTML file.");

      /* Copy from temporary HTML file to output file */
      Fil_FastCopyOfOpenFiles (FileHTML,Gbl.F.Out);

      /* Close and remove temporary HTML file */
      fclose (FileHTML);
      unlink (PathFileHTML);

      /***** End box *****/
      fprintf (Gbl.F.Out,"</div>");
      Box_EndBox ();

      return true;
     }

   return false;
  }

/*****************************************************************************/
/************* Check if exists and write page into HTML buffer ***************/
/*****************************************************************************/
// This function is called only from web service

int Inf_WritePlainTextIntoHTMLBuffer (char **HTMLBuffer)
  {
   extern const char *Txt_INFO_TITLE[Inf_NUM_INFO_TYPES];
   char TxtHTML[Cns_MAX_BYTES_LONG_TEXT + 1];
   char FileNameHTMLTmp[PATH_MAX + 1];
   FILE *FileHTMLTmp;
   size_t Length;

   /***** Initialize buffer *****/
   *HTMLBuffer = NULL;

   /***** Get info text from database *****/
   Inf_GetInfoTxtFromDB (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,
                         TxtHTML,NULL);

   if (TxtHTML[0])
     {
      /***** Create a unique name for the file *****/
      snprintf (FileNameHTMLTmp,sizeof (FileNameHTMLTmp),
	        "%s/%s_info.html",
	        Cfg_PATH_OUT_PRIVATE,Gbl.UniqueNameEncrypted);

      /***** Create a new temporary file for writing and reading *****/
      if ((FileHTMLTmp = fopen (FileNameHTMLTmp,"w+b")) == NULL)
         return soap_receiver_fault (Gbl.soap,
                                     "Plain text can not be copied into buffer",
                                     "Can not create temporary file");

      /***** Write start of HTML code *****/
      Lay_StartHTMLFile (FileHTMLTmp,Txt_INFO_TITLE[Gbl.Crs.Info.Type]);
      fprintf (FileHTMLTmp,"<body>\n"
                           "<div class=\"DAT LEFT_MIDDLE\">\n");

      /***** Write plain text into text buffer *****/
      /* Convert to respectful HTML and insert links */
      Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
                        TxtHTML,Cns_MAX_BYTES_LONG_TEXT,false);		// Convert from HTML to recpectful HTML
      Str_InsertLinks (TxtHTML,Cns_MAX_BYTES_LONG_TEXT,60);	// Insert links

      /* Write text */
      fprintf (FileHTMLTmp,"%s",TxtHTML);

      /***** Write end of page into file *****/
      fprintf (FileHTMLTmp,"</div>\n"
			   "</html>\n"
			   "</body>\n");

      /***** Compute length of file *****/
      Length = (size_t) ftell (FileHTMLTmp);

      /***** Allocate memory for buffer *****/
      if ((*HTMLBuffer = (char *) malloc (Length + 1)) == NULL)
	{
	 fclose (FileHTMLTmp);
	 unlink (FileNameHTMLTmp);
         return soap_receiver_fault (Gbl.soap,
                                     "Plain text can not be copied into buffer",
                                     "Not enough memory for buffer");
	}

      /***** Copy file content into buffer *****/
      fseek (FileHTMLTmp,0L,SEEK_SET);
      if (fread ((void *) *HTMLBuffer,sizeof (char),Length,FileHTMLTmp) != Length)
	{
	 fclose (FileHTMLTmp);
	 unlink (FileNameHTMLTmp);
         return soap_receiver_fault (Gbl.soap,
                                     "Plain text can not be copied into buffer",
                                     "Error reading file into buffer");
	}
      (*HTMLBuffer)[Length] = '\0';

      /***** Close and remove temporary file *****/
      fclose (FileHTMLTmp);
      unlink (FileNameHTMLTmp);
     }

   return SOAP_OK;
  }

/*****************************************************************************/
/************** Edit plain text information about the course *****************/
/*****************************************************************************/

void Inf_EditPlainTxtInfo (void)
  {
   extern const char *Txt_INFO_TITLE[Inf_NUM_INFO_TYPES];
   extern const char *Txt_Save_changes;
   char TxtHTML[Cns_MAX_BYTES_LONG_TEXT + 1];
   const char *HelpEdit[Inf_NUM_INFO_TYPES] =
     {
      Hlp_COURSE_Information_edit,	// Inf_INTRODUCTION
      Hlp_COURSE_Guide_edit,		// Inf_TEACHING_GUIDE
      Hlp_COURSE_Syllabus_edit,		// Inf_LECTURES
      Hlp_COURSE_Syllabus_edit,		// Inf_PRACTICALS
      Hlp_COURSE_Bibliography_edit,	// Inf_BIBLIOGRAPHY
      Hlp_COURSE_FAQ_edit,		// Inf_FAQ
      Hlp_COURSE_Links_edit,		// Inf_LINKS
      Hlp_ASSESSMENT_System_edit,	// Inf_ASSESSMENT
     };

   /***** Set info type *****/
   Gbl.Crs.Info.Type = Inf_AsignInfoType ();

   /***** Start form and box *****/
   Frm_StartForm (Inf_ActionsRcvPlaTxtInfo[Gbl.Crs.Info.Type]);
   Box_StartBox (NULL,Txt_INFO_TITLE[Gbl.Crs.Info.Type],NULL,
                 HelpEdit[Gbl.Crs.Info.Type],Box_NOT_CLOSABLE);

   if (Gbl.Crs.Info.Type == Inf_INTRODUCTION ||
       Gbl.Crs.Info.Type == Inf_TEACHING_GUIDE)
      Lay_WriteHeaderClassPhoto (false,false,Gbl.Hierarchy.Ins.InsCod,Gbl.Hierarchy.Deg.DegCod,Gbl.Hierarchy.Crs.CrsCod);

   /***** Get info text from database *****/
   Inf_GetInfoTxtFromDB (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,
                         TxtHTML,NULL);

   /***** Edition area *****/
   fprintf (Gbl.F.Out,"<div class=\"CENTER_MIDDLE\">");
   Lay_HelpPlainEditor ();
   fprintf (Gbl.F.Out,"<textarea name=\"Txt\" cols=\"80\" rows=\"20\">"
	              "%s"
	              "</textarea>"
	              "</div>",
            TxtHTML);

   /***** Send button and end box *****/
   Box_EndBoxWithButton (Btn_CONFIRM_BUTTON,Txt_Save_changes);
   Frm_EndForm ();
  }

/*****************************************************************************/
/*************** Edit rich text information about the course *****************/
/*****************************************************************************/

void Inf_EditRichTxtInfo (void)
  {
   extern const char *Txt_INFO_TITLE[Inf_NUM_INFO_TYPES];
   extern const char *Txt_Save_changes;
   char TxtHTML[Cns_MAX_BYTES_LONG_TEXT + 1];
   const char *HelpEdit[Inf_NUM_INFO_TYPES] =
     {
      Hlp_COURSE_Information_edit,	// Inf_INTRODUCTION
      Hlp_COURSE_Guide_edit,		// Inf_TEACHING_GUIDE
      Hlp_COURSE_Syllabus_edit,		// Inf_LECTURES
      Hlp_COURSE_Syllabus_edit,		// Inf_PRACTICALS
      Hlp_COURSE_Bibliography_edit,	// Inf_BIBLIOGRAPHY
      Hlp_COURSE_FAQ_edit,		// Inf_FAQ
      Hlp_COURSE_Links_edit,		// Inf_LINKS
      Hlp_ASSESSMENT_System_edit,	// Inf_ASSESSMENT
     };

   /***** Set info type *****/
   Gbl.Crs.Info.Type = Inf_AsignInfoType ();

   /***** Start form and box *****/
   Frm_StartForm (Inf_ActionsRcvRchTxtInfo[Gbl.Crs.Info.Type]);
   Box_StartBox (NULL,Txt_INFO_TITLE[Gbl.Crs.Info.Type],NULL,
                 HelpEdit[Gbl.Crs.Info.Type],Box_NOT_CLOSABLE);

   if (Gbl.Crs.Info.Type == Inf_INTRODUCTION ||
       Gbl.Crs.Info.Type == Inf_TEACHING_GUIDE)
      Lay_WriteHeaderClassPhoto (false,false,Gbl.Hierarchy.Ins.InsCod,Gbl.Hierarchy.Deg.DegCod,Gbl.Hierarchy.Crs.CrsCod);

   /***** Get info text from database *****/
   Inf_GetInfoTxtFromDB (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,
                         TxtHTML,NULL);

   /***** Edition area *****/
   fprintf (Gbl.F.Out,"<div class=\"CENTER_MIDDLE\">");
   Lay_HelpRichEditor ();
   fprintf (Gbl.F.Out,"<textarea name=\"Txt\" cols=\"80\" rows=\"20\">"
	              "%s"
	              "</textarea>"
	              "</div>",
            TxtHTML);

   /***** Send button and end box *****/
   Box_EndBoxWithButton (Btn_CONFIRM_BUTTON,Txt_Save_changes);
   Frm_EndForm ();
  }

/*****************************************************************************/
/*************** Receive and change plain text of course info ****************/
/*****************************************************************************/

void Inf_RecAndChangePlainTxtInfo (void)
  {
   char Txt_HTMLFormat    [Cns_MAX_BYTES_LONG_TEXT + 1];
   char Txt_MarkdownFormat[Cns_MAX_BYTES_LONG_TEXT + 1];

   /***** Set info type *****/
   Gbl.Crs.Info.Type = Inf_AsignInfoType ();

   /***** Get text with course information from form *****/
   Par_GetParameter (Par_PARAM_SINGLE,"Txt",Txt_HTMLFormat,
                     Cns_MAX_BYTES_LONG_TEXT,NULL);
   Str_Copy (Txt_MarkdownFormat,Txt_HTMLFormat,
             Cns_MAX_BYTES_LONG_TEXT);
   Str_ChangeFormat (Str_FROM_FORM,Str_TO_HTML,
                     Txt_HTMLFormat,Cns_MAX_BYTES_LONG_TEXT,true);	// Store in HTML format (not rigorous)
   Str_ChangeFormat (Str_FROM_FORM,Str_TO_MARKDOWN,
                     Txt_MarkdownFormat,Cns_MAX_BYTES_LONG_TEXT,true);	// Store a copy in Markdown format

   /***** Update text of course info in database *****/
   Inf_SetInfoTxtIntoDB (Txt_HTMLFormat,Txt_MarkdownFormat);

   /***** Change info source to "plain text" in database *****/
   Inf_SetInfoSrcIntoDB (Txt_HTMLFormat[0] ? Inf_INFO_SRC_PLAIN_TEXT :
	                                     Inf_INFO_SRC_NONE);
   if (Txt_HTMLFormat[0])
      /***** Show the updated info *****/
      Inf_ShowInfo ();
   else
      /***** Show again the form to select and send course info *****/
      Inf_FormsToSelSendInfo ();
  }

/*****************************************************************************/
/*************** Receive and change rich text of course info *****************/
/*****************************************************************************/

void Inf_RecAndChangeRichTxtInfo (void)
  {
   char Txt_HTMLFormat    [Cns_MAX_BYTES_LONG_TEXT + 1];
   char Txt_MarkdownFormat[Cns_MAX_BYTES_LONG_TEXT + 1];

   /***** Set info type *****/
   Gbl.Crs.Info.Type  = Inf_AsignInfoType ();

   /***** Get text with course information from form *****/
   Par_GetParameter (Par_PARAM_SINGLE,"Txt",Txt_HTMLFormat,
                     Cns_MAX_BYTES_LONG_TEXT,NULL);
   Str_Copy (Txt_MarkdownFormat,Txt_HTMLFormat,
             Cns_MAX_BYTES_LONG_TEXT);
   Str_ChangeFormat (Str_FROM_FORM,Str_TO_HTML,
                     Txt_HTMLFormat,Cns_MAX_BYTES_LONG_TEXT,true);	// Store in HTML format (not rigorous)
   Str_ChangeFormat (Str_FROM_FORM,Str_TO_MARKDOWN,
                     Txt_MarkdownFormat,Cns_MAX_BYTES_LONG_TEXT,true);	// Store a copy in Markdown format

   /***** Update text of course info in database *****/
   Inf_SetInfoTxtIntoDB (Txt_HTMLFormat,Txt_MarkdownFormat);

   /***** Change info source to "rich text" in database *****/
   Inf_SetInfoSrcIntoDB (Txt_HTMLFormat[0] ? Inf_INFO_SRC_RICH_TEXT :
	                                     Inf_INFO_SRC_NONE);
   if (Txt_HTMLFormat[0])
      /***** Show the updated info *****/
      Inf_ShowInfo ();
   else
      /***** Show again the form to select and send course info *****/
      Inf_FormsToSelSendInfo ();
  }

/*****************************************************************************/
/********* Receive a link to a page of syllabus, bibliography, etc. **********/
/*****************************************************************************/

void Inf_ReceiveURLInfo (void)
  {
   extern const char *Txt_The_URL_X_has_been_updated;
   char PathFile[PATH_MAX + 1];
   FILE *FileURL;
   bool URLIsOK = false;

   /***** Set info type *****/
   Gbl.Crs.Info.Type  = Inf_AsignInfoType ();

   /***** Get parameter with URL *****/
   Par_GetParToText ("InfoSrcURL",Gbl.Crs.Info.URL,Cns_MAX_BYTES_WWW);

   /***** Build path to file containing URL *****/
   Inf_BuildPathURL (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,PathFile);

   /***** Open file with URL *****/
   if ((FileURL = fopen (PathFile,"wb")) != NULL)
     {
      /***** Write URL *****/
      fprintf (FileURL,"%s",Gbl.Crs.Info.URL);

      /***** Close file *****/
      fclose (FileURL);

      /***** Write message *****/
      Ale_ShowAlert (Ale_SUCCESS,Txt_The_URL_X_has_been_updated,
                     Gbl.Crs.Info.URL);
      URLIsOK = true;
     }
   else
      Ale_ShowAlert (Ale_ERROR,"Error when saving URL to file.");

   if (URLIsOK)
     {
      /***** Change info source to URL in database *****/
      Inf_SetInfoSrcIntoDB (Inf_INFO_SRC_URL);

      /***** Show the updated info *****/
      Inf_ShowInfo ();
     }
   else
     {
      /***** Change info source to none in database *****/
      Inf_SetInfoSrcIntoDB (Inf_INFO_SRC_NONE);

      /***** Show again the form to select and send course info *****/
      Inf_FormsToSelSendInfo ();
     }
  }

/*****************************************************************************/
/************** Receive a page of syllabus, bibliography, etc. ***************/
/*****************************************************************************/

void Inf_ReceivePagInfo (void)
  {
   extern const char *Txt_The_file_type_is_X_and_should_be_HTML_or_ZIP;
   extern const char *Txt_The_HTML_file_has_been_received_successfully;
   extern const char *Txt_The_ZIP_file_has_been_received_successfully;
   extern const char *Txt_The_ZIP_file_has_been_unzipped_successfully;
   extern const char *Txt_Found_an_index_html_file;
   extern const char *Txt_Found_an_index_htm_file;
   extern const char *Txt_No_file_index_html_index_htm_found_within_the_ZIP_file;
   extern const char *Txt_The_file_type_should_be_HTML_or_ZIP;
   struct Param *Param;
   char SourceFileName[PATH_MAX + 1];
   char PathRelDirHTML[PATH_MAX + 1];
   char PathRelFileHTML[PATH_MAX + 1];
   char PathRelFileZIP[PATH_MAX + 1 + NAME_MAX + 1];
   char MIMEType[Brw_MAX_BYTES_MIME_TYPE + 1];
   char StrUnzip[128 + PATH_MAX * 2 + 1];
   bool WrongType = false;
   bool FileIsOK = false;

   /***** Set info type *****/
   Gbl.Crs.Info.Type  = Inf_AsignInfoType ();

   /***** First of all, store in disk the file from stdin (really from Gbl.F.Tmp) *****/
   Param = Fil_StartReceptionOfFile (Fil_NAME_OF_PARAM_FILENAME_ORG,
                                     SourceFileName,MIMEType);

   /***** Check that MIME type is HTML or ZIP *****/
   if (strcmp (MIMEType,"text/html"))
      if (strcmp (MIMEType,"text/plain"))
         if (strcmp (MIMEType,"application/x-zip-compressed"))
            if (strcmp (MIMEType,"application/zip"))
               if (strcmp (MIMEType,"application/x-download"))
		  if (strcmp (MIMEType,"application/octet-stream"))
	             if (strcmp (MIMEType,"application/octetstream"))
	                if (strcmp (MIMEType,"application/octet"))
                           WrongType = true;
   if (WrongType)
      Ale_ShowAlert (Ale_INFO,Txt_The_file_type_is_X_and_should_be_HTML_or_ZIP,
                     MIMEType);
   else
     {
      /***** Build path of directory containing web page *****/
      Inf_BuildPathPage (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,PathRelDirHTML);

      /***** End the reception of the data *****/
      if (Str_FileIs (SourceFileName,"html") ||
          Str_FileIs (SourceFileName,"htm" )) // .html or .htm file
        {
         Fil_RemoveTree (PathRelDirHTML);
         Fil_CreateDirIfNotExists (PathRelDirHTML);
         snprintf (PathRelFileHTML,sizeof (PathRelFileHTML),
                   "%s/index.html",
		   PathRelDirHTML);
         if (Fil_EndReceptionOfFile (PathRelFileHTML,Param))
           {
            Ale_ShowAlert (Ale_SUCCESS,Txt_The_HTML_file_has_been_received_successfully);
            FileIsOK = true;
           }
         else
            Ale_ShowAlert (Ale_ERROR,"Error uploading file.");
        }
      else if (Str_FileIs (SourceFileName,"zip")) // .zip file
        {
         Fil_RemoveTree (PathRelDirHTML);
         Fil_CreateDirIfNotExists (PathRelDirHTML);
         snprintf (PathRelFileZIP,sizeof (PathRelFileZIP),
                   "%s/%s.zip",
                   Gbl.Crs.PathPriv,
                   Inf_FileNamesForInfoType[Gbl.Crs.Info.Type]);

         if (Fil_EndReceptionOfFile (PathRelFileZIP,Param))
           {
            Ale_ShowAlert (Ale_SUCCESS,Txt_The_ZIP_file_has_been_received_successfully);

            /* Uncompress ZIP */
            snprintf (StrUnzip,sizeof (StrUnzip),
        	      "unzip -qq -o %s -d %s",
                      PathRelFileZIP,PathRelDirHTML);
            if (system (StrUnzip) == 0)
              {
               /* Check if uploaded file is index.html or index.htm */
               snprintf (PathRelFileHTML,sizeof (PathRelFileHTML),
        	         "%s/index.html",
			 PathRelDirHTML);
               if (Fil_CheckIfPathExists (PathRelFileHTML))
                 {
                  Ale_ShowAlert (Ale_SUCCESS,Txt_The_ZIP_file_has_been_unzipped_successfully);
                  Ale_ShowAlert (Ale_SUCCESS,Txt_Found_an_index_html_file);
                  FileIsOK = true;
                 }
	       else
	         {
	          snprintf (PathRelFileHTML,sizeof (PathRelFileHTML),
	        	    "%s/index.htm",
			    PathRelDirHTML);
	          if (Fil_CheckIfPathExists (PathRelFileHTML))
                    {
                     Ale_ShowAlert (Ale_SUCCESS,Txt_The_ZIP_file_has_been_unzipped_successfully);
                     Ale_ShowAlert (Ale_SUCCESS,Txt_Found_an_index_htm_file);
                     FileIsOK = true;
                    }
	          else
                     Ale_ShowAlert (Ale_WARNING,Txt_No_file_index_html_index_htm_found_within_the_ZIP_file);
	         }
	      }
            else
               Lay_ShowErrorAndExit ("Can not unzip file.");
           }
         else
            Ale_ShowAlert (Ale_ERROR,"Error uploading file.");
        }
      else
         Ale_ShowAlert (Ale_WARNING,Txt_The_file_type_should_be_HTML_or_ZIP);
     }

   if (FileIsOK)
     {
      /***** Change info source to page in database *****/
      Inf_SetInfoSrcIntoDB (Inf_INFO_SRC_PAGE);

      /***** Show the updated info *****/
      Inf_ShowInfo ();
     }
   else
     {
      /***** Change info source to none in database *****/
      Inf_SetInfoSrcIntoDB (Inf_INFO_SRC_NONE);

      /***** Show again the form to select and send course info *****/
      Inf_FormsToSelSendInfo ();
     }
  }

/*****************************************************************************/
/******************* Integrated editor for introduction **********************/
/*****************************************************************************/

void Inf_EditorCourseInfo (void)
  {
   extern const char *Txt_The_integrated_editor_is_not_yet_available;

   Ale_ShowAlert (Ale_WARNING,Txt_The_integrated_editor_is_not_yet_available);

   /***** Show again the form to select and send course info *****/
   Inf_FormsToSelSendInfo ();
  }

/*****************************************************************************/
/****************** Integrated editor for teaching guide *********************/
/*****************************************************************************/

void Inf_EditorTeachingGuide (void)
  {
   extern const char *Txt_The_integrated_editor_is_not_yet_available;

   Ale_ShowAlert (Ale_WARNING,Txt_The_integrated_editor_is_not_yet_available);

   /***** Show again the form to select and send course info *****/
   Inf_FormsToSelSendInfo ();
  }

/*****************************************************************************/
/******************* Integrated editor for bibliography **********************/
/*****************************************************************************/

void Inf_EditorBibliography (void)
  {
   extern const char *Txt_The_integrated_editor_is_not_yet_available;

   Ale_ShowAlert (Ale_WARNING,Txt_The_integrated_editor_is_not_yet_available);

   /***** Show again the form to select and send course info *****/
   Inf_FormsToSelSendInfo ();
  }

/*****************************************************************************/
/*********************** Integrated editor for FAQ ***************************/
/*****************************************************************************/

void Inf_EditorFAQ (void)
  {
   extern const char *Txt_The_integrated_editor_is_not_yet_available;

   Ale_ShowAlert (Ale_WARNING,Txt_The_integrated_editor_is_not_yet_available);

   /***** Show again the form to select and send course info *****/
   Inf_FormsToSelSendInfo ();
  }

/*****************************************************************************/
/********************* Integrated editor for links ***************************/
/*****************************************************************************/

void Inf_EditorLinks (void)
  {
   extern const char *Txt_The_integrated_editor_is_not_yet_available;

   Ale_ShowAlert (Ale_WARNING,Txt_The_integrated_editor_is_not_yet_available);

   /***** Show again the form to select and send course info *****/
   Inf_FormsToSelSendInfo ();
  }

/*****************************************************************************/
/****************** Integrated editor for assessment system ******************/
/*****************************************************************************/

void Inf_EditorAssessment (void)
  {
   extern const char *Txt_The_integrated_editor_is_not_yet_available;

   Ale_ShowAlert (Ale_WARNING,Txt_The_integrated_editor_is_not_yet_available);

   /***** Show again the form to select and send course info *****/
   Inf_FormsToSelSendInfo ();
  }
