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

352
LINES

< > BotCompany Repo | #1029968 // gazelle.rocks v2 [abandoned]

JavaX source code (Dynamic Module) [tags: use-pretranspiled] - run with: Stefan's OS

Uses 1059K of libraries. Compilation Failed (27943L/168K).

1  
!7
2  
3  
// trying to move to /admin, but that's complicated
4  
5  
sS passwordSalt() {
6  
  ret loadTextFileOrCreateWithRandomID(javaxSecretDir("password-salt"));
7  
}
8  
9  
concept PostReferenceable {}
10  
11  
concept User > PostReferenceable {
12  
  S name;
13  
  SecretValue<S> passwordMD5;
14  
  S contact; // e.g. mail address
15  
  bool isMaster;
16  
  SecretValue<S> botToken = aSecretGlobalID/*UnlessLoading*/();
17  
18  
  toString { ret nempty(name) ? "User " + name : super.toString(); }
19  
}
20  
21  
extend AuthedDialogID {
22  
  new Ref<User> user; // who is logged in
23  
  bool userMode; // show things as if user was not master
24  
}
25  
26  
concept UserCreatedObject > PostReferenceable {
27  
  new Ref<User> creator;
28  
  S botInfo; // info on how this was made
29  
  //S nameInRepo; // unique by user, for URL
30  
}
31  
32  
concept UserPost > UserCreatedObject {
33  
  S type, title, text;
34  
  bool isPublic = true;
35  
36  
  S text() { ret text; }
37  
38  
  new RefL<PostReferenceable> postRefs;
39  
  new LS postRefTags;
40  
41  
  sS _fieldOrder = "isPublic text type title postRefs";
42  
43  
  toString {
44  
    ret "Post by " + userName(creator!) + ": " + (nempty(title) ? shorten(title) : shorten(text));
45  
  }
46  
}
47  
48  
/*concept BotHandle > UserCreatedObject {
49  
  S globalID = aGlobalIDUnlessLoading();
50  
  S comment;
51  
  SecretValue<S> token;
52  
}*/
53  
54  
!include once #1029928 // new Compact Module include
55  
set flag NoNanoHTTPD.
56  
57  
/*c*/module GazelleExamples > DynNewBot2 {
58  
  void start {
59  
    baseLink = "/admin";
60  
    botName = heading = adminName = "gazelle.rocks";
61  
    set enableUsers;
62  
    set useWebSockets;
63  
    set showRegisterLink;
64  
    unset showTalkToBotLink;
65  
    set alwaysRedirectToHttps;
66  
    passwordSalt(); // make early
67  
    super.start();
68  
    db_mainConcepts().modifyOnCreate = true;
69  
    indexConceptFieldCI(User, "name");
70  
  }
71  
72  
  L<Class> crudClasses(Req req) {
73  
    L<Class> l = super.crudClasses(req);
74  
    l.add(UserPost);
75  
    if (req.masterAuthed)
76  
      l.add(User);
77  
    ret l;
78  
  }
79  
80  
  S serveRegisterForm(SS params) {
81  
    S user = trim(params.get("user"));
82  
    S pw = trim(params.get("f_pw"));
83  
    S pw2 = trim(params.get("pw2"));
84  
    S redirect = params.get("redirect");
85  
    S contact = trim(params.get("contact"));
86  
    
87  
    redirect = dropParamFromURL(redirect, "logout"); // don't log us out right again
88  
    if (empty(redirect)) redirect = baseLink + "/";
89  
90  
    new LS msgs;
91  
    if (nempty(user) || nempty(pw)) {
92  
      lock dbLock();
93  
      if (empty(user)) msgs.add("Please enter a user name");
94  
        else if (l(user) < 4) msgs.add("Minimum user name length: 4 characters");
95  
        else if (l(user) > 30) msgs.add("Maximum user name length: 30 characters");
96  
        else if (hasConceptIC User(name := user))
97  
          msgs.add("This user exists, please choose a different name");
98  
      if (regexpContainsIC("[^a-z0-9\\-\\.]", user))
99  
        msgs.add("Bad characters in user name (please use only a-z, 0-9, - and .)");
100  
      if (empty(pw)) msgs.add("Please enter a password");
101  
        else if (l(pw) < 6) msgs.add("Minimum password length: 6 characters");
102  
      if (neq(pw, pw2)) msgs.add("Passwords don't match");
103  
104  
      if (empty(msgs)) {
105  
        cnew User(name := user, passwordMD5 := SecretValue(hashPW(pw)), +contact);
106  
        ret hrefresh(5.0, redirect) + "User " + user + " created! Redirecting...";
107  
      }
108  
    }
109  
    
110  
    ret hhtml(hhead(htitle("Register new user")
111  
      + hsansserif() + hmobilefix() + hresponstable()) + hbody(hfullcenter(
112  
      h3_htmlEncode(adminName + " | Register new user")
113  
      + hpostform(
114  
          hhidden(+redirect)
115  
          + tag table(
116  
            (empty(msgs) ? "" : tr(td() + td() + td(htmlEncode2_nlToBr(lines_rtrim(msgs)))))
117  
          + tr(td_valignTop("Choose a user name:")
118  
            + td_valignTop(hinputfield(+user) + "<br>" + "(Minimum length 4. Characters allowed: a-z, 0-9, - and .)"))
119  
          + tr(td_valignTop("Choose a password:")
120  
            + td_valignTop(hpassword(f_pw := pw) + "<br>" + "(Minimum length 6 characters)"))
121  
          + tr(td_valignTop("Repeat the password please:") + td(hpassword(+pw2)))
122  
          + tr(td_valignTop("Way to contact you (e.g. e-mail) - optional:") + td(hpassword(+contact)))
123  
          + tr(td() + td(hsubmit("Register"))), class := "responstable"),
124  
        action := baseLink + "/register")
125  
      )));
126  
  }
127  
128  
  bool calcMasterAuthed(Req req) {
129  
    ret super.calcMasterAuthed(req)
130  
      || req.auth != null && req.auth.user.has() && req.auth.user->isMaster;
131  
  }
132  
  
133  
  O serveOtherPage(Req req) {
134  
    S uri = req.uri;
135  
    new Matches m;
136  
    
137  
    if (eq(uri, "/register"))
138  
      ret serveRegisterForm(req.params);
139  
140  
    if (eq(uri, "/becomeMaster") && req.auth != null && req.auth.user != null) {
141  
      S pw = trim(req.params.get("masterPW"));
142  
      if (eq(pw, realPW())) {
143  
        cset(req.auth.user, isMaster := true);
144  
        ret "You are master, " + req.auth.user + "!";
145  
      }
146  
      if (nempty(pw))
147  
        ret "Bad master PW";
148  
      ret hhtml(hhead(htitle("Become master")
149  
        + hsansserif() + hmobilefix() + hresponstable()) + hbody(hfullcenter(
150  
      h3_htmlEncode(adminName + " | Become master")
151  
      + hpostform(
152  
            tag table(
153  
            tr(td_valignTop("You are:")
154  
            + td_valignTop(htmlEncode2(req.auth.user->name)))
155  
          + tr(td_valignTop("Enter the master password:")
156  
            + td_valignTop(hpassword(masterPW := pw)))
157  
          + tr(td() + td(hsubmit("Become master user"))), class := "responstable"),
158  
        action := baseLink + "/becomeMaster")
159  
      )));
160  
    }
161  
162  
    if (swic(uri, "/text/", m)) {
163  
      long id = parseLong(m.rest());
164  
      UserPost post = getConceptOpt UserPost(id);
165  
      if (post == null) ret serve404("Post with ID " + id + " not found");
166  
      if (!post.isPublic) ret serve404("Post is not public");
167  
      ret serveText(post.text());
168  
    }
169  
170  
    S uri2 = dropSlashPrefix(uri);
171  
    if (isInteger(uri2)) {
172  
      long id = parseLong(uri2);
173  
      UserPost post = getConceptOpt UserPost(id);
174  
      if (post == null) ret serve404("Post with ID " + id + " not found");
175  
      ret servePost(post);
176  
    }
177  
178  
    try object serveOtherPage2(req);
179  
      
180  
    ret super.serveOtherPage(req);
181  
  }
182  
183  
  !include #1029962 // serveOtherPage2
184  
185  
  O servePost(UserPost post) {
186  
    if (!post.isPublic) ret serve404("Post is not public");
187  
    new HTMLFramer1 framer;
188  
    framer.title = or2(post.title, shorten(post.text));
189  
    framer.add(p("By " + (post.creator.has() ? htmlEncode2(post.creator->name) : "?") + ". " + renderConceptDate(post)));
190  
    if (nempty(post.type))
191  
      framer.add(p("Post type: "+ htmlEncode2(post.type)));
192  
    framer.add(hsourcecode(post.text));
193  
194  
    // show render link
195  
    if (eqic(post.type, "HTML"))
196  
      framer.add(p(ahref("/html/" + post.id, "Show as HTML page")));
197  
198  
    // show edit link
199  
    if (canEditPost(post))
200  
      framer.add(p(ahref(conceptEditLink(post), "Edit post")));
201  
202  
    // show reply link
203  
    framer.add(p(ahref(replyLink(post), "Reply to this post")));
204  
205  
    // show post refs
206  
    L<PostReferenceable> postRefs = cloneList(post.postRefs);
207  
    if (nempty(postRefs)) {
208  
      framer.add(p("In reference to:"));
209  
      framer.add(ul(cloneMap(postRefs, ref ->
210  
        ahref(objectLink(ref), htmlEncode_nlToBr_withIndents(str(ref))))));
211  
    }
212  
    
213  
    // show referencing posts
214  
    Cl<UserPost> refs = sortedByConceptID(instancesOf UserPost(allBackRefs(post)));
215  
    if (nempty(refs)) {
216  
      framer.add(p("Referenced by posts:"));
217  
      framer.add(ul(lmap objectToHTML(refs)));
218  
    }
219  
    ret framer.render();
220  
  }
221  
222  
  S serveAuthForm(S redirect) {
223  
    ret hAddToHead(super.serveAuthForm(redirect), hInitWebSocket());
224  
  }
225  
  
226  
  // handle user log-in (create/update AuthedDialogID concept)
227  
  O handleAuth(Req req, S cookie) null {
228  
    S name = trim(req.params.get('user)), pw = trim(req.params.get('pw));
229  
    if (nempty(name) && nempty(pw) && nempty(cookie)) {
230  
      Domain authDomain;
231  
      User user = conceptWhereCI User(+name);
232  
      if (user == null)
233  
        ret errorMsg("User " + htmlEncode2(name) + " not found");
234  
      if (!eq(getVar(user.passwordMD5), hashPW(pw)))
235  
        ret errorMsg("Bad password, please try again");
236  
237  
      // auth
238  
      cset(uniq AuthedDialogID(+cookie), restrictedToDomain := null, master := user.isMaster, +user);
239  
240  
      S redirect = req.params.get('redirect);
241  
      if (nempty(redirect))
242  
        ret hrefresh(redirect);
243  
    }
244  
  }
245  
246  
  O serve2(Req req) {
247  
    S userMode = req.params.get("userMode");
248  
    if (nempty(userMode) && req.auth != null)
249  
      req.auth.userMode = eq(userMode, "1");
250  
251  
    ret super.serve2(req);
252  
  }
253  
254  
  S hashPW(S pw) { ret md5(pw + passwordSalt()); }
255  
256  
  bool inMasterMode(Req req) {
257  
    ret req.masterAuthed && !req.auth.userMode;
258  
  }
259  
260  
  MapSO filtersForClass(Class c, Req req) {
261  
    if (c == UserPost) {
262  
      if (!inMasterMode(req))
263  
        ret litmap(creator := req.auth.user!);
264  
    }
265  
    ret super.filtersForClass(c, req);
266  
  }
267  
  
268  
  S loggedInUserDesc(Req req) {
269  
    ret req.auth == null ? "not logged in" : req.auth.user! == null ? (req.masterAuthed ? "master user" : "weird user") : "user " + req.auth.user->name;
270  
  }
271  
272  
  <A extends Concept> HCRUD_Concepts<A> crudData(Class<A> c, Req req) {
273  
    HCRUD_Concepts<A> cc = super.crudData(c, req);
274  
    if (c == UserPost) {
275  
      cc.addRenderer("text", new HCRUD_Data.TextArea(80, 10));
276  
      cc.fieldHelp(
277  
        type := "Type of post (any format, optional)",
278  
        title := "Title for this post (any format, optional)",
279  
        text := "Text contents of this post (any format)",
280  
        nameInRepo := "Name in repository (not really used yet)",
281  
        botInfo := "Info on which bot made this post (if any)");
282  
      cc.massageItemMapForList = (item, map) -> {
283  
        applyFunctionToValue shorten(map, "text", "title", "type", "nameInRepo");
284  
      };
285  
      cc.emptyObject = () -> {
286  
        MapSO item = cc.emptyObject_base();
287  
        item.put(creator := req.auth.user!); // set default creator to current user if in master mode
288  
        ret item;
289  
      };
290  
    }
291  
    ret cc;
292  
  }
293  
294  
  <A extends Concept> HCRUD makeCRUD(Class<A> c, Req req) {
295  
    HCRUD crud = super.makeCRUD(c, req);
296  
    if (c == UserPost) {
297  
      crud.renderCmds = map -> {
298  
        UserPost post = getConcept UserPost(toLong(crud.itemID(map)));
299  
        ret joinNemptiesWithVBar(crud.renderCmds_base(map),
300  
          targetBlank(postLink(post), "Show post"));
301  
      };
302  
      crud.uneditableFields = litset("postRefTags");
303  
    }
304  
    
305  
    if (c == User) {
306  
      crud.allowCreate = false;
307  
      crud.uneditableFields = litset("passwordMD5");
308  
    }
309  
310  
    ret crud;
311  
  }
312  
  
313  
  S authFormHeading() {
314  
    ret h3_htmlEncode("Welcome to the Gazelle AI System")
315  
      + p(hsnippetimg_scaleToWidth(200, #1101482, 425, 257, title := "Gazelle"));
316  
  }
317  
318  
  void makeFramer(Req req) {
319  
    super.makeFramer(req);
320  
    if (req.masterAuthed)
321  
      req.framer.add(p_vbar(
322  
        ahrefIf(req.auth.userMode, appendQueryToURL(baseLink + req.uri, userMode := 0), "Master mode", title := "View pages as master"),
323  
        ahrefIf(!req.auth.userMode, appendQueryToURL(baseLink + req.uri, userMode := 1), "User mode", title := "View pages as user")));
324  
  }
325  
326  
  // link to show post
327  
  S postLink(UserPost post) {
328  
    ret objectLink(post);
329  
  }
330  
  
331  
  S objectLink(Concept c) {
332  
    ret baseLink + "/" + c.id;
333  
  }
334  
335  
  S objectToHTML(Concept c) {
336  
    ret ahref(objectLink(c), htmlEncode_nlToBr_withIndents(str(c)));
337  
  }
338  
339  
  bool canEditPost(UserPost post) {
340  
    Req req = currentReq!;
341  
    ret req != null && req.auth != null && req.auth.user! != null
342  
      && (req.auth.user->isMaster || req.auth.user! == post.creator!);
343  
  }
344  
345  
  S replyLink(UserPost post) {
346  
    ret appendQueryToURL(crudLink(UserPost), cmd := "new", fList_postRefs_0 := post.id);
347  
  }
348  
} // end of module
349  
350  
sS userName(User user) {
351  
  ret user != null ? user.name : "?";
352  
}

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1029968
Snippet name: gazelle.rocks v2 [abandoned]
Eternal ID of this version: #1029968/5
Text MD5: 856eca7b06f25f3c29d0203152beeca9
Transpilation MD5: 047f23829d2300987249196c34d75644
Author: stefan
Category:
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-11-08 13:33:31
Source code size: 11958 bytes / 352 lines
Pitched / IR pitched: No / No
Views / Downloads: 99 / 166
Version history: 4 change(s)
Referenced in: [show references]