Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

427
LINES

< > BotCompany Repo | #1029962 // serveOtherPage2 [gazelle.rocks Include]

JavaX fragment (include)

1  
switchable double bot_maxPollSeconds = 600;
2  
switchable int bot_pollInterval = 500;
3  
4  
Class<? extends Concept> defaultCRUDClass() {
5  
  ret Sequence;
6  
}
7  
8  
O serveOtherPage2(Req req) null {
9  
  new Matches m;
10  
11  
  S uri = dropTrailingSlashIfNemptyAfterwards(req.uri);
12  
13  
  if (eq(uri, "/admin"))
14  
    ret hrefresh(baseLink + "/crud/" + shortClassName(defaultCRUDClass()));
15  
16  
  if (eq(uri, "/latestPost")) {
17  
    UserPost post = highestConceptByField UserPost("created");
18  
    if (post == null) ret serve404("No posts in database");
19  
    ret servePost(post, req);
20  
  }
21  
  
22  
  if (eq(uri, "/latestModifiedPost")) {
23  
    UserPost post = highestConceptByField UserPost("_modified");
24  
    if (post == null) ret serve404("No posts in database");
25  
    ret servePost(post, req);
26  
  }
27  
  
28  
  if (eq(uri, "/rootPosts")) {
29  
    Cl<UserPost> posts = sortedByConceptIDDesc(filter(list(UserPost), post -> empty(post.postRefs)));
30  
    framer().navBeforeTitle = true;
31  
    framer().title = "Root Posts";
32  
    framer().add(ul(lmap postToHTMLWithDate(posts)));
33  
    ret framer().render();
34  
  }
35  
  
36  
  if (eq(uri, "/allPosts")) {
37  
    Cl<UserPost> posts = sortedByConceptIDDesc(list(UserPost));
38  
    framer().navBeforeTitle = true;
39  
    framer().title = "All Posts";
40  
    framer().add(ul(lmap postToHTMLWithDate(posts)));
41  
    ret framer().render();
42  
  }
43  
  
44  
  if (eq(uri, "/latestPosts")) {
45  
    Cl<UserPost> posts = takeFirst(50, sortedByConceptIDDesc(list(UserPost)));
46  
    framer().navBeforeTitle = true;
47  
    framer().title = "Latest Posts & Replies";
48  
    framer().add(ul(lmap postToHTMLWithDate(posts)));
49  
    ret framer().render();
50  
  }
51  
  
52  
  if (eq(uri, "/latestModifiedPosts")) {
53  
    Cl<UserPost> posts = takeFirst(50, sortedByFieldDesc _modified(list(UserPost)));
54  
    framer().navBeforeTitle = true;
55  
    framer().title = "Latest Modified Posts & Replies";
56  
    framer().add(ul(lmap postToHTMLWithDate(posts)));
57  
    ret framer().render();
58  
  }
59  
  
60  
  if (eq(uri, "/mainPosts")) {
61  
    Cl<UserPost> posts = takeFirst(50, sortedByConceptID(conceptsWhereCI UserPost(type := "Main")));
62  
    framer().navBeforeTitle = true;
63  
    framer().title = "Main Posts";
64  
    framer().add(ul(lmap postToHTMLWithDate(posts)));
65  
    ret framer().render();
66  
  }
67  
  
68  
  if (swic(uri, "/html/", m) && isInteger(m.rest())) {
69  
    long id = parseLong(m.rest());
70  
    ret serveHTMLPost(id);
71  
  }
72  
  
73  
  if (swic(uri, "/htmlEmbedded/", m) && isInteger(m.rest())) {
74  
    long id = parseLong(m.rest());
75  
    UserPost post = getConcept UserPost(id);
76  
    if (post == null) ret serve404("Post not found");
77  
    
78  
    HTMLFramer1 framer = framer();
79  
    framer.title = "[" + post.id + "] " + or2(post.title, shorten(post.text));
80  
    framer.add(hcomment("Post begins here") + "\n" + post.text + "\n" + hcomment("Post ends here"));
81  
    
82  
    ret framer.render();
83  
  }
84  
  
85  
  if (swic(uri, "/css/", m) && isInteger(m.rest())) {
86  
    long id = parseLong(m.rest());
87  
    UserPost post = getConcept UserPost(id);
88  
    if (post == null) ret serve404("Post not found");
89  
    ret serveWithContentType(post.text, "text/css");
90  
  }
91  
92  
  if (eq(uri, "/search")) {
93  
    framer().navBeforeTitle = true;
94  
    S q = trim(req.params.get("q"));
95  
    framer().title = joinNemptiesWithColon("Search", q);
96  
    framer().add(hcomment("cookie: " + req.webRequest.cookie()));
97  
    framer().add(hform(hinputfield(+q, autofocus := true) + " " + hsubmit("Search")));
98  
    framer().add(p(small("Search help: If you search for multiple words, they can appear in any order in the text. An underscore matches any character. Plus means space. Post titles, texts and types are searched.")));
99  
100  
    if (nempty(q)) {
101  
      ScoredSearcher<UserPost> searcher = new(q);
102  
      long id = parseLongOpt_pcall(q);
103  
      for (UserPost post)
104  
        searcher.add(post, (post.id == id ? 4 : 0)
105  
          + searcher.score(post.title)*3
106  
          + searcher.score(post.text)*2
107  
          + searcher.score(post.type)
108  
          + searcher.score(joinWithSpace(post.postRefTags))*0.5);
109  
      L<UserPost> posts = searcher!;
110  
      framer().add(p(b(addPlusToCount(searcher.maxResults, l(posts), nPosts(posts))
111  
        + " found for " + htmlEncode2(q))));
112  
      framer().add(ul(lmap postToHTMLWithDate(posts)));
113  
    }
114  
    
115  
    ret framer().render();
116  
  }
117  
118  
  // serve replies for JSTree AJAX call
119  
  if (eq(uri, "/jstree/replies")) {
120  
    UserPost post = getConcept UserPost(parseLong(req.params.get("post")));
121  
    if (post == null) ret serveJSON(ll());
122  
    Cl<UserPost> refs = referencingPosts(post);
123  
    /*[
124  
      { "id" : "demo_root_1", "text" : "Root 1", "children" : true, "type" : "root" },
125  
      { "id" : "demo_root_2", "text" : "Root 2", "type" : "root" }
126  
    ]*/
127  
    ret serveJSON(map(refs, p -> litorderedmap(
128  
      id := p.id,
129  
      text := postToHTMLWithDate(post),
130  
      children := nempty(referencingPosts(p)) ? true : null,
131  
      type := "root" // ?
132  
    )));
133  
  }
134  
135  
  if (startsWith(uri, "/touchPost/", m)) {
136  
    long id = parseLong(m.rest());
137  
    UserPost post = getConcept UserPost(id);
138  
    if (post == null) ret serve404("Post not found");
139  
    if (!canEditPost(post)) ret "You are not authorized to do this";
140  
    touchConcept(post);
141  
    ret hscript([[setTimeout('history.go(-1)', 1000);]]) + "Post " + post.id + " touched";
142  
  }
143  
144  
  if (startsWith(uri, "/hidePost/", m)) {
145  
    long id = parseLong(m.rest());
146  
    UserPost post = getConcept UserPost(id);
147  
    if (post == null) ret serve404("Post not found");
148  
    if (!canEditPost(post)) ret "You are not authorized to do this";
149  
    cset(post, hidden := true);
150  
    ret hrefresh(postLink(post));
151  
  }
152  
153  
  if (startsWith(uri, "/unhidePost/", m)) {
154  
    long id = parseLong(m.rest());
155  
    UserPost post = getConcept UserPost(id);
156  
    if (post == null) ret serve404("Post not found");
157  
    if (!canEditPost(post)) ret "You are not authorized to do this";
158  
    cset(post, hidden := false);
159  
    ret hrefresh(postLink(post));
160  
  }
161  
162  
  if (startsWith(uri, "/markSafe/", m)) {
163  
    long id = parseLong(m.rest());
164  
    UserPost post = getConcept UserPost(id);
165  
    if (post == null) ret serve404("Post not found");
166  
    UserPost codePost = optCast UserPost(first(post.postRefs));
167  
    if (codePost == null) ret "Code post not found";
168  
    if (!canEditPost(codePost)) ret "You are not authorized to do this";
169  
    
170  
    // create "Mark safe" post
171  
    uniqCI UserPost(creator := currentUser(), text := "Mark safe", postRefs := ll(post));
172  
    
173  
    sleepSeconds(6); // allow bot to react
174  
175  
    // touch code post
176  
    touchConcept(codePost);
177  
    ret hrefresh(postLink(codePost));
178  
  }
179  
180  
  if (eq(uri, "/mirrorAllConversations")) {
181  
    if (!req.masterAuthed) ret serveAuthForm(rawLink(uri));
182  
    for (Conversation c)
183  
      rstUpdateMirrorPosts.add(c);
184  
    ret "OK";
185  
  }
186  
187  
  if (startsWith(uri, "/mirrorConversation/", m)) {
188  
    if (!req.masterAuthed) ret serveAuthForm(rawLink(uri));
189  
    long id = parseLong(m.rest());
190  
    Conversation conv = getConcept Conversation(id);
191  
    if (conv == null) ret "Conversation not found";
192  
    conv.updateMirrorPost();
193  
    ret "Mirror post updated (" + htmlEncode2(str(conv.mirrorPost!)) + ")";
194  
  }
195  
196  
  if (eqic(uri, "/favicon.ico"))
197  
    ret serveFavIcon();
198  
199  
  // start of /bot commands
200  
201  
  if (startsWith(uri, "/bot/", m)) {
202  
    req.subURI = m.rest();
203  
    S json = req.params.get("json");
204  
    new Map data;
205  
    if (nempty(json)) data = jsonDecodeMap(json);
206  
    data.putAll(withoutKey("json", req.params));
207  
    
208  
    User user;
209  
    S userName = cast data.get("_user");
210  
    if (nempty(userName)) {
211  
      S botToken = cast data.get("_botToken");
212  
      if (botToken == null) ret serveJSON(error := "Need _botToken");
213  
      user = conceptWhereIC User(name := userName);
214  
      if (user == null) ret serveJSON(error := "User not found");
215  
      if (!eq(botToken, getVar(user.botToken))) ret serveJSON(error := "Wrong bot token");
216  
    } else
217  
      user = user(req);
218  
      
219  
    S function = beforeSlashOrAll(req.subURI);
220  
    req.subURI = substring(req.subURI, l(function)+1);
221  
    req.webRequest.noSpam(); // might want to change this
222  
    
223  
    try object servePossiblyUserlessBotFunction(req, function, data, user);
224  
      
225  
    if (user == null) ret serveJSON(error := "Need _user");
226  
227  
    if (eq(function, "getCookie"))
228  
      ret serveJSON(cookie := req.cookie());
229  
      
230  
    if (eq(function, "authTest"))
231  
      ret serveJSON(status := "You are authorized as " + user.name + " " + roundBracket(user.isMaster ? "master user" : "non-master user"));
232  
      
233  
    if (eq(function, "postCount"))
234  
      ret serveJSON(result := countConcepts(UserPost));      
235  
      
236  
    if (eq(function, "listPosts")) {
237  
      LS fields = unnull(stringToStringListOpt tok_identifiersInOrder(data.get("fields")));
238  
      if (!fields.contains("id")) fields.add(0, "id");
239  
      long changedAfter = toLong(data.get("changedAfter"));
240  
      long repliesTo = toLong(data.get("repliesTo"));
241  
      double pollFor = min(bot_maxPollSeconds, toLong(data.get("pollFor"))); // how long to poll (seconds)
242  
      long startTime = sysNow();
243  
244  
      // We're super-anal about catching all changes. This will probably never trigger
245  
      if (changedAfter > 0 && changedAfter == now()) sleep(1);
246  
      
247  
      Cl<UserPost> posts;
248  
      while true {
249  
        if (repliesTo != 0) {
250  
          posts = referencingPosts(getConcept UserPost(repliesTo));
251  
          if (changedAfter != 0) posts = objectsWhereFieldGreaterThan(posts, _modified := changedAfter);
252  
        } else {
253  
          posts = changedAfter == 0 ? list(UserPost)
254  
            : conceptsWithFieldGreaterThan_sorted(UserPost, _modified := changedAfter);
255  
        }
256  
257  
        // return when there are results, no polling or poll expired
258  
        if (nempty(posts) || pollFor == 0 || elapsedSeconds_sysNow(startTime) >= pollFor)
259  
          ret serveJSON_breakAtLevels(2, result := map(posts, post ->
260  
            mapToValues(fields, field -> getPostFieldForBot(post, field))
261  
          ));
262  
263  
        // sleep and try again
264  
        sleep(bot_pollInterval);
265  
      }
266  
    }
267  
268  
    if (eq(function, "createPost")) {
269  
      Either<O[], S> e = createPostArgs(user, data);
270  
      if (e.isB())
271  
        ret serveJSON(error := e.b());
272  
273  
      Pair<UserPost, Bool> post = uniq2 UserPost(e.a());
274  
      if (!post.b) post.a.bump(); // bump if exact same post exists
275  
      ret serveJSON(status := post.b ? "Post created" : "Post existed already, bumped", postID := post.a.id);
276  
    }
277  
278  
    if (eq(function, "editPost")) {
279  
      long postID = toLong(data.get("postID"));
280  
      UserPost post = getConcept UserPost(postID);
281  
      if (post == null)
282  
        ret serveJSON(error := "Post " + postID + " not found");
283  
        
284  
      Either<O[], S> e = createPostArgs(user, data);
285  
      if (e.isB())
286  
        ret serveJSON(error := e.b());
287  
288  
      int changes = cset(post, e.a());
289  
      // TODO: bump if no changes?
290  
      ret serveJSON(changes > 0 ? "Post updated" : "Post updated, no changes", +postID);
291  
    }
292  
293  
    if (eq(function, "deletePosts")) {
294  
      L<Long> ids = allToLong(tok_integersInOrder((S) data.get("ids")));
295  
      new LS results;
296  
      new LS errors;
297  
      fOr (long id : ids) {
298  
        UserPost post = getConceptOpt UserPost(id);
299  
        if (post == null) errors.add("Post " + id + " not found");
300  
        else {
301  
          if (!user.isMaster && neq(post.creator!, user))
302  
            errors.add("Can't delete post " + id + " from other user");
303  
          else {
304  
            deletePost(post);
305  
            results.add("Post " + id + " deleted");
306  
          }
307  
        }
308  
      }
309  
      ret serveJSON(litorderedmap(+results, +errors));    
310  
    }
311  
312  
    ret serveBotFunction(req, function, data, user);
313  
  }
314  
315  
  if (teamPostID != 0 && eq(uri, "/team"))
316  
    ret serveHTMLPost(teamPostID);
317  
318  
  if (startsWith(uri, "/history/", m)) {
319  
    long id = parseLong(m.rest());
320  
    UserPost post = getConcept UserPost(id);
321  
    if (post == null) ret serve404("Post not found");
322  
    ret servePostHistory(post);
323  
  }
324  
325  
  if (startsWith(uri, "/htmlBot/", m)) {
326  
    long id = parseLong(beforeSlashOrAll(m.rest()));
327  
    UserPost post = getConcept UserPost(id);
328  
    if (post == null) ret serve404("Post not found");
329  
    ret doPost(htmlBotURLOnBotServer(id), req.params());
330  
  }
331  
332  
  if (eq(uri, "/deletedPosts")) {
333  
    temp CloseableItIt<S> lines = linesFromFile(deletedPostsFile());
334  
    new L<Map> posts;
335  
    while (lines.hasNext()) {
336  
      S line = lines.next();
337  
      if (isProperlyQuoted(line)) {
338  
        Map map = safeUnstructureMap(unquote(line));
339  
        posts.add(onlyKeys(map, "id", "title", "type"));
340  
      }
341  
    }
342  
    // TODO: show more info, allow restoring posts
343  
    ret serveText("Deleted posts:\n"
344  
      + lines(lmap struct(posts)));
345  
  }
346  
347  
  if (eq(uri, "/formToPost")) {
348  
    User user = currentUser();
349  
    if (user == null) serve500("Please log in first");
350  
    SS params = cloneMap(req.params);
351  
    S type = or2(getAndRemove(params, "_type"), "Form Input");
352  
    S title = getAndRemove(params, "_title");
353  
    L<UserPost> postRefs = map(id -> getConceptOpt UserPost(parseLong(id)), tok_integersInOrder(getAndRemove(params, "_postRefs")));
354  
    LS postRefTags = lines(getAndRemove(params, "_postRefTags"));
355  
    S text = sortLinesAlphaNumIC(mapToLines(params, (k, v) -> urlencode(k) + "=" + urlencode(v)));
356  
    Pair<UserPost,Bool> p = uniq2 UserPost(creator := user,
357  
      +text, +type, +title, +postRefs, +postRefTags);
358  
    ret (p.b ? "Post " + p.a.id + " created" : "Post " + p.a.id + " exists") + hrefresh(2.0, "/" + p.a.id);
359  
  }
360  
361  
  if (eq(uri, "/webPushSubscribe")) {
362  
    MapSO data = jsonDecodeMap(mapGet(req.webRequest.files(), "postData"));
363  
    printVars_str("webPushSubscribe", +data);
364  
    cnew(WebPushSubscription, +data, clientIP := req.webRequest.clientIP());
365  
    ret serveJSON(litmap(message := "success"));
366  
  }
367  
  
368  
  if (eq(uri, "/changePassword")) {
369  
    if (!req.masterAuthed) ret serveAuthForm(rawLink(uri));
370  
    S name = assertNempty(req.get("user"));
371  
    S newPW = assertNempty(req.get("newPW"));
372  
    User user = conceptWhereCI User(+name);
373  
    if (user == null) ret "User not found";
374  
    cset(user, passwordMD5 := SecretValue(hashPW(newPW)));
375  
    ret "PW updated";
376  
  }
377  
378  
  if (eq(uri, "/webPushNotify")) {
379  
    if (!req.masterAuthed) ret serveAuthForm(rawLink(uri));
380  
    S msg = or2(req.params.get("msg"), "Hello user. It is " + localTimeWithSeconds() + " on the server");
381  
    WebPushSubscription sub = getConcept WebPushSubscription(toLong(req.params.get("webPushSubID")));
382  
    if (sub == null) ret serve404("webPushSubID not found");
383  
    
384  
    S mod = dm_require("#1030463/WebPushKeyManager");
385  
    dm_call(mod, "sendNotification", sub.data, msg);
386  
    
387  
    ret "Push message sent";
388  
  }
389  
390  
  if (eq(uri, "/hashPW") && req.masterAuthed)
391  
    ret hashPW(req.params.get("pw"));
392  
} // end of serveOtherPage2
393  
394  
O servePostHistory(UserPost post) {
395  
  ret serveText(unquoteAllLines(loadTextFile(postHistoryFile(post))));
396  
}
397  
398  
// helper for bot functions. return params or error
399  
Either<O[], S> createPostArgs(User user, Map data) {
400  
  S text = cast data.get("text");
401  
  S type = cast data.get("type");
402  
  S title = cast data.get("title");
403  
  S botInfo = or2((S) data.get("botInfo"), "Made by bot");
404  
  LS postRefTags = unnull(lines((S) data.get("refTags")));
405  
  new L<UserPost> postRefs;
406  
  O _refs = data.get("refs");
407  
  if (_refs cast S)
408  
    for (S s : tok_integersInOrder(_refs)) {
409  
      UserPost ref = getConcept UserPost(parseLong(s));
410  
      if (ref == null) ret eitherB("Post " + s + " not found");
411  
      postRefs.add(ref);
412  
    }
413  
  if (empty(text) && empty(title))
414  
    ret eitherB("Need either a text or a title");
415  
416  
  bool isPublic = eqOneOf(data.get("isPublic"), null, true, "1", "t", "true");
417  
  ret eitherA(litparams(creator := user,
418  
    +text, +type, +title, +isPublic, +botInfo, +postRefs, +postRefTags));
419  
}
420  
421  
O serveBotFunction(Req req, S function, Map data, User user) {
422  
  ret serveJSON(error := "You are logged in correctly but function is unknown: " + function);
423  
}
424  
425  
O servePossiblyUserlessBotFunction(Req req, S function, Map data, User user) {
426  
  null;
427  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 5 computer(s): bhatertpkbcr, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, vouqrxazstgt

No comments. add comment

Snippet ID: #1029962
Snippet name: serveOtherPage2 [gazelle.rocks Include]
Eternal ID of this version: #1029962/153
Text MD5: 60137e3ed452a20e0ae7aed04d5139d9
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-10-16 17:18:44
Source code size: 16189 bytes / 427 lines
Pitched / IR pitched: No / No
Views / Downloads: 219 / 2844
Version history: 152 change(s)
Referenced in: [show references]