!7 //set flag alwaysSetGlobalIDs. // legacy conversion set flag NoNanoHTTPD. concept BEAObject > ConceptWithGlobalID { new Ref mirrorPost; void change :: after { ((GazelleBEA) botMod()).rstUpdateBEAMirrors.add(this); } void delete :: before { cdelete(mirrorPost!); } void updateMirrorPost { if (isDeleted()) ret; if (!mirrorPost.has()) cset(this, mirrorPost := cnew UserPost( type := "BEA Object", creator := ((DynGazelleRocks) botMod()).internalUser(), botInfo := "BEA Mirror Bot")); S text = structureString(); cset(mirrorPost!, title := str(this), +text); } S structureString() { S text = "Error"; pcall { structure_Data data = new { structure_ClassInfo newClass(Class c) { structure_ClassInfo info = super.newClass(c); if (c == Concept.Ref.class) { info.special = true; info.serializeObject = o -> { Concept cc = cast deref((Concept.Ref) o); //append("cu CRef " + (cc != null ? str(cc.id) : "null"), 3); if (cc cast BEAObject) append("CRef(gid=" + quote(cc.globalID()) + ")", 6); else if (cc != null) append("CRef(id=" + cc.id + ")", 6); else append("CRef", 1); }; } ret info; } void setFields(structure_ClassInfo info, L fields) { if (isSubclassOf(info.c, BEAObject)) { // Don't serialize "refs" and "backRefs" fields removeAll(fields, getField(BEAObject, "refs"), getField(BEAObject, "backRefs")); } super.setFields(info, fields); } }; S struct = structure(this, data); struct = dropLoadableUtilsPackageFromStruct(struct); text = indentStructureString_firstLevels(1, struct); } ret text; } toString { S type = strOrNull(cget type(this)); if (nempty(type)) ret type + " " + id; ret super.toString(); } } cmodule2 GazelleBEA > DynGazelleRocks { void init { super.init(); botName = heading = adminName = "Gazelle BEA"; set enableVars; set showTalkToBotLink; unset phoneNumberSpecialInputField; } start { // mirror all objects to be sure rstUpdateBEAMirrors.addAll(list(BEAObject)); } void makeIndices :: after { indexConceptFieldDesc(BEAObject, "_modified"); } L crudClasses(Req req) { ret listPlus(super.crudClasses(req), BEAObject); } S authFormHeading() { ret h3("Gazelle BEA"); } void makeFramer(Req req) { super.makeFramer(req); req.framer.renderTitle = () -> h1(ahref(baseLink + "/", "Gazelle BEA") + " " + htmlEncode2(req.framer.title)); } HCRUD_Concepts crudData(Class c, Req req) { HCRUD_Concepts cc = super.crudData(c, req); if (c == BEAObject) { cc.humanizeFieldNames = false; cc.convertConceptValuesToRefs = true; cc.itemName = () -> "BEA Object"; cc.getObjectForDuplication = id -> { MapSO item = cc.getObjectForDuplication_base(id); item.put(creator := req.auth.user!); // set default creator to current user item.put(createdFrom := getConcept(toLong(id))); ret item; }; } ret cc; } HCRUD makeCRUD(Class c, Req req, HTMLFramer1 framer) { HCRUD crud = super.makeCRUD(c, req, framer); if (c == BEAObject) { crud.cellColumnToolTips = true; crud.unshownFields = litset("mirrorPost"); HCRUD_Concepts cc = cast crud.data; crud.massageFormMatrix = (map, matrix) -> { S refSelector = crud.renderInput("newField_conceptValue", cc.makeConceptsComboBox("newField_conceptValue", BEAObject), null) + hjs([[$("[name=newField_conceptValue]").hide()]]); /*S refSelector = hselect_list(name := "newField_conceptValue", onchange := [[function() { var value = this.value; $("[name=newField_value]").toggle(value == "String"); $("[name=newField_conceptValue]").toggle(value == "BEAObject"); }]]);*/ LS types = ll("String", "BEAObject"); //S typeSelector = crud.renderComboBox("newField_type", "String", types, false); S typeSelector = hselect_list(types, name := "newField_type", onchange := [[ var value = this.value; $("[name=newField_value]").toggle(value == "String"); $("#newField_refBox").toggle(value == "BEAObject"); ]]); matrix.add(ll("Add field", htmlTable2_noHtmlEncode(ll( ll("Name", htextfield newField_name(), "Value", // string input htextfield newField_value(), span(refSelector, id := "newField_refBox", display := "none"), "Type", typeSelector )), noHeader := true))); }; crud.preprocessUpdateParams = params -> { params = cloneMap(params); // drop empty strings //removeFromMapWhereValue(params, v -> eq(v, "")); params = mapValues(params, v -> eq(v, "") ? null : v); S name = params.get("newField_name"), type = params.get("newField_type"), refValue = params.get("newField_conceptValue"), value = params.get("newField_value"); if (eq(type, "BEAObject")) { value = refValue; params.put("metaInfo_" + name, "concept"); } if (eq(value, "")) value = null; if (nempty(name) /*&& neqOneOf(value, null, "")*/) params.put(crud.fieldPrefix + name, value); ret params; }; } ret crud; } O serveBotFunction(Req req, S function, Map data, User user) { if (eq(function, "beaList")) { long changedAfter = toLong(data.get("changedAfter")); double pollFor = min(bot_maxPollSeconds, toLong(data.get("pollFor"))); // how long to poll (seconds) long startTime = sysNow(); // We're super-anal about catching all changes. This will probably never trigger if (changedAfter > 0 && changedAfter == now()) sleep(1); Cl objects; while true { objects = changedAfter == 0 ? list(BEAObject) : conceptsWithFieldGreaterThan_sorted(BEAObject, _modified := changedAfter); // return when there are results, no polling or poll expired if (nempty(objects) || pollFor == 0 || elapsedSeconds_sysNow(startTime) >= pollFor) ret serveJSON_breakAtLevels(2, result := map(objects, obj -> litorderedmap(gid := str(obj.globalID()), struct := obj.structureString()) )); // sleep and try again sleep(bot_pollInterval); } } ret super.serveBotFunction(req, function, data, user); } transient ReliableSingleThread_Multi rstUpdateBEAMirrors = new(100, c -> c.updateMirrorPost()); O serveOtherPage2(Req req) null { printVars_str serveOtherPage2(uri := req.uri); try object super.serveOtherPage2(req); S uri = dropTrailingSlashIfNemptyAfterwards(req.uri); printVars_str serveOtherPage2(uri2 := uri); if (eq(uri, "/inputs")) { ret htitle_h2("Inputs | Gazelle BEA") + ul_htmlEncode(map( conceptsWhereIC(BEAObject, type := "input"), c -> getString text(c))); } if (!inMasterMode(req)) null; if (eq(uri, "/uploadInputs")) { S inputs = req.get("inputs"); new LS output; if (nempty(inputs)) { for (S text : tlft(inputs)) { Pair p = uniqCI2_sync BEAObject(type := "Input", +text); if (cget uploadedBy(p.a) == null) cset(p.a, uploadedBy := req.auth.user); output.add("Input " + (p.b ? "added" : "exists") + " (ID " + p.a.id + "): " + text); } } ret h2("Upload inputs") + hpostform( p("Inputs (one per line):") + p(htextarea(inputs, name := "inputs")) + pIfNempty(htmlEncode_nlToBR(lines(output))) + hsubmit("Upload inputs")); } } } // end of module