Merge commit 'tg/master' - threads heavily reworked

This commit is contained in:
psyc://psyced.org/~lynX 2010-03-01 19:14:15 +01:00
commit d52ad5084c
12 changed files with 491 additions and 287 deletions

View File

@ -435,6 +435,12 @@ object compile_object(string file) {
#endif
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;
}
if (sscanf(file, "%s#%s.c", path, name) && name != "") {
unless (name = SIMUL_EFUN_FILE->legal_name(name))
return (object)0;
@ -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");

View File

@ -215,3 +215,11 @@ varargs string make_query_string(mapping params, int sort) {
}
return q;
}
checkToken(mapping query) {
string nick;
object user;
if (nick = query["user"]) user = find_person(nick);
if (user && user->validToken(query["token"])) return user;
return 0;
}

View File

@ -15,7 +15,7 @@ htget(prot, query, headers, qs) {
t = "_error_invalid_authentication_token";
} else {
PT(("replacing cookie %O\n", headers["cookie"]))
htok3(prot, 0, "Set-Cookie: psyced=\""+ qs +"\";\n");
htok3(prot, 0, "Set-Cookie: psyced=\""+ qs +"\"; path=/;\n");
#if 1
// login was supposed to something more than just /surf
// but until this is the case, why lose time?

View File

@ -9,8 +9,9 @@
#include "header.i"
volatile string url, file, qs, version;
volatile string url, file, qs, version, method, body = "";
volatile mapping headers;
volatile int length;
// we're using #'closures to point to the functions we're giving the
// next_input_to(). as i don't want to restructure the whole file, i need
@ -19,6 +20,7 @@ volatile mapping headers;
// quite stupid indeed, as they don't got any modifiers or whatever :)
parse_url(input);
parse_header(input);
parse_body(input);
devNull();
qScheme() { return "html"; }
@ -67,14 +69,17 @@ parse_wait(null) { // waiting to send my error message here
parse_url(input) {
P3(("=== SmallHTTP got: %O\n", input))
unless (sscanf(input, "GET%t%s%tHTTP/%s", url, version)) {
if (sscanf(input, "CONNECT%t%~s")) {
unless (sscanf(input, "%s%t%s%tHTTP/%s", method, url, version)) quit();
switch (method) {
case "CONNECT":
next_input_to(#'parse_wait);
return;
} else {
case "GET":
case "POST":
break;
default:
quit();
return;
}
}
version = "HTTP/" + version;
@ -95,11 +100,25 @@ parse_header(input) {
next_input_to(#'parse_header);
} else {
process();
next_input_to(#'devNull);
if (method == "POST" && (length = to_int(headers["content-length"])) &&
headers["content-type"] == "application/x-www-form-urlencoded") {
input_to(#'parse_body, INPUT_IGNORE_BANG | INPUT_CHARMODE | INPUT_NO_TELNET);
} else {
process();
next_input_to(#'devNull);
}
}
}
parse_body(input) {
//P4(("parse_body(%O)\n", input))
body += input;
if (strlen(body) == length)
process();
else
input_to(#'parse_body, INPUT_IGNORE_BANG | INPUT_CHARMODE | INPUT_NO_TELNET);
}
process() {
string t, ext;
mapping query = ([]);
@ -139,6 +158,9 @@ process() {
} else {
file = url;
}
if (method == "POST" && headers["content-type"] == "application/x-www-form-urlencoded") {
query = url_parse_query(query, body);
}
P4(("parsed query: %O\n", query))
switch (file) {
case "/favicon.ico":

View File

@ -51,12 +51,19 @@ private volatile mapping _sigs = ([
"_request_nickname": ({ "_request_nick_local", 0, "_nick_local", "_INTERNAL_stuss" }),
// the real thing, maybe? method inheritance could even lead to here
// for all of the _request_set_something methods. good? bad?
"_request_place": ({ "_request_set", 0, "_key", "_value" }),
"_request_set": ({ "_request_set", 0, "_key", "_value" }),
// when called by _request_set(), value might be in _value
"_request_set_masquerade": ({ "_request_masquerade", 0, "_flag_masquerade" }),
"_request_set_owners": ({ "_request_owners", 0, "_list_owners" }), // _tab
"_request_set_public": ({ "_request_public", 0, "_flag_public" }),
"_request_set_style": ({ "_request_set_style", 0, "_uniform_style" }),
"_request_set_topic": ({ "_request_set_topic", 0, "_value" }),
"_request_topic": ({ "_request_set_topic", 0, "_value" }),
"_request_topi": ({ "_request_set_topic", 0, "_value" }),
"_request_top": ({ "_request_set_topic", 0, "_value" }),
"_request_to": ({ "_request_set_topic", 0, "_value" }),
"_request_t": ({ "_request_set_topic", 0, "_value" }),
// "INTERNAL" METHODS
// all of the following "fake" _request methods are just the psyced
// way to handle command name variations and shortcuts. never use this
@ -73,35 +80,47 @@ private volatile mapping _sigs = ([
"_request_ni": ({ "_request_nick_local", 0, "_nick_local", "_INTERNAL_stuss" }),
"_request_public": ({ "_request_public", 0, "_flag_public" }),
"_request_pub": ({ "_request_public", 0, "_flag_public" }),
// threads
"_request_entries": ({ "_request_entries", 0, "_num" }),
"_request_ents": ({ "_request_entries", 0, "_num" }),
"_request_entry": ({ "_request_entry", 0, "_id" }),
"_request_ent": ({ "_request_entry", 0, "_id" }),
"_request_comment": ({ "_request_comment", 0, "_id", "_text" }),
"_request_com": ({ "_request_comment", 0, "_id", "_text" }),
"_request_title": ({ "_request_title", 0, "_id", "_title" }),
"_request_addentry": ({ "_request_addentry", 0, "_text" }),
"_request_addent": ({ "_request_addentry", 0, "_text" }),
"_request_submit": ({ "_request_addentry", 0, "_text" }),
"_request_blog": ({ "_request_addentry", 0, "_text" }),
"_request_delentry": ({ "_request_delentry", 0, "_id" }),
"_request_delent": ({ "_request_delentry", 0, "_id" }),
"_request_unsubmit": ({ "_request_delentry", 0, "_id" }),
"_request_unblog": ({ "_request_delentry", 0, "_id" }),
"_request_entry_reply": ({ "_request_entry_reply", 0, "_parent", "_text" }),
"_request_comment": ({ "_request_entry_reply", 0, "_parent", "_text" }),
"_request_com": ({ "_request_entry_reply", 0, "_parent", "_text" }),
"_request_entry_add": ({ "_request_entry_add", 0, "_text" }),
"_request_addentry": ({ "_request_entry_add", 0, "_text" }),
"_request_addent": ({ "_request_entry_add", 0, "_text" }),
"_request_submit": ({ "_request_entry_add", 0, "_text" }),
"_request_blog": ({ "_request_entry_add", 0, "_text" }),
"_request_entry_del": ({ "_request_entry_del", 0, "_id" }),
"_request_delentry": ({ "_request_entry_del", 0, "_id" }),
"_request_delent": ({ "_request_entry_del", 0, "_id" }),
"_request_unsubmit": ({ "_request_entry_del", 0, "_id" }),
"_request_unblog": ({ "_request_entry_del", 0, "_id" }),
"_request_entry_edit": ({ "_request_entry_edit", 0, "_id", "_text" }),
"_request_editentry": ({ "_request_entry_edit", 0, "_id", "_text" }),
"_request_edentry": ({ "_request_entry_edit", 0, "_id", "_text" }),
"_request_edent": ({ "_request_entry_edit", 0, "_id", "_text" }),
"_request_set_addact": ({ "_request_set_addaction", 0, "_value" }),
"_request_set_addaction":({ "_request_set_addaction", 0, "_value" }),
"_request_set_editact": ({ "_request_set_editaction", 0, "_value" }),
"_request_set_editaction":({ "_request_set_editaction", 0, "_value" }),
"_request_set_showform":({ "_request_set_showform", 0, "_value" }),
"_request_set_showcomments":({ "_request_set_showcomments", 0, "_value" }),
#ifdef _flag_enable_module_microblogging
"_request_add": ({ "_request_add", 0, "_person" }),
"_request_remove": ({ "_request_remove", 0, "_person" }),
"_request_priv": ({ "_request_privacy", 0, "_privacy" }),
"_request_privacy": ({ "_request_privacy", 0, "_privacy" }),
#ifdef TWITTER
"_request_tw": ({ "_request_twitter", 0, "_switch" }),
"_request_twitter": ({ "_request_twitter", 0, "_switch" }),
#endif
#ifdef IDENTICA
"_request_ica": ({ "_request_identica", 0, "_switch" }),
"_request_identica": ({ "_request_identica", 0, "_switch" }),
#endif
"_request_set_priv": ({ "_request_set_privacy", 0, "_value" }),
"_request_set_privacy": ({ "_request_set_privacy", 0, "_value" }),
# ifdef TWITTER
"_request_set_tw": ({ "_request_set_twitter", 0, "_value" }),
"_request_set_twitter": ({ "_request_set_twitter", 0, "_value" }),
# endif
# ifdef IDENTICA
"_request_set_ica": ({ "_request_set_identica", 0, "_value" }),
"_request_set_identica":({ "_request_set_identica", 0, "_value" }),
# endif
#endif
#ifdef EXPERIMENTAL
// stuff to play around with

View File

@ -179,3 +179,13 @@ string x(string str, int n) {
for (i = 0; i < n; i++) res += str;
return res;
}
int is_true(string v) {
if (strlen(v) && regmatch(v, "^(?:on|yes|true|enabled|1)$")) return 1;
return 0;
}
int is_false(string v) {
if (strlen(v) && regmatch(v, "^(?:off|no|false|disabled|0)$")) return 1;
return 0;
}

View File

@ -216,18 +216,6 @@ int qSaveImmediately() {
#endif
}
#ifdef _limit_amount_history_persistent
int qHistoryPersistentLimit() {
return _limit_amount_history_persistent;
}
#endif
#ifdef _limit_amount_history_export
int qHistoryExportLimit() {
return _limit_amount_history_export;
}
#endif
// to be overloaded by place.gen
qNewsfeed() { return 0; }
// _request_list_feature uses this in *all* place objects, dont ifdef
@ -237,7 +225,9 @@ mayLog(mc) { return abbrev("_message", mc); }
qHistory() { return v("log"); }
qHistoryGlimpse() { return DEFAULT_GLIMPSE; }
int qHistoryGlimpse() { return DEFAULT_GLIMPSE; }
int qHistoryPersistentLimit() { return _limit_amount_history_persistent; }
int qHistoryExportLimit() { return _limit_amount_history_export; }
histClear(a, b, source, vars) {
int l = logSize();
@ -466,6 +456,19 @@ showTopic(rcpt, verbose, mc) {
]) );
return 1;
}
# ifdef SIGS
_request_set_topic(source, mc, data, vars, b) {
string value = vars["_value"] || vars["_topic"];
if (strlen(value)) {
if (value == "-") value = 0;
sTopic(value, source, vars["_nick"]);
} else {
showTopic(source, 1, OFFICIAL);
}
return 1;
}
# endif
#endif
#if HAS_PORT(HTTP_PORT, HTTP_PATH) || HAS_PORT(HTTPS_PORT, HTTP_PATH)
@ -1844,6 +1847,7 @@ cmd(a, args, b, source, vars) {
return 1;
#endif
#ifdef PLACE_TOPIC_COMMAND
// not used anymore, see _request_set_topic() instead
case "topic":
case "topi":
case "top":
@ -2240,11 +2244,71 @@ _request_set(source, mc, data, vars, b) {
PT(("set(%O, %O, %O, %O, %O) called\n", source, mc, data, vars, b))
if (k = vars["_key"])
// pass on to a subhandler
if (call_signature(source, "_request_set"+ k, data, vars, b))
if (call_signature(source, "_request_set_"+ k, data, vars, b))
return 1;
return 0;
sendmsg(source, "_warning_usage_place", "Usage: /place <variable> [<value>]");
return 1;
}
_request_set_default_text(source, mc, data, vars, b) {
unless (qOwner(SNICKER)) return 0;
string key = vars["_key"];
string value = vars["_value"];
if (strlen(value)) {
vSet(key, value);
save();
}
sendmsg(source, "_info_set_place_" + key, "Setting \"[_key_set]\" is \"[_value]\".", (["_key_set": key, "_value": v(key)]));
return 1;
}
_request_set_default_list(source, mc, data, vars, b, list) {
unless (qOwner(SNICKER)) return 0;
string key = vars["_key"];
string value;
foreach (string item : list) {
if (item == vars["_value"]) {
value = vars["_value"];
break;
}
}
if (strlen(value)) {
vSet(key, value);
save();
} else if (strlen(vars["_value"])) {
sendmsg(source, "_error_invalid_setting_value",
"Sorry, that is not a valid value for the [_key_set] setting. Valid values are: [_values].",
([ "_key_set": key, "_values": implode(list, ", ") ]) );
return 1;
}
sendmsg(source, "_info_set_place_" + key, "Setting \"[_key_set]\" is \"[_value]\".", (["_key_set": key, "_value": v(key)]));
return 1;
}
_request_set_default_bool(source, mc, data, vars, b) {
unless (qOwner(SNICKER)) return 0;
string key = vars["_key"];
string value = vars["_value"];
if (is_true(value)) {
vSet(key, 1);
save();
} else if (is_false(value)) {
vSet(key, 0);
save();
}
sendmsg(source, "_info_set_place_" + key, "Setting \"[_key_set]\" is [_value].", (["_key_set": key, "_value": v(key) ? "on" : "off"]));
return 1;
}
#ifdef PLACE_OWNED
_request_owners(source, mc, data, vars, b) {
mixed t;
@ -2337,16 +2401,16 @@ _request_set_style(source, mc, data, vars, b) {
vSet("_uniform_style", value);
save();
} else if (value) {
sendmsg(source,
sendmsg(source,
"_error_illegal_scheme",
"That is not a valid [_scheme] URL for a file.",
"That is not a valid [_scheme] URL for a file.",
([ "_scheme" : "http" ]));
return 1;
return 1;
}
sendmsg(source, "_status_place_uniform_style",
"Style file [_uniform_style] is in use here.",
"Style file [_uniform_style] is in use here.",
([ "_uniform_style" :
v("_uniform_style") || "-" ]));
v("_uniform_style") || "-" ]));
return 1;
}
return 0;

View File

@ -15,24 +15,22 @@
inherit NET_PATH "place/owned";
qHistoryPersistentLimit() {
int qHistoryPersistentLimit() {
return 0;
}
canPost(snicker) {
return qAide(snicker);
}
canDeleteOwn(snicker) {
return qAide(snicker);
}
canDeleteEverything(snicker) {
return qOwner(snicker);
}
canPost(snicker) { return qAide(snicker); }
canReply(snicker) { return qAide(snicker); }
canEditOwn(snicker) { return qAide(snicker); }
canEditAll(snicker) { return qOwner(snicker); }
canDeleteOwn(snicker) { return qAide(snicker); }
canDeleteAll(snicker) { return qOwner(snicker); }
int mayLog(string mc) {
return abbrev("_notice_thread", mc) || abbrev("_message", mc);
if (abbrev("_notice_thread", mc))
return regmatch(mc, "_edit\\b") ? 0 : 1;
return abbrev("_message", mc);
}
int showWebLog() {
@ -51,6 +49,17 @@ create() {
logSet(0, ({0, 0, 0, 0}));
}
load(name, keep) {
int ret = ::load(name, keep);
unless (v("addaction")) vSet("addaction", "adds");
unless (v("editaction")) vSet("editaction", "edits");
unless (vExist("showform")) vSet("showform", 1);
unless (vExist("showcomments")) vSet("showcomments", 1);
return ret;
}
varargs array(mixed) entries(int limit, int offset, int reverse, int parent, int id) {
P3((">> entries(%O, %O, %O)\n", limit, offset, parent))
array(mixed) entries = ({}), entry, children, child;
@ -90,54 +99,69 @@ varargs array(mixed) entry(int id) {
return entries(0, 0, 0, 0, id);
}
varargs int addEntry(mixed source, string snicker, string text, string title, int parent_id) {
P3((">> addEntry(%O, %O, %O, %O, %O)\n", source, snicker, text, title, parent_id))
int id = logSize();
varargs int addEntry(mixed source, mapping vars, string _data, string _mc) {
P3((">> addEntry(%O, %O, %O, %O)\n", source, vars, _data, _mc))
string mc = "_notice_thread_entry";
string data = "[_nick] [_action]: ";
vars["_id"] = logSize();
vars["_action"] ||= v("addaction");
// this should only be set after a reply
m_delete(vars, "_children");
mapping vars = ([
"_id": id,
"_text": text,
"_nick": snicker,
"_action": "adds", //TODO: add a /set'ting for it, or find a better name
]);
if (parent_id) {
P3((">>> parent_id: %O\n", parent_id))
if (vars["_parent"]) {
array(mixed) parent;
unless (parent = logPick(parent_id)) return 0;
P3((">>> parent: %O\n", parent))
vars["_parent"] = to_int(vars["_parent"]);
unless (parent = logPick(vars["_parent"])) return 0;
PT((">>> parent: %O\n", parent))
unless (parent[LOG_VARS]["_children"]) parent[LOG_VARS]["_children"] = ({ });
parent[LOG_VARS]["_children"] += ({ id });
save();
parent[LOG_VARS]["_children"] += ({ vars["_id"] });
mc += "_reply";
data = member(parent[LOG_VARS], "_title") ?
"[_nick] [_action] in reply to #[_parent] ([_parent_title]): " :
"[_nick] [_action] in reply to #[_parent]: ",
vars += ([ "_parent": parent_id ]);
"[_nick] [_action] in reply to #[_parent]: ";
}
if (title && strlen(title)) {
vars += ([ "_title": title ]);
if (strlen(vars["_title"])) {
data += "[_title]\n[_text]";
} else {
data += "[_text]";
}
data += " (#[_id] in [_nick_place])";
if (_mc) mc += _mc;
if (_data) data = _data;
castmsg(source, mc, data, vars);
return 1;
}
int editEntry(mixed source, mapping vars, string data) {
P3((">> editEntry(%O, %O, %O)\n", source, vars, data))
array(mixed) entry;
vars["_id"] = to_int(vars["_id"]);
unless (entry = logPick(vars["_id"])) return 0;
string unick;
unless (canEditAll(SNICKER))
unless (canEditOwn(SNICKER) && lower_case(psyc_name(source)) == lower_case(entry[LOG_SOURCE][LOG_SOURCE_UNI]))
return 0;
if (strlen(data)) entry[LOG_DATA] = data;
foreach (string key : vars)
if (key != "_children") entry[LOG_VARS][key] = vars[key];
save();
castmsg(source, entry[LOG_MC] + "_edit", entry[LOG_DATA], vars + ([ "_action": v("editaction") ]));
return 1;
}
int delEntry(int id, mixed source, mapping vars) {
array(mixed) entry;
unless (entry = logPick(id)) return 0;
string unick;
unless (canDeleteEverything(SNICKER))
unless (canDeleteAll(SNICKER))
unless (canDeleteOwn(SNICKER) && lower_case(psyc_name(source)) == lower_case(entry[LOG_SOURCE][LOG_SOURCE_UNI]))
return 0;
@ -155,8 +179,8 @@ sendEntries(mixed source, array(mixed) entries, int level) {
PT(("entry: %O\n", entry))
vars = entry[LOG_VARS];
sendmsg(source, regreplace(entry[LOG_MC], "^_notice", "_list", 1),
"[_indent][_nick]: " + (vars["_title"] ? "[_title]\n" : "") + "[_text] (#[_id])",
vars + ([ "_level": level, "_indent": x(" ", level) ]));
"[_indent][_nick]: "+ (vars["_title"] ? "[_title]\n" : "") +"[_text] (#[_id])",
vars + ([ "_level": level, "_indent": x(" ", level), "_postfix_time_log": 1 ]));
if (sizeof(entry) >= LOG_CHILDREN + 1) sendEntries(source, entry[LOG_CHILDREN], level + 1);
n++;
}
@ -181,46 +205,58 @@ _request_entry(source, mc, data, vars, b) {
sendmsg(source, "_error_thread_invalid_entry",
"#[_id]: no such entry", (["_id": id]));
}
return 1;
}
_request_addentry(source, mc, data, vars, b) {
_request_entry_add(source, mc, data, vars, b) {
P3((">> _request_addentry(%O, %O, %O, %O, %O)\n", source, mc, data, vars, b))
unless (canPost(SNICKER)) return 0;
unless (vars["_text"] && strlen(vars["_text"])) {
sendmsg(source, "_warning_usage_addentry",
sendmsg(source, "_warning_usage_entry_add",
"Usage: /addentry <text>", ([ ]));
return 1;
}
addEntry(source, SNICKER, vars["_text"], vars["_title"]);
addEntry(source, vars, data);
return 1;
}
_request_comment(source, mc, data, vars, b) {
P3((">> _request_comment(%O, %O, %O, %O, %O)\n", source, mc, data, vars, b))
unless (vars["_id"] && strlen(vars["_id"]) &&
vars["_text"] && strlen(vars["_text"])) {
sendmsg(source, "_warning_usage_reply",
_request_entry_reply(source, mc, data, vars, b) {
P3((">> _request_entry_reply(%O, %O, %O, %O, %O)\n", source, mc, data, vars, b))
unless (canReply(SNICKER)) return 0;
unless (vars["_parent"] && strlen(vars["_text"])) {
sendmsg(source, "_warning_usage_entry_reply",
"Usage: /comment <id> <text>", ([ ]));
return 1;
}
int id = to_int(vars["_id"]);
string snicker = SNICKER;
P3((">>> id: %O, vars: %O\n", id, vars));
unless (addEntry(source, snicker, vars["_text"], vars["_title"], id))
unless (addEntry(source, vars))
sendmsg(source, "_error_thread_invalid_entry",
"#[_id]: no such entry", (["_id": id]));
"#[_id]: no such entry", (["_id": vars["_id"]]));
return 1;
}
_request_delentry(source, mc, data, vars, b) {
P3((">> _request_delentry(%O, %O, %O, %O, %O)\n", source, mc, data, vars, b))
unless (canPost(SNICKER)) return 0;
_request_entry_edit(source, mc, data, vars, b) {
P3((">> _request_title(%O, %O, %O, %O, %O)\n", source, mc, data, vars, b))
unless (canEditOwn(SNICKER)) return 0;
unless (vars["_id"] && strlen(vars["_id"])) {
sendmsg(source, "_warning_usage_delentry",
sendmsg(source, "_warning_usage_entry_edit",
"Usage: /editentry <id> <text>", ([ ]));
return 1;
}
unless (editEntry(source, vars))
sendmsg(source, "_error_thread_invalid_entry",
"#[_id]: no such entry", (["_id": vars["_id"]]));
return 1;
}
_request_entry_del(source, mc, data, vars, b) {
P3((">> _request_entry_del(%O, %O, %O, %O, %O)\n", source, mc, data, vars, b))
unless (canPost(SNICKER)) return 0;
unless (vars["_id"]) {
sendmsg(source, "_warning_usage_entry_del",
"Usage: /delentry <id>", ([ ]));
return 1;
}
@ -236,23 +272,21 @@ _request_delentry(source, mc, data, vars, b) {
return 1;
}
#if 0
_request_title(source, mc, data, vars, b) {
P3((">> _request_title(%O, %O, %O, %O, %O)\n", source, mc, data, vars, b))
unless (vars["_id"] && strlen(vars["_id"])) {
sendmsg(source, "_warning_usage_title",
"Usage: /title <id> <title>", ([ ]));
return 1;
}
int id = to_int(vars["_id"]);
unless (setTitle(id, vars["_title"]))
sendmsg(source, "_error_thread_invalid_entry",
"#[_id]: no such entry", (["_id": id]));
return 1;
_request_set_addaction(source, mc, data, vars, b) {
return _request_set_default_text(source, mc, data, vars, b);
}
_request_set_editaction(source, mc, data, vars, b) {
return _request_set_default_text(source, mc, data, vars, b);
}
_request_set_showcomments(source, mc, data, vars, b) {
return _request_set_default_bool(source, mc, data, vars, b);
}
_request_set_showform(source, mc, data, vars, b) {
return _request_set_default_bool(source, mc, data, vars, b);
}
#endif
msg(source, mc, data, vars){
P3(("thread:msg(%O, %O, %O, %O)", source, mc, data, vars))
@ -269,7 +303,8 @@ msg(source, mc, data, vars){
return ::msg(source, mc, data, vars);
}
varargs string htmlComments(array(mixed) entries, int level) {
varargs array(mixed) htmlComments(array(mixed) entries, int submit, int level, int n) {
array(mixed) ret;
mapping entry, vars;
string ht = "", style;
foreach(entry : entries) {
@ -277,65 +312,107 @@ varargs string htmlComments(array(mixed) entries, int level) {
ht = htquote(vars["_text"]);
ht = replace(ht, "\n", "<br/>\n");
style = level ? "style='padding-left: " + level + "em'" : "";
ht += "<div class='comment' title='" + isotime(ctime(vars["_time_place"]), 1) + "' " + style + "><span class='comment-author'>" + vars["_nick"] + "</span>: <span class='comment-text'>"+ ht +"</span></div>\n";
if (sizeof(entry) >= LOG_CHILDREN + 1) ht += htmlComments(entry[LOG_CHILDREN], level + 1);
style = level ? "style='padding-left: "+ level +"em'" : "";
ht += "<div class='comment' title='"+ isotime(ctime(vars["_time_place"]), 1) +"' "+ style +"><span class='comment-author'>"+ vars["_nick"] +"</span>: <span class='comment-text'>" + (submit ? "<a class='comment-form-toggle' onclick='toggleForm(this.parentNode, "+ vars["_id"] +")'>&raquo;</a>" : "")+ ht +"</span></div>\n";
n++;
if (sizeof(entry) >= LOG_CHILDREN + 1) {
ret = htmlComments(entry[LOG_CHILDREN], submit, level + 1, n);
ht += ret[0];
n = ret[1];
}
}
return ht;
return ({ ht, n });
}
varargs string htmlEntries(array(mixed) entries, int nojs, string chan, string submit, string url_prefix) {
P3((">> threads:htmlentries(%O, %O, %O, %O, %O)\n", entries, nojs, chan, submit, url_prefix))
varargs string htmlEntries(array(mixed) entries, int submit, int show_comments, int nojs, string chan, string submit_target, string url_prefix) {
P3((">> threads:htmlentries(%O, %O, %O, %O, %O, %O)\n", entries, submit, nojs, chan, submit_target, url_prefix))
string text, ht = "";
string id_prefix = chan ? chan + "-" : "";
string id_prefix = chan ? chan +"-" : "";
unless (url_prefix) url_prefix = "";
unless (nojs) ht +=
"<script type='text/javascript'>\n"
"function toggle(e) { if (typeof e == 'string') e = document.getElementById(e); e.className = e.className.match('hidden') ? e.className.replace(/ *hidden/, '') : e.className + ' hidden'; }\n"
"function $(id) { return document.getElementById(id); }"
"function toggle(e) { if (!e) return 0; if (typeof e == 'string') e = $(e); e.className = e.className.match('hidden') ? e.className.replace(/ *hidden/, '') : e.className + ' hidden'; return 1; }\n"
"function toggleForm(e, id) { toggle(e.nextSibling) || e.parentNode.appendChild($('entry-form')) && ($('entry-form').className=''); $('form-parent').value = id }"
"</script>\n";
if (submit) ht +=
"<form id='entry-form' class='hidden' action='"+ url_prefix +"'"+
(0 && submit_target
//FIXME: cmd is executed twice, because after a set-cookie it's parsed again
? "onsubmit=\"cmd('comment '+ $('form-parent').value +' '+ this.previousSibling.value, '"+ submit_target +"')\""
: "method='post'") +
">"
"<input type='hidden' name='request' value='post' />"
"<input type='hidden' id='form-parent' name='_parent' value='' />"
"<textarea name='_text' autocomplete='off'></textarea>"
"<input type='submit' value='Send'>"
"</form>";
mapping entry, vars;
foreach (entry : entries) {
P3((">>> entry: %O\n", entry))
vars = entry[LOG_VARS];
text = replace(htquote(vars["_text"]), "\n", "<br/>\n");
string comments = "";
if (sizeof(entry) >= LOG_CHILDREN + 1) comments = htmlComments(entry[LOG_CHILDREN]);
array(mixed) comments = ({ "", 0 });
if (sizeof(entry) >= LOG_CHILDREN + 1) comments = htmlComments(entry[LOG_CHILDREN], submit);
ht +=
"<div class='entry'>\n"
"<div class='header'>\n"
"<a href=\"" + url_prefix + "?id=" + vars["_id"] + "\">"
"<span class='id'>#" + vars["_id"] + "</span> - \n"
"<span class='author'>" + vars["_nick"] + "</span>\n"
"<a href=\""+ url_prefix +"?id="+ vars["_id"] +"\">"
"<span class='id'>#"+ vars["_id"] +"</span> - \n"
"<span class='author'>"+ vars["_nick"] +"</span>\n"
+ (vars["_title"] && strlen(vars["_title"]) ? " - " : "") +
"<span class='title'>" + htquote(vars["_title"] || "") + "</span>\n"
"<span class='title'>"+ htquote(vars["_title"] || "") +"</span>\n"
"</a>"
"</div>\n"
"<div class='body'>\n"
"<div class='text'>" + text + "</div>\n"
"<div id='comments-" + id_prefix + vars["_id"] + "' class='comments'>" + comments +
(submit && strlen(submit) ?
"<a onclick=\"toggle(this.nextSibling)\">&raquo; reply</a>"
"<div class='comment-submit hidden'>"
"<textarea autocomplete='off'></textarea>"
//FIXME: cmd is executed twice, because after a set-cookie it's parsed again
"<input type='button' value='Send' onclick=\"cmd('comment " + vars["_id"] + " '+ this.previousSibling.value, '" + submit + "')\">"
"</div>" : "") +
"</div>\n"
"<div class='text'>"+ text +"</div>\n"+
(show_comments ?
"<div id='comments-"+ id_prefix + vars["_id"] +"' class='comments'>"+ comments[0] +
(submit ? "<a onclick=\"toggleForm(this, "+ vars["_id"] +")\">&raquo; reply</a>" : "") +
"</div>\n" : "") +
"</div>\n"
"<div class='footer'>\n"
"<span class='date'>" + isotime(ctime(vars["_time_place"]), 1) + "</span>\n"
"<span class='date'>"+ isotime(ctime(vars["_time_place"]), 1) +"</span>\n"
"<span class='comments-link'>"
"<a onclick=\"toggle('comments-" + id_prefix + vars["_id"] + "')\">" + sizeof(vars["_children"]) + " comments</a>"
"<a " +
(show_comments
? "onclick=\"toggle('comments-"+ id_prefix + vars["_id"] +"')\""
: "href='"+ url_prefix +"?id="+ vars["_id"] +"''") +
">"+ comments[1] +" comments</a>"
"</span>\n"
"</div>\n"
"</div>\n";
}
P3((">>> ht: %O\n", ht))
return "<div class='threads'>" + ht + "</div>";
return "<div class='threads'>"+ ht +"</div>";
}
string htmlForm(int link, int title) {
if (link)
return
"<div class='threads'><a class='add-entry' href='?request=form'>Add entry</a></div>\n";
return
"<div class='threads'>"
"<div class='entry entry-form'>\n"
"<div class='header'>Add entry</div>\n"
"<div class='body'>\n"
"<form method='post'>\n"
"<input type='hidden' name='request' value='post' />\n" +
(title ?
"Title:<br><input type='text' name='_title'><br>\n"
"Text:<br/>" : "") +
"<textarea name='_text'></textarea>\n"
"<input type='submit' value='Send' />\n"
"</form>\n"
"</div>\n"
"<div class='footer'>\n"
"</div>\n"
"</div>\n"
"</div>\n";
}
// TODO: fix markup, not displayed correctly (in firefox at least)
@ -361,10 +438,10 @@ string rssEntries(array(mixed) entries) {
rss +=
"\n<item>\n"
"\t<title>"+ (vars["_title"] || "no title") +"</title>\n"
"\t<link>http://" + HTTP_OR_HTTPS_URL + "/" + pathName() + "?id=" + vars["_id"] + "</link>\n"
"\t<link>http://"+ HTTP_OR_HTTPS_URL +"/"+ pathName() + "?id="+ vars["_id"] +"</link>\n"
"\t<description>"+ ht +"</description>\n"
"\t<dc:date>" + isotime(ctime(vars["_time_place"]), 1) + "</dc:date>\n"
"\t<dc:creator>" + vars["_nick"] + "</dc:creator>\n"
"\t<dc:date>"+ isotime(ctime(vars["_time_place"]), 1) +"</dc:date>\n"
"\t<dc:creator>"+ vars["_nick"] +"</dc:creator>\n"
"</item>\n";
}
@ -387,14 +464,14 @@ string jsEntries(array(mixed) entries) {
foreach (entry : entries) {
vars = entry[LOG_VARS];
// should probably be htquoted too
js += "new Entry(" + vars["_id"] + ","
"\"" + vars["_title"] + "\","
"\"" + vars["_nick"] + "\","
+ isotime(ctime(vars["_time_place"]), 1) + ","
"\"" + vars["_text"] + "\"),\n";
js += "new Entry("+ vars["_id"] +","
"\""+ vars["_title"] +"\","
"\""+ vars["_nick"] +"\","
+ isotime(ctime(vars["_time_place"]), 1) +","
"\""+ vars["_text"] +"\"),\n";
}
return js[..<3] + ");";
return js[..<3] +");";
}
varargs string jsonEntries(int limit, int offset) {
@ -413,20 +490,24 @@ varargs void rssExport(int limit, int offset) {
write(rssEntries(entries(limit, offset, 1)));
}
varargs string htMain(int limit, int offset, string chan) {
return htmlEntries(entries(limit, offset, 1), 0, chan);
varargs string htMain(int limit, int offset, int submit, string chan) {
return htmlEntries(entries(limit, offset, 1), submit, v("showcomments"), 0, chan);
}
varargs void displayMain(int limit, int offset) {
write(htMain(limit, offset));
varargs void displayMain(int limit, int offset, int submit) {
write(htMain(limit, offset, submit));
}
string htEntry(int id) {
return htmlEntries(entry(id));
void displayForm(int link, int title) {
write(htmlForm(link, title));
}
void displayEntry(int id) {
write(htEntry(id) || "No such entry.");
string htEntry(int id, int submit) {
return htmlEntries(entry(id), submit, 1);
}
void displayEntry(int id, int submit) {
write(htEntry(id, submit) || "No such entry.");
}
// wir können zwei strategien fahren.. die technisch einfachere ist es
@ -438,10 +519,14 @@ void displayEntry(int id) {
// halten. womöglich kann man auch nachträglich plan A in plan B
// umwandeln..... hmmm -lynX
//
void displayHeader() {
void displayHeader(string class) {
w("_HTML_head_threads",
"<html><head><link rel='stylesheet' type='text/css' href='"+ STYLESHEET +"'></head>\n"+
"<body class='threads'>\n\n");
"<html><head>"
"<link rel='stylesheet' type='text/css' href='"+ STYLESHEET +"'>"
"<title>"+ MYNICK +"</title>"
"</head>\n"+
"<body class='threads "+ class +"'>\n"
"<h1><a href='/"+ pathName() +"'>"+ MYNICK +"</h1>\n");
}
void displayFooter() {
w("_HTML_tail_threads", "</body></html>");
@ -451,9 +536,16 @@ htget(prot, query, headers, qs, data) {
mapping entrymap;
mixed target;
string nick;
object user;
int a;
int limit = to_int(query["limit"]) || DEFAULT_BACKLOG;
int offset = to_int(query["offset"]);
int authed = checkToken(query) ? 1 : 0;
unless (isPublic() || authed) {
write("<h1>404</h1>");
return 1;
}
string webact = PLACE_PATH + MYLOWERNICK;
sTextPath(query["layout"], query["lang"], "html");
@ -461,84 +553,44 @@ htget(prot, query, headers, qs, data) {
if (query["id"]) {
htok(prot);
// kommentare + urspruengliche Nachricht anzeigen
displayHeader();
displayEntry(to_int(query["id"]));
#if 0
// eingabeformular ohne betreff
write("<form action='" + webact + "' method='GET'>\n"
"<input type='hidden' name='request' value='post'>\n"
"PSYC Uni: <input type='text' name='uni'><br>\n"
"<input type='hidden' name='reply' value='" + query["comments"] +"'>\n"
"<textarea name='text' rows='14' cols='80'>Enter your text here</textarea><br>\n"
"<input type='submit' value='submit'>\n"
"</form>\n");
write("<br><hr><br>");
#endif
displayHeader("entry");
displayEntry(to_int(query["id"]), authed);
//logView(a < 24 ? a : 12, "html", 15);
displayFooter();
return 1;
}
// formularbehandlung
if (query["request"] == "post" && query["uni"]) {
if (query["request"] == "post") {
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"] + "<br>\n");
// write("Remote auth doesn't work yet. TODO!!!\n");
// return 1;
// TODO: remote user auth
unless (user = checkToken(query)) {
write("Not authenticated!\n");
return 1;
}
#ifdef OWNED
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"] + "<br>\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");
} else {
write("not authenticated!\n");
}
#ifdef OWNED
} else {
unless (canPost(query["user"])) {
write("You are not owner or aide of this place.\n");
return 1;
}
#endif
return 1;
object vars = ([]);
// add query params beginning with _ as vars
foreach (string key, string value : query)
if (abbrev("_", key)) vars[key] = value;
vars["_nick"] = user->qName();
addEntry(user, vars);
}
// neuen Eintrag verfassen
if (query["request"] == "form") {
htok(prot);
displayHeader();
write("<form action='" + webact + "' method='GET'>\n"
"<input type='hidden' name='request' value='post'>\n"
"PSYC Identity:<br><input type='text' name='uni' size=60><br>\n"
"Thread:<br><input type='text' name='thread' size=60><br>\n"
"Text:<br>\n<textarea name='text' rows='14' cols='64'></textarea><br>\n"
"<input type='submit' value='CREATE MESSAGE'>\n"
"</form>\n");
displayHeader("entry-add");
displayForm(0, 1);
displayFooter();
return 1;
}
//::htget(prot, query, headers, qs, data, 1); // no processing, just info
::htget(prot, query, headers, qs, data, 1); // no processing, just info
string export = query["export"] || query["format"];
if (export == "js") {
@ -558,12 +610,13 @@ htget(prot, query, headers, qs, data) {
} else {
// normaler Export
//P2(("all entries: %O\n", _thread))
htok3(prot, "text/html", "Cache-Control: no-cache\n");
displayHeader();
htok3(prot, "text/html", "Cache-Control: no-cache\n");
displayHeader("entries");
if ((user = checkToken(query)) && canPost(user->qName()))
displayForm(!v("showform"));
// display the blog
displayMain(limit, offset);
displayMain(limit, offset, checkToken(query) ? 1 : 0);
// display the chatlog
if (showWebLog()) logView(a < 24 ? a : 12, "html", 15);
displayFooter();
}

View File

@ -24,17 +24,19 @@ volatile object identica;
load(name, keep) {
P3((">> userthreads:load(%O, %O)\n", name, keep))
int ret = ::load(name, keep);
sscanf(name, "~%s#%s", owner, channel);
vSet("owners", ([ lower_case(owner) ]));
vSet("privacy", "private");
vSet("twitter", 0);
vSet("identica", 0);
unless (v("owners")) vSet("owners", ([ lower_case(owner) ]));
unless (v("privacy")) vSet("privacy", "private");
unless (vExist("twitter")) vSet("twitter", 0);
unless (vExist("identica")) vSet("identica", 0);
vSet("_restrict_invitation", BLAME);
vSet("_filter_conversation", BLAME);
return ::load(name, keep);
return ret;
}
enter(source, mc, data, vars) {
@ -127,68 +129,63 @@ _request_remove(source, mc, data, vars, b) {
// set privacy: private or public
// - private: only friends & invited people can enter (default)
// - public: anyone can enter
_request_privacy(source, mc, data, vars, b) {
P3((">> userthreads:_request_privace(%O, %O, %O, %O, %O)\n", source, mc, data, vars, b))
string p = vars["_privacy"];
if (p == "public" || p == "private") {
vSet("privacy", p);
save();
}
sendmsg(source, "_status_privacy", "Privacy is: [_privacy].", (["_privacy": v("privacy")]));
return 1;
_request_set_privacy(source, mc, data, vars, b) {
return _request_set_default_list(source, mc, data, vars, b, ({"public", "private"}));
}
#ifdef TWITTER
_request_twitter(source, mc, data, vars, b) {
string sw = vars["_switch"];
if (sw == "on" || sw == "enabled" || sw == "1") {
_request_set_twitter(source, mc, data, vars, b) {
unless (qOwner(SNICKER)) return 0;
string value = vars["_value"];
if (is_true(value)) {
unless (twitter) twitter = clone_object(NET_PATH "twitter/client")->load(source);
vSet("twitter", 1);
save();
} else if (sw == "off" || sw == "disabled" || sw == "0") {
} else if (is_false(value)) {
if (twitter) twitter = 0;
vSet("twitter", 0);
save();
}
sendmsg(source, "_status_twitter", "Twitter submission is [_status].", (["_status": v("twitter") ? "enabled" : "disabled"]));
sendmsg(source, "_info_set_place_twitter", "Twitter submission is [_status].", (["_status": v("twitter") ? "enabled" : "disabled"]));
return 1;
}
#endif
#ifdef IDENTICA
_request_identica(source, mc, data, vars, b) {
string sw = vars["_switch"];
if (sw == "on" || sw == "enabled" || sw == "1") {
_request_set_identica(source, mc, data, vars, b) {
unless (qOwner(SNICKER)) return 0;
string value = vars["_value"];
if (is_true(value)) {
unless (identica) identica = clone_object(NET_PATH "identica/client")->load(source);
vSet("identica", 1);
save();
} else if (sw == "off" || sw == "disabled" || sw == "0") {
} else if (is_false(value)) {
if (identica) identica = 0;
vSet("identica", 0);
save();
}
sendmsg(source, "_status_identica", "Identi.ca submission is [_status].", (["_status": v("identica") ? "enabled" : "disabled"]));
sendmsg(source, "_info_set_place_identica", "Identi.ca submission is [_status].", (["_status": v("identica") ? "enabled" : "disabled"]));
return 1;
}
#endif
varargs int addEntry(mixed source, string snicker, string text, string title, int parent_id) {
varargs int addEntry(mixed source, mapping vars, string _data, string _mc) {
int ret;
if (ret = ::addEntry(source, snicker, text, title, parent_id)) {
if (ret = ::addEntry(source, vars, _data, _mc)) {
#ifdef TWITTER
if (v("twitter") && twitter) twitter->status_update(text);
if (v("twitter") && twitter) twitter->status_update(vars["_text"]);
#endif
#ifdef IDENTICA
if (v("identica") && identica) identica->status_update(text);
if (v("identica") && identica) identica->status_update(vars["_text"]);
#endif
}
return ret;
}
htMain(int limit, int offset) {
return ::htMain(limit, offset, channel);
htMain(int limit, int offset, int submit) {
return ::htMain(limit, offset, submit, channel);
}
canPost(snicker) {
@ -207,7 +204,7 @@ qChannel() {
return channel;
}
qHistoryGlimpse() {
int qHistoryGlimpse() {
return HISTORY_GLIMPSE;
}

View File

@ -68,6 +68,10 @@ vInc(k, howmuch) {
}
// vDec(k) { _v[k]--; }
vExist(k) {
return member(_v, k);
}
vEmpty() { return sizeof(_v) == 0; }
vSize() { return sizeof(_v); }

View File

@ -319,7 +319,7 @@ htDescription(anonymous, query, headers, qs, variant, vars) {
+ (first ? "class='selected'" : "") + ">#" + channel + "</a>\n";
contents +=
"<div id='tab-contents-" + channel + "' "
+ (first ? "class='selected'" : "") + ">" + threads->htmlEntries(entries, !first, channel, anonymous ? "" : vars["_identification"] + "#" + channel, vars["_profile_url"] + "/" + channel) + "</div>\n";
+ (first ? "class='selected'" : "") + ">" + threads->htmlEntries(entries, 1, 1, !first, channel, anonymous ? "" : vars["_identification"] + "#" + channel, vars["_profile_url"] + "/" + channel) + "</div>\n";
first = 0;
}
@ -1322,7 +1322,7 @@ w(string mc, string data, mapping vars, mixed source, int showingLog) {
else t = vars["_time_log"] || vars["_time_place"];
// would be nicer to have _time_log in /log rather than showingLog
if (!t && showingLog) t = vars["_time_INTERNAL"];
if (t && intp(t)) di["_prefix"] = time_or_date(t) +" ";
if (t && intp(t)) di["_prefix"] = time_or_date(t);
#if 0
template = T(di["_method"] || mc, 0);
#else
@ -1448,7 +1448,12 @@ w(string mc, string data, mapping vars, mixed source, int showingLog) {
// who is that output for anyway?
output = psyctext(template, vars, data, source);
if (di["_prefix"]) output = di["_prefix"]+output;
if (di["_prefix"]) {
if (vars["_postfix_time_log"])
output += " " + di["_prefix"];
else
output = di["_prefix"] +" "+ output;
}
if (output) {
if (template == "") {

View File

@ -19,7 +19,7 @@ body.threads,
padding: 4;
border: 1px solid #f33;
}
.comment-submit textarea, .comment-submit input,
.entry textarea, .entry input,
.Pe form input,select,textarea {
background: black;
color: #f33;
@ -74,6 +74,7 @@ body.threads,
width: 400;
}
a.add-entry,
.entry {
margin: 22px 44px;
}
@ -102,33 +103,54 @@ body.threads,
margin: 0 1em;
}
.entry .body .comment-submit input,
.entry .body .comment-submit textarea {
.entry .body .comment-form-toggle {
float: right;
}
.entry input,
.entry textarea {
font-size: 12px;
}
.entry .body .comment-submit textarea {
.entry input[type=text],
.entry textarea {
width: 100%;
}
body.entry-add textarea {
height: 40em;
}
.entry form {
margin: 0.2em 0;
padding: 0;
}
h1 a,
.entry a {
cursor: pointer;
text-decoration: none;
}
.entry a:hover {
text-decoration: underline;
}
.entry .header a {
color: black;
}
.entry .header .author {}
.entry .header .title {}
.entry .body a {
color: #f33;
}
h1 a,
a.add-entry,
.entry .footer a,
.entry .footer a:visited {
color: white;
}
.entry a {
cursor: pointer;
text-decoration: none;
}
.entry a:hover {
text-decoration: underline;
}
.entry .footer .comments-link {
float: right;
}