root/branches/release-30/lib/MT/CMS/User.pm @ 1369

Revision 1369, 56.8 kB (checked in by bchoate, 22 months ago)

Broke CMS into smaller parts to reduce memory footprint and group code into logical parts. BugId:58666

  • Property svn:keywords set to Id Revision
Line 
1package MT::CMS::User;
2
3use strict;
4
5use MT::Util qw( format_ts relative_date );
6use MT::Author;
7
8sub edit {
9    my $cb = shift;
10    my ($app, $id, $obj, $param) = @_;
11
12    my $author = $app->user;
13
14    if ($id) {
15        # TODO: Populate permissions / blogs for this user
16        # populate blog_loop, permission_loop
17        $param->{is_me} = 1 if $id == $author->id;
18        $param->{editing_other_profile} = 1
19          if !$param->{is_me} && $author->is_superuser;
20
21        $param->{userpic} = $obj->userpic_html();
22
23        require MT::Permission;
24
25        # General permissions...
26        my $sys_perms = MT::Permission->perms('system');
27        foreach (@$sys_perms) {
28            $param->{ 'perm_can_' . $_->[0] } =
29              ($obj->is_superuser || $obj->permissions(0)->has( $_->[0] )) ? 1 : 0;
30        }
31        $param->{perm_is_superuser} = $obj->is_superuser;
32
33        require MT::Auth;
34        if ( $app->user->is_superuser ) {
35            $param->{search_label} = $app->translate('Users');
36            $param->{object_type}  = 'author';
37            $param->{can_edit_username} = 1;
38        }
39        $param->{status_enabled} = $obj->is_active ? 1 : 0;
40        $param->{status_pending} =
41          $obj->status == MT::Author::PENDING() ? 1 : 0;
42        $param->{can_modify_password} =
43          ( $param->{editing_other_profile} || $param->{is_me} )
44          && MT::Auth->password_exists;
45        $param->{can_recover_password} = MT::Auth->can_recover_password;
46        $param->{languages} = $app->languages_list( $obj->preferred_language )
47          unless exists $param->{langauges};
48    } else {
49        $param->{create_personal_weblog} =
50          $app->config->NewUserAutoProvisioning ? 1 : 0
51          unless exists $param->{create_personal_weblog};
52        $param->{can_modify_password}  = MT::Auth->password_exists;
53        $param->{can_recover_password} = MT::Auth->can_recover_password;
54    }
55
56    $app->add_breadcrumb( $app->translate("Users"),
57          $app->user->is_superuser
58        ? $app->uri( mode => 'list_authors' )
59        : undef );
60    my $auth_prefs;
61    if ($obj) {
62        $app->add_breadcrumb( $obj->name );
63        $param->{languages} =
64          $app->languages_list( $obj->preferred_language );
65        $auth_prefs = $obj->entry_prefs;
66    }
67    else {
68        $app->add_breadcrumb( $app->translate("Create User") );
69        $param->{languages} =
70          $app->languages_list( $app->config('DefaultUserLanguage') )
71          unless ( exists $param->{languages} );
72        $auth_prefs = { tag_delim => $app->config->DefaultUserTagDelimiter }
73          unless ( exists $param->{'auth_pref_tag_delim'} );
74    }
75    $param->{text_filters} =
76      $app->load_text_filters( $obj ? $obj->text_format : undef,
77        'comment' );
78    unless ( exists $param->{'auth_pref_tag_delim'} ) {
79        my $delim = chr( $auth_prefs->{tag_delim} );
80        if ( $delim eq ',' ) {
81            $param->{'auth_pref_tag_delim_comma'} = 1;
82        }
83        elsif ( $delim eq ' ' ) {
84            $param->{'auth_pref_tag_delim_space'} = 1;
85        }
86        else {
87            $param->{'auth_pref_tag_delim_other'} = 1;
88        }
89        $param->{'auth_pref_tag_delim'} = $delim;
90    }
91    $param->{'nav_authors'} = 1;
92}
93
94sub edit_role {
95    my $app = shift;
96
97    $app->return_to_dashboard( redirect => 1 ) if $app->param('blog_id');
98
99    my %param  = $_[0] ? %{ $_[0] } : ();
100    my $q      = $app->param;
101    my $author = $app->user;
102    my $id     = $q->param('id');
103
104    require MT::Permission;
105    if ( !$author->is_superuser ) {
106        return $app->error( $app->translate("Invalid request.") );
107    }
108    my $role;
109    require MT::Role;
110    if ($id) {
111        $role = MT::Role->load($id);
112
113        # $param{is_enabled} = $role->is_active;
114        $param{is_enabled}  = 1;
115        $param{name}        = $role->name;
116        $param{description} = $role->description;
117        $param{id}          = $role->id;
118        my $creator = MT::Author->load( $role->created_by )
119          if $role->created_by;
120        $param{created_by} = $creator ? $creator->name : '';
121
122        my $permissions = $role->permissions;
123        if ( defined($permissions) && $permissions ) {
124            my @perms = split ',', $permissions;
125
126            my @roles = MT::Role->load_same(
127                { 'id' => [$id] },
128                { not  => { id => 1 } },
129                1,    # exact match
130                @perms
131            );
132            my @same_perms;
133            for my $other_role (@roles) {
134                push @same_perms,
135                  {
136                    name => $other_role->name,
137                    id   => $other_role->id,
138                  };
139            }
140            $param{same_perm_loop} = \@same_perms if @same_perms;
141        }
142        require MT::Association;
143        $param{user_count} = MT::Author->count( undef, { join  => MT::Association->join_on( 'author_id', { role_id => $id }, { unique => 1 } ) } );
144    }
145
146    my $all_perm_flags = MT::Permission->perms('blog');
147
148    my @p_data;
149    for my $ref (@$all_perm_flags) {
150        $param{ 'have_access-' . $ref->[0] } =
151          ( $role && $role->has( $ref->[0] ) ) ? 1 : 0;
152        $param{ 'prompt-' . $ref->[0] } = $app->translate( $ref->[1] );
153    }
154    $param{saved}          = $q->param('saved');
155    $param{nav_privileges} = 1;
156    $app->add_breadcrumb( $app->translate('Roles'),
157        $app->uri( mode => 'list_roles' ) );
158    if ($id) {
159        $app->add_breadcrumb( $role->name );
160    }
161    else {
162        $app->add_breadcrumb( $app->translate('Create Role') );
163    }
164    $param{screen_class}        = "settings-screen edit-role";
165    $param{object_type}         = 'role';
166    $param{object_label}        = MT::Role->class_label;
167    $param{object_label_plural} = MT::Role->class_label_plural;
168    $param{search_label}        = $app->translate('Users');
169    $app->load_tmpl( 'edit_role.tmpl', \%param );
170}
171
172sub list {
173    my $app = shift;
174    my (%param) = @_;
175
176    $app->return_to_dashboard( redirect => 1 )
177      if $app->param('blog_id');
178
179    my $this_author = $app->user;
180    return $app->return_to_dashboard( permission => 1 )
181      unless $this_author->is_superuser();
182
183    my $this_author_id = $this_author->id;
184    my $list_pref      = $app->list_pref('author');
185    %param             = (%param, %$list_pref);
186    my $limit          = $list_pref->{rows};
187    my $offset         = $app->param('offset') || 0;
188    my $args           = { offset => $offset, sort => 'name' };
189    $args->{limit} = $limit + 1;
190    my %author_entry_count;
191    $param{tab_users} = 1;
192    my ( $filter_col, $val );
193    $param{filter_args} = "";
194    my %terms = ( type => MT::Author::AUTHOR() );
195
196    my $filter_key = $app->param('filter_key');
197    if (   ( $filter_col = $app->param('filter') )
198        && ( $val = $app->param('filter_val') ) )
199    {
200        if ( !exists( $terms{$filter_col} ) ) {
201            $terms{$filter_col} = $val;
202            $param{filter}      = $filter_col;
203            $param{filter_val}  = $val;
204            $param{filter_args} = "&filter=" . encode_url($filter_col) . "&filter_val=" . encode_url($val);
205        }
206    } elsif ($filter_key) {
207        my $filters = $app->registry("list_filters", "sys_user") || {};
208        if ( my $filter = $filters->{$filter_key} ) {
209            if ( my $code = $filter->{code}
210                || $app->handler_to_coderef( $filter->{handler} ) )
211            {
212                $param{filter} = 1;
213                $param{filter_key}   = $filter_key;
214                $param{filter_label} = $filter->{label};
215                $code->( \%terms, $args );
216            }
217        }
218    }
219    $param{can_create_user}           = $this_author->is_superuser;
220    $param{synchronized}              = 1 if $app->param('synchronized');
221    $param{error}                     = 1 if $app->param('error');
222    my $author_iter = MT::Author->load_iter( \%terms, $args );
223    my ( @data, %authors, %entry_count_refs );
224    my $entry_class = $app->model('entry');
225    while ( my $au = $author_iter->() ) {
226        my $row = $au->column_values;
227        $row->{name} = '(unnamed)' if !$row->{name};
228        $authors{ $au->id } ||= $au;
229        $row->{id}    = $au->id;
230        $row->{email} = ''
231          unless ( !defined $au->email )
232          or ( $au->email =~ /@/ );
233        $row->{entry_count}          = 0;
234        $entry_count_refs{ $au->id } = \$row->{entry_count};
235        $row->{is_me}                = $au->id == $this_author_id;
236        $row->{has_edit_access}      = $this_author->is_superuser;
237        $row->{status_enabled}       = $au->is_active;
238        $row->{status_pending}       = $au->status == MT::Author::PENDING();
239
240        if ( $row->{created_by} ) {
241            my $parent_author = $authors{ $au->created_by } ||=
242              MT::Author->load( $au->created_by )
243              if $au->created_by;
244            if ($parent_author) {
245                $row->{created_by_name} = $parent_author->name;
246            }
247            else {
248                $row->{created_by_name} = $app->translate('(user deleted)');
249            }
250        }
251        push @data, $row;
252        last if scalar @data == $limit;
253    }
254    if ( keys %entry_count_refs ) {
255        my $author_entry_count_iter =
256          MT::Entry->count_group_by(
257            { author_id => [ keys %entry_count_refs ] },
258            { group     => ['author_id'] } );
259        while ( my ( $count, $author_id ) = $author_entry_count_iter->() ) {
260            ${ $entry_count_refs{$author_id} } = $count;
261        }
262    }
263    $param{object_loop} = \@data;
264    $param{object_type} = 'author';
265    $param{object_label} = $app->model('author')->class_label;
266    $param{object_label_plural} = $app->model('author')->class_label_plural;
267    if ( $this_author->is_superuser() ) {
268        $param{search_label} = $app->translate('Users');
269        $param{is_superuser} = 1;
270    }
271
272    $param{limit}      = $limit;
273    $param{list_start} = $offset + 1;
274    delete $args->{limit};
275    delete $args->{offset};
276    $param{list_total} = MT::Author->count( \%terms, $args );
277    $param{list_end}        = $offset + ( scalar @data );
278    $param{next_offset_val} = $offset + ( scalar @data );
279    $param{next_offset} = $param{next_offset_val} < $param{list_total} ? 1 : 0;
280    $param{next_max}    = $param{list_total} - $limit;
281    $param{next_max}    = 0 if ( $param{next_max} || 0 ) < $offset + 1;
282    if ( $offset > 0 ) {
283        $param{prev_offset}     = 1;
284        $param{prev_offset_val} = $offset - $limit;
285        $param{prev_offset_val} = 0 if $param{prev_offset_val} < 0;
286    }
287    $param{offset}            = $offset;
288    $param{list_filters}      = $app->list_filters("sys_user");
289    $param{list_noncron}      = 1;
290    $param{saved_deleted}     = $app->param('saved_deleted');
291    $param{saved_removed}     = $app->param('saved_removed');
292    $param{author_ldap_found} = $app->param('author_ldap_found');
293    $param{saved}             = $app->param('saved');
294    my $status = $app->param('saved_status');
295    $param{"saved_status_$status"} = 1 if $status;
296    $param{unchanged} = $app->param('unchanged');
297    $app->load_list_actions( 'author', \%param );
298    $param{page_actions} =
299      $app->page_actions('list_authors');
300
301    $param{nav_authors} = 1;
302    $param{listing_screen} = 1;
303    $param{screen_id} = "list-author";
304    my $tmpl_file = $param{output} || 'list_author.tmpl';
305    $app->load_tmpl( $tmpl_file, \%param );
306}
307
308# list of all users, regardless of commenter/author on a blog
309sub list_member {
310    my $app = shift;
311
312    my $blog_id = $app->param('blog_id');
313    $app->return_to_dashboard( redirect => 1 )
314      unless $blog_id;
315
316    my $blog  = $app->blog;
317    my $user  = $app->user;
318    my $perms = $app->permissions;
319    return $app->return_to_dashboard( permission => 1 )
320      unless $user->is_superuser() || ($perms && $perms->can_administer_blog());
321
322    my $super_user = 1 if $user->is_superuser();
323    my $args       = {};
324    my $terms      = {};
325    my $param = { list_noncron => 1 };
326    $args->{join} =
327      MT::Permission->join_on( 'author_id', { blog_id => $blog_id, } );
328
329    $args->{sort_order} = 'created_on';
330    $args->{direction}  = 'descend';
331
332    $param->{saved} = 1 if $app->param('saved');
333    $param->{search_label} = $app->translate('Users');
334    $param->{object_type} = 'author';
335
336    my $all_perms;
337    my @all_perms = @{ MT::Permission->perms() };
338    $all_perms = [@all_perms];
339    foreach (@$all_perms) {
340        $_->[1] = $app->translate( $_->[1] );
341    }
342
343    require MT::Association;
344    require MT::Role;
345    my @all_roles = MT::Role->load( undef, { sort => 'name' });
346
347    my $sel_role = 0;
348    my $filter = $app->param('filter') || '';
349    if ($filter eq 'role') {
350        my $val = scalar $app->param('filter_val');
351        if ($val) {
352            $sel_role = $val;
353            $args->{join} = MT::Association->join_on('author_id', { blog_id => $blog_id, role_id => $val });
354        }
355    }
356    elsif ($filter eq 'status') {
357        my $val = $app->param('filter_val');
358        if ($val eq 'disabled') {
359            $terms->{status} = 2;
360        }
361        elsif ($val eq 'pending') {
362            $terms->{status} = 3;
363        }
364        else {
365            $terms->{status} = 1;
366        }
367    }
368
369    my @role_loop;
370    foreach my $r (@all_roles) {
371        push @role_loop, { role_id => $r->id, role_name => $r->name, selected => $r->id == $sel_role };
372    }
373    $param->{role_loop} = \@role_loop;
374    my $hasher = sub {
375        my ( $obj, $row ) = @_;
376        if ( ( $row->{email} || '' ) !~ m/@/ ) {
377            $row->{email} = '';
378        }
379        if ( $row->{created_by} ) {
380            if ( my $created_by = MT::Author->load( $row->{created_by} ) ) {
381                $row->{created_by} = $created_by->name;
382            }
383            else {
384                $row->{created_by} = $app->translate('*User deleted*');
385            }
386        }
387        $row->{is_me}           = $row->{id} == $user->id;
388        $row->{has_edit_access} = 1 if $super_user;
389        $row->{usertype_author} = 1 if $obj->type == MT::Author::AUTHOR();
390        if ( $obj->type == MT::Author::COMMENTER() ) {
391            $row->{usertype_commenter} = 1;
392            $row->{status_trusted} = 1 if $obj->is_trusted($blog_id);
393            if ($row->{name} =~ m/^[a-f0-9]{32}$/) {
394                $row->{name} = $row->{nickname} || $row->{url};
395            }
396        }
397        $row->{status_enabled} = 1 if $obj->status == 1;
398        my @roles = MT::Role->load(undef, { join => MT::Association->join_on('role_id', { author_id => $row->{id}, blog_id => $blog_id }, { unique => 1 })});
399        my @role_loop;
400        foreach my $role (@roles) {
401            my @perms;
402            foreach (@$all_perms) {
403                next unless length( $_->[1] || '' );
404                push @perms, $app->translate( $_->[1] )
405                  if $role->has( $_->[0] );
406            }
407            my $role_perms = join(", ", @perms);
408            push @role_loop, { role_name => $role->name, role_id => $role->id, role_perms => $role_perms };
409        }
410        $row->{role_loop} = \@role_loop;
411        $row->{auth_icon_url} = $obj->auth_icon_url;
412    };
413    $param->{screen_id} = "list-member";
414
415    return $app->listing(
416        {
417            type     => 'user',
418            template => 'list_member.tmpl',
419            terms    => $terms,
420            params   => $param,
421            args     => $args,
422            code     => $hasher,
423        }
424    );
425}
426
427sub list_association {
428    my $app = shift;
429
430    my $blog_id   = $app->param('blog_id');
431    my $author_id = $app->param('author_id');
432    my $role_id   = $app->param('role_id');
433
434    my $this_user = $app->user;
435    if ( !$this_user->is_superuser ) {
436        if (
437            (
438                   !$blog_id
439                || !$this_user->permissions($blog_id)->can_administer_blog
440            )
441            && ( !$author_id || ( $author_id != $this_user->id ) )
442          )
443        {
444            return $app->errtrans("Permission denied.");
445        }
446    }
447
448    my ( $user, $role );
449    $app->error(undef);
450
451    if ($author_id) {
452        $app->add_breadcrumb( $app->translate('Users'),
453              $app->user->is_superuser
454            ? $app->uri( mode => 'list_authors' )
455            : undef );
456        if ( 'PSEUDO' ne $author_id ) {
457            my $author_class = $app->model('author');
458            $user = $author_class->load($author_id);
459            $app->add_breadcrumb(
460                $user->name,
461                $app->uri(
462                    mode => 'view',
463                    args => { _type => 'author', id => $author_id }
464                )
465            );
466        }
467        else {
468            $app->add_breadcrumb( $app->translate('(newly created user)') );
469        }
470        $app->add_breadcrumb( $app->translate('User Associations') );
471    }
472    if ($role_id) {
473        my $role_class = $app->model('role') or return;
474        $role = $role_class->load($role_id);
475        $app->add_breadcrumb( $app->translate("Roles"),
476            $app->uri( mode => "list_roles" ) );
477        $app->add_breadcrumb(
478            $role->name,
479            $app->uri(
480                mode => 'edit_role',
481                args => { _type => 'role', id => $role_id }
482            )
483        );
484        $app->add_breadcrumb( $app->translate("Role Users & Groups") );
485    }
486    if ( !$role_id  && !$author_id ) {
487        if ($blog_id) {
488            $app->add_breadcrumb( $app->translate("Users") );
489        }
490        else {
491            $app->add_breadcrumb( $app->translate("Associations") );
492        }
493    }
494
495    my $pref = $app->list_pref('association');
496    my $all_perms;
497    if ( $pref->{view_expanded} ) {
498        my @all_perms = @{ MT::Permission->perms() };
499        $all_perms = [@all_perms];
500        foreach (@$all_perms) {
501            $_->[1] = $app->translate( $_->[1] );
502        }
503    }
504
505    # Supplies additional parameters for the row being listed
506    my %users;
507    $users{ $this_user->id } = $this_user;
508    my $hasher = sub {
509        my ( $obj, $row ) = @_;
510        if ( my $user = $obj->user ) {
511            $row->{user_id}   = $user->id;
512            $row->{user_name} = $user->name;
513        }
514        if ( my $role = $obj->role ) {
515            $row->{role_name} = $role->name;
516
517            # populate permissions for the expanded view
518            if ( $pref->{view_expanded} ) {
519                my @perms;
520                foreach (@$all_perms) {
521                    next unless length( $_->[1] || '' );
522                    push @perms, { name => $app->translate( $_->[1] ) }
523                      if $role->has( $_->[0] );
524                }
525                $row->{perm_loop} = \@perms;
526            }
527        }
528        else {
529            $row->{role_name} = $app->translate("(Custom)");
530        }
531        if ( my $blog = $obj->blog ) {
532            $row->{blog_name} = $blog->name;
533        }
534        if ( my $ts = $obj->created_on ) {
535            $row->{created_on_formatted} =
536              format_ts( MT::App::CMS::LISTING_DATE_FORMAT(), $ts, $obj->blog, $app->user ? $app->user->preferred_language : undef );
537            $row->{created_on_time_formatted} =
538              format_ts( MT::App::CMS::LISTING_TIMESTAMP_FORMAT(), $ts, $obj->blog, $app->user ? $app->user->preferred_language : undef );
539            $row->{created_on_relative} =
540              relative_date( $ts, time, $obj->blog );
541        }
542        if ( $row->{created_by} ) {
543            my $created_user = $users{ $row->{created_by} } ||=
544              MT::Author->load( $row->{created_by} );
545            if ($created_user) {
546                $row->{created_by} = $created_user->name;
547            }
548            else {
549                $row->{created_by} = $app->translate('(user deleted)');
550            }
551        }
552    };
553    $app->model('association') or return;
554    my $types;
555    if ( !$author_id && !$blog_id ) {
556        $types = [
557            MT::Association::USER_BLOG_ROLE(),
558            MT::Association::USER_ROLE(),
559        ];
560    }
561    elsif ( !$author_id ) {
562        $types = [
563            MT::Association::USER_BLOG_ROLE(),
564        ];
565    }
566    elsif ($author_id) {
567        $types =
568          [ MT::Association::USER_BLOG_ROLE(), MT::Association::USER_ROLE() ];
569    }
570
571    my $pre_build = sub {
572        my ($param) = @_;
573        my $data = $param->{object_loop} || [];
574
575        #TODO: handle group_view
576        if ( $param->{user_view}
577            && ( 'PSEUDO' ne $param->{edit_author_id} ) )
578        {
579
580            # don't merge
581        }
582        elsif ( $param->{role_view} ) {
583            _merge_default_assignments( $app, $data, $hasher, 'role',
584                $param->{role_id} );
585        }
586        elsif ( $param->{blog_view} ) {
587            _merge_default_assignments( $app, $data, $hasher, 'blog',
588                $param->{blog_id} );
589        }
590        else {
591            _merge_default_assignments( $app, $data, $hasher, 'all' );
592        }
593    };
594
595    return $app->listing(
596        {
597            args  => { sort => 'created_on', direction => 'descend' },
598            type  => 'association',
599            code  => $hasher,
600            terms => {
601                type => $types,
602                $author_id ? ( author_id => $author_id ) : (),
603                $blog_id   ? ( blog_id   => $blog_id )   : (),
604                $role_id   ? ( role_id   => $role_id )   : (),
605            },
606            pre_build => $pre_build,
607            params => {
608                can_create_association => $app->user->is_superuser || ( $blog_id
609                    && $app->user->permissions($blog_id)->can_administer_blog ),
610                has_expanded_mode => 1,
611                nav_privileges =>
612                  ( $author_id || $blog_id ? 0 : 1 ) || $role_id,
613                nav_authors => ( $author_id || $blog_id ? 1 : 0 )
614                  && !$role_id,
615                blog_view     => $blog_id   ? 1 : 0,
616                user_view     => $author_id ? 1 : 0,
617                role_view     => $role_id   ? 1 : 0,
618                $role_id
619                ? (
620                    role_id   => $role_id,
621                    role_name => $role->name,
622                  )
623                : (),
624                $author_id
625                ? (
626                    edit_author_id   => $author_id,
627                    edit_name => $user
628                    ? ( $user->nickname ? $user->nickname : $user->name )
629                    : $app->translate('(newly created user)'),
630                    edit_object => $app->translate('The user'),
631                    group_count => $user ? $user->group_count() : 0,
632                    status_enabled => $user ? ( $user->is_active ? 1 : 0 ) : 0,
633                    status_pending => $user
634                    ? ( $user->status == MT::Author::PENDING() ? 1 : 0 )
635                    : 0,
636                  )
637                : (),
638                saved         => $app->param('saved')         || 0,
639                saved_deleted => $app->param('saved_deleted') || 0,
640                usergroup_view => !$author_id  && !$role_id,
641                blog_id => $blog_id,
642                search_label => $app->translate('Users'),
643                object_type  => 'association',
644                pt_name => $app->translate('User'),
645                screen_id => "list-associations",
646            },
647        }
648    );
649}
650
651sub list_role {
652    my $app = shift;
653
654    $app->return_to_dashboard( redirect => 1 ) if $app->param('blog_id');
655
656    my $pref = $app->list_pref('role');
657    my $all_perms;
658    if ( $pref->{view_expanded} ) {
659        my @all_perms = @{ MT::Permission->perms() };
660        $all_perms = [@all_perms];
661        foreach (@$all_perms) {
662            $_->[1] = $app->translate( $_->[1] );
663        }
664    }
665
666    my $author_class = $app->model('author');
667    my $assoc_class  = $app->model('association');
668    my $hasher       = sub {
669        my ( $obj, $row ) = @_;
670        my $user_count = $assoc_class->count(
671            {
672                role_id   => $obj->id,
673                author_id => [ 1, undef ],
674            },
675            {
676                unique     => 'author_id',
677                range_incl => { author_id => 1 },
678            }
679        );
680        $row->{members} = $user_count;
681        $row->{weblogs} = $assoc_class->count(
682            {
683                role_id => $obj->id,
684                blog_id => [ 1, undef ],
685            },
686            {
687                unique     => 'blog_id',
688                range_incl => { blog_id => 1 },
689            }
690        );
691        if ( $obj->created_by ) {
692            my $user = $author_class->load( $obj->created_by );
693            $row->{created_by} = $user ? $user->name : '';
694        }
695        else {
696            $row->{created_by} = '';
697        }
698
699        # populate permissions for the expanded view
700        if ( $pref->{view_expanded} ) {
701            my @perms;
702            foreach (@$all_perms) {
703                next unless length( $_->[1] || '' );
704                push @perms, { name => $app->translate( $_->[1] ) }
705                  if $obj->has( $_->[0] );
706            }
707            $row->{perm_loop} = \@perms;
708        }
709    };
710    unless ( $app->user->is_superuser() ) {
711        return $app->errtrans("Permission denied.");
712    }
713    $app->add_breadcrumb( $app->translate("Roles") );
714    $app->listing(
715        {
716            args   => { sort => 'name' },
717            type   => 'role',
718            code   => $hasher,
719            params => {
720                nav_privileges    => 1,
721                list_noncron      => 1,
722                can_create_role   => $app->user->is_superuser,
723                has_expanded_mode => 1,
724                search_label      => $app->translate('Users'),
725                object_type       => 'role',
726                screen_id         => 'list-role',
727            },
728        }
729    );
730}
731
732sub save_role {
733    my $app = shift;
734    my $q   = $app->param;
735    $app->validate_magic()   or return;
736    $app->user->is_superuser or return $app->errtrans("Invalid request.");
737
738    my $id    = $q->param('id');
739    my @perms = $q->param('permission');
740    my $role;
741    require MT::Role;
742    $role = $id ? MT::Role->load($id) : MT::Role->new;
743    my $name = $q->param('name') || '';
744    $name =~ s/(^\s+|\s+$)//g;
745    return $app->errtrans("Role name cannot be blank.")
746      if $name eq '';
747
748    my $role_by_name = MT::Role->load( { name => $name } );
749    if ( $role_by_name && ( ( $id && ( $role->id != $id ) ) || !$id ) ) {
750        return $app->errtrans("Another role already exists by that name.");
751    }
752    if ( !@perms ) {
753        return $app->errtrans("You cannot define a role without permissions.");
754    }
755
756    $role->name( $q->param('name') );
757    $role->description( $q->param('description') );
758    $role->clear_full_permissions;
759    $role->set_these_permissions(@perms);
760    if ( $role->id ) {
761        $role->modified_by( $app->user->id );
762    }
763    else {
764        $role->created_by( $app->user->id );
765    }
766    $role->save or return $app->error( $role->errstr );
767
768    my $url;
769    $url = $app->uri(
770        'mode' => 'edit_role',
771        args   => { id => $role->id, saved => 1 }
772    );
773    $app->redirect($url);
774}
775
776sub enable_object {
777    my $app = shift;
778    set_object_status( $app, MT::Author::ACTIVE() );
779}
780
781sub disable_object {
782    my $app = shift;
783    set_object_status( $app, MT::Author::INACTIVE() );
784}
785
786sub set_object_status {
787    my ($app, $new_status) = @_;
788
789    $app->validate_magic() or return;
790    return $app->error( $app->translate('Permission denied.') )
791      unless $app->user->is_superuser;
792    return $app->error( $app->translate("Invalid request.") )
793      if $app->request_method ne 'POST';
794
795    my $q    = $app->param;
796    my $type = $q->param('_type');
797    return $app->error( $app->translate('Invalid type') )
798      unless ( $type eq 'user' )
799      || ( $type eq 'author' )
800      || ( $type eq 'group' );
801
802    my $class = $app->model($type);
803
804    my @sync;
805    my $saved = 0;
806    for my $id ( $q->param('id') ) {
807        next unless $id;    # avoid 'empty' ids
808        my $obj = $class->load($id);
809        next unless $obj;
810        if ( ( $obj->id == $app->user->id ) && ( $type eq 'author' ) ) {
811            next;
812        }
813        next if $new_status == $obj->status;
814        $obj->status($new_status);
815        $obj->save;
816        $saved++;
817        if ( $type eq 'author' ) {
818            if ( $new_status == MT::Author::ACTIVE() ) {
819                push @sync, $obj;
820            }
821        }
822    }
823    my $unchanged = 0;
824    if (@sync) {
825        MT::Auth->synchronize_author( User => \@sync );
826        foreach (@sync) {
827            if ( $_->status != MT::Author::ACTIVE() ) {
828                $unchanged++;
829            }
830        }
831    }
832    if ( $saved && ( $saved > $unchanged ) ) {
833        $app->add_return_arg(
834            saved_status => ( $new_status == MT::Author::ACTIVE() )
835            ? 'enabled'
836            : 'disabled'
837        );
838    }
839    $app->add_return_arg( is_power_edit => 1 )
840      if $q->param('is_power_edit');
841    $app->add_return_arg( unchanged => $unchanged )
842      if $unchanged;
843    $app->call_return;
844}
845
846sub upload_userpic {
847    my $app = shift;
848
849    my ($asset, $bytes) = $app->_upload_file(
850        @_,
851        require_type => 'image',
852    );
853    return if !defined $asset;
854    return $asset if !defined $bytes;  # whatever it is
855
856    ## TODO: should this be layered into _upload_file somehow, so we don't
857    ## save the asset twice?
858    my $user_id = $app->param('user_id');
859
860    $asset->tags('@userpic');
861    $asset->created_by($user_id);
862    $asset->save;
863
864    $app->forward( 'asset_userpic', { asset => $asset, user_id => $user_id } );
865}
866
867sub cfg_system_users {
868    my $app = shift;
869    my %param;
870    if ( $app->param('blog_id') ) {
871        return $app->return_to_dashboard( redirect => 1 );
872    }
873
874    return $app->errtrans("Permission denied.")
875      unless $app->user->is_superuser();
876    my $cfg = $app->config;
877    $app->add_breadcrumb( $app->translate('General Settings') );
878    $param{nav_config}   = 1;
879
880    $param{nav_settings} = 1;
881    $param{languages} =
882      $app->languages_list( $app->config('DefaultUserLanguage') );
883    my $tag_delim = $app->config('DefaultUserTagDelimiter') || ord(',');
884    if ( $tag_delim eq ord(',') ) {
885        $tag_delim = 'comma';
886    }
887    elsif ( $tag_delim eq ord(' ') ) {
888        $tag_delim = 'space';
889    }
890    else {
891        $tag_delim = 'comma';
892    }
893    $param{"tag_delim_$tag_delim"} = 1;
894
895    ( my $tz = $app->config('DefaultTimezone') ) =~ s![-\.]!_!g;
896    $tz =~ s!_00$!!;
897    $param{ 'server_offset_' . $tz } = 1;
898
899    $param{default_site_root} = $app->config('DefaultSiteRoot');
900    $param{default_site_url}  = $app->config('DefaultSiteURL');
901    $param{personal_weblog_readonly} =
902      $app->config->is_readonly('NewUserAutoProvisioning');
903    $param{personal_weblog} = $app->config->NewUserAutoProvisioning ? 1 : 0;
904    if ( my $id = $param{new_user_template_blog_id} =
905        $app->config('NewUserTemplateBlogId') || '' )
906    {
907        my $blog = MT::Blog->load($id);
908        if ($blog) {
909            $param{new_user_template_blog_name} = $blog->name;
910        }
911        else {
912            $app->config( 'NewUserTemplateBlogId', undef, 1 );
913            $cfg->save_config();
914            delete $param{new_user_template_blog_id};
915        }
916    }
917    $param{system_email_address} = $cfg->EmailAddressMain;
918    $param{saved}                = $app->param('saved');
919    $param{error}                = $app->param('error');
920    $param{screen_class}         = "settings-screen system-general-settings";
921    my $registration = $cfg->CommenterRegistration;
922    if ( $registration->{Allow} ) {
923        $param{registration} = 1;
924        if ( my $ids = $registration->{Notify} ) {
925            my @ids = split ',', $ids;
926            my @sysadmins = MT::Author->load(
927                {
928                    id   => \@ids,
929                    type => MT::Author::AUTHOR()
930                },
931                {
932                    join => MT::Permission->join_on(
933                        'author_id',
934                        {
935                            permissions => "\%'administer'\%",
936                            blog_id     => '0',
937                        },
938                        { 'like' => { 'permissions' => 1 } }
939                    )
940                }
941            );
942            my @names;
943            foreach my $a (@sysadmins) {
944                push @names, $a->name . '(' . $a->id . ')';
945            }
946            $param{notify_user_id} = $ids;
947            $param{notify_user_name} = join ',', @names;
948        }
949    }
950    $app->load_tmpl( 'cfg_system_users.tmpl', \%param );
951}
952
953sub save_cfg_system_users {
954    my $app = shift;
955    $app->validate_magic or return;
956    return $app->errtrans("Permission denied.")
957      unless $app->user->is_superuser();
958
959    my $tmpl_blog_id = $app->param('new_user_template_blog_id') || '';
960    if ( $tmpl_blog_id =~ m/^\d+$/ ) {
961        MT::Blog->load($tmpl_blog_id)
962          or return $app->error(
963            $app->translate(
964                "Invalid ID given for personal blog clone source ID.")
965          );
966    }
967    else {
968        if ( $tmpl_blog_id ne '' ) {
969            return $app->error(
970                $app->translate(
971                    "Invalid ID given for personal blog clone source ID.")
972            );
973        }
974    }
975
976    my $cfg = $app->config;
977    my $tz  = $app->param('default_time_zone');
978    $app->config( 'DefaultTimezone', $tz || undef, 1 );
979    $app->config( 'DefaultSiteRoot', $app->param('default_site_root') || undef,
980        1 );
981    $app->config( 'DefaultSiteURL', $app->param('default_site_url') || undef,
982        1 );
983    $app->config( 'NewUserAutoProvisioning',
984        $app->param('personal_weblog') ? 1 : 0, 1 );
985    $app->config( 'NewUserTemplateBlogId', $tmpl_blog_id || undef, 1 );
986    $app->config( 'DefaultUserLanguage', $app->param('default_language'), 1 );
987    $app->config( 'DefaultUserTagDelimiter',
988        $app->param('default_user_tag_delimiter') || undef, 1 );
989    my $registration = $cfg->CommenterRegistration;
990    if ( my $reg = $app->param('registration') ) {
991        $registration->{Allow} = $reg ? 1 : 0;
992        $registration->{Notify} = $app->param('notify_user_id');
993        $cfg->CommenterRegistration( $registration, 1 );
994    }
995    elsif ( $registration->{Allow} ) {
996        $registration->{Allow} = 0;
997        $cfg->CommenterRegistration( $registration, 1 );
998    }
999    $cfg->save_config();
1000
1001    my $args = ();
1002
1003    if ( $app->config->NewUserAutoProvisioning() ne
1004        ( $app->param('personal_weblog') ? 1 : 0 ) )
1005    {
1006        $args->{error} =
1007          $app->translate(
1008'If personal blog is set, the default site URL and root are required.'
1009          );
1010    }
1011    else {
1012        $args->{saved} = 1;
1013    }
1014
1015    $app->redirect(
1016        $app->uri(
1017            'mode' => 'cfg_system_users',
1018            args   => $args
1019        )
1020    );
1021}
1022
1023sub remove_user_assoc {
1024    my $app = shift;
1025    $app->validate_magic or return;
1026
1027    my $user = $app->user;
1028    my $perms = $app->permissions;
1029    return $app->errtrans("Permission denied.")
1030        unless $perms->can_administer_blog;
1031
1032    my $blog_id = $app->param('blog_id');
1033    my @ids = $app->param('id');
1034    return $app->errtrans("Invalid request.")
1035        unless $blog_id && @ids;
1036
1037    require MT::Association;
1038    require MT::Permission;
1039    foreach my $id (@ids) {
1040        next unless $id;
1041        MT::Association->remove({ blog_id => $blog_id, author_id => $id });
1042        # these too, just in case there are no real associations
1043        # (ie, commenters)
1044        MT::Permission->remove({ blog_id => $blog_id, author_id => $id });
1045    }
1046
1047    $app->add_return_arg( saved => 1 );
1048    $app->call_return;
1049}
1050
1051sub revoke_role {
1052    my $app = shift;
1053    $app->validate_magic or return;
1054
1055    my $user = $app->user;
1056    my $perms = $app->permissions;
1057    return $app->errtrans("Permission denied.")
1058        unless $perms->can_administer_blog;
1059
1060    my $blog_id = $app->param('blog_id');
1061    my $role_id = $app->param('role_id');
1062    my $user_id = $app->param('author_id');
1063    return $app->errtrans("Invalid request.")
1064        unless $blog_id && $role_id && $user_id;
1065
1066    require MT::Association;
1067    require MT::Role;
1068    require MT::Blog;
1069
1070    my $author = MT::Author->load( $user_id );
1071    my $role = MT::Role->load( $role_id );
1072    my $blog = MT::Blog->load( $blog_id );
1073    return $app->errtrans("Invalid request.")
1074        unless $blog && $role && $author;
1075
1076    MT::Association->unlink( $blog => $role => $author );
1077
1078    $app->add_return_arg( saved => 1 );
1079    $app->call_return;
1080}
1081
1082sub grant_role {
1083    my $app = shift;
1084
1085    my $user = $app->user;
1086
1087    my $blogs   = $app->param('blog')   || '';
1088    my $authors = $app->param('author') || '';
1089    my $roles   = $app->param('role')   || '';
1090    my $author_id = $app->param('author_id');
1091    my $blog_id   = $app->param('blog_id');
1092    my $role_id   = $app->param('role_id');
1093
1094    my @blogs   = split /,/, $blogs;
1095    my @authors = split /,/, $authors;
1096    my @roles   = split /,/, $roles;
1097
1098    require MT::Blog;
1099    require MT::Role;
1100
1101    foreach (@blogs) {
1102        my $id = $_;
1103        $id =~ s/\D//g;
1104        $_ = MT::Blog->load($id);
1105    }
1106    foreach (@roles) {
1107        my $id = $_;
1108        $id =~ s/\D//g;
1109        $_ = MT::Role->load($id);
1110    }
1111    my $add_pseudo_new_user = 0;
1112    foreach (@authors) {
1113        my $id = $_;
1114        if ( 'author-PSEUDO' eq $id ) {
1115            $add_pseudo_new_user = 1;
1116            next;
1117        }
1118        $id =~ s/\D//g;
1119        $_ = MT::Author->load($id);
1120    }
1121    $app->error(undef);
1122
1123    if ($author_id) {
1124        if ( $author_id eq 'PSEUDO' ) {
1125            $add_pseudo_new_user = 1;
1126        }
1127        else {
1128            push @authors, MT::Author->load($author_id);
1129        }
1130    }
1131    push @blogs,   MT::Blog->load($blog_id)     if $blog_id;
1132    push @roles,   MT::Role->load($role_id)     if $role_id;
1133
1134    if ( !$user->is_superuser ) {
1135        if (   ( scalar @blogs != 1 )
1136            || ( !$user->permissions( $blogs[0] )->can_administer_blog ) )
1137        {
1138            return $app->errtrans("Permission denied.");
1139        }
1140    }
1141
1142    require MT::Association;
1143
1144    my @default_assignments;
1145
1146    # TBD: handle case for associating system roles to users/groups
1147    foreach my $blog (@blogs) {
1148        next unless ref $blog;
1149        foreach my $role (@roles) {
1150            next unless ref $role;
1151            if ($add_pseudo_new_user) {
1152                push @default_assignments, $role->id . ',' . $blog->id;
1153            }
1154            foreach my $ug ( @authors ) {
1155                next unless ref $ug;
1156                MT::Association->link( $ug => $role => $blog );
1157            }
1158        }
1159    }
1160
1161    if ( $add_pseudo_new_user && @default_assignments ) {
1162        my $da = $app->config('DefaultAssignments');
1163        $da .= ',' if $da;
1164        $app->config( 'DefaultAssignments',
1165            $da . join( ',', @default_assignments ), 1 );
1166        $app->config->save_config;
1167    }
1168
1169    $app->add_return_arg( saved => 1 );
1170    $app->call_return;
1171}
1172
1173sub dialog_select_sysadmin {
1174    my $app = shift;
1175    return $app->errtrans("Permission denied.")
1176      unless $app->user->is_superuser;
1177
1178    my $hasher = sub {
1179        my ( $obj, $row ) = @_;
1180        $row->{label}       = $row->{name};
1181        $row->{description} = $row->{nickname};
1182    };
1183
1184    $app->listing(
1185        {
1186            type  => 'author',
1187            terms => {
1188                type   => MT::Author::AUTHOR(),
1189                status => MT::Author::ACTIVE(),
1190            },
1191            args => {
1192                sort => 'name',
1193                join => MT::Permission->join_on(
1194                    'author_id',
1195                    {
1196                        permissions => "\%'administer'\%",
1197                        blog_id     => '0',
1198                    },
1199                    { 'like' => { 'permissions' => 1 } }
1200                ),
1201            },
1202            code     => $hasher,
1203            template => 'dialog/select_users.tmpl',
1204            params   => {
1205                dialog_title =>
1206                  $app->translate("Select a System Administrator"),
1207                items_prompt =>
1208                  $app->translate("Selected System Administrator"),
1209                search_prompt => $app->translate(
1210                    "Type a username to filter the choices below."),
1211                panel_label       => $app->translate("System Administrator"),
1212                panel_description => $app->translate("Name"),
1213                panel_type        => 'author',
1214                panel_multi       => defined $app->param('multi')
1215                ? $app->param('multi')
1216                : 0,
1217                panel_searchable => 1,
1218                panel_first      => 1,
1219                panel_last       => 1,
1220                list_noncron     => 1,
1221                idfield          => $app->param('idfield'),
1222                namefield        => $app->param('namefield'),
1223            },
1224        }
1225    );
1226}
1227
1228# This mode can be called to service a number of views
1229# Adding roles->blogs for a user
1230# Adding roles->blogs for a group
1231# Adding users->roles->blogs
1232# Adding groups->roles->blogs
1233sub dialog_grant_role {
1234    my $app = shift;
1235
1236    my $author_id = $app->param('author_id');
1237    my $blog_id   = $app->param('blog_id');
1238    my $role_id   = $app->param('role_id');
1239
1240    my $this_user = $app->user;
1241    if ( !$this_user->is_superuser ) {
1242        if (   !$blog_id
1243            || !$this_user->permissions($blog_id)->can_administer_blog )
1244        {
1245            return $app->errtrans("Permission denied.");
1246        }
1247    }
1248
1249    my $type = $app->param('_type');
1250    my ( $user, $role );
1251    if ($author_id) {
1252        $user = MT::Author->load($author_id);
1253    }
1254    if ($role_id) {
1255        require MT::Role;
1256        $role = MT::Role->load($role_id);
1257    }
1258
1259    my $hasher = sub {
1260        my ( $obj, $row ) = @_;
1261        $row->{label} = $row->{name};
1262        $row->{description} = $row->{nickname} if exists $row->{nickname};
1263    };
1264
1265    # Only show active users who are not commenters.
1266    my $terms = {};
1267    if ( $type && ( $type eq 'author' ) ) {
1268        $terms->{status} = MT::Author::ACTIVE();
1269        $terms->{type}   = MT::Author::AUTHOR();
1270    }
1271
1272    my $pseudo_user_row = {
1273        id    => 'PSEUDO',
1274        label => $app->translate('(newly created user)'),
1275        description =>
1276          $app->translate('represents a user who will be created afterwards'),
1277    };
1278
1279    if ( $app->param('search') || $app->param('json') ) {
1280        my $params = {
1281            panel_type   => $type,
1282            list_noncron => 1,
1283            panel_multi  => 1,
1284        };
1285        $app->listing(
1286            {
1287                terms    => $terms,
1288                type     => $type,
1289                code     => $hasher,
1290                params   => $params,
1291                template => 'include/listing_panel.tmpl',
1292                $app->param('search') ? () : (
1293                    pre_build => sub {
1294                        my ($param) = @_;
1295                        if ( $type && $type eq 'author' ) {
1296                            if ( !$app->param('offset') ) {
1297                                my $objs = $param->{object_loop} ||= [];
1298                                unshift @$objs, $pseudo_user_row;
1299                            }
1300                        }
1301                    }
1302                ),
1303            }
1304        );
1305    }
1306    else {
1307
1308        # traditional, full-screen listing
1309        my $params = {
1310            ($author_id || 0) eq 'PSEUDO'
1311            ? (
1312                edit_author_name => $app->translate('(newly created user)'),
1313                edit_author_id   => 'PSEUDO'
1314              )
1315            : $author_id
1316              ? (
1317                  edit_author_name => $user->nickname
1318                  ? $user->nickname
1319                  : $user->name,
1320                  edit_author_id => $user->id,
1321                )
1322              : (),
1323            $role_id
1324            ? (
1325                role_name => $role->name,
1326                role_id   => $role->id,
1327              )
1328            : (),
1329        };
1330
1331        my @panels;
1332        if ( !$role_id ) {
1333            push @panels, 'role';
1334        }
1335        if ( !$blog_id ) {
1336            push @panels, 'blog';
1337        }
1338        if ( !$author_id ) {
1339            if ( $type eq 'user' ) {
1340                unshift @panels, 'author';
1341            }
1342        }
1343
1344        my $panel_info = {
1345            'blog' => {
1346                panel_title       => $app->translate("Select Blogs"),
1347                panel_label       => $app->translate("Blog Name"),
1348                items_prompt      => $app->translate("Blogs Selected"),
1349                search_label      => $app->translate("Search Blogs"),
1350                panel_description => $app->translate("Description"),
1351            },
1352            'author' => {
1353                panel_title       => $app->translate("Select Users"),
1354                panel_label       => $app->translate("Username"),
1355                items_prompt      => $app->translate("Users Selected"),
1356                search_label      => $app->translate("Search Users"),
1357                panel_description => $app->translate("Name"),
1358            },
1359            'role' => {
1360                panel_title       => $app->translate("Select Roles"),
1361                panel_label       => $app->translate("Role Name"),
1362                items_prompt      => $app->translate("Roles Selected"),
1363                search_label      => $app->translate(""),
1364                panel_description => $app->translate("Description"),
1365            },
1366        };
1367
1368        $params->{blog_id}      = $blog_id;
1369        $params->{dialog_title} = $app->translate("Grant Permissions");
1370        $params->{panel_loop}   = [];
1371        $params->{panel_multi}  = 1;
1372
1373        for ( my $i = 0 ; $i <= $#panels ; $i++ ) {
1374            my $source       = $panels[$i];
1375            my $panel_params = {
1376                panel_type => $source,
1377                %{ $panel_info->{$source} },
1378                list_noncron     => 1,
1379                panel_last       => $i == $#panels,
1380                panel_first      => $i == 0,
1381                panel_number     => $i + 1,
1382                panel_total      => $#panels + 1,
1383                panel_has_steps  => ( $#panels == '0' ? 0 : 1 ),
1384                panel_searchable => ( $source eq 'role' ? 0 : 1 ),
1385            };
1386
1387            # Only show active user/groups.
1388            my $terms = {};
1389            if ( $source eq 'author' ) {
1390                $terms->{status} = MT::Author::ACTIVE();
1391                $terms->{type}   = MT::Author::AUTHOR();
1392            }
1393
1394            $app->listing(
1395                {
1396                    no_html => 1,
1397                    code    => $hasher,
1398                    type    => $source,
1399                    params  => $panel_params,
1400                    terms   => $terms,
1401                    args    => { sort => 'name' },
1402                }
1403            );
1404            if ( $source && ( $source eq 'author' ) ) {
1405                if ( !$app->param('offset') ) {
1406                    my $data = $panel_params->{object_loop} ||= [];
1407                    unshift @$data, $pseudo_user_row;
1408                }
1409            }
1410            if (
1411                !$panel_params->{object_loop}
1412                || ( $panel_params->{object_loop}
1413                    && @{ $panel_params->{object_loop} } < 1 )
1414              )
1415            {
1416                $params->{"missing_$source"} = 1;
1417                $params->{"missing_data"}    = 1;
1418            }
1419            push @{ $params->{panel_loop} }, $panel_params;
1420        }
1421
1422        # save the arguments from whence we came...
1423        $params->{return_args} = $app->return_args;
1424        $app->load_tmpl( 'dialog/create_association.tmpl', $params );
1425    }
1426}
1427
1428sub remove_userpic {
1429    my $app = shift;
1430    $app->validate_magic() or return;
1431    my $q  = $app->param;
1432    my $user_id = $q->param('user_id');
1433    my $user = $app->model('author')->load( { id => $user_id } )
1434        or return;
1435    if ($user->userpic_asset_id) {
1436        my $old_file = $user->userpic_file();
1437        my $fmgr = MT::FileMgr->new('Local');
1438        if ($fmgr->exists($old_file)) {
1439            $fmgr->delete($old_file);
1440        }
1441        $user->userpic_asset_id(0);
1442        $user->save;
1443    }
1444    return 'success';
1445}
1446
1447sub can_delete_association {
1448    my ( $eh, $app, $obj ) = @_;
1449
1450    my $blog_id = $app->param('blog_id');
1451    my $user    = $app->user;
1452    if ( !$user->is_superuser ) {
1453        if ( !$blog_id || !$user->permissions($blog_id)->can_administer_blog ) {
1454            return $eh->error( MT->translate("Permission denied.") );
1455        }
1456        if ( $obj->author_id == $user->id ) {
1457            return $eh->error(
1458                MT->translate("You cannot delete your own association.") );
1459        }
1460    }
1461    1;
1462}
1463
1464sub can_view {
1465    my ( $eh, $app, $id ) = @_;
1466    return $id && ( $app->user->id == $id );
1467}
1468
1469sub can_save {
1470    my ( $eh, $app, $id ) = @_;
1471    my $author = $app->user;
1472    if ( !$id ) {
1473        return $author->is_superuser;
1474    }
1475    else {
1476        return $author->id == $id;
1477    }
1478}
1479
1480sub can_delete {
1481    my ( $eh, $app, $obj ) = @_;
1482    my $author = $app->user;
1483    if ( $author->id == $obj->id ) {
1484        return $eh->error(
1485            MT->translate("You cannot delete your own user record.") );
1486    }
1487    return 1 if $author->is_superuser();
1488    if ( !( $obj->created_by && $obj->created_by == $author->id ) ) {
1489        return $eh->error(
1490            MT->translate(
1491                "You have no permission to delete the user [_1].", $obj->name
1492            )
1493        );
1494    }
1495}
1496
1497sub save_filter {
1498    my ( $eh, $app ) = @_;
1499
1500    my $status = $app->param('status');
1501    return 1 if $status and $status == MT::Author::INACTIVE();
1502
1503    require MT::Auth;
1504    my $auth_mode = $app->config('AuthenticationModule');
1505    my ($pref) = split /\s+/, $auth_mode;
1506
1507    my $name = $app->param('name');
1508    if ( $pref eq 'MT' ) {
1509        if ( defined $name ) {
1510            $name =~ s/(^\s+|\s+$)//g;
1511            $app->param( 'name', $name );
1512        }
1513        return $eh->error( $app->translate("User requires username") )
1514          if ( !$name );
1515    }
1516
1517    require MT::Author;
1518    my $existing = MT::Author->load(
1519        {
1520            name => $name,
1521            type => MT::Author::AUTHOR()
1522        }
1523    );
1524    my $id = $app->param('id');
1525    if ( $existing && ( ( $id && $existing->id ne $id ) || !$id ) ) {
1526        return $eh->error(
1527            $app->translate("A user with the same name already exists.") );
1528    }
1529
1530    return 1 if ( $pref ne 'MT' );
1531    if ( !$app->param('id') ) {    # it's a new object
1532        return $eh->error( $app->translate("User requires password") )
1533          if ( !$app->param('pass') );
1534        return $eh->error(
1535            $app->translate("User requires password recovery word/phrase") )
1536          if ( !$app->param('hint') );
1537    }
1538    return $eh->error(
1539        MT->translate("Email Address is required for password recovery") )
1540      unless $app->param('email');
1541    if ( $app->param('url') ) {
1542        my $url = $app->param('url');
1543        return $eh->error( MT->translate("Website URL is imperfect") )
1544          unless is_url($url);
1545    }
1546    1;
1547}
1548
1549sub pre_save {
1550    my $eh = shift;
1551    my ( $app, $obj, $original ) = @_;
1552
1553    # Authors should only be of type AUTHOR when created from
1554    # the CMS app; COMMENTERs are created from the Comments app.
1555    $obj->type( MT::Author::AUTHOR() );
1556
1557    my $pass = $app->param('pass');
1558    if ($pass) {
1559        $obj->set_password($pass);
1560    }
1561    elsif ( !$obj->id ) {
1562        $obj->password('(none)');
1563    }
1564
1565    my ( $delim, $delim2 ) = $app->param('tag_delim');
1566    $delim = $delim ? $delim : $delim2;
1567    if ( $delim =~ m/comma/i ) {
1568        $delim = ord(',');
1569    }
1570    elsif ( $delim =~ m/space/i ) {
1571        $delim = ord(' ');
1572    }
1573    else {
1574        $delim = ord(',');
1575    }
1576    $obj->entry_prefs( 'tag_delim' => $delim );
1577
1578    unless ( $obj->id ) {
1579        $obj->created_by( $app->user->id );
1580    }
1581    1;
1582}
1583
1584sub post_save {
1585    my $eh = shift;
1586    my ( $app, $obj, $original ) = @_;
1587
1588    if ( !$original->id ) {
1589        $app->log(
1590            {
1591                message => $app->translate(
1592                    "User '[_1]' (ID:[_2]) created by '[_3]'",
1593                    $obj->name, $obj->id, $app->user->name
1594                ),
1595                level    => MT::Log::INFO(),
1596                class    => 'author',
1597                category => 'new',
1598            }
1599        );
1600        $obj->add_default_roles;
1601
1602        my $author_id = $obj->id;
1603        if ( $app->param('create_personal_weblog') ) {
1604
1605            # provision new user with a personal blog
1606            $app->run_callbacks( 'new_user_provisioning', $obj );
1607        }
1608    }
1609    else {
1610        if ( $app->user->id == $obj->id ) {
1611            ## If this is a user editing his/her profile, $id will be
1612            ## some defined value; if so we should update the user's
1613            ## cookie to reflect any changes made to username and password.
1614            ## Otherwise, this is a new user, and we shouldn't update the
1615            ## cookie.
1616            $app->user($obj);
1617            if (   ( $obj->name ne $original->name )
1618                || ( $app->param('pass') ) )
1619            {
1620                $app->start_session();
1621            }
1622        }
1623    }
1624    1;
1625}
1626
1627sub post_delete {
1628    my ( $eh, $app, $obj ) = @_;
1629
1630    $app->log(
1631        {
1632            message => $app->translate(
1633                "User '[_1]' (ID:[_2]) deleted by '[_3]'",
1634                $obj->name, $obj->id, $app->user->name
1635            ),
1636            level    => MT::Log::INFO(),
1637            class    => 'system',
1638            category => 'delete'
1639        }
1640    );
1641}
1642
1643sub _merge_default_assignments {
1644    my $app = shift;
1645    my ( $data, $hasher, $type, $id ) = @_;
1646
1647    if ( my $def = MT->config->DefaultAssignments ) {
1648        my @def = split ',', $def;
1649        while ( my $role_id = shift @def ) {
1650            my $blog_id = shift @def;
1651            next unless $role_id && $blog_id;
1652            next if ( $type eq 'role' ) && ( $id != $role_id );
1653            next if ( $type eq 'blog' ) && ( $id != $blog_id );
1654            my $obj = MT::Association->new;
1655            $obj->role_id($role_id);
1656            $obj->blog_id($blog_id);
1657            $obj->id( 'PSEUDO-' . $role_id . '-' . $blog_id );
1658            my $row = $obj->column_values();
1659            $hasher->( $obj, $row ) if $hasher;
1660            $row->{user_id}   = 'PSEUDO';
1661            $row->{user_name} = MT->translate('(newly created user)');
1662            push @$data, $row;
1663        }
1664    }
1665}
1666
1667sub build_author_table {
1668    my $app = shift;
1669    my (%args) = @_;
1670
1671    my $i = 1;
1672    my @author;
1673    my $iter;
1674    if ( $args{load_args} ) {
1675        my $class = $app->model('author');
1676        $iter = $class->load_iter( @{ $args{load_args} } );
1677    }
1678    elsif ( $args{iter} ) {
1679        $iter = $args{iter};
1680    }
1681    elsif ( $args{items} ) {
1682        $iter = sub { pop @{ $args{items} } };
1683    }
1684    return [] unless $iter;
1685    my $blog_id = $app->param('blog_id');
1686    my $param = $args{param};
1687    $param->{has_edit_access}  = $app->user->is_superuser();
1688    $param->{is_administrator} = $app->user->is_superuser();
1689    my ( %blogs, %entry_count_refs );
1690    while ( my $author = $iter->() ) {
1691        my $row = {
1692            name           => $author->name,
1693            nickname       => $author->nickname,
1694            email          => $author->email,
1695            url            => $author->url,
1696            status_enabled => $author->is_active,
1697            status_pending => ( $author->status == MT::Author::PENDING() )
1698            ? 1
1699            : 0,
1700            id          => $author->id,
1701            entry_count => 0,
1702            is_me => ( $app->user->id == $author->id ? 1 : 0 )
1703        };
1704        $entry_count_refs{ $author->id } = \$row->{entry_count};
1705        if ( $author->created_by ) {
1706            if ( my $parent_author =
1707                $app->model('author')->load( $author->created_by ) )
1708            {
1709                $row->{created_by} = $parent_author->name;
1710            }
1711            else {
1712                $row->{created_by} = $app->translate('(user deleted)');
1713            }
1714        }
1715        $row->{object} = $author;
1716        $row->{usertype_author} = 1 if $author->type == MT::Author::AUTHOR();
1717        if ( $author->type == MT::Author::COMMENTER() ) {
1718            $row->{usertype_commenter} = 1;
1719            $row->{status_trusted} = 1 if $blog_id && $author->is_trusted($blog_id);
1720            if ($row->{name} =~ m/^[a-f0-9]{32}$/) {
1721                $row->{name} = $row->{nickname} || $row->{url};
1722            }
1723        }
1724        $row->{auth_icon_url} = $author->auth_icon_url;
1725        push @author, $row;
1726    }
1727    return [] unless @author;
1728    my $type = $app->param('entry_type') || 'entry';
1729    my $entry_class = $app->model($type);
1730    my $author_entry_count_iter =
1731      $entry_class->count_group_by( { author_id => [ keys %entry_count_refs ] },
1732        { group => ['author_id'] } );
1733    while ( my ( $count, $author_id ) = $author_entry_count_iter->() ) {
1734        ${ $entry_count_refs{$author_id} } = $count;
1735    }
1736    $param->{author_table}[0]{object_loop} = \@author;
1737
1738    $app->load_list_actions( 'author', $param->{author_table}[0] );
1739    $param->{page_actions} = $param->{author_table}[0]{page_actions} =
1740      $app->page_actions('list_authors');
1741    $param->{object_loop} = $param->{author_table}[0]{object_loop};
1742
1743    \@author;
1744}
1745
1746sub _delete_pseudo_association {
1747    my $app = shift;
1748    my ($pid, $bid) = @_;
1749    my $rid;
1750    if ($pid) {
1751        ( my $pseudo, $rid, $bid ) = split '-', $pid;
1752    }
1753    my @newdef;
1754    if ( my $def = $app->config->DefaultAssignments ) {
1755        my @def = split ',', $def;
1756        while ( my $role_id = shift @def ) {
1757            my $blog_id = shift @def;
1758            next unless $role_id && $blog_id;
1759            next
1760              if ( $rid && ( $role_id == $rid ) && ( $blog_id == $bid ) )
1761                || ( !defined($rid) && ( $blog_id == $bid ) );
1762            push @newdef, "$role_id,$blog_id";
1763        }
1764    }
1765    if (@newdef) {
1766        $app->config( 'DefaultAssignments', join( ',', @newdef ), 1 );
1767    }
1768    else {
1769        $app->config( 'DefaultAssignments', undef, 1 );
1770    }
1771    $app->config->save_config;
1772}
1773
17741;
Note: See TracBrowser for help on using the browser.