Version 21.64: Nov 28, 2021 New module swad_autolink to insert links in texts.

This commit is contained in:
acanas 2021-11-28 00:49:23 +01:00
parent 5517a2d430
commit c4ab84c2c7
27 changed files with 693 additions and 609 deletions

View File

@ -30,7 +30,7 @@ OBJS = swad_account.o swad_account_database.o swad_action.o swad_admin.o \
swad_admin_database.o swad_agenda.o swad_agenda_database.o swad_alert.o \
swad_announcement.o swad_announcement_database.o swad_API.o \
swad_API_database.o swad_assignment.o swad_assignment_database.o \
swad_attendance.o swad_attendance_database.o \
swad_attendance.o swad_attendance_database.o swad_autolink.o \
swad_banner.o swad_banner_database.o swad_box.o swad_browser.o \
swad_browser_database.o swad_building.o swad_building_database.o \
swad_button.o \

View File

@ -105,6 +105,7 @@ cp -f /home/acanas/swad/swad/swad /var/www/cgi-bin/
#include "swad_API.h"
#include "swad_API_database.h"
#include "swad_attendance_database.h"
#include "swad_autolink.h"
#include "swad_browser.h"
#include "swad_browser_database.h"
#include "swad_course_database.h"
@ -1426,7 +1427,7 @@ static int API_WritePlainTextIntoHTMLBuffer (struct soap *soap,
/* 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
ALn_InsertLinks (TxtHTML,Cns_MAX_BYTES_LONG_TEXT,60); // Insert links
/* Write text */
fprintf (FileHTMLTmp,"%s",TxtHTML);

View File

@ -29,6 +29,7 @@
#include <stddef.h> // For NULL
#include <string.h>
#include "swad_autolink.h"
#include "swad_call_for_exam_database.h"
#include "swad_changelog.h"
#include "swad_database.h"
@ -199,7 +200,7 @@ static void RSS_WriteNotices (FILE *FileRSS,struct Crs_Course *Crs)
/* Write full content of the notice */
Str_Copy (Content,row[3],sizeof (Content) - 1);
Str_InsertLinks (Content,Cns_MAX_BYTES_TEXT,40);
ALn_InsertLinks (Content,Cns_MAX_BYTES_TEXT,40);
fprintf (FileRSS,"<description><![CDATA[<p><em>%s %s %s:</em></p><p>%s</p>]]></description>\n",
UsrDat.FrstName,UsrDat.Surname1,UsrDat.Surname2,Content);

View File

@ -34,6 +34,7 @@
#include "swad_agenda.h"
#include "swad_agenda_database.h"
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_date.h"
@ -860,7 +861,7 @@ static void Agd_ShowOneEvent (struct Agd_Agenda *Agenda,
Agd_DB_GetEventTxt (&AgdEvent,Txt);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Txt,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to recpectful HTML
Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
ALn_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
HTM_Txt (Txt);
HTM_DIV_End ();
HTM_TD_End ();

View File

@ -27,6 +27,7 @@
#include "swad_announcement.h"
#include "swad_announcement_database.h"
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
@ -123,7 +124,7 @@ void Ann_ShowAllAnnouncements (void)
/* Get the subject (row[3]), the content (row[4]), and insert links */
Str_Copy (Subject,row[3],sizeof (Subject) - 1);
Str_Copy (Content,row[4],sizeof (Content) - 1);
Str_InsertLinks (Content,Cns_MAX_BYTES_TEXT,50);
ALn_InsertLinks (Content,Cns_MAX_BYTES_TEXT,50);
/* Show the announcement */
Ann_DrawAnAnnouncement (AnnCod,Status,Subject,Content,
@ -203,7 +204,7 @@ void Ann_ShowMyAnnouncementsNotMarkedAsSeen (void)
/* Get the subject (row[1]), the content (row[2]), and insert links */
Str_Copy (Subject,row[1],sizeof (Subject) - 1);
Str_Copy (Content,row[2],sizeof (Content) - 1);
Str_InsertLinks (Content,Cns_MAX_BYTES_TEXT,50);
ALn_InsertLinks (Content,Cns_MAX_BYTES_TEXT,50);
/* Show the announcement */
Ann_DrawAnAnnouncement (AnnCod,Ann_ACTIVE_ANNOUNCEMENT,Subject,Content,

View File

@ -34,6 +34,7 @@
#include "swad_assignment.h"
#include "swad_assignment_database.h"
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
@ -491,7 +492,7 @@ static void Asg_ShowOneAssignment (struct Asg_Assignments *Assignments,
Asg_DB_GetAssignmentTxtByCod (Asg.AsgCod,Txt);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Txt,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to recpectful HTML
Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
ALn_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
if (PrintView)
HTM_TD_Begin ("colspan=\"2\" class=\"LT\"");
else

View File

@ -35,6 +35,7 @@
#include "swad_attendance.h"
#include "swad_attendance_database.h"
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
@ -551,7 +552,7 @@ static void Att_ShowOneAttEvent (struct Att_Events *Events,
Att_DB_GetAttEventDescription (Event->AttCod,Description);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Description,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to recpectful HTML
Str_InsertLinks (Description,Cns_MAX_BYTES_TEXT,60); // Insert links
ALn_InsertLinks (Description,Cns_MAX_BYTES_TEXT,60); // Insert links
if (ShowOnlyThisAttEventComplete)
HTM_TD_Begin ("colspan=\"2\" class=\"LT\"");
else

611
swad_autolink.c Normal file
View File

@ -0,0 +1,611 @@
// swad_autolink.c: inserting automatic links in text
/*
SWAD (Shared Workspace At a Distance),
is a web platform developed at the University of Granada (Spain),
and used to support university teaching.
This file is part of SWAD core.
Copyright (C) 1999-2021 Antonio Cañas Vargas
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/
#define _GNU_SOURCE // For asprintf
#include <ctype.h> // For tolower
#include <stddef.h> // For NULL
#include <stdio.h> // For asprintf
#include <stdlib.h> // For malloc and free
#include <string.h> // For string functions
#include "swad_autolink.h"
#include "swad_config.h"
#include "swad_error.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_nickname.h"
#include "swad_nickname_database.h"
#include "swad_photo.h"
#include "swad_string.h"
#include "swad_user.h"
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
typedef enum
{
ALn_LINK_UNKNOWN = 0,
ALn_LINK_URL = 1,
ALn_LINK_NICK = 2,
} ALn_LinkType_t;
struct ALn_Substring
{
char *Str; // Pointer to the first char of substring
size_t Len; // Length of the substring
};
struct ALn_Link
{
ALn_LinkType_t Type;
struct ALn_Substring URLorNick;
struct
{
struct ALn_Substring Anchor1;
struct ALn_Substring Anchor2;
struct ALn_Substring Anchor3;
} Nick;
size_t AddedLengthUntilHere; // Total length of extra HTML code added until this link (included)
struct ALn_Link *Prev;
struct ALn_Link *Next;
};
/*****************************************************************************/
/******************** Global variables from other modules ********************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/*************************** Private prototypes ******************************/
/*****************************************************************************/
static void ALn_CreateFirstLink (struct ALn_Link **Link,
struct ALn_Link **LastLink);
static void ALn_CreateNextLink (struct ALn_Link **Link,
struct ALn_Link **LastLink);
static void ALn_FreeLinks (struct ALn_Link *LastLink);
static ALn_LinkType_t ALn_CheckURL (char **PtrSrc,char PrevCh,
struct ALn_Link **Link,
struct ALn_Link **LastLink,
size_t MaxCharsURLOnScreen);
static ALn_LinkType_t ALn_CheckNickname (char **PtrSrc,char PrevCh,
struct ALn_Link **Link,
struct ALn_Link **LastLink);
static void ALn_CopySubstring (const struct ALn_Substring *PtrSrc,char **PtrDst);
/*****************************************************************************/
/**************************** Private constants ******************************/
/*****************************************************************************/
// For URLs the length of anchor is fixed, so it can be calculated once
#define ALn_URL_ANCHOR_1 "<a href=\""
#define ALn_URL_ANCHOR_2 "\" target=\"_blank\">"
#define ALn_URL_ANCHOR_3 "</a>"
#define ALn_URL_ANCHOR_1_LENGTH (sizeof (ALn_URL_ANCHOR_1) - 1)
#define ALn_URL_ANCHOR_2_LENGTH (sizeof (ALn_URL_ANCHOR_2) - 1)
#define ALn_URL_ANCHOR_3_LENGTH (sizeof (ALn_URL_ANCHOR_3) - 1)
#define ALn_URL_ANCHOR_TOTAL_LENGTH (ALn_URL_ANCHOR_1_LENGTH + ALn_URL_ANCHOR_2_LENGTH + ALn_URL_ANCHOR_3_LENGTH)
static const struct ALn_Substring URLAnchor1 =
{
.Str = ALn_URL_ANCHOR_1,
.Len = ALn_URL_ANCHOR_1_LENGTH,
};
static const struct ALn_Substring URLAnchor2 =
{
.Str = ALn_URL_ANCHOR_2,
.Len = ALn_URL_ANCHOR_2_LENGTH,
};
static const struct ALn_Substring URLAnchor3 =
{
.Str = ALn_URL_ANCHOR_3,
.Len = ALn_URL_ANCHOR_3_LENGTH,
};
/*****************************************************************************/
/************** Insert automatic links in every URL or nickname **************/
/*****************************************************************************/
/*
Insertion example:
The web site of @rms is https://stallman.org/
The web site of <a href="https://openswad.org/?usr=@rms">@rms</a> is <a href="https://stallman.org/" target="_blank">https://stallman.org/</a>
*/
/*
<form id="form_z7FY4oFr-yot9R9xRVwDhIntnod3geLj36nyQ6jlJJs_53"
action="https://localhost/swad/es" method="post">
<input type="hidden" name="ses" value="2jb9CGhIJ81_qhDyeQ6MWDFKQ5ZaA_F68tq22ZAjYww">
<input type="hidden" name="usr" value="@acanas">
<a href="" onclick="document.getElementById('form_z7FY4oFr-yot9R9xRVwDhIntnod3geLj36nyQ6jlJJs_53').submit();return false;">
@acanas
</a>
</form>
*/
void ALn_InsertLinks (char *Txt,unsigned long MaxLength,size_t MaxCharsURLOnScreen)
{
size_t TxtLength;
char PrevCh = '\0';
char *PtrSrc;
char *PtrDst;
struct ALn_Link *Link;
struct ALn_Link *LastLink;
size_t Length;
size_t i;
struct ALn_Substring Limited; // URL displayed on screen (may be shorter than actual length)
const struct ALn_Substring *Anchor1;
const struct ALn_Substring *Anchor2;
const struct ALn_Substring *Anchor3;
/**************************************************************/
/***** Find starts and ends of links (URLs and nicknames) *****/
/**************************************************************/
ALn_CreateFirstLink (&Link,&LastLink);
for (PtrSrc = Txt;
*PtrSrc;)
/* Check if the next char is the start of a URL */
if ((Link->Type = ALn_CheckURL (&PtrSrc,PrevCh,
&Link,&LastLink,
MaxCharsURLOnScreen)) == ALn_LINK_UNKNOWN)
/* Check if the next char is the start of a nickname */
if ((Link->Type = ALn_CheckNickname (&PtrSrc,PrevCh,
&Link,&LastLink)) == ALn_LINK_UNKNOWN)
{
/* The next char is not the start of a URL or a nickname */
PrevCh = *PtrSrc;
PtrSrc++;
}
/**********************************************************************/
/***** If there are one or more links (URLs or nicknames) in text *****/
/**********************************************************************/
if (LastLink) // Not null ==> one or more links found
{
/***** Insert links from end to start of text,
only if there is enough space available in text *****/
TxtLength = strlen (Txt);
if (TxtLength + LastLink->AddedLengthUntilHere <= MaxLength)
for (Link = LastLink;
Link;
Link = Link->Prev)
{
/***** Set anchors *****/
switch (Link->Type)
{
case ALn_LINK_URL:
Anchor1 = &URLAnchor1;
Anchor2 = &URLAnchor2;
Anchor3 = &URLAnchor3;
break;
case ALn_LINK_NICK:
Anchor1 = &Link->Nick.Anchor1;
Anchor2 = &Link->Nick.Anchor2;
Anchor3 = &Link->Nick.Anchor3;
break;
default:
continue;
}
/***** Step 1: Move forward the text after the link (URL or nickname)
(it's mandatory to do the copy in reverse order
to avoid overwriting source) *****/
PtrSrc = (Link == LastLink) ? Txt + TxtLength :
Link->Next->URLorNick.Str - 1,
PtrDst = PtrSrc + Link->AddedLengthUntilHere,
Length = PtrSrc - (Link->URLorNick.Str + Link->URLorNick.Len - 1);
for (i = 0;
i < Length;
i++)
*PtrDst-- = *PtrSrc--;
/***** Step 2: Copy third part of anchor *****/
ALn_CopySubstring (Anchor3,&PtrDst);
/***** Step 3: Move forward the link (URL or nickname)
to be shown on screen *****/
switch (Link->Type)
{
case ALn_LINK_URL:
if (Link->URLorNick.Len <= MaxCharsURLOnScreen)
ALn_CopySubstring (&Link->URLorNick,&PtrDst);
else
{
/* Limit the length of URL */
if ((Limited.Str = malloc (Link->URLorNick.Len + 1)) == NULL)
Err_NotEnoughMemoryExit ();
strncpy (Limited.Str,Link->URLorNick.Str,Link->URLorNick.Len);
Limited.Str[Link->URLorNick.Len] = '\0';
Limited.Len = Str_LimitLengthHTMLStr (Limited.Str,MaxCharsURLOnScreen);
ALn_CopySubstring (&Limited,&PtrDst);
free (Limited.Str);
}
break;
case ALn_LINK_NICK:
ALn_CopySubstring (&Link->URLorNick,&PtrDst);
break;
default:
break;
}
/***** Step 4: Copy second part of anchor *****/
ALn_CopySubstring (Anchor2,&PtrDst);
/***** Step 5: Copy link into directive A
(it's mandatory to do the copy in reverse order
to avoid overwriting source URL or nickname) *****/
ALn_CopySubstring (&Link->URLorNick,&PtrDst);
/***** Step 6: Copy first part of anchor *****/
ALn_CopySubstring (Anchor1,&PtrDst);
}
}
/***********************************/
/***** Free memory for anchors *****/
/***********************************/
ALn_FreeLinks (LastLink);
}
/***************************** Create first link ******************************/
static void ALn_CreateFirstLink (struct ALn_Link **Link,
struct ALn_Link **LastLink)
{
/***** Reset last link pointer *****/
(*LastLink) = NULL;
/***** Allocate current link *****/
if (((*Link) = malloc (sizeof (struct ALn_Link))) == NULL)
Err_NotEnoughMemoryExit ();
/***** Initialize current link *****/
(*Link)->Prev = NULL;
(*Link)->Next = NULL;
}
/***************************** Create next link ******************************/
static void ALn_CreateNextLink (struct ALn_Link **Link,
struct ALn_Link **LastLink)
{
/***** Current link now is pointing to a correct link,
so set last link pointer to current link *****/
(*LastLink) = (*Link);
/***** Allocate next link *****/
if (((*Link)->Next = malloc (sizeof (struct ALn_Link))) == NULL)
Err_NotEnoughMemoryExit ();
/***** Initialize next link *****/
(*Link)->Next->Prev = *Link;
(*Link)->Next->Next = NULL;
/***** Change current link to just allocated link *****/
(*Link) = (*Link)->Next;
}
/***************************** Free found links ******************************/
static void ALn_FreeLinks (struct ALn_Link *LastLink)
{
struct ALn_Link *Link;
struct ALn_Link *PrevLink;
for (Link = LastLink;
Link;
Link = PrevLink)
{
PrevLink = Link->Prev;
if (Link->Type == ALn_LINK_NICK)
{
if (Link->Nick.Anchor3.Str) free (Link->Nick.Anchor3.Str);
if (Link->Nick.Anchor2.Str) free (Link->Nick.Anchor2.Str);
if (Link->Nick.Anchor1.Str) free (Link->Nick.Anchor1.Str);
}
free (Link);
}
}
/**************************** Check if a URL found ***************************/
static ALn_LinkType_t ALn_CheckURL (char **PtrSrc,char PrevCh,
struct ALn_Link **Link,
struct ALn_Link **LastLink,
size_t MaxCharsURLOnScreen)
{
unsigned char Ch;
size_t NumChars1;
size_t NumChars2;
char *PtrEnd; // Pointer to the last char of URL/nickname in original text
char *Limited; // URL displayed on screen (may be shorter than actual length)
ALn_LinkType_t Type = ALn_LINK_UNKNOWN;
/***** Check if the next char is the start of a URL *****/
if (tolower ((int) *(*PtrSrc)) == (int) 'h')
if (!Str_ChIsAlphaNum (PrevCh))
{
(*Link)->URLorNick.Str = (*PtrSrc);
if (tolower ((int) *++(*PtrSrc)) == (int) 't') // ht...
{
if (tolower ((int) *++(*PtrSrc)) == (int) 't') // htt...
{
if (tolower ((int) *++(*PtrSrc)) == (int) 'p') // http...
{
(*PtrSrc)++;
if (*(*PtrSrc) == ':') // http:...
{
if (*++(*PtrSrc) == '/') // http:/...
if (*++(*PtrSrc) == '/') // http://...
Type = ALn_LINK_URL;
}
else if (tolower ((int) *(*PtrSrc)) == (int) 's') // https...
{
if (*++(*PtrSrc) == ':') // https:...
{
if (*++(*PtrSrc) == '/') // https:/...
if (*++(*PtrSrc) == '/') // https://...
Type = ALn_LINK_URL;
}
}
}
}
}
if (Type == ALn_LINK_URL)
{
/***** Find URL end *****/
(*PtrSrc)++; // Points to first character after http:// or https://
for (;;)
{
NumChars1 = Str_GetNextASCIICharFromStr ((*PtrSrc),&Ch);
(*PtrSrc) += NumChars1;
if (Ch <= 32 || Ch == '<' || Ch == '"')
{
PtrEnd = (*PtrSrc) - NumChars1 - 1;
break;
}
else if (Ch == ',' || Ch == '.' || Ch == ';' || Ch == ':' ||
Ch == ')' || Ch == ']' || Ch == '}')
{
NumChars2 = Str_GetNextASCIICharFromStr ((*PtrSrc),&Ch);
(*PtrSrc) += NumChars2;
if (Ch <= 32 || Ch == '<' || Ch == '"')
{
PtrEnd = (*PtrSrc) - NumChars2 - NumChars1 - 1;
break;
}
}
}
/***** Compute number of bytes added until here *****/
(*Link)->AddedLengthUntilHere = (*Link)->Prev ? (*Link)->Prev->AddedLengthUntilHere :
0;
(*Link)->URLorNick.Len = (size_t) (PtrEnd + 1 - (*Link)->URLorNick.Str);
if ((*Link)->URLorNick.Len <= MaxCharsURLOnScreen)
(*Link)->AddedLengthUntilHere += ALn_URL_ANCHOR_TOTAL_LENGTH +
(*Link)->URLorNick.Len;
else // If URL is too long to be displayed ==> short it
{
if ((Limited = malloc ((*Link)->URLorNick.Len + 1)) == NULL)
Err_NotEnoughMemoryExit ();
strncpy (Limited,(*Link)->URLorNick.Str,(*Link)->URLorNick.Len);
Limited[(*Link)->URLorNick.Len] = '\0';
(*Link)->AddedLengthUntilHere += ALn_URL_ANCHOR_TOTAL_LENGTH +
Str_LimitLengthHTMLStr (Limited,MaxCharsURLOnScreen);
free (Limited);
}
/***** Create next link *****/
ALn_CreateNextLink (Link,LastLink);
}
}
return Type;
}
/************************* Check if a nickname found *************************/
static ALn_LinkType_t ALn_CheckNickname (char **PtrSrc,char PrevCh,
struct ALn_Link **Link,
struct ALn_Link **LastLink)
{
extern const char *Lan_STR_LANG_ID[1 + Lan_NUM_LANGUAGES];
char Ch;
size_t Length;
char ParamsStr[Frm_MAX_BYTES_PARAMS_STR];
struct UsrData UsrDat;
bool ShowPhoto = false;
char PhotoURL[PATH_MAX + 1];
char *CaptionStr;
char *ImgStr;
char NickWithoutArr[Nck_MAX_BYTES_NICK_WITHOUT_ARROBA + 1];
bool NickSeemsValid;
ALn_LinkType_t Type = ALn_LINK_UNKNOWN;
/***** Check if the next char is the start of a nickname *****/
Ch = *(*PtrSrc);
if (Ch == '@') // Current is @
if (!Str_ChIsAlphaNum (PrevCh)) // Previous is not alphanumeric
if (Str_ChIsAlphaNum (*((*PtrSrc) + 1))) // Next is alphanumeric
{
(*Link)->URLorNick.Str = (*PtrSrc);
/***** Find nickname end *****/
(*PtrSrc)++; // Points to first character after @
/***** A nick can have digits, letters and '_' *****/
for (;
*(*PtrSrc);
(*PtrSrc)++)
{
Ch = *(*PtrSrc);
if (!Str_ChIsAlphaNum (Ch))
break;
}
/***** Calculate length of this nickname *****/
(*Link)->URLorNick.Len = (size_t) ((*PtrSrc) - (*Link)->URLorNick.Str);
/***** A nick (without arroba) must have a number of characters
Nck_MIN_CHARS_NICK_WITHOUT_ARROBA <= Length <= Nck_MAX_CHARS_NICK_WITHOUT_ARROBA *****/
Length = (*Link)->URLorNick.Len - 1; // Do not count the initial @
NickSeemsValid = Length >= Nck_MIN_CHARS_NICK_WITHOUT_ARROBA &&
Length <= Nck_MAX_CHARS_NICK_WITHOUT_ARROBA;
if (NickSeemsValid)
{
/***** Get user's code using nickname *****/
Usr_UsrDataConstructor (&UsrDat);
strncpy (NickWithoutArr,(*Link)->URLorNick.Str + 1,Length);
NickWithoutArr[Length] = '\0';
if ((UsrDat.UsrCod = Nck_DB_GetUsrCodFromNickname (NickWithoutArr)) > 0)
{
Type = ALn_LINK_NICK;
Usr_GetUsrDataFromUsrCod (&UsrDat,
Usr_DONT_GET_PREFS,
Usr_DONT_GET_ROLE_IN_CURRENT_CRS);
}
if (Type == ALn_LINK_NICK)
{
/***** Reset anchors (checked on freeing) *****/
(*Link)->Nick.Anchor1.Str =
(*Link)->Nick.Anchor2.Str =
(*Link)->Nick.Anchor3.Str = NULL;
/***** Create id for this form *****/
Gbl.Form.Num++;
if (Gbl.Usrs.Me.Logged)
snprintf (Gbl.Form.UniqueId,sizeof (Gbl.Form.UniqueId),
"form_%s_%d",Gbl.UniqueNameEncrypted,Gbl.Form.Num);
else
snprintf (Gbl.Form.Id,sizeof (Gbl.Form.Id),
"form_%d",Gbl.Form.Num);
/***** Store first part of anchor *****/
Frm_SetParamsForm (ParamsStr,ActSeeOthPubPrf,true);
if (asprintf (&(*Link)->Nick.Anchor1.Str,
"<form method=\"post\" action=\"%s/%s\" id=\"%s\">"
"%s" // Parameters
"<input type=\"hidden\" name=\"usr\" value=\"",
Cfg_URL_SWAD_CGI,
Lan_STR_LANG_ID[Gbl.Prefs.Language],
Gbl.Usrs.Me.Logged ? Gbl.Form.UniqueId :
Gbl.Form.Id,
ParamsStr) < 0)
Err_NotEnoughMemoryExit ();
(*Link)->Nick.Anchor1.Len = strlen ((*Link)->Nick.Anchor1.Str);
/***** Store second part of anchor *****/
if (asprintf (&(*Link)->Nick.Anchor2.Str,
"\">"
"<a href=\"\""
" onclick=\"document.getElementById('%s').submit();return false;\">",
Gbl.Usrs.Me.Logged ? Gbl.Form.UniqueId :
Gbl.Form.Id) < 0)
Err_NotEnoughMemoryExit ();
(*Link)->Nick.Anchor2.Len = strlen ((*Link)->Nick.Anchor2.Str);
/***** Store third part of anchor *****/
ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (&UsrDat,PhotoURL);
Pho_BuildHTMLUsrPhoto (&UsrDat,ShowPhoto ? PhotoURL :
NULL,
"PHOTO12x16",Pho_ZOOM,
&CaptionStr,
&ImgStr);
if (asprintf (&(*Link)->Nick.Anchor3.Str,
"</a></form>%s%s",
CaptionStr,
ImgStr) < 0)
Err_NotEnoughMemoryExit ();
free (ImgStr);
free (CaptionStr);
(*Link)->Nick.Anchor3.Len = strlen ((*Link)->Nick.Anchor3.Str);
/***** Free memory used for user's data *****/
Usr_UsrDataDestructor (&UsrDat);
/***** Compute number of bytes added until here *****/
(*Link)->AddedLengthUntilHere = (*Link)->Prev ? (*Link)->Prev->AddedLengthUntilHere :
0;
(*Link)->AddedLengthUntilHere += (*Link)->Nick.Anchor1.Len +
(*Link)->URLorNick.Len +
(*Link)->Nick.Anchor2.Len +
(*Link)->Nick.Anchor3.Len;
/***** Create next link *****/
ALn_CreateNextLink (Link,LastLink);
}
}
}
return Type;
}
/************** Copy source substring to destination, backwards **************/
static void ALn_CopySubstring (const struct ALn_Substring *Src,char **Dst)
{
char *PtrSrc; // Local pointer optimizes access to memory
char *PtrDst; // Local pointer optimizes access to memory
size_t Len = Src->Len;
/*
Example: Src->Str = "<a href=\""
Src->Len = 9
Src->Str
|
_v_________________
|<|a|_|h|r|e|f|=|"|
| | | | | | | | |
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
| | | | | | | | |
_______v_v_v_v_v_v_v_v_v_____________
|_|_|_|<|a|_|h|r|e|f|=|"|_|_|_|_|_|_|
^ ^
| |
PtrDst (*Dst)
The copy has to be done backwards to avoid overwriting the original string
*/
if (Len)
{
PtrSrc = Src->Str + Len - 1;
PtrDst = *Dst; // Make a local copy of destination pointer
for (;
Len;
Len--)
*PtrDst-- = *PtrSrc--;
*Dst = PtrDst; // Update destination pointer
}
}

32
swad_autolink.h Normal file
View File

@ -0,0 +1,32 @@
// swad_autolink.h: inserting automatic links in text
#ifndef _SWAD_ALN
#define _SWAD_ALN
/*
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-2021 Antonio Cañas Vargas
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/***************************** Public prototypes ****************************/
/*****************************************************************************/
void ALn_InsertLinks (char *Txt,unsigned long MaxLength,size_t MaxCharsURLOnScreen);
#endif

View File

@ -602,13 +602,14 @@ TODO: FIX BUG, URGENT! En las fechas como par
TODO: En las encuestas, que los estudiantes no puedan ver los resultados hasta que no finalice el plazo.
*/
#define Log_PLATFORM_VERSION "SWAD 21.63 (2021-11-26)"
#define Log_PLATFORM_VERSION "SWAD 21.64 (2021-11-28)"
#define CSS_FILE "swad21.59.css"
#define JS_FILE "swad21.59.js"
/*
TODO: Rename CENTRE to CENTER in help wiki.
TODO: Rename ASSESSMENT.Announcements to ASSESSMENT.Calls_for_exams
Version 21.64: Nov 28, 2021 New module swad_autolink to insert links in texts. (320076 lines)
Version 21.63: Nov 26, 2021 Fixing of corruption in test prints and match prints. (320010 lines)
Version 21.62.3: Nov 25, 2021 Fixed bug in test questions. Reported by Javier Fernández Baldomero and Jesús González Peñalver. (319438 lines)
Version 21.62.2: Nov 24, 2021 Fixed bug in forums. Reported by Javier Fernández Baldomero. (319422 lines)

View File

@ -33,6 +33,7 @@
#include <stdlib.h> // For free
#include <string.h> // For string functions
#include "swad_autolink.h"
#include "swad_database.h"
#include "swad_error.h"
#include "swad_exam.h"
@ -610,7 +611,7 @@ static void Exa_ShowOneExam (struct Exa_Exams *Exams,
Exa_DB_GetExamTxt (Exam->ExaCod,Txt);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Txt,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to rigorous HTML
Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
ALn_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
HTM_DIV_Begin ("class=\"PAR %s\"",Exam->Hidden ? "DAT_LIGHT" :
"DAT");
HTM_Txt (Txt);

View File

@ -31,6 +31,7 @@
#include <stdio.h> // For asprintf
#include <string.h> // For string functions
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
@ -634,7 +635,7 @@ static void ExaPrn_GetAndWriteDescription (long ExaCod)
Exa_DB_GetExamTxt (ExaCod,Txt);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML, // Convert from HTML to rigorous HTML
Txt,Cns_MAX_BYTES_TEXT,false);
Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
ALn_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
/***** Write description *****/
HTM_DIV_Begin ("class=\"EXA_PRN_DESC DAT_SMALL\"");

View File

@ -33,6 +33,7 @@
#include <stdlib.h> // For free
#include <string.h> // For string functions
#include "swad_autolink.h"
#include "swad_database.h"
#include "swad_error.h"
#include "swad_figure.h"
@ -661,7 +662,7 @@ static void Gam_ShowOneGame (struct Gam_Games *Games,
Gam_DB_GetGameTxt (Game->GamCod,Txt);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Txt,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to rigorous HTML
Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
ALn_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
HTM_DIV_Begin ("class=\"PAR %s\"",Game->Hidden ? "DAT_LIGHT" :
"DAT");
HTM_Txt (Txt);

View File

@ -210,7 +210,7 @@ struct Globals
struct
{
unsigned Num; // Number of institutional links
struct Str_Link *Lst; // List of institutional links
struct Lnk_Link *Lst; // List of institutional links
} Links;
struct
{

View File

@ -34,6 +34,7 @@
#include <unistd.h> // For unlink
#include "swad_action.h"
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
@ -1499,7 +1500,7 @@ static bool Inf_CheckAndShowPlainTxt (void)
/***** 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
ALn_InsertLinks (TxtHTML,Cns_MAX_BYTES_LONG_TEXT,60); // Insert links
/***** Write text *****/
HTM_Txt (TxtHTML);

View File

@ -52,7 +52,7 @@ extern struct Globals Gbl;
/***************************** Private variables *****************************/
/*****************************************************************************/
static struct Str_Link *Lnk_EditingLnk = NULL; // Static variable to keep the link being edited
static struct Lnk_Link *Lnk_EditingLnk = NULL; // Static variable to keep the link being edited
/*****************************************************************************/
/***************************** Private prototypes ****************************/
@ -65,7 +65,7 @@ static void Lnk_WriteListOfLinks (void);
static void Lnk_EditLinksInternal (void);
static void Lnk_PutIconsEditingLinks (__attribute__((unused)) void *Args);
static void Lnk_GetDataOfLink (MYSQL_RES *mysql_res,struct Str_Link *Lnk);
static void Lnk_GetDataOfLink (MYSQL_RES *mysql_res,struct Lnk_Link *Lnk);
static void Lnk_ListLinksForEdition (void);
static void Lnk_PutParamLnkCod (void *LnkCod);
@ -309,7 +309,7 @@ void Lnk_GetListLinks (void)
/**************************** Get link full name *****************************/
/*****************************************************************************/
void Lnk_GetDataOfLinkByCod (struct Str_Link *Lnk)
void Lnk_GetDataOfLinkByCod (struct Lnk_Link *Lnk)
{
MYSQL_RES *mysql_res;
@ -334,7 +334,7 @@ void Lnk_GetDataOfLinkByCod (struct Str_Link *Lnk)
/**************************** Get data of link *******************************/
/*****************************************************************************/
static void Lnk_GetDataOfLink (MYSQL_RES *mysql_res,struct Str_Link *Lnk)
static void Lnk_GetDataOfLink (MYSQL_RES *mysql_res,struct Lnk_Link *Lnk)
{
MYSQL_ROW row;
@ -376,7 +376,7 @@ void Lnk_FreeListLinks (void)
static void Lnk_ListLinksForEdition (void)
{
unsigned NumLnk;
struct Str_Link *Lnk;
struct Lnk_Link *Lnk;
/***** Begin table *****/
HTM_TABLE_BeginWidePadding (2);

View File

@ -33,7 +33,7 @@
#define Lnk_MAX_CHARS_LINK_FULL_NAME (128 - 1) // 127
#define Lnk_MAX_BYTES_LINK_FULL_NAME ((Lnk_MAX_CHARS_LINK_FULL_NAME + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
struct Str_Link
struct Lnk_Link
{
long LnkCod;
char ShrtName[Lnk_MAX_BYTES_LINK_SHRT_NAME + 1];
@ -54,7 +54,7 @@ void Lnk_PutIconToViewLinks (void);
void Lnk_GetListLinks (void);
void Lnk_FreeListLinks (void);
void Lnk_GetDataOfLinkByCod (struct Str_Link *Lnk);
void Lnk_GetDataOfLinkByCod (struct Lnk_Link *Lnk);
long Lnk_GetParamLnkCod (void);
void Lnk_RemoveLink (void);
void Lnk_RenameLinkShort (void);

View File

@ -32,7 +32,7 @@
/**************************** Create a new link ******************************/
/*****************************************************************************/
void Lnk_DB_CreateLink (const struct Str_Link *Lnk)
void Lnk_DB_CreateLink (const struct Lnk_Link *Lnk)
{
DB_QueryINSERT ("can not create institutional link",
"INSERT INTO lnk_links"

View File

@ -35,7 +35,7 @@
/****************************** Public prototypes ****************************/
/*****************************************************************************/
void Lnk_DB_CreateLink (const struct Str_Link *Lnk);
void Lnk_DB_CreateLink (const struct Lnk_Link *Lnk);
void Lnk_DB_UpdateLnkName (long LnkCod,const char *FieldName,const char *NewLnkName);
void Lnk_DB_UpdateLnkWWW (long LnkCod,const char NewWWW[Cns_MAX_BYTES_WWW + 1]);

View File

@ -34,6 +34,7 @@
#include <time.h> // For time
#include "swad_action.h"
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_config.h"
#include "swad_course.h"
@ -2687,7 +2688,7 @@ void Msg_WriteMsgContent (char Content[Cns_MAX_BYTES_LONG_TEXT + 1],
{
/***** Insert links in URLs *****/
if (InsertLinks)
Str_InsertLinks (Content,Cns_MAX_BYTES_LONG_TEXT,60);
ALn_InsertLinks (Content,Cns_MAX_BYTES_LONG_TEXT,60);
/***** Write message to file *****/
if (ChangeBRToRet)

View File

@ -32,6 +32,7 @@
#include <stdlib.h> // For free
#include <string.h>
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
@ -380,7 +381,7 @@ void Not_ShowNotices (Not_Listing_t TypeNoticesListing,long HighlightNotCod)
Str_LimitLengthHTMLStr (Content,Not_MAX_CHARS_ON_NOTICE);
break;
case Not_LIST_FULL_NOTICES:
Str_InsertLinks (Content,Cns_MAX_BYTES_TEXT,
ALn_InsertLinks (Content,Cns_MAX_BYTES_TEXT,
Not_MaxCharsURLOnScreen[TypeNoticesListing]);
break;
}
@ -511,7 +512,7 @@ static void Not_GetDataAndShowNotice (long NotCod)
/* Get the content (row[2]) and insert links*/
Str_Copy (Content,row[2],sizeof (Content) - 1);
Str_InsertLinks (Content,Cns_MAX_BYTES_TEXT,
ALn_InsertLinks (Content,Cns_MAX_BYTES_TEXT,
Not_MaxCharsURLOnScreen[Not_LIST_FULL_NOTICES]);
/* Get status of the notice (row[3]) */

View File

@ -32,6 +32,7 @@
#include <stdlib.h> // For calloc
#include <string.h> // For string functions
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
@ -439,7 +440,7 @@ static void Prg_WriteRowItem (unsigned NumItem,struct Prg_Item *Item,
Prg_DB_GetItemTxt (Item->Hierarchy.ItmCod,Txt);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Txt,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to recpectful HTML
Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
ALn_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
HTM_DIV_Begin ("class=\"PAR PRG_TXT%s\"",
LightStyle ? "PRG_HIDDEN" :
"");

View File

@ -32,6 +32,7 @@
#include <stdlib.h> // For calloc
#include <string.h> // For string functions
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_browser_database.h"
#include "swad_database.h"
@ -1857,7 +1858,7 @@ static void Prj_ShowOneProjectTxtField (struct Prj_Project *Prj,
{
case Prj_LIST_PROJECTS:
case Prj_FILE_BROWSER_PROJECT:
Str_InsertLinks (TxtField,Cns_MAX_BYTES_TEXT,60); // Insert links
ALn_InsertLinks (TxtField,Cns_MAX_BYTES_TEXT,60); // Insert links
break;
default:
break;

View File

@ -1378,7 +1378,7 @@ unsigned Qst_DB_GetTextOfAnswers (MYSQL_RES **mysql_res,long QstCod)
}
/*****************************************************************************/
/********** Get answers correctness for a question in an exam set ************/
/*************** Get answers correctness for a question **********************/
/*****************************************************************************/
unsigned Qst_DB_GetQstAnswersCorr (MYSQL_RES **mysql_res,long QstCod)

View File

@ -29,79 +29,24 @@
#include <ctype.h> // For isprint, isspace, etc.
#include <locale.h> // For setlocale
#include <math.h> // For log10, floor, ceil, modf, sqrt...
#include <stddef.h> // For NULL
#include <stdio.h> // For asprintf
#include <stdlib.h> // For malloc and free
#include <string.h> // For string functions
#include "swad_error.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_ID.h"
#include "swad_nickname_database.h"
#include "swad_notification_database.h"
#include "swad_parameter.h"
#include "swad_photo.h"
#include "swad_string.h"
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
typedef enum
{
Str_LINK_UNKNOWN = 0,
Str_LINK_URL = 1,
Str_LINK_NICK = 2,
} Str_LinkType_t;
struct Str_Substring
{
char *Str; // Pointer to the first char of substring
size_t Len; // Length of the substring
};
struct Str_Link
{
Str_LinkType_t Type;
struct Str_Substring URLorNick;
struct
{
struct Str_Substring Anchor1;
struct Str_Substring Anchor2;
struct Str_Substring Anchor3;
} Nick;
size_t AddedLengthUntilHere; // Total length of extra HTML code added until this link (included)
struct Str_Link *Prev;
struct Str_Link *Next;
};
/*****************************************************************************/
/******************** Global variables from other modules ********************/
/*****************************************************************************/
extern struct Globals Gbl; // Declaration in swad.c
extern struct Globals Gbl;
/*****************************************************************************/
/*************************** Private prototypes ******************************/
/*****************************************************************************/
static void Str_CreateFirstLink (struct Str_Link **Link,
struct Str_Link **LastLink);
static void Str_CreateNextLink (struct Str_Link **Link,
struct Str_Link **LastLink);
static void Str_FreeLinks (struct Str_Link *LastLink);
static Str_LinkType_t Str_CheckURL (char **PtrSrc,char PrevCh,
struct Str_Link **Link,
struct Str_Link **LastLink,
size_t MaxCharsURLOnScreen);
static Str_LinkType_t Str_CheckNickname (char **PtrSrc,char PrevCh,
struct Str_Link **Link,
struct Str_Link **LastLink);
static void Str_CopySubstring (const struct Str_Substring *PtrSrc,char **PtrDst);
static unsigned Str_GetNextASCIICharFromStr (const char *Ptr,unsigned char *Ch);
static unsigned Str_FindHTMLEntity (const char *Ptr);
static int Str_ReadCharAndSkipComments (FILE *FileSrc,Str_SkipHTMLComments_t SkipHTMLComments);
@ -124,526 +69,6 @@ const char Str_BIN_TO_BASE64URL[64 + 1] =
static const char Str_LF[2] = {10,0};
static const char Str_CR[2] = {13,0};
/*****************************************************************************/
/****************** Insert a link in every URL or nickname *******************/
/*****************************************************************************/
/*
Insertion example:
The web site of @rms is https://stallman.org/
The web site of <a href="https://openswad.org/?usr=@rms">@rms</a> is <a href="https://stallman.org/" target="_blank">https://stallman.org/</a>
*/
/*
<form id="form_z7FY4oFr-yot9R9xRVwDhIntnod3geLj36nyQ6jlJJs_53"
action="https://localhost/swad/es" method="post">
<input type="hidden" name="ses" value="2jb9CGhIJ81_qhDyeQ6MWDFKQ5ZaA_F68tq22ZAjYww">
<input type="hidden" name="usr" value="@acanas">
<a href="" onclick="document.getElementById('form_z7FY4oFr-yot9R9xRVwDhIntnod3geLj36nyQ6jlJJs_53').submit();return false;">
@acanas
</a>
</form>
*/
// For URLs the length of anchor is fixed, so it can be calculated once
#define URL_ANCHOR_1 "<a href=\""
#define URL_ANCHOR_2 "\" target=\"_blank\">"
#define URL_ANCHOR_3 "</a>"
#define URL_ANCHOR_1_LENGTH (sizeof (URL_ANCHOR_1) - 1)
#define URL_ANCHOR_2_LENGTH (sizeof (URL_ANCHOR_2) - 1)
#define URL_ANCHOR_3_LENGTH (sizeof (URL_ANCHOR_3) - 1)
#define URL_ANCHOR_TOTAL_LENGTH (URL_ANCHOR_1_LENGTH + URL_ANCHOR_2_LENGTH + URL_ANCHOR_3_LENGTH)
// For nicknames the length of anchor is variable,
// so it can be calculated for each link,
// except the third part that is fixed
/*
#define NICK_ANCHOR_3 "</a></form>"
#define NICK_ANCHOR_3_LENGTH (sizeof (NICK_ANCHOR_3) - 1)
*/
static const struct Str_Substring URLAnchor1 =
{
.Str = URL_ANCHOR_1,
.Len = URL_ANCHOR_1_LENGTH,
};
static const struct Str_Substring URLAnchor2 =
{
.Str = URL_ANCHOR_2,
.Len = URL_ANCHOR_2_LENGTH,
};
static const struct Str_Substring URLAnchor3 =
{
.Str = URL_ANCHOR_3,
.Len = URL_ANCHOR_3_LENGTH,
};
/*
static const struct Str_Substring NickAnchor3 =
{
.Str = NICK_ANCHOR_3,
.Len = NICK_ANCHOR_3_LENGTH,
};
*/
void Str_InsertLinks (char *Txt,unsigned long MaxLength,size_t MaxCharsURLOnScreen)
{
size_t TxtLength;
char PrevCh = '\0';
char *PtrSrc;
char *PtrDst;
struct Str_Link *Link;
struct Str_Link *LastLink;
size_t Length;
size_t i;
struct Str_Substring Limited; // URL displayed on screen (may be shorter than actual length)
const struct Str_Substring *Anchor1;
const struct Str_Substring *Anchor2;
const struct Str_Substring *Anchor3;
/**************************************************************/
/***** Find starts and ends of links (URLs and nicknames) *****/
/**************************************************************/
Str_CreateFirstLink (&Link,&LastLink);
for (PtrSrc = Txt;
*PtrSrc;)
/* Check if the next char is the start of a URL */
if ((Link->Type = Str_CheckURL (&PtrSrc,PrevCh,
&Link,&LastLink,
MaxCharsURLOnScreen)) == Str_LINK_UNKNOWN)
/* Check if the next char is the start of a nickname */
if ((Link->Type = Str_CheckNickname (&PtrSrc,PrevCh,
&Link,&LastLink)) == Str_LINK_UNKNOWN)
{
/* The next char is not the start of a URL or a nickname */
PrevCh = *PtrSrc;
PtrSrc++;
}
/**********************************************************************/
/***** If there are one or more links (URLs or nicknames) in text *****/
/**********************************************************************/
if (LastLink) // Not null ==> one or more links found
{
/***** Insert links from end to start of text,
only if there is enough space available in text *****/
TxtLength = strlen (Txt);
if (TxtLength + LastLink->AddedLengthUntilHere <= MaxLength)
for (Link = LastLink;
Link;
Link = Link->Prev)
{
/***** Set anchors *****/
switch (Link->Type)
{
case Str_LINK_URL:
Anchor1 = &URLAnchor1;
Anchor2 = &URLAnchor2;
Anchor3 = &URLAnchor3;
break;
case Str_LINK_NICK:
Anchor1 = &Link->Nick.Anchor1;
Anchor2 = &Link->Nick.Anchor2;
Anchor3 = &Link->Nick.Anchor3;
break;
default:
continue;
}
/***** Step 1: Move forward the text after the link (URL or nickname)
(it's mandatory to do the copy in reverse order
to avoid overwriting source) *****/
PtrSrc = (Link == LastLink) ? Txt + TxtLength :
Link->Next->URLorNick.Str - 1,
PtrDst = PtrSrc + Link->AddedLengthUntilHere,
Length = PtrSrc - (Link->URLorNick.Str + Link->URLorNick.Len - 1);
for (i = 0;
i < Length;
i++)
*PtrDst-- = *PtrSrc--;
/***** Step 2: Copy third part of anchor *****/
Str_CopySubstring (Anchor3,&PtrDst);
/***** Step 3: Move forward the link (URL or nickname)
to be shown on screen *****/
switch (Link->Type)
{
case Str_LINK_URL:
if (Link->URLorNick.Len <= MaxCharsURLOnScreen)
Str_CopySubstring (&Link->URLorNick,&PtrDst);
else
{
/* Limit the length of URL */
if ((Limited.Str = malloc (Link->URLorNick.Len + 1)) == NULL)
Err_NotEnoughMemoryExit ();
strncpy (Limited.Str,Link->URLorNick.Str,Link->URLorNick.Len);
Limited.Str[Link->URLorNick.Len] = '\0';
Limited.Len = Str_LimitLengthHTMLStr (Limited.Str,MaxCharsURLOnScreen);
Str_CopySubstring (&Limited,&PtrDst);
free (Limited.Str);
}
break;
case Str_LINK_NICK:
Str_CopySubstring (&Link->URLorNick,&PtrDst);
break;
default:
break;
}
/***** Step 4: Copy second part of anchor *****/
Str_CopySubstring (Anchor2,&PtrDst);
/***** Step 5: Copy link into directive A
(it's mandatory to do the copy in reverse order
to avoid overwriting source URL or nickname) *****/
Str_CopySubstring (&Link->URLorNick,&PtrDst);
/***** Step 6: Copy first part of anchor *****/
Str_CopySubstring (Anchor1,&PtrDst);
}
}
/***********************************/
/***** Free memory for anchors *****/
/***********************************/
Str_FreeLinks (LastLink);
}
/***************************** Create first link ******************************/
static void Str_CreateFirstLink (struct Str_Link **Link,
struct Str_Link **LastLink)
{
/***** Reset last link pointer *****/
(*LastLink) = NULL;
/***** Allocate current link *****/
if (((*Link) = malloc (sizeof (struct Str_Link))) == NULL)
Err_NotEnoughMemoryExit ();
/***** Initialize current link *****/
(*Link)->Prev = NULL;
(*Link)->Next = NULL;
}
/***************************** Create next link ******************************/
static void Str_CreateNextLink (struct Str_Link **Link,
struct Str_Link **LastLink)
{
/***** Current link now is pointing to a correct link,
so set last link pointer to current link *****/
(*LastLink) = (*Link);
/***** Allocate next link *****/
if (((*Link)->Next = malloc (sizeof (struct Str_Link))) == NULL)
Err_NotEnoughMemoryExit ();
/***** Initialize next link *****/
(*Link)->Next->Prev = *Link;
(*Link)->Next->Next = NULL;
/***** Change current link to just allocated link *****/
(*Link) = (*Link)->Next;
}
/***************************** Free found links ******************************/
static void Str_FreeLinks (struct Str_Link *LastLink)
{
struct Str_Link *Link;
struct Str_Link *PrevLink;
for (Link = LastLink;
Link;
Link = PrevLink)
{
PrevLink = Link->Prev;
if (Link->Type == Str_LINK_NICK)
{
if (Link->Nick.Anchor3.Str) free (Link->Nick.Anchor3.Str);
if (Link->Nick.Anchor2.Str) free (Link->Nick.Anchor2.Str);
if (Link->Nick.Anchor1.Str) free (Link->Nick.Anchor1.Str);
}
free (Link);
}
}
/**************************** Check if a URL found ***************************/
static Str_LinkType_t Str_CheckURL (char **PtrSrc,char PrevCh,
struct Str_Link **Link,
struct Str_Link **LastLink,
size_t MaxCharsURLOnScreen)
{
unsigned char Ch;
size_t NumChars1;
size_t NumChars2;
char *PtrEnd; // Pointer to the last char of URL/nickname in original text
char *Limited; // URL displayed on screen (may be shorter than actual length)
Str_LinkType_t Type = Str_LINK_UNKNOWN;
/***** Check if the next char is the start of a URL *****/
if (tolower ((int) *(*PtrSrc)) == (int) 'h')
if (!Str_ChIsAlphaNum (PrevCh))
{
(*Link)->URLorNick.Str = (*PtrSrc);
if (tolower ((int) *++(*PtrSrc)) == (int) 't') // ht...
{
if (tolower ((int) *++(*PtrSrc)) == (int) 't') // htt...
{
if (tolower ((int) *++(*PtrSrc)) == (int) 'p') // http...
{
(*PtrSrc)++;
if (*(*PtrSrc) == ':') // http:...
{
if (*++(*PtrSrc) == '/') // http:/...
if (*++(*PtrSrc) == '/') // http://...
Type = Str_LINK_URL;
}
else if (tolower ((int) *(*PtrSrc)) == (int) 's') // https...
{
if (*++(*PtrSrc) == ':') // https:...
{
if (*++(*PtrSrc) == '/') // https:/...
if (*++(*PtrSrc) == '/') // https://...
Type = Str_LINK_URL;
}
}
}
}
}
if (Type == Str_LINK_URL)
{
/***** Find URL end *****/
(*PtrSrc)++; // Points to first character after http:// or https://
for (;;)
{
NumChars1 = Str_GetNextASCIICharFromStr ((*PtrSrc),&Ch);
(*PtrSrc) += NumChars1;
if (Ch <= 32 || Ch == '<' || Ch == '"')
{
PtrEnd = (*PtrSrc) - NumChars1 - 1;
break;
}
else if (Ch == ',' || Ch == '.' || Ch == ';' || Ch == ':' ||
Ch == ')' || Ch == ']' || Ch == '}')
{
NumChars2 = Str_GetNextASCIICharFromStr ((*PtrSrc),&Ch);
(*PtrSrc) += NumChars2;
if (Ch <= 32 || Ch == '<' || Ch == '"')
{
PtrEnd = (*PtrSrc) - NumChars2 - NumChars1 - 1;
break;
}
}
}
/***** Compute number of bytes added until here *****/
(*Link)->AddedLengthUntilHere = (*Link)->Prev ? (*Link)->Prev->AddedLengthUntilHere :
0;
(*Link)->URLorNick.Len = (size_t) (PtrEnd + 1 - (*Link)->URLorNick.Str);
if ((*Link)->URLorNick.Len <= MaxCharsURLOnScreen)
(*Link)->AddedLengthUntilHere += URL_ANCHOR_TOTAL_LENGTH +
(*Link)->URLorNick.Len;
else // If URL is too long to be displayed ==> short it
{
if ((Limited = malloc ((*Link)->URLorNick.Len + 1)) == NULL)
Err_NotEnoughMemoryExit ();
strncpy (Limited,(*Link)->URLorNick.Str,(*Link)->URLorNick.Len);
Limited[(*Link)->URLorNick.Len] = '\0';
(*Link)->AddedLengthUntilHere += URL_ANCHOR_TOTAL_LENGTH +
Str_LimitLengthHTMLStr (Limited,MaxCharsURLOnScreen);
free (Limited);
}
/***** Create next link *****/
Str_CreateNextLink (Link,LastLink);
}
}
return Type;
}
/************************* Check if a nickname found *************************/
static Str_LinkType_t Str_CheckNickname (char **PtrSrc,char PrevCh,
struct Str_Link **Link,
struct Str_Link **LastLink)
{
extern const char *Lan_STR_LANG_ID[1 + Lan_NUM_LANGUAGES];
char Ch;
size_t Length;
char ParamsStr[Frm_MAX_BYTES_PARAMS_STR];
struct UsrData UsrDat;
bool ShowPhoto = false;
char PhotoURL[PATH_MAX + 1];
char *CaptionStr;
char *ImgStr;
char NickWithoutArr[Nck_MAX_BYTES_NICK_WITHOUT_ARROBA + 1];
bool NickSeemsValid;
Str_LinkType_t Type = Str_LINK_UNKNOWN;
/***** Check if the next char is the start of a nickname *****/
Ch = *(*PtrSrc);
if (Ch == '@') // Current is @
if (!Str_ChIsAlphaNum (PrevCh)) // Previous is not alphanumeric
if (Str_ChIsAlphaNum (*((*PtrSrc) + 1))) // Next is alphanumeric
{
(*Link)->URLorNick.Str = (*PtrSrc);
/***** Find nickname end *****/
(*PtrSrc)++; // Points to first character after @
/***** A nick can have digits, letters and '_' *****/
for (;
*(*PtrSrc);
(*PtrSrc)++)
{
Ch = *(*PtrSrc);
if (!Str_ChIsAlphaNum (Ch))
break;
}
/***** Calculate length of this nickname *****/
(*Link)->URLorNick.Len = (size_t) ((*PtrSrc) - (*Link)->URLorNick.Str);
/***** A nick (without arroba) must have a number of characters
Nck_MIN_CHARS_NICK_WITHOUT_ARROBA <= Length <= Nck_MAX_CHARS_NICK_WITHOUT_ARROBA *****/
Length = (*Link)->URLorNick.Len - 1; // Do not count the initial @
NickSeemsValid = Length >= Nck_MIN_CHARS_NICK_WITHOUT_ARROBA &&
Length <= Nck_MAX_CHARS_NICK_WITHOUT_ARROBA;
if (NickSeemsValid)
{
/***** Get user's code using nickname *****/
Usr_UsrDataConstructor (&UsrDat);
strncpy (NickWithoutArr,(*Link)->URLorNick.Str + 1,Length);
NickWithoutArr[Length] = '\0';
if ((UsrDat.UsrCod = Nck_DB_GetUsrCodFromNickname (NickWithoutArr)) > 0)
{
Type = Str_LINK_NICK;
Usr_GetUsrDataFromUsrCod (&UsrDat,
Usr_DONT_GET_PREFS,
Usr_DONT_GET_ROLE_IN_CURRENT_CRS);
}
if (Type == Str_LINK_NICK)
{
/***** Reset anchors (checked on freeing) *****/
(*Link)->Nick.Anchor1.Str =
(*Link)->Nick.Anchor2.Str =
(*Link)->Nick.Anchor3.Str = NULL;
/***** Create id for this form *****/
Gbl.Form.Num++;
if (Gbl.Usrs.Me.Logged)
snprintf (Gbl.Form.UniqueId,sizeof (Gbl.Form.UniqueId),
"form_%s_%d",Gbl.UniqueNameEncrypted,Gbl.Form.Num);
else
snprintf (Gbl.Form.Id,sizeof (Gbl.Form.Id),
"form_%d",Gbl.Form.Num);
/***** Store first part of anchor *****/
Frm_SetParamsForm (ParamsStr,ActSeeOthPubPrf,true);
if (asprintf (&(*Link)->Nick.Anchor1.Str,
"<form method=\"post\" action=\"%s/%s\" id=\"%s\">"
"%s" // Parameters
"<input type=\"hidden\" name=\"usr\" value=\"",
Cfg_URL_SWAD_CGI,
Lan_STR_LANG_ID[Gbl.Prefs.Language],
Gbl.Usrs.Me.Logged ? Gbl.Form.UniqueId :
Gbl.Form.Id,
ParamsStr) < 0)
Err_NotEnoughMemoryExit ();
(*Link)->Nick.Anchor1.Len = strlen ((*Link)->Nick.Anchor1.Str);
/***** Store second part of anchor *****/
if (asprintf (&(*Link)->Nick.Anchor2.Str,
"\">"
"<a href=\"\""
" onclick=\"document.getElementById('%s').submit();return false;\">",
Gbl.Usrs.Me.Logged ? Gbl.Form.UniqueId :
Gbl.Form.Id) < 0)
Err_NotEnoughMemoryExit ();
(*Link)->Nick.Anchor2.Len = strlen ((*Link)->Nick.Anchor2.Str);
/***** Store third part of anchor *****/
ShowPhoto = Pho_ShowingUsrPhotoIsAllowed (&UsrDat,PhotoURL);
Pho_BuildHTMLUsrPhoto (&UsrDat,ShowPhoto ? PhotoURL :
NULL,
"PHOTO12x16",Pho_ZOOM,
&CaptionStr,
&ImgStr);
if (asprintf (&(*Link)->Nick.Anchor3.Str,
"</a></form>%s%s",
CaptionStr,
ImgStr) < 0)
Err_NotEnoughMemoryExit ();
free (ImgStr);
free (CaptionStr);
(*Link)->Nick.Anchor3.Len = strlen ((*Link)->Nick.Anchor3.Str);
/***** Free memory used for user's data *****/
Usr_UsrDataDestructor (&UsrDat);
/***** Compute number of bytes added until here *****/
(*Link)->AddedLengthUntilHere = (*Link)->Prev ? (*Link)->Prev->AddedLengthUntilHere :
0;
(*Link)->AddedLengthUntilHere += (*Link)->Nick.Anchor1.Len +
(*Link)->URLorNick.Len +
(*Link)->Nick.Anchor2.Len +
(*Link)->Nick.Anchor3.Len;
/***** Create next link *****/
Str_CreateNextLink (Link,LastLink);
}
}
}
return Type;
}
/************** Copy source substring to destination, backwards **************/
static void Str_CopySubstring (const struct Str_Substring *Src,char **Dst)
{
char *PtrSrc; // Local pointer optimizes access to memory
char *PtrDst; // Local pointer optimizes access to memory
size_t Len = Src->Len;
/*
Example: Src->Str = "<a href=\""
Src->Len = 9
Src->Str
|
_v_________________
|<|a|_|h|r|e|f|=|"|
| | | | | | | | |
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
\ \ \ \ \ \ \ \ \
| | | | | | | | |
_______v_v_v_v_v_v_v_v_v_____________
|_|_|_|<|a|_|h|r|e|f|=|"|_|_|_|_|_|_|
^ ^
| |
PtrDst (*Dst)
The copy has to be done backwards to avoid overwriting the original string
*/
if (Len)
{
PtrSrc = Src->Str + Len - 1;
PtrDst = *Dst; // Make a local copy of destination pointer
for (;
Len;
Len--)
*PtrDst-- = *PtrSrc--;
*Dst = PtrDst; // Update destination pointer
}
}
/*****************************************************************************/
/*********** Check if a character is in set { a-z, A-Z, 0-9, _ } *************/
/*****************************************************************************/
@ -661,7 +86,7 @@ bool Str_ChIsAlphaNum (char Ch)
/*****************************************************************************/
// Returns number of char analyzed
static unsigned Str_GetNextASCIICharFromStr (const char *Ptr,unsigned char *Ch)
unsigned Str_GetNextASCIICharFromStr (const char *Ptr,unsigned char *Ch)
{
unsigned NumChars;
unsigned Num;

View File

@ -82,10 +82,10 @@ typedef enum
/***************************** Public prototypes ****************************/
/*****************************************************************************/
void Str_InsertLinks (char *Txt,unsigned long MaxLength,size_t MaxCharsURLOnScreen);
bool Str_ChIsAlphaNum (char Ch);
unsigned Str_GetNextASCIICharFromStr (const char *Ptr,unsigned char *Ch);
size_t Str_LimitLengthHTMLStr (char *Str,size_t MaxCharsOnScreen);
void Str_AnalyzeTxtAndStoreNotifyEventToMentionedUsrs (long PubCod,const char *Txt);

View File

@ -33,6 +33,7 @@
#include <string.h> // For string functions
#include "swad_attendance.h"
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
@ -669,7 +670,7 @@ static void Svy_ShowOneSurvey (struct Svy_Surveys *Surveys,
Svy_DB_GetSurveyTxt (Svy.SvyCod,Txt);
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
Txt,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to rigorous HTML
Str_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
ALn_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
HTM_DIV_Begin ("class=\"PAR %s\"",Svy.Status.Visible ? "DAT" :
"DAT_LIGHT");
HTM_Txt (Txt);