1 package Newslash::Model::Users;
2 use Newslash::Model::Base -base;
4 use Newslash::Model::Users::Friends;
5 use Newslash::Model::Users::Util;
6 use Newslash::Model::Users::ACL2;
7 use Newslash::Model::Users::Prefs;
8 use Newslash::Model::Users::Configs;
9 use Newslash::Model::Users::Achievements;
10 use Newslash::Model::Users::Passwords;
11 use Newslash::Model::Users::Info;
12 use Newslash::Model::Users::ACL;
13 use Newslash::Model::Users::Comments;
14 use Newslash::Model::Users::User;
15 use Newslash::Model::Users::Sidebar;
16 use Newslash::Model::Users::Messages;
17 use Newslash::Model::Users::Index;
18 use Newslash::Model::Users::Class;
19 use Newslash::Model::Users::Param;
20 use Newslash::Model::Users::Permissions;
22 use Newslash::Util::Timezones;
26 use Mojo::JSON qw(to_json from_json);
28 use Encode qw(encode_utf8 decode_utf8);
31 # nickname, matchname, and realemail columns doesn't have UNIQUE limitation,
32 # but practically they are unique
37 unique => [qw(nickname realemail matchname)],
38 datetime => [qw(newpasswd_ts journal_last_entry_date)],
39 other => [qw(fakeemail homepage
40 passwd sig seclev newpasswd
42 aliases => { user_id => "uid",
52 return 1 if $self->check_readonly;
55 $self->sidebar->on_start_up;
57 # create `ns_users_config` table
58 $self->configs->_check_and_create_table;
60 # check anonymous user exists
61 my $anon = $self->anonymous_user;
63 $self->hard_delete(1);
64 $self->create("Anonymous Coward", 'AnonymousCoward@example.com', {uid => 1});
65 $self->update(uid => 1, seclev => 0);
66 $self->info->update(uid => 1,
67 realname => "名無しのゴンベエ",
69 $self->warn("create anonymous_user");
72 # convert AC (uid==1) user's data
73 $self->prefs->convert_old_prefs(1);
78 sub friends { return shift->_get_model("_friends", '::Users::Friends'); }
79 sub prefs { return shift->_get_model("_prefs", '::Users::Prefs'); }
80 sub configs { return shift->_get_model("_configs", '::Users::Configs'); }
81 sub achievements { return shift->_get_model("_achievements", '::Users::Achievements'); }
82 sub passwords { return shift->_get_model("_passwords", '::Users::Passwords'); }
83 sub info { return shift->_get_model("_info", '::Users::Info'); }
84 sub acl { return shift->_get_model("_acl", '::Users::ACL'); }
85 sub acl2 { return shift->_get_model("_acl2", '::Users::ACL2'); }
86 sub comments { return shift->_get_model("_comments", '::Users::Comments'); }
87 sub sidebar { return shift->_get_model("_sidebar", '::Users::Sidebar'); }
88 sub messages { return shift->_get_model("_messages", '::Users::Messages'); }
89 sub index { return shift->_get_model("_index", '::Users::Index'); }
90 sub class { return shift->_get_model("_class", '::Users::Class'); }
91 sub param { return shift->_get_model("_param", '::Users::Param'); }
92 sub permissions { return shift->_get_model("_permissions", '::Users::Permissions'); }
95 my ($self, $name, $class) = @_;
96 if (!$self->{$name}) {
97 $self->{$name} = $self->new_instance_of($class);
100 if ($self->transaction_mode && !$$self->{$name}->transaction_mode) {
101 $self->{$name}->use_transaction($self->{_tr_dbh});
104 return $self->{$name};
107 ##### utility functions
110 sub authentification {
111 return shift->passwords->authentification(@_);
114 sub is_anonymous_uid {
115 my ($self, $uid) = @_;
121 return $self->select(uid => 1);
124 #========================================================================
126 =head2 nickname_to_matchname
128 Convert nickname to matchname.
130 nickname consists of almost all alphabet, number, symbol.
132 matchname consists of lower alphabet and number.
136 sub nickname_to_matchname {
140 $nick =~ s/[^a-zA-Z0-9]//g;
148 my $uid = $params->{uid};
151 my $add = $params->{add};
152 my $karma => $params->{karma};
153 return if (!defined $karma && !defined $add);
156 if (defined $karma) {
157 $rs = $self->info->update(uid => $uid, karma => $karma);
158 return if !defined $rs;
162 my $rs1 = $self->info->update(uid => $uid,
163 karma => {add => $add});
164 return if !defined $rs1;
171 ##### select/create/update/delete
175 my $rs = $self->generic_select(params => $params);
176 return if (!defined $rs);
177 return $self->_create_user_object($rs);
180 sub get_user_config {
181 my ($self, $users) = @_;
182 my $target = (ref($users) eq "HASH") ? [$users,] : $users;
184 for my $u (@$target) {
185 $u->{config} = $self->configs->select(id => $u->{uid});
186 $u->{configJSON} = to_json($u->{config});
190 sub _create_user_object {
191 my ($self, $user) = @_;
195 my $result = {%$user};
196 #for my $k (qw{uid nickname realemail fakeemail homepage sig seclev matchname author}) {
197 # $result->{$k} = $user->{$k};
199 # if seclev is greater than 10000, the user is admin
200 # if seclev is 0, the user is Anonymous user
201 $result->{is_admin} = $result->{seclev} >= 10000 ? 1 : 0;
202 $result->{is_login} = $result->{seclev} != 0 ? 1 : 0;
204 #$result->{config} = $self->configs->select(id => $user->{uid});
205 #$result->{configJSON} = to_json($result->{config});
210 if (ref($user) eq 'ARRAY') {
211 my @ret = map { $_fn->($_) } @$user;
215 return $_fn->($user);
224 if ($params->{passwd}) {
225 $params->{passwd} = $self->passwords->encrypt_password($params->{passwd});
228 return $self->generic_update(params => $params);
232 my ($self, $uid) = @_;
233 return if $self->check_readonly;
236 my $dbh = $self->connect_db({AutoCommit => 0,});
237 for my $table (qw(users users_info users_prefs
238 users_comments users_hits users_class
239 users_index users_param)) {
240 my $sql = "DELETE FROM $table WHERE uid = ?";
241 my $rs = $dbh->do($sql, undef, $uid);
242 if (!defined $rs || $rs == 0) {
243 Mojo::Log->new->warn("DELETE FROM $table failed. uid is $uid.");
248 $self->disconnect_db;
253 #========================================================================
270 my ($self, $nickname, $email, $passwd, $opts) = @_;
271 return if $self->check_readonly;
274 # check: nickname is given?
275 if ($nickname =~ m/\A\s+\z/) {
276 $self->set_error("INVALID_ID");
280 my $matchname = $self->nickname_to_matchname($nickname);
282 # check: email is valid?
283 if (!Email::Valid->address($email)) {
284 $self->set_error("INVALID_EMAIL");
288 # check: nickname already exists?
289 my $user = $self->select(nickname => $nickname);
291 $self->set_error("ID_EXISTS");
295 # check: mail already registered?
296 $user = $self->select(realemail => $email);
298 $self->set_error("EMAIL_EXISTS");
302 my $dbh = $self->start_transaction;
307 # create user and set temporary password
309 $passwd = $self->passwords->generate_random_password;
311 my $enc_passwd = $self->passwords->encrypt_password($passwd);
312 my $newpasswd = $opts->{password_token};
314 # build SQL and parameters
315 if ($opts->{password_token}) {
317 INSERT INTO users (uid, realemail, nickname, matchname, seclev, passwd, newpasswd, newpasswd_ts)
318 VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
320 @params = ($opts->{uid},
324 $opts->{seclev} // 1,
326 $opts->{password_token},
331 INSERT INTO users (uid, realemail, nickname, matchname, seclev, passwd)
332 VALUES (?, ?, ?, ?, ?, ?)
334 @params = ($opts->{uid}, $email, $nickname, $matchname, 1, $enc_passwd);
337 $dbh->do($sql, undef, @params);
338 my $uid = $dbh->last_insert_id(undef, undef, undef, undef);
342 $self->set_error($dbh->errstr, $dbh->err);
346 # create users_info, etc.
349 INSERT INTO users_info (uid, lastaccess, created_at, bio)
350 VALUES (?, NOW(), NOW(), ?)
352 $result = $dbh->do($sql, undef, $uid, '');
355 $self->set_error($dbh->errstr, $dbh->err);
359 for my $table (qw{users_prefs users_comments users_hits users_class}) {
360 $sql = "INSERT INTO $table (uid) VALUES (?)";
361 $result = $dbh->do($sql, undef, $uid);
364 $self->set_error($dbh->errstr, $dbh->err);
370 INSERT INTO users_index (uid, story_never_topic, slashboxes, story_always_topic)
373 $result = $dbh->do($sql, undef, $uid, '', '', '');
376 $self->set_error($dbh->errstr, $dbh->err);