diff --git a/Makefile b/Makefile
index b016ec41b..ced82a0f9 100644
--- a/Makefile
+++ b/Makefile
@@ -59,9 +59,9 @@ OBJS = swad_account.o swad_action.o swad_agenda.o swad_alert.o \
swad_scope.o swad_search.o swad_session.o swad_setting.o \
swad_statistic.o swad_string.o swad_survey.o swad_syllabus.o \
swad_system_config.o \
- swad_tab.o swad_test.o swad_test_config.o swad_test_import.o \
- swad_test_print.o swad_test_visibility.o swad_theme.o swad_timeline.o \
- swad_timetable.o \
+ swad_tab.o swad_tag.o swad_test.o swad_test_config.o \
+ swad_test_import.o swad_test_print.o swad_test_visibility.o \
+ swad_theme.o swad_timeline.o swad_timetable.o \
swad_user.o \
swad_xml.o \
swad_zip.o
diff --git a/swad_API.c b/swad_API.c
index 746c50784..5a8bef1dd 100644
--- a/swad_API.c
+++ b/swad_API.c
@@ -4248,9 +4248,9 @@ static int API_GetTstTags (struct soap *soap,
/* Get tag text (row[1]) */
getTestsOut->tagsArray.__ptr[NumRow].tagText =
- (char *) soap_malloc (soap,Tst_MAX_BYTES_TAG + 1);
+ (char *) soap_malloc (soap,Tag_MAX_BYTES_TAG + 1);
Str_Copy (getTestsOut->tagsArray.__ptr[NumRow].tagText,row[1],
- Tst_MAX_BYTES_TAG);
+ Tag_MAX_BYTES_TAG);
}
}
diff --git a/swad_action.c b/swad_action.c
index 3c66b7208..f6851e59e 100644
--- a/swad_action.c
+++ b/swad_action.c
@@ -92,6 +92,7 @@
#include "swad_survey.h"
#include "swad_system_config.h"
#include "swad_tab.h"
+#include "swad_tag.h"
#include "swad_test_import.h"
#include "swad_timeline.h"
#include "swad_timetable.h"
diff --git a/swad_changelog.h b/swad_changelog.h
index 62c5e981d..90c07615c 100644
--- a/swad_changelog.h
+++ b/swad_changelog.h
@@ -557,7 +557,7 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - *
En OpenSWAD:
ps2pdf source.ps destination.pdf
*/
-#define Log_PLATFORM_VERSION "SWAD 19.234 (2020-05-17)"
+#define Log_PLATFORM_VERSION "SWAD 19.235 (2020-05-17)"
#define CSS_FILE "swad19.230.1.css"
#define JS_FILE "swad19.230.3.js"
/*
@@ -565,6 +565,7 @@ TODO: Comprobar si el directorio p
// Si no existe, hay que crear un nuevo directorio y meterlo en cache
TODO: ¿Mover importar y exportar a icono en la esquina?
+ Version 19.235: May 17, 2020 New module swad_tag for question tags. (301147 lines)
Version 19.234: May 17, 2020 Option to edit tags in bank of questions. (301061 lines)
Copy the following 3 icons to icon public directory:
sudo cp icon/tag.svg /var/www/html/swad/icon/
diff --git a/swad_database.c b/swad_database.c
index aa0d71fdc..931b7e42f 100644
--- a/swad_database.c
+++ b/swad_database.c
@@ -3281,7 +3281,7 @@ mysql> DESCRIBE tst_tags;
"TagCod INT NOT NULL AUTO_INCREMENT,"
"CrsCod INT NOT NULL DEFAULT -1,"
"ChangeTime DATETIME NOT NULL,"
- "TagTxt VARCHAR(2047) NOT NULL," // Tst_MAX_BYTES_TAG
+ "TagTxt VARCHAR(2047) NOT NULL," // Tag_MAX_BYTES_TAG
"TagHidden ENUM('N','Y') NOT NULL,"
"UNIQUE INDEX(TagCod),"
"INDEX(CrsCod,ChangeTime))");
diff --git a/swad_tag.c b/swad_tag.c
new file mode 100644
index 000000000..90185e11d
--- /dev/null
+++ b/swad_tag.c
@@ -0,0 +1,658 @@
+// swad_tag.c: tags for questions
+
+/*
+ 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 // To access MySQL databases
+#include // For boolean type
+#include // For string functions
+
+#include "swad_action.h"
+#include "swad_database.h"
+#include "swad_form.h"
+#include "swad_global.h"
+#include "swad_tag.h"
+#include "swad_theme.h"
+
+/*****************************************************************************/
+/***************************** Public constants ******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/**************************** Private constants ******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/******************************* Private types *******************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/************** External global variables from others modules ****************/
+/*****************************************************************************/
+
+extern struct Globals Gbl;
+
+/*****************************************************************************/
+/************************* Private global variables **************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Private prototypes ****************************/
+/*****************************************************************************/
+
+static void Tag_EnableOrDisableTag (long TagCod,bool TagHidden);
+
+static long Tag_GetParamTagCode (void);
+
+static long Tag_GetTagCodFromTagTxt (const char *TagTxt);
+static long Tag_CreateNewTag (long CrsCod,const char *TagTxt);
+
+static void Tag_PutIconEnable (long TagCod,const char *TagTxt);
+static void Tag_PutIconDisable (long TagCod,const char *TagTxt);
+
+/*****************************************************************************/
+/********************************* Reset tags ********************************/
+/*****************************************************************************/
+
+void Tag_ResetTags (struct Tag_Tags *Tags)
+ {
+ unsigned IndTag;
+
+ Tags->Num = 0;
+ Tags->All = false;
+ Tags->List = NULL;
+
+ /***** Initialize all tags in question to empty string *****/
+ for (IndTag = 0;
+ IndTag < Tag_MAX_TAGS_PER_QUESTION;
+ IndTag++)
+ Tags->Txt[IndTag][0] = '\0';
+ }
+
+/*****************************************************************************/
+/**************** Free memory allocated for the list of tags *****************/
+/*****************************************************************************/
+
+void Tag_FreeTagsList (struct Tag_Tags *Tags)
+ {
+ if (Tags->List)
+ {
+ free (Tags->List);
+ Tag_ResetTags (Tags);
+ }
+ }
+
+/*****************************************************************************/
+/******************* Check if current course has test tags *******************/
+/*****************************************************************************/
+// Return the number of rows of the result
+
+bool Tag_CheckIfCurrentCrsHasTestTags (void)
+ {
+ /***** Get available tags from database *****/
+ return (DB_QueryCOUNT ("can not check if course has tags",
+ "SELECT COUNT(*) FROM tst_tags"
+ " WHERE CrsCod=%ld",
+ Gbl.Hierarchy.Crs.CrsCod) != 0);
+ }
+
+/*****************************************************************************/
+/********* Get all (enabled or disabled) test tags for this course ***********/
+/*****************************************************************************/
+// Return the number of rows of the result
+
+unsigned Tag_GetAllTagsFromCurrentCrs (MYSQL_RES **mysql_res)
+ {
+ /***** Get available tags from database *****/
+ return (unsigned) DB_QuerySELECT (mysql_res,"can not get available tags",
+ "SELECT TagCod," // row[0]
+ "TagTxt," // row[1]
+ "TagHidden" // row[2]
+ " FROM tst_tags"
+ " WHERE CrsCod=%ld"
+ " ORDER BY TagTxt",
+ Gbl.Hierarchy.Crs.CrsCod);
+ }
+
+/*****************************************************************************/
+/********************** Get enabled test tags for this course ****************/
+/*****************************************************************************/
+// Return the number of rows of the result
+
+unsigned Tag_GetEnabledTagsFromThisCrs (MYSQL_RES **mysql_res)
+ {
+ /***** Get available not hidden tags from database *****/
+ return (unsigned) DB_QuerySELECT (mysql_res,"can not get available enabled tags",
+ "SELECT TagCod," // row[0]
+ "TagTxt" // row[1]
+ " FROM tst_tags"
+ " WHERE CrsCod=%ld AND TagHidden='N'"
+ " ORDER BY TagTxt",
+ Gbl.Hierarchy.Crs.CrsCod);
+ }
+
+/*****************************************************************************/
+/******************************* Enable a test tag ***************************/
+/*****************************************************************************/
+
+void Tag_EnableTag (void)
+ {
+ long TagCod = Tag_GetParamTagCode ();
+
+ /***** Change tag status to enabled *****/
+ Tag_EnableOrDisableTag (TagCod,false);
+
+ /***** Show again the form to edit tags *****/
+ Tag_ShowFormEditTags ();
+ }
+
+/*****************************************************************************/
+/****************************** Disable a test tag ***************************/
+/*****************************************************************************/
+
+void Tag_DisableTag (void)
+ {
+ long TagCod = Tag_GetParamTagCode ();
+
+ /***** Change tag status to disabled *****/
+ Tag_EnableOrDisableTag (TagCod,true);
+
+ /***** Show again the form to edit tags *****/
+ Tag_ShowFormEditTags ();
+ }
+
+/*****************************************************************************/
+/********** Change visibility of an existing tag into tst_tags table *********/
+/*****************************************************************************/
+
+static void Tag_EnableOrDisableTag (long TagCod,bool TagHidden)
+ {
+ /***** Insert new tag into tst_tags table *****/
+ DB_QueryUPDATE ("can not update the visibility of a tag",
+ "UPDATE tst_tags SET TagHidden='%c',ChangeTime=NOW()"
+ " WHERE TagCod=%ld AND CrsCod=%ld",
+ TagHidden ? 'Y' :
+ 'N',
+ TagCod,Gbl.Hierarchy.Crs.CrsCod);
+ }
+
+/*****************************************************************************/
+/************************* Get parameter with tag code ***********************/
+/*****************************************************************************/
+
+static long Tag_GetParamTagCode (void)
+ {
+ long TagCod;
+
+ /***** Get tag code *****/
+ if ((TagCod = Par_GetParToLong ("TagCod")) <= 0)
+ Lay_ShowErrorAndExit ("Code of tag is missing.");
+
+ return TagCod;
+ }
+
+/*****************************************************************************/
+/************************ Rename a tag of test questions *********************/
+/*****************************************************************************/
+
+void Tag_RenameTag (void)
+ {
+ extern const char *Txt_The_tag_X_has_been_renamed_as_Y;
+ extern const char *Txt_The_tag_X_has_not_changed;
+ char OldTagTxt[Tag_MAX_BYTES_TAG + 1];
+ char NewTagTxt[Tag_MAX_BYTES_TAG + 1];
+ long ExistingTagCod;
+ long OldTagCod;
+ bool ComplexRenaming;
+
+ /***** Get old and new tags from the form *****/
+ Par_GetParToText ("OldTagTxt",OldTagTxt,Tag_MAX_BYTES_TAG);
+ Par_GetParToText ("NewTagTxt",NewTagTxt,Tag_MAX_BYTES_TAG);
+
+ /***** Check that the new tag is not empty *****/
+ if (NewTagTxt[0]) // New tag not empty
+ {
+ /***** Check if the old tag is equal to the new one *****/
+ if (!strcmp (OldTagTxt,NewTagTxt)) // The old and the new tag
+ // are exactly the same (case sensitively).
+ // This happens when user press INTRO
+ // without changing anything in the form.
+ Ale_ShowAlert (Ale_INFO,Txt_The_tag_X_has_not_changed,
+ NewTagTxt);
+ else // The old and the new tag
+ // are not exactly the same (case sensitively).
+ {
+ /***** Check if renaming is complex or easy *****/
+ ComplexRenaming = false;
+ if (strcasecmp (OldTagTxt,NewTagTxt)) // The old and the new tag
+ // are not the same (case insensitively)
+ /* Check if the new tag text is equal to any of the tags
+ already present in the database */
+ if ((ExistingTagCod = Tag_GetTagCodFromTagTxt (NewTagTxt)) > 0)
+ // The new tag was already in database
+ ComplexRenaming = true;
+
+ if (ComplexRenaming) // Renaming is not easy
+ {
+ /***** Complex update made to not repeat tags:
+ - If the new tag existed for a question ==>
+ delete old tag from tst_question_tags;
+ the new tag will remain
+ - If the new tag did not exist for a question ==>
+ change old tag to new tag in tst_question_tags *****/
+ /* Get tag code of the old tag */
+ if ((OldTagCod = Tag_GetTagCodFromTagTxt (OldTagTxt)) < 0)
+ Lay_ShowErrorAndExit ("Tag does not exists.");
+
+ /* Create a temporary table with all the question codes
+ that had the new tag as one of their tags */
+ DB_Query ("can not remove temporary table",
+ "DROP TEMPORARY TABLE IF EXISTS tst_question_tags_tmp");
+
+ DB_Query ("can not create temporary table",
+ "CREATE TEMPORARY TABLE tst_question_tags_tmp"
+ " ENGINE=MEMORY"
+ " SELECT QstCod FROM tst_question_tags"
+ " WHERE TagCod=%ld",
+ ExistingTagCod);
+
+ /* Remove old tag in questions where it would be repeated */
+ // New tag existed for a question ==> delete old tag
+ DB_QueryDELETE ("can not remove a tag from some questions",
+ "DELETE FROM tst_question_tags"
+ " WHERE TagCod=%ld"
+ " AND QstCod IN"
+ " (SELECT QstCod FROM tst_question_tags_tmp)",
+ OldTagCod);
+
+ /* Change old tag to new tag in questions where it would not be repeated */
+ // New tag did not exist for a question ==> change old tag to new tag
+ DB_QueryUPDATE ("can not update a tag in some questions",
+ "UPDATE tst_question_tags"
+ " SET TagCod=%ld"
+ " WHERE TagCod=%ld"
+ " AND QstCod NOT IN"
+ " (SELECT QstCod FROM tst_question_tags_tmp)",
+ ExistingTagCod,
+ OldTagCod);
+
+ /* Drop temporary table, no longer necessary */
+ DB_Query ("can not remove temporary table",
+ "DROP TEMPORARY TABLE IF EXISTS tst_question_tags_tmp");
+
+ /***** Delete old tag from tst_tags
+ because it is not longer used *****/
+ DB_QueryDELETE ("can not remove old tag",
+ "DELETE FROM tst_tags WHERE TagCod=%ld",
+ OldTagCod);
+ }
+ else // Renaming is easy
+ {
+ /***** Simple update replacing each instance of the old tag by the new tag *****/
+ DB_QueryUPDATE ("can not update tag",
+ "UPDATE tst_tags SET TagTxt='%s',ChangeTime=NOW()"
+ " WHERE tst_tags.CrsCod=%ld"
+ " AND tst_tags.TagTxt='%s'",
+ NewTagTxt,Gbl.Hierarchy.Crs.CrsCod,OldTagTxt);
+ }
+
+ /***** Write message to show the change made *****/
+ Ale_ShowAlert (Ale_SUCCESS,Txt_The_tag_X_has_been_renamed_as_Y,
+ OldTagTxt,NewTagTxt);
+ }
+ }
+ else // New tag empty
+ Ale_ShowAlertYouCanNotLeaveFieldEmpty ();
+
+ /***** Show again the form to edit tags *****/
+ Tag_ShowFormEditTags ();
+ }
+
+/*****************************************************************************/
+/***************** Check if this tag exists for current course ***************/
+/*****************************************************************************/
+
+static long Tag_GetTagCodFromTagTxt (const char *TagTxt)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned long NumRows;
+ long TagCod = -1L; // -1 means that the tag does not exist in database
+
+ /***** Get tag code from database *****/
+ NumRows = DB_QuerySELECT (&mysql_res,"can not get tag",
+ "SELECT TagCod FROM tst_tags"
+ " WHERE CrsCod=%ld AND TagTxt='%s'",
+ Gbl.Hierarchy.Crs.CrsCod,TagTxt);
+ if (NumRows == 1)
+ {
+ /***** Get tag code *****/
+ row = mysql_fetch_row (mysql_res);
+ if ((TagCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
+ Ale_CreateAlert (Ale_ERROR,NULL,
+ "Wrong code of tag.");
+ }
+ else if (NumRows > 1)
+ Ale_CreateAlert (Ale_ERROR,NULL,
+ "Duplicated tag.");
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+
+ /***** Abort on error *****/
+ if (Ale_GetTypeOfLastAlert () == Ale_ERROR)
+ Ale_ShowAlertsAndExit ();
+
+ return TagCod;
+ }
+
+/*****************************************************************************/
+/*********************** Insert tags in the tags table ***********************/
+/*****************************************************************************/
+
+void Tag_InsertTagsIntoDB (long QstCod,const struct Tag_Tags *Tags)
+ {
+ unsigned NumTag;
+ unsigned TagIdx;
+ long TagCod;
+
+ /***** For each tag... *****/
+ for (NumTag = 0, TagIdx = 0;
+ TagIdx < Tags->Num;
+ NumTag++)
+ if (Tags->Txt[NumTag][0])
+ {
+ /***** Check if this tag exists for current course *****/
+ if ((TagCod = Tag_GetTagCodFromTagTxt (Tags->Txt[NumTag])) < 0)
+ /* This tag is new for current course. Add it to tags table */
+ TagCod = Tag_CreateNewTag (Gbl.Hierarchy.Crs.CrsCod,Tags->Txt[NumTag]);
+
+ /***** Insert tag in tst_question_tags *****/
+ DB_QueryINSERT ("can not create tag",
+ "INSERT INTO tst_question_tags"
+ " (QstCod,TagCod,TagInd)"
+ " VALUES"
+ " (%ld,%ld,%u)",
+ QstCod,TagCod,TagIdx);
+
+ TagIdx++;
+ }
+ }
+
+/*****************************************************************************/
+/********************* Insert new tag into tst_tags table ********************/
+/*****************************************************************************/
+
+static long Tag_CreateNewTag (long CrsCod,const char *TagTxt)
+ {
+ /***** Insert new tag into tst_tags table *****/
+ return
+ DB_QueryINSERTandReturnCode ("can not create new tag",
+ "INSERT INTO tst_tags"
+ " (CrsCod,ChangeTime,TagTxt,TagHidden)"
+ " VALUES"
+ " (%ld,NOW(),'%s','Y')", // Hidden by default
+ CrsCod,TagTxt);
+ }
+
+/*****************************************************************************/
+/********************* Show a form to select test tags ***********************/
+/*****************************************************************************/
+
+void Tag_ShowFormSelTags (const struct Tag_Tags *Tags,
+ MYSQL_RES *mysql_res,
+ bool ShowOnlyEnabledTags)
+ {
+ extern const char *The_ClassFormInBox[The_NUM_THEMES];
+ extern const char *Txt_Tags;
+ extern const char *Txt_All_tags;
+ extern const char *Txt_Tag_not_allowed;
+ extern const char *Txt_Tag_allowed;
+ unsigned NumTag;
+ MYSQL_ROW row;
+ bool TagHidden = false;
+ bool Checked;
+ const char *Ptr;
+ char TagText[Tag_MAX_BYTES_TAG + 1];
+ /*
+ row[0] TagCod
+ row[1] TagTxt
+ row[2] TagHidden
+ */
+ HTM_TR_Begin (NULL);
+
+ /***** Label *****/
+ HTM_TD_Begin ("class=\"RT %s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
+ HTM_TxtF ("%s:",Txt_Tags);
+ HTM_TD_End ();
+
+ /***** Select all tags *****/
+ HTM_TD_Begin ("class=\"LT\"");
+
+ HTM_TABLE_BeginPadding (2);
+ HTM_TR_Begin (NULL);
+ if (!ShowOnlyEnabledTags)
+ HTM_TD_Empty (1);
+
+ HTM_TD_Begin ("class=\"LM\"");
+ HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
+ HTM_INPUT_CHECKBOX ("AllTags",HTM_DONT_SUBMIT_ON_CHANGE,
+ "value=\"Y\"%s onclick=\"togglecheckChildren(this,'ChkTag');\"",
+ Tags->All ? " checked=\"checked\"" :
+ "");
+ HTM_TxtF (" %s",Txt_All_tags);
+ HTM_LABEL_End ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Select tags one by one *****/
+ for (NumTag = 1;
+ NumTag <= Tags->Num;
+ NumTag++)
+ {
+ row = mysql_fetch_row (mysql_res);
+ HTM_TR_Begin (NULL);
+
+ if (!ShowOnlyEnabledTags)
+ {
+ TagHidden = (row[2][0] == 'Y');
+ HTM_TD_Begin ("class=\"LM\"");
+ Ico_PutIconOff (TagHidden ? "eye-slash-red.svg" :
+ "eye-green.svg",
+ TagHidden ? Txt_Tag_not_allowed :
+ Txt_Tag_allowed);
+ HTM_TD_End ();
+ }
+
+ Checked = false;
+ if (Tags->List)
+ {
+ Ptr = Tags->List;
+ while (*Ptr)
+ {
+ Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG);
+ if (!strcmp (row[1],TagText))
+ {
+ Checked = true;
+ break;
+ }
+ }
+ }
+
+ HTM_TD_Begin ("class=\"LM\"");
+ HTM_LABEL_Begin ("class=\"DAT\"");
+ HTM_INPUT_CHECKBOX ("ChkTag",HTM_DONT_SUBMIT_ON_CHANGE,
+ "value=\"%s\"%s onclick=\"checkParent(this,'AllTags');\"",
+ row[1],
+ Checked ? " checked=\"checked\"" :
+ "");
+ HTM_TxtF (" %s",row[1]);
+ HTM_LABEL_End ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+ }
+
+ HTM_TABLE_End ();
+ HTM_TD_End ();
+ HTM_TR_End ();
+ }
+
+/*****************************************************************************/
+/************* Show a form to enable/disable and rename test tags ************/
+/*****************************************************************************/
+
+void Tag_ShowFormEditTags (void)
+ {
+ extern const char *Hlp_ASSESSMENT_Tests_configuring_tests;
+ extern const char *Txt_No_test_questions;
+ extern const char *Txt_Tags;
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumTags;
+ unsigned NumTag;
+ long TagCod;
+
+ /***** Get current tags in current course *****/
+ if ((NumTags = Tag_GetAllTagsFromCurrentCrs (&mysql_res)))
+ {
+ /***** Begin box and table *****/
+ Box_BoxTableBegin (NULL,Txt_Tags,
+ NULL,NULL,
+ Hlp_ASSESSMENT_Tests_configuring_tests,Box_NOT_CLOSABLE,2);
+
+ /***** Show tags *****/
+ for (NumTag = 0;
+ NumTag < NumTags;
+ NumTag++)
+ {
+ row = mysql_fetch_row (mysql_res);
+ /*
+ row[0] TagCod
+ row[1] TagTxt
+ row[2] TagHidden
+ */
+ if ((TagCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
+ Lay_ShowErrorAndExit ("Wrong code of tag.");
+
+ HTM_TR_Begin (NULL);
+
+ /* Form to enable / disable this tag */
+ if (row[2][0] == 'Y') // Tag disabled
+ Tag_PutIconEnable (TagCod,row[1]);
+ else
+ Tag_PutIconDisable (TagCod,row[1]);
+
+ /* Form to rename this tag */
+ HTM_TD_Begin ("class=\"LM\"");
+ Frm_StartForm (ActRenTag);
+ Par_PutHiddenParamString (NULL,"OldTagTxt",row[1]);
+ HTM_INPUT_TEXT ("NewTagTxt",Tag_MAX_CHARS_TAG,row[1],
+ HTM_SUBMIT_ON_CHANGE,
+ "size=\"36\" required=\"required\"");
+ Frm_EndForm ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+ }
+
+ /***** End table and box *****/
+ Box_BoxTableEnd ();
+ }
+ else
+ Ale_ShowAlert (Ale_INFO,Txt_No_test_questions);
+
+ /* Free structure that stores the query result */
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/******************* Put a link and an icon to enable a tag ******************/
+/*****************************************************************************/
+
+static void Tag_PutIconEnable (long TagCod,const char *TagTxt)
+ {
+ extern const char *Txt_Tag_X_not_allowed_Click_to_allow_it;
+
+ HTM_TD_Begin ("class=\"BM\"");
+ Frm_StartForm (ActEnaTag);
+ Par_PutHiddenParamLong (NULL,"TagCod",TagCod);
+ Ico_PutIconLink ("eye-slash-red.svg",
+ Str_BuildStringStr (Txt_Tag_X_not_allowed_Click_to_allow_it,
+ TagTxt));
+ Str_FreeString ();
+ Frm_EndForm ();
+ HTM_TD_End ();
+ }
+
+/*****************************************************************************/
+/****************** Put a link and an icon to disable a tag ******************/
+/*****************************************************************************/
+
+static void Tag_PutIconDisable (long TagCod,const char *TagTxt)
+ {
+ extern const char *Txt_Tag_X_allowed_Click_to_disable_it;
+
+ HTM_TD_Begin ("class=\"BM\"");
+ Frm_StartForm (ActDisTag);
+ Par_PutHiddenParamLong (NULL,"TagCod",TagCod);
+ Ico_PutIconLink ("eye-green.svg",
+ Str_BuildStringStr (Txt_Tag_X_allowed_Click_to_disable_it,
+ TagTxt));
+ Str_FreeString ();
+ Frm_EndForm ();
+ HTM_TD_End ();
+ }
+
+/*****************************************************************************/
+/************************** Remove tags from a question **********************/
+/*****************************************************************************/
+
+void Tag_RemTagsFromQst (long QstCod)
+ {
+ /***** Remove tags *****/
+ DB_QueryDELETE ("can not remove the tags of a question",
+ "DELETE FROM tst_question_tags WHERE QstCod=%ld",
+ QstCod);
+ }
+
+/*****************************************************************************/
+/********************** Remove unused tags in a course ***********************/
+/*****************************************************************************/
+
+void Tag_RemoveUnusedTagsFromCrs (long CrsCod)
+ {
+ /***** Remove unused tags from tst_tags *****/
+ DB_QueryDELETE ("can not remove unused tags",
+ "DELETE FROM tst_tags"
+ " WHERE CrsCod=%ld AND TagCod NOT IN"
+ " (SELECT DISTINCT tst_question_tags.TagCod"
+ " FROM tst_questions,tst_question_tags"
+ " WHERE tst_questions.CrsCod=%ld"
+ " AND tst_questions.QstCod=tst_question_tags.QstCod)",
+ CrsCod,
+ CrsCod);
+ }
diff --git a/swad_tag.h b/swad_tag.h
new file mode 100644
index 000000000..af2f1bf1a
--- /dev/null
+++ b/swad_tag.h
@@ -0,0 +1,80 @@
+// swad_tag.h: tags for questions
+
+#ifndef _SWAD_TAG
+#define _SWAD_TAG
+/*
+ SWAD (Shared Workspace At a Distance in Spanish),
+ 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_string.h"
+
+/*****************************************************************************/
+/***************************** Public constants ******************************/
+/*****************************************************************************/
+
+#define Tag_MAX_TAGS_PER_QUESTION 5
+
+#define Tag_MAX_CHARS_TAG (128 - 1) // 127
+#define Tag_MAX_BYTES_TAG ((Tag_MAX_CHARS_TAG + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
+
+#define Tag_MAX_BYTES_TAGS_LIST (16 * 1024)
+
+/*****************************************************************************/
+/******************************* Public types ********************************/
+/*****************************************************************************/
+
+struct Tag_Tags
+ {
+ unsigned Num;
+ bool All;
+ char *List;
+ char Txt[Tag_MAX_TAGS_PER_QUESTION][Tag_MAX_BYTES_TAG + 1];
+ };
+
+/*****************************************************************************/
+/***************************** Public prototypes *****************************/
+/*****************************************************************************/
+
+void Tag_ResetTags (struct Tag_Tags *Tags);
+void Tag_FreeTagsList (struct Tag_Tags *Tags);
+
+bool Tag_CheckIfCurrentCrsHasTestTags (void);
+unsigned Tag_GetAllTagsFromCurrentCrs (MYSQL_RES **mysql_res);
+unsigned Tag_GetEnabledTagsFromThisCrs (MYSQL_RES **mysql_res);
+
+void Tag_EnableTag (void);
+void Tag_DisableTag (void);
+void Tag_RenameTag (void);
+
+void Tag_InsertTagsIntoDB (long QstCod,const struct Tag_Tags *Tags);
+
+void Tag_ShowFormSelTags (const struct Tag_Tags *Tags,
+ MYSQL_RES *mysql_res,
+ bool ShowOnlyEnabledTags);
+void Tag_ShowFormEditTags (void);
+
+void Tag_RemTagsFromQst (long QstCod);
+void Tag_RemoveUnusedTagsFromCrs (long CrsCod);
+
+#endif
diff --git a/swad_test.c b/swad_test.c
index 7817cc31a..b0e46e8ee 100644
--- a/swad_test.c
+++ b/swad_test.c
@@ -88,8 +88,6 @@ const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES] =
/**************************** Private constants ******************************/
/*****************************************************************************/
-#define Tst_MAX_BYTES_TAGS_LIST (16 * 1024)
-
// Test images will be saved with:
// - maximum width of Tst_IMAGE_SAVED_MAX_HEIGHT
// - maximum height of Tst_IMAGE_SAVED_MAX_HEIGHT
@@ -125,8 +123,6 @@ extern struct Globals Gbl;
static void Tst_TstConstructor (struct Tst_Test *Test);
static void Tst_TstDestructor (struct Tst_Test *Test);
-static void Tst_ResetTags (struct Tst_Tags *Tags);
-static void Tst_FreeTagsList (struct Tst_Tags *Tags);
static void Tst_ShowFormRequestTest (struct Tst_Test *Test);
@@ -158,17 +154,6 @@ static void Tst_PutIconsBankQsts (void *Test);
static void Tst_PutIconsTests (__attribute__((unused)) void *Args);
static void Tst_PutButtonToAddQuestion (void);
-static long Tag_GetParamTagCode (void);
-static bool Tst_CheckIfCurrentCrsHasTestTags (void);
-static unsigned Tst_GetAllTagsFromCurrentCrs (MYSQL_RES **mysql_res);
-static unsigned Tst_GetEnabledTagsFromThisCrs (MYSQL_RES **mysql_res);
-
-static void Tag_ShowFormSelTags (const struct Tst_Tags *Tags,
- MYSQL_RES *mysql_res,
- bool ShowOnlyEnabledTags);
-static void Tag_PutIconEnable (long TagCod,const char *TagTxt);
-static void Tag_PutIconDisable (long TagCod,const char *TagTxt);
-
static void Tst_ShowFormConfigTst (void);
static void Tst_PutInputFieldNumQst (const char *Field,const char *Label,
@@ -221,7 +206,7 @@ static bool Tst_GetParamsTst (struct Tst_Test *Test,
Tst_ActionToDoWithQuestions_t ActionToDoWithQuestions);
static unsigned Tst_GetParamNumTst (void);
static unsigned Tst_GetParamNumQsts (void);
-static unsigned Tst_CountNumTagsInList (const struct Tst_Tags *Tags);
+static unsigned Tst_CountNumTagsInList (const struct Tag_Tags *Tags);
static int Tst_CountNumAnswerTypesInList (const struct Tst_AnswerTypes *AnswerTypes);
static void Tst_PutFormEditOneQst (struct Tst_Question *Question);
@@ -245,10 +230,6 @@ static Tst_AnswerType_t Tst_ConvertFromUnsignedStrToAnsTyp (const char *Unsigned
static void Tst_GetQstFromForm (struct Tst_Question *Question);
static void Tst_MoveMediaToDefinitiveDirectories (struct Tst_Question *Question);
-static long Tst_GetTagCodFromTagTxt (const char *TagTxt);
-static long Tst_CreateNewTag (long CrsCod,const char *TagTxt);
-static void Tst_EnableOrDisableTag (long TagCod,bool TagHidden);
-
static void Tst_PutParamsRemoveSelectedQsts (void *Test);
static void Tst_PutIconToRemoveOneQst (void *QstCod);
static void Tst_PutParamsRemoveOnlyThisQst (void *QstCod);
@@ -256,12 +237,9 @@ static void Tst_PutParamsRemoveOneQstWhileEditing (void *Test);
static void Tst_RemoveOneQstFromDB (long CrsCod,long QstCod);
static void Tst_InsertOrUpdateQstIntoDB (struct Tst_Question *Question);
-static void Tst_InsertTagsIntoDB (const struct Tst_Question *Question);
static void Tst_InsertAnswersIntoDB (struct Tst_Question *Question);
static void Tst_RemAnsFromQst (long QstCod);
-static void Tst_RemTagsFromQst (long QstCod);
-static void Tst_RemoveUnusedTagsFromCrs (long CrsCod);
static void Tst_RemoveAllMedFilesFromStemOfAllQstsInCrs (long CrsCod);
static void Tst_RemoveMediaFromAllAnsOfQst (long CrsCod,long QstCod);
@@ -296,7 +274,7 @@ void Tst_RequestTest (void)
static void Tst_TstConstructor (struct Tst_Test *Test)
{
/***** Reset tags *****/
- Tst_ResetTags (&Test->Tags);
+ Tag_ResetTags (&Test->Tags);
/***** Reset answer types *****/
Test->AnswerTypes.All = false;
@@ -319,39 +297,7 @@ static void Tst_TstDestructor (struct Tst_Test *Test)
Tst_QstDestructor (&Test->Question);
/***** Free tag list *****/
- Tst_FreeTagsList (&Test->Tags);
- }
-
-/*****************************************************************************/
-/********************************* Reset tags ********************************/
-/*****************************************************************************/
-
-static void Tst_ResetTags (struct Tst_Tags *Tags)
- {
- unsigned IndTag;
-
- Tags->Num = 0;
- Tags->All = false;
- Tags->List = NULL;
-
- /***** Initialize all tags in question to empty string *****/
- for (IndTag = 0;
- IndTag < Tst_MAX_TAGS_PER_QUESTION;
- IndTag++)
- Tags->Txt[IndTag][0] = '\0';
- }
-
-/*****************************************************************************/
-/**************** Free memory allocated for the list of tags *****************/
-/*****************************************************************************/
-
-static void Tst_FreeTagsList (struct Tst_Tags *Tags)
- {
- if (Tags->List)
- {
- free (Tags->List);
- Tst_ResetTags (Tags);
- }
+ Tag_FreeTagsList (&Test->Tags);
}
/*****************************************************************************/
@@ -376,7 +322,7 @@ static void Tst_ShowFormRequestTest (struct Tst_Test *Test)
Hlp_ASSESSMENT_Tests,Box_NOT_CLOSABLE);
/***** Get tags *****/
- if ((Test->Tags.Num = Tst_GetEnabledTagsFromThisCrs (&mysql_res)) != 0)
+ if ((Test->Tags.Num = Tag_GetEnabledTagsFromThisCrs (&mysql_res)) != 0)
{
/***** Check if minimum date-time of next access to test is older than now *****/
if (Tst_CheckIfNextTstAllowed ())
@@ -1235,7 +1181,7 @@ static void Tst_ShowFormRequestEditTests (struct Tst_Test *Test)
Hlp_ASSESSMENT_Tests_editing_questions,Box_NOT_CLOSABLE);
/***** Get tags already present in the table of questions *****/
- if ((Test->Tags.Num = Tst_GetAllTagsFromCurrentCrs (&mysql_res)))
+ if ((Test->Tags.Num = Tag_GetAllTagsFromCurrentCrs (&mysql_res)))
{
Frm_StartForm (ActLstTstQst);
Par_PutHiddenParamUnsigned (NULL,"Order",(unsigned) Tst_DEFAULT_ORDER);
@@ -1333,7 +1279,7 @@ static void Tst_ShowFormRequestSelectTestsForSet (struct Exa_Exams *Exams,
Hlp_ASSESSMENT_Exams_questions,Box_NOT_CLOSABLE);
/***** Get tags already present in the table of questions *****/
- if ((Test->Tags.Num = Tst_GetAllTagsFromCurrentCrs (&mysql_res)))
+ if ((Test->Tags.Num = Tag_GetAllTagsFromCurrentCrs (&mysql_res)))
{
Frm_StartForm (ActLstTstQstForSet);
ExaSet_PutParamsOneSet (Exams);
@@ -1395,7 +1341,7 @@ static void Tst_ShowFormRequestSelectTestsForGame (struct Gam_Games *Games,
Hlp_ASSESSMENT_Games_questions,Box_NOT_CLOSABLE);
/***** Get tags already present in the table of questions *****/
- if ((Test->Tags.Num = Tst_GetAllTagsFromCurrentCrs (&mysql_res)))
+ if ((Test->Tags.Num = Tag_GetAllTagsFromCurrentCrs (&mysql_res)))
{
Frm_StartForm (ActGamLstTstQst);
Gam_PutParams (Games);
@@ -1541,168 +1487,6 @@ void Tst_ShowFormConfig (void)
Tst_ShowFormConfigTst ();
}
-/*****************************************************************************/
-/******************************* Enable a test tag ***************************/
-/*****************************************************************************/
-
-void Tag_EnableTag (void)
- {
- long TagCod = Tag_GetParamTagCode ();
-
- /***** Change tag status to enabled *****/
- Tst_EnableOrDisableTag (TagCod,false);
-
- /***** Show again the form to edit tags *****/
- Tag_ShowFormEditTags ();
- }
-
-/*****************************************************************************/
-/****************************** Disable a test tag ***************************/
-/*****************************************************************************/
-
-void Tag_DisableTag (void)
- {
- long TagCod = Tag_GetParamTagCode ();
-
- /***** Change tag status to disabled *****/
- Tst_EnableOrDisableTag (TagCod,true);
-
- /***** Show again the form to edit tags *****/
- Tag_ShowFormEditTags ();
- }
-
-/*****************************************************************************/
-/************************* Get parameter with tag code ***********************/
-/*****************************************************************************/
-
-static long Tag_GetParamTagCode (void)
- {
- long TagCod;
-
- /***** Get tag code *****/
- if ((TagCod = Par_GetParToLong ("TagCod")) <= 0)
- Lay_ShowErrorAndExit ("Code of tag is missing.");
-
- return TagCod;
- }
-
-/*****************************************************************************/
-/************************ Rename a tag of test questions *********************/
-/*****************************************************************************/
-
-void Tag_RenameTag (void)
- {
- extern const char *Txt_The_tag_X_has_been_renamed_as_Y;
- extern const char *Txt_The_tag_X_has_not_changed;
- char OldTagTxt[Tst_MAX_BYTES_TAG + 1];
- char NewTagTxt[Tst_MAX_BYTES_TAG + 1];
- long ExistingTagCod;
- long OldTagCod;
- bool ComplexRenaming;
-
- /***** Get old and new tags from the form *****/
- Par_GetParToText ("OldTagTxt",OldTagTxt,Tst_MAX_BYTES_TAG);
- Par_GetParToText ("NewTagTxt",NewTagTxt,Tst_MAX_BYTES_TAG);
-
- /***** Check that the new tag is not empty *****/
- if (NewTagTxt[0]) // New tag not empty
- {
- /***** Check if the old tag is equal to the new one *****/
- if (!strcmp (OldTagTxt,NewTagTxt)) // The old and the new tag
- // are exactly the same (case sensitively).
- // This happens when user press INTRO
- // without changing anything in the form.
- Ale_ShowAlert (Ale_INFO,Txt_The_tag_X_has_not_changed,
- NewTagTxt);
- else // The old and the new tag
- // are not exactly the same (case sensitively).
- {
- /***** Check if renaming is complex or easy *****/
- ComplexRenaming = false;
- if (strcasecmp (OldTagTxt,NewTagTxt)) // The old and the new tag
- // are not the same (case insensitively)
- /* Check if the new tag text is equal to any of the tags
- already present in the database */
- if ((ExistingTagCod = Tst_GetTagCodFromTagTxt (NewTagTxt)) > 0)
- // The new tag was already in database
- ComplexRenaming = true;
-
- if (ComplexRenaming) // Renaming is not easy
- {
- /***** Complex update made to not repeat tags:
- - If the new tag existed for a question ==>
- delete old tag from tst_question_tags;
- the new tag will remain
- - If the new tag did not exist for a question ==>
- change old tag to new tag in tst_question_tags *****/
- /* Get tag code of the old tag */
- if ((OldTagCod = Tst_GetTagCodFromTagTxt (OldTagTxt)) < 0)
- Lay_ShowErrorAndExit ("Tag does not exists.");
-
- /* Create a temporary table with all the question codes
- that had the new tag as one of their tags */
- DB_Query ("can not remove temporary table",
- "DROP TEMPORARY TABLE IF EXISTS tst_question_tags_tmp");
-
- DB_Query ("can not create temporary table",
- "CREATE TEMPORARY TABLE tst_question_tags_tmp"
- " ENGINE=MEMORY"
- " SELECT QstCod FROM tst_question_tags"
- " WHERE TagCod=%ld",
- ExistingTagCod);
-
- /* Remove old tag in questions where it would be repeated */
- // New tag existed for a question ==> delete old tag
- DB_QueryDELETE ("can not remove a tag from some questions",
- "DELETE FROM tst_question_tags"
- " WHERE TagCod=%ld"
- " AND QstCod IN"
- " (SELECT QstCod FROM tst_question_tags_tmp)",
- OldTagCod);
-
- /* Change old tag to new tag in questions where it would not be repeated */
- // New tag did not exist for a question ==> change old tag to new tag
- DB_QueryUPDATE ("can not update a tag in some questions",
- "UPDATE tst_question_tags"
- " SET TagCod=%ld"
- " WHERE TagCod=%ld"
- " AND QstCod NOT IN"
- " (SELECT QstCod FROM tst_question_tags_tmp)",
- ExistingTagCod,
- OldTagCod);
-
- /* Drop temporary table, no longer necessary */
- DB_Query ("can not remove temporary table",
- "DROP TEMPORARY TABLE IF EXISTS tst_question_tags_tmp");
-
- /***** Delete old tag from tst_tags
- because it is not longer used *****/
- DB_QueryDELETE ("can not remove old tag",
- "DELETE FROM tst_tags WHERE TagCod=%ld",
- OldTagCod);
- }
- else // Renaming is easy
- {
- /***** Simple update replacing each instance of the old tag by the new tag *****/
- DB_QueryUPDATE ("can not update tag",
- "UPDATE tst_tags SET TagTxt='%s',ChangeTime=NOW()"
- " WHERE tst_tags.CrsCod=%ld"
- " AND tst_tags.TagTxt='%s'",
- NewTagTxt,Gbl.Hierarchy.Crs.CrsCod,OldTagTxt);
- }
-
- /***** Write message to show the change made *****/
- Ale_ShowAlert (Ale_SUCCESS,Txt_The_tag_X_has_been_renamed_as_Y,
- OldTagTxt,NewTagTxt);
- }
- }
- else // New tag empty
- Ale_ShowAlertYouCanNotLeaveFieldEmpty ();
-
- /***** Show again the form to edit tags *****/
- Tag_ShowFormEditTags ();
- }
-
/*****************************************************************************/
/*************** Get configuration of test for current course ****************/
/*****************************************************************************/
@@ -1747,270 +1531,11 @@ bool Tst_CheckIfCourseHaveTestsAndPluggableIsUnknown (void)
/***** Get if current course has tests from database *****/
if (TstCfg_GetConfigPluggable () == TstCfg_PLUGGABLE_UNKNOWN)
- return Tst_CheckIfCurrentCrsHasTestTags (); // Return true if course has tests
+ return Tag_CheckIfCurrentCrsHasTestTags (); // Return true if course has tests
return false; // Pluggable is not unknown
}
-/*****************************************************************************/
-/******************* Check if current course has test tags *******************/
-/*****************************************************************************/
-// Return the number of rows of the result
-
-static bool Tst_CheckIfCurrentCrsHasTestTags (void)
- {
- /***** Get available tags from database *****/
- return (DB_QueryCOUNT ("can not check if course has tags",
- "SELECT COUNT(*) FROM tst_tags"
- " WHERE CrsCod=%ld",
- Gbl.Hierarchy.Crs.CrsCod) != 0);
- }
-
-/*****************************************************************************/
-/********* Get all (enabled or disabled) test tags for this course ***********/
-/*****************************************************************************/
-// Return the number of rows of the result
-
-static unsigned Tst_GetAllTagsFromCurrentCrs (MYSQL_RES **mysql_res)
- {
- /***** Get available tags from database *****/
- return (unsigned) DB_QuerySELECT (mysql_res,"can not get available tags",
- "SELECT TagCod," // row[0]
- "TagTxt," // row[1]
- "TagHidden" // row[2]
- " FROM tst_tags"
- " WHERE CrsCod=%ld"
- " ORDER BY TagTxt",
- Gbl.Hierarchy.Crs.CrsCod);
- }
-
-/*****************************************************************************/
-/********************** Get enabled test tags for this course ****************/
-/*****************************************************************************/
-// Return the number of rows of the result
-
-static unsigned Tst_GetEnabledTagsFromThisCrs (MYSQL_RES **mysql_res)
- {
- /***** Get available not hidden tags from database *****/
- return (unsigned) DB_QuerySELECT (mysql_res,"can not get available enabled tags",
- "SELECT TagCod," // row[0]
- "TagTxt" // row[1]
- " FROM tst_tags"
- " WHERE CrsCod=%ld AND TagHidden='N'"
- " ORDER BY TagTxt",
- Gbl.Hierarchy.Crs.CrsCod);
- }
-
-/*****************************************************************************/
-/********************* Show a form to select test tags ***********************/
-/*****************************************************************************/
-
-static void Tag_ShowFormSelTags (const struct Tst_Tags *Tags,
- MYSQL_RES *mysql_res,
- bool ShowOnlyEnabledTags)
- {
- extern const char *The_ClassFormInBox[The_NUM_THEMES];
- extern const char *Txt_Tags;
- extern const char *Txt_All_tags;
- extern const char *Txt_Tag_not_allowed;
- extern const char *Txt_Tag_allowed;
- unsigned NumTag;
- MYSQL_ROW row;
- bool TagHidden = false;
- bool Checked;
- const char *Ptr;
- char TagText[Tst_MAX_BYTES_TAG + 1];
- /*
- row[0] TagCod
- row[1] TagTxt
- row[2] TagHidden
- */
- HTM_TR_Begin (NULL);
-
- /***** Label *****/
- HTM_TD_Begin ("class=\"RT %s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
- HTM_TxtF ("%s:",Txt_Tags);
- HTM_TD_End ();
-
- /***** Select all tags *****/
- HTM_TD_Begin ("class=\"LT\"");
-
- HTM_TABLE_BeginPadding (2);
- HTM_TR_Begin (NULL);
- if (!ShowOnlyEnabledTags)
- HTM_TD_Empty (1);
-
- HTM_TD_Begin ("class=\"LM\"");
- HTM_LABEL_Begin ("class=\"%s\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
- HTM_INPUT_CHECKBOX ("AllTags",HTM_DONT_SUBMIT_ON_CHANGE,
- "value=\"Y\"%s onclick=\"togglecheckChildren(this,'ChkTag');\"",
- Tags->All ? " checked=\"checked\"" :
- "");
- HTM_TxtF (" %s",Txt_All_tags);
- HTM_LABEL_End ();
- HTM_TD_End ();
-
- HTM_TR_End ();
-
- /***** Select tags one by one *****/
- for (NumTag = 1;
- NumTag <= Tags->Num;
- NumTag++)
- {
- row = mysql_fetch_row (mysql_res);
- HTM_TR_Begin (NULL);
-
- if (!ShowOnlyEnabledTags)
- {
- TagHidden = (row[2][0] == 'Y');
- HTM_TD_Begin ("class=\"LM\"");
- Ico_PutIconOff (TagHidden ? "eye-slash-red.svg" :
- "eye-green.svg",
- TagHidden ? Txt_Tag_not_allowed :
- Txt_Tag_allowed);
- HTM_TD_End ();
- }
-
- Checked = false;
- if (Tags->List)
- {
- Ptr = Tags->List;
- while (*Ptr)
- {
- Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tst_MAX_BYTES_TAG);
- if (!strcmp (row[1],TagText))
- {
- Checked = true;
- break;
- }
- }
- }
-
- HTM_TD_Begin ("class=\"LM\"");
- HTM_LABEL_Begin ("class=\"DAT\"");
- HTM_INPUT_CHECKBOX ("ChkTag",HTM_DONT_SUBMIT_ON_CHANGE,
- "value=\"%s\"%s onclick=\"checkParent(this,'AllTags');\"",
- row[1],
- Checked ? " checked=\"checked\"" :
- "");
- HTM_TxtF (" %s",row[1]);
- HTM_LABEL_End ();
- HTM_TD_End ();
-
- HTM_TR_End ();
- }
-
- HTM_TABLE_End ();
- HTM_TD_End ();
- HTM_TR_End ();
- }
-
-/*****************************************************************************/
-/************* Show a form to enable/disable and rename test tags ************/
-/*****************************************************************************/
-
-void Tag_ShowFormEditTags (void)
- {
- extern const char *Hlp_ASSESSMENT_Tests_configuring_tests;
- extern const char *Txt_No_test_questions;
- extern const char *Txt_Tags;
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned NumTags;
- unsigned NumTag;
- long TagCod;
-
- /***** Get current tags in current course *****/
- if ((NumTags = Tst_GetAllTagsFromCurrentCrs (&mysql_res)))
- {
- /***** Begin box and table *****/
- Box_BoxTableBegin (NULL,Txt_Tags,
- NULL,NULL,
- Hlp_ASSESSMENT_Tests_configuring_tests,Box_NOT_CLOSABLE,2);
-
- /***** Show tags *****/
- for (NumTag = 0;
- NumTag < NumTags;
- NumTag++)
- {
- row = mysql_fetch_row (mysql_res);
- /*
- row[0] TagCod
- row[1] TagTxt
- row[2] TagHidden
- */
- if ((TagCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
- Lay_ShowErrorAndExit ("Wrong code of tag.");
-
- HTM_TR_Begin (NULL);
-
- /* Form to enable / disable this tag */
- if (row[2][0] == 'Y') // Tag disabled
- Tag_PutIconEnable (TagCod,row[1]);
- else
- Tag_PutIconDisable (TagCod,row[1]);
-
- /* Form to rename this tag */
- HTM_TD_Begin ("class=\"LM\"");
- Frm_StartForm (ActRenTag);
- Par_PutHiddenParamString (NULL,"OldTagTxt",row[1]);
- HTM_INPUT_TEXT ("NewTagTxt",Tst_MAX_CHARS_TAG,row[1],
- HTM_SUBMIT_ON_CHANGE,
- "size=\"36\" required=\"required\"");
- Frm_EndForm ();
- HTM_TD_End ();
-
- HTM_TR_End ();
- }
-
- /***** End table and box *****/
- Box_BoxTableEnd ();
- }
- else
- Ale_ShowAlert (Ale_INFO,Txt_No_test_questions);
-
- /* Free structure that stores the query result */
- DB_FreeMySQLResult (&mysql_res);
- }
-
-/*****************************************************************************/
-/******************* Put a link and an icon to enable a tag ******************/
-/*****************************************************************************/
-
-static void Tag_PutIconEnable (long TagCod,const char *TagTxt)
- {
- extern const char *Txt_Tag_X_not_allowed_Click_to_allow_it;
-
- HTM_TD_Begin ("class=\"BM\"");
- Frm_StartForm (ActEnaTag);
- Par_PutHiddenParamLong (NULL,"TagCod",TagCod);
- Ico_PutIconLink ("eye-slash-red.svg",
- Str_BuildStringStr (Txt_Tag_X_not_allowed_Click_to_allow_it,
- TagTxt));
- Str_FreeString ();
- Frm_EndForm ();
- HTM_TD_End ();
- }
-
-/*****************************************************************************/
-/****************** Put a link and an icon to disable a tag ******************/
-/*****************************************************************************/
-
-static void Tag_PutIconDisable (long TagCod,const char *TagTxt)
- {
- extern const char *Txt_Tag_X_allowed_Click_to_disable_it;
-
- HTM_TD_Begin ("class=\"BM\"");
- Frm_StartForm (ActDisTag);
- Par_PutHiddenParamLong (NULL,"TagCod",TagCod);
- Ico_PutIconLink ("eye-green.svg",
- Str_BuildStringStr (Txt_Tag_X_allowed_Click_to_disable_it,
- TagTxt));
- Str_FreeString ();
- Frm_EndForm ();
- HTM_TD_End ();
- }
-
/*****************************************************************************/
/********************* Show a form to to configure test **********************/
/*****************************************************************************/
@@ -2366,7 +1891,7 @@ static void Tst_GetQuestions (struct Tst_Test *Test,MYSQL_RES **mysql_res)
long LengthQuery;
unsigned NumItemInList;
const char *Ptr;
- char TagText[Tst_MAX_BYTES_TAG + 1];
+ char TagText[Tag_MAX_BYTES_TAG + 1];
char LongStr[Cns_MAX_DECIMAL_DIGITS_LONG + 1];
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
Tst_AnswerType_t AnsType;
@@ -2425,7 +1950,7 @@ static void Tst_GetQuestions (struct Tst_Test *Test,MYSQL_RES **mysql_res)
Ptr = Test->Tags.List;
while (*Ptr)
{
- Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tst_MAX_BYTES_TAG);
+ Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG);
LengthQuery = LengthQuery + 35 + strlen (TagText) + 1;
if (LengthQuery > Tst_MAX_BYTES_QUERY_TEST - 256)
Lay_ShowErrorAndExit ("Query size exceed.");
@@ -2451,7 +1976,7 @@ static void Tst_GetQuestions (struct Tst_Test *Test,MYSQL_RES **mysql_res)
Ptr = Test->AnswerTypes.List;
while (*Ptr)
{
- Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Tst_MAX_BYTES_TAG);
+ Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Tag_MAX_BYTES_TAG);
AnsType = Tst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr);
LengthQuery = LengthQuery + 35 + strlen (Tst_StrAnswerTypesDB[AnsType]) + 1;
if (LengthQuery > Tst_MAX_BYTES_QUERY_TEST - 256)
@@ -2525,7 +2050,7 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
long LengthQuery;
unsigned NumItemInList;
const char *Ptr;
- char TagText[Tst_MAX_BYTES_TAG + 1];
+ char TagText[Tag_MAX_BYTES_TAG + 1];
char UnsignedStr[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
Tst_AnswerType_t AnswerType;
bool Shuffle;
@@ -2572,7 +2097,7 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
Ptr = Test->Tags.List;
while (*Ptr)
{
- Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tst_MAX_BYTES_TAG);
+ Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG);
LengthQuery = LengthQuery + 35 + strlen (TagText) + 1;
if (LengthQuery > Tst_MAX_BYTES_QUERY_TEST - 128)
Lay_ShowErrorAndExit ("Query size exceed.");
@@ -2598,7 +2123,7 @@ static void Tst_GetQuestionsForNewTestFromDB (struct Tst_Test *Test,
Ptr = Test->AnswerTypes.List;
while (*Ptr)
{
- Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Tst_MAX_BYTES_TAG);
+ Par_GetNextStrUntilSeparParamMult (&Ptr,UnsignedStr,Tag_MAX_BYTES_TAG);
AnswerType = Tst_ConvertFromUnsignedStrToAnsTyp (UnsignedStr);
LengthQuery = LengthQuery + 35 + strlen (Tst_StrAnswerTypesDB[AnswerType]) + 1;
if (LengthQuery > Tst_MAX_BYTES_QUERY_TEST - 128)
@@ -3872,9 +3397,9 @@ static bool Tst_GetParamsTst (struct Tst_Test *Test,
Test->Tags.All = Par_GetParToBool ("AllTags");
/* Get the tags */
- if ((Test->Tags.List = (char *) malloc (Tst_MAX_BYTES_TAGS_LIST + 1)) == NULL)
+ if ((Test->Tags.List = (char *) malloc (Tag_MAX_BYTES_TAGS_LIST + 1)) == NULL)
Lay_NotEnoughMemoryExit ();
- Par_GetParMultiToText ("ChkTag",Test->Tags.List,Tst_MAX_BYTES_TAGS_LIST);
+ Par_GetParMultiToText ("ChkTag",Test->Tags.List,Tag_MAX_BYTES_TAGS_LIST);
/* Check number of tags selected */
if (Tst_CountNumTagsInList (&Test->Tags) == 0) // If no tags selected...
@@ -3982,17 +3507,17 @@ static unsigned Tst_GetParamNumQsts (void)
/***************** Count number of tags in the list of tags ******************/
/*****************************************************************************/
-static unsigned Tst_CountNumTagsInList (const struct Tst_Tags *Tags)
+static unsigned Tst_CountNumTagsInList (const struct Tag_Tags *Tags)
{
const char *Ptr;
unsigned NumTags = 0;
- char TagText[Tst_MAX_BYTES_TAG + 1];
+ char TagText[Tag_MAX_BYTES_TAG + 1];
/***** Go over the list of tags counting the number of tags *****/
Ptr = Tags->List;
while (*Ptr)
{
- Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tst_MAX_BYTES_TAG);
+ Par_GetNextStrUntilSeparParamMult (&Ptr,TagText,Tag_MAX_BYTES_TAG);
NumTags++;
}
@@ -4149,7 +3674,7 @@ static void Tst_PutFormEditOneQst (struct Tst_Question *Question)
HTM_TR_End ();
/***** Get tags already existing for questions in current course *****/
- NumTags = Tst_GetAllTagsFromCurrentCrs (&mysql_res);
+ NumTags = Tag_GetAllTagsFromCurrentCrs (&mysql_res);
/***** Write the tags *****/
HTM_TR_Begin (NULL);
@@ -4162,7 +3687,7 @@ static void Tst_PutFormEditOneQst (struct Tst_Question *Question)
HTM_TABLE_BeginPadding (2); // Table for tags
for (IndTag = 0;
- IndTag < Tst_MAX_TAGS_PER_QUESTION;
+ IndTag < Tag_MAX_TAGS_PER_QUESTION;
IndTag++)
{
HTM_TR_Begin (NULL);
@@ -4213,7 +3738,7 @@ static void Tst_PutFormEditOneQst (struct Tst_Question *Question)
snprintf (StrTagTxt,sizeof (StrTagTxt),
"TagTxt%u",
IndTag);
- HTM_INPUT_TEXT (StrTagTxt,Tst_MAX_CHARS_TAG,Question->Tags.Txt[IndTag],
+ HTM_INPUT_TEXT (StrTagTxt,Tag_MAX_CHARS_TAG,Question->Tags.Txt[IndTag],
HTM_DONT_SUBMIT_ON_CHANGE,
"id=\"%s\" class=\"TAG_TXT\" onchange=\"changeSelTag('%u')\"",
StrTagTxt,IndTag);
@@ -4545,7 +4070,7 @@ void Tst_QstConstructor (struct Tst_Question *Question)
unsigned NumOpt;
/***** Reset question tags *****/
- Tst_ResetTags (&Question->Tags);
+ Tag_ResetTags (&Question->Tags);
/***** Reset edition time *****/
Question->EditTime = (time_t) 0;
@@ -4820,7 +4345,7 @@ bool Tst_GetQstDataFromDB (struct Tst_Question *Question)
{
row = mysql_fetch_row (mysql_res);
Str_Copy (Question->Tags.Txt[NumRow],row[0],
- Tst_MAX_BYTES_TAG);
+ Tag_MAX_BYTES_TAG);
}
/* Free structure that stores the query result */
@@ -5072,18 +4597,18 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question)
/***** Get question tags *****/
for (NumTag = 0;
- NumTag < Tst_MAX_TAGS_PER_QUESTION;
+ NumTag < Tag_MAX_TAGS_PER_QUESTION;
NumTag++)
{
snprintf (TagStr,sizeof (TagStr),
"TagTxt%u",
NumTag);
- Par_GetParToText (TagStr,Question->Tags.Txt[NumTag],Tst_MAX_BYTES_TAG);
+ Par_GetParToText (TagStr,Question->Tags.Txt[NumTag],Tag_MAX_BYTES_TAG);
if (Question->Tags.Txt[NumTag][0])
{
Str_ChangeFormat (Str_FROM_FORM,Str_TO_TEXT,
- Question->Tags.Txt[NumTag],Tst_MAX_BYTES_TAG,true);
+ Question->Tags.Txt[NumTag],Tag_MAX_BYTES_TAG,true);
/* Check if not repeated */
for (NumTagRead = 0;
NumTagRead < NumTag;
@@ -5230,7 +4755,7 @@ static void Tst_GetQstFromForm (struct Tst_Question *Question)
/***** Adjust variables related to this test question *****/
for (NumTag = 0, Question->Tags.Num = 0;
- NumTag < Tst_MAX_TAGS_PER_QUESTION;
+ NumTag < Tag_MAX_TAGS_PER_QUESTION;
NumTag++)
if (Question->Tags.Txt[NumTag][0])
Question->Tags.Num++;
@@ -5580,75 +5105,6 @@ long Tst_GetIntAnsFromStr (char *Str)
return LongNum;
}
-/*****************************************************************************/
-/***************** Check if this tag exists for current course ***************/
-/*****************************************************************************/
-
-static long Tst_GetTagCodFromTagTxt (const char *TagTxt)
- {
- MYSQL_RES *mysql_res;
- MYSQL_ROW row;
- unsigned long NumRows;
- long TagCod = -1L; // -1 means that the tag does not exist in database
-
- /***** Get tag code from database *****/
- NumRows = DB_QuerySELECT (&mysql_res,"can not get tag",
- "SELECT TagCod FROM tst_tags"
- " WHERE CrsCod=%ld AND TagTxt='%s'",
- Gbl.Hierarchy.Crs.CrsCod,TagTxt);
- if (NumRows == 1)
- {
- /***** Get tag code *****/
- row = mysql_fetch_row (mysql_res);
- if ((TagCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
- Ale_CreateAlert (Ale_ERROR,NULL,
- "Wrong code of tag.");
- }
- else if (NumRows > 1)
- Ale_CreateAlert (Ale_ERROR,NULL,
- "Duplicated tag.");
-
- /***** Free structure that stores the query result *****/
- DB_FreeMySQLResult (&mysql_res);
-
- /***** Abort on error *****/
- if (Ale_GetTypeOfLastAlert () == Ale_ERROR)
- Ale_ShowAlertsAndExit ();
-
- return TagCod;
- }
-
-/*****************************************************************************/
-/********************* Insert new tag into tst_tags table ********************/
-/*****************************************************************************/
-
-static long Tst_CreateNewTag (long CrsCod,const char *TagTxt)
- {
- /***** Insert new tag into tst_tags table *****/
- return
- DB_QueryINSERTandReturnCode ("can not create new tag",
- "INSERT INTO tst_tags"
- " (CrsCod,ChangeTime,TagTxt,TagHidden)"
- " VALUES"
- " (%ld,NOW(),'%s','Y')", // Hidden by default
- CrsCod,TagTxt);
- }
-
-/*****************************************************************************/
-/********** Change visibility of an existing tag into tst_tags table *********/
-/*****************************************************************************/
-
-static void Tst_EnableOrDisableTag (long TagCod,bool TagHidden)
- {
- /***** Insert new tag into tst_tags table *****/
- DB_QueryUPDATE ("can not update the visibility of a tag",
- "UPDATE tst_tags SET TagHidden='%c',ChangeTime=NOW()"
- " WHERE TagCod=%ld AND CrsCod=%ld",
- TagHidden ? 'Y' :
- 'N',
- TagCod,Gbl.Hierarchy.Crs.CrsCod);
- }
-
/*****************************************************************************/
/***************** Request the removal of selected questions *****************/
/*****************************************************************************/
@@ -5878,8 +5334,8 @@ static void Tst_RemoveOneQstFromDB (long CrsCod,long QstCod)
/***** Remove the question from all the tables *****/
/* Remove answers and tags from this test question */
Tst_RemAnsFromQst (QstCod);
- Tst_RemTagsFromQst (QstCod);
- Tst_RemoveUnusedTagsFromCrs (CrsCod);
+ Tag_RemTagsFromQst (QstCod);
+ Tag_RemoveUnusedTagsFromCrs (CrsCod);
/* Remove the question itself */
DB_QueryDELETE ("can not remove a question",
@@ -5973,10 +5429,10 @@ void Tst_InsertOrUpdateQstTagsAnsIntoDB (struct Tst_Question *Question)
if (Question->QstCod > 0)
{
/***** Insert tags in the tags table *****/
- Tst_InsertTagsIntoDB (Question);
+ Tag_InsertTagsIntoDB (Question->QstCod,&Question->Tags);
/***** Remove unused tags in current course *****/
- Tst_RemoveUnusedTagsFromCrs (Gbl.Hierarchy.Crs.CrsCod);
+ Tag_RemoveUnusedTagsFromCrs (Gbl.Hierarchy.Crs.CrsCod);
/***** Insert answers in the answers table *****/
Tst_InsertAnswersIntoDB (Question);
@@ -6047,43 +5503,10 @@ static void Tst_InsertOrUpdateQstIntoDB (struct Tst_Question *Question)
/* Remove answers and tags from this test question */
Tst_RemAnsFromQst (Question->QstCod);
- Tst_RemTagsFromQst (Question->QstCod);
+ Tag_RemTagsFromQst (Question->QstCod);
}
}
-/*****************************************************************************/
-/*********************** Insert tags in the tags table ***********************/
-/*****************************************************************************/
-
-static void Tst_InsertTagsIntoDB (const struct Tst_Question *Question)
- {
- unsigned NumTag;
- unsigned TagIdx;
- long TagCod;
-
- /***** For each tag... *****/
- for (NumTag = 0, TagIdx = 0;
- TagIdx < Question->Tags.Num;
- NumTag++)
- if (Question->Tags.Txt[NumTag][0])
- {
- /***** Check if this tag exists for current course *****/
- if ((TagCod = Tst_GetTagCodFromTagTxt (Question->Tags.Txt[NumTag])) < 0)
- /* This tag is new for current course. Add it to tags table */
- TagCod = Tst_CreateNewTag (Gbl.Hierarchy.Crs.CrsCod,Question->Tags.Txt[NumTag]);
-
- /***** Insert tag in tst_question_tags *****/
- DB_QueryINSERT ("can not create tag",
- "INSERT INTO tst_question_tags"
- " (QstCod,TagCod,TagInd)"
- " VALUES"
- " (%ld,%ld,%u)",
- Question->QstCod,TagCod,TagIdx);
-
- TagIdx++;
- }
- }
-
/*****************************************************************************/
/******************* Insert answers in the answers table *********************/
/*****************************************************************************/
@@ -6240,36 +5663,6 @@ static void Tst_RemAnsFromQst (long QstCod)
QstCod);
}
-/*****************************************************************************/
-/************************** Remove tags from a test question *****************/
-/*****************************************************************************/
-
-static void Tst_RemTagsFromQst (long QstCod)
- {
- /***** Remove tags *****/
- DB_QueryDELETE ("can not remove the tags of a question",
- "DELETE FROM tst_question_tags WHERE QstCod=%ld",
- QstCod);
- }
-
-/*****************************************************************************/
-/********************** Remove unused tags in a course ***********************/
-/*****************************************************************************/
-
-static void Tst_RemoveUnusedTagsFromCrs (long CrsCod)
- {
- /***** Remove unused tags from tst_tags *****/
- DB_QueryDELETE ("can not remove unused tags",
- "DELETE FROM tst_tags"
- " WHERE CrsCod=%ld AND TagCod NOT IN"
- " (SELECT DISTINCT tst_question_tags.TagCod"
- " FROM tst_questions,tst_question_tags"
- " WHERE tst_questions.CrsCod=%ld"
- " AND tst_questions.QstCod=tst_question_tags.QstCod)",
- CrsCod,
- CrsCod);
- }
-
/*****************************************************************************/
/** Remove all media associated to stems of all test questions in a course ***/
/*****************************************************************************/
diff --git a/swad_test.h b/swad_test.h
index d8d4adc6c..d2fb1b827 100644
--- a/swad_test.h
+++ b/swad_test.h
@@ -66,7 +66,7 @@ typedef enum
struct Tst_Test
{
- struct Tst_Tags Tags; // Selected tags
+ struct Tag_Tags Tags; // Selected tags
struct Tst_AnswerTypes AnswerTypes; // Selected answer types
Tst_QuestionsOrder_t SelectedOrder; // Order for listing questions
unsigned NumQsts; // Number of questions
@@ -143,14 +143,9 @@ unsigned long Tst_GetTagsQst (long QstCod,MYSQL_RES **mysql_res);
void Tst_GetAndWriteTagsQst (long QstCod);
void Tst_ShowFormConfig (void);
-void Tag_EnableTag (void);
-void Tag_DisableTag (void);
-void Tag_RenameTag (void);
bool Tst_CheckIfCourseHaveTestsAndPluggableIsUnknown (void);
-void Tag_ShowFormEditTags (void);
-
unsigned Tst_CountNumQuestionsInList (const char *ListQuestions);
void Tst_ShowFormEditOneQst (void);
diff --git a/swad_test_import.c b/swad_test_import.c
index 3a586e930..78b613286 100644
--- a/swad_test_import.c
+++ b/swad_test_import.c
@@ -566,7 +566,7 @@ static void TsI_ImportQuestionsFromXMLBuffer (const char *XMLBuffer)
if (!strcmp (TagsElem->TagName,"tags"))
{
for (TagElem = TagsElem->FirstChild;
- TagElem != NULL && Question.Tags.Num < Tst_MAX_TAGS_PER_QUESTION;
+ TagElem != NULL && Question.Tags.Num < Tag_MAX_TAGS_PER_QUESTION;
TagElem = TagElem->NextBrother)
if (!strcmp (TagElem->TagName,"tag"))
{
@@ -574,7 +574,7 @@ static void TsI_ImportQuestionsFromXMLBuffer (const char *XMLBuffer)
{
Str_Copy (Question.Tags.Txt[Question.Tags.Num],
TagElem->Content,
- Tst_MAX_BYTES_TAG);
+ Tag_MAX_BYTES_TAG);
Question.Tags.Num++;
}
}
diff --git a/swad_test_type.h b/swad_test_type.h
index a8f083fc3..a6cef58b7 100644
--- a/swad_test_type.h
+++ b/swad_test_type.h
@@ -28,16 +28,12 @@
/*****************************************************************************/
#include "swad_media.h"
+#include "swad_tag.h"
/*****************************************************************************/
/***************************** Public constants ******************************/
/*****************************************************************************/
-#define Tst_MAX_TAGS_PER_QUESTION 5
-
-#define Tst_MAX_CHARS_TAG (128 - 1) // 127
-#define Tst_MAX_BYTES_TAG ((Tst_MAX_CHARS_TAG + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
-
#define Tst_MAX_BYTES_FLOAT_ANSWER 30 // Maximum length of the strings that store an floating point answer
#define Tst_MAX_OPTIONS_PER_QUESTION 10
@@ -53,14 +49,6 @@
/******************************* Public types ********************************/
/*****************************************************************************/
-struct Tst_Tags
- {
- unsigned Num;
- bool All;
- char *List;
- char Txt[Tst_MAX_TAGS_PER_QUESTION][Tst_MAX_BYTES_TAG + 1];
- };
-
#define Tst_NUM_ANS_TYPES 6
#define Tst_MAX_BYTES_LIST_ANSWER_TYPES (Tst_NUM_ANS_TYPES * (Cns_MAX_DECIMAL_DIGITS_UINT + 1))
typedef enum
@@ -77,7 +65,7 @@ typedef enum
struct Tst_Question
{
long QstCod;
- struct Tst_Tags Tags;
+ struct Tag_Tags Tags;
time_t EditTime;
char *Stem;
char *Feedback;