diff --git a/css/swad18.68.3.css b/css/swad18.77.css similarity index 99% rename from css/swad18.68.3.css rename to css/swad18.77.css index 96b47402..00d2a00f 100644 --- a/css/swad18.68.3.css +++ b/css/swad18.77.css @@ -2719,32 +2719,22 @@ a:hover img.CENTRE_PHOTO_SHOW vertical-align:top; margin-bottom:10px; } -.MED_UPL_BUT /* Upload button _*/ +.MED_UPL_ICO_CON /* Upload icon container */ { - cursor:pointer; + display:inline-block; } .MED_UPL_ICO /* Upload icon */ { width:16px; height:16px; - margin:0 5px; + margin:5px 20px; vertical-align:middle; } -.MED_UPL_FIL /* Upload file */ - { - display:none; - } -.MED_UPL_NAM /* Upload filename */ - { - color:#808080; - font-size:12pt; - } .MED_PLAY { position:relative; } - /* The icon inside the overlay is positioned in the middle vertically and horizontally */ .MED_PLAY_ICO { @@ -2757,6 +2747,25 @@ a:hover img.CENTRE_PHOTO_SHOW color:white; text-shadow: 0 0 8px black; } + +/* For YouTube embedded videos. See https://avexdesigns.com/responsive-youtube-embed/ */ +.MED_VIDEO_CONT /* Adjust container height to get a 16:9 aspect ratio */ + { + position:relative; + box-sizing:border-box; + margin:10px 0; + padding-bottom:56.25%; /* percentage relative to width */ + } +.MED_VIDEO_CONT iframe, +.MED_VIDEO_CONT object, +.MED_VIDEO_CONT embed + { + position:absolute; + top:0; + left:0; + width:100%; + height:100%; + } /********** Author of assignments, attendance, messages, surveys... **********/ .AUTHOR_1_LINE diff --git a/icon/file-upload.svg b/icon/file-upload.svg new file mode 100644 index 00000000..464e57e9 --- /dev/null +++ b/icon/file-upload.svg @@ -0,0 +1 @@ + diff --git a/icon/paperclip.svg b/icon/paperclip.svg new file mode 100644 index 00000000..ffe6407b --- /dev/null +++ b/icon/paperclip.svg @@ -0,0 +1 @@ + diff --git a/js/swad18.75.js b/js/swad18.77.js similarity index 97% rename from js/swad18.75.js rename to js/swad18.77.js index b770d3a1..f2df976d 100644 --- a/js/swad18.75.js +++ b/js/swad18.77.js @@ -767,6 +767,40 @@ function AJAXCreateObject () { return obj; } +/*****************************************************************************/ +/****************************** Media uploader *******************************/ +/*****************************************************************************/ + +function mediaActivateUpload (id) { + if (document.getElementById (id + '_par_upl').disabled) { // Upload disabled + + document.getElementById (id + '_par_emb').disabled = true; // Disable embed + document.getElementById (id + '_par_upl').disabled = false; // Enable upload + + document.getElementById (id + '_ico_upl').style.opacity = '1'; // Highlight upload icon + document.getElementById (id + '_ico_emb').style.opacity = '0.2'; // Shadow embed icon + + document.getElementById (id + '_fil').style.display = ''; // Show input + document.getElementById (id + '_url').style.display = ''; // Show input + document.getElementById (id + '_tit').style.display = ''; // Show input + } +} + +function mediaActivateEmbed (id) { + if (document.getElementById (id + '_par_emb').disabled) { // Embed disabled + + document.getElementById (id + '_par_upl').disabled = true; // Disable upload + document.getElementById (id + '_par_emb').disabled = false; // Enable embed + + document.getElementById (id + '_ico_emb').style.opacity = '1'; // Highlight embed icon + document.getElementById (id + '_ico_upl').style.opacity = '0.2'; // Shadow upload icon + + document.getElementById (id + '_fil').style.display = 'none'; // Hide input + document.getElementById (id + '_url').style.display = ''; // Show input + document.getElementById (id + '_tit').style.display = 'none'; // Hide input + } +} + /*****************************************************************************/ /********************** Change display of animated GIF ***********************/ /*****************************************************************************/ @@ -1102,15 +1136,6 @@ function disableDetailedClicks () { document.getElementById('RowsPage').disabled = true; } -/*****************************************************************************/ -/************************** Upload images in a form **************************/ -/*****************************************************************************/ - -function mediaUploadOnSelectFile (inputFile,id) { - document.getElementById(id + '_fil').innerHTML = inputFile.value; // Display image filename - document.getElementById(id + '_tit_url').style.display = ''; // Show hidden fields -} - /*****************************************************************************/ /************************* Draw an academic calendar *************************/ /*****************************************************************************/ diff --git a/sql/swad.sql b/sql/swad.sql index 91d2442f..87812d78 100644 --- a/sql/swad.sql +++ b/sql/swad.sql @@ -560,7 +560,7 @@ CREATE TABLE IF NOT EXISTS forum_post ( Subject TEXT NOT NULL, Content LONGTEXT NOT NULL, MediaName VARCHAR(43) NOT NULL DEFAULT '', - MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none', + MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none', MediaTitle VARCHAR(2047) NOT NULL DEFAULT '', MediaURL VARCHAR(255) NOT NULL DEFAULT '', UNIQUE INDEX(PstCod), @@ -809,7 +809,7 @@ CREATE TABLE IF NOT EXISTS msg_content ( Subject TEXT NOT NULL, Content LONGTEXT NOT NULL, MediaName VARCHAR(43) NOT NULL DEFAULT '', - MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none', + MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none', MediaTitle VARCHAR(2047) NOT NULL DEFAULT '', MediaURL VARCHAR(255) NOT NULL DEFAULT '', UNIQUE INDEX(MsgCod), @@ -822,7 +822,7 @@ CREATE TABLE IF NOT EXISTS msg_content_deleted ( Subject TEXT NOT NULL, Content LONGTEXT NOT NULL, MediaName VARCHAR(43) NOT NULL DEFAULT '', - MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none', + MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none', MediaTitle VARCHAR(2047) NOT NULL DEFAULT '', MediaURL VARCHAR(255) NOT NULL DEFAULT '', UNIQUE INDEX(MsgCod), @@ -1026,7 +1026,7 @@ CREATE TABLE IF NOT EXISTS social_comments ( PubCod BIGINT NOT NULL, Content LONGTEXT NOT NULL, MediaName VARCHAR(43) NOT NULL DEFAULT '', - MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none', + MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none', MediaTitle VARCHAR(2047) NOT NULL DEFAULT '', MediaURL VARCHAR(255) NOT NULL DEFAULT '', UNIQUE INDEX(PubCod), @@ -1075,7 +1075,7 @@ CREATE TABLE IF NOT EXISTS social_posts ( PstCod INT NOT NULL AUTO_INCREMENT, Content LONGTEXT NOT NULL, MediaName VARCHAR(43) NOT NULL DEFAULT '', - MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none', + MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none', MediaTitle VARCHAR(2047) NOT NULL DEFAULT '', MediaURL VARCHAR(255) NOT NULL DEFAULT '', UNIQUE INDEX(PstCod), @@ -1204,7 +1204,7 @@ CREATE TABLE IF NOT EXISTS tst_answers ( Answer TEXT NOT NULL, Feedback TEXT NOT NULL, MediaName VARCHAR(43) NOT NULL DEFAULT '', - MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none', + MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none', MediaTitle VARCHAR(2047) NOT NULL DEFAULT '', MediaURL VARCHAR(255) NOT NULL DEFAULT '', Correct ENUM('N','Y') NOT NULL, @@ -1265,7 +1265,7 @@ CREATE TABLE IF NOT EXISTS tst_questions ( Stem TEXT NOT NULL, Feedback TEXT NOT NULL, MediaName VARCHAR(43) NOT NULL DEFAULT '', - MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none', + MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none', MediaTitle VARCHAR(2047) NOT NULL DEFAULT '', MediaURL VARCHAR(255) NOT NULL DEFAULT '', NumHits INT NOT NULL DEFAULT 0, diff --git a/swad_changelog.h b/swad_changelog.h index 85460e34..fce9afaa 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -450,10 +450,23 @@ En OpenSWAD: ps2pdf source.ps destination.pdf */ -#define Log_PLATFORM_VERSION "SWAD 18.76.2 (2019-03-14)" -#define CSS_FILE "swad18.68.3.css" -#define JS_FILE "swad18.75.js" +#define Log_PLATFORM_VERSION "SWAD 18.77 (2019-03-15)" +#define CSS_FILE "swad18.77.css" +#define JS_FILE "swad18.77.js" /* +TODO: Fix edition of media in test edition + Version 18.77: Mar 14, 2019 Embedded YouTube videos. Not finished. (240214 lines) + 7 changes necessary in database: +ALTER TABLE forum_post CHANGE COLUMN MediaType MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'; +ALTER TABLE msg_content CHANGE COLUMN MediaType MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'; +ALTER TABLE msg_content_deleted CHANGE COLUMN MediaType MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'; +ALTER TABLE social_comments CHANGE COLUMN MediaType MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'; +ALTER TABLE social_posts CHANGE COLUMN MediaType MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'; +ALTER TABLE tst_answers CHANGE COLUMN MediaType MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'; +ALTER TABLE tst_questions CHANGE COLUMN MediaType MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'; + Copy the following icon to icon public directory: +sudo cp icon/file-image.svg /var/www/html/swad/icon/ + Version 18.76.2: Mar 14, 2019 Changes in translation of several messages related to media. (239849 lines) Version 18.76.1: Mar 13, 2019 Changes in video tag. (239810 lines) Version 18.76: Mar 13, 2019 Video allowed as multimedia. (239814 lines) diff --git a/swad_cryptography.c b/swad_cryptography.c index 9921c7e4..da7fdbbe 100644 --- a/swad_cryptography.c +++ b/swad_cryptography.c @@ -51,7 +51,7 @@ /*****************************************************************************/ extern struct Globals Gbl; -extern const char Str_BIN_TO_BASE64URL[64]; +extern const char Str_BIN_TO_BASE64URL[64 + 1]; /*****************************************************************************/ /************************ Internal global variables **************************/ diff --git a/swad_database.c b/swad_database.c index 7a722440..768ad754 100644 --- a/swad_database.c +++ b/swad_database.c @@ -1207,22 +1207,22 @@ mysql> DESCRIBE forum_disabled_post; /***** Table forum_post *****/ /* mysql> DESCRIBE forum_post; -+------------+---------------------------------------------+------+-----+---------+----------------+ -| Field | Type | Null | Key | Default | Extra | -+------------+---------------------------------------------+------+-----+---------+----------------+ -| PstCod | int(11) | NO | PRI | NULL | auto_increment | -| ThrCod | int(11) | NO | MUL | NULL | | -| UsrCod | int(11) | NO | MUL | NULL | | -| CreatTime | datetime | NO | MUL | NULL | | -| ModifTime | datetime | NO | MUL | NULL | | -| NumNotif | int(11) | NO | | 0 | | -| Subject | text | NO | | NULL | | -| Content | longtext | NO | | NULL | | -| MediaName | varchar(43) | NO | | | | -| MediaType | enum('none','jpg','gif','mp4','webm','ogg') | NO | | none | | -| MediaTitle | varchar(2047) | NO | | | | -| MediaURL | varchar(255) | NO | | | | -+------------+---------------------------------------------+------+-----+---------+----------------+ ++------------+-------------------------------------------------------+------+-----+---------+----------------+ +| Field | Type | Null | Key | Default | Extra | ++------------+-------------------------------------------------------+------+-----+---------+----------------+ +| PstCod | int(11) | NO | PRI | NULL | auto_increment | +| ThrCod | int(11) | NO | MUL | NULL | | +| UsrCod | int(11) | NO | MUL | NULL | | +| CreatTime | datetime | NO | MUL | NULL | | +| ModifTime | datetime | NO | MUL | NULL | | +| NumNotif | int(11) | NO | | 0 | | +| Subject | text | NO | | NULL | | +| Content | longtext | NO | | NULL | | +| MediaName | varchar(43) | NO | | | | +| MediaType | enum('none','jpg','gif','mp4','webm','ogg','youtube') | NO | | none | | +| MediaTitle | varchar(2047) | NO | | | | +| MediaURL | varchar(255) | NO | | | | ++------------+-------------------------------------------------------+------+-----+---------+----------------+ 12 rows in set (0.00 sec) */ DB_CreateTable ("CREATE TABLE IF NOT EXISTS forum_post (" @@ -1235,7 +1235,7 @@ mysql> DESCRIBE forum_post; "Subject TEXT NOT NULL," // Cns_MAX_BYTES_SUBJECT "Content LONGTEXT NOT NULL," // Cns_MAX_BYTES_LONG_TEXT "MediaName VARCHAR(43) NOT NULL DEFAULT ''," // Med_BYTES_NAME - "MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none'," + "MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'," "MediaTitle VARCHAR(2047) NOT NULL DEFAULT ''," // Med_MAX_BYTES_TITLE "MediaURL VARCHAR(255) NOT NULL DEFAULT ''," // Cns_MAX_BYTES_WWW "UNIQUE INDEX(PstCod)," @@ -1733,17 +1733,17 @@ mysql> DESCRIBE msg_banned; /***** Table msg_content *****/ /* mysql> DESCRIBE msg_content; -+------------+---------------------------------------------+------+-----+---------+----------------+ -| Field | Type | Null | Key | Default | Extra | -+------------+---------------------------------------------+------+-----+---------+----------------+ -| MsgCod | int(11) | NO | PRI | NULL | auto_increment | -| Subject | text | NO | MUL | NULL | | -| Content | longtext | NO | | NULL | | -| MediaName | varchar(43) | NO | | | | -| MediaType | enum('none','jpg','gif','mp4','webm','ogg') | NO | | none | | -| MediaTitle | varchar(2047) | NO | | | | -| MediaURL | varchar(255) | NO | | | | -+------------+---------------------------------------------+------+-----+---------+----------------+ ++------------+-------------------------------------------------------+------+-----+---------+----------------+ +| Field | Type | Null | Key | Default | Extra | ++------------+-------------------------------------------------------+------+-----+---------+----------------+ +| MsgCod | int(11) | NO | PRI | NULL | auto_increment | +| Subject | text | NO | MUL | NULL | | +| Content | longtext | NO | | NULL | | +| MediaName | varchar(43) | NO | | | | +| MediaType | enum('none','jpg','gif','mp4','webm','ogg','youtube') | NO | | none | | +| MediaTitle | varchar(2047) | NO | | | | +| MediaURL | varchar(255) | NO | | | | ++------------+-------------------------------------------------------+------+-----+---------+----------------+ 7 rows in set (0.00 sec) */ DB_CreateTable ("CREATE TABLE IF NOT EXISTS msg_content (" @@ -1751,7 +1751,7 @@ mysql> DESCRIBE msg_content; "Subject TEXT NOT NULL," "Content LONGTEXT NOT NULL," "MediaName VARCHAR(43) NOT NULL DEFAULT ''," // Med_BYTES_NAME - "MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none'," + "MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'," "MediaTitle VARCHAR(2047) NOT NULL DEFAULT ''," // Med_MAX_BYTES_TITLE "MediaURL VARCHAR(255) NOT NULL DEFAULT ''," // Cns_MAX_BYTES_WWW "UNIQUE INDEX(MsgCod)," @@ -1760,17 +1760,17 @@ mysql> DESCRIBE msg_content; /***** Table msg_content_deleted *****/ /* mysql> DESCRIBE msg_content_deleted; -+------------+---------------------------------------------+------+-----+---------+-------+ -| Field | Type | Null | Key | Default | Extra | -+------------+---------------------------------------------+------+-----+---------+-------+ -| MsgCod | int(11) | NO | PRI | NULL | | -| Subject | text | NO | MUL | NULL | | -| Content | longtext | NO | | NULL | | -| MediaName | varchar(43) | NO | | | | -| MediaType | enum('none','jpg','gif','mp4','webm','ogg') | NO | | none | | -| MediaTitle | varchar(2047) | NO | | | | -| MediaURL | varchar(255) | NO | | | | -+------------+---------------------------------------------+------+-----+---------+-------+ ++------------+-------------------------------------------------------+------+-----+---------+-------+ +| Field | Type | Null | Key | Default | Extra | ++------------+-------------------------------------------------------+------+-----+---------+-------+ +| MsgCod | int(11) | NO | PRI | NULL | | +| Subject | text | NO | MUL | NULL | | +| Content | longtext | NO | | NULL | | +| MediaName | varchar(43) | NO | | | | +| MediaType | enum('none','jpg','gif','mp4','webm','ogg','youtube') | NO | | none | | +| MediaTitle | varchar(2047) | NO | | | | +| MediaURL | varchar(255) | NO | | | | ++------------+-------------------------------------------------------+------+-----+---------+-------+ 7 rows in set (0.00 sec) */ DB_CreateTable ("CREATE TABLE IF NOT EXISTS msg_content_deleted (" @@ -1778,7 +1778,7 @@ mysql> DESCRIBE msg_content_deleted; "Subject TEXT NOT NULL," "Content LONGTEXT NOT NULL," "MediaName VARCHAR(43) NOT NULL DEFAULT ''," // Med_BYTES_NAME - "MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none'," + "MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'," "MediaTitle VARCHAR(2047) NOT NULL DEFAULT ''," // Med_MAX_BYTES_TITLE "MediaURL VARCHAR(255) NOT NULL DEFAULT ''," // Cns_MAX_BYTES_WWW "UNIQUE INDEX(MsgCod)," @@ -2176,23 +2176,23 @@ mysql> DESCRIBE sessions; /***** Table social_comments *****/ /* mysql> DESCRIBE social_comments; -+------------+---------------------------------------------+------+-----+---------+-------+ -| Field | Type | Null | Key | Default | Extra | -+------------+---------------------------------------------+------+-----+---------+-------+ -| PubCod | bigint(20) | NO | PRI | NULL | | -| Content | longtext | NO | MUL | NULL | | -| MediaName | varchar(43) | NO | | | | -| MediaType | enum('none','jpg','gif','mp4','webm','ogg') | NO | | none | | -| MediaTitle | varchar(2047) | NO | | | | -| MediaURL | varchar(255) | NO | | | | -+------------+---------------------------------------------+------+-----+---------+-------+ ++------------+-------------------------------------------------------+------+-----+---------+-------+ +| Field | Type | Null | Key | Default | Extra | ++------------+-------------------------------------------------------+------+-----+---------+-------+ +| PubCod | bigint(20) | NO | PRI | NULL | | +| Content | longtext | NO | MUL | NULL | | +| MediaName | varchar(43) | NO | | | | +| MediaType | enum('none','jpg','gif','mp4','webm','ogg','youtube') | NO | | none | | +| MediaTitle | varchar(2047) | NO | | | | +| MediaURL | varchar(255) | NO | | | | ++------------+-------------------------------------------------------+------+-----+---------+-------+ 6 rows in set (0.00 sec) */ DB_CreateTable ("CREATE TABLE IF NOT EXISTS social_comments (" "PubCod BIGINT NOT NULL," "Content LONGTEXT NOT NULL," "MediaName VARCHAR(43) NOT NULL DEFAULT ''," // Med_BYTES_NAME - "MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none'," + "MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'," "MediaTitle VARCHAR(2047) NOT NULL DEFAULT ''," // Med_MAX_BYTES_TITLE "MediaURL VARCHAR(255) NOT NULL DEFAULT ''," // Cns_MAX_BYTES_WWW "UNIQUE INDEX(PubCod)," @@ -2274,23 +2274,23 @@ mysql> DESCRIBE social_notes_fav; /***** Table social_posts *****/ /* mysql> DESCRIBE social_posts; -+------------+---------------------------------------------+------+-----+---------+----------------+ -| Field | Type | Null | Key | Default | Extra | -+------------+---------------------------------------------+------+-----+---------+----------------+ -| PstCod | int(11) | NO | PRI | NULL | auto_increment | -| Content | longtext | NO | MUL | NULL | | -| MediaName | varchar(43) | NO | | | | -| MediaType | enum('none','jpg','gif','mp4','webm','ogg') | NO | | none | | -| MediaTitle | varchar(2047) | NO | | | | -| MediaURL | varchar(255) | NO | | | | -+------------+---------------------------------------------+------+-----+---------+----------------+ ++------------+-------------------------------------------------------+------+-----+---------+----------------+ +| Field | Type | Null | Key | Default | Extra | ++------------+-------------------------------------------------------+------+-----+---------+----------------+ +| PstCod | int(11) | NO | PRI | NULL | auto_increment | +| Content | longtext | NO | MUL | NULL | | +| MediaName | varchar(43) | NO | | | | +| MediaType | enum('none','jpg','gif','mp4','webm','ogg','youtube') | NO | | none | | +| MediaTitle | varchar(2047) | NO | | | | +| MediaURL | varchar(255) | NO | | | | ++------------+-------------------------------------------------------+------+-----+---------+----------------+ 6 rows in set (0.00 sec) */ DB_CreateTable ("CREATE TABLE IF NOT EXISTS social_posts (" "PubCod INT NOT NULL AUTO_INCREMENT," "Content LONGTEXT NOT NULL," "MediaName VARCHAR(43) NOT NULL DEFAULT ''," // Med_BYTES_NAME - "MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none'," + "MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'," "MediaTitle VARCHAR(2047) NOT NULL DEFAULT ''," // Med_MAX_BYTES_TITLE "MediaURL VARCHAR(255) NOT NULL DEFAULT ''," // Cns_MAX_BYTES_WWW "UNIQUE INDEX(PubCod)," @@ -2547,19 +2547,19 @@ mysql> DESCRIBE timetable_tut; /***** Table tst_answers *****/ /* mysql> DESCRIBE tst_answers; -+------------+---------------------------------------------+------+-----+---------+-------+ -| Field | Type | Null | Key | Default | Extra | -+------------+---------------------------------------------+------+-----+---------+-------+ -| QstCod | int(11) | NO | MUL | NULL | | -| AnsInd | tinyint(4) | NO | | NULL | | -| Answer | text | NO | | NULL | | -| Feedback | text | NO | | NULL | | -| MediaName | varchar(43) | NO | | | | -| MediaType | enum('none','jpg','gif','mp4','webm','ogg') | NO | | none | | -| MediaTitle | varchar(2047) | NO | | | | -| MediaURL | varchar(255) | NO | | | | -| Correct | enum('N','Y') | NO | | NULL | | -+------------+---------------------------------------------+------+-----+---------+-------+ ++------------+-------------------------------------------------------+------+-----+---------+-------+ +| Field | Type | Null | Key | Default | Extra | ++------------+-------------------------------------------------------+------+-----+---------+-------+ +| QstCod | int(11) | NO | MUL | NULL | | +| AnsInd | tinyint(4) | NO | | NULL | | +| Answer | text | NO | | NULL | | +| Feedback | text | NO | | NULL | | +| MediaName | varchar(43) | NO | | | | +| MediaType | enum('none','jpg','gif','mp4','webm','ogg','youtube') | NO | | none | | +| MediaTitle | varchar(2047) | NO | | | | +| MediaURL | varchar(255) | NO | | | | +| Correct | enum('N','Y') | NO | | NULL | | ++------------+-------------------------------------------------------+------+-----+---------+-------+ 9 rows in set (0.00 sec) */ DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_answers (" @@ -2568,7 +2568,7 @@ mysql> DESCRIBE tst_answers; "Answer TEXT NOT NULL," // Tst_MAX_BYTES_ANSWER_OR_FEEDBACK "Feedback TEXT NOT NULL," // Tst_MAX_BYTES_ANSWER_OR_FEEDBACK "MediaName VARCHAR(43) NOT NULL DEFAULT ''," // Med_BYTES_NAME - "MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none'," + "MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'," "MediaTitle VARCHAR(2047) NOT NULL DEFAULT ''," // Med_MAX_BYTES_TITLE "MediaURL VARCHAR(255) NOT NULL DEFAULT ''," // Cns_MAX_BYTES_WWW "Correct ENUM('N','Y') NOT NULL," @@ -2685,7 +2685,7 @@ mysql> DESCRIBE tst_questions; | Stem | text | NO | | NULL | | | Feedback | text | NO | | NULL | | | MediaName | varchar(43) | NO | | | | -| MediaType | enum('none','jpg','gif','mp4','webm','ogg') | NO | | none | | +| MediaType | enum('none','jpg','gif','mp4','webm','ogg','youtube') | NO | | none | | | MediaTitle | varchar(2047) | NO | | | | | MediaURL | varchar(255) | NO | | | | | NumHits | int(11) | NO | | 0 | | @@ -2703,7 +2703,7 @@ mysql> DESCRIBE tst_questions; "Stem TEXT NOT NULL," // Cns_MAX_BYTES_TEXT "Feedback TEXT NOT NULL," // Cns_MAX_BYTES_TEXT "MediaName VARCHAR(43) NOT NULL DEFAULT ''," // Med_BYTES_NAME - "MediaType ENUM('none','jpg','gif','mp4','webm','ogg') NOT NULL DEFAULT 'none'," + "MediaType ENUM('none','jpg','gif','mp4','webm','ogg','youtube') NOT NULL DEFAULT 'none'," "MediaTitle VARCHAR(2047) NOT NULL DEFAULT ''," // Med_MAX_BYTES_TITLE "MediaURL VARCHAR(255) NOT NULL DEFAULT ''," // Cns_MAX_BYTES_WWW "NumHits INT NOT NULL DEFAULT 0," diff --git a/swad_forum.c b/swad_forum.c index 1570bf7b..1abfc1a7 100644 --- a/swad_forum.c +++ b/swad_forum.c @@ -502,7 +502,7 @@ static long For_InsertForumPst (long ThrCod,long UsrCod, /***** Check if image is received and processed *****/ if (Media->Action == Med_ACTION_NEW_MEDIA && // Upload new image - Media->Status == Med_FILE_PROCESSED) // The new image received has been processed + Media->Status == Med_PROCESSED) // The new image received has been processed /* Move processed image to definitive directory */ Med_MoveMediaToDefinitiveDir (Media); diff --git a/swad_media.c b/swad_media.c index 3bbb7395..4ef8b6c2 100644 --- a/swad_media.c +++ b/swad_media.c @@ -25,8 +25,10 @@ /*********************************** Headers *********************************/ /*****************************************************************************/ +#define _GNU_SOURCE // For strcasestr, asprintf #include // For PATH_MAX #include // For boolean type +#include // For asprintf #include // For exit, system, malloc, free, etc #include // For string functions #include // For lstat @@ -57,6 +59,7 @@ const char *Med_StringsTypeDB[Med_NUM_TYPES] = "mp4", // Med_MP4 "webm", // Med_WEBM "ogg", // Med_OGG + "youtube", // Med_YOUTUBE }; const char *Med_Extensions[Med_NUM_TYPES] = @@ -67,6 +70,7 @@ const char *Med_Extensions[Med_NUM_TYPES] = "mp4", // Med_MP4 "webm", // Med_WEBM "ogg", // Med_OGG + "", // Med_YOUTUBE }; #define Med_MAX_SIZE_GIF (5UL * 1024UL * 1024UL) // 5 MiB @@ -76,6 +80,15 @@ const char *Med_Extensions[Med_NUM_TYPES] = /****************************** Internal types *******************************/ /*****************************************************************************/ +#define Med_NUM_FORM_TYPES 3 + +typedef enum + { + Med_FORM_UNKNOWN = 0, + Med_FORM_FILE = 1, + Med_FORM_EMBED = 2, + } Med_FormType_t; + /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ @@ -91,8 +104,11 @@ extern struct Globals Gbl; /*****************************************************************************/ static Med_Action_t Med_GetMediaActionFromForm (const char *ParamAction); -static void Med_GetAndProcessFileFromForm (struct Media *Media, - const char *ParamFile); +static Med_FormType_t Usr_GetFormTypeFromForm (void); +static void Usr_GetURLFromForm (const char *ParamName,struct Media *Media); +static void Usr_GetTitleFromForm (const char *ParamName,struct Media *Media); +static void Med_GetAndProcessFileFromForm (const char *ParamFile, + struct Media *Media); static bool Med_DetectIfAnimated (struct Media *Media, const char PathMedPrivTmp[PATH_MAX + 1], const char PathFileOrg[PATH_MAX + 1]); @@ -113,6 +129,9 @@ static int Med_ResizeImage (struct Media *Media, static int Med_GetFirstFrame (const char PathFileOriginal[PATH_MAX + 1], const char PathFileProcessed[PATH_MAX + 1]); +static void Med_GetAndProcessEmbedFromForm (const char *ParamURL, + struct Media *Media); + static bool Med_MoveTmpFileToDefDir (struct Media *Media, const char PathMedPrivTmp[PATH_MAX + 1], const char PathMedPriv[PATH_MAX + 1], @@ -127,6 +146,8 @@ static void Med_ShowGIF (struct Media *Media, static void Med_ShowVideo (struct Media *Media, const char PathMedPriv[PATH_MAX + 1], const char *ClassMedia); +static void Med_ShowYoutube (struct Media *Media, + const char *ClassMedia); static Med_Type_t Med_GetTypeFromExtAndMIME (const char *Extension, const char *MIMEType); @@ -151,9 +172,9 @@ void Med_MediaConstructor (struct Media *Media) void Med_ResetMediaExceptTitleAndURL (struct Media *Media) { Media->Action = Med_ACTION_NO_MEDIA; - Media->Status = Med_FILE_NONE; + Media->Status = Med_STATUS_NONE; Media->Name[0] = '\0'; - Media->Type = Med_NONE; + Media->Type = Med_TYPE_NONE; } /*****************************************************************************/ @@ -215,9 +236,25 @@ void Med_GetMediaDataFromRow (const char *Name, Media->Type = Med_GetTypeFromStrInDB (TypeStr); /***** Set status of media file *****/ - Media->Status = (Media->Name[0] && - Media->Type != Med_NONE) ? Med_NAME_STORED_IN_DB : - Med_FILE_NONE; + Media->Status = (Media->Type != Med_TYPE_NONE) ? Med_STORED_IN_DB : + Med_STATUS_NONE; + + /***** Copy image URL to struct *****/ + // Media->URL can be empty or filled with previous value + // If filled ==> free it + Med_FreeMediaURL (Media); + if (URL[0]) + { + /* Get and limit length of the URL */ + Length = strlen (URL); + if (Length > Cns_MAX_BYTES_WWW) + Length = Cns_MAX_BYTES_WWW; + + if ((Media->URL = (char *) malloc (Length + 1)) == NULL) + Lay_ShowErrorAndExit ("Error allocating memory for image URL."); + Str_Copy (Media->URL,URL, + Length); + } /***** Copy image title to struct *****/ // Media->Title can be empty or filled with previous value @@ -235,23 +272,6 @@ void Med_GetMediaDataFromRow (const char *Name, Str_Copy (Media->Title,Title, Length); } - - /***** Copy image URL to struct *****/ - // Media->URL can be empty or filled with previous value - // If filled ==> free it - Med_FreeMediaURL (Media); - if (URL[0]) - { - /* Get and limit length of the URL */ - Length = strlen (URL); - if (Length > Med_MAX_BYTES_TITLE) - Length = Med_MAX_BYTES_TITLE; - - if ((Media->URL = (char *) malloc (Length + 1)) == NULL) - Lay_ShowErrorAndExit ("Error allocating memory for image URL."); - Str_Copy (Media->URL,URL, - Length); - } } /*****************************************************************************/ @@ -261,7 +281,6 @@ void Med_GetMediaDataFromRow (const char *Name, void Med_PutMediaUploader (int NumMediaInForm,const char *ClassMediaTitURL) { extern const char *Txt_Image_video; - extern const char *Txt_optional; extern const char *Txt_Title_attribution; extern const char *Txt_Link; struct ParamUploadMedia ParamUploadMedia; @@ -274,48 +293,88 @@ void Med_PutMediaUploader (int NumMediaInForm,const char *ClassMediaTitURL) Frm_SetUniqueId (Id); /***** Start container *****/ - fprintf (Gbl.F.Out,"
"); + fprintf (Gbl.F.Out,"
"); // container /***** Action to perform on media *****/ Par_PutHiddenParamUnsigned (ParamUploadMedia.Action,(unsigned) Med_ACTION_NEW_MEDIA); - /***** Media file *****/ - fprintf (Gbl.F.Out,"", - Gbl.Prefs.URLIcons, - Txt_Image_video,Txt_Image_video,Txt_optional, - ParamUploadMedia.File, - Id,Id); - - /***** Media title/attribution and URL *****/ - fprintf (Gbl.F.Out,"
", + /***** Upload icon *****/ + fprintf (Gbl.F.Out,"
_ico_upl + " class=\"MED_UPL_ICO_CON\">" + "\"%s\"" + "
", // _ico_upl + Id, + Gbl.Prefs.URLIcons, + Txt_Image_video,Txt_Image_video, Id); - fprintf (Gbl.F.Out,"", - ParamUploadMedia.Title, - Txt_Title_attribution,Txt_optional, - ClassMediaTitURL,Med_MAX_CHARS_TITLE); - fprintf (Gbl.F.Out,"
" - "", + + /***** Form type *****/ + fprintf (Gbl.F.Out,"_par_upl + " name=\"FormType\" value=\"%u\"" + " disabled=\"disabled\" />", + Id, + (unsigned) Med_FORM_FILE); + + /***** Embed icon *****/ + fprintf (Gbl.F.Out,"
_ico_emb + " class=\"MED_UPL_ICO_CON\">" + "\"%s\"" + "
", // _ico_emb + Id, + Gbl.Prefs.URLIcons, + "Embed URL","Embed URL", + Id); + + /***** Form type *****/ + fprintf (Gbl.F.Out,"_par_emb + " name=\"FormType\" value=\"%u\"" + " disabled=\"disabled\" />", + Id, + (unsigned) Med_FORM_EMBED); + + /***** Media file *****/ + fprintf (Gbl.F.Out,"
_fil + " style=\"display:none;\">" + "" + "
", // _fil + Id, + ParamUploadMedia.File); + + /***** Media URL *****/ + fprintf (Gbl.F.Out,"
_url + " style=\"display:none;\">" + "" + "
", // _url + Id, ParamUploadMedia.URL, - Txt_Link,Txt_optional, + Txt_Link, ClassMediaTitURL,Cns_MAX_CHARS_WWW); - fprintf (Gbl.F.Out,"
"); + + /***** Media title *****/ + fprintf (Gbl.F.Out,"
_tit + " style=\"display:none;\">" + "", + Id, + ParamUploadMedia.Title, + Txt_Title_attribution, + ClassMediaTitURL,Med_MAX_CHARS_TITLE); + fprintf (Gbl.F.Out,"
"); // _tit /***** End container *****/ - fprintf (Gbl.F.Out,"
"); + fprintf (Gbl.F.Out,"
"); // container } /*****************************************************************************/ @@ -328,37 +387,54 @@ void Med_GetMediaFromForm (int NumMediaInForm,struct Media *Media, void (*GetMediaFromDB) (int NumMediaInForm,struct Media *Media)) { struct ParamUploadMedia ParamUploadMedia; - char Title[Med_MAX_BYTES_TITLE + 1]; - char URL[Cns_MAX_BYTES_WWW + 1]; - size_t Length; + Med_FormType_t FormType; /***** Set names of parameters depending on number of media in form *****/ Med_SetParamNames (&ParamUploadMedia,NumMediaInForm); - /***** First, get action and initialize media (image/video) + /***** Get action and initialize media (image/video) (except title, that will be get after the media file) *****/ Media->Action = Med_GetMediaActionFromForm (ParamUploadMedia.Action); - Media->Status = Med_FILE_NONE; + Media->Status = Med_STATUS_NONE; Media->Name[0] = '\0'; - Media->Type = Med_NONE; + Media->Type = Med_TYPE_NONE; - /***** Secondly, get the media (image/video) name and the file *****/ + /***** Get form type *****/ + FormType = Usr_GetFormTypeFromForm (); + + /***** Get the media (image/video) name and the file *****/ switch (Media->Action) { case Med_ACTION_NEW_MEDIA: // Upload new image/video - /***** Get new media (if present ==> process and create temporary file) *****/ - Med_GetAndProcessFileFromForm (Media,ParamUploadMedia.File); - switch (Media->Status) + /***** Get new media *****/ + switch (FormType) { - case Med_FILE_NONE: // No new image/video received - Media->Action = Med_ACTION_NO_MEDIA; - Media->Name[0] = '\0'; - Media->Type = Med_NONE; + case Med_FORM_FILE: + /***** Get image/video (if present ==> process and create temporary file) *****/ + Med_GetAndProcessFileFromForm (ParamUploadMedia.File,Media); + switch (Media->Status) + { + case Med_STATUS_NONE: // No new image/video received + Media->Action = Med_ACTION_NO_MEDIA; + Media->Name[0] = '\0'; + Media->Type = Med_TYPE_NONE; + break; + case Med_RECEIVED: // New image/video received, but not processed + Media->Status = Med_STATUS_NONE; + Media->Name[0] = '\0'; + Media->Type = Med_TYPE_NONE; + break; + case Med_PROCESSED: + Usr_GetURLFromForm (ParamUploadMedia.URL,Media); + Usr_GetTitleFromForm (ParamUploadMedia.Title,Media); + break; + default: + break; + } break; - case Med_FILE_RECEIVED: // New image/video received, but not processed - Media->Status = Med_FILE_NONE; - Media->Name[0] = '\0'; - Media->Type = Med_NONE; + case Med_FORM_EMBED: + /***** Get and process embed URL from form *****/ + Med_GetAndProcessEmbedFromForm (ParamUploadMedia.URL,Media); break; default: break; @@ -370,9 +446,29 @@ void Med_GetMediaFromForm (int NumMediaInForm,struct Media *Media, GetMediaFromDB (NumMediaInForm,Media); break; case Med_ACTION_CHANGE_MEDIA: // Replace old image/video by new one - /***** Get new image/video (if present ==> process and create temporary file) *****/ - Med_GetAndProcessFileFromForm (Media,ParamUploadMedia.File); - if (Media->Status != Med_FILE_PROCESSED && // No new media received-processed successfully + switch (FormType) + { + case Med_FORM_FILE: + /***** Get image/video (if present ==> process and create temporary file) *****/ + Med_GetAndProcessFileFromForm (ParamUploadMedia.File,Media); + switch (Media->Status) + { + case Med_PROCESSED: + Usr_GetURLFromForm (ParamUploadMedia.URL,Media); + Usr_GetTitleFromForm (ParamUploadMedia.Title,Media); + break; + default: + break; + } + break; + case Med_FORM_EMBED: + /***** Get and process embed URL from form *****/ + Med_GetAndProcessEmbedFromForm (ParamUploadMedia.URL,Media); + break; + default: + break; + } + if (Media->Status != Med_PROCESSED && // No new media received-processed successfully GetMediaFromDB != NULL) /* Get media (image/video) name */ GetMediaFromDB (NumMediaInForm,Media); @@ -380,36 +476,6 @@ void Med_GetMediaFromForm (int NumMediaInForm,struct Media *Media, case Med_ACTION_NO_MEDIA: // Do not use image/video (remove current image/video if exists) break; } - - /***** Third, get image/video title from form *****/ - Par_GetParToText (ParamUploadMedia.Title,Title,Med_MAX_BYTES_TITLE); - /* If the title coming from the form is empty, keep current media title unchanged - If not empty, copy it to current media title */ - if ((Length = strlen (Title)) > 0) - { - /* Overwrite current title (empty or coming from database) - with the title coming from the form */ - Med_FreeMediaTitle (Media); - if ((Media->Title = (char *) malloc (Length + 1)) == NULL) - Lay_ShowErrorAndExit ("Error allocating memory for media title."); - Str_Copy (Media->Title,Title, - Length); - } - - /***** By last, get media URL from form *****/ - Par_GetParToText (ParamUploadMedia.URL,URL,Cns_MAX_BYTES_WWW); - /* If the URL coming from the form is empty, keep current media URL unchanged - If not empty, copy it to current media URL */ - if ((Length = strlen (URL)) > 0) - { - /* Overwrite current URL (empty or coming from database) - with the URL coming from the form */ - Med_FreeMediaURL (Media); - if ((Media->URL = (char *) malloc (Length + 1)) == NULL) - Lay_ShowErrorAndExit ("Error allocating memory for media URL."); - Str_Copy (Media->URL,URL, - Length); - } } /*****************************************************************************/ @@ -462,12 +528,74 @@ static Med_Action_t Med_GetMediaActionFromForm (const char *ParamAction) (unsigned long) Med_ACTION_DEFAULT); } +/*****************************************************************************/ +/********************* Get from form the type of form ************************/ +/*****************************************************************************/ + +static Med_FormType_t Usr_GetFormTypeFromForm (void) + { + return (Med_FormType_t) Par_GetParToUnsignedLong ("FormType", + 0, + Med_NUM_FORM_TYPES - 1, + (unsigned long) Med_FORM_UNKNOWN); + } + +/*****************************************************************************/ +/********************* Get from form the type of form ************************/ +/*****************************************************************************/ + +static void Usr_GetURLFromForm (const char *ParamName,struct Media *Media) + { + char URL[Cns_MAX_BYTES_WWW + 1]; + size_t Length; + + /***** Get media URL from form *****/ + Par_GetParToText (ParamName,URL,Cns_MAX_BYTES_WWW); + /* If the URL coming from the form is empty, keep current media URL unchanged + If not empty, copy it to current media URL */ + if ((Length = strlen (URL)) > 0) + { + /* Overwrite current URL (empty or coming from database) + with the URL coming from the form */ + Med_FreeMediaURL (Media); + if ((Media->URL = (char *) malloc (Length + 1)) == NULL) + Lay_ShowErrorAndExit ("Error allocating memory for media URL."); + Str_Copy (Media->URL,URL, + Length); + } + } + +/*****************************************************************************/ +/********************* Get from form the type of form ************************/ +/*****************************************************************************/ + +static void Usr_GetTitleFromForm (const char *ParamName,struct Media *Media) + { + char Title[Med_MAX_BYTES_TITLE + 1]; + size_t Length; + + /***** Get image/video title from form *****/ + Par_GetParToText (ParamName,Title,Med_MAX_BYTES_TITLE); + /* If the title coming from the form is empty, keep current media title unchanged + If not empty, copy it to current media title */ + if ((Length = strlen (Title)) > 0) + { + /* Overwrite current title (empty or coming from database) + with the title coming from the form */ + Med_FreeMediaTitle (Media); + if ((Media->Title = (char *) malloc (Length + 1)) == NULL) + Lay_ShowErrorAndExit ("Error allocating memory for media title."); + Str_Copy (Media->Title,Title, + Length); + } + } + /*****************************************************************************/ /**************************** Get media from form ****************************/ /*****************************************************************************/ -static void Med_GetAndProcessFileFromForm (struct Media *Media, - const char *ParamFile) +static void Med_GetAndProcessFileFromForm (const char *ParamFile, + struct Media *Media) { struct Param *Param; char FileNameImgSrc[PATH_MAX + 1]; @@ -478,8 +606,8 @@ static void Med_GetAndProcessFileFromForm (struct Media *Media, char PathMedPrivTmp[PATH_MAX + 1]; char PathFileOrg[PATH_MAX + 1]; // Full name of original uploaded file - /***** Set media file status *****/ - Media->Status = Med_FILE_NONE; + /***** Set media status *****/ + Media->Status = Med_STATUS_NONE; /***** Get filename and MIME type *****/ Param = Fil_StartReceptionOfFile (ParamFile,FileNameImgSrc,MIMEType); @@ -501,7 +629,7 @@ static void Med_GetAndProcessFileFromForm (struct Media *Media, /* Check extension */ Media->Type = Med_GetTypeFromExtAndMIME (PtrExtension,MIMEType); - if (Media->Type == Med_NONE) + if (Media->Type == Med_TYPE_NONE) return; /***** Assign a unique name for the media *****/ @@ -525,14 +653,14 @@ static void Med_GetAndProcessFileFromForm (struct Media *Media, /***** End the reception of original not processed media (it may be very big) into a temporary file *****/ - Media->Status = Med_FILE_NONE; + Media->Status = Med_STATUS_NONE; snprintf (PathFileOrg,sizeof (PathFileOrg), "%s/%s_original.%s", PathMedPrivTmp,Media->Name,PtrExtension); if (Fil_EndReceptionOfFile (PathFileOrg,Param)) // Success { - Media->Status = Med_FILE_RECEIVED; + Media->Status = Med_RECEIVED; /***** Detect if animated GIF *****/ if (Media->Type == Med_GIF) @@ -626,7 +754,7 @@ static void Med_ProcessJPG (struct Media *Media, PathMedPrivTmp,Media->Name,Med_Extensions[Med_JPG]); if (Med_ResizeImage (Media,PathFileOrg,PathFileJPGTmp) == 0) // On success ==> 0 is returned /* Success */ - Media->Status = Med_FILE_PROCESSED; + Media->Status = Med_PROCESSED; else // Error processing media { /* Remove temporary destination media file */ @@ -682,7 +810,7 @@ static void Med_ProcessGIF (struct Media *Media, Ale_ShowAlert (Ale_ERROR,Txt_The_file_could_not_be_processed_successfully); } else // Success - Media->Status = Med_FILE_PROCESSED; + Media->Status = Med_PROCESSED; } else // Error getting first frame { @@ -736,7 +864,7 @@ static void Med_ProcessVideo (struct Media *Media, /* Show error alert */ Ale_ShowAlert (Ale_ERROR,Txt_The_file_could_not_be_processed_successfully); else // Success - Media->Status = Med_FILE_PROCESSED; + Media->Status = Med_PROCESSED; } else // Size exceeded { @@ -805,6 +933,187 @@ static int Med_GetFirstFrame (const char PathFileOriginal[PATH_MAX + 1], return ReturnCode; } +/*****************************************************************************/ +/**************************** Get media from form ****************************/ +/*****************************************************************************/ + +static void Med_GetAndProcessEmbedFromForm (const char *ParamURL, + struct Media *Media) + { + extern const char Str_BIN_TO_BASE64URL[64 + 1]; + char *PtrHost = NULL; + char *PtrPath = NULL; + char *PtrParams = NULL; + char *PtrCode = NULL; + size_t CodeLength; + char *Code; + enum + { + WRONG, // Bad formed YouTube URL + SHORT, // youtu.be + FULL, // www.youtube.com/watch? + EMBED, // www.youtube.com/embed/ + } YouTube = WRONG; + + /***** Set media status *****/ + Media->Status = Med_STATUS_NONE; + + /***** Get embed URL from form *****/ + Usr_GetURLFromForm (ParamURL,Media); + // Ale_ShowAlert (Ale_INFO,"DEBUG: Media->URL = '%s'",Media->URL); + + /***** Process URL trying to convert it to a YouTube embed URL *****/ + if (Media->URL) + if (Media->URL[0]) // URL given by user is not empty + { + /* Examples of valid YouTube URLs: + https://www.youtube.com/watch?v=xu9IbeF9CBw + https://www.youtube.com/watch?v=xu9IbeF9CBw&t=10 + https://youtu.be/xu9IbeF9CBw + https://youtu.be/xu9IbeF9CBw?t=10 + https://www.youtube.com/embed/xu9IbeF9CBw + https://www.youtube.com/embed/xu9IbeF9CBw?start=10 + */ + /***** Step 1: Skip scheme *****/ + if (!strncasecmp (Media->URL,"https://",8)) // URL starts by https:// + PtrHost = &Media->URL[8]; + else if (!strncasecmp (Media->URL,"http://" ,7)) // URL starts by http:// + PtrHost = &Media->URL[7]; + else if (!strncasecmp (Media->URL,"//" ,2)) // URL starts by // + PtrHost = &Media->URL[2]; + else + PtrHost = &Media->URL[0]; + + if (PtrHost[0]) + { + /***** Step 2: Skip host *****/ + // Ale_ShowAlert (Ale_INFO,"DEBUG: PtrHost = '%s'",PtrHost); + if (!strncasecmp (PtrHost,"youtu.be/" , 9)) // Host starts by youtu.be/ + { + YouTube = SHORT; + PtrPath = &PtrHost[9]; + } + else if (!strncasecmp (PtrHost,"www.youtube.com/",16)) // Host starts by www.youtube.com/ + { + YouTube = FULL; + PtrPath = &PtrHost[16]; + } + else if (!strncasecmp (PtrHost,"youtube.com/" ,12)) // Host starts by youtube.com/ + { + YouTube = FULL; + PtrPath = &PtrHost[12]; + } + + /* Check pointer to path */ + if (PtrPath) + { + if (!PtrPath[0]) + YouTube = WRONG; + } + else + YouTube = WRONG; + + if (YouTube != WRONG) + { + // Ale_ShowAlert (Ale_INFO,"DEBUG: PtrPath = '%s'",PtrPath); + /***** Step 3: Skip path *****/ + if (YouTube == FULL) + { + if (!strncasecmp (PtrPath,"watch?",6)) // Path starts by watch? + PtrParams = &PtrPath[6]; + else if (!strncasecmp (PtrPath,"embed/",6)) // Path starts by embed/ + { + YouTube = EMBED; + PtrParams = &PtrPath[6]; + } + else + YouTube = WRONG; + } + else + PtrParams = &PtrPath[0]; + + /* Check pointer to params */ + if (PtrParams) + { + if (!PtrParams[0]) + YouTube = WRONG; + } + else + YouTube = WRONG; + + if (YouTube != WRONG) + { + // Ale_ShowAlert (Ale_INFO,"DEBUG: PtrParams = '%s'",PtrParams); + /***** Step 4: Search for video code *****/ + switch (YouTube) + { + case SHORT: + PtrCode = PtrParams; + break; + case FULL: + /* Search for v= */ + PtrCode = strcasestr (PtrPath,"v="); + if (PtrCode) + PtrCode += 2; + break; + case EMBED: + PtrCode = PtrParams; + break; + default: + PtrCode = NULL; + break; + } + + /* Check pointer to code */ + if (PtrCode) + { + if (!PtrCode[0]) + YouTube = WRONG; + } + else + YouTube = WRONG; + + if (YouTube != WRONG) + { + // Ale_ShowAlert (Ale_INFO,"DEBUG: PtrCode = '%s'",PtrCode); + /***** Step 5: Get video code *****/ + CodeLength = strspn (PtrCode,Str_BIN_TO_BASE64URL); + if (CodeLength > 0) + { + /* Allocate space for YouTube code */ + if ((Code = (char *) malloc (CodeLength + 1)) == NULL) + Lay_ShowErrorAndExit ("Error allocating memory for YouTube code."); + + /* Copy code */ + strncpy (Code,PtrCode,CodeLength); + Code[CodeLength] = '\0'; + // Ale_ShowAlert (Ale_INFO,"DEBUG: Code = '%s'",Code); + + /* Overwrite current URL with the embed URL */ + Med_FreeMediaURL (Media); + if (asprintf (&Media->URL,"https://www.youtube.com/embed/%s", + Code) < 0) + Lay_NotEnoughMemoryExit (); + if (strlen (Media->URL) <= Cns_MAX_BYTES_WWW) + { + /***** Success! *****/ + // Ale_ShowAlert (Ale_INFO,"DEBUG: Media->URL = '%s'",Media->URL); + Media->Type = Med_YOUTUBE; + Media->Status = Med_PROCESSED; + } + else + Med_FreeMediaURL (Media); + + /* Free YouTube code */ + free ((void *) Code); + } + } + } + } + } + } + } + /*****************************************************************************/ /**** Move temporary processed media file to definitive private directory ****/ /*****************************************************************************/ @@ -814,6 +1123,12 @@ void Med_MoveMediaToDefinitiveDir (struct Media *Media) char PathMedPrivTmp[PATH_MAX + 1]; char PathMedPriv[PATH_MAX + 1]; + /***** Check trivial cases *****/ + if (Media->Type == Med_TYPE_NONE) + Lay_ShowErrorAndExit ("Wrong media type."); + if (Media->Type == Med_YOUTUBE) + return; // Nothing to do with files + /***** Build temporary path *****/ snprintf (PathMedPrivTmp,sizeof (PathMedPrivTmp), "%s/%s/%s", @@ -907,57 +1222,68 @@ void Med_ShowMedia (struct Media *Media, char PathMedPriv[PATH_MAX + 1]; /***** If no media to show ==> nothing to do *****/ - if (!Media->Name) + if (Media->Status != Med_STORED_IN_DB) return; - if (!Media->Name[0]) - return; - if (Media->Type == Med_NONE) - return; - if (Media->Status != Med_NAME_STORED_IN_DB) + if (Media->Type == Med_TYPE_NONE) return; /***** Start media container *****/ - fprintf (Gbl.F.Out,"
",ClassContainer); + fprintf (Gbl.F.Out,"
Type == Med_YOUTUBE) + fprintf (Gbl.F.Out," MED_VIDEO_CONT"); + fprintf (Gbl.F.Out,"\">"); - /***** Start optional link to external URL *****/ - PutLink = false; - if (Media->URL) - if (Media->URL[0]) - PutLink = true; - if (PutLink) - fprintf (Gbl.F.Out,"",Media->URL); - - /***** Create a temporary public directory used to show the media *****/ - Brw_CreateDirDownloadTmp (); - - /***** Build path to private directory with the media *****/ - snprintf (PathMedPriv,sizeof (PathMedPriv), - "%s/%s/%c%c", - Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA, - Media->Name[0], - Media->Name[1]); - - /***** Show media *****/ - switch (Media->Type) + if (Media->Type == Med_YOUTUBE) + /***** Show media *****/ + Med_ShowYoutube (Media,ClassMedia); + else // Uploaded file { - case Med_JPG: - Med_ShowJPG (Media,PathMedPriv,ClassMedia); - break; - case Med_GIF: - Med_ShowGIF (Media,PathMedPriv,ClassMedia); - break; - case Med_MP4: - case Med_WEBM: - case Med_OGG: - Med_ShowVideo (Media,PathMedPriv,ClassMedia); - break; - default: - break; - } + /***** If no media to show ==> nothing to do *****/ + if (!Media->Name) + return; + if (!Media->Name[0]) + return; - /***** End optional link to external URL *****/ - if (PutLink) - fprintf (Gbl.F.Out,""); + /***** Start optional link to external URL *****/ + PutLink = false; + if (Media->URL) + if (Media->URL[0]) + PutLink = true; + if (PutLink) + fprintf (Gbl.F.Out,"",Media->URL); + + /***** Create a temporary public directory used to show the media *****/ + Brw_CreateDirDownloadTmp (); + + /***** Build path to private directory with the media *****/ + snprintf (PathMedPriv,sizeof (PathMedPriv), + "%s/%s/%c%c", + Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA, + Media->Name[0], + Media->Name[1]); + + /***** Show media *****/ + switch (Media->Type) + { + case Med_JPG: + Med_ShowJPG (Media,PathMedPriv,ClassMedia); + break; + case Med_GIF: + Med_ShowGIF (Media,PathMedPriv,ClassMedia); + break; + case Med_MP4: + case Med_WEBM: + case Med_OGG: + Med_ShowVideo (Media,PathMedPriv,ClassMedia); + break; + default: + break; + } + + /***** End optional link to external URL *****/ + if (PutLink) + fprintf (Gbl.F.Out,""); + } /***** End media container *****/ fprintf (Gbl.F.Out,"
"); @@ -1107,7 +1433,7 @@ static void Med_ShowGIF (struct Media *Media, } /*****************************************************************************/ -/************************** Show a user uploaded MP4 *************************/ +/************************ Show a user uploaded video *************************/ /*****************************************************************************/ static void Med_ShowVideo (struct Media *Media, @@ -1162,6 +1488,39 @@ static void Med_ShowVideo (struct Media *Media, fprintf (Gbl.F.Out,"%s",Txt_File_not_found); } +/*****************************************************************************/ +/*************************** Show an embed media *****************************/ +/*****************************************************************************/ + +static void Med_ShowYoutube (struct Media *Media, + const char *ClassMedia) + { + /***** Check if embed URL exists *****/ + if (Media->URL[0]) // Embed URL + { + /***** Show linked external media *****/ + // Example of code give by YouTube: + // + fprintf (Gbl.F.Out,""); + } + } + /*****************************************************************************/ /*** Remove private files with an image/video, given the image/video name ****/ /*****************************************************************************/ @@ -1193,6 +1552,12 @@ void Med_RemoveMediaFiles (const char *Name,Med_Type_t Type) char PathMedPriv[PATH_MAX + 1]; char FullPathMediaPriv[PATH_MAX + 1]; + /***** Trivial cases *****/ + if (Type == Med_TYPE_NONE) + Lay_ShowErrorAndExit ("Wrong media type."); + if (Type == Med_YOUTUBE) + return; + /***** Build path to private directory with the media *****/ snprintf (PathMedPriv,sizeof (PathMedPriv), "%s/%s/%c%c", @@ -1236,8 +1601,7 @@ void Med_RemoveMediaFiles (const char *Name,Med_Type_t Type) unlink (FullPathMediaPriv); break; - case Med_NONE: - Lay_ShowErrorAndExit ("Wrong media type."); + default: break; } @@ -1259,7 +1623,7 @@ Med_Type_t Med_GetTypeFromStrInDB (const char *Str) if (!strcasecmp (Str,Med_StringsTypeDB[Type])) return Type; - return Med_NONE; + return Med_TYPE_NONE; } /*****************************************************************************/ @@ -1317,7 +1681,7 @@ static Med_Type_t Med_GetTypeFromExtAndMIME (const char *Extension, !strcmp (MIMEType,"application/octet" )) return Med_OGG; - return Med_NONE; + return Med_TYPE_NONE; } /*****************************************************************************/ @@ -1328,7 +1692,7 @@ const char *Med_GetStringTypeForDB (Med_Type_t Type) { /***** Check if type is out of valid range *****/ if (Type > (Med_Type_t) (Med_NUM_TYPES - 1)) - return Med_StringsTypeDB[Med_NONE]; + return Med_StringsTypeDB[Med_TYPE_NONE]; /***** Get string from type *****/ return Med_StringsTypeDB[Type]; diff --git a/swad_media.h b/swad_media.h index beefb8b8..d59d3b72 100644 --- a/swad_media.h +++ b/swad_media.h @@ -47,10 +47,10 @@ #define Med_NUM_ACTIONS 4 typedef enum { - Med_ACTION_NEW_MEDIA, // Upload new image/video - Med_ACTION_KEEP_MEDIA, // Keep current image/video unchanged - Med_ACTION_CHANGE_MEDIA, // Change existing image/video by a new one - Med_ACTION_NO_MEDIA, // Do not use image/video (remove current image/video if exists) + Med_ACTION_NEW_MEDIA, // Upload new media + Med_ACTION_KEEP_MEDIA, // Keep current media unchanged + Med_ACTION_CHANGE_MEDIA, // Change existing media by a new one + Med_ACTION_NO_MEDIA, // Do not use media (remove current media if exists) } Med_Action_t; #define Med_ACTION_DEFAULT Med_ACTION_NO_MEDIA @@ -81,29 +81,30 @@ xx-unique-name: a unique name encrypted starting by two random chars xx */ typedef enum { - Med_FILE_NONE, - Med_FILE_RECEIVED, - Med_FILE_PROCESSED, + Med_STATUS_NONE, + Med_RECEIVED, + Med_PROCESSED, Med_FILE_MOVED, - Med_NAME_STORED_IN_DB, - } Med_FileStatus_t; + Med_STORED_IN_DB, + } Med_Status_t; -#define Med_NUM_TYPES 6 +#define Med_NUM_TYPES 7 typedef enum { - Med_NONE, + Med_TYPE_NONE, Med_JPG, Med_GIF, Med_MP4, Med_WEBM, Med_OGG, + Med_YOUTUBE, } Med_Type_t; /***** Struct used to get images/videos from forms *****/ struct Media { Med_Action_t Action; - Med_FileStatus_t Status; + Med_Status_t Status; char Name[Med_BYTES_NAME + 1]; Med_Type_t Type; char *Title; // Title/attribution (it must be initialized to NULL @@ -120,10 +121,10 @@ struct Media #define Med_MAX_BYTES_PARAM_UPLOAD_MEDIA (16 - 1) struct ParamUploadMedia { - char Action[Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1]; - char File [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1]; - char Title [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1]; - char URL [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1]; + char Action [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1]; + char File [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1]; + char Title [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1]; + char URL [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1]; }; /*****************************************************************************/ diff --git a/swad_message.c b/swad_message.c index 3c67eca6..ef511417 100644 --- a/swad_message.c +++ b/swad_message.c @@ -1282,7 +1282,7 @@ static long Msg_InsertNewMsg (const char *Subject,const char *Content, /***** Check if image is received and processed *****/ if (Media->Action == Med_ACTION_NEW_MEDIA && // Upload new image - Media->Status == Med_FILE_PROCESSED) // The new image received has been processed + Media->Status == Med_PROCESSED) // The new image received has been processed /* Move processed image to definitive directory */ Med_MoveMediaToDefinitiveDir (Media); diff --git a/swad_string.c b/swad_string.c index 4f0984d0..fbfc7a4d 100644 --- a/swad_string.c +++ b/swad_string.c @@ -67,10 +67,11 @@ static int Str_ReadCharAndSkipCommentsBackward (FILE *FileSrc,Str_SkipHTMLCommen /***** Conversion to Base64URL *****/ // base64url is described in document http://tools.ietf.org/html/rfc4648. // It uses '-' and '_' because they are safe for URL/parameters (without enconding) and for filenames. -const char Str_BIN_TO_BASE64URL[64] = +const char Str_BIN_TO_BASE64URL[64 + 1] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', - '0','1','2','3','4','5','6','7','8','9','-','_'}; + '0','1','2','3','4','5','6','7','8','9','-','_', + '\0'}; // NULL-terminated string static const char Str_LF[2] = {10,0}; static const char Str_CR[2] = {13,0}; diff --git a/swad_test.c b/swad_test.c index 9b533b05..424006f7 100644 --- a/swad_test.c +++ b/swad_test.c @@ -6005,7 +6005,7 @@ bool Tst_CheckIfQstFormatIsCorrectAndCountNumOptions (void) if ((Gbl.Test.Media.Action == Med_ACTION_NEW_MEDIA || // Upload new image Gbl.Test.Media.Action == Med_ACTION_CHANGE_MEDIA) && // Replace existing image by new image - Gbl.Test.Media.Status != Med_FILE_PROCESSED) + Gbl.Test.Media.Status != Med_PROCESSED) { Ale_ShowAlert (Ale_WARNING,Txt_Error_receiving_or_processing_image); return false; @@ -6187,7 +6187,7 @@ static void Tst_MoveMediaToDefinitiveDirectories (void) if ((Gbl.Test.Media.Action == Med_ACTION_NEW_MEDIA || // Upload new image Gbl.Test.Media.Action == Med_ACTION_CHANGE_MEDIA) && // Replace existing image by new image - Gbl.Test.Media.Status == Med_FILE_PROCESSED) // The new image received has been processed + Gbl.Test.Media.Status == Med_PROCESSED) // The new image received has been processed /* Move processed image to definitive directory */ Med_MoveMediaToDefinitiveDir (&Gbl.Test.Media); @@ -6207,7 +6207,7 @@ static void Tst_MoveMediaToDefinitiveDirectories (void) if ((Gbl.Test.Answer.Options[NumOpt].Media.Action == Med_ACTION_NEW_MEDIA || // Upload new image Gbl.Test.Answer.Options[NumOpt].Media.Action == Med_ACTION_CHANGE_MEDIA) && // Replace existing image by new image - Gbl.Test.Answer.Options[NumOpt].Media.Status == Med_FILE_PROCESSED) // The new image received has been processed + Gbl.Test.Answer.Options[NumOpt].Media.Status == Med_PROCESSED) // The new image received has been processed /* Move processed image to definitive directory */ Med_MoveMediaToDefinitiveDir (&Gbl.Test.Answer.Options[NumOpt].Media); } @@ -6560,10 +6560,9 @@ static void Tst_InsertOrUpdateQstIntoDB (void) Gbl.Test.Media.Title ? Gbl.Test.Media.Title : "", Gbl.Test.Media.URL ? Gbl.Test.Media.URL : ""); - /* Update image status */ - if (Gbl.Test.Media.Name[0] && - Gbl.Test.Media.Type != Med_NONE) - Gbl.Test.Media.Status = Med_NAME_STORED_IN_DB; + /* Update media status */ + if (Gbl.Test.Media.Type != Med_TYPE_NONE) + Gbl.Test.Media.Status = Med_STORED_IN_DB; } else // It's an existing question { @@ -6586,10 +6585,9 @@ static void Tst_InsertOrUpdateQstIntoDB (void) Gbl.Test.Media.URL ? Gbl.Test.Media.URL : "", Gbl.Test.QstCod,Gbl.CurrentCrs.Crs.CrsCod); - /* Update image status */ - if (Gbl.Test.Media.Name[0] && - Gbl.Test.Media.Type != Med_NONE) - Gbl.Test.Media.Status = Med_NAME_STORED_IN_DB; + /* Update media status */ + if (Gbl.Test.Media.Type != Med_TYPE_NONE) + Gbl.Test.Media.Status = Med_STORED_IN_DB; /* Remove answers and tags from this test question */ Tst_RemAnsFromQst (); @@ -6661,7 +6659,7 @@ static void Tst_InsertAnswersIntoDB (void) "'','%s','','','Y')", Gbl.Test.QstCod, Gbl.Test.Answer.Integer, - Med_GetStringTypeForDB (Med_NONE)); + Med_GetStringTypeForDB (Med_TYPE_NONE)); break; case Tst_ANS_FLOAT: Str_SetDecimalPointToUS (); // To print the floating point as a dot @@ -6677,7 +6675,7 @@ static void Tst_InsertAnswersIntoDB (void) "'','%s','','','Y')", Gbl.Test.QstCod,i, Gbl.Test.Answer.FloatingPoint[i], - Med_GetStringTypeForDB (Med_NONE)); + Med_GetStringTypeForDB (Med_TYPE_NONE)); Str_SetDecimalPointToLocal (); // Return to local system break; case Tst_ANS_TRUE_FALSE: @@ -6690,7 +6688,7 @@ static void Tst_InsertAnswersIntoDB (void) "'','%s','','','Y')", Gbl.Test.QstCod, Gbl.Test.Answer.TF, - Med_GetStringTypeForDB (Med_NONE)); + Med_GetStringTypeForDB (Med_TYPE_NONE)); break; case Tst_ANS_UNIQUE_CHOICE: case Tst_ANS_MULTIPLE_CHOICE: @@ -6718,9 +6716,8 @@ static void Tst_InsertAnswersIntoDB (void) 'N'); /* Update image status */ - if (Gbl.Test.Answer.Options[NumOpt].Media.Name[0] && - Gbl.Test.Answer.Options[NumOpt].Media.Type != Med_NONE) - Gbl.Test.Answer.Options[NumOpt].Media.Status = Med_NAME_STORED_IN_DB; + if (Gbl.Test.Answer.Options[NumOpt].Media.Type != Med_TYPE_NONE) + Gbl.Test.Answer.Options[NumOpt].Media.Status = Med_STORED_IN_DB; } break; default: diff --git a/swad_text.c b/swad_text.c index 9935e3d0..f84ce81e 100644 --- a/swad_text.c +++ b/swad_text.c @@ -3569,23 +3569,23 @@ const char *Txt_Change_IDs = const char *Txt_Change_image = #if L==1 // ca - "Canviar imatge"; + "Canviar imatge / vídeo"; #elif L==2 // de - "Abbild ändern"; + "Abbild / Video ändern"; #elif L==3 // en - "Change image"; + "Change image / video"; #elif L==4 // es - "Cambiar imagen"; + "Cambiar imagen / vídeo"; #elif L==5 // fr - "Changer image"; + "Changer image / vidéo"; #elif L==6 // gn - "Moambue ta'ãnga"; + "Moambue ta'ãnga / video"; #elif L==7 // it - "Cambiare immagine"; + "Cambiare immagine / video"; #elif L==8 // pl - "Zmień obraz"; + "Zmień obraz / wideo"; #elif L==9 // pt - "Alterar imagem"; + "Alterar imagem / vídeo"; #endif const char *Txt_Change_logo = @@ -6616,23 +6616,23 @@ const char *Txt_Current_email = const char *Txt_Current_image = #if L==1 // ca - "Imatge actual"; + "Imatge / vídeo actual"; #elif L==2 // de - "Aktuelles Abbild"; + "Aktuelles Abbild / Video"; #elif L==3 // en - "Current image"; + "Current image / video"; #elif L==4 // es - "Imagen actual"; + "Imagen / vídeo actual"; #elif L==5 // fr - "Image actuelle"; + "Image / vidéo actuelle"; #elif L==6 // gn - "Imagen actual"; // Okoteve traducción + "Imagen / vídeo actual"; // Okoteve traducción #elif L==7 // it - "Immagine corrente"; + "Immagine / video corrente"; #elif L==8 // pl - "Bieżący obraz"; + "Bieżący obraz / wideo"; #elif L==9 // pt - "Imagem atual"; + "Imagem / vídeo atual"; #endif const char *Txt_Current_nickname = @@ -24650,23 +24650,23 @@ const char *Txt_No_holidays = const char *Txt_No_image = // Without any image #if L==1 // ca - "Sense imatge"; + "Sense imatge / vídeo"; #elif L==2 // de - "Ohne Abbild"; + "Ohne Abbild / Video"; #elif L==3 // en - "No image"; + "No image / video"; #elif L==4 // es - "Sin imagen"; + "Sin imagen / vídeo"; #elif L==5 // fr - "Pas d'image"; + "Pas d'image / vidéo"; #elif L==6 // gn - "Sin imagen"; // Okoteve traducción + "Sin imagen / vídeo"; // Okoteve traducción #elif L==7 // it - "Nessuna immagine"; + "Nessuna immagine / video"; #elif L==8 // pl - "Brak obrazka"; + "Brak obrazka / wideo"; #elif L==9 // pt - "Sem imagem"; + "Sem imagem / vídeo"; #endif const char *Txt_No_INDEX = // Short version of "Number" (as an index)... diff --git a/swad_timeline.c b/swad_timeline.c index f9cda4cb..48acc108 100644 --- a/swad_timeline.c +++ b/swad_timeline.c @@ -2472,12 +2472,12 @@ static long TL_ReceivePost (void) Media.Quality = TL_IMAGE_SAVED_QUALITY; Med_GetMediaFromForm (-1,&Media,NULL); - if (Content[0] || // Text not empty - (Media.Name[0] && Media.Type != Med_NONE)) // A media is attached + if (Content[0] || // Text not empty + Media.Status == Med_PROCESSED) // A media is attached { /***** Check if image is received and processed *****/ if (Media.Action == Med_ACTION_NEW_MEDIA && // Upload new image - Media.Status == Med_FILE_PROCESSED) // The new image received has been processed + Media.Status == Med_PROCESSED) // The new image received has been processed /* Move processed image to definitive directory */ Med_MoveMediaToDefinitiveDir (&Media); @@ -2494,10 +2494,8 @@ static long TL_ReceivePost (void) Content, Media.Name, Med_GetStringTypeForDB (Media.Type), - (Media.Name[0] && // Save image title only if image attached - Media.Title) ? Media.Title : "", - (Media.Name[0] && // Save image URL only if image attached - Media.URL ) ? Media.URL : ""); + Media.Title ? Media.Title : "", + Media.URL ? Media.URL : ""); /* Insert post in notes */ TL_StoreAndPublishNote (TL_NOTE_POST,PstCod,&SocPub); @@ -2655,7 +2653,7 @@ static void TL_WriteCommentsInNote (const struct TL_Note *SocNot) // Never hide only one comment // So, the number of comments initially hidden must be 0 or >= 2 NumCommentsInitiallyHidden = (NumComments <= TL_NUM_VISIBLE_COMMENTS + 1) ? 0 : // Show all - NumComments - TL_NUM_VISIBLE_COMMENTS; + NumComments - TL_NUM_VISIBLE_COMMENTS; if (NumCommentsInitiallyHidden) { /***** Create unique id for list of hidden comments *****/ @@ -3200,12 +3198,12 @@ static long TL_ReceiveComment (void) Media.Quality = TL_IMAGE_SAVED_QUALITY; Med_GetMediaFromForm (-1,&Media,NULL); - if (Content[0] || // Text not empty - (Media.Name[0] && Media.Type != Med_NONE)) // A media is attached + if (Content[0] || // Text not empty + Media.Status == Med_PROCESSED) // A media is attached { /***** Check if image is received and processed *****/ if (Media.Action == Med_ACTION_NEW_MEDIA && // Upload new image - Media.Status == Med_FILE_PROCESSED) // The new image received has been processed + Media.Status == Med_PROCESSED) // The new image received has been processed /* Move processed image to definitive directory */ Med_MoveMediaToDefinitiveDir (&Media); @@ -3228,10 +3226,8 @@ static long TL_ReceiveComment (void) Content, Media.Name, Med_GetStringTypeForDB (Media.Type), - (Media.Name[0] && // Save image title only if image attached - Media.Title) ? Media.Title : "", - (Media.Name[0] && // Save image URL only if image attached - Media.URL ) ? Media.URL : ""); + Media.Title ? Media.Title : "", + Media.URL ? Media.URL : ""); /***** Store notifications about the new comment *****/ Ntf_StoreNotifyEventsToAllUsrs (Ntf_EVENT_TIMELINE_COMMENT,SocPub.PubCod); @@ -5118,6 +5114,7 @@ static void Str_AnalyzeTxtAndStoreNotifyEventToMentionedUsrs (long PubCod,const { /* Copy nickname */ strncpy (UsrDat.Nickname,Nickname.PtrStart,Nickname.Length); + UsrDat.Nickname[Nickname.Length] = '\0'; if ((UsrDat.UsrCod = Nck_GetUsrCodFromNickname (UsrDat.Nickname)) > 0) { diff --git a/swad_web_service.c b/swad_web_service.c index 5efc9bd4..ad18f837 100644 --- a/swad_web_service.c +++ b/swad_web_service.c @@ -120,7 +120,7 @@ cp -f /home/acanas/swad/swad/swad /var/www/cgi-bin/ /*****************************************************************************/ extern struct Globals Gbl; -extern const char Str_BIN_TO_BASE64URL[64]; +extern const char Str_BIN_TO_BASE64URL[64 + 1]; /*****************************************************************************/ /***************************** Private constants *****************************/ @@ -3452,7 +3452,7 @@ static int Svc_SendMessageToUsr (long OriginalMsgCod, " ('%s','%s'," "'','%s','','')", Subject,Content, - Med_GetStringTypeForDB (Med_NONE)); + Med_GetStringTypeForDB (Med_TYPE_NONE)); /* Insert message in sent messages */ DB_QueryINSERT ("can not create message",