10d24a04f2cc45af8484f29d35899bec2e53db46
jcasper
  Fri May 27 16:29:24 2016 -0700
Initial commit of public session listing CGI hgPublicSessions and supporting changes to hgSession. refs #15312

diff --git src/hg/hgPublicSessions/hgPublicSessions.c src/hg/hgPublicSessions/hgPublicSessions.c
new file mode 100644
index 0000000..dc80a15
--- /dev/null
+++ src/hg/hgPublicSessions/hgPublicSessions.c
@@ -0,0 +1,261 @@
+/* hgPublicSessions - A gallery for hgTracks sessions. */
+#include "common.h"
+#include "linefile.h"
+#include "hash.h"
+#include "options.h"
+#include "jksql.h"
+#include "htmshell.h"
+#include "web.h"
+#include "cheapcgi.h"
+#include "cart.h"
+#include "hui.h"
+#include "ra.h"
+#include "dystring.h"
+#include "hPrint.h"
+#include "hgConfig.h"
+#include "sessionThumbnail.h"
+
+struct galleryEntry
+/* Holds data for a single session in the gallery*/
+    {
+    struct galleryEntry *next;
+    char *userName, *realName, *sessionName, *settings, *db, *firstUse;
+    char *imgPath, *imgUri;
+    struct dyString *sessionUrl;
+    unsigned long useCount;
+    };
+
+/* Global Variables */
+struct cart *cart;             /* CGI and other variables */
+struct hash *oldVars = NULL;
+
+
+struct galleryEntry *galLoad(char **row)
+/* Load a session entry from a row.  A row consists of:
+ * 0. gbMembers.realName
+ * 1. namedSessionDb.userName
+ * 2. gbMembers.idx
+ * 3. namedSessionDb.sessionName
+ * 4. namedSessionDb.useCount
+ * 5. namedSessionDb.settings
+ * 6. namedSessionDb.contents
+ * 7. namedSessionDb.firstUse */
+{
+char *dbIdx, *dbEnd;
+struct galleryEntry *ret;
+AllocVar(ret);
+ret->realName = cloneString(row[0]);
+ret->userName = cloneString(row[1]);
+cgiDecodeFull(ret->userName, ret->userName, strlen(ret->userName));
+ret->sessionName = cloneString(row[3]);
+cgiDecodeFull(ret->sessionName, ret->sessionName, strlen(ret->sessionName));
+ret->sessionUrl = dyStringCreate("hgS_doOtherUser=submit&hgS_otherUserName=%s&hgS_otherUserSessionName=%s", row[1], row[3]);
+
+ret->imgPath = sessionThumbnailFilePath(row[2], row[3], row[7]);
+if (fileExists(ret->imgPath))
+    ret->imgUri = sessionThumbnailFileUri(row[2], row[3], row[7]);
+else
+    ret->imgUri = NULL;
+ret->useCount = sqlUnsignedLong(row[4]);
+ret->settings = cloneString(row[5]);
+dbIdx = strstr(row[6], "db=") + 3;
+dbEnd = strchr(dbIdx, '&');
+if (dbEnd != NULL)
+    ret->db = cloneStringZ(dbIdx, dbEnd-dbIdx);
+else
+    ret->db = cloneString(dbIdx);
+ret->firstUse = cloneString(row[7]);
+char *spacePt = strchr(ret->firstUse, ' ');
+if (spacePt != NULL) *spacePt = '\0';
+return ret;
+}
+
+void deleteGallery (struct galleryEntry **pGal)
+/* Free all memory associated with a gallery entry */
+{
+struct galleryEntry *gal;
+if ((gal = *pGal) != NULL)
+    {
+    freeMem(gal->realName);
+    freeMem(gal->userName);
+    freeMem(gal->sessionName);
+    freeMem(gal->settings);
+    freeMem(gal->db);
+    freeMem(gal->firstUse);
+    freeMem(gal->imgPath);
+    freeMem(gal->imgUri);
+    dyStringFree(&(gal->sessionUrl));
+    freez(pGal);
+    }
+}
+
+struct galleryEntry *galleryFetch()
+/* Return an slList of gallery entries fetched from hgcentral */
+{
+struct sqlConnection *conn = hConnectCentral();
+struct sqlResult *sr = NULL;
+struct galleryEntry *gal, *galList = NULL;
+char otherConstraints[80] = "", query[2048], **row;
+
+sqlSafef (query, sizeof(query),
+    "select m.realName, s.userName, m.idx, s.sessionName, s.useCount, s.settings, s.contents, s.firstUse from "
+    "%s s left join gbMembers m on m.userName = s.userName where shared = 2%s limit 30"
+    , namedSessionTable, otherConstraints);
+sr = sqlGetResult(conn, query);
+
+while ((row = sqlNextRow(sr)) != NULL)
+    {
+    gal = galLoad(row);
+    slAddHead (&galList, gal);
+    }
+
+sqlFreeResult(&sr);
+hDisconnectCentral(&conn);
+return galList;
+}
+
+void doGalleryIncludes()
+/* Print external links to the jquery js and css files used for this CGI.  Unfortunately
+ * this conflicts a bit with the jquery we inherit from cartWebStart().  Hope
+ * to resolve that some day by figuring out what's preventing us from updating
+ * our jquery version elsewhere */
+{
+printf ("<link rel=\"stylesheet\" type=\"text/css\" "
+        "href=\"https://cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css\">\n");
+printf ("<script type=\"text/javascript\" "
+        "src=\"https://code.jquery.com/jquery-1.12.3.min.js\"></script>\n");
+printf ("<script type=\"text/javascript\" charset=\"utf8\" "
+        "src=\"https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.js\"></script>\n");
+}
+
+
+void galleryDisplay(struct galleryEntry *galList)
+/* Print a table containing the gallery data from galList */
+{
+struct galleryEntry *thisSession = galList;
+
+printf ("<script type=\"text/javascript\">"
+"$(document).ready(function () {\n"
+"    $('#sessionTable').DataTable({\"columnDefs\": [{\"visible\":false, \"targets\":[2,3]},\n"
+"                                                   {\"orderable\":false, \"targets\":[0,1]}\n"
+"                                                  ],\n"
+"                                       \"order\":[3,'desc']});\n"
+"} );\n"
+"function changeSort() {\n"
+"   var newSort = document.getElementById('sortMethod').value;\n"
+"   var theTable = $('#sessionTable').DataTable();\n"
+"   if (newSort == \"useDesc\") {theTable.order([3,'desc']).draw(); }\n"
+"   if (newSort == \"useAsc\") {theTable.order([3,'asc']).draw(); }\n"
+"   if (newSort == \"dateDesc\") {theTable.order([2,'desc']).draw(); }\n"
+"   if (newSort == \"dateAsc\") {theTable.order([2,'asc']).draw(); }\n"
+"}\n"
+"</script>\n");
+
+printf ("<p>\n");
+printf ("<b>Sort by:</b> <select id=\"sortMethod\" onchange=\"changeSort()\">\n");
+printf ("\t\t<option value=\"useDesc\">Popularity (descending)</option>\n");
+printf ("\t\t<option value=\"useAsc\">Popularity (ascending)</option>\n");
+printf ("\t\t<option value=\"dateDesc\">Creation (newest first)</option>\n");
+printf ("\t\t<option value=\"dateAsc\">Creation (oldest first)</option>\n");
+printf ("</select></p>\n");
+printf ("<table id=\"sessionTable\" class=\"display compact\" width=\"100%%\">\n"
+"    <thead>"
+"        <tr>"
+"            <th>Screenshot</th>\n"
+"            <th>Session Properties</th>\n"
+"            <th>Creation Date</th>\n"
+"            <th>Use Count</th>\n"
+"        </tr>\n"
+"    </thead>\n");
+
+printf ("<tbody>\n");
+
+while (thisSession != NULL)
+    {
+    char *settingString = NULL;
+    printf ("\t<tr>\n");
+    if (isNotEmpty(thisSession->imgUri))
+        {
+        printf ("\t\t<td><a href=\"../cgi-bin/hgTracks?%s\">",
+            dyStringContents(thisSession->sessionUrl));
+        printf ("<img src=\"%s\" class=\"sessionThumbnail\"></a></td>\n", thisSession->imgUri);
+        }
+    else
+        {
+        printf ("\t\t<td><center><nobr>Screenshot not available</nobr><br>\n");
+        printf ("\t\t<a href=\"../cgi-bin/hgTracks?%s\">Click Here</a> to view</center></td>\n",
+            dyStringContents(thisSession->sessionUrl));
+        }
+
+    struct hash *settingsHash = raFromString(thisSession->settings);
+    settingString = (char*) hashFindVal(settingsHash, "description");
+    if (settingString == NULL)
+        settingString = "";
+    else
+        {
+        settingString = replaceChars(settingString, "\\\\", "\\__ESC__");
+        settingString = replaceChars(settingString, "\\r", "\r");
+        settingString = replaceChars(settingString, "\\n", "\n");
+        settingString = replaceChars(settingString, "\\__ESC__", "\\");
+        }
+    printf ("\t\t<td><b>Description:</b> %s<br>\n", settingString);
+    printf ("\t\t<b>Author:</b> %s<br>\n", thisSession->userName);
+    printf ("\t\t<b>Session Name:</b> %s<br>\n", thisSession->sessionName);
+    printf ("\t\t<b>Genome Assembly:</b> %s<br>\n", thisSession->db);
+    printf ("\t\t<b>Creation Date:</b> %s<br>\n", thisSession->firstUse);
+    printf ("\t\t<b>Views:</b> %ld\n", thisSession->useCount);
+    printf ("\t\t</td>\n");
+    struct tm creationDate;
+    strptime(thisSession->firstUse, "%Y-%m-%d", &creationDate);
+    /* Hidden columns */
+    printf ("\t\t<td>%ld</td>\n", mktime(&creationDate));
+    printf ("\t\t<td>%ld</td>\n", thisSession->useCount);
+    printf ("\t</tr>\n");
+    thisSession = thisSession->next;
+    }
+
+printf ("</tbody>\n");
+printf ("</table>\n");
+}
+
+void showGalleryTab ()
+/* Rather boring now, but a placeholder against the day that there's also a "favorites" tab */
+{
+struct galleryEntry *galList = galleryFetch();
+galleryDisplay(galList);
+}
+
+void doMiddle(struct cart *theCart)
+/* Set up globals and make web page */
+{
+cart = theCart;
+char *db = cartUsualString(cart, "db", hDefaultDb());
+cartWebStart(cart, db, "Public Sessions");
+
+doGalleryIncludes();
+
+printf("<p>Our users have marked the following sessions as being of "
+    "interest to the community."
+    "<br>See the <a href=\"../goldenPath/help/hgSessionHelp.html\" "
+    "target=_blank>Sessions User's Guide</a> "
+    "for more information on how to add your sessions to this page.<p/>\n");
+
+showGalleryTab();
+
+printf ("<p>You can adjust the settings for your own sessions on\n"
+    "the <a href=\"hgSession\">Sessions</a> page.\n</p>");
+
+cartWebEnd();
+}
+
+/* Null terminated list of CGI Variables we don't want to save
+ * permanently. */
+char *excludeVars[] = {"Submit", "submit", NULL,};
+
+int main(int argc, char *argv[])
+/* Process command line. */
+{
+cgiSpoof(&argc, argv);
+cartEmptyShell(doMiddle, hUserCookie(), excludeVars, oldVars);
+return 0;
+}