From a5b52d8264cbd3cd6a5d3e52cc6b515637c60c1c Mon Sep 17 00:00:00 2001 From: Gabor Adam Toth Date: Wed, 24 Feb 2010 05:55:08 +0100 Subject: [PATCH] threads web interface improvements, accessible now at /~nick/channel & /@place --- world/drivers/ldmud/master/accept.c | 12 +- world/net/http/oauth.c | 2 +- world/net/http/server.c | 18 +- world/net/include/net.h | 3 + world/net/person.c | 1 + world/net/place/archetype.gen | 1 + world/net/place/threads.c | 644 ++++++++++++++-------------- world/net/place/userthreads.c | 17 +- world/net/user.c | 2 +- world/static/examine.css | 15 +- 10 files changed, 372 insertions(+), 343 deletions(-) diff --git a/world/drivers/ldmud/master/accept.c b/world/drivers/ldmud/master/accept.c index b5c849c..880c8ff 100644 --- a/world/drivers/ldmud/master/accept.c +++ b/world/drivers/ldmud/master/accept.c @@ -384,6 +384,12 @@ object compile_object(string file) { return rob; } # endif + if (sscanf(file, "%s/text.c", path) && path != "") { + rob = clone_object(NET_PATH "text"); + rob -> sPath(path); + D2(if (rob) PP(("DB CLONED: %O becomes %s/text\n", rob, path));) + return rob; + } if (sscanf(file, "place/%s.c", name) && name != "") { #ifdef SANDBOX string t; @@ -444,12 +450,6 @@ object compile_object(string file) { rob, name, path));) return rob; } - if (sscanf(file, "%s/text.c", path) && path != "") { - rob = clone_object(NET_PATH "text"); - rob -> sPath(path); - D2(if (rob) PP(("DB CLONED: %O becomes %s/text\n", rob, path));) - return rob; - } # ifdef JABBER_PATH if (abbrev("S:xmpp:", file)) { rob = clone_object(JABBER_PATH "gateway"); diff --git a/world/net/http/oauth.c b/world/net/http/oauth.c index a8c455c..a8cb21c 100644 --- a/world/net/http/oauth.c +++ b/world/net/http/oauth.c @@ -18,7 +18,7 @@ mapping request_params = ([ ]); mapping access_params = ([ ]); string access_token_url; string authorize_url; -string callback_url = "http://" + my_lower_case_host() + ":" + HTTP_PORT + "/oauth"; //TODO: https? +string callback_url = HTTPS_OR_HTTP_URL + "/oauth"; object user; varargs void fetch(object ua, string url, string method, mapping post, mapping oauth) { diff --git a/world/net/http/server.c b/world/net/http/server.c index d83b0eb..dbff168 100644 --- a/world/net/http/server.c +++ b/world/net/http/server.c @@ -191,15 +191,21 @@ case "/oauth": quit(); return 1; } + string name; switch (file[1]) { case '~': - if (o = summon_person(file[2..], NET_PATH "user")) { - o->htinfo(version, query, headers, qs); + string channel, nick = file[2..]; + if (sscanf(file, "/~%s/%s", nick, channel)) { + name = "~" + nick + "#" + channel; + } else if (o = summon_person(nick, NET_PATH "user")) { + o->htinfo(version, query, headers, qs, channel); + quit(); + return 1; } - quit(); - return 1; + //fall thru case '@': - file = PLACE_PATH+ lower_case(file[2..]); + unless(name) name = file[2..]; + o = find_place(name); break; default: if (abbrev("/static/", file)) { @@ -232,7 +238,7 @@ case "/oauth": return; } - o = file -> load(); + unless (o) o = file -> load(); if (objectp(o) || o = find_object(file)) done = o->htget(version, query, headers, qs) != HTMORE; diff --git a/world/net/include/net.h b/world/net/include/net.h index b40fa71..d31a5a3 100644 --- a/world/net/include/net.h +++ b/world/net/include/net.h @@ -125,6 +125,9 @@ # endif #endif +#define HTTPS_OR_HTTP_URL (HTTPS_URL ? HTTPS_URL : HTTP_URL) +#define HTTP_OR_HTTPS_URL (HTTP_URL ? HTTP_URL : HTTPS_URL) + #ifdef _uniform_node # define SERVER_UNIFORM _uniform_node #else diff --git a/world/net/person.c b/world/net/person.c index fb70bde..1004b75 100644 --- a/world/net/person.c +++ b/world/net/person.c @@ -477,6 +477,7 @@ qDescription(source, vars, profile, itsme) { } // don't make_json for anonymous queries which are handled locally dv["_channels"] = source ? make_json(channels) : channels; + dv["_profile_url"] = HTTPS_OR_HTTP_URL + "/~" + MYNICK; #endif // PT(("sending: %O\n", dv)) return dv; diff --git a/world/net/place/archetype.gen b/world/net/place/archetype.gen index c7f9fe0..262deac 100644 --- a/world/net/place/archetype.gen +++ b/world/net/place/archetype.gen @@ -450,6 +450,7 @@ showTopic(rcpt, verbose, mc) { #if HAS_PORT(HTTP_PORT, HTTP_PATH) || HAS_PORT(HTTPS_PORT, HTTP_PATH) htget(prot, query, headers, qs, data, noprocess) { + //P3((">> archetype.gen:htget(%O, %O, %O, %O, %O, %O)\n", prot, query, headers, qs, data, noprocess)) # ifdef PLACE_SCRATCHPAD sTextPath(query["layout"] || MYNICK, query["lang"], "html"); if (!noprocess && (!qs || query["scratchpad"])) { diff --git a/world/net/place/threads.c b/world/net/place/threads.c index 35338b7..f4c28d8 100644 --- a/world/net/place/threads.c +++ b/world/net/place/threads.c @@ -7,7 +7,11 @@ inherit NET_PATH "place/owned"; #ifndef DEFAULT_BACKLOG -# define DEFAULT_BACKLOG 5 +# define DEFAULT_BACKLOG 10 +#endif + +#ifndef STYLESHEET +# define STYLESHEET (v("_uniform_style") || "/static/examine.css") #endif // datenstruktur für threads? @@ -173,19 +177,6 @@ _request_delentry(source, mc, data, vars, b) { return 1; } -#if 0 -_request_iterator(source, mc, data, vars, b) { - unless (canPost(SNICKER)) return 0; - sendmsg(source, "_notice_thread_iterator", - "[_iterator] blog entries have been requested " - "since creation.", ([ - // i suppose this wasn't intentionally using - // MMP _count so i rename it to _iterator - "_iterator" : v("iterator") - ]) ); - return 1; -#endif - msg(source, mc, data, vars){ P3(("thread:msg(%O, %O, %O, %O)", source, mc, data, vars)) // TODO: die source muss hierbei uebereinstimmen mit dem autor @@ -201,63 +192,6 @@ msg(source, mc, data, vars){ return ::msg(source, mc, data, vars); } -#if 0 -listLastEntries(number) { - mapping* entries; - int i; - entries = _thread || ({ }); - - unless (sizeof(entries)) return 1; - - i = v("iterator") || 0; - vSet("iterator", i + 1); - - return entries[= 0 && id <= sizeof(_thread) && _thread[id]) return 0; _thread[id]["thread"] = thread; @@ -336,242 +270,195 @@ delEntry(int id, source, vars) { } htget(prot, query, headers, qs, data) { - mapping entrymap; - mixed target; - string nick; - int i; - int a; - mapping* entries; - - int num_entries = query["last"] ? to_int(query["last"]) : DEFAULT_BACKLOG; - unless (webact) webact = PLACE_PATH + MYLOWERNICK; - // shouldnt it be "html" here? - sTextPath(query["layout"] || MYNICK, query["lang"], "ht"); + mapping entrymap; + mixed target; + string nick; + int a; + int limit = to_int(query["limit"]) || DEFAULT_BACKLOG; + int offset = to_int(query["offset"]); - // Kommentare anzeigen - if (query["comments"]) { - htok(prot); - // kommentare + urspruengliche Nachricht anzeigen - displayHeader(); - displayComments(_thread[to_int(query["comments"])]); - // eingabeformular ohne betreff - write("
\n" - "\n" - "PSYC Uni:
\n" - "\n" - "
\n" - "\n" - "
\n"); - write("


"); - logView(a < 24 ? a : 12, "html", 15); - displayFooter(); - return 1; + unless (webact) webact = PLACE_PATH + MYLOWERNICK; + // shouldnt it be "html" here? + sTextPath(query["layout"] || MYNICK, query["lang"], "ht"); + + // Kommentare anzeigen + if (query["id"]) { + htok(prot); + // kommentare + urspruengliche Nachricht anzeigen + displayHeader(); + displayEntry(to_int(query["id"])); +#if 0 + // eingabeformular ohne betreff + write("
\n" + "\n" + "PSYC Uni:
\n" + "\n" + "
\n" + "\n" + "
\n"); + write("


"); +#endif + //logView(a < 24 ? a : 12, "html", 15); + displayFooter(); + return 1; + } + + // formularbehandlung + if (query["request"] == "post" && query["uni"]) { + htok(prot); + /* + sendmsg uni -> _request_authentication mit thread und text drin + dann auf die antwort warten die nen vars mapping mit thread + text hat wieder + */ + if (nick = legal_name(target = query["uni"])) { + target = summon_person(nick); + nick = target->qNick(); + } else { + nick = target; + // write("Hello " + query["uni"] + "
\n"); + // write("Remote auth doesn't work yet. TODO!!!\n"); + // return 1; } - - // formularbehandlung - if (query["request"] == "post" && query["uni"]) { - htok(prot); - /* - sendmsg uni -> _request_authentication mit thread und text drin - dann auf die antwort warten die nen vars mapping mit thread + text hat wieder - */ - if (nick = legal_name(target = query["uni"])) { - target = summon_person(nick); - nick = target->qNick(); - } else { - nick = target; -// write("Hello " + query["uni"] + "
\n"); -// write("Remote auth doesn't work yet. TODO!!!\n"); -// return 1; - } #ifdef OWNED - if (canPost(nick)) { + if (canPost(nick)) { #endif #if 0 - sendmsg(target, "_request_authentication", "please auth me!", - (["_host_IP" : query_ip_number(), - "_blog_thread" : query["thread"], - "_blog_text" : query["text"] ])); - write("your submit is avaiting authentication by " + query["uni"] + "
\n"); + sendmsg(target, "_request_authentication", "please auth me!", + (["_host_IP" : query_ip_number(), + "_blog_thread" : query["thread"], + "_blog_text" : query["text"] ])); + write("your submit is avaiting authentication by " + query["uni"] + "
\n"); #endif // 0 - if (target->checkAuthentication(ME, ([ "_host_IP" : query_ip_number() ]) ) > 0) { - // check ob reply auf irgendwas ist... - if (query["reply"]) { - addComment(query["text"], query["uni"], to_int(query["reply"])); - } else { - addEntry(query["text"], query["uni"], query["thread"]); - } - write("authentication successful!\n"); + if (target->checkAuthentication(ME, ([ "_host_IP" : query_ip_number() ]) ) > 0) { + // check ob reply auf irgendwas ist... + if (query["reply"]) { + addComment(query["text"], query["uni"], to_int(query["reply"])); } else { - write("not authenticated!\n"); + addEntry(query["text"], query["uni"], query["thread"]); } + write("authentication successful!\n"); + } else { + write("not authenticated!\n"); + } #ifdef OWNED - } else { - write("You are not owner or aide of this place.\n"); - } -#endif - return 1; - } - // neuen Eintrag verfassen - if (query["request"] == "form") { - htok(prot); - displayHeader(); - write("
\n" - "\n" - "PSYC Identity:

\n" - "Thread:

\n" - "Text:
\n
\n" - "\n" - "
\n"); - displayFooter(); - return 1; - } - ::htget(prot, query, headers, qs, data, 1); // no processing, just info - // javascript-export - if (query["export"] == "javascript") { - // check If-Modified-Since header - htok3(prot, "application/x-javascript", "Cache-Control: no-cache\n"); - jscriptExport(num_entries); - } else if (query["export"] == "rss" || query["export"] == "rdf") { - // export als RSS - // scheinbar gibt es ein limit von 15 items / channel - // htquote auch hier anwenden - // check If-Modified-Since header - htok3(prot, "text/xml", ""); - rssExport(num_entries); } else { - // normaler Export - P2(("all entries: %O\n", _thread)) - htok3(prot, "text/html", "Cache-Control: no-cache\n"); - displayHeader(); - // display the blog - displayMain(num_entries); - // display the chatlog - logView(a < 24 ? a : 12, "html", 15); - displayFooter(); + write("You are not owner or aide of this place.\n"); } - return 1; -} - -rssExport(last) { - int i; - int len; - - len = sizeof(_thread); - if (last > len) last = len; - write("\n" - "\n\n" - "\n" - "\tPSYC - Protocol for Synchronous Conferencing\n" - "\thttp://www.psyc.eu\n" - "\tNews about the PSYC project\n" - "\n"); - for (i = len - last; i < len; i++) { - write("\n\n" - "\t"+ _thread[i]["thread"] +"\n" - "\thttp://" + SERVER_HOST + ":33333" + webact + "?comments=" + i + "\n" - "\t" + _thread[i]["text"] + "\n" - "\t" + isotime(ctime(_thread[i]["date"]), 1) + "\n" - "\t" + _thread[i]["author"] + "\n"); - write("\n"); - } - - write("\n"); -} - -jscriptExport(last) { - mapping item; - string buf = ""; - - // htok3(prot, "application/x-javascript", "Cache-Control: no-cache\n"); - write("function Entry(thread, author, date, text) {\n" - "\tthis.thread = thread;\n" - "\tthis.author = author;\n" - "\tthis.date = date;\n" - "\tthis.text = text;\n" - "}\n\n" - "document.blogentries = new Array(\n"); - foreach (item : _thread[ len) last = len; - P2(("len %d, last %d\n", len, last)) - for (i = len - last; i < len; i++) { - write("" - "" - "" - "" - "
" + _thread[i]["thread"] - + "" + _thread[i]["author"] + "" + isotime(ctime(_thread[i]["date"]), 1) + "
" + _thread[i]["text"] - + "
"); - write("there are " + sizeof(_thread[i]["comments"]) + " comments"); - write("
\n"); - } -} #endif + return 1; + } + // neuen Eintrag verfassen + if (query["request"] == "form") { + htok(prot); + displayHeader(); + write("
\n" + "\n" + "PSYC Identity:

\n" + "Thread:

\n" + "Text:
\n
\n" + "\n" + "
\n"); + displayFooter(); + return 1; + } -htmlEntries(array(mapping) entries, int js, string chan, string submit) { - P3((">> threads:htmlentries(%O, %O, %O, %O)\n", entries, js, chan, submit)) + //::htget(prot, query, headers, qs, data, 1); // no processing, just info + + string export = query["export"] || query["format"]; + if (export == "js") { + // check If-Modified-Since header + htok3(prot, "application/x-javascript", "Cache-Control: no-cache\n"); + jsExport(limit, offset); + } else if (export == "json") { + // check If-Modified-Since header + htok3(prot, "application/json", "Cache-Control: no-cache\n"); + jsonExport(limit, offset); + } else if (export == "rss" || export == "rdf") { + // export als RSS + // scheinbar gibt es ein limit von 15 items / channel + // htquote auch hier anwenden + // check If-Modified-Since header + htok3(prot, "text/xml", ""); + rssExport(limit, offset); + } else { + // normaler Export + P2(("all entries: %O\n", _thread)) + htok3(prot, "text/html", "Cache-Control: no-cache\n"); + displayHeader(); + // display the blog + displayMain(limit, offset); + // display the chatlog + + if (showWebLog()) logView(a < 24 ? a : 12, "html", 15); + displayFooter(); + } + return 1; +} + +entries(int limit, int offset) { + array(mapping) entries = ({ }); + int i, n = 0, o = 0; + for (i = sizeof(_thread) - 1; i >= 0; i--) { + P3((">>> _thread[%O]: %O\n", i, _thread[i])) + unless (_thread[i]) continue; + if (o++ < offset) continue; + entries += ({ _thread[i] }); + if (++n >= limit) break; + } + return entries; +} + +htmlEntries(array(mapping) entries, int nojs, string chan, string submit, string url_prefix) { + P3((">> threads:htmlentries(%O, %O, %O, %O)\n", entries, nojs, chan, submit)) string t, ht = ""; string id_prefix = chan ? chan + "-" : ""; - if (js) ht += + unless(url_prefix) url_prefix = ""; + unless (nojs) ht += "\n"; - foreach (mapping item : entries) { - P3((">>> item: %O\n", item)) - unless (item) continue; - t = htquote(item["text"]); + foreach (mapping entry : entries) { + P3((">>> entry: %O\n", entry)) + unless (entry) continue; + + t = htquote(entry["text"]); t = replace(t, "\n", "
\n"); t = replace(t, "<", "<"); t = replace(t, ">", ">"); string c = ""; - if (item["comments"]) - foreach(mapping comment : item["comments"]) + if (entry["comments"]) + foreach(mapping comment : entry["comments"]) c += "
" + comment["nick"] + ": " + comment["text"] + "
\n"; ht += "
\n" "
\n" - "#" + item["id"] + " - \n" - "" + item["author"] + "\n" - + (item["thread"] && strlen(item["thread"]) ? " - " : "") + - "" + htquote(item["thread"]) + "\n" + "" + "#" + entry["id"] + " - \n" + "" + entry["author"] + "\n" + + (entry["thread"] && strlen(entry["thread"]) ? " - " : "") + + "" + htquote(entry["thread"]) + "\n" + "" "
\n" "
\n" "
" + t + "
\n" - "\n"; @@ -580,50 +467,107 @@ htmlEntries(array(mapping) entries, int js, string chan, string submit) { return "
" + ht + "
"; } -htMain(int last) { - return htmlEntries(entries(last), 1); -} +rssEntries(array(mapping) entries) { + string rss = + "\n" + "\n\n" + "\n" + "\tPSYC - Protocol for Synchronous Conferencing\n" + "\thttp://www.psyc.eu\n" + "\tNews about the PSYC project\n" + "\n"; -entries(int last) { - array(mapping) entries = ({ }); - int i, n = 0; - for (i = sizeof(_thread) - 1; i >= 0; i--) { - P3((">>> _thread[%O]: %O\n", i, _thread[i])) - unless (_thread[i]) continue; - entries += ({ _thread[i] }); - if (++n >= last) break; + foreach (mapping entry : entries) { + rss += + "\n\n" + "\t"+ entry["thread"] +"\n" + "\thttp://" + SERVER_HOST + ":33333" + webact + "?id=" + entry["id"] + "\n" + "\t" + entry["text"] + "\n" + "\t" + isotime(ctime(entry["date"]), 1) + "\n" + "\t" + entry["author"] + "\n" + "\n"; } - return entries; + rss += "\n"; + return rss; } -jsonEntries(int last) { - return make_json(entries(last)); -} +jsEntries(array(mapping) entries) { + string js = + "function Entry(id, thread, author, date, text) {\n" + "\tthis.id = id;\n" + "\tthis.thread = thread;\n" + "\tthis.author = author;\n" + "\tthis.date = date;\n" + "\tthis.text = text;\n" + "}\n\n" + "document.blogentries = new Array(\n"; -htComments(entry) { - mapping item; - string ht = ""; - - write("" + entry["author"] + ": " + entry["text"] + "

\n"); - if (entry["comments"]) { - foreach(item : entry["comments"]) { - ht += "" + item["nick"] + ": " + item["text"] + "
\n"; + foreach (mapping entry : entries) { + js += "new Entry(" + entry["id"] + "," + "\"" + entry["thread"] + "\"," + "\"" + entry["author"] + "\"," + + isotime(ctime(entry["date"]), 1) + "," + "\"" + entry["text"] + "\"),\n"; } - } else { - ht += "no comments...
\n"; - } - return ht; + + return js[..<3] + ");"; } -displayMain(last) { - write(htMain(last)); +jsonEntries(int limit, int offset) { + return make_json(entries(limit, offset)); } -displayComments(entry) { - write(htComments(entry)); +jsonExport(int limit, int offset) { + write(jsonEntries(limit, offset)); } +jsExport(int limit, int offset) { + write(jsEntries(limit, offset)); +} + +rssExport(int limit, int offset) { + write(rssEntries(entries(limit, offset))); +} + +htMain(int limit, int offset, string chan) { + return htmlEntries(entries(limit, offset), 0, chan); +} + +displayMain(int limit, int offset) { + write(htMain(limit, offset)); +} + +htEntry(int id) { + unless (_thread && id >= 0 && id <= sizeof(_thread) && _thread[id]) return 0; + return htmlEntries(({ _thread[id] })); +} + +displayEntry(int id) { + write(htEntry(id) || "No such entry."); +} + +// wir können zwei strategien fahren.. die technisch einfachere ist es +// die reihenfolge der elemente festzulegen und für jedes ein w(_HTML_xy +// auszuspucken. flexibler wär's stattdessen wenn jede seite ein einziges +// w(_PAGES_xy ausgeben würde in dem es per [_HTML_list_threads] oder +// ähnlichem die blog-elemente per psyctext-vars übergibt ... dann kann +// es immernoch per {_HTML_head_threads} header und footer einheitlich +// halten. womöglich kann man auch nachträglich plan A in plan B +// umwandeln..... hmmm -lynX +// +displayHeader() { + w("_HTML_head_threads", + "\n"+ + "\n\n"); +} +displayFooter() { + w("_HTML_tail_threads", ""); +} + + nntpget(cmd, args) { mapping item; int i; @@ -671,29 +615,6 @@ default: } } -#ifndef STYLESHEET -# define STYLESHEET (v("_uniform_style") || "/static/examine.css") -#endif - -// wir können zwei strategien fahren.. die technisch einfachere ist es -// die reihenfolge der elemente festzulegen und für jedes ein w(_HTML_xy -// auszuspucken. flexibler wär's stattdessen wenn jede seite ein einziges -// w(_PAGES_xy ausgeben würde in dem es per [_HTML_list_threads] oder -// ähnlichem die blog-elemente per psyctext-vars übergibt ... dann kann -// es immernoch per {_HTML_head_threads} header und footer einheitlich -// halten. womöglich kann man auch nachträglich plan A in plan B -// umwandeln..... hmmm -lynX -// -displayHeader() { - w("_HTML_head_threads", - "\n"+ - "\n\n"); -} -displayFooter() { - w("_HTML_tail_threads", ""); -} - canPost(snicker) { return qAide(snicker); } @@ -702,6 +623,87 @@ mayLog(mc) { return abbrev("_notice_thread", mc) || abbrev("_message", mc); } +showWebLog() { + return 1; +} + numEntries() { return sizeof(_thread); } + +pathName() { + return psycName(); +} + +// old stuff + +#if 0 +_request_iterator(source, mc, data, vars, b) { + unless (canPost(SNICKER)) return 0; + sendmsg(source, "_notice_thread_iterator", + "[_iterator] blog entries have been requested " + "since creation.", ([ + // i suppose this wasn't intentionally using + // MMP _count so i rename it to _iterator + "_iterator" : v("iterator") + ]) ); + return 1; +#endif + +#if 0 +listLastEntries(number) { + mapping* entries; + int i; + entries = _thread || ({ }); + + unless (sizeof(entries)) return 1; + + i = v("iterator") || 0; + vSet("iterator", i + 1); + + return entries[#" + channel + "\n"; contents += "
" + threads->htmlEntries(entries, 0, first, channel, anonymous ? "" : vars["_identification"] + "#" + channel) + "
\n"; + + (first ? "class='selected'" : "") + ">" + threads->htmlEntries(entries, !first, channel, anonymous ? "" : vars["_identification"] + "#" + channel, vars["_profile_url"] + "/" + channel) + "
\n"; first = 0; } diff --git a/world/static/examine.css b/world/static/examine.css index 6d2238b..77bc6cc 100644 --- a/world/static/examine.css +++ b/world/static/examine.css @@ -13,7 +13,7 @@ body.threads, .Pe a { text-decoration: none; color: #f33; - background: black; + /*background: black;*/ } .Peb a { padding: 4; @@ -110,19 +110,22 @@ body.threads, width: 100%; } -.entry .title {} +.entry .title a { + color: black; +} .entry .title .author {} .entry .title .subject {} .entry .footer a, .entry .footer a:visited { - cursor: pointer; color: white; - background: transparent; } -.entry .footer a:hover, -.entry .footer a:visited:hover { +.entry a { + cursor: pointer; + text-decoration: none; +} +.entry a:hover { text-decoration: underline; }