diff --git a/world/drivers/ldmud/master/accept.c b/world/drivers/ldmud/master/accept.c index db63890..445bfec 100644 --- a/world/drivers/ldmud/master/accept.c +++ b/world/drivers/ldmud/master/accept.c @@ -336,6 +336,7 @@ object connect(int uid, int port, string service) { #ifdef DRIVER_HAS_RENAMED_CLONES // named clones -lynx object compile_object(string file) { + P3((">> compile_object(%O)\n", file)) string path, name; object rob; @@ -383,36 +384,46 @@ object compile_object(string file) { return rob; } # endif - if (sscanf(file, "%s#%s.c", path, name) && name != "") { - unless (name = SIMUL_EFUN_FILE->legal_name(name)) - return (object)0; - rob = clone_object(path); - rob -> sName(name); - D2(if (rob) PP(("NAMED CLONE: %O becomes %s of %s\n", - rob, name, path));) - return rob; - } if (sscanf(file, "place/%s.c", name) && name != "") { #ifdef SANDBOX string t; #endif - unless (name = SIMUL_EFUN_FILE->legal_name(name)) + unless (name = SIMUL_EFUN_FILE->legal_name(name, 1)) return (object)0; + string username; + if (sscanf(file, "place/~%s#updates", username)) { + object p; + unless ((p = SIMUL_EFUN_FILE->summon_person(username, NET_PATH "user")) && p->vQuery("password")) { + P3(("PLACE %O NOT CLONED: %O isn't a registered user\n", name, username)); + return (object)0; + } + + if (rob = clone_object(NET_PATH "place/userthreads")) { + PP(("PLACE CLONED: %O becomes %O\n", rob, file)); + rob->sName(name); + return rob; + } else { + P3(("ERROR: could not clone place %O\n", name)); + return (object)0; + } + } + #ifdef SANDBOX if (file_size(t = USER_PATH + name + ".c") != -1) { rob = t -> sName(name); D2(if (rob) PP(("USER PLACE loaded: %O becomes %O\n", rob, file));) } else { #endif + #ifdef _flag_disable_places_arbitrary P2(("WARN: cloned places disabled by #define %O\n", file)) return (object)0; #else #ifdef _path_archetype_place_default - rob = clone_object(_path_archetype_place_default); + rob = clone_object(_path_archetype_place_default); #else - rob = clone_object(NET_PATH "place/default"); + rob = clone_object(NET_PATH "place/default"); #endif rob -> sName(name); D2(if (rob) PP(("PLACE CLONED: %O becomes %O\n", rob, file));) @@ -422,6 +433,15 @@ object compile_object(string file) { #endif return rob; } + if (sscanf(file, "%s#%s.c", path, name) && name != "") { + unless (name = SIMUL_EFUN_FILE->legal_name(name)) + return (object)0; + rob = clone_object(path); + rob -> sName(name); + D2(if (rob) PP(("NAMED CLONE: %O becomes %s of %s\n", + rob, name, path));) + return rob; + } if (sscanf(file, "%s/text.c", path) && path != "") { rob = clone_object(NET_PATH "text"); rob -> sPath(path); diff --git a/world/net/library.i b/world/net/library.i index 89bfe40..65f86f9 100644 --- a/world/net/library.i +++ b/world/net/library.i @@ -546,12 +546,13 @@ int boss(mixed guy) { } mixed find_place(mixed a) { + P3((">> find_place(%O)\n", a)) string path, err; object o; if (objectp(a)) return a; if (path = lower_uniform(a)) return path; - unless (a = legal_name(a)) return 0; + unless (a = legal_name(a, 1)) return 0; path = PLACE_PATH + lower_case(a); // assumes amylaar o = find_object(path); if (o) return o; diff --git a/world/net/library/legal.c b/world/net/library/legal.c index 2f40f97..e54a6c2 100644 --- a/world/net/library/legal.c +++ b/world/net/library/legal.c @@ -2,8 +2,9 @@ #include // legal nickname/placename test.. -string legal_name(string n) { +varargs string legal_name(string name, int place) { int i; + string n = name; //PT(("legal_name(%O) in %O\n", n, ME)) if (shutdown_in_progress) { @@ -25,17 +26,24 @@ string legal_name(string n) { P1(("not legal_name: %O has !=- as first char.\n", n)) return 0; } - for (i=strlen(n)-1; i>=0; i--) { - if (index("\ + + string nick; + if (place && sscanf(name, "~%s#updates", nick)) + n = nick; + + string chars = "\ abcdefghijklmnopqrstuvwxyz\ ABCDEFGHIJKLMNOPQRSTUVWXYZ\ -0123456789_-=+", n[i]) == -1) { +0123456789_-=+"; + + for (i=strlen(n)-1; i>=0; i--) { + if (index(chars, n[i]) == -1) { P1(("not legal_name: %O has ill char at %d (%O).\n", n, i, n[i])) return 0; } } - return n; + return name; } array(string) legal_password(string pw, string nick) { diff --git a/world/net/place/archetype.gen b/world/net/place/archetype.gen index eab4ddd..9976a01 100644 --- a/world/net/place/archetype.gen +++ b/world/net/place/archetype.gen @@ -291,8 +291,8 @@ msgView(source, mc, data, vars, showingLog) { // t = regreplace(s, ": ", "\", \"", 1); // if (s == t) return; if (showingLog == "html") -// fmt = "(%s) %s %s: %s
\n"; - fmt = stringp(t[2]) ? "%s%s %s%s\n" : "%s%s %s\n"; +// fmt = "(%s) %s %s: %s
\n"; + fmt = stringp(t[2]) ? "
%s %s %s %s
\n" : "
%s %s %s
\n"; else // showingLog == "ecmascript" fmt = "\t%O, %O, %O, %O,\n"; @@ -2614,5 +2614,3 @@ qAide(snicker, aidesonly) { qOwner(snicker) { return member(v("owners"), lower_case(snicker)); } #endif - - diff --git a/world/net/place/threads.c b/world/net/place/threads.c index 8b19a7f..bb4a8e6 100644 --- a/world/net/place/threads.c +++ b/world/net/place/threads.c @@ -35,11 +35,13 @@ volatile int last_modified; volatile string webact; create() { + P3((">> threads:create()\n")) ::create(); unless (pointerp(_thread)) _thread = ({ }); } cmd(a, args, b, source, vars) { + P3((">> threads:cmd(%O, %O, %O, %O, %O)", a, args, b, source, vars)) // TODO: multiline-sachen irgendwie mapping entry; int i = 0; @@ -51,34 +53,73 @@ cmd(a, args, b, source, vars) { // _thread[<5..] foreach( entry : _thread[ 1){ + sendmsg(source, "_warning_usage_entry", + "Usage: /entry ", ([ ])); + return 1; + } + int n = to_int(args[1]); + entry = _thread[n]; + sendmsg(source, "_list_thread_item", + "#[_number] [_author][_sep][_thread]: [_text] ([_comments])", + ([ + "_sep" : strlen(entry["thread"]) ? " - " : "", + "_thread" : entry["thread"], + "_text" : entry["text"], + "_author" : entry["author"], + "_date" : entry["date"], + "_comments": sizeof(entry["comments"]), + "_number" : n, + "_nick_place" : MYNICK ]) ); + + if (entry["comments"]) { + foreach(mapping item : entry["comments"]) { + sendmsg(source, "_list_thread_comment", + "> [_nick]: [_text]", + ([ + "_nick" : item["nick"], + "_text" : item["text"], + "_nick_place" : MYNICK ]) ); + } + } + return 1; case "thread": - unless (sizeof(args) > 2){ // num + thread + unless (sizeof(args) > 2){ sendmsg(source, "_warning_usage_thread", "Usage: /thread ", ([ ])); return 1; } return setSubject(to_int(args[1]), ARGS(2)); + case "comment": + unless (sizeof(args) >= 2) { + sendmsg(source, "_warning_usage_reply", + "Usage: /comment <threadid> <text>", ([ ])); + return 1; + } + return addComment(ARGS(2), SNICKER, to_int(args[1])); case "blog": case "submit": case "addentry": unless (qAide(SNICKER)) return; - unless (sizeof(args) >= 2) { + unless (sizeof(args) >= 1) { sendmsg(source, "_warning_usage_submit", - "Usage: /submit <title> <text>", ([ ])); + "Usage: /submit <text>", ([ ])); return 1; } - return addEntry(ARGS(2), SNICKER, args[1]); + return addEntry(ARGS(1), SNICKER); // TODO: append fuer multiline-sachen #if 0 case "iterator": @@ -111,8 +152,8 @@ cmd(a, args, b, source, vars) { return ::cmd(a, args, b, source, vars); } -msg(source, mc, data, mapping vars){ - P2(("blog.c's msg: mc %O, source %O\n", mc, source)) +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 if (abbrev("_notice_authentication", mc)){ sendmsg(source, "_notice_place_blog_authentication_success", "([_entry]) has been authenticated", @@ -252,6 +293,7 @@ delEntry(int number, source, vars) { } _thread = entries[0..number-1] + entries[number+1..]; + //_thread[number] = 0; save(); return 1; @@ -449,47 +491,61 @@ displayMain(last) { } #endif -displayMain(last) { +htMain(last) { int i; int len; string t; + string ht = ""; len = sizeof(_thread); if (last > len) last = len; // reverse order for (i = len-1; i >= len - last; i--) { + P3((">>> _thread[%O]: %O\n", i, _thread[i])) t = htquote(_thread[i]["text"]); t = replace(t, "\n", "<br>\n"); - write("<table class='newsTable' width='560px'>\n" - "<tr>" - "<td class='newsAuthor' width='20%'>" + _thread[i]["author"] + "</td>" - "<td class='newsTime' nowrap width='20%'>" + _thread[i]["date"] + "</td>" - "<td class='newsSubject' width='60%'>" + htquote(_thread[i]["thread"]) + "</td>" - "</tr>\n" - "<tr>" - "<td colspan='3' class='newsText'>" + t + "</td>" - "</tr>\n" - "<tr>" - "<td colspan='3' class='newsLink'><a href='" + webact + "?comments" - "=" + i + "'>" + sizeof(_thread[i]["comments"]) + " comments</a></td>" - "</tr>\n" - "</table>\n"); + t = replace(t, "<", "<"); + t = replace(t, ">", ">"); + + ht += "<div class='entry'>\n" + "<div class='title'>\n" + "<span class='author'>" + _thread[i]["author"] + "</span>\n" + "<span class='subject'>" + htquote(_thread[i]["thread"]) + "</span>\n" + "</div>\n" + "<div class='text'>" + t + "</div>\n" + "<div class='footer'>\n" + "<span class='date'>" + _thread[i]["date"] + "</span>\n" + "<span class='comments'>" + "<a href='" + webact + "?comments=" + i + "'>" + sizeof(_thread[i]["comments"]) + " comments</a>" + "</span>\n" + "</div>\n" + "</div>\n"; } + return "<div class='threads'>" + ht + "</div>"; +} + +htComments(data) { + mapping item; + string ht = ""; + + write("<b>" + data["author"] + "</b>: " + data["text"] + "<br><br>\n"); + if (data["comments"]) { + foreach(item : data["comments"]) { + ht += "<b>" + item["nick"] + "</b>: " + item["text"] + "<br>\n"; + } + } else { + ht += "no comments...<br>\n"; + } + return ht; +} + +displayMain(last) { + write(htMain(last)); } displayComments(data) { - mapping item; - - write("Ursprüngliche Nachricht:<br>" - "<b>" + data["author"] + "</b>: " + data["text"] + "<br><br>\n"); - if (data["comments"]) { - foreach(item : data["comments"]) { - write("<b>" + item["nick"] + "</b>: " + item["text"] + "<br>\n"); - } - } else { - write("no comments...<br>\n"); - } + write(htComments(data)); } nntpget(cmd, args) { @@ -539,7 +595,7 @@ default: } #ifndef STYLESHEET -# define STYLESHEET (v("_uniform_style") || "http://www.psyc.eu/blog.css") +# define STYLESHEET (v("_uniform_style") || "/static/examine.css") #endif // wir können zwei strategien fahren.. die technisch einfachere ist es @@ -553,28 +609,10 @@ default: // displayHeader() { w("_HTML_head_threads", - "<link rel='stylesheet' type='text/css' href='"+ - STYLESHEET +"'>\n"+ - "<style type='text/css'>\n" - "<!--\n" - "body { font-family: lucida,verdana,geneva; }\n" - "td { font-family: lucida,verdana,geneva; }\n" - "table.newsTable {border: 1px solid #6f6; padding:4px; margin:6px;}\n" - "table td.newsAuthor {color:#fff; font-weight:bold;" - " border: 1px solid #6f6;" - " background-color:#041;}\n" - "table td.newsTime {color:#ddd;border: 1px solid #6f6;" - " background-color:#030;}\n" - "table td.newsSubject {color:#ddd; border: 1px solid #6f6;" - " background-color:#030;}\n" - "table td.newsLink {background-color:#030;}\n" - "table td.newsText {}\n" - "//-->\n" - "</style>\n" - "<body bgcolor='#002200' text='#33ff33' link='#ffdf00'" - "vlink='#ffaf00' alink='#00ff00'>\n\n"); + "<html><head><link rel='stylesheet' type='text/css' href='"+ + STYLESHEET +"'></head>\n"+ + "<body class='threads'>\n\n"); } displayFooter() { - w("_HTML_tail_threads", "</body>"); + w("_HTML_tail_threads", "</body></html>"); } - diff --git a/world/net/place/userthreads.c b/world/net/place/userthreads.c new file mode 100644 index 0000000..b9a1c70 --- /dev/null +++ b/world/net/place/userthreads.c @@ -0,0 +1,15 @@ +#include <net.h> +#include <person.h> +#include <status.h> + +inherit NET_PATH "place/threads"; + +load(name, keep) { + P3((">> userthreads:load(%O, %O)\n", name, keep)) + string nick; + + if (sscanf(name, "~%s#updates", nick)) + vSet("owners", ([ nick: 0 ])); + + return ::load(name, keep); +} diff --git a/world/net/psyc/library.i b/world/net/psyc/library.i index 69016ef..52468ef 100644 --- a/world/net/psyc/library.i +++ b/world/net/psyc/library.i @@ -117,6 +117,7 @@ object psyc_object(string uniform) { #ifndef FORK object find_psyc_object(array(mixed) u) { + P3((">> find_psyc_object(%O)\n", u)) string t, r, svc, user; object o; @@ -131,7 +132,15 @@ object find_psyc_object(array(mixed) u) { #endif if (strlen(r)) switch(r[0]) { case '^': + break; case '~': + if (u[UChannel]) { + t = lower_case(r + "#" + u[UChannel]); + r = PLACE_PATH + t; + if (o = find_object(r)) break; + unless (t = legal_name(t)) break; + catch(o = r -> load(t)); + } break; case '$': // target wird auf serv/args gesetzt @@ -174,6 +183,7 @@ object find_psyc_object(array(mixed) u) { o = summon_person(user); //, PSYC_PATH "user"); P2(("%O summoning %O: %O\n", ME, user, o)) } + P3((">>> found psyc object: %O\n", o)) return o; } diff --git a/world/net/user.c b/world/net/user.c index d9d2e0a..125137b 100644 --- a/world/net/user.c +++ b/world/net/user.c @@ -283,6 +283,10 @@ htDescription(anonymous, query, headers, qs, variant, vars) { // <input type=hidden name=token value=\""+v("token")+"\">\n\ // <input type=hidden name=lang value=\""+v("language")+"\">\n\ // + + object u = find_place("~" + nick + "#updates"); + string updates = objectp(u) ? u->htMain(10) : ""; + return psyctext(page, vars + ([ "_FORM_start" : "\ <form class=\"Pef\" name=\"Pef\" action=\"\">\n\ @@ -292,7 +296,8 @@ htDescription(anonymous, query, headers, qs, variant, vars) { "_nick_me" : MYNICK, "_FORM_end" : "</form>\n", ]) - ); + ) + updates; +; } #endif diff --git a/world/static/examine.css b/world/static/examine.css index dc85af2..75d5667 100644 --- a/world/static/examine.css +++ b/world/static/examine.css @@ -1,7 +1,8 @@ +body.threads, .Pe { font-family: verdana,helvetica; - font-weight: bold; - font-size: 9; + /*font-weight: bold;*/ + font-size: 12px; padding: 44; background: #333; color: white; @@ -30,8 +31,12 @@ padding: 4; margin-bottom: 23px; } + .ldp { font-size: 9; +} +.entry, +.ldp { font-weight: normal; border: 3px dashed #333; background: #933; @@ -39,6 +44,7 @@ margin: 44; width: 562; } +.entry .title, .ldpc { background: #f33; color: black; @@ -67,3 +73,32 @@ width: 400; } +.entry { + margin: 22px 44px; +} + +.entry .text { + background: black; + padding: 5px; +} + +.entry .title { + display: none; +} +.entry .title .author {} +.entry .title .subject {} + +.entry .footer a, +.entry .footer a:visited { + color: white; + background: transparent; +} + +.entry .footer a:hover, +.entry .footer a:visited:hover { + text-decoration: underline; +} + +.entry .footer .comments { + float: right; +}