SlideShare a Scribd company logo
1 of 248
Download to read offline
1
1
Antonio Cañas et al.
Nov 3, 2023, Granada, Spain
24 years developing SWAD:
hits, misses & lessons learned
Including timeline implementation
Antonio Cañas
University of Granada
@acanasvargas acanas@ugr.es acanas@openswad.org
https://openswad.org/ @openswad
Contents
●
SWAD
●
Features
●
History
●
Software
●
HTML
●
CSS
●
JavaScript
●
CGI in C
●
Database
●
Other modules
●
Timeline
●
Notes & Pubs
●
Database
●
Getting pubs
●
Showing pubs
●
Layout
●
Inserting links
●
Hits
●
Misses
●
Lessons
learned
●
Conclusions
●
About us
SWAD
“swad: a bunch, a grouping
of a number of similar things”
.
https://www.thefreedictionary.com/swad
Sistema Web de Apoyo a la Docencia
(Web System for Teaching Support)
⬇
Social Workspace At a Distance
https://swad.ugr.es/ https://openswad.org/
A web platform to manage courses, students and teachers,
with functions to support teaching and learning.
SWAD
SWAD
This is how it looks.
Some parts of its appearance,
such as colors or icons, are customizable
SWAD: Features
Free software · 10 languages · Responsive design · Android
app · iOS app · Face-to-face or blended learning
Hierarchical organization: System · Countries · Institutions
(universities, companies) · Centers (faculties, schools) ·
Degrees · Courses · Group types · Groups
10 available roles: Unknown · Guest · User · Student · Non-
editing teacher · Teacher · Degree admin · Center admin ·
Institution admin · System admin
SWAD: Features
Social network · Calendar · Notifications · Course
information · Syllabus · Documents · Shared files ·
Portfolio · Grades · Assignments · Projects · Exam
announcements · Quizzes · Exams · Games · Rubrics ·
Groups · Lists of students and teachers · Attendance
control using QR codes · Forums · Notices · Messaging
system · Surveys · Statistics · Agenda · Preferences
All features in
https://github.com/acanas/swad-core/wiki/UserGuide.en
SWAD: History
●
LMS in 1999: WebCT
(1997), Moodle (1999)
Radio Granada, May 12, 2000
I just want to test
if it's possible to fill out
the student record card
via the web
SWAD: History
●
LMS in 1999: WebCT
(1997), Moodle (1999)
Version 4.2.1, May 13, 2003
(it was not called SWAD yet)
I just want to test
if it's possible to fill out
the student record card
via the web
SWAD: History
●
LMS in 2023: a very broad offer
●
Hundreds of LMS (Learning Management Systems)
●
proprietary / free software
●
expensive / free of charge
●
specific / generic
●
installable on the client's servers / accessible in the cloud
SWAD: History
736
SWAD: History
1340
Dpt.ATC: 1999-2003
1º TIP: 2003-2004
2º TIP: 2005-2006
3º TIP: 2006-2008
CEVUG: 2008-2016
Free Software: 2010...
UNA.py: 2012-2015
OpenSWAD: 2012…
Dpt.ATC: 2016-2022
Dpt.ICAR: 2022...
SWAD: History
openswad.org
2012...
ugr CEVUG
2008-2016
ugr TIPs
2003-2008
ugr ATC
1999-2003
una.py
2012-2015
ugr ATC
2016-2022
24 years of development and use
free software
2010...
ugr ICAR
2022...
Software: HTML
“If you think math is hard, try web design.”
Trish Parr – Web designer
Software: HTML
●
HTML (HyperText Markup Language)
●
1990 (Tim Berners-Lee)
●
Standard language for documents to be displayed in a web browser
●
Describes the semantic structure of a web page
●
...by using tags and attributes: <a href="https://openswad.org/">...</a>
●
Assisted by CSS (appearance) and JavaScript (behavior)
●
Web browsers
●
receive HTML documents from a web server
●
render the documents into multimedia web pages
Software: HTML
●
HTML:
<html lang="en">
<head>
<link rel="stylesheet"
href="https://openswad.org/swad/swad21.95.5.css"
type="text/css" />
<script type="text/javascript"
src="https://openswad.org/swad/swad21.92.js">
</script>
<title>
OpenSWAD: social learning platform
</title>
</head>
<body class="BODY_GREY" onload="init();">
<!-- HTML depending on action -->
</body>
</html>
HTML code
in user’s browser
Software: HTML
●
Example:
Software: HTML
●
Example:
Software: HTML
●
HTML:
<form method="post" action="https://openswad.org/en">
<input type="hidden" name="act" value="1492">
<input type="hidden" name="ses" value="6BJQ...G46g">
<input type="hidden" name="Who" value="4">
<textarea name="Txt" rows="1" maxlength="1000"
placeholder="New post…"
class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH"
onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');">
</textarea>
<div id="id_EwuT...BhSs_6" style="display:none;">
...
<button type="submit" class="...">
Post
</button>
</div>
</form>
HTML code
in user’s browser
Software: CSS
“Learning HTML and CSS is a lot more challenging
than it used to be. Responsive web design adds more
layers of complexity to design and develop websites.”
Jacob Lett – Digital Marketing & Web Development Manager
Software: CSS
●
CSS (Cascading Style Sheets) language
●
1996 (Håkon Wium Lie, Bert Bos)
●
Separates
●
presentation (layout, colors, fonts)
●
content
of an HTML document
●
Multiple web pages can share formatting in a .css file
●
“Cascading”: priority scheme
●
determines which style rule applies if more than one rule matches a particular element
Software: CSS
●
CSS:
.Tml_COM_TEXTAREA
{
box-sizing: border-box;
margin: 0;
resize: none;
}
@media only screen and (max-width: 590px)
{ /* For mobile-phones */
...
.Tml_RIGHT_WIDTH {width: 260px;} /* 500-240 */
...
}
@media only screen and (min-width: 590px)
{ /* For tablets and desktop */
...
.Tml_RIGHT_WIDTH {width: 500px;}
...
}
swad23.35.1.css
Software: JavaScript
“The strength of JavaScript is that you can do
anything. The weakness is that you will.”
Reg Braithwaite – Canadian programmer and writer
Software: JavaScript
●
JavaScript:
●
1995 (Brendan Eich, Netscape, Sun)
●
Multi-paradigm, interpreted / just-in-time compiled, syntax similar to C and Java
●
Modern browsers interpret JavaScript code
●
embedded in the web page
●
or in a .js file
to add interactive features
●
Interacts with the Document Object Model (DOM)
●
Represents a document as a logical tree, each node is an object (part of the document )
<form method="post" action="https://openswad.org/en">
<input type="hidden" name="act" value="1492">
<input type="hidden" name="ses" value="6BJQ...G46g">
<input type="hidden" name="Who" value="4">
<textarea name="Txt" rows="1" maxlength="1000"
placeholder="New post…"
class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH"
onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');">
</textarea>
<div id="id_EwuT...BhSs_6" style="display:none;">
...
<button type="submit" class="...">
Post
</button>
</div>
</form>
Software: JavaScript
●
HTML:
Textarea used to write the post
<form method="post" action="https://openswad.org/en">
<input type="hidden" name="act" value="1492">
<input type="hidden" name="ses" value="6BJQ...G46g">
<input type="hidden" name="Who" value="4">
<textarea name="Txt" rows="1" maxlength="1000"
placeholder="New post…"
class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH"
onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');">
</textarea>
<div id="id_EwuT...BhSs_6" style="display:none;">
...
<button type="submit" class="...">
Post
</button>
</div>
</form>
Software: JavaScript
●
HTML:
Hidden container including button
Software: JavaScript
●
HTML
●
JavaScript:
/*****************************************************************************/
/*********************** Expand textarea when focus **************************/
/*****************************************************************************/
// Called from a textarea onfocus
function expandTextarea (textareaElem,idButton,rows) {
textareaElem.rows = rows;
document.getElementById(idButton).style.display = '';
}
swad22.49.js
<textarea name="Txt" rows="1" maxlength="1000"
placeholder="New post…"
class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH"
onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');">
</textarea>
Software: CGI in C
“C is quirky, flawed, and an enormous success.”
Dennis Ritchie – Creator of the C programming language
Software: CGI in C
●
C language
●
imperative, procedural, general-purpose
●
1970s (Dennis Ritchie)
●
Use
●
Remains widely used in OSs, device drivers, embedded system applications, libraries...
●
Decreasingly used for application software
●
Hardly used for the web
Software: CGI in C
●
All code is contained within functions
●
Function parameters are passed by value
●
Pass-by-reference is simulated by passing pointers
#include <stdio.h>
int main (void)
{
printf ("hello, worldn");
return 0;
}
main.c
compiled, not interpreted
Software: CGI in C
●
Common Gateway Interface (CGI)
1.The user submits a web form on a web page (eg by clicking on a submit button)
2.The form's data is sent by browser to web server within an HTTP request with a URL
denoting a server CGI script in a cgi-bin directory
●
Written in a scripting language (eg Perl)
●
Or may be a compiled program (a C program in our case)
3.The web server (Apache in our case) launches the CGI script in a new process, passing
the form data to it
4.The output sent to standard output by the CGI script, usually in the form of HTML, is
passed to the client browser instead of being shown on-screen in a terminal window
Up to 400K times per day in 2014
Up to 2000 times / minute (30 times / second)
Software: CGI in C
“click”
logged access
HTML5 + CSS3 + JavaScript
server
MySQL database
swad-core
Software: CGI in C
●
Example:
●
HTML:
<form method="post" action="https://openswad.org/en">
<input type="hidden" name="act" value="1492">
<input type="hidden" name="ses" value="6BJQ...G46g">
<input type="hidden" name="Who" value="4">
<textarea name="Txt" rows="6" maxlength="1000"
placeholder="New post…"
class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH"
onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');">
</textarea>
<div id="id_EwuT...BhSs_6" style="">
...
<button type="submit" class="...">
Post
</button>
</div>
</form>
Software: CGI in C
HTML code
in user’s browser
●
HTML:
<form method="post" action="https://openswad.org/en">
<input type="hidden" name="act" value="1492">
<input type="hidden" name="ses" value="6BJQ...G46g">
<input type="hidden" name="Who" value="4">
<textarea name="Txt" rows="6" maxlength="1000"
placeholder="New post…"
class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH"
onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');">
</textarea>
<div id="id_EwuT...BhSs_6" style="">
...
<button type="submit" class="...">
Post
</button>
</div>
</form>
Software: CGI in C
ScriptAlias /en "/var/www/cgi-bin/swad/swad_en"
swad_en: CGI program written in C
●
HTML:
<form method="post" action="https://openswad.org/en">
<input type="hidden" name="act" value="1492">
<input type="hidden" name="ses" value="6BJQ...G46g">
<input type="hidden" name="Who" value="4">
<textarea name="Txt" rows="6" maxlength="1000"
placeholder="New post…"
class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH"
onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');">
</textarea>
<div id="id_EwuT...BhSs_6" style="">
...
<button type="submit" class="...">
Post
</button>
</div>
</form>
Software: CGI in C
Parameters passed to swad_en
●
HTML:
<form method="post" action="https://openswad.org/en">
<input type="hidden" name="act" value="1492">
<input type="hidden" name="ses" value="6BJQ...G46g">
<input type="hidden" name="Who" value="4">
<textarea name="Txt" rows="6" maxlength="1000"
placeholder="New post…"
class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH"
onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');">
</textarea>
<div id="id_EwuT...BhSs_6" style="">
...
<button type="submit" class="...">
Post
</button>
</div>
</form>
Software: CGI in C
Textarea used to write the post
●
HTML:
<form method="post" action="https://openswad.org/en">
<input type="hidden" name="act" value="1492">
<input type="hidden" name="ses" value="6BJQ...G46g">
<input type="hidden" name="Who" value="4">
<textarea name="Txt" rows="6" maxlength="1000"
placeholder="New post…"
class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH"
onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');">
</textarea>
<div id="id_EwuT...BhSs_6" style="">
...
<button type="submit" class="...">
Post
</button>
</div>
</form>
Software: CGI in C
Button clicked to submit the post
Software: CGI in C
●
Input:
Method ← getenv ("REQUEST_METHOD");
ContentType ← getenv ("CONTENT_TYPE");
if (!strcmp (Method,"GET")) { // GET method
ContentLength = strlen (getenv ("QUERY_STRING"));
/* Copy query string from environment variable */
QueryString = malloc (ContentLength + 1);
Str_Copy (QueryString,getenv ("QUERY_STRING"),ContentLength);
}
else { // POST method
ContentLength ← getenv ("CONTENT_LENGTH");
/* Copy query string from stdin */
QueryString = malloc (ContentLength + 1);
fread (QueryString,sizeof (char),ContentLength,stdin);
}
/* Parse QueryString creating linked list of parameters */
Par_CreateListOfParams ();
Simplified C code on the server (swad-core)
Software: CGI in C
●
Output:
fprintf (stdout,"Content-type: text/html;rnrn"
"<!DOCTYPE html>n");
HTM_TxtF ("<html lang="%s">n",
Lan_STR_LANG_ID[Gbl.Prefs.Language]);
HTM_Txt ("<head>n");
HTM_TxtF ("<link rel="stylesheet" href="%s/%s" type="text/css" />n",
Cfg_URL_SWAD_PUBLIC,CSS_FILE);
HTM_TxtF ("<script type="text/javascript" src="%s/%s">n",
Cfg_URL_SWAD_PUBLIC,JS_FILE);
HTM_Txt ("</script>n");
...
HTM_Txt ("</head>n");
HTM_Txt ("<body onload="init();">n");
/* Write HTML depending on action */
...
HTM_Txt ("</body>n");
HTM_Txt ("</html>n");
Simplified C code on the server (swad-core)
Software: CGI in C
●
Output:
void HTM_TxtF (const char *fmt,...) {
va_list ap;
int NumBytesPrinted;
char *Attr;
if (fmt)
if (fmt[0]) {
va_start (ap,fmt);
NumBytesPrinted = vasprintf (&Attr,fmt,ap);
va_end (ap);
if (NumBytesPrinted < 0) // -1 if no memory or any other error
Err_NotEnoughMemoryExit ();
/***** Print HTML *****/
HTM_Txt (Attr);
free (Attr);
}
}
Simplified C code on the server (swad-core)
void HTM_Txt (const char *Txt) {
if (Txt)
if (Txt[0])
fputs (Txt,Gbl.F.Out);
}
Software: CGI in C
●
To enable CGI in Apache*:
●
CentOS:
●
cd /etc/httpd/conf.d/
●
sudo gedit ssl.conf
●
Before </VirtualHost>, add the lines:
●
Ubuntu:
●
sudo a2enmod cgi
●
sudo a2enmod ssl
●
sudo a2ensite default-ssl
●
cd /etc/apache2/sites-available/
●
sudo gedit default-ssl.conf
●
Before </VirtualHost>, add the lines:
*More details in https://openswad.org/install/
ScriptAlias /ca "/usr/lib/cgi-bin/swad/swad_ca"
ScriptAlias /de "/usr/lib/cgi-bin/swad/swad_de"
ScriptAlias /en "/usr/lib/cgi-bin/swad/swad_en"
ScriptAlias /es "/usr/lib/cgi-bin/swad/swad_es"
ScriptAlias /fr "/usr/lib/cgi-bin/swad/swad_fr"
ScriptAlias /gn "/usr/lib/cgi-bin/swad/swad_gn"
ScriptAlias /it "/usr/lib/cgi-bin/swad/swad_it"
ScriptAlias /pl "/usr/lib/cgi-bin/swad/swad_pl"
ScriptAlias /pt "/usr/lib/cgi-bin/swad/swad_pt"
ScriptAlias /ca "/var/www/cgi-bin/swad/swad_ca"
ScriptAlias /de "/var/www/cgi-bin/swad/swad_de"
ScriptAlias /en "/var/www/cgi-bin/swad/swad_en"
ScriptAlias /es "/var/www/cgi-bin/swad/swad_es"
ScriptAlias /fr "/var/www/cgi-bin/swad/swad_fr"
ScriptAlias /gn "/var/www/cgi-bin/swad/swad_gn"
ScriptAlias /it "/var/www/cgi-bin/swad/swad_it"
ScriptAlias /pl "/var/www/cgi-bin/swad/swad_pl"
ScriptAlias /pt "/var/www/cgi-bin/swad/swad_pt"
Software: Database
“Information is the oil of the 21st century, and
analytics is the combustion engine.”
Peter Sondergaard – Former CEO of Gartner, Inc.
Software: Database
151 MySQL/MariaDB tables
Queries made through C API
Software: Other modules
“We build our computer systems the way we build our
cities: over time, without a plan, on top of ruins.”
Ellen Ullman – American computer programmer and author
Software: Other modules
●
User photos
●
Our own automatic system for detecting
faces and improving the quality of
photos, trained with 90K photos
●
Program written in C++ / OpenCV and
called from swad-core
●
Daniel J. Calandria Hernández
●
Jesús Mesa González
Software: Other modules
●
Photo average
●
Average / median
photo, pixel by pixel,
of all the students of a
degree
●
Program written in C++
/ OpenCV and called
from swad-core
Daniel J. Calandria
Hernández
Software: Other modules
●
API
●
It is possible to develop add-ons (plugins) that run on:
●
other servers
●
mobile devices
●
SWADroid, iSWAD, Triswados
●
The plugins interact with swad-core through its integrated API
●
https://openswad.org/api/
Software: Other modules
●
SWADroid
●
App for Android
●
https://play.google.com/store/apps/details?id=es.ugr.swad.swadroid
By Juan Miguel Boyero Corral (2010-2023)
●
https://github.com/Amab/SWADroid
Software: Other modules
●
Former developers:
Antonio Manuel Aguilera Malagón (2012, QR attendance control)
●
https://github.com/aguilerin/SWADroid
●
https://es.slideshare.net/antonioaguileramalagon/swadroid-pasar-lista-manual-de-usuario
Helena Rodríguez Gijón (2012, file download, registration in groups, notices)
●
https://es.slideshare.net/helenarguez/swa-droid-f4
●
https://swadroid.wordpress.com/2012/11/17/publicado-swadroid-0-10-1/
●
https://github.com/hrguez
José Antonio Guerrero Avilés (2014, course information, messages)
●
https://github.com/antonio314/SWADroid
●
https://es.slideshare.net/JoseAntonioGuerreroA/presentacion-proyecto-fin-de-carrera-ampliacion-de-swadroid
●
https://www.linkedin.com/in/antonioguerreroaviles/overlay/50268922/single-media-viewer/
●
https://www.linkedin.com/in/antonioguerreroaviles/overlay/50268914/single-media-viewer/
Software: Other modules
Alejandro Alcalde Barros (2014, user interface)
●
https://swadroid.wordpress.com/author/algui913/
●
https://github.com/elbaulp
Rubén Martín Hidalgo (2016, messages, attendance control)
●
https://github.com/romilgildo/SWADroid
●
https://www.youtube.com/watch?v=l-P9eoyZLuk
●
https://www.youtube.com/watch?v=A4onmR1Njic
Javier Bueno López (2020, geolocation)
●
https://github.com/JaviBL8/TFG
●
https://github.com/JaviBL8/SWADroid
Sergio Díaz Rueda (2020, games)
●
https://www.linkedin.com/in/sergio-d%C3%ADaz-rueda-b4a013211/
Software: Other modules
swad-core SWADroid
Affero GPL v3 license
https://github.com/acanas/swad-core
GPL v3 license
https://git.cuernodehipnos.es/Marown/SWADroid
387,435
C code lines
151
MySQL tables
39,216
Java code lines
17K
downloads
101 person-years
estimated effort*
9 person-years
estimated effort*
$5,579,446
estimated cost*
$500,680
estimated cost*
Other modules and more info: https://openswad.org/source
* According to the COCOMO model in Open Hub
Software: Other modules
●
iSWAD
●
App for iOS
https://apps.apple.com/es/app/iswad-platform/id1570162425
By Bate Ye (2021, Swift)
https://github.com/WolfYe98/iSWAD
Software: Other modules
●
Former developers:
Diego Montesinos Hervás (2011, Objective C)
https://github.com/diegort/iSWAD
Raúl Álvarez Hinojosa (2016, Swift)
https://github.com/Rauleinstein/iSWAD
Adrián Lara Roldán (2018, Swift)
https://github.com/mitomono/iSWAD
Timeline: Notes & Pubs
“You think you know when you learn, are more sure
when you can write, even more when you can teach,
but certain when you can program.”
Alan Perlis – Computer scientist and professor
Timeline: Notes & Pubs
Timeline: Notes & Pubs
●
24 files
●
12 .c + 12 .h
●
170 C functions
●
~14 functions / .c file
●
6662 non-blank lines (2% of swad-core)
●
5881 non-blank lines in .c files
●
~35 non-blank lines / function
●
781 non-blank lines in .h files
Timeline: Notes & Pubs
●
Timeline: set of publications
●
from a user
●
global
●
Only me
●
Followed users
●
All users
typedef enum
{
Tml_Usr_TIMELINE_USR,
Tml_Usr_TIMELINE_GBL,
} Tml_Usr_UsrOrGbl_t;
typedef enum
{
Usr_WHO_UNKNOWN,
Usr_WHO_ME,
Usr_WHO_SELECTED, // Not applicable to timeline
Usr_WHO_FOLLOWED,
Usr_WHO_ALL,
} Usr_Who_t;
Timeline: Notes & Pubs
●
Publication: · original note (26851, 75% of 35968)
· shared note ( 1456, 4% of 35968)
· comment to a note ( 7661, 21% of 35968)
typedef enum
{
Tml_Pub_UNKNOWN = 0,
Tml_Pub_ORIGINAL_NOTE = 1,
Tml_Pub_SHARED_NOTE = 2,
Tml_Pub_COMMENT_TO_NOTE = 3,
} TmlPub_Type_t;
struct TmlPub_Publication
{
long PubCod; // Publication code
long NotCod; // Note code
long PublisherCod; // Sharer or writer of the publication
TmlPub_Type_t Type; // Original note, shared note, comment
struct TmlPub_Publication *Next; // Used for chained list
};
*swad.ugr.es, nov 2023
Timeline: Notes & Pubs
●
Note: timeline post ( 5484, 20% of 26851)
public file ( 82, <1% of 26851)
call for exam ( 3067, 11% of 26851)
notice (18011, 67% of 26851)
forum post ( 207, 1% of 26851)
*swad.ugr.es, nov 2023
Timeline: Notes & Pubs
typedef enum
{
TmlNot_UNKNOWN = 0,
/* Start tab */
TmlNot_POST = 10, // Post written directly in timeline
/* Institution tab */
TmlNot_INS_DOC_PUB_FILE = 1, // Public file in documents of institution
TmlNot_INS_SHA_PUB_FILE = 2, // Public file in shared files of institution
/* Center tab */
TmlNot_CTR_DOC_PUB_FILE = 3, // Public file in documents of center
TmlNot_CTR_SHA_PUB_FILE = 4, // Public file in shared files of center
/* Degree tab */
TmlNot_DEG_DOC_PUB_FILE = 5, // Public file in documents of degree
TmlNot_DEG_SHA_PUB_FILE = 6, // Public file in shared files of degree
/* Course tab */
TmlNot_CRS_DOC_PUB_FILE = 7, // Public file in documents of course
TmlNot_CRS_SHA_PUB_FILE = 8, // Public file in shared files of course
/* Assessment tab */
TmlNot_CALL_FOR_EXAM = 9, // Call for exam in a course
/* Users tab */
/* Messages tab */
TmlNot_NOTICE = 12, // A public notice in a course
TmlNot_FORUM_POST = 11, // Post in global/swad forums
/* Analytics tab */
/* Profile tab */
} TmlNot_Type_t;
Timeline: Notes & Pubs
struct TmlNot_Note
{
long NotCod; // Unique code/identifier for each note
TmlNot_Type_t Type; // Timeline post, public file,
// call for exam, notice, forum post...
long UsrCod; // Publisher
long HieCod; // Hierarchy code
// (institution/center/degree/course)
long Cod; // Code of file, forum post,
// notice, timeline post...
bool Unavailable; // File, forum post, notice...
// unavailable (removed)
time_t DateTimeUTC; // Date-time of publication in UTC time
unsigned NumShared; // Number of times (users)
// this note has been shared
unsigned NumFavs; // Number of times (users)
// this note has been favourited
};
Timeline: Notes & Pubs
__________________
|@author |
| Note |
|__________________|
|@author |
| Comment 1 |
|______________|
|@author |
| Comment 2 |
|______________|
| |
| ... |
|______________|
|@author |
| Comment n |
|______________|
●
A note can have comments attached to it:
Timeline: Notes & Pubs
_tml_pubs______ _tml_comments
| | | |
| Publication p |---------->| Comment c |-----+
| (comment) | | (to note 2) | |
|_______________| |_____________| |
| | | | |
· ... · · ... · |
· ... · · ... · |
|_______________| |_____________| |
| | | | |
|Publication i+4|---------->| Comment 1 |---+ |
| (comment) | | (to note n) | | |
|_______________| |_____________| | |
| | (7661) | |
|Publication i+3|-- | |
|(original note)|  | |
|_______________|  _tml_notes_____ | | _cfe_exams_____
| |  | | | | | |
|Publication i+2|-- ---->| Note n |<-+ | | Call for exam | (5707)
|(original note)|  |(exam announc.)|-(3067)->|_______________|
|_______________|  |_______________| 11% __brw_files____
| |  | | | | |
|Publication i+1|-- ---->| Note n-1 |-(82)--->| Public file | (1649245)
|(original note)|  | (public file) | <1% |_______________|
|_______________|  |_______________| | _not_notices___
| |  | | | | |
| Publication i |-- ---->| Note n-2 |-(18011)>| Notice | (15571)
|(original note)|  | (notice) | 67% |_______________|
|_______________|  |_______________| | __tml_posts____
| |  | | | | |
· ... · ---->| Note n-3 |-(5484)->| Post s |
· ... · | (tl. post) | 20% | |
|_______________| |_______________| | |_______________|
| | | | | | |
| Publication 3 | · ... · | · ... · (5484)
| (shared note) |--- · ... · | · ... ·
|_______________|  |_______________| | |_______________|
| |  | | | | |
| Publication 2 | ---->| Note 2 |<---+ | Post 1 |
|(original note)|--------->| (tl. post) |-------->| |
|_______________| |_______________| |_______________|
| | | | _for_posts_____
| Publication 1 |--------->| Note 1 | | |
|(original note)| | (forum post) |-(207)-->| Forum post | (66891)
|_______________| |_______________| 1% |_______________|
(33504) (26851)
timeline posts
public files
calls for exams
notices
forum posts
notes
comments
publications *swad.ugr.es, nov 2023
Timeline: Database
“Don't forget to put the WHERE in the DELETE FROM.”
Jorge Rubira Santos – Entrepreneur and Youtuber
Timeline: Database
151 database tables
7 for timeline
Timeline: Database
mysql> SHOW TABLES LIKE 'tml_%';
+------------------------+
| Tables_in_swad (tml_%) |
+------------------------+
| tml_comments |
| tml_comments_fav |
| tml_notes |
| tml_notes_fav |
| tml_posts |
| tml_pubs |
| tml_timelines |
+------------------------+
7 rows in set (0.00 sec)
Timeline: Database
mysql> DESCRIBE tml_pubs;
+--------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+----------+------+-----+---------+----------------+
| PubCod | bigint | NO | PRI | NULL | auto_increment |
| NotCod | bigint | NO | MUL | NULL | |
| PublisherCod | int | NO | MUL | NULL | |
| PubType | tinyint | NO | MUL | NULL | |
| TimePublish | datetime | NO | MUL | NULL | |
+--------------+----------+------+-----+---------+----------------+
Timeline: Database
mysql> DESCRIBE tml_notes;
+-------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+----------------+
| NotCod | bigint | NO | PRI | NULL | auto_increment |
| NoteType | tinyint | NO | MUL | NULL | |
| Cod | int | NO | | -1 | |
| UsrCod | int | NO | MUL | NULL | |
| HieCod | int | NO | | -1 | |
| Unavailable | enum('N','Y') | NO | | N | |
| TimeNote | datetime | NO | MUL | NULL | |
+-------------+---------------+------+-----+---------+----------------+
mysql> DESCRIBE tml_notes_fav;
+---------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+----------------+
| FavCod | bigint | NO | PRI | NULL | auto_increment |
| NotCod | bigint | NO | MUL | NULL | |
| UsrCod | int | NO | MUL | NULL | |
| TimeFav | datetime | NO | | NULL | |
+---------+----------+------+-----+---------+----------------+
Timeline: Database
mysql> DESCRIBE tml_comments;
+--------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+----------+------+-----+---------+-------+
| PubCod | bigint | NO | PRI | NULL | |
| Txt | longtext | NO | MUL | NULL | |
| MedCod | int | NO | MUL | -1 | |
+--------+----------+------+-----+---------+-------+
mysql> DESCRIBE tml_comments_fav;
+---------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+----------------+
| FavCod | bigint | NO | PRI | NULL | auto_increment |
| PubCod | bigint | NO | MUL | NULL | |
| UsrCod | int | NO | MUL | NULL | |
| TimeFav | datetime | NO | | NULL | |
+---------+----------+------+-----+---------+----------------+
Timeline: Database
mysql> DESCRIBE tml_posts;
+--------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+----------+------+-----+---------+----------------+
| PstCod | int | NO | PRI | NULL | auto_increment |
| Txt | longtext | NO | MUL | NULL | |
| MedCod | int | NO | MUL | -1 | |
+--------+----------+------+-----+---------+----------------+
mysql> DESCRIBE cfe_exams;
+-------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+----------------+
| ExaCod | int | NO | PRI | NULL | auto_increment |
| ... | ... | ... | ... | ... | ... |
mysql> DESCRIBE brw_files;
+-----------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------------+------+-----+---------+----------------+
| FilCod | int | NO | PRI | NULL | auto_increment |
| ... | ... | ... | ... | ... | ... |
mysql> DESCRIBE not_notices;
+-----------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------+------+-----+---------+----------------+
| NotCod | int | NO | PRI | NULL | auto_increment |
| ... | ... | ... | ... | ... | ... |
mysql> DESCRIBE for_posts;
+-----------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------+------+-----+---------+----------------+
| PstCod | int | NO | PRI | NULL | auto_increment |
| ... | ... | ... | ... | ... | ... |
Timeline: Database
mysql> DESCRIBE tml_timelines;
+-----------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------+------+-----+---------+-------+
| SessionId | char(43) | NO | PRI | NULL | |
| NotCod | bigint | NO | PRI | NULL | |
+-----------+----------+------+-----+---------+-------+
mysql> DESCRIBE tml_tmp_timeline;
+--------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------+------+-----+---------+-------+
| NotCod | bigint | NO | PRI | NULL | NULL |
+--------+--------+------+-----+---------+-------+
What is being displayed on the screens of each of the users
What is being displayed on the current user's screen
Timeline: Database
●
Example of
database query:
unsigned TmlDB_GetPubDataByCod (long PubCod,
MYSQL_RES **mysql_res) {
return (unsigned)
DB_QuerySELECT (mysql_res,
"can not get data of publication",
"SELECT PubCod," // row[0]
"NotCod," // row[1]
"PublisherCod," // row[2]
"PubType" // row[3]
" FROM tml_pubs"
" WHERE PubCod=%ld",
PubCod);
}
void TmlPub_GetPubDataFromRow (MYSQL_RES *mysql_res,
struct TmlPub_Publication *Pub) {
MYSQL_ROW row;
row = mysql_fetch_row (mysql_res);
Pub->PubCod = Str_ConvertStrCodToLongCod (row[0]);
Pub->NotCod = Str_ConvertStrCodToLongCod (row[1]);
Pub->PublisherCod = Str_ConvertStrCodToLongCod (row[2]);
Pub->Type = TmlPub_GetPubTypeFromStr (row[3]);
}
Timeline: Database
unsigned long DB_QuerySELECT (MYSQL_RES **mysql_res,
const char *MsgError,
const char *fmt,...) {
va_list ap;
int NumBytesPrinted;
char *Query;
/***** Create query string *****/
va_start (ap,fmt);
NumBytesPrinted = vasprintf (&Query,fmt,ap);
va_end (ap);
if (NumBytesPrinted < 0) // -1 if no memory or any other error
Err_NotEnoughMemoryExit ();
/***** Do SELECT query *****/
return DB_QuerySELECTusingQueryStr (Query,mysql_res,MsgError);
}
static unsigned long DB_QuerySELECTusingQueryStr (char *Query,
MYSQL_RES **mysql_res,
const char *MsgError) {
int Result;
[...]
Result = mysql_query (&DB_Database.mysql,Query); // 0 on success
free (Query);
if (Result)
DB_ExitOnMySQLError (MsgError);
if ((*mysql_res = mysql_store_result (&DB_Database.mysql)) == NULL)
DB_ExitOnMySQLError (MsgError);
return (unsigned long) mysql_num_rows (*mysql_res);
}
Timeline: Getting pubs
“When I wrote this code, only God and I understood
what I did. Now only God knows.”
Unknown source
Timeline: Getting pubs
●
Our algorithm:
●
Select publications one by one in a loop
●
In each iteration:
●
Get the most recent publication (original, shared or comment)
checking that its note is not already retrieved
●
After getting a publication, save its note code to not get it again.
SELECT PubCod
FROM tml_pubs
WHERE NotCod NOT IN
(SELECT NotCod
FROM tml_tmp_timeline)
ORDER BY PubCod DESC
LIMIT 1;
Timeline: Getting pubs
●
Slower alternative (may need seconds for large tables):
●
Get the maximum PubCod, i.e more recent publication (original, shared or commment),
of every set of publications corresponding to the same note:
SELECT MAX(PubCod) AS NewestPubCod
FROM tml_pubs
GROUP BY NotCod
ORDER BY NewestPubCod DESC
LIMIT 10;
Timeline: Getting pubs
●
Restricting publications to mine and those I follow:
CREATE TEMPORARY TABLE fol_tmp_me_and_followed
(UsrCod INT NOT NULL,
UNIQUE INDEX(UsrCod)) ENGINE=MEMORY
SELECT my_usr_cod AS UsrCod
UNION
SELECT FollowedCod AS UsrCod
FROM usr_follow
WHERE FollowerCod=my_usr_cod;
------------------------------
SELECT tml_pubs.PubCod,
tml_pubs.NotCod,
tml_pubs.PublisherCod,
tml_pubs.PubType
FROM tml_pubs,
fol_tmp_me_and_followed
WHERE tml_pubs.PublisherCod=fol_tmp_me_and_followed.UsrCod
AND tml_pubs.NotCod NOT IN
(SELECT NotCod
FROM tml_tmp_timeline)
ORDER BY PubCod DESC
LIMIT 1;
mysql> DESCRIBE usr_follow;
+-------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------+------+-----+---------+-------+
| FollowerCod | int | NO | PRI | NULL | |
| FollowedCod | int | NO | PRI | NULL | |
| FollowTime | datetime | NO | MUL | NULL | |
+-------------+----------+------+-----+---------+-------+
Timeline: Getting pubs
●
Three types of timeline updates
_ ______________________
/ |______________________|
New < |______________________|
_|______________________|
_|_See_new_activity_(3)_|
/ |______________________|
| |______________________|
Recent < |______________________|
| |______________________|
_|______________________|
_|_______See_more_______|
/ |______________________|
| |______________________|
Old < |______________________|
| |______________________|
_|______________________|
typedef enum
{
Tml_GET_NEW_PUBS, // automatically
// from time
// to time (AJAX)
Tml_GET_REC_PUBS, // user clicks
// on menu
// or after
// editing
// timeline
Tml_GET_OLD_PUBS, // user clicks
// on bottom
// link (AJAX)
} Tml_WhatToGet_t;
Timeline: Getting pubs
tml_pubs
_____
|_____|11
|_____|10
_|_____| 9 <-- RangePubsToGet.Top
Get / |_____| 8
pubs | |_____| 7
from < |_____| 6
this | |_____| 5
range _|_____| 4
|_____| 3 <-- RangePubsToGet.Bottom
|_____| 2
|_____| 1
0
Timeline: Getting pubs
case Tml_GET_REC_PUBS: // Get some limited recent publications
/* First query to get initial timeline shown
==> no notes yet in current timeline table */
RangePubsToGet->Top = 0;
/* _ _____ 0 <-- RangePubsToGet.Top = +infinite
/ |_____| 8
Get | |_____| 7
pubs < |_____| 6
from | |_____| 5
all | |_____| 4
range . |_____| 3
. |_____| 2
. |_____| 1
0 <-- RangePubsToGet.Bottom = -infinite */
RangePubsToGet->Bottom = 0;
Timeline: Getting pubs
case Tml_GET_NEW_PUBS: // Get the publications (without limit)
// newer than last pub. code
/* Via AJAX automatically from time to time */
RangePubsToGet->Top = 0;
/* _ _____ 0 <-- RangePubsToGet.Top = +infinite
Get / |_____|11
these < |_____|10
pubs _|_____| 9
/ |_____| 8 <-- RangePubsToGet.Bottom = last pub. code
Pubs | |_____| 7
already < |_____| 6
shown | |_____| 5
| |_____| 4
. |_____| .
. |_____| .
. |_____| .
*/
RangePubsToGet->Bottom = Tml_DB_GetPubCodFromSession (Tml_Pub_LAST);
Timeline: Getting pubs
case Tml_GET_OLD_PUBS: // Get some limited publications
// older than first pub. code
/* Via AJAX when I click in link to get old publications */
RangePubsToGet->Top = Tml_DB_GetPubCodFromSession (Tml_Pub_FIRST);
/* _____
. |_____| .
. |_____| .
. |_____| .
Pubs | |_____| 8
already < |_____| 7
shown | |_____| 6
| |_____| 5
Get _|_____| 4 <-- RangePubsToGet.Top = first pub. code
pubs / |_____| 3
from < |_____| 2
this _|_____| 1
rage 0 <-- RangePubsToGet.Bottom = -infinite */
RangePubsToGet->Bottom = 0;
Timeline: Getting pubs
●
Restricting publications to range:
SELECT tml_pubs.PubCod,
tml_pubs.NotCod,
tml_pubs.PublisherCod,
tml_pubs.PubType
FROM tml_pubs,
fol_tmp_me_and_followed
WHERE tml_pubs.PublisherCod=fol_tmp_me_and_followed.UsrCod
AND tml_pubs.PubCod>bottom // if type == Tml_GET_NEW_PUBS
AND tml_pubs.PubCod<top // if type == Tml_GET_OLD_PUBS
AND tml_pubs.NotCod NOT IN
(SELECT NotCod
FROM tml_tmp_timeline)
ORDER BY PubCod DESC
LIMIT 1;
Timeline: Getting pubs
Timeline->Pubs.Top Pub #0
______ ______ Pub #1
|______|------>|______| ______ Pub #2
|______| -> |______| ______ Pub #3
|______| / |______| ->|______| ______
|______| / |______| / |______| ->|______|
|_Next_|-- |______| / |______| // |______|
more recent |_Next_|-- |______| // |______|
______ |_Next_|--/ |______|
|______|---------------------------------------------- |_NULL_|
older
Timeline->Pubs.Bottom
●
After getting the publications, the result is a chained list:
Timeline: Showing pubs
“There is beauty when something works
and works intuitively.”
Jonathan Paul Ive – Designer
Timeline: Showing pubs
_____
/ |_____| just_now_timeline_list (Posts retrieved automatically
| |_____| via AJAX from time to time.
| |_____| They are transferred inmediately
| | to new_timeline_list.)
Hidden < __v__
| |_____| new_timeline_list (Posts retrieved but hidden.
| |_____| When user clicks to view them,
| |_____| the most recent of each note
 |_____| is transferred
| to visible timeline_list.)
See new activity (0)
__v__
/ |_____| timeline_list (Posts visible on page)
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
 |_____|
^
See more
__|__
/ |_____| old_timeline_list (Posts just retrieved via AJAX
| |_____| when user clicks "see more".
| |_____| They are transferred inmediately
Hidden < |_____| to timeline_list.)
| |_____|
| |_____|
 |_____|
<ul id="just_now_timeline_list" ...>
</ul>
<ul id="new_timeline_list" ...>
</ul>
<div id="view_new_container" ...
style="display:none;">
<a href="" ...
onclick="moveNewTimelineToTimeline();
return false;">
See new activity
(<span id="view_new_count">
0
</span>)
</a>
</div>
<ul id="timeline_list" ...>
visible timeline
</ul>
<div id="view_old_container" ...>
<a href="" ...
onclick="...
refreshOldTimeline();
return false;">
...
See more
</a>
</div>
<ul id="old_timeline_list" ...>
</ul>
Timeline: Showing pubs
<head>
...
<script type="text/javascript" ...>
var delayNewTml = Cfg_TIME_TO_REFRESH_TIMELINE; // 2000 ms
function init() {
ActionAJAX = "SWAD_URL";
...
setTimeout('refreshNewTimeline()',delayNewTL);
...
}
</script>
<script type="text/javascript" ...>
var refreshParamIdSes = "ses=...";
var refreshParamNxtActNewPub = "act=...";
var refreshParamWho = "Who=...";
</script>
...
</head>
<body onload="init();">
...
</body>
●
Automatic refresh via AJAX every 2 s → 3 s → 4 s...
_____
/ |_____| just_now_timeline_list
| |_____|
| |_____|
| |
Hidden < __v__
| |_____| new_timeline_list
| |_____|
| |_____|
 |_____|
|
See new activity (0)
__v__
/ |_____| timeline_list
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
 |_____|
^
See more
__|__
/ |_____| old_timeline_list
| |_____|
| |_____|
Hidden < |_____|
| |_____|
| |_____|
 |_____|
Timeline: Showing pubs
var objXMLHttpReqNewTml = false;
function refreshNewTimeline () {
objXMLHttpReqNewTml = AJAXCreateObject(); // new XMLHttpRequest()
if (objXMLHttpReqNewTml) {
var RefreshParams = refreshParamNxtActNewPub + '&' +
refreshParamIdSes + '&' +
refreshParamWho;
objXMLHttpReqNewTml.onreadystatechange = readNewTimelineData;
objXMLHttpReqNewTml.open('POST',ActionAJAX,true);
objXMLHttpReqNewTml.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
objXMLHttpReqNewTml.send(RefreshParams);
}
}
_____
/ |_____| just_now_timeline_list
| |_____|
| |_____|
| |
Hidden < __v__
| |_____| new_timeline_list
| |_____|
| |_____|
 |_____|
|
See new activity (0)
__v__
/ |_____| timeline_list
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
 |_____|
^
See more
__|__
/ |_____| old_timeline_list
| |_____|
| |_____|
Hidden < |_____|
| |_____|
| |_____|
 |_____|
Timeline: Showing pubs
function readNewTimelineData () {
if (objXMLHttpReqNewTml.readyState == 4) // Check if data have been received
if (objXMLHttpReqNewTml.status == 200) {
// Access to UL for just now timeline
var justNowTimeline = document.getElementById('just_now_timeline_list');
if (justNowTimeline) {
// Update list of publications in just now timeline
justNowTimeline.innerHTML = objXMLHttpReqNewTml.responseText;
var numNotesJustGot = justNowTimeline.childNodes.length;
if (numNotesJustGot) {// New notes received
// Scripts in timeline got via AJAX not executed ==> execute them
evalScriptsInElem (justNowTimeline);
// Process maths
MathJax.typeset();
...
_____
/ |_____| just_now_timeline_list
| |_____|
| |_____|
| |
Hidden < __v__
| |_____| new_timeline_list
| |_____|
| |_____|
 |_____|
|
See new activity (0)
__v__
/ |_____| timeline_list
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
 |_____|
^
See more
__|__
/ |_____| old_timeline_list
| |_____|
| |_____|
Hidden < |_____|
| |_____|
| |_____|
 |_____|
Timeline: Showing pubs
...
// Move all the LI elements (notes) in UL 'just_now_timeline_list'
// ...to the top of UL 'new_timeline_list'
var newTimeline = document.getElementById('new_timeline_list');
for (var i=0; i<numNotesJustGot; i++) {
// Move node from just now timeline to new timeline
newTimeline.insertBefore(justNowTimeline.lastChild,
newTimeline.firstChild);
newTimeline.firstChild.className += " Tml_NEW_PUB";
}
// Update number of notes in new timeline
var viewNewCount = document.getElementById('view_new_count');
viewNewCount.innerHTML = newTimeline.childNodes.length;
// Unhide message with number of notes if hidden
var viewNewContainer = document.getElementById('view_new_container');
viewNewContainer.style.display = '';
}
}
// Global delay variable is set initially in swad-core
delayNewTml += 1000; // Increase one second on each call
setTimeout('refreshNewTimeline()',delayNewTml);
}
}
_____
/ |_____| just_now_timeline_list
| |_____|
| |_____|
| |
Hidden < __v__
| |_____| new_timeline_list
| |_____|
| |_____|
 |_____|
|
See new activity (0)
__v__
/ |_____| timeline_list
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
 |_____|
^
See more
__|__
/ |_____| old_timeline_list
| |_____|
| |_____|
Hidden < |_____|
| |_____|
| |_____|
 |_____|
_____
/ |_____| just_now_timeline_list
| |_____|
| |_____|
| |
Hidden < __v__
| |_____| new_timeline_list
| |_____|
| |_____|
 |_____|
|
See new activity (0)
__v__
/ |_____| timeline_list
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
 |_____|
^
See more
__|__
/ |_____| old_timeline_list
| |_____|
| |_____|
Hidden < |_____|
| |_____|
| |_____|
 |_____|
Timeline: Showing pubs
function moveNewTimelineToTimeline () {
// Move the LI elements (notes) in UL 'new_timeline_list'...
// ...to the top of UL 'timeline_list', only if not repeated before
var newTimeline = document.getElementById('new_timeline_list');
var numNewNotes = newTimeline.childNodes.length;
if (numNewNotes) {
var timeline = document.getElementById("timeline_list");
for (var i=1; i<=numNewNotes; i++) {
// Check if the last child (the oldest) in the new timeline...
// ...is the last ocurrence of the note
var mostRecentOcurrenceOfNote = true;
var lastChildIndex = numNewNotes - i;
var noteCode = newTimeline.lastChild.dataset.noteCode;
for (var j=0; j<lastChildIndex; j++)
if (newTimeline.childNodes[j].dataset.noteCode == noteCode) {
mostRecentOcurrenceOfNote = false;
break;
}
...
●
User clicks "See new activity" → View new pubs.
Timeline: Showing pubs
...
// Move or remove node from new timeline
if (mostRecentOcurrenceOfNote) {
// Move node from new timeline to timeline
timeline.insertBefore(newTimeline.lastChild,timeline.firstChild);
timeline.firstChild.className += " Tml_NEW_PUB";
}
else
// Remove last child (because is repeated in more recent pubs)
newTimeline.removeChild(newTimeline.lastChild);
}
}
// Reset number of new publications after moving
var viewNewCount = document.getElementById('view_new_count');
viewNewCount.innerHTML = 0;
// Hide link to view new publications after moving
var viewNewContainer = document.getElementById('view_new_container');
viewNewContainer.style.display = 'none';
}
_____
/ |_____| just_now_timeline_list
| |_____|
| |_____|
| |
Hidden < __v__
| |_____| new_timeline_list
| |_____|
| |_____|
 |_____|
|
See new activity (0)
__v__
/ |_____| timeline_list
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
 |_____|
^
See more
__|__
/ |_____| old_timeline_list
| |_____|
| |_____|
Hidden < |_____|
| |_____|
| |_____|
 |_____|
_____
/ |_____| just_now_timeline_list
| |_____|
| |_____|
| |
Hidden < __v__
| |_____| new_timeline_list
| |_____|
| |_____|
 |_____|
|
See new activity (0)
__v__
/ |_____| timeline_list
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
 |_____|
^
See more
__|__
/ |_____| old_timeline_list
| |_____|
| |_____|
Hidden < |_____|
| |_____|
| |_____|
 |_____|
Timeline: Showing pubs
var objXMLHttpReqOldTml = false;
function refreshOldTimeline () {
objXMLHttpReqOldTml = AJAXCreateObject (); // new XMLHttpRequest()
if (objXMLHttpReqOldTml) {
var refreshParams = refreshParamNxtActOldPub + '&' +
refreshParamIdSes;
if (typeof refreshParamUsr !== 'undefined') {
if (refreshParamUsr.length)
refreshParams += '&' + refreshParamUsr;
}
if (typeof refreshParamWho !== 'undefined') {
if (refreshParamWho.length)
refreshParams += '&' + refreshParamWho;
}
objXMLHttpReqOldTml.onreadystatechange = readOldTimelineData;
objXMLHttpReqOldTml.open('POST',actionAJAX,true);
objXMLHttpReqOldTml.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
objXMLHttpReqOldTml.send(refreshParams);
}
}
●
User clicks "See more..." → View old pubs.
Timeline: Showing pubs
function readOldTimelineData () {
if (objXMLHttpReqOldTml.readyState == 4) // Check if data have been received
if (objXMLHttpReqOldTml.status == 200) {
// Access to UL with the old timeline
var oldTimeline = document.getElementById('old_timeline_list');
if (oldTimeline) {
// Fill list of publications in old timeline
oldTimeline.innerHTML = objXMLHttpReqOldTml.responseText;
var countOldTimeline = oldTimeline.childNodes.length;
if (countOldTimeline) {
// Scripts in timeline got via AJAX not executed ==> execute them
evalScriptsInElem (oldTimeline);
// Process maths
MathJax.typeset();
// Move all elements in 'old_timeline_list to bottom of 'timeline_list'
var timeline = document.getElementById("timeline_list");
for (var i=0; i<countOldTimeline; i++)
timeline.appendChild(oldTimeline.firstChild);
}
else // No old publications retrieved, so we have reached the oldest pub.
// Hide container with link to get old publications
document.getElementById("view_old_pubs_container").style.display = 'none';
}
}
}
_____
/ |_____| just_now_timeline_list
| |_____|
| |_____|
| |
Hidden < __v__
| |_____| new_timeline_list
| |_____|
| |_____|
 |_____|
|
See new activity (0)
__v__
/ |_____| timeline_list
| |_____|
Visible | |_____|
on < |_____|
page | |_____|
| |_____|
 |_____|
^
See more
__|__
/ |_____| old_timeline_list
| |_____|
| |_____|
Hidden < |_____|
| |_____|
| |_____|
 |_____|
Timeline: Layout
“If you find an element of your interface requires
instructions, then you need to redesign it.”
Dan Rubin – Designer, photographer
Timeline: Layout
___________________________________________
| | 
| Top message: | > top message
|___________________________________________| /
| _____ | | |  
|| | | Author's name | Date-time | | |
||Auth.| |______________________|___________| | |
||photo| | | | author's |
||_____| | | > name, time |
| | Note | | and content |
| | content | | |
| | | | |
| |__________________________________| / |
| | | | |  |
| | Favs | Shared |Remove| | > note
| |_____________|_____________|______| | |
|________| | | |
| | List | | buttons |
| Comment| of | > and |
| icon | comments | | comments |
|________|__________________________________| | |
| | | |
| Form to write new comment | | |
|__________________________________| / /
Timeline: Layout
●
Top message
<div class="Tml_TOP_CONT Tml_TOP_PUBLISHER Tml_WIDTH">
<form method="post" action="https://openswad.org/en" id="form_..." ...>
<input type="hidden" name="act" value="1402">
<input type="hidden" name="ses" value="...">
<input type="hidden" name="OtherUsrCod" value="...">
<button type="submit" title="Another user's profile"
class="BT_LINK Tml_TOP_PUBLISHER">
user name
</button>
</form>
has shared:
</div>
Timeline: Layout
___________________________________________
| | 
| Top message: | > top message
|___________________________________________| /
| _____ | | |  
|| | | Author's name | Date-time | | |
||Auth.| |______________________|___________| | |
||photo| | | | author's |
||_____| | | > name, time |
| | Note | | and content |
| | content | | |
| | | | |
| |__________________________________| / |
| | | | |  |
| | Favs | Shared |Remove| | > note
| |_____________|_____________|______| | |
|________| | | |
| | List | | buttons |
| Comment| of | > and |
| icon | comments | | comments |
|________|__________________________________| | |
| | | |
| Form to write new comment | | |
|__________________________________| / /
Timeline: Layout
●
Photo
<div class="Tml_LEFT_PHOTO">
<form method="post" action="https://openswad.org/en" id="form_..." ...>
<input type="hidden" name="act" value="1402">
<input type="hidden" name="ses" value="...">
<input type="hidden" name="OtherUsrCod" value="...">
<button type="submit" class="BT_LINK">
<img src="user-photo.jpg" alt="" title="username" class="PHOTO45x60"
onmouseover="zoom(this,'user-photo.jpg','id_...');" onmouseout="noZoom();">
<div id="id_..." class="NOT_SHOWN">
<div class="ZOOM_TXT_LINE DAT_N_BOLD">first name<br>last name</div>
<div class="ZOOM_TXT_LINE DAT_SMALL_N">@nickname</div>
<div class="ZOOM_TXT_LINE DAT_SMALL">institution&nbsp;(country)</div>
<div class="ZOOM_TXT_LINE DAT_SMALL">
<div class="ZOOM_DEG" style="background-image:url('user-icon.svg');">degree</div>
</div>
<div class="ZOOM_TXT_LINE">
<span class="DAT_N_BOLD">1</span><span class="DAT_SMALL">&nbsp;Following&nbsp;</span>
<span class="DAT_N_BOLD">1</span><span class="DAT_SMALL">&nbsp;Followers</span>
</div>
</div>
</button>
</form>
</div>
Photo
caption
Timeline: Layout
___________________________________________
| | 
| Top message: | > top message
|___________________________________________| /
| _____ | | |  
|| | | Author's name | Date-time | | |
||Auth.| |______________________|___________| | |
||photo| | | | author's |
||_____| | | > name, time |
| | Note | | and content |
| | content | | |
| | | | |
| |__________________________________| / |
| | | | |  |
| | Favs | Shared |Remove| | > note
| |_____________|_____________|______| | |
|________| | | |
| | List | | buttons |
| Comment| of | > and |
| icon | comments | | comments |
|________|__________________________________| | |
| | | |
| Form to write new comment | | |
|__________________________________| / /
Timeline: Layout
●
Author's name, date-time and note content
<div class="Tml_RIGHT_CONT Tml_RIGHT_WIDTH">
<form method="post" action="https://openswad.org/en" id="form_..." ...>
<input type="hidden" name="act" value="1402">
<input type="hidden" name="ses" value="...">
<input type="hidden" name="OtherUsrCod" value="...">
<button type="submit" title="My public profile"
class="BT_LINK Tml_RIGHT_AUTHOR Tml_RIGHT_AUTHOR_WIDTH DAT_N_BOLD">
user name
</button>
</form>
<div id="id_..." class="Tml_RIGHT_TIME DAT_LIGHT">date,&nbsp;time</div>
<script type="text/javascript">
writeLocalDateHMSFromUTC ('id_...',unix-time,1,',&nbsp;',3,true,true,false,0x6);
</script>
<div class="Tml_TXT">
post content
</div>
</div>
Timeline: Layout
function writeLocalDateHMSFromUTC (id,TimeUTC,DateFormat,
Separator,Language,
WriteToday,WriteDateOnSameDay,
WriteWeekDay,WriteHMS) {
// HMS: Hour, Minutes, Seconds
var today = new Date();
var todayYea = today.getFullYear ();
var todayMon = today.getMonth () + 1;
var todayDay = today.getDate ();
var d = new Date();
var WriteDate;
var Yea,Mon,Day;
var DayOfWeek;
var Hou,Min,Sec;
var StrDat;
var StrMon;
var StrDay;
var StrHou;
var StrMin;
var StrSec;
d.setTime (TimeUTC * 1000);
Yea = d.getFullYear ();
Mon = d.getMonth () + 1;
Day = d.getDate ();
if (WriteDateOnSameDay)
WriteDate = true;
// Check to see if the last date has been initialized
else if (typeof writeLocalDateHMSFromUTC.lastd == 'undefined')
// lastd: static variable to remember date for the next call
// Not initialized
WriteDate = true;
else
WriteDate = (Yea !=
writeLocalDateHMSFromUTC.lastd.getFullYear () ||
Mon !=
writeLocalDateHMSFromUTC.lastd.getMonth () + 1 ||
Day !=
writeLocalDateHMSFromUTC.lastd.getDate ());
// Update last date for the next call
writeLocalDateHMSFromUTC.lastd = d;
/* Set date */
StrDat = '';
if (WriteDate) {
WriteToday = WriteToday && (Yea == todayYea &&
Mon == todayMon &&
Day == todayDay); // Date is today
if (WriteToday)
StrDat = txtToday[Language];
else
switch (DateFormat) {
case 0: // Dat_FORMAT_YYYY_MM_DD
StrMon = ((Mon < 10) ? '0' : '') + Mon;
StrDay = ((Day < 10) ? '0' : '') + Day;
StrDat = Yea.toString () + '-' +
StrMon + '-' +
StrDay;
break;
case 1: // Dat_FORMAT_DD_MONTH_YYYY
StrDat = Day.toString () + '&nbsp;' +
MonthsShort[Mon - 1] + '&nbsp;' +
Yea.toString ();
break;
case 2: // Dat_FORMAT_MONTH_DD_YYYY
StrDat = MonthsShort[Mon - 1] + '&nbsp;' +
Day.toString() + ',&nbsp;' +
Yea.toString ();
break;
default:
break;
}
if (WriteWeekDay) {
DayOfWeek = d.getDay ();
DayOfWeek = (DayOfWeek == 0) ? 6 : DayOfWeek - 1;
StrDat += Separator + DAYS[DayOfWeek];
}
StrDat += Separator;
}
/* Set HH:MM:SS */
StrHou = '';
StrMin = '';
StrSec = '';
if (WriteHMS & (1<<2)) {
// Bit 2 on => Write hour
Hou = d.getHours();
StrHou = ((Hou < 10) ? '0' : '') +
Hou;
if (WriteHMS & (1<<1)) {
// Bits 2,1 on => Write minutes
Min = d.getMinutes ();
StrMin = ((Min < 10) ? ':0' : ':') +
Min;
if (WriteHMS & 1) {
// Bits 2,1,0 on => Write seconds
Sec = d.getSeconds ();
StrSec = ((Sec < 10) ? ':0' : ':') +
Sec;
}
}
}
/* Write date and time */
document.getElementById (id).innerHTML = StrDat +
StrHou +
StrMin +
StrSec;
}
Timeline: Layout
___________________________________________
| | 
| Top message: | > top message
|___________________________________________| /
| _____ | | |  
|| | | Author's name | Date-time | | |
||Auth.| |______________________|___________| | |
||photo| | | | author's |
||_____| | | > name, time |
| | Note | | and content |
| | content | | |
| | | | |
| |__________________________________| / |
| | | | |  |
| | Favs | Shared |Remove| | > note
| |_____________|_____________|______| | |
|________| | | |
| | List | | buttons |
| Comment| of | > and |
| icon | comments | | comments |
|________|__________________________________| | |
| | | |
| Form to write new comment | | |
|__________________________________| / /
Timeline: Layout
___________________________________________________________________________
| div which content will be updated (parent of parent of form) |
| _____________________ _______ _____________________________________ |
| | div (parent of form)| | div | | div for users | |
| | _________________ | | for | | ______ ______ ______ ______ | |
| | | this form | | | num. | | | | | | | | | form | | |
| | | _____________ | | | of | | | user | | user | | user | | to | | |
| | | | | | | | users | | | 1 | | 2 | | 3 | | show | | |
| | | |_____________| | | | | | | | | | | | | all | | |
| | |_________________| | | | | |______| |______| |______| |______| | |
| |_____________________| |_______| |_____________________________________| |
|___________________________________________________________________________|
typedef enum
{
Tml_Usr_SHOW_FEW_USRS, // Show a few first favers/sharers
Tml_Usr_SHOW_ALL_USRS, // Show all favers/sharers
} Tml_Usr_HowManyUsrs_t;
Timeline: Layout
●
Favourite
<div class="Tml_BOTTOM_RIGHT Tml_RIGHT_WIDTH">
<div class="Tml_FOOT Tml_RIGHT_WIDTH">
<div id="fav_not_..." class="Tml_FAV_NOT Tml_FAV_NOT_WIDTH">
<div class="Tml_ICO">
<form method="post" action="https://openswad.org/en" id="form_..."
onsubmit="updateDivFaversSharers (this,'act=1512"
"&amp;ses=session&amp;NotCod=note-code');"
"return false;" ...>
<input type="image" src="...fav-icon.svg"
alt="Fav" title="Fav"
class="CONTEXT_OPT ICO_HIGHLIGHT CONTEXT_ICO_16x16">
</form>
</div>
<div class="Tml_NUM_USRS">&nbsp;0</div>
<div class="Tml_USRS"></div>
</div>
Timeline: Layout
●
Share
<div id="sha_not_..." class="Tml_SHA_NOT Tml_SHA_NOT_WIDTH">
<div class="Tml_ICO">
<form method="post" action="https://openswad.org/en" id="form_..."
onsubmit="updateDivFaversSharers (this,'act=1495"
"&amp;ses=session&amp;NotCod=note-code');"
"return false;" ...>
<input type="image" src="...share-icon.svg"
alt="Share" title="Share"
class="CONTEXT_OPT ICO_HIGHLIGHT CONTEXT_ICO_16x16">
</form>
</div>
<div class="Tml_NUM_USRS">&nbsp;0</div>
<div class="Tml_USRS"></div>
</div>
Timeline: Layout
●
Show all users
<div id="fav_not_..." class="Tml_FAV_NOT Tml_FAV_NOT_WIDTH">
<div class="Tml_ICO">fav icon</div>
<div class="Tml_NUM_USRS">number of users</div>
<div class="Tml_USRS">
<div class="Tml_SHARER">user</div>
...
<div class="Tml_SHARER">user</div>
<form method="post" action="https://openswad.org/en" id="form_..."
onsubmit="updateDivFaversSharers (this,'act=1767"
"&amp;ses=session&amp;NotCod=note-code');"
" return false;" ...>
<input type="image" src="...ellipsis-h.svg"
alt="View all" title="View all"
class="CONTEXT_OPT ICO_HIGHLIGHT CONTEXT_ICO_16x16">
</form>
</div>
</div>
Timeline: Layout
function updateDivFaversSharers (form,Params) {
var id = form.parentNode.parentNode.id;
var objXMLHttp = AJAXCreateObject ();
if (objXMLHttp) {
/* Send request to server */
objXMLHttp.onreadystatechange = function () {
if (objXMLHttp.readyState == 4) // Check if data have been received
if (objXMLHttp.status == 200)
if (id) {
var div = document.getElementById (id); // Access to DIV
if (div)
div.innerHTML = objXMLHttp.responseText; // Update DIV content
}
};
objXMLHttp.open ('POST',actionAJAX,true);
objXMLHttp.setRequestHeader ('Content-Type','application/x-www-form-urlencoded');
objXMLHttp.send (Params);
}
}
Timeline: Layout
___________________________________________
| | 
| Top message: | > top message
|___________________________________________| /
| _____ | | |  
|| | | Author's name | Date-time | | |
||Auth.| |______________________|___________| | |
||photo| | | | author's |
||_____| | | > name, time |
| | Note | | and content |
| | content | | |
| | | | |
| |__________________________________| / |
| | | | |  |
| | Favs | Shared |Remove| | > note
| |_____________|_____________|______| | |
|________| | | |
| | List | | buttons |
| Comment| of | > and |
| icon | comments | | comments |
|________|__________________________________| | |
| | | |
| Form to write new comment | | |
|__________________________________| / /
Timeline: Layout
●
Remove
<div class="Tml_REM">
<form method="post" action="https://openswad.org/en" id="form_..." ...>
<input type="hidden" name="act" value="1494">
<input type="hidden" name="ses" value="session">
<input type="hidden" name="Who" value="4">
<input type="hidden" name="NotCod" value="note-code">
<input type="image" src="remove-icon.svg" alt="Remove" title="Remove"
class="CONTEXT_OPT ICO_HIGHLIGHT CONTEXT_ICO_16x16">
</form>
</div>
</div>
</div>
Timeline: Layout
___________________________________________
| | 
| Top message: | > top message
|___________________________________________| /
| _____ | | |  
|| | | Author's name | Date-time | | |
||Auth.| |______________________|___________| | |
||photo| | | | author's |
||_____| | | > name, time |
| | Note | | and content |
| | content | | |
| | | | |
| |__________________________________| / |
| | | | |  |
| | Favs | Shared |Remove| | > note
| |_____________|_____________|______| | |
|________| | | |
| | List | | buttons |
| Comment| of | > and |
| icon | comments | | comments |
|________|__________________________________| | |
| | | |
| Form to write new comment | | |
|__________________________________| / /
Timeline: Layout
●
Comment icon
<div class="Tml_BOTTOM_LEFT">
<div id="id_..._ico" class="Tml_ICO_COM_OFF">
<a href="" onclick="toggleNewComment ('id_...');return false;">
<img src="comment-icon.svg" alt="Comment" title="Comment"
class="CONTEXT_ICO_16x16">
</a>
</div>
</div>
function toggleNewComment (id) {
var iconDiv = document.getElementById (id + '_ico');
iconDiv.className = (iconDiv.className == 'Tml_ICO_COM_OFF') ? 'Tml_ICO_COM_ON' :'Tml_ICO_COM_OFF';
toggleDisplay(id);
} function toggleDisplay (elementID) {
var element = document.getElementById (elementID);
var stl;
if (element) {
stl = element.style;
stl.display = (stl.display === 'none') ? '' : 'none';
}
}
Timeline: Layout
●
New comment
<div id="id_..." class="Tml_FORM_NEW_COM Tml_RIGHT_WIDTH" style="">
<div class="Tml_COM_PHOTO">...</div>
<div class="Tml_COM_CONT Tml_COMM_WIDTH">
<form method="post" action="https://openswad.org/en" id="..."
enctype="multipart/form-data" ...>
<input type="hidden" name="act" value="1503">
<input type="hidden" name="ses" value="session">
<input type="hidden" name="Who" value="4">
<input type="hidden" name="NotCod" value="note code">
<textarea name="Txt" rows="6" maxlength="1000" placeholder="New comment…"
class="Tml_COM_TEXTAREA Tml_COMM_WIDTH"
onfocus="expandTextarea (this,'id_...','6');">
</textarea>
<div id="id_..." style="">
<div class="HELP_EDIT">...</div>
<div class="MED_UPLOADER">...</div>
<button type="submit" class="BT_SUBMIT_INLINE BT_CREATE">Post</button>
</div>
</form>
</div>
</div>
function expandTextarea (textareaElem,idButton,rows) {
textareaElem.rows = rows;
document.getElementById (idButton).style.display = '';
}
Timeline: Layout
___________________________________________
| | 
| Top message: | > top message
|___________________________________________| /
| _____ | | |  
|| | | Author's name | Date-time | | |
||Auth.| |______________________|___________| | |
||photo| | | | author's |
||_____| | | > name, time |
| | Note | | and content |
| | content | | |
| | | | |
| |__________________________________| / |
| | | | |  |
| | Favs | Shared |Remove| | > note
| |_____________|_____________|______| | |
|________| | | |
| | List | | buttons |
| Comment| of | > and |
| icon | comments | | comments |
|________|__________________________________| | |
| | | |
| Form to write new comment | | |
|__________________________________| / /
Timeline: Layout
___________________________________________
| _____ | | |  
|| | | Author's name | Date-time | | |
||Auth.| |______________________|___________| | |
||photo| | | | author's |
||_____| | | > name, time |
| | Comment | | and content > comment
| | content | | |
| | | | |
| |__________________________________| / |
| | | |  |
| | Favs |Remove| > buttons |
|________|___________________________|______| / /
●
Layout and code similar to those described for the notes.
Timeline: Layout
Before clicking "See prev..." --> After clicking "See prev..."
_________________________________ _________________________________
| div con_<id> | | div con_<id> |
| (hidden) | | (visible) |
| _____________________________ | | _____________________________ |
| | v See only the latest | | | | v See only the latest | |
| |_________(contract)__________| | | |_________(contract)__________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| div <id> | | div <id> updated |
| which content | | _____________________________ |
| will be updated via AJAX | | | ul com_<id> | |
| (parent of parent of form) | | | _________________________ | |
| | | | | li (comment 1) | | |
| | | | |_________________________| | |
| | | | | ... | | |
| | | | |_________________________| | |
| | | | | li (comment n) | | |
| | --> | | |_________________________| | |
| | | |_____________________________| |
| _____________________________ | | _____________________________ |
| | div exp_<id> | | | | div exp_<id> | |
| | (visible) | | | | (hidden) | |
| | _________________________ | | | | | |
| | | form | | | | | | |
| | | _____________________ | | | | | _____________________ | |
| | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | |
| | | |_______(expand)______| | | | | | |_______(expand)______| | |
| | |_________________________| | | | | | |
| |_____________________________| | | |_____________________________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| ul | | ul |
| _________________________ | | _________________________ |
| | li (comment n+1) | | | | li (comment n+1) | |
| |_________________________| | | |_________________________| |
| | ... | | | | ... | |
| |_________________________| | | |_________________________| |
| | li (comment m) | | | | li (comment m) | |
| |_________________________| | | |_________________________| |
|_________________________________| |_________________________________|
●
Only the last comments are displayed.
Timeline: Layout
Before clicking "See prev..." --> After clicking "See prev..."
_________________________________ _________________________________
| div con_<id> | | div con_<id> |
| (hidden) | | (visible) |
| _____________________________ | | _____________________________ |
| | v See only the latest | | | | v See only the latest | |
| |_________(contract)__________| | | |_________(contract)__________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| div <id> | | div <id> updated |
| which content | | _____________________________ |
| will be updated via AJAX | | | ul com_<id> | |
| (parent of parent of form) | | | _________________________ | |
| | | | | li (comment 1) | | |
| | | | |_________________________| | |
| | | | | ... | | |
| | | | |_________________________| | |
| | | | | li (comment n) | | |
| | --> | | |_________________________| | |
| | | |_____________________________| |
| _____________________________ | | _____________________________ |
| | div exp_<id> | | | | div exp_<id> | |
| | (visible) | | | | (hidden) | |
| | _________________________ | | | | | |
| | | form | | | | | | |
| | | _____________________ | | | | | _____________________ | |
| | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | |
| | | |_______(expand)______| | | | | | |_______(expand)______| | |
| | |_________________________| | | | | | |
| |_____________________________| | | |_____________________________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| ul | | ul |
| _________________________ | | _________________________ |
| | li (comment n+1) | | | | li (comment n+1) | |
| |_________________________| | | |_________________________| |
| | ... | | | | ... | |
| |_________________________| | | |_________________________| |
| | li (comment m) | | | | li (comment m) | |
| |_________________________| | | |_________________________| |
|_________________________________| |_________________________________|
●
Only the last comments are displayed.
●
You can click on "See previous".
Timeline: Layout
Before clicking "See prev..." --> After clicking "See prev..."
_________________________________ _________________________________
| div con_<id> | | div con_<id> |
| (hidden) | | (visible) |
| _____________________________ | | _____________________________ |
| | v See only the latest | | | | v See only the latest | |
| |_________(contract)__________| | | |_________(contract)__________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| div <id> | | div <id> updated |
| which content | | _____________________________ |
| will be updated via AJAX | | | ul com_<id> | |
| (parent of parent of form) | | | _________________________ | |
| | | | | li (comment 1) | | |
| | | | |_________________________| | |
| | | | | ... | | |
| | | | |_________________________| | |
| | | | | li (comment n) | | |
| | --> | | |_________________________| | |
| | | |_____________________________| |
| _____________________________ | | _____________________________ |
| | div exp_<id> | | | | div exp_<id> | |
| | (visible) | | | | (hidden) | |
| | _________________________ | | | | | |
| | | form | | | | | | |
| | | _____________________ | | | | | _____________________ | |
| | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | |
| | | |_______(expand)______| | | | | | |_______(expand)______| | |
| | |_________________________| | | | | | |
| |_____________________________| | | |_____________________________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| ul | | ul |
| _________________________ | | _________________________ |
| | li (comment n+1) | | | | li (comment n+1) | |
| |_________________________| | | |_________________________| |
| | ... | | | | ... | |
| |_________________________| | | |_________________________| |
| | li (comment m) | | | | li (comment m) | |
| |_________________________| | | |_________________________| |
|_________________________________| |_________________________________|
●
Only the last comments are displayed.
●
You can click on "See previous".
●
Previous comments are retrieved via
AJAX.
Timeline: Layout
Before clicking "See prev..." --> After clicking "See prev..."
_________________________________ _________________________________
| div con_<id> | | div con_<id> |
| (hidden) | | (visible) |
| _____________________________ | | _____________________________ |
| | v See only the latest | | | | v See only the latest | |
| |_________(contract)__________| | | |_________(contract)__________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| div <id> | | div <id> updated |
| which content | | _____________________________ |
| will be updated via AJAX | | | ul com_<id> | |
| (parent of parent of form) | | | _________________________ | |
| | | | | li (comment 1) | | |
| | | | |_________________________| | |
| | | | | ... | | |
| | | | |_________________________| | |
| | | | | li (comment n) | | |
| | --> | | |_________________________| | |
| | | |_____________________________| |
| _____________________________ | | _____________________________ |
| | div exp_<id> | | | | div exp_<id> | |
| | (visible) | | | | (hidden) | |
| | _________________________ | | | | | |
| | | form | | | | | | |
| | | _____________________ | | | | | _____________________ | |
| | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | |
| | | |_______(expand)______| | | | | | |_______(expand)______| | |
| | |_________________________| | | | | | |
| |_____________________________| | | |_____________________________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| ul | | ul |
| _________________________ | | _________________________ |
| | li (comment n+1) | | | | li (comment n+1) | |
| |_________________________| | | |_________________________| |
| | ... | | | | ... | |
| |_________________________| | | |_________________________| |
| | li (comment m) | | | | li (comment m) | |
| |_________________________| | | |_________________________| |
|_________________________________| |_________________________________|
●
Only the last comments are displayed.
●
You can click on "See previous".
●
Previous comments are retrieved via
AJAX.
●
"See previous" is hidden.
Timeline: Layout
Before clicking "See prev..." --> After clicking "See prev..."
_________________________________ _________________________________
| div con_<id> | | div con_<id> |
| (hidden) | | (visible) |
| _____________________________ | | _____________________________ |
| | v See only the latest | | | | v See only the latest | |
| |_________(contract)__________| | | |_________(contract)__________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| div <id> | | div <id> updated |
| which content | | _____________________________ |
| will be updated via AJAX | | | ul com_<id> | |
| (parent of parent of form) | | | _________________________ | |
| | | | | li (comment 1) | | |
| | | | |_________________________| | |
| | | | | ... | | |
| | | | |_________________________| | |
| | | | | li (comment n) | | |
| | --> | | |_________________________| | |
| | | |_____________________________| |
| _____________________________ | | _____________________________ |
| | div exp_<id> | | | | div exp_<id> | |
| | (visible) | | | | (hidden) | |
| | _________________________ | | | | | |
| | | form | | | | | | |
| | | _____________________ | | | | | _____________________ | |
| | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | |
| | | |_______(expand)______| | | | | | |_______(expand)______| | |
| | |_________________________| | | | | | |
| |_____________________________| | | |_____________________________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| ul | | ul |
| _________________________ | | _________________________ |
| | li (comment n+1) | | | | li (comment n+1) | |
| |_________________________| | | |_________________________| |
| | ... | | | | ... | |
| |_________________________| | | |_________________________| |
| | li (comment m) | | | | li (comment m) | |
| |_________________________| | | |_________________________| |
|_________________________________| |_________________________________|
●
Only the last comments are displayed.
●
You can click on "See previous".
●
Previous comments are retrieved via
AJAX.
●
"See previous" is hidden.
●
"See only the last" is unhidden.
Timeline: Layout
Before clicking "See prev..." --> After clicking "See prev..."
_________________________________ _________________________________
| div con_<id> | | div con_<id> |
| (hidden) | | (visible) |
| _____________________________ | | _____________________________ |
| | v See only the latest | | | | v See only the latest | |
| |_________(contract)__________| | | |_________(contract)__________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| div <id> | | div <id> updated |
| which content | | _____________________________ |
| will be updated via AJAX | | | ul com_<id> | |
| (parent of parent of form) | | | _________________________ | |
| | | | | li (comment 1) | | |
| | | | |_________________________| | |
| | | | | ... | | |
| | | | |_________________________| | |
| | | | | li (comment n) | | |
| | --> | | |_________________________| | |
| | | |_____________________________| |
| _____________________________ | | _____________________________ |
| | div exp_<id> | | | | div exp_<id> | |
| | (visible) | | | | (hidden) | |
| | _________________________ | | | | | |
| | | form | | | | | | |
| | | _____________________ | | | | | _____________________ | |
| | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | |
| | | |_______(expand)______| | | | | | |_______(expand)______| | |
| | |_________________________| | | | | | |
| |_____________________________| | | |_____________________________| |
|_________________________________| |_________________________________|
_________________________________ _________________________________
| ul | | ul |
| _________________________ | | _________________________ |
| | li (comment n+1) | | | | li (comment n+1) | |
| |_________________________| | | |_________________________| |
| | ... | | | | ... | |
| |_________________________| | | |_________________________| |
| | li (comment m) | | | | li (comment m) | |
| |_________________________| | | |_________________________| |
|_________________________________| |_________________________________|
●
Only the last comments are displayed.
●
You can click on "See previous".
●
Previous comments are retrieved via
AJAX.
●
"See previous" is hidden.
●
"See only the last" is unhidden.
●
From now on no AJAX is needed,
sections are hidden / unhidden using
JavaScript.
Timeline: Layout
<div class="MED_UPLOADER">
<div id="id_..._med_ico">
<a href="" onclick="mediaActivateMediaUploader('id_...');"
"return false;">
<img src="...paperclip.svg" alt="Multimedia"
title="Multimedia" class="ICO_HIGHLIGHT ICOx16">
</a>
</div>
<div id="id_..._med_upl" style="display:none;">
<div class="FRAME_CONT">
<div class="FRAME">
<div class="FRAME_ICO">
<div class="FRAME_ICO_RIGHT">
help-link
</div>
</div>
<div class="FRAME_TITLE FRAME_TITLE_SMALL">
Multimedia
</div>
<input type="hidden" name="MedAct" value="2">
_container_____________________________________________
| _<id>_med_ico |
| |____Clip_____| |
| |
| _container <id>_med_upl_(initially hidden)_________ |
| | _box___________________________________________ | |
| | | ? | | |
| | | Multimedia | | |
| | | | | |
| | | _prefs_container___________________ | | |
| | | | _pref_container_________________ | | | |
| | | | | _______ _______ _______ | | | | |
| | | | | | Image/| |YouTube| | Embed | | | | | |
| | | | | |_video_| |_______| |_______| | | | | |
| | | | |_______________________________| | | | |
| | | |___________________________________| | | |
| | | _file_container____________________________ | | |
| | | | ___________ | | | |
| | | | |_Browse..._| No file selected. | | | |
| | | |___________________________________________| | | |
| | | _URL_container_____________________________ | | |
| | | | _______________________________________ | | | |
| | | | |_Link__________________________________| | | | |
| | | |___________________________________________| | | |
| | | _title_container___________________________ | | |
| | | | _______________________________________ | | | |
| | | | |_Title/attribution_____________________| | | | |
| | | |___________________________________________| | | |
| | |_______________________________________________| | |
| |___________________________________________________| |
|_______________________________________________________|
function mediaActivateMediaUploader (id) {
document.getElementById (id + '_med_ico').style.display = 'none';
document.getElementById (id + '_med_upl').style.display = '';
}
Timeline: Layout
<div class="PREF_CONTS">
<div class="PREF_CONT">
<div id="id_..._ico_upl" class="PREF_OFF">
<a href="" onclick="mediaClickOnActivateUpload('id_...');"
"return false;">
<img src="...photo-video.svg" alt="Image/video"
title="Image/video" class="ICO_HIGHLIGHT ICOx16">
</a>
</div>
<div id="id_..._ico_you" class="PREF_OFF">
<a href="" onclick="mediaClickOnActivateYoutube('id_...');"
"return false;">
<img src="...youtube-brands.svg" alt="YouTube"
title="YouTube" class="ICO_HIGHLIGHT ICOx16">
</a>
</div>
<div id="id_..._ico_emb" class="PREF_OFF">
<a href="" onclick="mediaClickOnActivateEmbed('id_...');"
"return false;">
<img src="...code.svg" alt="Embed"
title="Embed" class="ICO_HIGHLIGHT ICOx16">
</a>
</div>
</div>
</div>
_container_____________________________________________
| _<id>_med_ico |
| |____Clip_____| |
| |
| _container <id>_med_upl_(initially hidden)_________ |
| | _box___________________________________________ | |
| | | ? | | |
| | | Multimedia | | |
| | | | | |
| | | _prefs_container___________________ | | |
| | | | _pref_container_________________ | | | |
| | | | | _______ _______ _______ | | | | |
| | | | | | Image/| |YouTube| | Embed | | | | | |
| | | | | |_video_| |_______| |_______| | | | | |
| | | | |_______________________________| | | | |
| | | |___________________________________| | | |
| | | _file_container____________________________ | | |
| | | | ___________ | | | |
| | | | |_Browse..._| No file selected. | | | |
| | | |___________________________________________| | | |
| | | _URL_container_____________________________ | | |
| | | | _______________________________________ | | | |
| | | | |_Link__________________________________| | | | |
| | | |___________________________________________| | | |
| | | _title_container___________________________ | | |
| | | | _______________________________________ | | | |
| | | | |_Title/attribution_____________________| | | | |
| | | |___________________________________________| | | |
| | |_______________________________________________| | |
| |___________________________________________________| |
|_______________________________________________________|
_container_____________________________________________
| _<id>_med_ico |
| |____Clip_____| |
| |
| _container <id>_med_upl_(initially hidden)_________ |
| | _box___________________________________________ | |
| | | ? | | |
| | | Multimedia | | |
| | | | | |
| | | _prefs_container___________________ | | |
| | | | _pref_container_________________ | | | |
| | | | | _______ _______ _______ | | | | |
| | | | | | Image/| |YouTube| | Embed | | | | | |
| | | | | |_video_| |_______| |_______| | | | | |
| | | | |_______________________________| | | | |
| | | |___________________________________| | | |
| | | _file_container____________________________ | | |
| | | | ___________ | | | |
| | | | |_Browse..._| No file selected. | | | |
| | | |___________________________________________| | | |
| | | _URL_container_____________________________ | | |
| | | | _______________________________________ | | | |
| | | | |_Link__________________________________| | | | |
| | | |___________________________________________| | | |
| | | _title_container___________________________ | | |
| | | | _______________________________________ | | | |
| | | | |_Title/attribution_____________________| | | | |
| | | |___________________________________________| | | |
| | |_______________________________________________| | |
| |___________________________________________________| |
|_______________________________________________________|
Timeline: Layout
<input type="hidden" id="id_..._par_upl" name="MedFrm"
value="1" disabled>
<input type="hidden" id="id_..._par_you" name="MedFrm"
value="2" disabled>
<input type="hidden" id="id_..._par_emb" name="MedFrm"
value="3" disabled>
<div>
<input type="file" name="MedFil" accept="image/,video/"
id="id_..._fil" class="Tml_MED_INPUT_WIDTH"
style="display:none;" disabled>
</div>
<div>
<input type="url" name="MedURL" maxlength="255" value=""
id="id_..._url" class="Tml_MED_INPUT_WIDTH"
placeholder="Link" style="display:none;" disabled>
</div>
<div>
<input type="text" name="MedTit" maxlength="127" value=""
id="id_..._tit" class="Tml_MED_INPUT_WIDTH"
placeholder="Title/attribution"
style="display:none;" disabled>
</div>
</div>
</div>
</div>
</div>
</div>
Timeline: Layout
_container_____________________________________________
| _<id>_med_ico |
| |____Clip_____| |
| |
| _container <id>_med_upl_(initially hidden)_________ |
| | _box___________________________________________ | |
| | | ? | | |
| | | Multimedia | | |
| | | | | |
| | | _prefs_container___________________ | | |
| | | | _pref_container_________________ | | | |
| | | | | _______ _______ _______ | | | | |
| | | | | | Image/| |YouTube| | Embed | | | | | |
| | | | | |_video_| |_______| |_______| | | | | |
| | | | |_______________________________| | | | |
| | | |___________________________________| | | |
| | | _file_container____________________________ | | |
| | | | ___________ | | | |
| | | | |_Browse..._| No file selected. | | | |
| | | |___________________________________________| | | |
| | | _URL_container_____________________________ | | |
| | | | _______________________________________ | | | |
| | | | |_Link__________________________________| | | | |
| | | |___________________________________________| | | |
| | | _title_container___________________________ | | |
| | | | _______________________________________ | | | |
| | | | |_Title/attribution_____________________| | | | |
| | | |___________________________________________| | | |
| | |_______________________________________________| | |
| |___________________________________________________| |
|_______________________________________________________|
function mediaClickOnActivateUpload (id) {
var par_upl = document.getElementById (id + '_par_upl');
if (par_upl.disabled) { // Click on highlighted icon
// par_upl already got
var par_you = document.getElementById (id + '_par_you');
var par_emb = document.getElementById (id + '_par_emb');
var ico_upl = document.getElementById (id + '_ico_upl');
var ico_you = document.getElementById (id + '_ico_you');
var ico_emb = document.getElementById (id + '_ico_emb');
var fil = document.getElementById (id + '_fil');
var url = document.getElementById (id + '_url');
var tit = document.getElementById (id + '_tit');
// Enable upload, disable others
par_upl.disabled = false; // Enable upload
par_you.disabled = true; // Disable youtube
par_emb.disabled = true; // Disable embed
ico_upl.className = 'PREF_ON'; // Highlighted upload icon
ico_you.className = 'PREF_OFF'; // Normal youtube icon
ico_emb.className = 'PREF_OFF'; // Normal embed icon
fil.style.display = ''; // Show file input
fil.disabled = false; // Enable file input
url.style.display = ''; // Show URL input
url.disabled = false; // Enable URL input
tit.style.display = ''; // Show title input
tit.disabled = false; // Enable title input
}
else // Click on shadowed icon
mediaDisableAll (id);
}
Timeline: Inserting links
“Some of the best programming is done on paper.
Putting it into the computer is just a minor detail.”
Max Kanat-Alexander – Software Engineer at Google, author of Code Simplicity
Timeline: Inserting links
●
1. Parse string creating a list of links (URLs or nicknames)
●
Hi @admin, can I use https://openswad.org for free?
______ ______ ______ ______
|______|<-- -->|______|<-- -->|______|<-- -->|______|<--- LastLink
|______| / |______| / |______| / |______|
|______| / |______| / |______| / |______|
|_NULL_| / ---|_Prev_| / ---|_Prev_| / ---|_Prev_|
|_Next_|-- |_Next_|-- |_Next_|-- |_NULL_|
struct ALn_Link
{
ALn_LinkType_t Type; // URL or nickname?
struct ALn_Substring URLorNick; // Link text
struct ALn_Substring NickAnchor[3]; // Pointer to anchors if nick
size_t LengthAddedUpToHere; // Total length of extra HTML code...
// ...added up to this link (included)
struct ALn_Link *Prev; // Pointer to previous link
struct ALn_Link *Next; // Pointer to next link
};
struct ALn_Substring
{
// Pointer to
// the first
// char of
// substring
char *Str;
// Length of
// the substring
size_t Len;
};
Timeline: Inserting links
●
2. For each link in the list, going back from last to first:
●
1 Move forward the text after the link
●
2 Copy the 3rd part of the anchor
●
3 Move forward the link
_______________________________
|H|i|_|@|a|d|m|i|n|,|_|c|a|n|...|
| | | | | | | | | | | | | | |
| | | | | | | | | ___________________________________________1____
| | |            
| | | ______________________________3____ | | | | | |
| | |             | | | | | |
| | | ______5____ | | | | | | | | | | | |
| | |       | | | | | | | | | | | |
| | | 6 | | | | | | 4 | | | | | | 2 | | | | | |
v v v anchor#1 v v v v v v anchor#2 v v v v v v anchor#3 v v v v v v
___________________________________________________________________________
|H|i|_|<|_|_|_|_|@|a|d|m|i|n|_|_|_|_|>|@|a|d|m|i|n|<|_|_|_|_|>|,|_|c|a|n|...|
●
4 Copy the 2nd part of the anchor
●
5 Copy the link into the anchor
●
6 Copy the 1st part of the anchor
Miss: Written in C
“The C language combines
all the power of assembly language
with all the ease-of-use of assembly language.”
Mark Pearce – Freelance consultant and developer
Miss: Written in C
●
swad-core written from scratch in C
✘Disadvantages:
●
lack of specialized library functions for the web
●
rejected by potential collaborators who see it as an old programming
language
Hit: Written in C
“The C language combines
all the power of assembly language
with all the ease-of-use of assembly language.”
Mark Pearce – Freelance consultant and developer
Hit: Written in C
●
swad-core written from scratch in C
Advantages:
●
It requires few resources
●
+ speed
●
- memory
●
swad-core executable: 3.2 MB
●
Functional even in a Raspberry Pi
●
Reliability
●
Compile-time error detection
●
It works 24 hours, fast and almost without failures
●
Source code stability over time
Hit: It's fast, you don't
need a cluster
“Software is getting slower more rapidly than
hardware is becoming faster.” (Wirth's law)
Niklaus Wirth – Designer of several programming languages, including Pascal
●
SWAD-UGR former servers (1999-2016)
2nd: 2004-2006
Pentium 4 HT
RAM 2 GiB
2 HD 160 GB
Fedora 3
3rd: 2007-2008
Core 2 Duo
RAM 4 GiB
2 HD 500 GB
Fedora 6
4th: 2009-2010
Core 2 Quad
RAM 4 GiB
2 HD 146 GB
2 HD 1 TB
Fedora 10
5th: 2011-2016
2 Xeon Quad
RAM 24 GiB
4 HD 146 GB
4 HD 500 GB
CentOS 5.7
1st: 1999-2003
Shared server
Hit: It's fast, you don't need a cluster
Hit: It's fast, you don't need a cluster
●
SWAD-UGR current server (6th: 2016-2023)
●
HP Proliant DL160 G9, 2 Xeon with 6 cores, RAM 32 GiB
4 HD 146 GB
SAS 15000 rpm
RAID 1+0 (292 GB)
SO CentOS 7.2
MySQL database
4 HD 1 TB
SAS 7200 rpm
RAID 5 (3 TB)
Web files
( /var/www )
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf
SWAD-24-years.pdf

More Related Content

What's hot

Grid computing notes
Grid computing notesGrid computing notes
Grid computing notesSyed Mustafa
 
CS878 Green Computing Anna University Question Paper
CS878 Green Computing Anna University Question Paper CS878 Green Computing Anna University Question Paper
CS878 Green Computing Anna University Question Paper Gobinath Subramaniam
 
File replication
File replicationFile replication
File replicationKlawal13
 
Chapter 8 distributed file systems
Chapter 8 distributed file systemsChapter 8 distributed file systems
Chapter 8 distributed file systemsAbDul ThaYyal
 
AVX512 assembly language in FFmpeg
AVX512 assembly language in FFmpegAVX512 assembly language in FFmpeg
AVX512 assembly language in FFmpegKieran Kunhya
 
RPC communication,thread and processes
RPC communication,thread and processesRPC communication,thread and processes
RPC communication,thread and processesshraddha mane
 
Task scheduling Survey in Cloud Computing
Task scheduling Survey in Cloud ComputingTask scheduling Survey in Cloud Computing
Task scheduling Survey in Cloud ComputingRamandeep Kaur
 
426 lecture 7: Designing AR Interfaces
426 lecture 7: Designing AR Interfaces426 lecture 7: Designing AR Interfaces
426 lecture 7: Designing AR InterfacesMark Billinghurst
 
The 17 V’s of Big Data
The 17 V’s of Big DataThe 17 V’s of Big Data
The 17 V’s of Big DataIRJET Journal
 

What's hot (12)

Grid computing notes
Grid computing notesGrid computing notes
Grid computing notes
 
CS878 Green Computing Anna University Question Paper
CS878 Green Computing Anna University Question Paper CS878 Green Computing Anna University Question Paper
CS878 Green Computing Anna University Question Paper
 
File replication
File replicationFile replication
File replication
 
Chapter 8 distributed file systems
Chapter 8 distributed file systemsChapter 8 distributed file systems
Chapter 8 distributed file systems
 
AVX512 assembly language in FFmpeg
AVX512 assembly language in FFmpegAVX512 assembly language in FFmpeg
AVX512 assembly language in FFmpeg
 
cloud computing: Vm migration
cloud computing: Vm migrationcloud computing: Vm migration
cloud computing: Vm migration
 
RPC communication,thread and processes
RPC communication,thread and processesRPC communication,thread and processes
RPC communication,thread and processes
 
Case study
Case studyCase study
Case study
 
Virtualization - cloud computing
Virtualization - cloud computingVirtualization - cloud computing
Virtualization - cloud computing
 
Task scheduling Survey in Cloud Computing
Task scheduling Survey in Cloud ComputingTask scheduling Survey in Cloud Computing
Task scheduling Survey in Cloud Computing
 
426 lecture 7: Designing AR Interfaces
426 lecture 7: Designing AR Interfaces426 lecture 7: Designing AR Interfaces
426 lecture 7: Designing AR Interfaces
 
The 17 V’s of Big Data
The 17 V’s of Big DataThe 17 V’s of Big Data
The 17 V’s of Big Data
 

Similar to SWAD-24-years.pdf

SWAD, an Open Learning Management System
SWAD, an Open Learning Management SystemSWAD, an Open Learning Management System
SWAD, an Open Learning Management SystemAntonio Cañas Vargas
 
HTML5 New Features and Resources
HTML5 New Features and ResourcesHTML5 New Features and Resources
HTML5 New Features and ResourcesRon Reiter
 
Bruce Lawson Opera Indonesia
Bruce Lawson Opera IndonesiaBruce Lawson Opera Indonesia
Bruce Lawson Opera Indonesiabrucelawson
 
The Importance Things of Full Stack Development
The Importance Things of Full Stack DevelopmentThe Importance Things of Full Stack Development
The Importance Things of Full Stack DevelopmentMike Taylor
 
Programming for non tech entrepreneurs
Programming for non tech entrepreneursProgramming for non tech entrepreneurs
Programming for non tech entrepreneursRodrigo Gil
 
Amruth_Kumar_Juturu_Resume
Amruth_Kumar_Juturu_ResumeAmruth_Kumar_Juturu_Resume
Amruth_Kumar_Juturu_ResumeAmruth Kumar
 
Shaping the Future of Automatic Programming
Shaping the Future of Automatic ProgrammingShaping the Future of Automatic Programming
Shaping the Future of Automatic ProgrammingChristos Tsakostas
 
Serverless computing with Google Cloud
Serverless computing with Google CloudServerless computing with Google Cloud
Serverless computing with Google Cloudwesley chun
 
Node.js Web Apps @ ebay scale
Node.js Web Apps @ ebay scaleNode.js Web Apps @ ebay scale
Node.js Web Apps @ ebay scaleDmytro Semenov
 
Industrial training project ppt of online shopping
Industrial training project ppt of online  shoppingIndustrial training project ppt of online  shopping
Industrial training project ppt of online shoppinganil kumar
 
Perchè potresti aver bisogno di un database NoSQL anche se non sei Google o F...
Perchè potresti aver bisogno di un database NoSQL anche se non sei Google o F...Perchè potresti aver bisogno di un database NoSQL anche se non sei Google o F...
Perchè potresti aver bisogno di un database NoSQL anche se non sei Google o F...Codemotion
 
Basic html5 and javascript
Basic html5 and javascriptBasic html5 and javascript
Basic html5 and javascriptwendy017
 
[Srijan Wednesday Webinars] How to Build a Cloud Native Platform for Enterpri...
[Srijan Wednesday Webinars] How to Build a Cloud Native Platform for Enterpri...[Srijan Wednesday Webinars] How to Build a Cloud Native Platform for Enterpri...
[Srijan Wednesday Webinars] How to Build a Cloud Native Platform for Enterpri...Srijan Technologies
 
Azure + DataStax Enterprise Powers Office 365 Per User Store
Azure + DataStax Enterprise Powers Office 365 Per User StoreAzure + DataStax Enterprise Powers Office 365 Per User Store
Azure + DataStax Enterprise Powers Office 365 Per User StoreDataStax Academy
 
AstroLabs_Academy_Learning_to_Code-Coding_Bootcamp_Day1.pdf
AstroLabs_Academy_Learning_to_Code-Coding_Bootcamp_Day1.pdfAstroLabs_Academy_Learning_to_Code-Coding_Bootcamp_Day1.pdf
AstroLabs_Academy_Learning_to_Code-Coding_Bootcamp_Day1.pdfFarHanWasif1
 
Introduction to Modern DevOps Technologies
Introduction to  Modern DevOps TechnologiesIntroduction to  Modern DevOps Technologies
Introduction to Modern DevOps TechnologiesKriangkrai Chaonithi
 
Workshop About Software Engineering Skills 2019
Workshop About Software Engineering Skills 2019Workshop About Software Engineering Skills 2019
Workshop About Software Engineering Skills 2019PhuocNT (Fresher.VN)
 
Google cloud Study Jam 2023.pptx
Google cloud Study Jam 2023.pptxGoogle cloud Study Jam 2023.pptx
Google cloud Study Jam 2023.pptxGDSCNiT
 

Similar to SWAD-24-years.pdf (20)

SWAD, an Open Learning Management System
SWAD, an Open Learning Management SystemSWAD, an Open Learning Management System
SWAD, an Open Learning Management System
 
HTML5 New Features and Resources
HTML5 New Features and ResourcesHTML5 New Features and Resources
HTML5 New Features and Resources
 
Bruce Lawson Opera Indonesia
Bruce Lawson Opera IndonesiaBruce Lawson Opera Indonesia
Bruce Lawson Opera Indonesia
 
The Importance Things of Full Stack Development
The Importance Things of Full Stack DevelopmentThe Importance Things of Full Stack Development
The Importance Things of Full Stack Development
 
Programming for non tech entrepreneurs
Programming for non tech entrepreneursProgramming for non tech entrepreneurs
Programming for non tech entrepreneurs
 
SWAD Timeline 4:3
SWAD Timeline 4:3SWAD Timeline 4:3
SWAD Timeline 4:3
 
Amruth_Kumar_Juturu_Resume
Amruth_Kumar_Juturu_ResumeAmruth_Kumar_Juturu_Resume
Amruth_Kumar_Juturu_Resume
 
Shaping the Future of Automatic Programming
Shaping the Future of Automatic ProgrammingShaping the Future of Automatic Programming
Shaping the Future of Automatic Programming
 
Swad Timeline
Swad TimelineSwad Timeline
Swad Timeline
 
Serverless computing with Google Cloud
Serverless computing with Google CloudServerless computing with Google Cloud
Serverless computing with Google Cloud
 
Node.js Web Apps @ ebay scale
Node.js Web Apps @ ebay scaleNode.js Web Apps @ ebay scale
Node.js Web Apps @ ebay scale
 
Industrial training project ppt of online shopping
Industrial training project ppt of online  shoppingIndustrial training project ppt of online  shopping
Industrial training project ppt of online shopping
 
Perchè potresti aver bisogno di un database NoSQL anche se non sei Google o F...
Perchè potresti aver bisogno di un database NoSQL anche se non sei Google o F...Perchè potresti aver bisogno di un database NoSQL anche se non sei Google o F...
Perchè potresti aver bisogno di un database NoSQL anche se non sei Google o F...
 
Basic html5 and javascript
Basic html5 and javascriptBasic html5 and javascript
Basic html5 and javascript
 
[Srijan Wednesday Webinars] How to Build a Cloud Native Platform for Enterpri...
[Srijan Wednesday Webinars] How to Build a Cloud Native Platform for Enterpri...[Srijan Wednesday Webinars] How to Build a Cloud Native Platform for Enterpri...
[Srijan Wednesday Webinars] How to Build a Cloud Native Platform for Enterpri...
 
Azure + DataStax Enterprise Powers Office 365 Per User Store
Azure + DataStax Enterprise Powers Office 365 Per User StoreAzure + DataStax Enterprise Powers Office 365 Per User Store
Azure + DataStax Enterprise Powers Office 365 Per User Store
 
AstroLabs_Academy_Learning_to_Code-Coding_Bootcamp_Day1.pdf
AstroLabs_Academy_Learning_to_Code-Coding_Bootcamp_Day1.pdfAstroLabs_Academy_Learning_to_Code-Coding_Bootcamp_Day1.pdf
AstroLabs_Academy_Learning_to_Code-Coding_Bootcamp_Day1.pdf
 
Introduction to Modern DevOps Technologies
Introduction to  Modern DevOps TechnologiesIntroduction to  Modern DevOps Technologies
Introduction to Modern DevOps Technologies
 
Workshop About Software Engineering Skills 2019
Workshop About Software Engineering Skills 2019Workshop About Software Engineering Skills 2019
Workshop About Software Engineering Skills 2019
 
Google cloud Study Jam 2023.pptx
Google cloud Study Jam 2023.pptxGoogle cloud Study Jam 2023.pptx
Google cloud Study Jam 2023.pptx
 

Recently uploaded

Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfFerryKemperman
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Angel Borroy López
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfLivetecs LLC
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 

Recently uploaded (20)

Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdf
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdf
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 

SWAD-24-years.pdf

  • 1. 1 1 Antonio Cañas et al. Nov 3, 2023, Granada, Spain 24 years developing SWAD: hits, misses & lessons learned Including timeline implementation Antonio Cañas University of Granada @acanasvargas acanas@ugr.es acanas@openswad.org https://openswad.org/ @openswad
  • 2. Contents ● SWAD ● Features ● History ● Software ● HTML ● CSS ● JavaScript ● CGI in C ● Database ● Other modules ● Timeline ● Notes & Pubs ● Database ● Getting pubs ● Showing pubs ● Layout ● Inserting links ● Hits ● Misses ● Lessons learned ● Conclusions ● About us
  • 3. SWAD “swad: a bunch, a grouping of a number of similar things” . https://www.thefreedictionary.com/swad
  • 4. Sistema Web de Apoyo a la Docencia (Web System for Teaching Support) ⬇ Social Workspace At a Distance https://swad.ugr.es/ https://openswad.org/ A web platform to manage courses, students and teachers, with functions to support teaching and learning. SWAD
  • 5. SWAD This is how it looks. Some parts of its appearance, such as colors or icons, are customizable
  • 6. SWAD: Features Free software · 10 languages · Responsive design · Android app · iOS app · Face-to-face or blended learning Hierarchical organization: System · Countries · Institutions (universities, companies) · Centers (faculties, schools) · Degrees · Courses · Group types · Groups 10 available roles: Unknown · Guest · User · Student · Non- editing teacher · Teacher · Degree admin · Center admin · Institution admin · System admin
  • 7. SWAD: Features Social network · Calendar · Notifications · Course information · Syllabus · Documents · Shared files · Portfolio · Grades · Assignments · Projects · Exam announcements · Quizzes · Exams · Games · Rubrics · Groups · Lists of students and teachers · Attendance control using QR codes · Forums · Notices · Messaging system · Surveys · Statistics · Agenda · Preferences All features in https://github.com/acanas/swad-core/wiki/UserGuide.en
  • 8. SWAD: History ● LMS in 1999: WebCT (1997), Moodle (1999) Radio Granada, May 12, 2000 I just want to test if it's possible to fill out the student record card via the web
  • 9. SWAD: History ● LMS in 1999: WebCT (1997), Moodle (1999) Version 4.2.1, May 13, 2003 (it was not called SWAD yet) I just want to test if it's possible to fill out the student record card via the web
  • 10. SWAD: History ● LMS in 2023: a very broad offer ● Hundreds of LMS (Learning Management Systems) ● proprietary / free software ● expensive / free of charge ● specific / generic ● installable on the client's servers / accessible in the cloud
  • 13. Dpt.ATC: 1999-2003 1º TIP: 2003-2004 2º TIP: 2005-2006 3º TIP: 2006-2008 CEVUG: 2008-2016 Free Software: 2010... UNA.py: 2012-2015 OpenSWAD: 2012… Dpt.ATC: 2016-2022 Dpt.ICAR: 2022... SWAD: History openswad.org 2012... ugr CEVUG 2008-2016 ugr TIPs 2003-2008 ugr ATC 1999-2003 una.py 2012-2015 ugr ATC 2016-2022 24 years of development and use free software 2010... ugr ICAR 2022...
  • 14. Software: HTML “If you think math is hard, try web design.” Trish Parr – Web designer
  • 15. Software: HTML ● HTML (HyperText Markup Language) ● 1990 (Tim Berners-Lee) ● Standard language for documents to be displayed in a web browser ● Describes the semantic structure of a web page ● ...by using tags and attributes: <a href="https://openswad.org/">...</a> ● Assisted by CSS (appearance) and JavaScript (behavior) ● Web browsers ● receive HTML documents from a web server ● render the documents into multimedia web pages
  • 16. Software: HTML ● HTML: <html lang="en"> <head> <link rel="stylesheet" href="https://openswad.org/swad/swad21.95.5.css" type="text/css" /> <script type="text/javascript" src="https://openswad.org/swad/swad21.92.js"> </script> <title> OpenSWAD: social learning platform </title> </head> <body class="BODY_GREY" onload="init();"> <!-- HTML depending on action --> </body> </html> HTML code in user’s browser
  • 19. Software: HTML ● HTML: <form method="post" action="https://openswad.org/en"> <input type="hidden" name="act" value="1492"> <input type="hidden" name="ses" value="6BJQ...G46g"> <input type="hidden" name="Who" value="4"> <textarea name="Txt" rows="1" maxlength="1000" placeholder="New post…" class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH" onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');"> </textarea> <div id="id_EwuT...BhSs_6" style="display:none;"> ... <button type="submit" class="..."> Post </button> </div> </form> HTML code in user’s browser
  • 20. Software: CSS “Learning HTML and CSS is a lot more challenging than it used to be. Responsive web design adds more layers of complexity to design and develop websites.” Jacob Lett – Digital Marketing & Web Development Manager
  • 21. Software: CSS ● CSS (Cascading Style Sheets) language ● 1996 (Håkon Wium Lie, Bert Bos) ● Separates ● presentation (layout, colors, fonts) ● content of an HTML document ● Multiple web pages can share formatting in a .css file ● “Cascading”: priority scheme ● determines which style rule applies if more than one rule matches a particular element
  • 22. Software: CSS ● CSS: .Tml_COM_TEXTAREA { box-sizing: border-box; margin: 0; resize: none; } @media only screen and (max-width: 590px) { /* For mobile-phones */ ... .Tml_RIGHT_WIDTH {width: 260px;} /* 500-240 */ ... } @media only screen and (min-width: 590px) { /* For tablets and desktop */ ... .Tml_RIGHT_WIDTH {width: 500px;} ... } swad23.35.1.css
  • 23. Software: JavaScript “The strength of JavaScript is that you can do anything. The weakness is that you will.” Reg Braithwaite – Canadian programmer and writer
  • 24. Software: JavaScript ● JavaScript: ● 1995 (Brendan Eich, Netscape, Sun) ● Multi-paradigm, interpreted / just-in-time compiled, syntax similar to C and Java ● Modern browsers interpret JavaScript code ● embedded in the web page ● or in a .js file to add interactive features ● Interacts with the Document Object Model (DOM) ● Represents a document as a logical tree, each node is an object (part of the document )
  • 25. <form method="post" action="https://openswad.org/en"> <input type="hidden" name="act" value="1492"> <input type="hidden" name="ses" value="6BJQ...G46g"> <input type="hidden" name="Who" value="4"> <textarea name="Txt" rows="1" maxlength="1000" placeholder="New post…" class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH" onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');"> </textarea> <div id="id_EwuT...BhSs_6" style="display:none;"> ... <button type="submit" class="..."> Post </button> </div> </form> Software: JavaScript ● HTML: Textarea used to write the post
  • 26. <form method="post" action="https://openswad.org/en"> <input type="hidden" name="act" value="1492"> <input type="hidden" name="ses" value="6BJQ...G46g"> <input type="hidden" name="Who" value="4"> <textarea name="Txt" rows="1" maxlength="1000" placeholder="New post…" class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH" onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');"> </textarea> <div id="id_EwuT...BhSs_6" style="display:none;"> ... <button type="submit" class="..."> Post </button> </div> </form> Software: JavaScript ● HTML: Hidden container including button
  • 27. Software: JavaScript ● HTML ● JavaScript: /*****************************************************************************/ /*********************** Expand textarea when focus **************************/ /*****************************************************************************/ // Called from a textarea onfocus function expandTextarea (textareaElem,idButton,rows) { textareaElem.rows = rows; document.getElementById(idButton).style.display = ''; } swad22.49.js <textarea name="Txt" rows="1" maxlength="1000" placeholder="New post…" class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH" onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');"> </textarea>
  • 28. Software: CGI in C “C is quirky, flawed, and an enormous success.” Dennis Ritchie – Creator of the C programming language
  • 29. Software: CGI in C ● C language ● imperative, procedural, general-purpose ● 1970s (Dennis Ritchie) ● Use ● Remains widely used in OSs, device drivers, embedded system applications, libraries... ● Decreasingly used for application software ● Hardly used for the web
  • 30. Software: CGI in C ● All code is contained within functions ● Function parameters are passed by value ● Pass-by-reference is simulated by passing pointers #include <stdio.h> int main (void) { printf ("hello, worldn"); return 0; } main.c compiled, not interpreted
  • 31. Software: CGI in C ● Common Gateway Interface (CGI) 1.The user submits a web form on a web page (eg by clicking on a submit button) 2.The form's data is sent by browser to web server within an HTTP request with a URL denoting a server CGI script in a cgi-bin directory ● Written in a scripting language (eg Perl) ● Or may be a compiled program (a C program in our case) 3.The web server (Apache in our case) launches the CGI script in a new process, passing the form data to it 4.The output sent to standard output by the CGI script, usually in the form of HTML, is passed to the client browser instead of being shown on-screen in a terminal window
  • 32. Up to 400K times per day in 2014 Up to 2000 times / minute (30 times / second) Software: CGI in C “click” logged access HTML5 + CSS3 + JavaScript server MySQL database swad-core
  • 33. Software: CGI in C ● Example:
  • 34. ● HTML: <form method="post" action="https://openswad.org/en"> <input type="hidden" name="act" value="1492"> <input type="hidden" name="ses" value="6BJQ...G46g"> <input type="hidden" name="Who" value="4"> <textarea name="Txt" rows="6" maxlength="1000" placeholder="New post…" class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH" onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');"> </textarea> <div id="id_EwuT...BhSs_6" style=""> ... <button type="submit" class="..."> Post </button> </div> </form> Software: CGI in C HTML code in user’s browser
  • 35. ● HTML: <form method="post" action="https://openswad.org/en"> <input type="hidden" name="act" value="1492"> <input type="hidden" name="ses" value="6BJQ...G46g"> <input type="hidden" name="Who" value="4"> <textarea name="Txt" rows="6" maxlength="1000" placeholder="New post…" class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH" onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');"> </textarea> <div id="id_EwuT...BhSs_6" style=""> ... <button type="submit" class="..."> Post </button> </div> </form> Software: CGI in C ScriptAlias /en "/var/www/cgi-bin/swad/swad_en" swad_en: CGI program written in C
  • 36. ● HTML: <form method="post" action="https://openswad.org/en"> <input type="hidden" name="act" value="1492"> <input type="hidden" name="ses" value="6BJQ...G46g"> <input type="hidden" name="Who" value="4"> <textarea name="Txt" rows="6" maxlength="1000" placeholder="New post…" class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH" onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');"> </textarea> <div id="id_EwuT...BhSs_6" style=""> ... <button type="submit" class="..."> Post </button> </div> </form> Software: CGI in C Parameters passed to swad_en
  • 37. ● HTML: <form method="post" action="https://openswad.org/en"> <input type="hidden" name="act" value="1492"> <input type="hidden" name="ses" value="6BJQ...G46g"> <input type="hidden" name="Who" value="4"> <textarea name="Txt" rows="6" maxlength="1000" placeholder="New post…" class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH" onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');"> </textarea> <div id="id_EwuT...BhSs_6" style=""> ... <button type="submit" class="..."> Post </button> </div> </form> Software: CGI in C Textarea used to write the post
  • 38. ● HTML: <form method="post" action="https://openswad.org/en"> <input type="hidden" name="act" value="1492"> <input type="hidden" name="ses" value="6BJQ...G46g"> <input type="hidden" name="Who" value="4"> <textarea name="Txt" rows="6" maxlength="1000" placeholder="New post…" class="Tml_COM_TEXTAREA Tml_RIGHT_WIDTH" onfocus="expandTextarea (this, 'id_EwuT...BhSs_6', '6');"> </textarea> <div id="id_EwuT...BhSs_6" style=""> ... <button type="submit" class="..."> Post </button> </div> </form> Software: CGI in C Button clicked to submit the post
  • 39. Software: CGI in C ● Input: Method ← getenv ("REQUEST_METHOD"); ContentType ← getenv ("CONTENT_TYPE"); if (!strcmp (Method,"GET")) { // GET method ContentLength = strlen (getenv ("QUERY_STRING")); /* Copy query string from environment variable */ QueryString = malloc (ContentLength + 1); Str_Copy (QueryString,getenv ("QUERY_STRING"),ContentLength); } else { // POST method ContentLength ← getenv ("CONTENT_LENGTH"); /* Copy query string from stdin */ QueryString = malloc (ContentLength + 1); fread (QueryString,sizeof (char),ContentLength,stdin); } /* Parse QueryString creating linked list of parameters */ Par_CreateListOfParams (); Simplified C code on the server (swad-core)
  • 40. Software: CGI in C ● Output: fprintf (stdout,"Content-type: text/html;rnrn" "<!DOCTYPE html>n"); HTM_TxtF ("<html lang="%s">n", Lan_STR_LANG_ID[Gbl.Prefs.Language]); HTM_Txt ("<head>n"); HTM_TxtF ("<link rel="stylesheet" href="%s/%s" type="text/css" />n", Cfg_URL_SWAD_PUBLIC,CSS_FILE); HTM_TxtF ("<script type="text/javascript" src="%s/%s">n", Cfg_URL_SWAD_PUBLIC,JS_FILE); HTM_Txt ("</script>n"); ... HTM_Txt ("</head>n"); HTM_Txt ("<body onload="init();">n"); /* Write HTML depending on action */ ... HTM_Txt ("</body>n"); HTM_Txt ("</html>n"); Simplified C code on the server (swad-core)
  • 41. Software: CGI in C ● Output: void HTM_TxtF (const char *fmt,...) { va_list ap; int NumBytesPrinted; char *Attr; if (fmt) if (fmt[0]) { va_start (ap,fmt); NumBytesPrinted = vasprintf (&Attr,fmt,ap); va_end (ap); if (NumBytesPrinted < 0) // -1 if no memory or any other error Err_NotEnoughMemoryExit (); /***** Print HTML *****/ HTM_Txt (Attr); free (Attr); } } Simplified C code on the server (swad-core) void HTM_Txt (const char *Txt) { if (Txt) if (Txt[0]) fputs (Txt,Gbl.F.Out); }
  • 42. Software: CGI in C ● To enable CGI in Apache*: ● CentOS: ● cd /etc/httpd/conf.d/ ● sudo gedit ssl.conf ● Before </VirtualHost>, add the lines: ● Ubuntu: ● sudo a2enmod cgi ● sudo a2enmod ssl ● sudo a2ensite default-ssl ● cd /etc/apache2/sites-available/ ● sudo gedit default-ssl.conf ● Before </VirtualHost>, add the lines: *More details in https://openswad.org/install/ ScriptAlias /ca "/usr/lib/cgi-bin/swad/swad_ca" ScriptAlias /de "/usr/lib/cgi-bin/swad/swad_de" ScriptAlias /en "/usr/lib/cgi-bin/swad/swad_en" ScriptAlias /es "/usr/lib/cgi-bin/swad/swad_es" ScriptAlias /fr "/usr/lib/cgi-bin/swad/swad_fr" ScriptAlias /gn "/usr/lib/cgi-bin/swad/swad_gn" ScriptAlias /it "/usr/lib/cgi-bin/swad/swad_it" ScriptAlias /pl "/usr/lib/cgi-bin/swad/swad_pl" ScriptAlias /pt "/usr/lib/cgi-bin/swad/swad_pt" ScriptAlias /ca "/var/www/cgi-bin/swad/swad_ca" ScriptAlias /de "/var/www/cgi-bin/swad/swad_de" ScriptAlias /en "/var/www/cgi-bin/swad/swad_en" ScriptAlias /es "/var/www/cgi-bin/swad/swad_es" ScriptAlias /fr "/var/www/cgi-bin/swad/swad_fr" ScriptAlias /gn "/var/www/cgi-bin/swad/swad_gn" ScriptAlias /it "/var/www/cgi-bin/swad/swad_it" ScriptAlias /pl "/var/www/cgi-bin/swad/swad_pl" ScriptAlias /pt "/var/www/cgi-bin/swad/swad_pt"
  • 43. Software: Database “Information is the oil of the 21st century, and analytics is the combustion engine.” Peter Sondergaard – Former CEO of Gartner, Inc.
  • 44. Software: Database 151 MySQL/MariaDB tables Queries made through C API
  • 45. Software: Other modules “We build our computer systems the way we build our cities: over time, without a plan, on top of ruins.” Ellen Ullman – American computer programmer and author
  • 46. Software: Other modules ● User photos ● Our own automatic system for detecting faces and improving the quality of photos, trained with 90K photos ● Program written in C++ / OpenCV and called from swad-core ● Daniel J. Calandria Hernández ● Jesús Mesa González
  • 47. Software: Other modules ● Photo average ● Average / median photo, pixel by pixel, of all the students of a degree ● Program written in C++ / OpenCV and called from swad-core Daniel J. Calandria Hernández
  • 48. Software: Other modules ● API ● It is possible to develop add-ons (plugins) that run on: ● other servers ● mobile devices ● SWADroid, iSWAD, Triswados ● The plugins interact with swad-core through its integrated API ● https://openswad.org/api/
  • 49. Software: Other modules ● SWADroid ● App for Android ● https://play.google.com/store/apps/details?id=es.ugr.swad.swadroid By Juan Miguel Boyero Corral (2010-2023) ● https://github.com/Amab/SWADroid
  • 50. Software: Other modules ● Former developers: Antonio Manuel Aguilera Malagón (2012, QR attendance control) ● https://github.com/aguilerin/SWADroid ● https://es.slideshare.net/antonioaguileramalagon/swadroid-pasar-lista-manual-de-usuario Helena Rodríguez Gijón (2012, file download, registration in groups, notices) ● https://es.slideshare.net/helenarguez/swa-droid-f4 ● https://swadroid.wordpress.com/2012/11/17/publicado-swadroid-0-10-1/ ● https://github.com/hrguez José Antonio Guerrero Avilés (2014, course information, messages) ● https://github.com/antonio314/SWADroid ● https://es.slideshare.net/JoseAntonioGuerreroA/presentacion-proyecto-fin-de-carrera-ampliacion-de-swadroid ● https://www.linkedin.com/in/antonioguerreroaviles/overlay/50268922/single-media-viewer/ ● https://www.linkedin.com/in/antonioguerreroaviles/overlay/50268914/single-media-viewer/
  • 51. Software: Other modules Alejandro Alcalde Barros (2014, user interface) ● https://swadroid.wordpress.com/author/algui913/ ● https://github.com/elbaulp Rubén Martín Hidalgo (2016, messages, attendance control) ● https://github.com/romilgildo/SWADroid ● https://www.youtube.com/watch?v=l-P9eoyZLuk ● https://www.youtube.com/watch?v=A4onmR1Njic Javier Bueno López (2020, geolocation) ● https://github.com/JaviBL8/TFG ● https://github.com/JaviBL8/SWADroid Sergio Díaz Rueda (2020, games) ● https://www.linkedin.com/in/sergio-d%C3%ADaz-rueda-b4a013211/
  • 52. Software: Other modules swad-core SWADroid Affero GPL v3 license https://github.com/acanas/swad-core GPL v3 license https://git.cuernodehipnos.es/Marown/SWADroid 387,435 C code lines 151 MySQL tables 39,216 Java code lines 17K downloads 101 person-years estimated effort* 9 person-years estimated effort* $5,579,446 estimated cost* $500,680 estimated cost* Other modules and more info: https://openswad.org/source * According to the COCOMO model in Open Hub
  • 53. Software: Other modules ● iSWAD ● App for iOS https://apps.apple.com/es/app/iswad-platform/id1570162425 By Bate Ye (2021, Swift) https://github.com/WolfYe98/iSWAD
  • 54. Software: Other modules ● Former developers: Diego Montesinos Hervás (2011, Objective C) https://github.com/diegort/iSWAD Raúl Álvarez Hinojosa (2016, Swift) https://github.com/Rauleinstein/iSWAD Adrián Lara Roldán (2018, Swift) https://github.com/mitomono/iSWAD
  • 55. Timeline: Notes & Pubs “You think you know when you learn, are more sure when you can write, even more when you can teach, but certain when you can program.” Alan Perlis – Computer scientist and professor
  • 57. Timeline: Notes & Pubs ● 24 files ● 12 .c + 12 .h ● 170 C functions ● ~14 functions / .c file ● 6662 non-blank lines (2% of swad-core) ● 5881 non-blank lines in .c files ● ~35 non-blank lines / function ● 781 non-blank lines in .h files
  • 58. Timeline: Notes & Pubs ● Timeline: set of publications ● from a user ● global ● Only me ● Followed users ● All users typedef enum { Tml_Usr_TIMELINE_USR, Tml_Usr_TIMELINE_GBL, } Tml_Usr_UsrOrGbl_t; typedef enum { Usr_WHO_UNKNOWN, Usr_WHO_ME, Usr_WHO_SELECTED, // Not applicable to timeline Usr_WHO_FOLLOWED, Usr_WHO_ALL, } Usr_Who_t;
  • 59. Timeline: Notes & Pubs ● Publication: · original note (26851, 75% of 35968) · shared note ( 1456, 4% of 35968) · comment to a note ( 7661, 21% of 35968) typedef enum { Tml_Pub_UNKNOWN = 0, Tml_Pub_ORIGINAL_NOTE = 1, Tml_Pub_SHARED_NOTE = 2, Tml_Pub_COMMENT_TO_NOTE = 3, } TmlPub_Type_t; struct TmlPub_Publication { long PubCod; // Publication code long NotCod; // Note code long PublisherCod; // Sharer or writer of the publication TmlPub_Type_t Type; // Original note, shared note, comment struct TmlPub_Publication *Next; // Used for chained list }; *swad.ugr.es, nov 2023
  • 60. Timeline: Notes & Pubs ● Note: timeline post ( 5484, 20% of 26851) public file ( 82, <1% of 26851) call for exam ( 3067, 11% of 26851) notice (18011, 67% of 26851) forum post ( 207, 1% of 26851) *swad.ugr.es, nov 2023
  • 61. Timeline: Notes & Pubs typedef enum { TmlNot_UNKNOWN = 0, /* Start tab */ TmlNot_POST = 10, // Post written directly in timeline /* Institution tab */ TmlNot_INS_DOC_PUB_FILE = 1, // Public file in documents of institution TmlNot_INS_SHA_PUB_FILE = 2, // Public file in shared files of institution /* Center tab */ TmlNot_CTR_DOC_PUB_FILE = 3, // Public file in documents of center TmlNot_CTR_SHA_PUB_FILE = 4, // Public file in shared files of center /* Degree tab */ TmlNot_DEG_DOC_PUB_FILE = 5, // Public file in documents of degree TmlNot_DEG_SHA_PUB_FILE = 6, // Public file in shared files of degree /* Course tab */ TmlNot_CRS_DOC_PUB_FILE = 7, // Public file in documents of course TmlNot_CRS_SHA_PUB_FILE = 8, // Public file in shared files of course /* Assessment tab */ TmlNot_CALL_FOR_EXAM = 9, // Call for exam in a course /* Users tab */ /* Messages tab */ TmlNot_NOTICE = 12, // A public notice in a course TmlNot_FORUM_POST = 11, // Post in global/swad forums /* Analytics tab */ /* Profile tab */ } TmlNot_Type_t;
  • 62. Timeline: Notes & Pubs struct TmlNot_Note { long NotCod; // Unique code/identifier for each note TmlNot_Type_t Type; // Timeline post, public file, // call for exam, notice, forum post... long UsrCod; // Publisher long HieCod; // Hierarchy code // (institution/center/degree/course) long Cod; // Code of file, forum post, // notice, timeline post... bool Unavailable; // File, forum post, notice... // unavailable (removed) time_t DateTimeUTC; // Date-time of publication in UTC time unsigned NumShared; // Number of times (users) // this note has been shared unsigned NumFavs; // Number of times (users) // this note has been favourited };
  • 63. Timeline: Notes & Pubs __________________ |@author | | Note | |__________________| |@author | | Comment 1 | |______________| |@author | | Comment 2 | |______________| | | | ... | |______________| |@author | | Comment n | |______________| ● A note can have comments attached to it:
  • 64. Timeline: Notes & Pubs _tml_pubs______ _tml_comments | | | | | Publication p |---------->| Comment c |-----+ | (comment) | | (to note 2) | | |_______________| |_____________| | | | | | | · ... · · ... · | · ... · · ... · | |_______________| |_____________| | | | | | | |Publication i+4|---------->| Comment 1 |---+ | | (comment) | | (to note n) | | | |_______________| |_____________| | | | | (7661) | | |Publication i+3|-- | | |(original note)| | | |_______________| _tml_notes_____ | | _cfe_exams_____ | | | | | | | | |Publication i+2|-- ---->| Note n |<-+ | | Call for exam | (5707) |(original note)| |(exam announc.)|-(3067)->|_______________| |_______________| |_______________| 11% __brw_files____ | | | | | | | |Publication i+1|-- ---->| Note n-1 |-(82)--->| Public file | (1649245) |(original note)| | (public file) | <1% |_______________| |_______________| |_______________| | _not_notices___ | | | | | | | | Publication i |-- ---->| Note n-2 |-(18011)>| Notice | (15571) |(original note)| | (notice) | 67% |_______________| |_______________| |_______________| | __tml_posts____ | | | | | | | · ... · ---->| Note n-3 |-(5484)->| Post s | · ... · | (tl. post) | 20% | | |_______________| |_______________| | |_______________| | | | | | | | | Publication 3 | · ... · | · ... · (5484) | (shared note) |--- · ... · | · ... · |_______________| |_______________| | |_______________| | | | | | | | | Publication 2 | ---->| Note 2 |<---+ | Post 1 | |(original note)|--------->| (tl. post) |-------->| | |_______________| |_______________| |_______________| | | | | _for_posts_____ | Publication 1 |--------->| Note 1 | | | |(original note)| | (forum post) |-(207)-->| Forum post | (66891) |_______________| |_______________| 1% |_______________| (33504) (26851) timeline posts public files calls for exams notices forum posts notes comments publications *swad.ugr.es, nov 2023
  • 65. Timeline: Database “Don't forget to put the WHERE in the DELETE FROM.” Jorge Rubira Santos – Entrepreneur and Youtuber
  • 66. Timeline: Database 151 database tables 7 for timeline
  • 67. Timeline: Database mysql> SHOW TABLES LIKE 'tml_%'; +------------------------+ | Tables_in_swad (tml_%) | +------------------------+ | tml_comments | | tml_comments_fav | | tml_notes | | tml_notes_fav | | tml_posts | | tml_pubs | | tml_timelines | +------------------------+ 7 rows in set (0.00 sec)
  • 68. Timeline: Database mysql> DESCRIBE tml_pubs; +--------------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+----------+------+-----+---------+----------------+ | PubCod | bigint | NO | PRI | NULL | auto_increment | | NotCod | bigint | NO | MUL | NULL | | | PublisherCod | int | NO | MUL | NULL | | | PubType | tinyint | NO | MUL | NULL | | | TimePublish | datetime | NO | MUL | NULL | | +--------------+----------+------+-----+---------+----------------+
  • 69. Timeline: Database mysql> DESCRIBE tml_notes; +-------------+---------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+---------------+------+-----+---------+----------------+ | NotCod | bigint | NO | PRI | NULL | auto_increment | | NoteType | tinyint | NO | MUL | NULL | | | Cod | int | NO | | -1 | | | UsrCod | int | NO | MUL | NULL | | | HieCod | int | NO | | -1 | | | Unavailable | enum('N','Y') | NO | | N | | | TimeNote | datetime | NO | MUL | NULL | | +-------------+---------------+------+-----+---------+----------------+ mysql> DESCRIBE tml_notes_fav; +---------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+----------+------+-----+---------+----------------+ | FavCod | bigint | NO | PRI | NULL | auto_increment | | NotCod | bigint | NO | MUL | NULL | | | UsrCod | int | NO | MUL | NULL | | | TimeFav | datetime | NO | | NULL | | +---------+----------+------+-----+---------+----------------+
  • 70. Timeline: Database mysql> DESCRIBE tml_comments; +--------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+----------+------+-----+---------+-------+ | PubCod | bigint | NO | PRI | NULL | | | Txt | longtext | NO | MUL | NULL | | | MedCod | int | NO | MUL | -1 | | +--------+----------+------+-----+---------+-------+ mysql> DESCRIBE tml_comments_fav; +---------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+----------+------+-----+---------+----------------+ | FavCod | bigint | NO | PRI | NULL | auto_increment | | PubCod | bigint | NO | MUL | NULL | | | UsrCod | int | NO | MUL | NULL | | | TimeFav | datetime | NO | | NULL | | +---------+----------+------+-----+---------+----------------+
  • 71. Timeline: Database mysql> DESCRIBE tml_posts; +--------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------+----------+------+-----+---------+----------------+ | PstCod | int | NO | PRI | NULL | auto_increment | | Txt | longtext | NO | MUL | NULL | | | MedCod | int | NO | MUL | -1 | | +--------+----------+------+-----+---------+----------------+ mysql> DESCRIBE cfe_exams; +-------------+---------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+---------------+------+-----+---------+----------------+ | ExaCod | int | NO | PRI | NULL | auto_increment | | ... | ... | ... | ... | ... | ... | mysql> DESCRIBE brw_files; +-----------------+---------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------------+---------------+------+-----+---------+----------------+ | FilCod | int | NO | PRI | NULL | auto_increment | | ... | ... | ... | ... | ... | ... | mysql> DESCRIBE not_notices; +-----------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+----------+------+-----+---------+----------------+ | NotCod | int | NO | PRI | NULL | auto_increment | | ... | ... | ... | ... | ... | ... | mysql> DESCRIBE for_posts; +-----------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+----------+------+-----+---------+----------------+ | PstCod | int | NO | PRI | NULL | auto_increment | | ... | ... | ... | ... | ... | ... |
  • 72. Timeline: Database mysql> DESCRIBE tml_timelines; +-----------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+----------+------+-----+---------+-------+ | SessionId | char(43) | NO | PRI | NULL | | | NotCod | bigint | NO | PRI | NULL | | +-----------+----------+------+-----+---------+-------+ mysql> DESCRIBE tml_tmp_timeline; +--------+--------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+--------+------+-----+---------+-------+ | NotCod | bigint | NO | PRI | NULL | NULL | +--------+--------+------+-----+---------+-------+ What is being displayed on the screens of each of the users What is being displayed on the current user's screen
  • 73. Timeline: Database ● Example of database query: unsigned TmlDB_GetPubDataByCod (long PubCod, MYSQL_RES **mysql_res) { return (unsigned) DB_QuerySELECT (mysql_res, "can not get data of publication", "SELECT PubCod," // row[0] "NotCod," // row[1] "PublisherCod," // row[2] "PubType" // row[3] " FROM tml_pubs" " WHERE PubCod=%ld", PubCod); } void TmlPub_GetPubDataFromRow (MYSQL_RES *mysql_res, struct TmlPub_Publication *Pub) { MYSQL_ROW row; row = mysql_fetch_row (mysql_res); Pub->PubCod = Str_ConvertStrCodToLongCod (row[0]); Pub->NotCod = Str_ConvertStrCodToLongCod (row[1]); Pub->PublisherCod = Str_ConvertStrCodToLongCod (row[2]); Pub->Type = TmlPub_GetPubTypeFromStr (row[3]); }
  • 74. Timeline: Database unsigned long DB_QuerySELECT (MYSQL_RES **mysql_res, const char *MsgError, const char *fmt,...) { va_list ap; int NumBytesPrinted; char *Query; /***** Create query string *****/ va_start (ap,fmt); NumBytesPrinted = vasprintf (&Query,fmt,ap); va_end (ap); if (NumBytesPrinted < 0) // -1 if no memory or any other error Err_NotEnoughMemoryExit (); /***** Do SELECT query *****/ return DB_QuerySELECTusingQueryStr (Query,mysql_res,MsgError); } static unsigned long DB_QuerySELECTusingQueryStr (char *Query, MYSQL_RES **mysql_res, const char *MsgError) { int Result; [...] Result = mysql_query (&DB_Database.mysql,Query); // 0 on success free (Query); if (Result) DB_ExitOnMySQLError (MsgError); if ((*mysql_res = mysql_store_result (&DB_Database.mysql)) == NULL) DB_ExitOnMySQLError (MsgError); return (unsigned long) mysql_num_rows (*mysql_res); }
  • 75. Timeline: Getting pubs “When I wrote this code, only God and I understood what I did. Now only God knows.” Unknown source
  • 76. Timeline: Getting pubs ● Our algorithm: ● Select publications one by one in a loop ● In each iteration: ● Get the most recent publication (original, shared or comment) checking that its note is not already retrieved ● After getting a publication, save its note code to not get it again. SELECT PubCod FROM tml_pubs WHERE NotCod NOT IN (SELECT NotCod FROM tml_tmp_timeline) ORDER BY PubCod DESC LIMIT 1;
  • 77. Timeline: Getting pubs ● Slower alternative (may need seconds for large tables): ● Get the maximum PubCod, i.e more recent publication (original, shared or commment), of every set of publications corresponding to the same note: SELECT MAX(PubCod) AS NewestPubCod FROM tml_pubs GROUP BY NotCod ORDER BY NewestPubCod DESC LIMIT 10;
  • 78. Timeline: Getting pubs ● Restricting publications to mine and those I follow: CREATE TEMPORARY TABLE fol_tmp_me_and_followed (UsrCod INT NOT NULL, UNIQUE INDEX(UsrCod)) ENGINE=MEMORY SELECT my_usr_cod AS UsrCod UNION SELECT FollowedCod AS UsrCod FROM usr_follow WHERE FollowerCod=my_usr_cod; ------------------------------ SELECT tml_pubs.PubCod, tml_pubs.NotCod, tml_pubs.PublisherCod, tml_pubs.PubType FROM tml_pubs, fol_tmp_me_and_followed WHERE tml_pubs.PublisherCod=fol_tmp_me_and_followed.UsrCod AND tml_pubs.NotCod NOT IN (SELECT NotCod FROM tml_tmp_timeline) ORDER BY PubCod DESC LIMIT 1; mysql> DESCRIBE usr_follow; +-------------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+----------+------+-----+---------+-------+ | FollowerCod | int | NO | PRI | NULL | | | FollowedCod | int | NO | PRI | NULL | | | FollowTime | datetime | NO | MUL | NULL | | +-------------+----------+------+-----+---------+-------+
  • 79. Timeline: Getting pubs ● Three types of timeline updates _ ______________________ / |______________________| New < |______________________| _|______________________| _|_See_new_activity_(3)_| / |______________________| | |______________________| Recent < |______________________| | |______________________| _|______________________| _|_______See_more_______| / |______________________| | |______________________| Old < |______________________| | |______________________| _|______________________| typedef enum { Tml_GET_NEW_PUBS, // automatically // from time // to time (AJAX) Tml_GET_REC_PUBS, // user clicks // on menu // or after // editing // timeline Tml_GET_OLD_PUBS, // user clicks // on bottom // link (AJAX) } Tml_WhatToGet_t;
  • 80. Timeline: Getting pubs tml_pubs _____ |_____|11 |_____|10 _|_____| 9 <-- RangePubsToGet.Top Get / |_____| 8 pubs | |_____| 7 from < |_____| 6 this | |_____| 5 range _|_____| 4 |_____| 3 <-- RangePubsToGet.Bottom |_____| 2 |_____| 1 0
  • 81. Timeline: Getting pubs case Tml_GET_REC_PUBS: // Get some limited recent publications /* First query to get initial timeline shown ==> no notes yet in current timeline table */ RangePubsToGet->Top = 0; /* _ _____ 0 <-- RangePubsToGet.Top = +infinite / |_____| 8 Get | |_____| 7 pubs < |_____| 6 from | |_____| 5 all | |_____| 4 range . |_____| 3 . |_____| 2 . |_____| 1 0 <-- RangePubsToGet.Bottom = -infinite */ RangePubsToGet->Bottom = 0;
  • 82. Timeline: Getting pubs case Tml_GET_NEW_PUBS: // Get the publications (without limit) // newer than last pub. code /* Via AJAX automatically from time to time */ RangePubsToGet->Top = 0; /* _ _____ 0 <-- RangePubsToGet.Top = +infinite Get / |_____|11 these < |_____|10 pubs _|_____| 9 / |_____| 8 <-- RangePubsToGet.Bottom = last pub. code Pubs | |_____| 7 already < |_____| 6 shown | |_____| 5 | |_____| 4 . |_____| . . |_____| . . |_____| . */ RangePubsToGet->Bottom = Tml_DB_GetPubCodFromSession (Tml_Pub_LAST);
  • 83. Timeline: Getting pubs case Tml_GET_OLD_PUBS: // Get some limited publications // older than first pub. code /* Via AJAX when I click in link to get old publications */ RangePubsToGet->Top = Tml_DB_GetPubCodFromSession (Tml_Pub_FIRST); /* _____ . |_____| . . |_____| . . |_____| . Pubs | |_____| 8 already < |_____| 7 shown | |_____| 6 | |_____| 5 Get _|_____| 4 <-- RangePubsToGet.Top = first pub. code pubs / |_____| 3 from < |_____| 2 this _|_____| 1 rage 0 <-- RangePubsToGet.Bottom = -infinite */ RangePubsToGet->Bottom = 0;
  • 84. Timeline: Getting pubs ● Restricting publications to range: SELECT tml_pubs.PubCod, tml_pubs.NotCod, tml_pubs.PublisherCod, tml_pubs.PubType FROM tml_pubs, fol_tmp_me_and_followed WHERE tml_pubs.PublisherCod=fol_tmp_me_and_followed.UsrCod AND tml_pubs.PubCod>bottom // if type == Tml_GET_NEW_PUBS AND tml_pubs.PubCod<top // if type == Tml_GET_OLD_PUBS AND tml_pubs.NotCod NOT IN (SELECT NotCod FROM tml_tmp_timeline) ORDER BY PubCod DESC LIMIT 1;
  • 85. Timeline: Getting pubs Timeline->Pubs.Top Pub #0 ______ ______ Pub #1 |______|------>|______| ______ Pub #2 |______| -> |______| ______ Pub #3 |______| / |______| ->|______| ______ |______| / |______| / |______| ->|______| |_Next_|-- |______| / |______| // |______| more recent |_Next_|-- |______| // |______| ______ |_Next_|--/ |______| |______|---------------------------------------------- |_NULL_| older Timeline->Pubs.Bottom ● After getting the publications, the result is a chained list:
  • 86. Timeline: Showing pubs “There is beauty when something works and works intuitively.” Jonathan Paul Ive – Designer
  • 87. Timeline: Showing pubs _____ / |_____| just_now_timeline_list (Posts retrieved automatically | |_____| via AJAX from time to time. | |_____| They are transferred inmediately | | to new_timeline_list.) Hidden < __v__ | |_____| new_timeline_list (Posts retrieved but hidden. | |_____| When user clicks to view them, | |_____| the most recent of each note |_____| is transferred | to visible timeline_list.) See new activity (0) __v__ / |_____| timeline_list (Posts visible on page) | |_____| Visible | |_____| on < |_____| page | |_____| | |_____| |_____| ^ See more __|__ / |_____| old_timeline_list (Posts just retrieved via AJAX | |_____| when user clicks "see more". | |_____| They are transferred inmediately Hidden < |_____| to timeline_list.) | |_____| | |_____| |_____| <ul id="just_now_timeline_list" ...> </ul> <ul id="new_timeline_list" ...> </ul> <div id="view_new_container" ... style="display:none;"> <a href="" ... onclick="moveNewTimelineToTimeline(); return false;"> See new activity (<span id="view_new_count"> 0 </span>) </a> </div> <ul id="timeline_list" ...> visible timeline </ul> <div id="view_old_container" ...> <a href="" ... onclick="... refreshOldTimeline(); return false;"> ... See more </a> </div> <ul id="old_timeline_list" ...> </ul>
  • 88. Timeline: Showing pubs <head> ... <script type="text/javascript" ...> var delayNewTml = Cfg_TIME_TO_REFRESH_TIMELINE; // 2000 ms function init() { ActionAJAX = "SWAD_URL"; ... setTimeout('refreshNewTimeline()',delayNewTL); ... } </script> <script type="text/javascript" ...> var refreshParamIdSes = "ses=..."; var refreshParamNxtActNewPub = "act=..."; var refreshParamWho = "Who=..."; </script> ... </head> <body onload="init();"> ... </body> ● Automatic refresh via AJAX every 2 s → 3 s → 4 s... _____ / |_____| just_now_timeline_list | |_____| | |_____| | | Hidden < __v__ | |_____| new_timeline_list | |_____| | |_____| |_____| | See new activity (0) __v__ / |_____| timeline_list | |_____| Visible | |_____| on < |_____| page | |_____| | |_____| |_____| ^ See more __|__ / |_____| old_timeline_list | |_____| | |_____| Hidden < |_____| | |_____| | |_____| |_____|
  • 89. Timeline: Showing pubs var objXMLHttpReqNewTml = false; function refreshNewTimeline () { objXMLHttpReqNewTml = AJAXCreateObject(); // new XMLHttpRequest() if (objXMLHttpReqNewTml) { var RefreshParams = refreshParamNxtActNewPub + '&' + refreshParamIdSes + '&' + refreshParamWho; objXMLHttpReqNewTml.onreadystatechange = readNewTimelineData; objXMLHttpReqNewTml.open('POST',ActionAJAX,true); objXMLHttpReqNewTml.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); objXMLHttpReqNewTml.send(RefreshParams); } } _____ / |_____| just_now_timeline_list | |_____| | |_____| | | Hidden < __v__ | |_____| new_timeline_list | |_____| | |_____| |_____| | See new activity (0) __v__ / |_____| timeline_list | |_____| Visible | |_____| on < |_____| page | |_____| | |_____| |_____| ^ See more __|__ / |_____| old_timeline_list | |_____| | |_____| Hidden < |_____| | |_____| | |_____| |_____|
  • 90. Timeline: Showing pubs function readNewTimelineData () { if (objXMLHttpReqNewTml.readyState == 4) // Check if data have been received if (objXMLHttpReqNewTml.status == 200) { // Access to UL for just now timeline var justNowTimeline = document.getElementById('just_now_timeline_list'); if (justNowTimeline) { // Update list of publications in just now timeline justNowTimeline.innerHTML = objXMLHttpReqNewTml.responseText; var numNotesJustGot = justNowTimeline.childNodes.length; if (numNotesJustGot) {// New notes received // Scripts in timeline got via AJAX not executed ==> execute them evalScriptsInElem (justNowTimeline); // Process maths MathJax.typeset(); ... _____ / |_____| just_now_timeline_list | |_____| | |_____| | | Hidden < __v__ | |_____| new_timeline_list | |_____| | |_____| |_____| | See new activity (0) __v__ / |_____| timeline_list | |_____| Visible | |_____| on < |_____| page | |_____| | |_____| |_____| ^ See more __|__ / |_____| old_timeline_list | |_____| | |_____| Hidden < |_____| | |_____| | |_____| |_____|
  • 91. Timeline: Showing pubs ... // Move all the LI elements (notes) in UL 'just_now_timeline_list' // ...to the top of UL 'new_timeline_list' var newTimeline = document.getElementById('new_timeline_list'); for (var i=0; i<numNotesJustGot; i++) { // Move node from just now timeline to new timeline newTimeline.insertBefore(justNowTimeline.lastChild, newTimeline.firstChild); newTimeline.firstChild.className += " Tml_NEW_PUB"; } // Update number of notes in new timeline var viewNewCount = document.getElementById('view_new_count'); viewNewCount.innerHTML = newTimeline.childNodes.length; // Unhide message with number of notes if hidden var viewNewContainer = document.getElementById('view_new_container'); viewNewContainer.style.display = ''; } } // Global delay variable is set initially in swad-core delayNewTml += 1000; // Increase one second on each call setTimeout('refreshNewTimeline()',delayNewTml); } } _____ / |_____| just_now_timeline_list | |_____| | |_____| | | Hidden < __v__ | |_____| new_timeline_list | |_____| | |_____| |_____| | See new activity (0) __v__ / |_____| timeline_list | |_____| Visible | |_____| on < |_____| page | |_____| | |_____| |_____| ^ See more __|__ / |_____| old_timeline_list | |_____| | |_____| Hidden < |_____| | |_____| | |_____| |_____|
  • 92. _____ / |_____| just_now_timeline_list | |_____| | |_____| | | Hidden < __v__ | |_____| new_timeline_list | |_____| | |_____| |_____| | See new activity (0) __v__ / |_____| timeline_list | |_____| Visible | |_____| on < |_____| page | |_____| | |_____| |_____| ^ See more __|__ / |_____| old_timeline_list | |_____| | |_____| Hidden < |_____| | |_____| | |_____| |_____| Timeline: Showing pubs function moveNewTimelineToTimeline () { // Move the LI elements (notes) in UL 'new_timeline_list'... // ...to the top of UL 'timeline_list', only if not repeated before var newTimeline = document.getElementById('new_timeline_list'); var numNewNotes = newTimeline.childNodes.length; if (numNewNotes) { var timeline = document.getElementById("timeline_list"); for (var i=1; i<=numNewNotes; i++) { // Check if the last child (the oldest) in the new timeline... // ...is the last ocurrence of the note var mostRecentOcurrenceOfNote = true; var lastChildIndex = numNewNotes - i; var noteCode = newTimeline.lastChild.dataset.noteCode; for (var j=0; j<lastChildIndex; j++) if (newTimeline.childNodes[j].dataset.noteCode == noteCode) { mostRecentOcurrenceOfNote = false; break; } ... ● User clicks "See new activity" → View new pubs.
  • 93. Timeline: Showing pubs ... // Move or remove node from new timeline if (mostRecentOcurrenceOfNote) { // Move node from new timeline to timeline timeline.insertBefore(newTimeline.lastChild,timeline.firstChild); timeline.firstChild.className += " Tml_NEW_PUB"; } else // Remove last child (because is repeated in more recent pubs) newTimeline.removeChild(newTimeline.lastChild); } } // Reset number of new publications after moving var viewNewCount = document.getElementById('view_new_count'); viewNewCount.innerHTML = 0; // Hide link to view new publications after moving var viewNewContainer = document.getElementById('view_new_container'); viewNewContainer.style.display = 'none'; } _____ / |_____| just_now_timeline_list | |_____| | |_____| | | Hidden < __v__ | |_____| new_timeline_list | |_____| | |_____| |_____| | See new activity (0) __v__ / |_____| timeline_list | |_____| Visible | |_____| on < |_____| page | |_____| | |_____| |_____| ^ See more __|__ / |_____| old_timeline_list | |_____| | |_____| Hidden < |_____| | |_____| | |_____| |_____|
  • 94. _____ / |_____| just_now_timeline_list | |_____| | |_____| | | Hidden < __v__ | |_____| new_timeline_list | |_____| | |_____| |_____| | See new activity (0) __v__ / |_____| timeline_list | |_____| Visible | |_____| on < |_____| page | |_____| | |_____| |_____| ^ See more __|__ / |_____| old_timeline_list | |_____| | |_____| Hidden < |_____| | |_____| | |_____| |_____| Timeline: Showing pubs var objXMLHttpReqOldTml = false; function refreshOldTimeline () { objXMLHttpReqOldTml = AJAXCreateObject (); // new XMLHttpRequest() if (objXMLHttpReqOldTml) { var refreshParams = refreshParamNxtActOldPub + '&' + refreshParamIdSes; if (typeof refreshParamUsr !== 'undefined') { if (refreshParamUsr.length) refreshParams += '&' + refreshParamUsr; } if (typeof refreshParamWho !== 'undefined') { if (refreshParamWho.length) refreshParams += '&' + refreshParamWho; } objXMLHttpReqOldTml.onreadystatechange = readOldTimelineData; objXMLHttpReqOldTml.open('POST',actionAJAX,true); objXMLHttpReqOldTml.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); objXMLHttpReqOldTml.send(refreshParams); } } ● User clicks "See more..." → View old pubs.
  • 95. Timeline: Showing pubs function readOldTimelineData () { if (objXMLHttpReqOldTml.readyState == 4) // Check if data have been received if (objXMLHttpReqOldTml.status == 200) { // Access to UL with the old timeline var oldTimeline = document.getElementById('old_timeline_list'); if (oldTimeline) { // Fill list of publications in old timeline oldTimeline.innerHTML = objXMLHttpReqOldTml.responseText; var countOldTimeline = oldTimeline.childNodes.length; if (countOldTimeline) { // Scripts in timeline got via AJAX not executed ==> execute them evalScriptsInElem (oldTimeline); // Process maths MathJax.typeset(); // Move all elements in 'old_timeline_list to bottom of 'timeline_list' var timeline = document.getElementById("timeline_list"); for (var i=0; i<countOldTimeline; i++) timeline.appendChild(oldTimeline.firstChild); } else // No old publications retrieved, so we have reached the oldest pub. // Hide container with link to get old publications document.getElementById("view_old_pubs_container").style.display = 'none'; } } } _____ / |_____| just_now_timeline_list | |_____| | |_____| | | Hidden < __v__ | |_____| new_timeline_list | |_____| | |_____| |_____| | See new activity (0) __v__ / |_____| timeline_list | |_____| Visible | |_____| on < |_____| page | |_____| | |_____| |_____| ^ See more __|__ / |_____| old_timeline_list | |_____| | |_____| Hidden < |_____| | |_____| | |_____| |_____|
  • 96. Timeline: Layout “If you find an element of your interface requires instructions, then you need to redesign it.” Dan Rubin – Designer, photographer
  • 97. Timeline: Layout ___________________________________________ | | | Top message: | > top message |___________________________________________| / | _____ | | | || | | Author's name | Date-time | | | ||Auth.| |______________________|___________| | | ||photo| | | | author's | ||_____| | | > name, time | | | Note | | and content | | | content | | | | | | | | | |__________________________________| / | | | | | | | | | Favs | Shared |Remove| | > note | |_____________|_____________|______| | | |________| | | | | | List | | buttons | | Comment| of | > and | | icon | comments | | comments | |________|__________________________________| | | | | | | | Form to write new comment | | | |__________________________________| / /
  • 98. Timeline: Layout ● Top message <div class="Tml_TOP_CONT Tml_TOP_PUBLISHER Tml_WIDTH"> <form method="post" action="https://openswad.org/en" id="form_..." ...> <input type="hidden" name="act" value="1402"> <input type="hidden" name="ses" value="..."> <input type="hidden" name="OtherUsrCod" value="..."> <button type="submit" title="Another user's profile" class="BT_LINK Tml_TOP_PUBLISHER"> user name </button> </form> has shared: </div>
  • 99. Timeline: Layout ___________________________________________ | | | Top message: | > top message |___________________________________________| / | _____ | | | || | | Author's name | Date-time | | | ||Auth.| |______________________|___________| | | ||photo| | | | author's | ||_____| | | > name, time | | | Note | | and content | | | content | | | | | | | | | |__________________________________| / | | | | | | | | | Favs | Shared |Remove| | > note | |_____________|_____________|______| | | |________| | | | | | List | | buttons | | Comment| of | > and | | icon | comments | | comments | |________|__________________________________| | | | | | | | Form to write new comment | | | |__________________________________| / /
  • 100. Timeline: Layout ● Photo <div class="Tml_LEFT_PHOTO"> <form method="post" action="https://openswad.org/en" id="form_..." ...> <input type="hidden" name="act" value="1402"> <input type="hidden" name="ses" value="..."> <input type="hidden" name="OtherUsrCod" value="..."> <button type="submit" class="BT_LINK"> <img src="user-photo.jpg" alt="" title="username" class="PHOTO45x60" onmouseover="zoom(this,'user-photo.jpg','id_...');" onmouseout="noZoom();"> <div id="id_..." class="NOT_SHOWN"> <div class="ZOOM_TXT_LINE DAT_N_BOLD">first name<br>last name</div> <div class="ZOOM_TXT_LINE DAT_SMALL_N">@nickname</div> <div class="ZOOM_TXT_LINE DAT_SMALL">institution&nbsp;(country)</div> <div class="ZOOM_TXT_LINE DAT_SMALL"> <div class="ZOOM_DEG" style="background-image:url('user-icon.svg');">degree</div> </div> <div class="ZOOM_TXT_LINE"> <span class="DAT_N_BOLD">1</span><span class="DAT_SMALL">&nbsp;Following&nbsp;</span> <span class="DAT_N_BOLD">1</span><span class="DAT_SMALL">&nbsp;Followers</span> </div> </div> </button> </form> </div> Photo caption
  • 101. Timeline: Layout ___________________________________________ | | | Top message: | > top message |___________________________________________| / | _____ | | | || | | Author's name | Date-time | | | ||Auth.| |______________________|___________| | | ||photo| | | | author's | ||_____| | | > name, time | | | Note | | and content | | | content | | | | | | | | | |__________________________________| / | | | | | | | | | Favs | Shared |Remove| | > note | |_____________|_____________|______| | | |________| | | | | | List | | buttons | | Comment| of | > and | | icon | comments | | comments | |________|__________________________________| | | | | | | | Form to write new comment | | | |__________________________________| / /
  • 102. Timeline: Layout ● Author's name, date-time and note content <div class="Tml_RIGHT_CONT Tml_RIGHT_WIDTH"> <form method="post" action="https://openswad.org/en" id="form_..." ...> <input type="hidden" name="act" value="1402"> <input type="hidden" name="ses" value="..."> <input type="hidden" name="OtherUsrCod" value="..."> <button type="submit" title="My public profile" class="BT_LINK Tml_RIGHT_AUTHOR Tml_RIGHT_AUTHOR_WIDTH DAT_N_BOLD"> user name </button> </form> <div id="id_..." class="Tml_RIGHT_TIME DAT_LIGHT">date,&nbsp;time</div> <script type="text/javascript"> writeLocalDateHMSFromUTC ('id_...',unix-time,1,',&nbsp;',3,true,true,false,0x6); </script> <div class="Tml_TXT"> post content </div> </div>
  • 103. Timeline: Layout function writeLocalDateHMSFromUTC (id,TimeUTC,DateFormat, Separator,Language, WriteToday,WriteDateOnSameDay, WriteWeekDay,WriteHMS) { // HMS: Hour, Minutes, Seconds var today = new Date(); var todayYea = today.getFullYear (); var todayMon = today.getMonth () + 1; var todayDay = today.getDate (); var d = new Date(); var WriteDate; var Yea,Mon,Day; var DayOfWeek; var Hou,Min,Sec; var StrDat; var StrMon; var StrDay; var StrHou; var StrMin; var StrSec; d.setTime (TimeUTC * 1000); Yea = d.getFullYear (); Mon = d.getMonth () + 1; Day = d.getDate (); if (WriteDateOnSameDay) WriteDate = true; // Check to see if the last date has been initialized else if (typeof writeLocalDateHMSFromUTC.lastd == 'undefined') // lastd: static variable to remember date for the next call // Not initialized WriteDate = true; else WriteDate = (Yea != writeLocalDateHMSFromUTC.lastd.getFullYear () || Mon != writeLocalDateHMSFromUTC.lastd.getMonth () + 1 || Day != writeLocalDateHMSFromUTC.lastd.getDate ()); // Update last date for the next call writeLocalDateHMSFromUTC.lastd = d; /* Set date */ StrDat = ''; if (WriteDate) { WriteToday = WriteToday && (Yea == todayYea && Mon == todayMon && Day == todayDay); // Date is today if (WriteToday) StrDat = txtToday[Language]; else switch (DateFormat) { case 0: // Dat_FORMAT_YYYY_MM_DD StrMon = ((Mon < 10) ? '0' : '') + Mon; StrDay = ((Day < 10) ? '0' : '') + Day; StrDat = Yea.toString () + '-' + StrMon + '-' + StrDay; break; case 1: // Dat_FORMAT_DD_MONTH_YYYY StrDat = Day.toString () + '&nbsp;' + MonthsShort[Mon - 1] + '&nbsp;' + Yea.toString (); break; case 2: // Dat_FORMAT_MONTH_DD_YYYY StrDat = MonthsShort[Mon - 1] + '&nbsp;' + Day.toString() + ',&nbsp;' + Yea.toString (); break; default: break; } if (WriteWeekDay) { DayOfWeek = d.getDay (); DayOfWeek = (DayOfWeek == 0) ? 6 : DayOfWeek - 1; StrDat += Separator + DAYS[DayOfWeek]; } StrDat += Separator; } /* Set HH:MM:SS */ StrHou = ''; StrMin = ''; StrSec = ''; if (WriteHMS & (1<<2)) { // Bit 2 on => Write hour Hou = d.getHours(); StrHou = ((Hou < 10) ? '0' : '') + Hou; if (WriteHMS & (1<<1)) { // Bits 2,1 on => Write minutes Min = d.getMinutes (); StrMin = ((Min < 10) ? ':0' : ':') + Min; if (WriteHMS & 1) { // Bits 2,1,0 on => Write seconds Sec = d.getSeconds (); StrSec = ((Sec < 10) ? ':0' : ':') + Sec; } } } /* Write date and time */ document.getElementById (id).innerHTML = StrDat + StrHou + StrMin + StrSec; }
  • 104. Timeline: Layout ___________________________________________ | | | Top message: | > top message |___________________________________________| / | _____ | | | || | | Author's name | Date-time | | | ||Auth.| |______________________|___________| | | ||photo| | | | author's | ||_____| | | > name, time | | | Note | | and content | | | content | | | | | | | | | |__________________________________| / | | | | | | | | | Favs | Shared |Remove| | > note | |_____________|_____________|______| | | |________| | | | | | List | | buttons | | Comment| of | > and | | icon | comments | | comments | |________|__________________________________| | | | | | | | Form to write new comment | | | |__________________________________| / /
  • 105. Timeline: Layout ___________________________________________________________________________ | div which content will be updated (parent of parent of form) | | _____________________ _______ _____________________________________ | | | div (parent of form)| | div | | div for users | | | | _________________ | | for | | ______ ______ ______ ______ | | | | | this form | | | num. | | | | | | | | | form | | | | | | _____________ | | | of | | | user | | user | | user | | to | | | | | | | | | | | users | | | 1 | | 2 | | 3 | | show | | | | | | |_____________| | | | | | | | | | | | | all | | | | | |_________________| | | | | |______| |______| |______| |______| | | | |_____________________| |_______| |_____________________________________| | |___________________________________________________________________________| typedef enum { Tml_Usr_SHOW_FEW_USRS, // Show a few first favers/sharers Tml_Usr_SHOW_ALL_USRS, // Show all favers/sharers } Tml_Usr_HowManyUsrs_t;
  • 106. Timeline: Layout ● Favourite <div class="Tml_BOTTOM_RIGHT Tml_RIGHT_WIDTH"> <div class="Tml_FOOT Tml_RIGHT_WIDTH"> <div id="fav_not_..." class="Tml_FAV_NOT Tml_FAV_NOT_WIDTH"> <div class="Tml_ICO"> <form method="post" action="https://openswad.org/en" id="form_..." onsubmit="updateDivFaversSharers (this,'act=1512" "&amp;ses=session&amp;NotCod=note-code');" "return false;" ...> <input type="image" src="...fav-icon.svg" alt="Fav" title="Fav" class="CONTEXT_OPT ICO_HIGHLIGHT CONTEXT_ICO_16x16"> </form> </div> <div class="Tml_NUM_USRS">&nbsp;0</div> <div class="Tml_USRS"></div> </div>
  • 107. Timeline: Layout ● Share <div id="sha_not_..." class="Tml_SHA_NOT Tml_SHA_NOT_WIDTH"> <div class="Tml_ICO"> <form method="post" action="https://openswad.org/en" id="form_..." onsubmit="updateDivFaversSharers (this,'act=1495" "&amp;ses=session&amp;NotCod=note-code');" "return false;" ...> <input type="image" src="...share-icon.svg" alt="Share" title="Share" class="CONTEXT_OPT ICO_HIGHLIGHT CONTEXT_ICO_16x16"> </form> </div> <div class="Tml_NUM_USRS">&nbsp;0</div> <div class="Tml_USRS"></div> </div>
  • 108. Timeline: Layout ● Show all users <div id="fav_not_..." class="Tml_FAV_NOT Tml_FAV_NOT_WIDTH"> <div class="Tml_ICO">fav icon</div> <div class="Tml_NUM_USRS">number of users</div> <div class="Tml_USRS"> <div class="Tml_SHARER">user</div> ... <div class="Tml_SHARER">user</div> <form method="post" action="https://openswad.org/en" id="form_..." onsubmit="updateDivFaversSharers (this,'act=1767" "&amp;ses=session&amp;NotCod=note-code');" " return false;" ...> <input type="image" src="...ellipsis-h.svg" alt="View all" title="View all" class="CONTEXT_OPT ICO_HIGHLIGHT CONTEXT_ICO_16x16"> </form> </div> </div>
  • 109. Timeline: Layout function updateDivFaversSharers (form,Params) { var id = form.parentNode.parentNode.id; var objXMLHttp = AJAXCreateObject (); if (objXMLHttp) { /* Send request to server */ objXMLHttp.onreadystatechange = function () { if (objXMLHttp.readyState == 4) // Check if data have been received if (objXMLHttp.status == 200) if (id) { var div = document.getElementById (id); // Access to DIV if (div) div.innerHTML = objXMLHttp.responseText; // Update DIV content } }; objXMLHttp.open ('POST',actionAJAX,true); objXMLHttp.setRequestHeader ('Content-Type','application/x-www-form-urlencoded'); objXMLHttp.send (Params); } }
  • 110. Timeline: Layout ___________________________________________ | | | Top message: | > top message |___________________________________________| / | _____ | | | || | | Author's name | Date-time | | | ||Auth.| |______________________|___________| | | ||photo| | | | author's | ||_____| | | > name, time | | | Note | | and content | | | content | | | | | | | | | |__________________________________| / | | | | | | | | | Favs | Shared |Remove| | > note | |_____________|_____________|______| | | |________| | | | | | List | | buttons | | Comment| of | > and | | icon | comments | | comments | |________|__________________________________| | | | | | | | Form to write new comment | | | |__________________________________| / /
  • 111. Timeline: Layout ● Remove <div class="Tml_REM"> <form method="post" action="https://openswad.org/en" id="form_..." ...> <input type="hidden" name="act" value="1494"> <input type="hidden" name="ses" value="session"> <input type="hidden" name="Who" value="4"> <input type="hidden" name="NotCod" value="note-code"> <input type="image" src="remove-icon.svg" alt="Remove" title="Remove" class="CONTEXT_OPT ICO_HIGHLIGHT CONTEXT_ICO_16x16"> </form> </div> </div> </div>
  • 112. Timeline: Layout ___________________________________________ | | | Top message: | > top message |___________________________________________| / | _____ | | | || | | Author's name | Date-time | | | ||Auth.| |______________________|___________| | | ||photo| | | | author's | ||_____| | | > name, time | | | Note | | and content | | | content | | | | | | | | | |__________________________________| / | | | | | | | | | Favs | Shared |Remove| | > note | |_____________|_____________|______| | | |________| | | | | | List | | buttons | | Comment| of | > and | | icon | comments | | comments | |________|__________________________________| | | | | | | | Form to write new comment | | | |__________________________________| / /
  • 113. Timeline: Layout ● Comment icon <div class="Tml_BOTTOM_LEFT"> <div id="id_..._ico" class="Tml_ICO_COM_OFF"> <a href="" onclick="toggleNewComment ('id_...');return false;"> <img src="comment-icon.svg" alt="Comment" title="Comment" class="CONTEXT_ICO_16x16"> </a> </div> </div> function toggleNewComment (id) { var iconDiv = document.getElementById (id + '_ico'); iconDiv.className = (iconDiv.className == 'Tml_ICO_COM_OFF') ? 'Tml_ICO_COM_ON' :'Tml_ICO_COM_OFF'; toggleDisplay(id); } function toggleDisplay (elementID) { var element = document.getElementById (elementID); var stl; if (element) { stl = element.style; stl.display = (stl.display === 'none') ? '' : 'none'; } }
  • 114. Timeline: Layout ● New comment <div id="id_..." class="Tml_FORM_NEW_COM Tml_RIGHT_WIDTH" style=""> <div class="Tml_COM_PHOTO">...</div> <div class="Tml_COM_CONT Tml_COMM_WIDTH"> <form method="post" action="https://openswad.org/en" id="..." enctype="multipart/form-data" ...> <input type="hidden" name="act" value="1503"> <input type="hidden" name="ses" value="session"> <input type="hidden" name="Who" value="4"> <input type="hidden" name="NotCod" value="note code"> <textarea name="Txt" rows="6" maxlength="1000" placeholder="New comment…" class="Tml_COM_TEXTAREA Tml_COMM_WIDTH" onfocus="expandTextarea (this,'id_...','6');"> </textarea> <div id="id_..." style=""> <div class="HELP_EDIT">...</div> <div class="MED_UPLOADER">...</div> <button type="submit" class="BT_SUBMIT_INLINE BT_CREATE">Post</button> </div> </form> </div> </div> function expandTextarea (textareaElem,idButton,rows) { textareaElem.rows = rows; document.getElementById (idButton).style.display = ''; }
  • 115. Timeline: Layout ___________________________________________ | | | Top message: | > top message |___________________________________________| / | _____ | | | || | | Author's name | Date-time | | | ||Auth.| |______________________|___________| | | ||photo| | | | author's | ||_____| | | > name, time | | | Note | | and content | | | content | | | | | | | | | |__________________________________| / | | | | | | | | | Favs | Shared |Remove| | > note | |_____________|_____________|______| | | |________| | | | | | List | | buttons | | Comment| of | > and | | icon | comments | | comments | |________|__________________________________| | | | | | | | Form to write new comment | | | |__________________________________| / /
  • 116. Timeline: Layout ___________________________________________ | _____ | | | || | | Author's name | Date-time | | | ||Auth.| |______________________|___________| | | ||photo| | | | author's | ||_____| | | > name, time | | | Comment | | and content > comment | | content | | | | | | | | | |__________________________________| / | | | | | | | | Favs |Remove| > buttons | |________|___________________________|______| / / ● Layout and code similar to those described for the notes.
  • 117. Timeline: Layout Before clicking "See prev..." --> After clicking "See prev..." _________________________________ _________________________________ | div con_<id> | | div con_<id> | | (hidden) | | (visible) | | _____________________________ | | _____________________________ | | | v See only the latest | | | | v See only the latest | | | |_________(contract)__________| | | |_________(contract)__________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | div <id> | | div <id> updated | | which content | | _____________________________ | | will be updated via AJAX | | | ul com_<id> | | | (parent of parent of form) | | | _________________________ | | | | | | | li (comment 1) | | | | | | | |_________________________| | | | | | | | ... | | | | | | | |_________________________| | | | | | | | li (comment n) | | | | | --> | | |_________________________| | | | | | |_____________________________| | | _____________________________ | | _____________________________ | | | div exp_<id> | | | | div exp_<id> | | | | (visible) | | | | (hidden) | | | | _________________________ | | | | | | | | | form | | | | | | | | | | _____________________ | | | | | _____________________ | | | | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | | | | | |_______(expand)______| | | | | | |_______(expand)______| | | | | |_________________________| | | | | | | | |_____________________________| | | |_____________________________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | ul | | ul | | _________________________ | | _________________________ | | | li (comment n+1) | | | | li (comment n+1) | | | |_________________________| | | |_________________________| | | | ... | | | | ... | | | |_________________________| | | |_________________________| | | | li (comment m) | | | | li (comment m) | | | |_________________________| | | |_________________________| | |_________________________________| |_________________________________| ● Only the last comments are displayed.
  • 118. Timeline: Layout Before clicking "See prev..." --> After clicking "See prev..." _________________________________ _________________________________ | div con_<id> | | div con_<id> | | (hidden) | | (visible) | | _____________________________ | | _____________________________ | | | v See only the latest | | | | v See only the latest | | | |_________(contract)__________| | | |_________(contract)__________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | div <id> | | div <id> updated | | which content | | _____________________________ | | will be updated via AJAX | | | ul com_<id> | | | (parent of parent of form) | | | _________________________ | | | | | | | li (comment 1) | | | | | | | |_________________________| | | | | | | | ... | | | | | | | |_________________________| | | | | | | | li (comment n) | | | | | --> | | |_________________________| | | | | | |_____________________________| | | _____________________________ | | _____________________________ | | | div exp_<id> | | | | div exp_<id> | | | | (visible) | | | | (hidden) | | | | _________________________ | | | | | | | | | form | | | | | | | | | | _____________________ | | | | | _____________________ | | | | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | | | | | |_______(expand)______| | | | | | |_______(expand)______| | | | | |_________________________| | | | | | | | |_____________________________| | | |_____________________________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | ul | | ul | | _________________________ | | _________________________ | | | li (comment n+1) | | | | li (comment n+1) | | | |_________________________| | | |_________________________| | | | ... | | | | ... | | | |_________________________| | | |_________________________| | | | li (comment m) | | | | li (comment m) | | | |_________________________| | | |_________________________| | |_________________________________| |_________________________________| ● Only the last comments are displayed. ● You can click on "See previous".
  • 119. Timeline: Layout Before clicking "See prev..." --> After clicking "See prev..." _________________________________ _________________________________ | div con_<id> | | div con_<id> | | (hidden) | | (visible) | | _____________________________ | | _____________________________ | | | v See only the latest | | | | v See only the latest | | | |_________(contract)__________| | | |_________(contract)__________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | div <id> | | div <id> updated | | which content | | _____________________________ | | will be updated via AJAX | | | ul com_<id> | | | (parent of parent of form) | | | _________________________ | | | | | | | li (comment 1) | | | | | | | |_________________________| | | | | | | | ... | | | | | | | |_________________________| | | | | | | | li (comment n) | | | | | --> | | |_________________________| | | | | | |_____________________________| | | _____________________________ | | _____________________________ | | | div exp_<id> | | | | div exp_<id> | | | | (visible) | | | | (hidden) | | | | _________________________ | | | | | | | | | form | | | | | | | | | | _____________________ | | | | | _____________________ | | | | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | | | | | |_______(expand)______| | | | | | |_______(expand)______| | | | | |_________________________| | | | | | | | |_____________________________| | | |_____________________________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | ul | | ul | | _________________________ | | _________________________ | | | li (comment n+1) | | | | li (comment n+1) | | | |_________________________| | | |_________________________| | | | ... | | | | ... | | | |_________________________| | | |_________________________| | | | li (comment m) | | | | li (comment m) | | | |_________________________| | | |_________________________| | |_________________________________| |_________________________________| ● Only the last comments are displayed. ● You can click on "See previous". ● Previous comments are retrieved via AJAX.
  • 120. Timeline: Layout Before clicking "See prev..." --> After clicking "See prev..." _________________________________ _________________________________ | div con_<id> | | div con_<id> | | (hidden) | | (visible) | | _____________________________ | | _____________________________ | | | v See only the latest | | | | v See only the latest | | | |_________(contract)__________| | | |_________(contract)__________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | div <id> | | div <id> updated | | which content | | _____________________________ | | will be updated via AJAX | | | ul com_<id> | | | (parent of parent of form) | | | _________________________ | | | | | | | li (comment 1) | | | | | | | |_________________________| | | | | | | | ... | | | | | | | |_________________________| | | | | | | | li (comment n) | | | | | --> | | |_________________________| | | | | | |_____________________________| | | _____________________________ | | _____________________________ | | | div exp_<id> | | | | div exp_<id> | | | | (visible) | | | | (hidden) | | | | _________________________ | | | | | | | | | form | | | | | | | | | | _____________________ | | | | | _____________________ | | | | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | | | | | |_______(expand)______| | | | | | |_______(expand)______| | | | | |_________________________| | | | | | | | |_____________________________| | | |_____________________________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | ul | | ul | | _________________________ | | _________________________ | | | li (comment n+1) | | | | li (comment n+1) | | | |_________________________| | | |_________________________| | | | ... | | | | ... | | | |_________________________| | | |_________________________| | | | li (comment m) | | | | li (comment m) | | | |_________________________| | | |_________________________| | |_________________________________| |_________________________________| ● Only the last comments are displayed. ● You can click on "See previous". ● Previous comments are retrieved via AJAX. ● "See previous" is hidden.
  • 121. Timeline: Layout Before clicking "See prev..." --> After clicking "See prev..." _________________________________ _________________________________ | div con_<id> | | div con_<id> | | (hidden) | | (visible) | | _____________________________ | | _____________________________ | | | v See only the latest | | | | v See only the latest | | | |_________(contract)__________| | | |_________(contract)__________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | div <id> | | div <id> updated | | which content | | _____________________________ | | will be updated via AJAX | | | ul com_<id> | | | (parent of parent of form) | | | _________________________ | | | | | | | li (comment 1) | | | | | | | |_________________________| | | | | | | | ... | | | | | | | |_________________________| | | | | | | | li (comment n) | | | | | --> | | |_________________________| | | | | | |_____________________________| | | _____________________________ | | _____________________________ | | | div exp_<id> | | | | div exp_<id> | | | | (visible) | | | | (hidden) | | | | _________________________ | | | | | | | | | form | | | | | | | | | | _____________________ | | | | | _____________________ | | | | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | | | | | |_______(expand)______| | | | | | |_______(expand)______| | | | | |_________________________| | | | | | | | |_____________________________| | | |_____________________________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | ul | | ul | | _________________________ | | _________________________ | | | li (comment n+1) | | | | li (comment n+1) | | | |_________________________| | | |_________________________| | | | ... | | | | ... | | | |_________________________| | | |_________________________| | | | li (comment m) | | | | li (comment m) | | | |_________________________| | | |_________________________| | |_________________________________| |_________________________________| ● Only the last comments are displayed. ● You can click on "See previous". ● Previous comments are retrieved via AJAX. ● "See previous" is hidden. ● "See only the last" is unhidden.
  • 122. Timeline: Layout Before clicking "See prev..." --> After clicking "See prev..." _________________________________ _________________________________ | div con_<id> | | div con_<id> | | (hidden) | | (visible) | | _____________________________ | | _____________________________ | | | v See only the latest | | | | v See only the latest | | | |_________(contract)__________| | | |_________(contract)__________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | div <id> | | div <id> updated | | which content | | _____________________________ | | will be updated via AJAX | | | ul com_<id> | | | (parent of parent of form) | | | _________________________ | | | | | | | li (comment 1) | | | | | | | |_________________________| | | | | | | | ... | | | | | | | |_________________________| | | | | | | | li (comment n) | | | | | --> | | |_________________________| | | | | | |_____________________________| | | _____________________________ | | _____________________________ | | | div exp_<id> | | | | div exp_<id> | | | | (visible) | | | | (hidden) | | | | _________________________ | | | | | | | | | form | | | | | | | | | | _____________________ | | | | | _____________________ | | | | | | ^ See prev.comments | | | | | | | ^ See prev.comments | | | | | | |_______(expand)______| | | | | | |_______(expand)______| | | | | |_________________________| | | | | | | | |_____________________________| | | |_____________________________| | |_________________________________| |_________________________________| _________________________________ _________________________________ | ul | | ul | | _________________________ | | _________________________ | | | li (comment n+1) | | | | li (comment n+1) | | | |_________________________| | | |_________________________| | | | ... | | | | ... | | | |_________________________| | | |_________________________| | | | li (comment m) | | | | li (comment m) | | | |_________________________| | | |_________________________| | |_________________________________| |_________________________________| ● Only the last comments are displayed. ● You can click on "See previous". ● Previous comments are retrieved via AJAX. ● "See previous" is hidden. ● "See only the last" is unhidden. ● From now on no AJAX is needed, sections are hidden / unhidden using JavaScript.
  • 123. Timeline: Layout <div class="MED_UPLOADER"> <div id="id_..._med_ico"> <a href="" onclick="mediaActivateMediaUploader('id_...');" "return false;"> <img src="...paperclip.svg" alt="Multimedia" title="Multimedia" class="ICO_HIGHLIGHT ICOx16"> </a> </div> <div id="id_..._med_upl" style="display:none;"> <div class="FRAME_CONT"> <div class="FRAME"> <div class="FRAME_ICO"> <div class="FRAME_ICO_RIGHT"> help-link </div> </div> <div class="FRAME_TITLE FRAME_TITLE_SMALL"> Multimedia </div> <input type="hidden" name="MedAct" value="2"> _container_____________________________________________ | _<id>_med_ico | | |____Clip_____| | | | | _container <id>_med_upl_(initially hidden)_________ | | | _box___________________________________________ | | | | | ? | | | | | | Multimedia | | | | | | | | | | | | _prefs_container___________________ | | | | | | | _pref_container_________________ | | | | | | | | | _______ _______ _______ | | | | | | | | | | | Image/| |YouTube| | Embed | | | | | | | | | | | |_video_| |_______| |_______| | | | | | | | | | |_______________________________| | | | | | | | |___________________________________| | | | | | | _file_container____________________________ | | | | | | | ___________ | | | | | | | | |_Browse..._| No file selected. | | | | | | | |___________________________________________| | | | | | | _URL_container_____________________________ | | | | | | | _______________________________________ | | | | | | | | |_Link__________________________________| | | | | | | | |___________________________________________| | | | | | | _title_container___________________________ | | | | | | | _______________________________________ | | | | | | | | |_Title/attribution_____________________| | | | | | | | |___________________________________________| | | | | | |_______________________________________________| | | | |___________________________________________________| | |_______________________________________________________| function mediaActivateMediaUploader (id) { document.getElementById (id + '_med_ico').style.display = 'none'; document.getElementById (id + '_med_upl').style.display = ''; }
  • 124. Timeline: Layout <div class="PREF_CONTS"> <div class="PREF_CONT"> <div id="id_..._ico_upl" class="PREF_OFF"> <a href="" onclick="mediaClickOnActivateUpload('id_...');" "return false;"> <img src="...photo-video.svg" alt="Image/video" title="Image/video" class="ICO_HIGHLIGHT ICOx16"> </a> </div> <div id="id_..._ico_you" class="PREF_OFF"> <a href="" onclick="mediaClickOnActivateYoutube('id_...');" "return false;"> <img src="...youtube-brands.svg" alt="YouTube" title="YouTube" class="ICO_HIGHLIGHT ICOx16"> </a> </div> <div id="id_..._ico_emb" class="PREF_OFF"> <a href="" onclick="mediaClickOnActivateEmbed('id_...');" "return false;"> <img src="...code.svg" alt="Embed" title="Embed" class="ICO_HIGHLIGHT ICOx16"> </a> </div> </div> </div> _container_____________________________________________ | _<id>_med_ico | | |____Clip_____| | | | | _container <id>_med_upl_(initially hidden)_________ | | | _box___________________________________________ | | | | | ? | | | | | | Multimedia | | | | | | | | | | | | _prefs_container___________________ | | | | | | | _pref_container_________________ | | | | | | | | | _______ _______ _______ | | | | | | | | | | | Image/| |YouTube| | Embed | | | | | | | | | | | |_video_| |_______| |_______| | | | | | | | | | |_______________________________| | | | | | | | |___________________________________| | | | | | | _file_container____________________________ | | | | | | | ___________ | | | | | | | | |_Browse..._| No file selected. | | | | | | | |___________________________________________| | | | | | | _URL_container_____________________________ | | | | | | | _______________________________________ | | | | | | | | |_Link__________________________________| | | | | | | | |___________________________________________| | | | | | | _title_container___________________________ | | | | | | | _______________________________________ | | | | | | | | |_Title/attribution_____________________| | | | | | | | |___________________________________________| | | | | | |_______________________________________________| | | | |___________________________________________________| | |_______________________________________________________|
  • 125. _container_____________________________________________ | _<id>_med_ico | | |____Clip_____| | | | | _container <id>_med_upl_(initially hidden)_________ | | | _box___________________________________________ | | | | | ? | | | | | | Multimedia | | | | | | | | | | | | _prefs_container___________________ | | | | | | | _pref_container_________________ | | | | | | | | | _______ _______ _______ | | | | | | | | | | | Image/| |YouTube| | Embed | | | | | | | | | | | |_video_| |_______| |_______| | | | | | | | | | |_______________________________| | | | | | | | |___________________________________| | | | | | | _file_container____________________________ | | | | | | | ___________ | | | | | | | | |_Browse..._| No file selected. | | | | | | | |___________________________________________| | | | | | | _URL_container_____________________________ | | | | | | | _______________________________________ | | | | | | | | |_Link__________________________________| | | | | | | | |___________________________________________| | | | | | | _title_container___________________________ | | | | | | | _______________________________________ | | | | | | | | |_Title/attribution_____________________| | | | | | | | |___________________________________________| | | | | | |_______________________________________________| | | | |___________________________________________________| | |_______________________________________________________| Timeline: Layout <input type="hidden" id="id_..._par_upl" name="MedFrm" value="1" disabled> <input type="hidden" id="id_..._par_you" name="MedFrm" value="2" disabled> <input type="hidden" id="id_..._par_emb" name="MedFrm" value="3" disabled> <div> <input type="file" name="MedFil" accept="image/,video/" id="id_..._fil" class="Tml_MED_INPUT_WIDTH" style="display:none;" disabled> </div> <div> <input type="url" name="MedURL" maxlength="255" value="" id="id_..._url" class="Tml_MED_INPUT_WIDTH" placeholder="Link" style="display:none;" disabled> </div> <div> <input type="text" name="MedTit" maxlength="127" value="" id="id_..._tit" class="Tml_MED_INPUT_WIDTH" placeholder="Title/attribution" style="display:none;" disabled> </div> </div> </div> </div> </div> </div>
  • 126. Timeline: Layout _container_____________________________________________ | _<id>_med_ico | | |____Clip_____| | | | | _container <id>_med_upl_(initially hidden)_________ | | | _box___________________________________________ | | | | | ? | | | | | | Multimedia | | | | | | | | | | | | _prefs_container___________________ | | | | | | | _pref_container_________________ | | | | | | | | | _______ _______ _______ | | | | | | | | | | | Image/| |YouTube| | Embed | | | | | | | | | | | |_video_| |_______| |_______| | | | | | | | | | |_______________________________| | | | | | | | |___________________________________| | | | | | | _file_container____________________________ | | | | | | | ___________ | | | | | | | | |_Browse..._| No file selected. | | | | | | | |___________________________________________| | | | | | | _URL_container_____________________________ | | | | | | | _______________________________________ | | | | | | | | |_Link__________________________________| | | | | | | | |___________________________________________| | | | | | | _title_container___________________________ | | | | | | | _______________________________________ | | | | | | | | |_Title/attribution_____________________| | | | | | | | |___________________________________________| | | | | | |_______________________________________________| | | | |___________________________________________________| | |_______________________________________________________| function mediaClickOnActivateUpload (id) { var par_upl = document.getElementById (id + '_par_upl'); if (par_upl.disabled) { // Click on highlighted icon // par_upl already got var par_you = document.getElementById (id + '_par_you'); var par_emb = document.getElementById (id + '_par_emb'); var ico_upl = document.getElementById (id + '_ico_upl'); var ico_you = document.getElementById (id + '_ico_you'); var ico_emb = document.getElementById (id + '_ico_emb'); var fil = document.getElementById (id + '_fil'); var url = document.getElementById (id + '_url'); var tit = document.getElementById (id + '_tit'); // Enable upload, disable others par_upl.disabled = false; // Enable upload par_you.disabled = true; // Disable youtube par_emb.disabled = true; // Disable embed ico_upl.className = 'PREF_ON'; // Highlighted upload icon ico_you.className = 'PREF_OFF'; // Normal youtube icon ico_emb.className = 'PREF_OFF'; // Normal embed icon fil.style.display = ''; // Show file input fil.disabled = false; // Enable file input url.style.display = ''; // Show URL input url.disabled = false; // Enable URL input tit.style.display = ''; // Show title input tit.disabled = false; // Enable title input } else // Click on shadowed icon mediaDisableAll (id); }
  • 127. Timeline: Inserting links “Some of the best programming is done on paper. Putting it into the computer is just a minor detail.” Max Kanat-Alexander – Software Engineer at Google, author of Code Simplicity
  • 128. Timeline: Inserting links ● 1. Parse string creating a list of links (URLs or nicknames) ● Hi @admin, can I use https://openswad.org for free? ______ ______ ______ ______ |______|<-- -->|______|<-- -->|______|<-- -->|______|<--- LastLink |______| / |______| / |______| / |______| |______| / |______| / |______| / |______| |_NULL_| / ---|_Prev_| / ---|_Prev_| / ---|_Prev_| |_Next_|-- |_Next_|-- |_Next_|-- |_NULL_| struct ALn_Link { ALn_LinkType_t Type; // URL or nickname? struct ALn_Substring URLorNick; // Link text struct ALn_Substring NickAnchor[3]; // Pointer to anchors if nick size_t LengthAddedUpToHere; // Total length of extra HTML code... // ...added up to this link (included) struct ALn_Link *Prev; // Pointer to previous link struct ALn_Link *Next; // Pointer to next link }; struct ALn_Substring { // Pointer to // the first // char of // substring char *Str; // Length of // the substring size_t Len; };
  • 129. Timeline: Inserting links ● 2. For each link in the list, going back from last to first: ● 1 Move forward the text after the link ● 2 Copy the 3rd part of the anchor ● 3 Move forward the link _______________________________ |H|i|_|@|a|d|m|i|n|,|_|c|a|n|...| | | | | | | | | | | | | | | | | | | | | | | | | ___________________________________________1____ | | | | | | ______________________________3____ | | | | | | | | | | | | | | | | | | ______5____ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 6 | | | | | | 4 | | | | | | 2 | | | | | | v v v anchor#1 v v v v v v anchor#2 v v v v v v anchor#3 v v v v v v ___________________________________________________________________________ |H|i|_|<|_|_|_|_|@|a|d|m|i|n|_|_|_|_|>|@|a|d|m|i|n|<|_|_|_|_|>|,|_|c|a|n|...| ● 4 Copy the 2nd part of the anchor ● 5 Copy the link into the anchor ● 6 Copy the 1st part of the anchor
  • 130. Miss: Written in C “The C language combines all the power of assembly language with all the ease-of-use of assembly language.” Mark Pearce – Freelance consultant and developer
  • 131. Miss: Written in C ● swad-core written from scratch in C ✘Disadvantages: ● lack of specialized library functions for the web ● rejected by potential collaborators who see it as an old programming language
  • 132. Hit: Written in C “The C language combines all the power of assembly language with all the ease-of-use of assembly language.” Mark Pearce – Freelance consultant and developer
  • 133. Hit: Written in C ● swad-core written from scratch in C Advantages: ● It requires few resources ● + speed ● - memory ● swad-core executable: 3.2 MB ● Functional even in a Raspberry Pi ● Reliability ● Compile-time error detection ● It works 24 hours, fast and almost without failures ● Source code stability over time
  • 134. Hit: It's fast, you don't need a cluster “Software is getting slower more rapidly than hardware is becoming faster.” (Wirth's law) Niklaus Wirth – Designer of several programming languages, including Pascal
  • 135. ● SWAD-UGR former servers (1999-2016) 2nd: 2004-2006 Pentium 4 HT RAM 2 GiB 2 HD 160 GB Fedora 3 3rd: 2007-2008 Core 2 Duo RAM 4 GiB 2 HD 500 GB Fedora 6 4th: 2009-2010 Core 2 Quad RAM 4 GiB 2 HD 146 GB 2 HD 1 TB Fedora 10 5th: 2011-2016 2 Xeon Quad RAM 24 GiB 4 HD 146 GB 4 HD 500 GB CentOS 5.7 1st: 1999-2003 Shared server Hit: It's fast, you don't need a cluster
  • 136. Hit: It's fast, you don't need a cluster ● SWAD-UGR current server (6th: 2016-2023) ● HP Proliant DL160 G9, 2 Xeon with 6 cores, RAM 32 GiB 4 HD 146 GB SAS 15000 rpm RAID 1+0 (292 GB) SO CentOS 7.2 MySQL database 4 HD 1 TB SAS 7200 rpm RAID 5 (3 TB) Web files ( /var/www )