!7 // See #1023660 for the older 99 lines version !include once #1024472 // agi.blue core db // global constants static SS searchTypeToText = litmap( leven := "Leven", literal := "Literal", scored := "Scored"); // global vars static int sourceCodeLines; static RealAGIBlue agiBlue; static Renderer renderer = new Renderer1; set flag AllPublic. sS classesToShare = [[ DynamicObject Concept Concepts Page Entry Slice LoadedSlice IConceptIndex IFieldIndex IConceptCounter ConceptFieldIndexCI ConceptFieldIndexDesc AbstractEntry Signer Session User GoogleUser MinimalUser CookieUser BlobEntry SliceInfo CentralIndexEntry AGIBlue RealAGIBlue TimedCache GlobalID Renderer IterableIterator CloseableIterableIterator ]]; // end of global data p { agiBlue = new RealAGIBlue; } svoid _onLoad_sourceCodeLines { sourceCodeLines = countLines(mySource()); } svoid cleanMeUp { cleanUp(agiBlue); agiBlue = null; } sinterface Renderer { O html(S uri, SS params); } set flag NoNanoHTTPD. html { printWithTime("Starting request"); O o = renderer.html(uri, params); printWithTime("Done with request"); ret o; } sclass RealAGIBlue > AGIBlue { volatile bool started, broken; TimedCache sizeOnDisk = new(60.0, f guessClusteredSizeOfProgramDirWithoutBackups); *() { try { if (bootstrapDataFrom(#1023558)) deleteMyBackups(); thinMyBackups(); fan = slicesLoadedOnDemand(loadedSlices); mainConcepts = fan.get("", null); // index main DB indexConceptFields(Slice, 'globalID, Slice, 'caseID); indexConceptField(CookieUser, 'cookie); idx_slicesByName = new ConceptFieldIndexCI(Slice, 'name); idx_slicesByModification = new ConceptFieldIndexDesc(Slice, '_modified); idx_usersByName = new ConceptFieldIndexCI(GoogleUser, 'googleLastName); idx_usersBySeen = new ConceptFieldIndexDesc(User, 'lastSeen); cset(uniq_returnIfNew Slice(caseID := ""), name := "main slice"); loadedSlices.get("").initialSetup(toGlobalIDObj(mainSliceGlobalID())); loadedSlices.get("").sliceConcept = sliceConceptForCaseID(""); backgroundFan = slicesLoadedOnDemand(backgroundSlices); backgroundFan.lock = fan.lock; // legacy conversions! //deleteConcepts(Slice, globalID := GlobalID('wftlawbagrwprywn)); // Approve this machine's key PKIKeyPair machineKey = agiBot_trustedKeyForMachine(); if (machineKey != null) { print("Approving this machine's key: " + machineKey.publicKey); cset(uniq_sync(Signer, publicKey := machineKey.publicKey), trusted := true, approvedBy := "local"); } rst_index.trigger(); } catch print e { set broken; } finally { set started; } // do stuff after we are officially started // calculate DB size regularly iff there were changes sizeOnDisk.keepValueWhileCalculating = true; doEveryAndNow(60.0, rWatcher(() -> db_mainConceptsChangeCount(), r { sizeOnDisk! })); } // end of main program L entriesOnPage(Page p) { ret p == null ? null : sortedByField count(findBackRefs(p, Entry)); } L pagesSortedByLength(L l) { ret sortedByCalculatedField(l, p -> l(p.q)); } sbool agiBlue_isOriginal() { ret amProgram(#1023558); } S agiBlueURL(S subUri) { ret agiBlueURL() + prependSlash(subUri); } S agiBlueURL() { ret agiBlue_isOriginal() ? "https://agi.blue" : "/" + psI(programID()) + "/raw"; } S agiBlueName() { ret agiBlue_isOriginal() ? "agi.blue" : isProgramID(#1024512) ? "agi.blue productive" : "agi.blue clone " + programID(); } void cleanMeUp { cleanUp(fan); } LoadedSlice loadSlice(Slice slice) { ret loadSlice(slice.caseID, str(slice.globalID)); } LoadedSlice loadSlice(S caseID, S globalID) { caseID = unnull(caseID); lock fan.lock; // move slice from background to foreground Concepts cc = backgroundFan.getIfLoaded(caseID); if (cc != null) { LoadedSlice slice = assertNotNull(backgroundSlices.get(caseID)); backgroundFan.loaded.remove(caseID); fan.loaded.put(caseID, cc); backgroundSlices.remove(caseID); loadedSlices.put(caseID, slice); ret slice; } fan.get(caseID, globalID); ret loadedSlices.get(caseID); } void unloadSlice(S caseID) { fan.unloadCase(caseID); backgroundFan.unloadCase(caseID); } // load background slice LoadedSlice loadBackgroundSlice(Slice slice) { ret loadBackgroundSlice(slice.caseID, str(slice.globalID)); } LoadedSlice loadBackgroundSlice(S caseID, S globalID) { caseID = unnull(caseID); lock fan.lock; // check if in foreground if (fan.getIfLoaded(caseID) != null) ret loadedSlices.get(caseID); backgroundFan.get(caseID, globalID); LoadedSlice slice = assertNotNull(backgroundSlices.get(caseID)); slice.lastAccess = sysNow(); while (l(backgroundSlices) > maxBackgroundSlices) unloadLeastRecentlyAccessedBackgroundSlice(); assertNotNull("slice.sliceConcept", slice.sliceConcept); ret slice; } // TODO: sync unloading slice with accessors void unloadLeastRecentlyAccessedBackgroundSlice() { lock fan.lock; S caseID = lowestByField lastAccess(keys(backgroundSlices)); if (caseID != null) backgroundFan.unloadCase(caseID); } Slice sliceConceptForGlobalID(S globalID) { ret sliceConceptForGlobalID(toGlobalIDObj(globalID)); } Slice sliceConceptForGlobalID(GlobalID globalID) { ret conceptWhere Slice(+globalID); } Slice sliceConceptForCaseID(S caseID) { ret conceptWhere Slice(+caseID); } Slice sliceForName(S name) { ret conceptWhereCI Slice(+name); } class Query extends WorksOnSlice { SS vars = ciMap(); *(LoadedSlice slice) { super(slice); } O process(S query) { try object processLines(agiBlue_parseQueryScript(query)); ret serveJSON("No return statement"); } O processLines(L lines) { for (ALQLLine line : lines) { if (line cast ALQLSlice) { Slice slice = sliceForName(line.slice); if (slice == null) ret serveJSON("Slice not found: " + line.slice); LoadedSlice oldSlice = Query.this.slice; try { setSlice(loadSlice(slice)); try object processLines(line.contents); } finally { setSlice(oldSlice); } } else if (line cast ALQLReturn) ret serveJSON(ll(getOrKeep(vars, line.var))); else if (line cast ALQLReturnTuple) ret serveJSON(map(v -> getOrKeep(vars, v), line.vars)); else if (line cast ALQLTriple) { T3S t = line.triple; t = tripleMap(t, s -> getOrKeep(vars, s)); Cl pages; S var; if (isDollarVar(t.c)) { var = t.c; if (isDollarVar(t.a)) { if (isDollarVar(t.b)) todo(t); Entry e = random(conceptsWhereCI(cc, Entry, key := t.b)); if (e == null) ret serveJSON("No results for " + var); pages = pagesForKeyAndValue(t.b, t.c); vars.put(t.a, e.page->q); vars.put(t.c, e.value); continue; } else if (isDollarVar(t.b)) { Page page = findPageFromQ(t.a); Entry e = random(findBackRefs(page, Entry)); if (e == null) ret serveJSON("No results for " + var); vars.put(t.b, e.key); vars.put(t.c, e.value); continue; } else { S val = getValue(t.a, t.b); if (val == null) ret serveJSON("No results for " + var); pages = ll(pageFromQ(val)); } } else if (isDollarVar(t.b)) { var = t.b; if (isDollarVar(t.c)) todo(t); if (isDollarVar(t.a)) { L entries = conceptsWhereCI(cc, Entry, value := t.c); if (empty(entries)) ret serveJSON("No results for " + var); Entry e = random(entries); vars.put(t.a, e.page->q); vars.put(t.b, e.key); continue; } else { Cl keys = keysForPageAndValue(t.a, t.c); if (empty(keys)) ret serveJSON("No results for " + var); pages = map(f pageFromQ, keys); } } else { var = t.a; if (!isDollarVar(t.a)) todo(t); if (isDollarVar(t.b)) todo(t); if (isDollarVar(t.c)) todo(t); pages = pagesForKeyAndValue(t.b, t.c); } if (empty(pages)) ret serveJSON("No results for " + var); vars.put(var, random(pages).q); } else if (line cast ALQLPage) { if (eq(line.matchMethod, 'eqic)) findPageFromQ(line.page); else if (eq(line.matchMethod, 'flexMatchDollarVarsIC_first)) { new L l; for (Page p : list(cc, Page)) addIfNotNull(l, flexMatchDollarVarsIC_first(line.page, p.q)); if (empty(l)) ret serveJSON("No results for " + curly(line.page)); vars.putAll(random(l)); } else fail("Unknown match method: " + line.matchMethod); } else fail("Can't interpret: " + line); } null; } } /*S globalIDFromCaseID(S caseID) { ret assertGlobalID(takeLast(globalIDLength(), caseID)); }*/ S agiBlue_pageURLWithSlice(Page p) { ret p == null ? null : agiBlueURL() + hquery(slice := _get(sliceForPage(p), 'globalID), q := p.q); } Slice sliceForPage(Page p) { if (p == null) null; SliceInfo info = conceptWhere(p._concepts, SliceInfo); ret info == null ? null : sliceConceptForGlobalID(info.globalID); } GlobalID mainSliceGlobalID() { ret conceptWhere Slice(caseID := "").globalID; } ConceptsLoadedOnDemand slicesLoadedOnDemand(Map loadedSlices) { new ConceptsLoadedOnDemand fan; fan.onCaseLoaded(voidfunc(S caseID, O globalID, Concepts concepts) { LoadedSlice ls = new(caseID, concepts); concepts.miscMap = mapPutOrCreate_multi(concepts.miscMap, agiBlue := RealAGIBlue.this, loadedSlice := ls); ls.sliceConcept = sliceConceptForCaseID(caseID); if (nempty(globalID)) ls.initialSetup(toGlobalIDObj((S) globalID)); loadedSlices.put(caseID, ls); }); fan.onUnloadingCase(voidfunc(S caseID, Concepts concepts) { loadedSlices.remove(caseID); }); ret fan; } // check if slices are loaded both in foreground and background (BAD) S checkDoubleLoads() { LS l = sharedKeys(loadedSlices, backgroundSlices); ret empty(l) ? null : "ERROR: Slice loaded in background and foreground: " + first(l); } // just use size of concepts.structure.gz long estimatedSliceDataSize(Slice slice) { ret estimatedSliceDataSize(slice.caseID); } long estimatedSliceDataSize(S caseID) { ret l(conceptsFile(fan.dbID(caseID))); } // restart magic! void softRestart { // TODO print("Cloning..."); cloningSince = sysNow(); Class c = hotwireDependent(programID()); // New strategy: Share AGIBlue & just grab the new renderer :) copyFields(mc(), c, 'agiBlue, 'mainConcepts, 'creator_class); renderer = (Renderer) get renderer(c); print("Got new renderer."); // TODO: any clean up of this instance? } } // end of RealAGIBlue sclass WorksOnSlice { delegate LoadedSlice to AGIBlue. Concepts cc; LoadedSlice slice; *() {} *(LoadedSlice *slice) { cc = slice.cc; } void setSlice(LoadedSlice slice) { this.slice = slice; cc = slice.cc; } Page findPageFromParams(Map map) { S q = getString q(map); ret empty(q) ? null : findPageFromQ(q); } Page findOrMakePageFromParams(Map map) { ret pageFromQ(getString q(map)); } Page findPageFromQ(S q) { ret conceptWhereCI(cc, Page, +q); } Page pageFromQ(S q) { ret slice.pageFromQ(q); } Set pagesForKeyAndValue(S key, S value) { L entries = conceptsWhereIC(cc, Entry, +value, +key); ret asSet(ccollect page(entries)); } Set pagesWithKey(S key) { L entries = conceptsWhereIC(cc, Entry, +key); ret asSet(ccollect page(entries)); } Cl keysForPageAndValue(S q, S value) { Page page = findPageFromQ(q); if (page == null) null; ret collect key(conceptsWhereIC(cc, Entry, +page, +value)); } // DB functions bool hasPage(S q) { ret hasConceptWhereIC(Page, +q); } S getValue(Page page, S key) { ret page == null || empty(key) ? null : getString value(highestByField count(objectsWhereIC(findBackRefs(page, Entry), +key))); } S getValue(S page, S key) { ret getValue(findPageFromQ(page), key); } S pageDisplayName(Page page) { /*S name = getValue(page, "read as"); bool unnaturalName = nempty(name) && !eq(makeAGIDomain(name), page.url); ret unnaturalName ? name + " " + squareBracketed(page.url) : or2(name, unpackAGIDomainOpt(page.url));*/ ret page.q; } S renderThing(S s, bool forceURLDisplay) { ret forceURLDisplay || isURL(s) || isAGIDomain(s) ? ahref(fixAGILink(absoluteURL(s)), htmlencode2(shorten(agiBlue.displayLength, s))) : ahref(agiBlue_linkForPhrase(s, slice := slice.isMainSlice() ? null : slice.sliceConcept.globalID), htmlencode2(shorten(agiBlue.displayLength, s))); } GlobalID sliceID() { ret slice.globalID(); } SliceInfo sliceInfo() { ret slice.sliceInfo; } } // end of WorksOnSlice sclass Renderer1 implements Renderer { // Serve page O html(S uri, SS params) { sleepWhile(() -> !agiBlue.started); if (agiBlue.broken) ret subBot_serve500("Internal error"); ret new Request().serve(uri, params); } // forward things to AGIBlue object delegate agiBlue_pageURLWithSlice to agiBlue. delegate newCentralIndexGet to agiBlue. delegate centralIndexMade to agiBlue. delegate agiBlueURL to agiBlue. delegate dumpSliceToFile to agiBlue. delegate checkDoubleLoads to agiBlue. delegate asyncSearch to agiBlue. delegate centralIndexGetSlices to agiBlue. delegate dbLog to agiBlue. delegate entryToTriple to agiBlue. delegate loadSlice to agiBlue. delegate agiBlueName to agiBlue. delegate idx_slicesByModification to agiBlue. delegate idx_slicesByName to agiBlue. delegate idx_usersBySeen to agiBlue. delegate idx_usersByName to agiBlue. delegate agiBlue_isOriginal to agiBlue. delegate loadBackgroundSlice to agiBlue. delegate maxSliceNameLength to agiBlue. delegate pagesSortedByLength to agiBlue. delegate entriesOnPage to agiBlue. delegate centralIndex to agiBlue. delegate showGoogleLogIn to agiBlue. delegate rst_index to agiBlue. delegate LoadedSlice to AGIBlue. delegate centralIndexMadeIn to agiBlue. delegate sideSearchType to agiBlue. delegate sideDisplayLength to agiBlue. delegate sliceConceptForGlobalID to agiBlue. delegate mainSliceGlobalID to agiBlue. delegate sliceForName to agiBlue. class Request extends WorksOnSlice { S cookie; Session session; Slice sliceConcept; S uri; SS params; long started = sysNow(); S q, domain; Set get; // when serving a concept page, info that may be grabbed // by thought bots L usesAsKey; // should also work for standalone bool isHomeDomain() { S domain = domain(); ret eqic(domain, "www.agi.blue") || !ewic(domain, ".agi.blue"); } O serve(S uri, SS params) { this.uri = dropMultipleSlashes(uri); this.params = params; new Matches m; print(uri + " ? " + unnull(subBot_query())); // get cookie & session cookie = cookieFromUser(); if (cookie != null) { session = uniq_sync(Session, +cookie); if (session.user! == null) cset(session, user := uniq_sync CookieUser(+cookie)); } else session = unlistedWithValues(Session); // check if user wants to change slices S selectSlice = params.get('slice); if (isGlobalID(selectSlice)) cset(session, selectedSlice := selectSlice); if (session.selectedSlice == null) cset(session, selectedSlice := str(mainSliceGlobalID())); // get slice's case ID S caseID = ""; sliceConcept = sliceConceptForGlobalID(session.selectedSlice); print("Selected slice: " + session.selectedSlice + ", obj? " + (sliceConcept != null)); if (sliceConcept != null) caseID = sliceConcept.caseID; print("caseID: " + caseID); // load slice slice = assertNotNull(loadSlice(caseID, session.selectedSlice)); cc = slice.cc; // Check for special URIs if (swic(uri, "/bot/")) ret serveBot(); if (swic(uri, "/user/", m)) { User user = conceptWhere User(globalID := toGlobalIDObj(assertGlobalID(m.rest()))); if (user == null) ret subBot_serve404("User not found"); S title = "User " + user; ret hhtml_miniPage(title, h3(agiBlueNameHTML() + " | " + title) + "User type: " + classShortName(user)); } // eleu appends a slash to the URI if it's a single identifier, so we drop it again S uri2 = dropTrailingSlash(uri); if (eqic(uri2, "/google-verify")) { print("Google-verify started."); Payload payload = printStruct(googleVerifyUserToken2(googleSignInID(), params.get("token"))); if (payload == null) ret print("google-verify", "No"); S email = payload.getEmail(); if (empty(email)) ret print("google-verify", "No"); GoogleUser user = uniqCI_sync GoogleUser(googleEmail := email); cset(user, googleEmailVerified := payload.getEmailVerified(), googleFirstName := strOrNull(payload.get("given_name")), googleLastName := strOrNull(payload.get("family_name"))); cset(session, +user); ret print("google-verify", payload.getEmail() + " " + (payload.getEmailVerified() ? "(verified)" : "(not verified)")); } cset(session.user!, lastSeen := now()); if (nempty(params.get('searchAction))) ret serveSearchAction(); if (eqic(uri2, "/users")) ret serveUsersList(); if (eqic(uri2, "/slices")) ret serveSlicesList(); if (eqic(uri2, "/classes")) ret serveClasses(); if (eqic(uri2, "/search")) ret serveScoredSearch(); if (eqic(uri2, "/literalSearch")) ret serveLiteralSearch(); if (eqic(uri2, "/levenSearch")) ret serveLevenSearch(); if (eqic(uri2, "/query")) ret serveQueryPage(); if (eqic(uri2, "/createSlice")) ret serveCreateSlicePage(); if (eqic(uri2, "/deletePage")) ret serveDeletePage(); if (eqic(uri2, "/deleteSlice")) ret serveDeleteSlice(); if (eqic(uri2, "/checkboxes")) ret serveCheckboxes(); if (eqic(uri2, "/multiAdd")) ret serveMultiAdd(); if (eqic(uri2, "/version")) ret myTranspilationDate(); if (eqic(uri2, "/restart") && authed()) { agiBlue.softRestart(); ret "OK"; } q = params.get('q); get = asCISet(nempties(subBot_paramsAsMultiMap().get('get))); domain = or2(params.get('domain), domain()); S raw = firstKeyWithValue("", params); // agi.blue?something if (nempty(raw) && empty(q)) q = raw; /*if (nempty(q)) { domain = makeAGIDomain(q); if (l(domain) > maximumDomainPartLength()) // escape with "domain=" ret hrefresh(agiBlueURL() + hquery(+domain, key := "read as", value := q)); ret hrefresh("http://" + domain + (eq(q, domain) ? "" : "/" + hquery(key := "read as", value := q))); //uri = "/"; replaceMapWithParams(params, key := "read as", value := q); }*/ S url = domain + dropTrailingSlash(uri); // domain to query //if (empty(q)) q = url; if (empty(q)) { S qq = agiBlue_urlToQuery(url); if (neqic(qq, "agi.blue")) q = qq; } Page page, bool newPage = unpair uniqCI2_sync(cc, Page, +q); if (newPage) dbLog("New page", +q); //printStructs(+params, +raw, +q); if (empty(params.get('q)) && empty(raw) && isHomeDomain()) { //L pages = sortedByFieldDesc _modified(list(cc, Page)); // TODO: use index L pages = cloneList(slice.idx_latestChangedPages.objectIterator()); int start = parseInt(params.get("start")), step = 100; S nav = pageNav2("/", l(pages), start, step, "start"); S content = hform(b("GIVE ME INPUT:   ") + htextinput('q, autofocus := true) + " " + hsubmit("Search", name := 'searchAction) + " " + hsubmit("Add to slice")) + h1(sliceAsHTML() + " has " + nPages(countConcepts(cc, Page)) + " and " + nConnections(countConcepts(cc, Entry))) + p(nav) + p_nemptyLines(map(pageToHTMLLink(), subList(pages, start, start+step))); ret hhtml_agiBlue(hhead_title("Slice " + sliceConcept().name + " | agi.blue") // SERVE SLICE HOME PAGE + hbody(hfullcenterAndTopLeft(top() + content + footer(), sliceSelector() ))); } S key = trim(params.get('key)), value = trim(params.get('value)); L entries = agiBlue.new GetEntriesAndPost(cc).go(page, params).entries; //S get = params.get('get); if (nempty(get)) ret serveJSON(collect value(llNotNulls(firstThat(entries, e -> get.contains(e.key))))); S key2 = key, value2 = value; if (nempty(key) && nempty(value)) key2 = value2 = ""; // input reset bool withHidden = eq(params.get('withHidden), "1"); new Set hide; if (!withHidden) for (Entry e : entries) if (eqic(e.key, 'hide) && isSquareBracketedInt(e.value)) addAll(hide, e.count, parseInt(unSquareBracket(e.value))); new MultiMap mmMeta; for (Entry e : entries) if (isSquareBracketedInt(e.key)) mmMeta.put(parseInt(unSquareBracket(e.key)), e.value); new MultiMap mm; for (Entry e : entries) mm.put(e.key, e.value); //S name = or2(/* ouch */ last(mm.get("read as")), /* end ouch */ unpackAGIDomain(page.url), page.url); S name = page.q; // Find references L refs = concatLists(conceptsWhereIC(cc, Entry, value := name), conceptsWhereIC(cc, Entry, key := name)); Set refPages = asSet(ccollect page(refs)); refPages.remove(page); // don't list current page as reference // Search in page names (depending on default search type) L searchResults; int totalResults = 0, searchResultsToShow = 50; if (eq(sideSearchType, 'leven)) searchResults = levenSearch(page.q, max := searchResultsToShow); else if (eq(sideSearchType, 'literal)) { searchResults = literalSearch(page.q, max := maxInt()); print("totalResults", totalResults = l(searchResults)); searchResults = takeFirst(searchResultsToShow, searchResults); } else searchResults = (L) dm_call('agiBlueSearch, 'search, page.q, maxResult := clipIntPlus(searchResultsToShow, 1), agiBlueBotID := programID(), +cc); searchResults.remove(page); // Find uses as key usesAsKey = conceptsWhereIC(cc, Entry, key := page.q); // Find page in other slices CentralIndexEntry cie = centralIndex.get(page.q); L pageInOtherSlices = cie == null ? null : listMinus(cie.slices, slice.sliceConcept); S pageName_html = htmlEncode2(shorten(agiBlue.displayLength, name)); S mainContents = top() + h1(ahref_unstyled(agiBlue_pageURLWithSlice(page), pageName_html) + (!isURL(page.q) ? "" : " " + targetBlank(page.q, hsnippetimg(#1101876))) + (newPage ? " [huh????]" : "")) + p_nemptyLines(map(entries, func(Entry e) -> S { !withHidden && (hide.contains(e.count) || eqic(e.key, "read as") && eqic(e.value, name)) ? "" : "[" + e.count + "] " + renderThing(e.key, false) + ": " + b(renderThing(e.value, cic(mmMeta.get(e.count), "is a URL"))) })) + hpostform(h3("Add an entry") + "Key: " + hinputfield(key := key2) + " Value: " + hinputfield(value := value2) + "

" + hsubmit("Add") ) + p(ahref(agiBlueURL() + "/literalSearch" + hquery(q := page.q), "[literal search]", title := "Search pages with a name containing this page's name literally") + " " + ahref(agiBlueURL() + "/levenSearch" + hquery(q := page.q), "[leven search 1]", title := "Search pages with a Levenshtein similarity of 1 containing this page's name literally") + " " + ahref(agiBlueURL() + "/search" + hquery(q := page.q), "[scored search]", title := "Search pages with ScoredSearch") + " " + ahref(agiBlueURL() + "/deletePage" + hquery(slice := sliceID(), q := page.q), "[delete page]") ) // optional "uses as key" section + (empty(usesAsKey) ? "" : h3(quote(pageName_html) + " as key") + p_nemptyLines_showFirst(50, map(usesAsKey, e -> pageToHTMLLink().get(e.page!) + unicode_spacedRightPointingTriangle() + pageName_html + unicode_spacedRightPointingTriangle() + pageToHTMLLink().get(pageFromQ(e.value)) ))) // optional "in other slices" section + (empty(pageInOtherSlices) ? "" : h3(quote(pageName_html) + " in other slices") + p_nemptyLines_showFirst(50, map(slice -> ahref(agiBlueURL() + hquery(slice := slice.globalID, q := page.q), htmlEncode2(slice.name)), pageInOtherSlices))) ; // end of mainContents S sideContents = hform(b("GIVE ME INPUT:") + " " + htextinput('q) + " " + hsubmit("Ask", onclick := "document.getElementById('newInputForm').target = '';") + " " + hsubmit("+Tab", title := "Ask and show result in a new tab", onclick := "document.getElementById('newInputForm').target = '_blank';"), id := 'newInputForm) + h3("References (" + l(refPages) + ")") + p_nemptyLines_showFirst(10, map(pageToHTMLLink(displayLength := sideDisplayLength), refPages)) + h3(searchTypeToText.get(sideSearchType) + " search results (" + (l(searchResults) >= searchResultsToShow ? searchResultsToShow + "+" : str(l(searchResults))) + (totalResults > l(searchResults) ? " of " + n2(totalResults) : "") + ")") + p_nemptyLines_showFirst(searchResultsToShow, map(pageToHTMLLink(displayLength := sideDisplayLength), searchResults)) + hdiv("", id := 'extraStuff); // TODO: sync search delivery with WebSocket creation if (asyncSearch) doLater(6.0, r { dm_call('agiBlueSearch, 'searchAndPost, page.q, agiBlueBotID := programID(), +cc) }); // notify local interested parties vmBus_send('agiBlue_servingConceptPage, this, page); S iframe = params.get('render_iframe); // serve a concept page ret hhtml_agiBlue(hhead_title(pageDisplayName(page)) + hbody( tag('table, tr( td(sliceSelector(), valign := 'top) + td(sideContents, align := 'right, valign := 'top, rowspan := 2)) + tr(td(mainContents + (empty(iframe) ? "" : iframe(iframe, width := 600, height := 400)) + footer(), align := 'center, valign := 'top)), width := "100%", height := "100%"))); } O servePagesToBot(Iterable pages) { ret serveListToBot(map(pageToMap(wrapMapAsParams(params)), pages)); } O serveListToBot(Collection l) { if (nempty(params.get('max))) l = takeFirst(parseInt(params.get('max)), l); ret serveJSON(l); } // uri starts with "/bot/" O serveBot() { S q = params.get('q); if (eqic(uri, "/bot/hello")) ret serveJSON("hello"); if (eqic(uri, "/bot/hasPage")) ret serveJSON(hasPage(q)); if (eqic(uri, "/bot/randomPageContaining")) { assertNempty(q); ret servePageToBot(random(filter(list(cc, Page), p -> cic(p.q, q))), params); } if (eqic(uri, "/bot/allPages")) ret servePagesToBot(list(cc, Page)); if (eqic(uri, "/bot/allPagesStartingWith")) { assertNempty(q); ret servePagesToBot(filter(list(cc, Page), p -> swic(p.q, q))); } if (eqic(uri, "/bot/allPagesEndingWith")) { assertNempty(q); ret servePagesToBot(filter(list(cc, Page), p -> ewic(p.q, q))); } if (eqic(uri, "/bot/allPagesContaining")) { assertNempty(q); ret servePagesToBot(filter(list(cc, Page), p -> cic(p.q, q))); } if (eqic(uri, "/bot/allPagesContainingRegexp")) { assertNempty(q); Pattern pat = regexpIC(q); ret servePagesToBot(filter(list(cc, Page), p -> regexpFindIC(pat, p.q))); } if (eqicOneOf(uri, "/bot/postSigned", /*"/bot/makePhysicalSlice",*/ "/bot/approveTrustRequest")) { S text = rtrim(params.get('text)); S key = getSignerKey(text); if (empty(key)) ret subBot_serve500("Please include your public key"); if (!isSignedWithKey(text, key)) ret subBot_serve500("Signature didn't verify"); text = dropLastTwoLines(text); // drop signer + sig line Signer signer = uniq_sync Signer(publicKey := key); /*if (eqic(uri, "/bot/makePhysicalSlice")) { if (!signer.trusted) ret subBot_serve500("Untrusted signer"); Page page = findPageFromParams(jsonDecodeMap(text)); if (page == null) ret subBot_serve500("Page not found"); ret serveJSON(uniq2_sync(PhysicalSlice, slicePage := page).b ? "Slice made" : "Slice exists"); }*/ if (eqic(uri, "/bot/postSigned")) { new L out; for (S line : tlft(text)) { SS map = jsonDecodeMap(line); AGIBlue.GetEntriesAndPost x = agiBlue.new GetEntriesAndPost(cc); x.signer = signer; Page page = findOrMakePageFromParams(map); if (page == null) continue with out.add("Invalid page reference"); x.go(page, map); out.add(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value"); } ret serveJSON(out); } if (eqic(uri, "/bot/approveTrustRequest")) { if (!signer.trusted) ret subBot_serve500("Untrusted signer"); Signer toApprove = conceptWhere Signer(publicKey := trim(text)); if (toApprove == null) ret subBot_serve500("Signer to approve not found"); cset(toApprove, trusted := true, approvedBy := signer.globalID); ret serveJSON("Approved: " + trim(text)); } ret subBot_serve500("CONFUSION"); } if (eqic(uri, "/bot/post")) { AGIBlue.GetEntriesAndPost x = agiBlue.new GetEntriesAndPost(cc); x.go(pageFromQ(q), params); ret serveJSON(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value"); } if (eqic(uri, "/bot/entriesOnPage")) ret serveJSON(map(entriesOnPage(findPageFromParams(params)), entryToMap(false))); if (eqic(uri, "/bot/entriesForKey")) ret serveJSON(map(conceptsWhereIC(cc, Entry, key := params.get('key)), entryToMap(true))); // look up value from page & key in current slice if (eqic(uri, "/bot/lookup")) { S key = params.get('key); if (empty(key)) ret serveJSON("Need key"); S value = getValue(findPageFromParams(params), key); ret serveJSON(empty(value) ? "" : litmap(+value)); } if (eqic(uri, "/bot/multiLookup")) { S key = params.get('key); if (empty(key)) ret serveJSON("Need key"); ret serveJSON(collect value(objectsWhereIC(findBackRefs(findPageFromParams(params), Entry), +key))); } if (eqic(uri, "/bot/lookupMultipleKeys")) { Set keys = asCISet(subBot_paramsAsMultiMap().get('key)); L entries = findBackRefs(findPageFromParams(params), Entry); ret serveJSON(map(entryToMap(false), filter(entries, e -> contains(keys, e.key)))); } // look up value from page & key in all slices if (eqic(uri, "/bot/multiLookupInAllSlices")) { S key = params.get('key); if (empty(key)) ret serveJSON("Need key"); Cl slices = centralIndexGetSlices(key); new L results; fOr (Slice s : slices) { WorksOnSlice wos = new(loadBackgroundSlice(s)); Page page = wos.findPageFromParams(params); if (page != null) fOr (Entry e : objectsWhereIC(findBackRefs(page, Entry), +key)) results.add(litorderedmap("c" := e.value, "slice" := str(s.globalID()))); } ret serveJSON(results); } if (eqic(uri, "/bot/latestEntries")) ret serveJSON(map(takeFirst(10, slice.idx_latestEntries.objectIterator()), entryToMap(true))); if (eqic(uri, "/bot/latestPages")) ret serveJSON(map(takeFirst(10, slice.idx_latestCreatedPages.objectIterator()), pageToMap())); if (eqic(uri, "/bot/latestChangedPages")) ret serveJSON(map(takeFirst(10, slice.idx_latestChangedPages.objectIterator()), pageToMap())); if (eqic(uri, "/bot/googleUsersCount")) ret serveJSON(countConcepts(GoogleUser)); if (eqic(uri, "/bot/totalPageCount")) ret serveJSON(countConcepts(Page)); /*if (eqic(uri, "/bot/pageWithoutPhysicalSliceCount")) ret serveJSON(countConceptsWhere(Page, slice := null)); if (eqic(uri, "/bot/physicalSliceCount")) ret serveJSON(countConcepts(PhysicalSlice));*/ if (eqic(uri, "/bot/trustedSignersCount")) ret serveJSON(countConcepts(Signer, trusted := true)); if (eqic(uri, "/bot/valueSearch")) { S value = params.get('value); L entries = conceptsWhereIC(cc, Entry, +value); ret serveJSON(map(takeFirst(100, entries), entryToMap(true))); } if (eqic(uri, "/bot/keyAndValueSearch")) { S key = params.get('key), value = params.get('value); Cl pages = pagesForKeyAndValue(key, value); ret servePagesToBot(pages); } if (eqic(uri, "/bot/pagesWithKey")) { S key = params.get('key); ret servePagesToBot(pagesWithKey(key)); } if (eqic(uri, "/bot/match")) { PositionalTokenIndex2 index = slice.positionalIndex; if (index == null) ret "No positional index"; ret serveJSON(fastJoinAll(positionalTokenIndex2_matchAll_returnTokenizations(q, index))); } if (eqic(uri, "/bot/aLookup")) { S key = params.get('key), value = params.get('value); Cl pages = pagesForKeyAndValue(key, value); ret serveJSON(collect q(pages)); } if (eqic(uri, "/bot/bLookup")) { S value = params.get('value); ret serveJSON(keysForPageAndValue(q, value)); } if (eqic(uri, "/bot/keyValuePairsByPopularity")) { L pairs = map(list(Entry), e -> pair(e.key, e.value)); LPair pairs2 = multiSetTopPairs(ciMultiSet(map pairToUglyStringForCIComparison(pairs))); ret serveJSON(map(pairs2, p -> { S key, value = unpair pairFromUglyString(p.a); ret litorderedmap(n := p.b, +key, +value); })); } // technically a duplicate of entriesForKey if (eqic(uri, "/bot/pagesAndValuesForKey")) { S key = params.get('key); L entries = conceptsWhereIC(cc, Entry, +key); L pairs = map(entries, e -> pair(e.page->q, e.value)); ret serveJSON(pairsToLists(pairs)); } if (eqic(uri, "/bot/allKeys")) ret serveListToBot(distinctCIFieldValuesOfConcepts(cc, Entry, 'key)); if (eqic(uri, "/bot/allKeysByPopularity")) ret serveListToBot(mapMultiSetByPopularity(distinctCIFieldValuesOfConcepts_multiSet(cc, Entry, 'key), (key, n) -> litorderedmap(+n, +key))); if (eqic(uri, "/bot/dbSize")) ret serveJSON(l(conceptsFile())); if (eqic(uri, "/bot/query")) ret serveBotQuery(); if (eqic(uri, "/bot/createSlice")) { Slice slice = createSlice(assertNempty(params.get('name))); ret serveJSON(litorderedmap(id := str(slice.globalID), name := slice.name)); } if (eqic(uri, "/bot/dumpAllSlices") && authed()) { new L out; for (Slice slice) try { out.add(litorderedmap(slice := slice.caseID, ms := returnTimeFor(() -> dumpSliceToFile(loadBackgroundSlice(slice))))); } catch print e { out.add(litorderedmap(slice.caseID, error := exceptionToStringShort(e))); } ret serveJSON(out); } if (eqic(uri, "/bot/dumpSlice")) { File f = dumpSliceToFile(slice); ret "OK, saved as: " + f2s(f); } if (eqic(uri, "/bot/diskStats")) ret serveJSON(litcimap( sizeOnDisk := agiBlue.sizeOnDisk!, freeDiskSpace := freeDiskSpace() )); if (eqic(uri, "/bot/memStats")) { Map fg = cloneMap(agiBlue.loadedSlices); Map bg = cloneMap(agiBlue.backgroundSlices); ret serveJSON(litcimap( error := checkDoubleLoads(), centralIndexEntries := l(centralIndex), centralIndexMade := renderHowLongAgo(centralIndexMade), centralIndexMadeInMS := centralIndexMadeIn, numLoadedForegroundSlices := l(fg), numLoadedBackgroundSlices := l(bg), loadedForegroundSlicesEstimatedSize := longSum(map(s -> agiBlue.estimatedSliceDataSize(s), keys(fg))), loadedBackgroundSlicesEstimatedSize := longSum(map(s -> agiBlue.estimatedSliceDataSize(s), keys(bg))), loadedForegroundSlices := sortedIC(keysList(fg)), loadedBackgroundSlices := sortedIC(keysList(bg)), sessionsWithGoogleUsers := countPred(list(Session), s -> s.user! instanceof GoogleUser), userObjects := countConcepts(User), processSize := getOpt(vmBus_query('processSize), 'processSize), slices := countConcepts(Slice) )); } if (eqic(uri, "/bot/centralIndexGet")) { CentralIndexEntry e = centralIndex.get(q); ret serveJSON(e == null ? ll() : map(e.slices, sliceToMap())); } // return primary triple from each slice if (eqic(uri, "/bot/centralIndexGrab")) { CentralIndexEntry ie = centralIndex.get(q); new L out; if (ie != null) for (Slice slice : ie.slices) { LoadedSlice ls = loadBackgroundSlice(slice); WorksOnSlice wos = new(ls); Page page = wos.findPageFromQ(q); Iterable entries = entriesOnPage(page); print("Page for " + q + " in slice " + ls.caseID + ": " + yesNo(page != null) + " - " + l(entries)); Entry e = first(entries); if (e != null) out.add(litorderedmap(a := page.q, b := e.key, c := e.value, slice := slice.caseID)); } ret serveJSON(out); } // Search for literal substring in central index entry // Returns only the words found, not the slices if (eqic(uri, "/bot/centralIndexLiteralSearch")) { LS entries = containingIC(keys(centralIndex), q); ret serveJSON(entries); } if (eqic(uri, "/bot/updateCentralIndex") && authed()) { rst_index.trigger(); ret "OK"; } if (eqic(uri, "/bot/allGoogleEmails") && authed()) ret serveJSON(collect googleEmail(list(GoogleUser))); if (eqic(uri, "/bot/words2_spaces_all")) ret serveJSON(mapToValues_ciMap words2_spaces_cached(collect q(conceptsSortedByFieldCI(slice.cc, Page, 'q)))); if (eqic(uri, "/bot/words2_spaces_collapse_all")) ret serveJSON(mapToValues_ciMap words2_spaces_collapse_cached(collect q(conceptsSortedByFieldCI(slice.cc, Page, 'q)))); if (eqic(uri, "/bot/makeManyPages")) { //LS qs = nempties(subBot_paramsAsMultiMap().get('q)); LS qs = tlft(params.get("pages")); slice.haltSliceDumping = true; try { fOr ping (S _q : qs) pageFromQ(_q); } finally { slice.haltSliceDumping = true; slice.rstDumpSlice.trigger(); } ret "OK (" + l(qs) + ")"; } if (eqic(uri, "/bot/sliceNamesMap")) ret serveJSON(mapToMap(list(Slice), s -> pair(str(s.globalID), s.name))); if (eqic(uri, "/bot/sliceForName")) { Slice slice = sliceForName(q); ret serveJSON(slice == null ? null : str(slice.globalID)); } if (eqic(uri, "/bot/allowOpenPosting")) { try object errorIfNotOwnerOfSlice(); bool allow = eq("1", params.get('allow)); cset(sliceInfo(), openPosting := allow); ret "Set openPosting to " + allow; } if (eqic(uri, "/bot/createUnusedNumberedPage")) { int n = 1; // TODO: sync while (findPageFromQ(q + n) != null) ++n; ret serveJSON(litmap(q := pageFromQ(q + n).q)); } // end of bot methods ret subBot_serve404(); } O serveBotQuery() { S query = params.get('query); ret agiBlue.new Query(slice).process(query); } int searchResultsToShow() { ret agiBlue.searchResultsToShow; } O serveLiteralSearch() { S q = params.get('q); L searchResults = literalSearch(q); ret serveSearchResults("literal search" , q, searchResultsToShow(), searchResults); } L literalSearch(S q, O... _) { int searchResultsToShow = optPar max(_, 100); // quick search in random order //L searchResults = takeFirst(clipIntPlus(searchResultsToShow, 1), filterIterator(iterator(list(cc, Page)), p -> cic(p.q, q)); // full search, order by length ret takeFirst(clipIntPlus(searchResultsToShow, 1), pagesSortedByLength(filter(list(cc, Page), p -> cic(p.q, q)))); } O serveScoredSearch() { S q = params.get('q); L searchResults = (L) dm_call('agiBlueSearch, 'search, q, +cc); ret serveSearchResults("scored search" , q, searchResultsToShow(), searchResults); } O serveLevenSearch() { S q = params.get('q); L searchResults = levenSearch(q); ret serveSearchResults("leven search with distance 1" , q, searchResultsToShow(), searchResults); } L levenSearch(S q, O... _) { int searchResultsToShow = optPar max(_, 100); int maxEditDistance = 1; new Map map; for (Page p : list(cc, Page)) { int distance = leven_limitedIC(q, p.q, maxEditDistance+1); if (distance <= maxEditDistance) map.put(p, distance); } ret takeFirst(clipIntPlus(searchResultsToShow, 1), keysSortedByValue(map)); } O serveSearchResults(S searchType, S q, int searchResultsToShow, Collection searchResults) { S title = "agi.blue " + searchType + " for " + htmlEncode2(quote(q)) + " (" + (l(searchResults) >= searchResultsToShow ? searchResultsToShow + "+ results" : nResults(l(searchResults))) + ")"; ret hhtml_agiBlue(hhead_title(htmldecode_dropAllTags(title)) + hbody(hfullcenter(//top() + h3(title) + p_nemptyLines_showFirst(searchResultsToShow, map(pageToHTMLLink(), searchResults))))); } O serveQueryPage() { S query = params.get('query); if (query == null) query = loadSnippet(#1024258); S title = agiBlueNameHTML() + " | Execute a query script (" + targetBlank("http://code.botcompany.de/1024274", "ALQL") + ")"; ret hhtml_miniPage(title, h3(title) + form( htextarea(query, name := 'query, cols := 80, rows := 10, autofocus := true) + "

" + hsubmit("Execute"), action := "/bot/query" )); } O hhtml_miniPage(S htmlTitle, O contents) { ret hhtml_agiBlue(hhead_title(htmldecode_dropAllTags(htmlTitle)) + hbody(hfullcenter(contents))); } S sliceHomeURL() { ret slice == null ? agiBlueURL() : sliceHomeURL(slice.sliceConcept.globalID); } S sliceHomeURL(Slice slice) { ret sliceHomeURL(slice.globalID); } S sliceHomeURL(GlobalID slice) { ret agiBlueURL() + hquery(+slice); } S sliceLinkHTML(Slice slice) { ret slice == null ? "-": ahref(sliceHomeURL(slice), htmlEncode2(slice.name)); } O serveCreateSlicePage() { S sliceName = trim(params.get('sliceName)); if (eq(params.get('doIt), "1") && nempty(sliceName)) { // TODO: check for existing name Slice slice = createSlice(sliceName); ret hrefresh(sliceHomeURL(slice.globalID)); } S title = agiBlueNameHTML() + " | Create slice"; ret hhtml_agiBlue(hhead_title(htmldecode_dropAllTags(title)) + hbody(hfullcenter( h3(title) + form( hhidden(doIt := 1) + "Slice name: " + htextinput(+sliceName, autofocus := true) + "

" + hsubmit("Create slice")) ))); } O serveDeletePage() { q = params.get('q); Page page = findPageFromQ(q); if (page == null) ret "Page " + quote(q) + " not found in slice " + htmlEncode(slice.name()); try object errorIfNotOwnerOfSlice(); deleteConcepts(findBackRefs(page, AbstractEntry)); cdelete(page); ret hrefresh(1.0, sliceHomeURL()) + "Page deleted"; } // TODO: delete without loading O serveDeleteSlice() { assertNempty("Need slice param", params.get('slice)); try object errorIfNotOwnerOfSlice(); S caseID = sliceConcept.caseID; agiBlue.unloadSlice(caseID); File dir = sliceDir(sliceConcept); if (!dirExists(dir)) ret "Dir not found: " + f2s(dir); moveDirectory(dir, programFile("deletedSlices/" + sliceConcept.caseID)); cset(session, selectedSlice := null); cdelete(sliceConcept); ret serveText("Slice " + caseID + " deleted (& backed up)"); } O errorIfNotOwnerOfSlice() { bool ownage = slice.sliceConcept.owner! == session.user!; if (!ownage) ret subBot_serve403("Sorry, you don't own slice " + htmlEncode(slice.name())); null; } F1 pageToHTMLLink(O... _) { optPar int displayLength = agiBlue.displayLength; ret func(Page p) -> S { S name = pageDisplayName(p); ret ahref(agiBlue_pageURLWithSlice(p), htmlEncode2(shorten(displayLength, name)), title := name); }; } // and google log-in S sliceSelector() { ret htag table(tr( (!showGoogleLogIn ? "" : td( googleSignIn_signInButton(agiBlueURL() + "/google-verify", "console.log(data);", ""), style := "padding-right: 10px;") + td(nobr(ahref("javascript:signOut()", "Sign out")), style := "padding-right: 10px; padding-bottom: 0.15em")) + (!agiBlue.showSliceSelector ? "" : hform(td( "Select reality slice: " + hselect(availableSlices(), session.selectedSlice, name := 'slice, onchange := "this.form.submit()") /*+ " " + hsubmit("Go")*/ + "   | " + ahref(agiBlueURL("/slices"), nSlices(countConcepts(Slice))) + " | " + ahref(agiBlueURL() + "/createSlice", "Create slice...") ))))); } S sliceAsHTML() { if (slice.isMainSlice()) ret htmlEncode2(agiBlueName() + "'s main slice"); if (slice.sliceConcept == null) ret "Slice ???"; ret htmlEncode2("Slice " + quote(slice.sliceConcept.name)); } Slice sliceConcept() { ret slice.sliceConcept; } S footer() { ret p(small(elapsedMS_sysNow(started) + " ms")); } S verifiedEmail() { if (session == null || !(session.user! instanceof GoogleUser)) null; ret ((GoogleUser) session.user!).googleEmail; } bool authed() { bool ver = eqic(verifiedEmail(), "stefan.reich.maker.of.eye@googlemail.com"); print("Auth check " + verifiedEmail() + " -> " + ver); ret ver; } S googleSignInID() { ret eqic(domain(), "botcompany.de") ? botCompanyGoogleSignInID() : agiBlueGoogleSignInID(); } S hhtml_agiBlue(S contents) { ret hhtml(hAddToHead_fast(contents, hIncludeGoogleFont("Source Sans Pro") //+ [[]] + loadJQuery() + hmobilefix() + googleSignIn_header("", googleSignInID()) + hstylesheet("body { font-family: Source Sans Pro }"))); } // list classes in current slice O serveClasses() { // find classes & counts L entries = conceptsWhereCI(cc, Entry, key := "_class"); MultiSet classesAndCount = ciMultiSet(); for (Entry e : entries) classesAndCount.add(e.value); S title = agiBlueNameHTML() + " | " + htmlEncode(sliceConcept().name) + " | Classes"; ret hhtml_miniPage(title, h3(title) + htmlTable2(map(keys(classesAndCount), c -> litorderedmap( "Class" := pageToHTMLLink().get(findPageFromQ(c)), "Elements" := classesAndCount.get(c), )), htmlEncode := false)); } O serveSlicesList() { S sort = getAny(params, 'sort, 'by); L slices; S shown; if (eqic(sort, 'name)) { slices = asList(idx_slicesByName.objectIterator()); shown = "sorted by name"; } else { sort = 'date; slices = asList(idx_slicesByModification.objectIterator()); shown = "last modified first"; } S title = agiBlueNameHTML() + " has " + nSlices(slices) /*+ " (" + shown + ")"*/; new PreIncLongCounter counter; ret hhtml_agiBlue(hhead_title_decode(title) + hbody(hfullcenter( h1(title) + p(joinWithSpacedVBar( ahrefIf(neqic(sort, 'name), agiBlueURL() + "/slices" + hquery(sort := 'name), "List by name"), ahrefIf(neqic(sort, 'date), agiBlueURL() + "/slices" + hquery(sort := 'date), "List by date"), ahref(agiBlueURL() + "/createSlice", "Create slice..."))) + htmlTable2(map(slices, slice -> litorderedmap( "Name" := sliceLinkHTML(slice), "ID" := ahref(sliceHomeURL(slice), str(slice.globalID)), "Owner" := str(slice.owner!) )), htmlEncode := false) ))); } O serveUsersList() { S sort = getAny(params, 'sort, 'by); L users; S shown; if (eqic(sort, 'name)) users = asList(idx_usersByName.objectIterator()); else { sort = 'seen; users= asList(idx_usersBySeen.objectIterator()); } S title = agiBlueNameHTML() + " has " + nUsers(users); new PreIncLongCounter counter; ret hhtml_agiBlue(hhead_title_decode(title) + hbody(hfullcenter( h1(title) + p(joinWithSpacedVBar( ahrefIf(neqic(sort, 'name), agiBlueURL() + "/users" + hquery(sort := 'name), "List by name"), ahrefIf(neqic(sort, 'seen), agiBlueURL() + "/users" + hquery(sort := 'seen), "List by last seen"), ahref(agiBlueURL() + "/createSlice", "Create slice..."))) + htmlTable2(map(users, user -> litorderedmap( "Name" := ahref(userHomeURL(user), htmlEncode2(str(user))), "Last seen" := renderHowLongAgo(user.lastSeen) )), htmlEncode := false) ))); } S sliceInfoHTML() { User owner = slice.sliceConcept.owner!; ret owner == null ? "" : p("This slice is owned by " + userHTML(owner)); } S userHomeURL(User user) { ret user == null ? null : agiBlueURL() + "/user/" + user.globalID; } S userHTML(User user) { ret user == null ? "nobody" : ahref(userHomeURL(user), htmlEncode2(str(user))); } S top() { ret nempty(get) ? "" : hcomment("cookie: " + takeFirst(4, session.cookie)) + hSilentComputatorWithFlag("agi.blue: " + q) + p(ahref(sliceHomeURL(), //hsnippetimg(#1101682, width := 565/5, height := 800/5, title := "Robot by Peerro @ DeviantArt") //hsnippetimg(#1101778, width := 96, height := 96, title := "agi.blue - a database for everything") hsnippetimg(#1101822, width := 314, height := 125, title := "agi.blue - Wikipedia for robots") )) + h2(ahref_unstyled(sliceHomeURL(), htmlEncode2(slice.sliceConcept.name), style := "color: yellow")) + sliceInfoHTML() + p(small( agiBlueNameHTML_boldWithSize() + (agiBlue_isOriginal() ? "" : " " + targetBlank("http://agi.blue", "[original]")) + " | " + ahref(agiBlueURL("/slices"), nSlices(countConcepts(Slice))) + " | " + targetBlank(progLink(), "source code") + " of this web site (" + nLines(sourceCodeLines) + ") | " + /*targetBlank("https://gitter.im/agi-blue/community", "sponsor https?") + " | " */ + "by " + targetBlank("https://BotCompany.de", "BC") + " | " + targetBlank("http://fiverr.tinybrain.de/", "Fiverr") + " | " + targetBlank("https://discordapp.com/invite/SEAjPqk", "Discord") + " | " + targetBlank("https://www.youtube.com/watch?v=b6jtRdV3Ev8", "Video") + " | " + targetBlank("http://code.botcompany.de/1024233", "Notes") + " | " + ahref(agiBlueURL() + "/query", "Query") )); } Slice createSlice(S name) { name = shorten(name, maxSliceNameLength); if (sliceForName(name) != null) fail("A slice with the name " + quote(name) + " exists, please choose another name."); Slice slice = cnew Slice(+name, owner := session.user); cset(slice, caseID := slice.defaultCaseID()); loadSlice(slice).initialSetup(slice.globalID); ret slice; } O serveCheckboxes() { set conceptsSortedByFieldCI_verbose; int showMax = 1000; Cl pages = takeFirst(showMax, conceptsSortedByFieldCI(slice.cc, Page, 'q)); S title = "Select pages"; ret hhtml_miniPage(title, h3(title) + hpostform( htmlTable2(map(pages, page -> litorderedmap( "Page" := hcheckbox("page_" + page.globalID()) + " " + pageToHTMLLink().get(page) )), htmlEncode := false) + h3("Add an entry to all selected pages") + p("Key: " + hinputfield("key") + " Value: " + hinputfield("value")) + p(hsubmit("Add entries")), action := agiBlueURL() + "/multiAdd")); } O serveMultiAdd() { S key = trim(params.get('key)), value = trim(params.get('value)); if (empty(key) || empty(value)) ret "Need key and value"; new Matches m; new L pages; for (S a, b : params) if (startsWith(a, "page_", m)) addIfNotNull(pages, conceptWhere(cc, Page, globalID := m.rest())); if (empty(pages)) ret "No pages selected"; for (Page page : pages) agiBlue.new GetEntriesAndPost(cc).go(page, params); ret "Entry added to " + nPages(pages); } S agiBlueNameHTML() { ret ahref(agiBlueURL(), htmlEncode2(agiBlueName())); } S agiBlueNameHTML_boldWithSize() { Long size = agiBlue.sizeOnDisk.peek(); ret b(agiBlueNameHTML()) + (size == null ? "" : " " + spanTitle("Size of agi.blue's database on disk", "(" + toM(size) + " MB)")); } IF1 pageToMap(O... _) { optPar bool withEntries; bool nameOnly = eqOneOf(optPar nameOnly(_), "1", true); if (nameOnly) ret (IF1) p -> litmap(q := p.q); ret (IF1) p -> { L entries = findBackRefs(p, Entry); ret litorderedmap( q := p.q, nEntries := l(entries), created := p.created, modified := p._modified, entries := !withEntries ? null : map(entries, entryToMap(false))); }; } IF1 entryToMap(bool withPage) { ret (IF1) e -> litorderedmap( created := e.created, i := e.count, key := e.key, value := e.value, q := withPage ? e.page->q : null, signer := getString globalID(e.signer!)); } IF1 sliceToMap() { ret (IF1) s -> litorderedmap( created := s.created, globalID := str(s.globalID), name := s.name, caseID := s.caseID); } O servePageToBot(Page page, SS params) { if (page == null) ret serveJSON(null); params = asCIMap(params); Map map = pageToMap(withEntries := valueIs1 withEntries(params)).get(page); ret serveJSON(map); } SS availableSlices() { L slices = asList(idx_slicesByName.objectIterator()); ret mapToOrderedMap(s -> pair(str(s.globalID), s.name + " [ID: " + s.globalID + "]"), slices); } O serveSearchAction() { q = trim(params.get('q)); if (empty(q)) ret "You are searching for nothing"; Map ci = centralIndex; LS entries = containingIC(keys(ci), q); S title = htmlEncode2("Searching for " + quote(q)); ret hhtml_miniPage(title, h2(agiBlueNameHTML() + " | " + title) + h3("Results in slice " + htmlEncode2(quote(sliceConcept().name))) + p("TODO") + h3(firstToUpper(nResults(entries)) + " in central index (" + l(ci) + ")") + htmlTable2(map(entries, e -> { CentralIndexEntry ee = ci.get(e); ret litorderedmap( "Phrase" := ahref( empty(ee.slices) ? null : agiBlue_linkForPhrase(e, slice := first(ee.slices).globalID), htmlEncode2(e)), "Slices" := joinWithComma(map(takeFirst(3, ee.slices), s -> ahref(agiBlue_linkForPhrase(e, slice := s.globalID), htmlEncode(s.name)))) + (l(ee.slices) > 3 ? " + " + (l(ee.slices)-3) + " more" : "") ); }), htmlEncode := false) ); } } // end of Request } // end of renderer set flag hotwire_here. // share ISpec interface with sub-modules static JavaXClassLoader hotwire_makeClassLoader(L files) { ClassLoader cl = myClassLoader(); // Avoid class loader chaining, always reference base class loader // (TODO) /*if (agiBlue().isClone && agiBlue().cloningSince != 0) { ClassLoader parent = cast getOpt(cl, 'virtualParent); print("Cloned class loader. " + parent); if (parent != null) cl = parent; }*/ ret new JavaXClassLoaderWithParent2(null, files, cl, parseClassesToShareList(classesToShare)); } static AGIBlue agiBlue() { ret agiBlue; } static File sliceDir(Slice slice) { ret programFile(slice.caseID); }