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

279
LINES

< > BotCompany Repo | #1029963 // gazelle.rocks [working backup before post refs]

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

Uses 1059K of libraries. Click here for Pure Java version (26964L/160K).

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

Author comment

Began life as a copy of #1029913

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1029963
Snippet name: gazelle.rocks [working backup before post refs]
Eternal ID of this version: #1029963/1
Text MD5: 14fccc989acca0a1077c9cae4929c4d8
Transpilation MD5: 8949d69ba8d218ef7a1fcf52c6cd3755
Author: stefan
Category: javax
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-10-23 10:23:09
Source code size: 9564 bytes / 279 lines
Pitched / IR pitched: No / No
Views / Downloads: 195 / 287
Referenced in: [show references]