LJSUP-17669: Login.bml form refactoring
[livejournal.git] / backup / htdocs / create.bml
blob11be1a9a1b21de3f446087d000c4142588fddf53
1 <?page
2 title=><?_code BML::ml('.title_1', { 'siteabbr' => $LJ::SITENAMEABBREV}); _code?>
3 head<=
4 <?_code
5     return $head;
6 _code?>
8 <script language="javascript">
9 <!--//
10 function tblhilite(tbl, styname) {
11         tbl.className = styname;
13 // -->
14 </script>
15 <=head
16 body<=
17 <?_code
19  my $crumb = $LJ::IS_SSL ? 'securecreatejournal_1' : 'createjournal_1';
20  LJ::set_active_crumb($crumb);
22  return LJ::server_down_html() if ($LJ::SERVER_DOWN);
24  return "<?badinput?>" unless LJ::text_in(\%POST);
26  my $mode = $POST{'mode'};
27  my $code = $POST{'code'} || $GET{'code'};
29  my @getargs;
30  push @getargs,"from=$GET{from}" if $GET{from};
31  push @getargs,"user=$GET{user}" if $GET{user};
32  my $getextra = '?' . join('&', @getargs) if @getargs;
34  if ($LJ::USE_SSL && ! $LJ::IS_SSL && $FORM{'ssl'} ne "no") {
35      return BML::redirect("$LJ::SSLROOT/create.bml$getextra");
36  }
38  # with no mode, decide which screen the user sees first, based
39  # on whether or not this LJ installation lets in free users
40  if ($mode eq "") {
41      $mode = $LJ::USE_ACCT_CODES ?
42          ($code ? "codesubmit" : "entercode")
43              : "getinfo";
44  }
46  my $remote = LJ::get_remote();
48  my %errors;
49  my $error_msg = sub {
50      my $key = shift;
51      my $pre = shift;
52      my $post = shift;
53      my $msg = $errors{$key};
54      return unless $msg;
55      return "$pre $msg $post";
56  };
58  # Flag to indicate they've submitted with 'audio' as the answer to the spambot
59  # challenge.
60  my $wants_audio = 0;
62  # Captcha id
63  my ($capid, $anum);
65  # validate a code they've entered and throw them back to entercode
66  # mode if it's invalid
67  if ($code && $mode eq "submit" || # account codes turned off, but one specified anyway
68      $LJ::USE_ACCT_CODES && ($mode eq "codesubmit" || $mode eq "submit")) # account codes required
69  {
70      my $error;
71      my $userid = 0;  # acceptable userid for double-click protection
72      if ($mode eq "submit") {
73          my $u = LJ::load_user($POST{'user'});
74          $userid = $u->{'userid'};
75      }
76      $errors{'code'} = $error
77          unless (LJ::acct_code_check($code, \$error, $userid));
78      if (%errors) {
79          $mode = "entercode";
80      } elsif ($mode eq "codesubmit") {
81          $mode = "getinfo";
82      }
83  }
85  # MODE: entercode - enter an account code to proceed making an account
86  if ($LJ::USE_ACCT_CODES && $mode eq "entercode")
87  {
88      my $ret;
89      my $v;
91      $ret .= "<form method=\"post\" action=\"create.bml\">\n";
92      $ret .= LJ::html_hidden(mode => 'codesubmit',
93                              ssl => $FORM{'ssl'});
95      $ret .= "<?h1 $ML{'.useacctcodes.welcome'} h1?><?p $ML{'.useacctcodes.entercode'} p?>";
97      $v = LJ::ehtml($code);
98      $ret .= "<?standout Code: <input type=\"text\" name=\"code\" value=\"$v\" size=\"13\" maxlength=\"12\"> <input type=\"submit\" value=\"$ML{'.btn.proceed'}\">";
99      $ret .= $error_msg->('code', '<br>');
100      $ret .= " standout?>";
101      $ret .= "</form>\n";
103      open (REM, "$LJ::HOME/htdocs/inc/account-codes");
104      while (<REM>) {
105          $ret .= $_;
106      }
107      close REM;
109      return $ret;
112 # MODE: submit - if they've given 'audio' as the answer to the spambot-blocker,
113 # reset the mode to 'getinfo' and set the audio flag
114 if ( $LJ::HUMAN_CHECK{create} && $mode eq 'submit' && lc($POST{answer}) eq 'audio' )
116     $mode = 'getinfo';
117     $wants_audio = 1;
120  # MODE: submit - try to create an account.  might change mode
121  #       if there are errors, we'll populate %errors and
122  #       return to "getinfo" mode below
123  SUBMIT:
124  while ($mode eq "submit")  # using while instead of if so we can 'last' out of it
126      return "<b>$ML{'Error'}</b>: $ML{'.error.postrequired'}" unless LJ::did_post();
128      my $user = LJ::canonical_username($POST{'user'});
129      my $email = LJ::trim(lc($POST{'email'}));
131      # setup global things that can be used to modify the user later
132      my $is_underage = 0; # turn on if the user should be marked as underage
133      my $ofage = 0;       # turn on to note that the user is over 13 in actuality
134                           #   (but is_underage might be on which just means that their
135                           #   account is being marked as underage--even if they're old
136                           #   enough [unique cookie check])
138      # reject this email?
139      return LJ::sysban_block(0, "Create user blocked based on email",
140                              { 'new_user' => $user, 'email' => $email, 'name' => $user })
141          if LJ::sysban_check('email', $email);
143      my $dbh = LJ::get_db_writer();
145      if (length($user) > 15) {
146          $errors{'username'} = "$ML{'error.usernamelong'}";
147      }
148      if ($POST{'user'} && ! $user) {
149          $errors{'username'} = "$ML{'error.usernameinvalid'}";
150      }
151      unless ($POST{'user'}) {
152          $errors{'username'} = "$ML{'.error.username.mustenter'}";
153      }
154      foreach my $re ("^system\$", @LJ::PROTECTED_USERNAMES) {
155          next unless ($user =~ /$re/);
157          # you can give people sharedjournal priv ahead of time to create
158          # reserved communities:
159          next if LJ::check_priv($remote, "sharedjournal", $user);
161          $errors{'username'} = "$ML{'.error.username.reserved'}";
162      }
164      # see if they're confused and entered a valid account code
165      # for their username (happens often)
166      if ($LJ::USE_ACCT_CODES && $user =~ /^.....a[ab].....$/) {
167          # see if the acctcode is valid and unused
168          my ($acid, $auth) = LJ::acct_code_decode($user);
169          my $is_valid = $dbh->selectrow_array("SELECT COUNT(*) FROM acctcode ".
170                                               "WHERE acid=? AND rcptid=0",
171                                               undef, $acid);
172          $errors{'username'} = "$ML{'.error.username.iscode'}"
173              if $is_valid;
174      }
176      my $u = LJ::load_user($user);
177      my $second_submit = 0;
179      # do not create if this account name is purged
180      if ($u && $u->{'statusvis'} eq "X") {
181          $errors{'username'} =  BML::ml('.error.username.purged',
182                                         {'aopts' => 'href="/rename/"'} );
183      } elsif ($u) {
184          my $in_use = 1;
186          if ($u->{'email'} eq $POST{'email'}) {
187              if (LJ::login_ip_banned($u)) {
188                  # brute-force possible going on
189              } else {
190                  if ($u->{'password'} eq $POST{'password1'}) {
191                      # okay either they double-clicked the submit button
192                      # or somebody entered an account name that already exists
193                      # with the existing password
194                      $second_submit = 1;
195                      $in_use = 0;
196                  } else {
197                      LJ::handle_bad_login($u);
198                  }
199              }
200          }
202          if ($in_use) {
203              $errors{'username'} = "$ML{'.error.username.inuse'}";
204          }
205      }
207      $POST{'password1'} = LJ::trim($POST{'password1'});
208      $POST{'password2'} = LJ::trim($POST{'password2'});
210      if ($POST{'password1'} ne $POST{'password2'}) {
211          $errors{'password'} = "$ML{'.error.password.nomatch'}";
212      } else {
213          my $checkpass = LJ::run_hook("bad_password",
214                                       {
215                                           'user'     => $user,
216                                           'email'    => $email,
217                                           'password' => $POST{'password1'},
218                                       });
219          if ($checkpass) {
220              $errors{'password'} = "Bad password: $checkpass";
221          }
222      }
223      if (! $POST{'password1'}) {
224          $errors{'password'} = "$ML{'.error.password.blank'}";
225      } elsif (length $POST{'password1'} > 30) {
226          $errors{'password'} = "$ML{'password.max30'}";
227      }
229      unless (LJ::is_ascii($POST{'password1'})) {
230          $errors{'password'} = "$ML{'.error.password.asciionly'}";
231      }
233      ### start COPPA_CHECK
234      # age checking to determine how old they are
235      if ($LJ::COPPA_CHECK) {
236          my $uniq;
237          if ($LJ::UNIQ_COOKIES) {
238              $uniq = LJ::Request->notes('uniq');
239              if ($uniq) {
240                  my $timeof = $dbh->selectrow_array('SELECT timeof FROM underage WHERE uniq = ?', undef, $uniq);
241                  $is_underage = 1 if $timeof && $timeof > 0;
242              }
243          }
245          my ($year, $mon, $day) = ( $POST{"bday_yyyy"}+0, $POST{"bday_mm"}+0, $POST{"bday_dd"}+0 );
246          if ($year < 100 && $year > 0) {
247              $POST{'bday_yyyy'} += 1900;
248              $year += 1900;
249          }
251          # get current time
252          my ($nday, $nmon, $nyear) = (gmtime())[3, 4, 5];
253          $nyear += 1900;
254          $nmon += 1;
256          # require dates in the 1900s (or beyond)
257          if ($year && $mon && $day && $year >= 1900 && $year < $nyear) {
258              # now see how many years back they are
259              my $ofageyear = $year + 13;
260              if ($ofageyear > $nyear) {
261                  $is_underage = 1;
262              } elsif ($ofageyear == $nyear) {
263                  # years match, see if they were born after this month
264                  if ($mon > $nmon) {
265                      $is_underage = 1;
266                  } elsif ($mon == $nmon) {
267                      # now check the day
268                      if ($day > $nday) {
269                          $is_underage = 1;
270                      } else {
271                          $ofage = 1;
272                      }
273                  } else {
274                      $ofage = 1;
275                  }
276              } else {
277                  $ofage = 1;
278              }
279          } else {
280              $errors{'bday'} = "$ML{'.error.birthday.invalid'}";
281          }
283          # note this unique cookie as underage (if we have a unique cookie)
284          if ($is_underage && $uniq) {
285              $dbh->do("REPLACE INTO underage (uniq, timeof) VALUES (?, UNIX_TIMESTAMP())", undef, $uniq);
286          }
287      }
288      ### end COPPA_CHECK
290      if ($LJ::TOS_CHECK && ! $POST{'agree_tos'}) {
291          $errors{'agree_tos'} = $ML{'tos.error'};
292      }
294      # check the email address
295      {
296          my @email_errors;
297          LJ::check_email($email, \@email_errors);
298          if ($LJ::USER_EMAIL and $email =~ /\@\Q$LJ::USER_DOMAIN\E$/i) {
299              push @email_errors, BML::ml(".error.email.lj_domain",
300                                          {domain => $LJ::USER_DOMAIN});
301          }
302          $errors{'email'} = join(", ", @email_errors) if @email_errors;
303      }
305      # Check the turing test answer if it's turned on
306      if ($LJ::HUMAN_CHECK{create}) {
307          ($capid, $anum) = LJ::Captcha::session_check_code($POST{captcha_chal}, $POST{answer});
308          $errors{'captcha'} = $ML{'.captcha.invalid'} unless $capid && $anum;
309      }
310      last SUBMIT if %errors;
312      my $clusterid = ($LJ::DEBUG{'allow_cluster_select'}
313                       ? $POST{'cluster_id'}
314                       : LJ::new_account_cluster()) + 0;
315      die "Cluster 0 not supported" unless $clusterid;
317      my $userid = $u ? $u->{'userid'}+0 : 0;
319      # create user and send email as long as the user didn't double-click submit
320      # (or they tried to re-create a purged account)
321      unless ($second_submit) {
322          my $bdate = sprintf("%04d-%02d-%02d", $POST{bday_yyyy}, $POST{bday_mm}, $POST{bday_dd});
324          my $nu = LJ::User->create_personal(
325                                             'user'       => $user,
326                                             'bdate'      => $bdate,
327                                             'email'      => $email,
328                                             'password'   => $POST{password1},
329                                             'get_ljnews' => $POST{news},
330                                             'inviter'    => $inviter,
331                                             'underage'   => $is_underage,
332                                             'ofage'      => $ofage,
333                                            );
334          return "There was an error creating your account." unless $nu;
336          # Mark the turing test for deletion
337          if ($LJ::HUMAN_CHECK{create}) {
338              LJ::Captcha::expire($capid, $anum, $nu->id);
339          }
341          # if we're using account codes on this site, mark the code as used
342          if ($code) {
343              my ($acid, $auth) = LJ::acct_code_decode($code);
344              $dbh->do("UPDATE acctcode SET rcptid=$nu->id WHERE acid=$acid");
345              if ($dbh->err) { return $dbh->errstr; }
346          }
348          foreach my $friend (@LJ::INITIAL_OPTIONAL_FRIENDS) {
349              my $friendid = LJ::get_userid($friend);
350              LJ::add_friend($nu->id, $friendid) if $friendid and $POST{"initial_optional_friend_$friend"};
351          }
353          # Mark the turing test for deletion
354          if ($LJ::HUMAN_CHECK{create}) {
355              LJ::Captcha::expire($capid, $anum, $nu->id);
356          }
358          # send welcome mail... unless they're underage
359          unless ($is_underage) {
360              my $aa = LJ::register_authaction($nu->id, "validateemail", $email);
362              my $body = BML::ml('email.newacct4.body', {
363                  "sitename" => $LJ::SITENAME,
364                  "regurl" => "$LJ::SITEROOT/confirm/$aa->{'aaid'}.$aa->{'authcode'}",
365                  "journal_base" => $nu->journal_base,
366                  "username" => $nu->user,
367                  "siteroot" => $LJ::SITEROOT,
368                  "sitenameshort" => $LJ::SITENAMESHORT,
369                  "lostinfourl" => "$LJ::SITEROOT/lostinfo.bml",
370                  "editprofileurl" => "$LJ::SITEROOT/manage/profile/",
371                  "searchinterestsurl" => "$LJ::SITEROOT/interests.bml",
372                  "editpicsurl" => "$LJ::SITEROOT/editpics.bml",
373                  "customizeurl" => "$LJ::SITEROOT/customize/style.bml",
374                  "postentryurl" => "$LJ::SITEROOT/update.bml",
375              });
377              LJ::send_mail({
378                  'to' => $email,
379                  'from' => $LJ::ADMIN_EMAIL,
380                  'fromname' => $LJ::SITENAME,
381                  'charset' => 'utf-8',
382                  'subject' => BML::ml('email.newacct.subject', {'sitename' => $LJ::SITENAME}),
383                  'body' => $body,
384              });
385          }
387          # If they were invited then add friend and do joins
388          # Needed to do this after we load the $nu object
389          if ($POST{from}) {
390              my $ivf = LJ::load_user($POST{from});
391              my $friend = $POST{"inviter_friend_$ivf->{user}"};
393              LJ::add_friend($nu->id, $ivf->{userid})
394                  if $friend;
396              my @ijoins;
397              foreach (split(',', $POST{inviter_joins})) {
398                  # Join the comm and add to friends list
399                  next unless $POST{"inviter_join_$_"};
401                  push @ijoins, $_;
403                  my $ci = LJ::get_community_row($_);
404                  next unless $ci;
405                  if ($ci->{membership} eq 'open') {
406                      LJ::join_community($nu->id, $_, 1);
407                    } else {
408                        my $cu = LJ::load_userid($_);
409                        next unless $cu && $nu;
411                        LJ::comm_join_request($cu, $nu);
412                        LJ::add_friend($nu->id, $cu->{userid});
413                    }
414              }
416              # Log who invited them
417              my $ijoinsstr = join(',', @ijoins);
418              LJ::statushistory_add($nu->id, $ivf->{userid}, 'create_from_invite',
419                                    "Recommended: $POST{inviter_joins}\nJoined: $ijoinsstr");
421              # Send the inviter an email
422              my $body = BML::ml('.email.invite.body', {
423                  username   => $ivf->{user},
424                  sitename   => $LJ::SITENAMESHORT,
425                  infolink   => "$LJ::SITEROOT/userinfo.bml?user=$nu->{user}",
426                  invitelink => "$LJ::SITEROOT/friends/invite.bml"
427                  });
429              LJ::send_mail({
430                  'to' => $ivf->{email},
431                  'from' => $LJ::ADMIN_EMAIL,
432                  'fromname' => $LJ::SITENAME,
433                  'charset' => 'utf-8',
434                  'subject' => BML::ml('.email.invite.subject', {'sitename' => $LJ::SITENAMESHORT}),
435                  'body' => $body,
436              });
438          }
440          if ($LJ::TOS_CHECK) {
441              my $err = "";
442              $nu->tosagree_set(\$err)
443                  or return LJ::bad_input($err);
444          }
446          $nu->make_login_session;
448          # local sites may want to override what happens at this point
449          my $redirect = undef;
450          my $stop_output;
451          LJ::run_hooks("create.bml_postsession", {
452              post => \%POST,
453              u => $nu,
454              redirect => \$redirect,
455              ret => \$ret,
456              stop_output => \$stop_output,
457          });
458          return BML::redirect($redirect) if $redirect;
459          return $ret if $stop_output;
461          return BML::redirect("$LJ::SITEROOT/newuser.bml");
462      }
466  if ($mode eq "getinfo" || %errors)
468      my $ret;
469      my $v;
471      if (%errors) {
472          my @errors_order = ('code', 'username', 'email', 'password', 'agree_tos', 'captcha', 'invalidform');
473          my %errors_def;
474          $errors_def{$_} = 1 for @errors_order;
475          foreach my $key (keys %errors) { push @errors_order, $key unless $errors_def{$key}; }
476          $ret .= "<?standout <strong>$ML{'.errors.label'}</strong><ul><li>";
477          $ret .= join ("</li><li>", grep { $_ } map { $errors{$_} } @errors_order);
478          $ret .= "</li></ul> standout?>";
479      }
481      $ret .= "<?p $ML{'.create.text_1'} " .
482               BML::ml('.community', { aopts => "href='$LJ::SITEROOT/community/create.bml'" }) .
483               " p?>" unless %errors;
484      $ret .= "<form action=\"create.bml\" method=\"post\">\n";
485      $ret .= LJ::html_hidden(mode => 'submit',
486                              code => $code,
487                              ssl => $FORM{'ssl'});
489      $ret .= "<ol>";
491      ### username
492      $v = LJ::ehtml($FORM{'user'});
493      $ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.username.box.head'}</div>";
494      $ret .= $error_msg->('username', '<p class="formitemFlag">', '</p>');
495      $ret .= "<div class='formitemDesc'>" .
496              BML::ml(".username.text_1", {'sitename' => $LJ::SITENAME,
497                                         'lc_sitename' => lc($LJ::SITENAME) })
498              . "</div>";
499      $ret .= LJ::html_text({'name' => 'user', 'size' => 15, 'maxlength' => 15, 'value' => $v, raw => 'style="<?loginboxstyle?>"' });
500      $ret .= " ($ML{'.username.maxchars'})<br />\n";
501      $ret .= "<div class='formitemNote'>$ML{'.username.charsallowed_1'}</div>" if (!%errors || exists $errors{'username'});
502      $ret .= "</div></li>";
504      ### email address
505      $v = LJ::ehtml($FORM{'email'});
506      $ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.email.input.head'}</div>";
507      $ret .= $error_msg->('email', '<p class="formitemFlag">', '</p>');
508      $ret .= "<div class='formitemDesc'>" . BML::ml('.email.text_1', {
509          aopts => "target='_new' href='$LJ::SITEROOT/legal/privacy.bml'",
510      }) . "</div>";
511      $ret .= LJ::html_text({'name' => 'email', 'size' => 40, 'maxlength' => 50, 'value' => $v,});
512      $ret .= "</div></li>";
514      # Password
515      $ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.password.input.head1'}</div>\n";
516      $ret .= $error_msg->('password', '<p class="formitemFlag">', '</p>');
517      $ret .= "<div class='formitemFlag'>$ML{'.password.secure'}</div>" if exists $errors{'password'};
518      $ret .= "<div style='float: left;'><div class='formitemDesc'>$ML{'.password.text'}</div>\n<div>";
519      my $pass_value = $errors{'password'} ? "" : $POST{'password1'};
520      $ret .= LJ::html_text({'name' => 'password1', 'size' => 30, 'maxlength' => 31, 'type' => "password",
521                             value => $pass_value, });
522      $ret .= "</div><div class='formitemDesc'>$ML{'.password.input.head2'}</div>\n<div>";
523      $ret .= LJ::html_text({'name' => 'password2', 'size' => 30, 'maxlength' => 31, 'type' => "password",
524                             value => $pass_value, });
525      $ret .= "</div></div><div class='formitemNote' style='float: left; margin-left: 20px; width: 340px'>\n";
526      unless ($LJ::NO_PASSWORD_CHECK) {
527         $ret .= BML::ml('.password.secure_1',
528                 { aopts => "target='_new' href='$LJ::HELPURL{secure_password}'"});
529         $ret .= "<ul style='margin-left: 20px; padding-left: 0px;'>";
530         $ret .= "<li>$ML{'.password.secure.pt1'}</li>\n";
531         $ret .= "<li>$ML{'.password.secure.pt2'}</li>\n";
532         $ret .= "<li>$ML{'.password.secure.pt3'}</li>\n";
533         $ret .= "<li>$ML{'.password.secure.pt4'}</li>\n</ul>\n";
534      }
535      $ret .= "</div><div style='clear: both'></div></li>";
537      if (@LJ::INITIAL_OPTIONAL_FRIENDS) {
538          $ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.initialfriends.heading'}</div>";
539          $ret .= "<div class='formitemDesc'>$ML{'.initialfriends'}</div>";
540          $ret .= "<div>";
541          foreach my $friend (@LJ::INITIAL_OPTIONAL_FRIENDS) {
542              my $selected = LJ::did_post() ? $POST{"initial_optional_friend_$friend"} :
543                  (grep { $_ eq $friend } @LJ::INITIAL_OPTOUT_FRIENDS);
545              $ret .= LJ::html_check({'name' => "initial_optional_friend_$friend",
546                                      'value' => 1,
547                                      'selected' => $selected,
548                                      'id' => "optfriend_$friend",
549                                  });
550              $ret .= "<label for='optfriend_$friend'>" .
551                  LJ::ljuser($friend) . " " . $ML{".initial.friend.$friend"} .
552                  "</label><br />";
553          }
554          $ret .= "</div></div></li>";
555      }
558      # Was this person invited by another user?  If so,
559      # then show interface to add inviter as friend as
560      # well as 10ish communities they are a member of
561      my $ifrom = LJ::did_post() ? $POST{from} : $GET{from};
562      my $inviter;
563      $inviter = LJ::load_user($ifrom)
564          if $ifrom;
566      if ($inviter) {
567          $ret .= "<li><div class='formitem'><div class='formitemName'>Your friend " . LJ::ljuser($inviter) . " welcomes you to ";
568          $ret .= "$LJ::SITENAMESHORT</div>";
570          $ret .= "<div>";
572          $ret .= LJ::html_hidden('from', $inviter->{user});
574          $ret .= LJ::html_check({ name     => "inviter_friend_$inviter->{user}",
575                                   value    => 1,
576                                   selected => LJ::did_post() ? $POST{"inviter_friend_$inviter->{user}"} : 1,
577                                   id       => "inviter_$inviter->{user}",
578                              });
579          $ret .= "<label for='inviter_$inviter->{user}'>" .
580              'add ' . LJ::ljuser($inviter->{user}) . ' as a friend' .
581              "</label><br />";
583          my %comms;
584          unless ($POST{inviter_joins}) {
585              # Load their friendofs to determine community membership
586              my @ids = LJ::get_friendofs($inviter);
587              my %fro;
588              LJ::load_userids_multiple([ map { $_ => \$fro{$_} } @ids ]);
590              foreach my $ulocal (values %fro) {
591                  next unless $ulocal->{'statusvis'} eq 'V';
592                  next unless $ulocal->{'journaltype'} eq 'C';
594                  # TODO: This is bad if they belong to a lot of communities,
595                  # is a db query to global each call
596                  my $ci = LJ::get_community_row($ulocal);
597                  next if $ci->{'membership'} eq 'closed';
599                  # Add to %comms
600                  $ulocal->{istatus} = 'normal';
601                  $comms{$ulocal->{userid}} = $ulocal;
602              }
604              # Get usage information about comms
605              if (%comms) {
606                  my $ids = join(',', map { $_->{userid} } values %comms);
608                  my $dbr = LJ::get_db_reader;
609                  my $sth = $dbr->prepare("SELECT UNIX_TIMESTAMP(timeupdate), UNIX_TIMESTAMP(timecreate), userid ".
610                                          "FROM userusage WHERE userid IN($ids)");
611                  $sth->execute;
613                  while (my @row = $sth->fetchrow_array) {
614                      ($comms{$row[2]}->{'timeupdate'},
615                       $comms{$row[2]}->{'timecreate'}) = ($row[0], $row[1]);
616                  }
617              }
619              # Prune the list by time last updated and make sure to
620              # display comms created in the past 10 days or where
621              # the inviter is a maint or mod
622              my $over30 = 0;
623              my $now = time();
624              foreach my $comm (sort {$b->{timeupdate} <=> $a->{timeupdate}} values %comms) {
625                  if ($now - $comm->{timecreate} <= 86400*10) {
626                      $comm->{istatus} = 'new';
627                      next;
628                  }
630                  my $maintainers = LJ::load_rel_user_cache($comm->{userid}, 'A') || [];
631                  my $moderators  = LJ::load_rel_user_cache($comm->{userid}, 'M') || [];
632                  foreach (@$maintainers, @$moderators) {
633                      if ($_ == $inviter->{userid}) {
634                          $comm->{istatus} = 'mm';
635                          next;
636                      }
637                  }
639                  if ($over30) {
640                      delete $comms{$comm->{userid}};
641                      next;
642                  } else {
643                      if (time() - $comm->{timeupdate} > 86400*30) {
644                          delete $comms{$comm->{userid}};
645                          $over30 = 1;
646                      }
647                  }
648              }
650              # If we still have more than 20 comms, delete any with less than
651              # five members
652              if (scalar keys %comms > 20) {
653                  foreach my $comm (values %comms) {
654                      next unless $comm->{istatus} eq 'normal';
656                      my $ids = LJ::get_friends($comm);
657                      if (scalar values %$ids < 5) {
658                      delete $comms{$comm->{userid}};
659                      }
660                  }
661              }
662          }
664          if (LJ::did_post()) {
665              foreach (split(',', $POST{inviter_joins})) {
666                  my $cj = LJ::load_userid($_);
667                  $comms{$_} = $cj
668                      if $cj;
669              }
670          }
672          if (%comms) {
673              $ret .= "<br />Select which of your friend's communities you'd like to join:<br />";
675              my $i = 0;
676              foreach my $comm (sort { $a->{user} cmp $b->{user} } values %comms) {
677                  last if $i >= 20;
678                  $ret .= LJ::html_check({ name     => "inviter_join_$comm->{userid}",
679                                           value    => 1,
680                                           selected => LJ::did_post() ? $POST{"inviter_join_$comm->{user}"} : 0,
681                                           id       => "inviter_$comm->{user}",
682                                       });
684                  my $le = $comm->{istatus} eq 'mm' ? ' *' : '';
686                  $ret .= "<label for='inviter_$comm->{user}'>" .
687                      LJ::ljuser($comm->{user}) . " - $comm->{name}$le</label><br />";
688                  push @joins, $comm->{userid};
689                  $i++;
690              }
691              $ret .= LJ::html_hidden('inviter_joins', join(',', @joins));
693              $ret .= "<?de A community with a * means that your friend is a maintainer or moderator of that community. de?>";
694          }
695          $ret .= "</div></li>";
696      }
698      if ($LJ::COPPA_CHECK)
699      {
700          $ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.birthday.head'}</div>";
701          $ret .= "<div class='formitemFlag'>$errors{'bday'}</div>" if exists $errors{'bday'};
702          $ret .= "<div class='formitemDesc'>";
703          $ret .= BML::ml('.birthday.question_3', {'aopts' => "target='_new' href='$LJ::SITEROOT/legal/privacy.bml'"});
704          $ret .= "</div><div>";
705          $ret .= "<table><tr><td><span class='formitemName'>$ML{'.birthday.birthdate'}</span></td><td>";
706          $ret .= LJ::html_datetime({ name => 'bday', notime => 1,
707              default => sprintf("%04d-%02d-%02d", $POST{bday_yyyy}, $POST{bday_mm}, $POST{bday_dd}) });
708          $ret .= "</td></tr>";
709          $ret .= "</table></div></div></li>";
711          $ret .= "<div class='formitemNote'>$ML{'.birthday.warning'}</div>";
712      }
714      LJ::run_hooks("create.bml_opts", {
715          post => \%POST,
716          get => \%GET,
717          ret => \$ret,
718      });
720      if ($LJ::TOS_CHECK)
721      {
722          $ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.tos.heading'}</div>";
723           $ret .= LJ::tosagree_widget($POST{agree_tos}, $errors->{agree_tos});
724           $ret .= "</div></li>";
725      }
727      if ($LJ::DEBUG{'allow_cluster_select'}) {
728          $ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.clusterselect.head'}</div>";
729          $ret .= "<div class='formitemDesc'>$ML{'.clusterselect.text'}</div>";
730          $ret .= LJ::html_select({ 'name' => 'cluster_id' },
731                                  "0", "$BML{'.clusterselect.nocluster'}",
732                                  map { $_, BML::ml(".clusterselect.clusternum", {'number' => $_}) } @LJ::CLUSTERS);
734          $ret .= "<div class='formitemNote'>$ML{'.clusterselect.cluster'}</div>";
735          $ret .= "</div></li>";
736      }
738      if ($LJ::HUMAN_CHECK{create}) {
739          my ($captcha_chal, $captcha_sess);
741          my $answer = $POST{answer};
742          undef $answer if $errors{'captcha'} || $wants_audio;
743          $captcha_chal = $POST{captcha_chal};
744          undef $captcha_chal if $errors{'captcha'};
746          $captcha_chal = $captcha_chal || LJ::challenge_generate(900);
747          $captcha_sess = LJ::get_challenge_attributes($captcha_chal);
749          my $try = 0;
750          if ($form->{captcha_chal}) {
751              my $dbcm = LJ::get_cluster_reader();
752              $try = $dbcm->selectrow_array('SELECT trynum FROM captcha_session ' .
753                                            'WHERE sess=?', undef, $captcha_sess);
754          }
756          $ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.captcha.prove'}</div>";
758          # Visual challenge
759          unless ( $wants_audio || $POST{audio_chal} ) {
760              $ret .= "<div class='formitemDesc'>$ML{'.captcha.desc'}</div>";
761              if ($capid && $anum) { # previously entered correctly
762                  $ret .= "<img src='/captcha/image.bml?capid=$capid&amp;anum=$anum' width='175' height='35' />";
763              } else {
764                  $ret .= "<img src='/captcha/image.bml?chal=$captcha_chal&amp;try=$try' width='175' height='35' />";
765              }
766          }
768          # Audio challenge
769          else {
770              $ret .= "<div class='formitemDesc'>$ML{'.captcha.audiodesc'}</div>";
771              if ($capid && $anum) {
772                  $ret .= "<a href='/captcha/audio.bml?capid=$capid&amp;anum=$anum'>$ML{'.captcha.play'}</a>";
773              } else {
774                  $ret .= "<a href='/captcha/audio.bml?chal=$captcha_chal&amp;try=$try'>$ML{'.captcha.play'}</a>";
775              }
776              $ret .= LJ::html_hidden(audio_chal => 1);
777          }
779          $ret .= "<br /><br />$ML{'.captcha.answer'}";
780          $ret .= LJ::html_text({ name => 'answer', size => 15, value => $answer });
781          $ret .= LJ::html_hidden(captcha_chal => $captcha_chal);
782          $ret .= $error_msg->('captcha', '<p class="formitemFlag">', '</p>');
783          $ret .= "</div></li>";
784      }
786      $ret .= "</ol>";
788      $ret .= "<div style='width:600; text-align: center'>";
789      $ret .= "<input type=\"submit\" value=\"$ML{'.btn.create'}\">";
790      $ret .= "</div>";
791      $ret .= "</form>";
793      return $ret;
796  return "$ML{'error.unknownmode'}: <b>$mode</b>";
798 _code?>
799 <=body
800 page?><?_c <LJDEP>
801 link: htdocs/legal/privacy.bml
802 post: htdocs/create.bml, htdocs/manage/profile/index.bml
803 file: htdocs/inc/account-codes
804 hook: post_create
805 </LJDEP> _c?>