Warning: session_start(): open(/var/lib/php/sessions/sess_hr90o3t5esdkfa76kguk7ktrkl, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
!7
!include #1015743 // concept AIList
static ConceptFieldIndexCI nameIndex;
static volatile long totalChars = -1;
concept Session {
S cookie;
S mech_filter;
}
p {
dbSaveEvery(60);
dbIndexing(AIList, 'name);
nameIndex = indexConceptFieldCI(AIList, 'name);
thread { mech_guessLanguage(""); } // preload
calcOnConceptChanges(10000, f calcTotalSize, true);
updateListNamesList();
}
html {
temp countDispatch('html);
O response;
new Matches m;
S cookie = cookieFromUser();
S filter = params.get('mech_filter);
print("cookie=" + cookie + ", filter=" + filter);
Session session = empty(cookie) ? null : uniq(Session, +cookie);
if (session != null && filter != null) {
cset(session, mech_filter := filter);
ret hrefresh(rawSelfLink());
}
bool authed = webAuthed(params);
if (eq(uri, "/list-count")) ret serveText(countConcepts(AIList));
if ((response = serveListText(uri, params, authed)) != null) ret response;
// Append API
if (swic(uri, "/bot-list-append/", m)) {
params.put(name := urldecode(m.rest());
uri = "/bot-list-append";
}
if (eq(uri, "/bot-list-append")) {
S mode = params.get('mode);
lock dbLock();
S name = params.get("name"), text = params.get("text");
if (!authed)
name = "Unauthorized Appends | " + name;
AIList l = getList(name);
//if (!authed) cset(l, status := "PUBLIC READ");
if (eq(mode, "uniqCI")) {
text = lines(listMinusSet(tlft(text), asCISet(tlft(l.text))));
if (empty(text)) ret "No change";
}
if (eq(mode, "uniq")) {
text = lines(listMinusSet(tlft(text), tlft(l.text)));
if (empty(text)) ret "No change";
}
listAppendWithLog(l, text);
ret "Changed";
}
if (!authed && eq(uri, "/list-names"))
ret serveText(jsonEncode(sortedIC(collect(publicReadLists(), 'name))));
if (!authed && eq(uri, "/list-md5s"))
ret serveText(jsonEncode(map(publicReadLists(), func(AIList l) -> LS { ll(l.name, md5OfList(l)) })));
if (!authed && eq(uri, "/list-md5s-and-statuses"))
ret serveText(jsonEncode(map(publicReadLists(), func(AIList l) -> LS { ll(l.name, md5OfList(l), l.status) })));
if (eq(uri, "/authed")) ret yesno(authed);
// Show list (unauthed)
if (!authed && startsWith(uri, "/list/", m)) {
AIList l = getList_noCreate(urldecode(m.get(0)));
if (l == null || !l.isPublicRead()) ret serve404("List not found");
ret htitle_h2(htmlencode(l.name)) + hpre_htmlencode(l.text);
}
bool alphabetical = eq(uri, "/alphabetical");
if (!authed && (eq(uri, "/") || alphabetical)) {
L list = alphabetical
? sortedByFieldIC('name, publicReadLists())
: sortedByFieldDesc('modified, publicReadLists());
ret h3_title("Bot Data")
+ hBoolSelector(alphabetical, "/", "By date", "/alphabetical", "Alphabetical")
+ listLists(list);
}
if (!authed) ret redirectToWebAuth();
// AUTHED FROM HERE ON
try object html_serveDispatches(uri);
if (swic(uri, "/list-id/", m)) {
AIList l = getList_noCreate(urldecode(m.rest()));
ret l == null ? "List not found" : str(l.id);
}
if (eq(uri, "/fix-unauth")) {
for (AIList l) if (swic(l.name, "Unauthorized Appends |"))
cset(l, status := "");
ret "OK";
}
// API
if (eq(uri, "/download"))
ret subBot_serveFileWithName("mechlists-" + ymd_minus_hms() + ".gz", conceptsFile()); // TODO: concurrent writes?
if (eq(uri, "/list-names"))
ret serveText(jsonEncode(sortedIC(collect(list(AIList), 'name))));
if (eq(uri, "/list-md5s"))
ret serveText(jsonEncode(map(list(AIList), func(AIList l) -> LS { ll(l.name, md5OfList(l)) })));
if (eq(uri, "/list-md5s-and-statuses"))
ret serveText(jsonEncode(map(list(AIList), func(AIList l) -> LS { ll(l.name, md5OfList(l), l.status) })));
if (eq(uri, "/bot-edited-lists"))
ret serveText(jsonEncode(sortedIC(collect(conceptsWhere(AIList, botFlag := true), 'name))));
// Create API
if (eq(uri, "/bot-list-create")) {
lock dbLock();
S name = params.get("name");
getList(name);
ret "OK";
}
// Edit API
if (eq(uri, "/bot-list-edit")) {
lock dbLock();
S name = params.get("name"), text = params.get("text");
AIList l = getList(name);
if (eq(l.text, text)) ret "No change";
listReplaceWithLog(l, text);
ret "Changed";
}
// Show list
if (startsWith(uri, "/list/", m)) {
S name = urldecode(m.get(0));
AIList l = eq("1", params.get("create")) ? getList(name) : getList_noCreate(name);
if (l == null) ret "List not found";
// Rename list
S renameTo = params.get('renameTo);
if (nempty(renameTo)) {
if (neqic(l.name, renameTo) && getList_noCreate(renameTo) != null)
ret "Can't rename - list " + quote(renameTo) + " already exists";
S oldName = l.name;
cset(l, name := renameTo);
updateListNamesList();
ret nav() + "List " + quote(oldName) + " renamed to " + quote(renameTo);
}
// Update text
S text = params.get("human");
S status = params.get("status");
if (eq(params.get("delete"), "1")) status = "delete me";
if (text != null) {
lock dbLock();
//printStruct(ll(+text, +status));
if (neqAny(text, l.text, status, unnull(l.status))) {
logStructure("versions.log", litmap(id := l.id, +text, +status, date := now()));
if (eqic(status, "Delete me")) {
deleteConcept(l);
updateListNamesList();
ret nav() + "List deleted";
}
if (status != null) cset(l, +status);
cset(l, +text, textMD5 := md5(text), botFlag := false, modified := now());
listTextChanged(l);
//printStruct("l.text=" + sfu(text));
}
ret hrefresh(0, rawLink(uri) + "?random=" + randomID());
}
ret nav() + htitle_h2(htmlencode(l.name))
+ hpostform(
"Status: " + htextinput('status, value := l.status)
+ h3("Contents (" + (l.botFlag ? "bot" : "human") + "-edited)")
+ p(hsubmit("Save"))
+ htextarea(l.text, name := 'human, cols := 80, rows := 30)
+ p(hbutton("Delete list", name := "delete", type := "submit", value := 1, onClick := "return confirm('Really delete?')"))
)
+ hpostform(
"New name: " + htextinput('renameTo, value := l.name)
+ " "
+ hsubmit("Rename list")
);
}
// New list
S newList = trim(params.get('newList));
if (nempty(newList)) {
AIList l = getList(newList);
ret hrefresh(rawLink("/list/" + urlencode(l.name)));
}
// Search results
S q = params.get('q);
if (nempty(q))
ret hmobilefix()
+ htitle_h3("mech.tinybrain.de: Search results for " + htmlencode(singleQuote(q)))
+ listLists(scoredSearch_AIList(q, list(AIList)));
// Overview
L list = alphabetical
? sortedByFieldIC('name, list(AIList))
: sortedByFieldDesc('modified, list(AIList));
fS prefix = session == null ? null : session.mech_filter;
if (nempty(prefix))
list = [AIList l : list | swic(l.name, prefix)];
ret hmobilefix()
+ hcomment("cookie=" + cookie)
+ htitle_h3("mech.tinybrain.de [" + n2(l(list), "list")
+ (totalChars >= 0 ? ", " + toK(totalChars) + "K chars" : "")
+ "]")
+ htableRaw_singleRow(ll(
hform("Search: " + htextinput('q, style := "width: 200px") + " " + hsubmit("Search")),
hpostform("| New list: " + htextinput('newList) + " " + hsubmit("Create list"))))
+ hBoolSelector(alphabetical, "/", "By date", "/alphabetical", "Alphabetical")
+ listLists(list)
+ hpostform("Only show lists starting with: "
+ htextinput('mech_filter, value := prefix)
+ " " + hsubmit("OK"));
}
sS nav() {
ret hmobilefix() + p(ahref(rawLink(), "<< back"));
}
static AIList getList_noCreate(S name) {
ret getList(name, false);
}
static AIList getList(S name) {
ret getList(name, true);
}
static AIList getList(S name, bool create) {
lock dbLock();
AIList l = findConcept(AIList, +name);
if (l == null) l = nameIndex.get(name);
if (l == null) if (!create) null; else {
l = cnew(AIList, +name);
logStructure("versions.log", litmap(id := l.id, +name, date := now(), mode := "List created"));
updateListNamesList();
}
if (l.modified == 0) cset(l, modified := now());
ret l;
}
sS listLists(L list) {
ret ul(map(list, func(AIList l) -> S {
int n = countLines(l.text);
new L status;
bool german = eq('german, mech_guessLanguage_quick(l.name));
//if (l.mighty) status.add(german ? "GENUTZT" : "USED");
addIfNempty(status, htmlencode(l.status));
if (n != 0) status.add(n2(n, german ? "Zeile" : "line", german ? "Zeilen": "lines"));
ret ahref(rawLink("/list/" + urlencode(l.name)),
htmlencode(l.name)) + appendBracketed(joinWithComma(status));
}));
}
static L scoredSearch_AIList(S query, Iterable data) {
new Map scores;
L prepared = scoredSearch_prepare(query);
for (AIList l : data)
putUnlessZero(scores, l,
3*scoredSearch_score(l.name, prepared)
+ 2*scoredSearch_score(l.status, prepared)
+ scoredSearch_score(l.text, prepared));
ret keysSortedByValuesDesc(scores);
}
svoid calcTotalSize {
long total = 0;
for (AIList l)
total += l(l.text);
totalChars = total;
}
sS export_getListText(S listName) {
AIList l = getList_noCreate(listName);
ret l == null ? "" : l.text;
}
sS export_setListText(S listName, S text) {
AIList l = getList(listName);
if (eq(l.text, text)) ret "No change";
logStructure("versions.log", litmap(id := l.id, +text, date := now(), mode := "bot edit"));
cset(l, +text, textMD5 := md5(text), botFlag := true, modified := now());
listTextChanged(l);
ret "Changed";
}
sO serveListText(S uri, SS params, bool authed) {
new Matches m;
if (startsWith(uri, "/list-text/", m)) {
bool opt = eq("1", params.get("opt"));
bool withStatus = eq("1", params.get("withStatus"));
bool create = authed && eq("1", params.get("create"));
S md5 = params.get("md5");
int md5Len = parseIntOpt(params.get('l));
S name = urldecode(m.get(0));
lock dbLock();
AIList l = create ? getList(name) : getList_noCreate(name);
if (!authed && l != null && !l.isPublicRead())
l = null;
//ret subBot_serve404("Not logged in");
cset(l, mighty := true);
if (l == null)
if (opt)
ret serveText(jsonEncode(litmap("Text" := "")));
else
ret serveText(jsonEncode(litmap("Error", ll("List not found", name))));
else {
Map map = litmap("Name" := l.name, "Status" := withStatus ? l.status : null);
if (md5 != null) {
S actualMD5 = md5OfList(l);
print("md5: " + md5 + " / " + actualMD5);
if (eq(actualMD5, md5))
ret serveText(jsonEncode(mapPlus(map, "Same" := true)));
else if (md5Len != 0 && eq(md5(takeFirst(l.text, md5Len)), md5))
ret serveText(jsonEncode(mapPlus(map, "Appended" := true, "Text" := substring(l.text, md5Len))));
}
ret serveText(jsonEncode(mapPlus(map, "Text" := l.text)));
}
}
null;
}
sS md5OfList(AIList l) {
lock dbLock();
if (l.textMD5 == null) l.textMD5 = md5(l.text);
ret l.textMD5;
}
static L publicReadLists() {
ret [AIList l : list(AIList) | l.isPublicRead()];
}
svoid updateListNamesList {
lock dbLock();
AIList l = getList("All public-read mech lists");
TreeSet actual = new TreeSet(collect(publicReadLists(), 'name));
Set listed = asHashSet(lines(l.text));
if (nempty(setMinusSet(listed, actual)))
listReplaceWithLog(l, lines(actual));
else
listAppendWithLog(l, lines(setMinusSet(actual, listed)));
}
svoid listAppendWithLog(AIList l, S text) {
if (emptyAfterTrim(text)) ret;
logStructure("versions.log", litmap(id := l.id, +text, date := now(), mode := "bot append"));
cset(l, text := appendNewLineIfNempty(rtrim(l.text)) + text, botFlag := true, modified := now(), textMD5 := null);
listTextChanged(l);
}
svoid listReplaceWithLog(AIList l, S text) {
if (eq(text, l.text)) ret;
logStructure("versions.log", litmap(id := l.id, +text, date := now(), mode := "bot edit"));
cset(l, +text, textMD5 := md5(text), botFlag := true, modified := now());
listTextChanged(l);
}
static new ThreadLocal listTextChanged_noRecurse;
svoid listTextChanged(AIList l) {
if (!isTrue(listTextChanged_noRecurse!) && cic(l.status, "auto global ids")) pcall {
L entries = splitAtEmptyLines(l.text);
S text = joinWithEmptyLines(map nlLogic_addGlobalID(entries));
temp tempSetThreadLocal(listTextChanged_noRecurse, true);
listReplaceWithLog(l, text);
}
}
sS mechList_opt_raw_fresh(S name) {
ret export_getListText(name);
}