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

Revision 1378, 58.6 kB (checked in by takayama, 22 months ago)

Fixed BugId:67669
* Fixed a performance issue.

  • Changed to dialog based author selection instead of combo box.
  • 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_author {
1174    my $app = shift;
1175
1176    my $hasher = sub {
1177        my ( $obj, $row ) = @_;
1178        $row->{label}       = $row->{name};
1179        $row->{description} = $row->{nickname};
1180    };
1181
1182    $app->listing(
1183        {
1184            type  => 'author',
1185            terms => {
1186                type   => MT::Author::AUTHOR(),
1187                status => MT::Author::ACTIVE(),
1188            },
1189            args => {
1190                sort => 'name',
1191                join => MT::Permission->join_on(
1192                    'author_id',
1193                    {
1194                        permissions => "\%'create_post'\%",
1195                        blog_id     => $app->blog->id,
1196                    },
1197                    { 'like' => { 'permissions' => 1 } }
1198                ),
1199            },
1200            code     => $hasher,
1201            template => 'dialog/select_users.tmpl',
1202            params   => {
1203                dialog_title =>
1204                  $app->translate("Select a entry author"),
1205                items_prompt =>
1206                  $app->translate("Selected author"),
1207                search_prompt => $app->translate(
1208                    "Type a username to filter the choices below."),
1209                panel_label       => $app->translate("Entry author"),
1210                panel_description => $app->translate("Name"),
1211                panel_type        => 'author',
1212                panel_multi       => defined $app->param('multi')
1213                ? $app->param('multi')
1214                : 0,
1215                panel_searchable => 1,
1216                panel_first      => 1,
1217                panel_last       => 1,
1218                list_noncron     => 1,
1219                idfield          => $app->param('idfield'),
1220                namefield        => $app->param('namefield'),
1221            },
1222        }
1223    );
1224}
1225
1226sub dialog_select_sysadmin {
1227    my $app = shift;
1228    return $app->errtrans("Permission denied.")
1229      unless $app->user->is_superuser;
1230
1231    my $hasher = sub {
1232        my ( $obj, $row ) = @_;
1233        $row->{label}       = $row->{name};
1234        $row->{description} = $row->{nickname};
1235    };
1236
1237    $app->listing(
1238        {
1239            type  => 'author',
1240            terms => {
1241                type   => MT::Author::AUTHOR(),
1242                status => MT::Author::ACTIVE(),
1243            },
1244            args => {
1245                sort => 'name',
1246                join => MT::Permission->join_on(
1247                    'author_id',
1248                    {
1249                        permissions => "\%'administer'\%",
1250                        blog_id     => '0',
1251                    },
1252                    { 'like' => { 'permissions' => 1 } }
1253                ),
1254            },
1255            code     => $hasher,
1256            template => 'dialog/select_users.tmpl',
1257            params   => {
1258                dialog_title =>
1259                  $app->translate("Select a System Administrator"),
1260                items_prompt =>
1261                  $app->translate("Selected System Administrator"),
1262                search_prompt => $app->translate(
1263                    "Type a username to filter the choices below."),
1264                panel_label       => $app->translate("System Administrator"),
1265                panel_description => $app->translate("Name"),
1266                panel_type        => 'author',
1267                panel_multi       => defined $app->param('multi')
1268                ? $app->param('multi')
1269                : 0,
1270                panel_searchable => 1,
1271                panel_first      => 1,
1272                panel_last       => 1,
1273                list_noncron     => 1,
1274                idfield          => $app->param('idfield'),
1275                namefield        => $app->param('namefield'),
1276            },
1277        }
1278    );
1279}
1280
1281# This mode can be called to service a number of views
1282# Adding roles->blogs for a user
1283# Adding roles->blogs for a group
1284# Adding users->roles->blogs
1285# Adding groups->roles->blogs
1286sub dialog_grant_role {
1287    my $app = shift;
1288
1289    my $author_id = $app->param('author_id');
1290    my $blog_id   = $app->param('blog_id');
1291    my $role_id   = $app->param('role_id');
1292
1293    my $this_user = $app->user;
1294    if ( !$this_user->is_superuser ) {
1295        if (   !$blog_id
1296            || !$this_user->permissions($blog_id)->can_administer_blog )
1297        {
1298            return $app->errtrans("Permission denied.");
1299        }
1300    }
1301
1302    my $type = $app->param('_type');
1303    my ( $user, $role );
1304    if ($author_id) {
1305        $user = MT::Author->load($author_id);
1306    }
1307    if ($role_id) {
1308        require MT::Role;
1309        $role = MT::Role->load($role_id);
1310    }
1311
1312    my $hasher = sub {
1313        my ( $obj, $row ) = @_;
1314        $row->{label} = $row->{name};
1315        $row->{description} = $row->{nickname} if exists $row->{nickname};
1316    };
1317
1318    # Only show active users who are not commenters.
1319    my $terms = {};
1320    if ( $type && ( $type eq 'author' ) ) {
1321        $terms->{status} = MT::Author::ACTIVE();
1322        $terms->{type}   = MT::Author::AUTHOR();
1323    }
1324
1325    my $pseudo_user_row = {
1326        id    => 'PSEUDO',
1327        label => $app->translate('(newly created user)'),
1328        description =>
1329          $app->translate('represents a user who will be created afterwards'),
1330    };
1331
1332    if ( $app->param('search') || $app->param('json') ) {
1333        my $params = {
1334            panel_type   => $type,
1335            list_noncron => 1,
1336            panel_multi  => 1,
1337        };
1338        $app->listing(
1339            {
1340                terms    => $terms,
1341                type     => $type,
1342                code     => $hasher,
1343                params   => $params,
1344                template => 'include/listing_panel.tmpl',
1345                $app->param('search') ? () : (
1346                    pre_build => sub {
1347                        my ($param) = @_;
1348                        if ( $type && $type eq 'author' ) {
1349                            if ( !$app->param('offset') ) {
1350                                my $objs = $param->{object_loop} ||= [];
1351                                unshift @$objs, $pseudo_user_row;
1352                            }
1353                        }
1354                    }
1355                ),
1356            }
1357        );
1358    }
1359    else {
1360
1361        # traditional, full-screen listing
1362        my $params = {
1363            ($author_id || 0) eq 'PSEUDO'
1364            ? (
1365                edit_author_name => $app->translate('(newly created user)'),
1366                edit_author_id   => 'PSEUDO'
1367              )
1368            : $author_id
1369              ? (
1370                  edit_author_name => $user->nickname
1371                  ? $user->nickname
1372                  : $user->name,
1373                  edit_author_id => $user->id,
1374                )
1375              : (),
1376            $role_id
1377            ? (
1378                role_name => $role->name,
1379                role_id   => $role->id,
1380              )
1381            : (),
1382        };
1383
1384        my @panels;
1385        if ( !$role_id ) {
1386            push @panels, 'role';
1387        }
1388        if ( !$blog_id ) {
1389            push @panels, 'blog';
1390        }
1391        if ( !$author_id ) {
1392            if ( $type eq 'user' ) {
1393                unshift @panels, 'author';
1394            }
1395        }
1396
1397        my $panel_info = {
1398            'blog' => {
1399                panel_title       => $app->translate("Select Blogs"),
1400                panel_label       => $app->translate("Blog Name"),
1401                items_prompt      => $app->translate("Blogs Selected"),
1402                search_label      => $app->translate("Search Blogs"),
1403                panel_description => $app->translate("Description"),
1404            },
1405            'author' => {
1406                panel_title       => $app->translate("Select Users"),
1407                panel_label       => $app->translate("Username"),
1408                items_prompt      => $app->translate("Users Selected"),
1409                search_label      => $app->translate("Search Users"),
1410                panel_description => $app->translate("Name"),
1411            },
1412            'role' => {
1413                panel_title       => $app->translate("Select Roles"),
1414                panel_label       => $app->translate("Role Name"),
1415                items_prompt      => $app->translate("Roles Selected"),
1416                search_label      => $app->translate(""),
1417                panel_description => $app->translate("Description"),
1418            },
1419        };
1420
1421        $params->{blog_id}      = $blog_id;
1422        $params->{dialog_title} = $app->translate("Grant Permissions");
1423        $params->{panel_loop}   = [];
1424        $params->{panel_multi}  = 1;
1425
1426        for ( my $i = 0 ; $i <= $#panels ; $i++ ) {
1427            my $source       = $panels[$i];
1428            my $panel_params = {
1429                panel_type => $source,
1430                %{ $panel_info->{$source} },
1431                list_noncron     => 1,
1432                panel_last       => $i == $#panels,
1433                panel_first      => $i == 0,
1434                panel_number     => $i + 1,
1435                panel_total      => $#panels + 1,
1436                panel_has_steps  => ( $#panels == '0' ? 0 : 1 ),
1437                panel_searchable => ( $source eq 'role' ? 0 : 1 ),
1438            };
1439
1440            # Only show active user/groups.
1441            my $terms = {};
1442            if ( $source eq 'author' ) {
1443                $terms->{status} = MT::Author::ACTIVE();
1444                $terms->{type}   = MT::Author::AUTHOR();
1445            }
1446
1447            $app->listing(
1448                {
1449                    no_html => 1,
1450                    code    => $hasher,
1451                    type    => $source,
1452                    params  => $panel_params,
1453                    terms   => $terms,
1454                    args    => { sort => 'name' },
1455                }
1456            );
1457            if ( $source && ( $source eq 'author' ) ) {
1458                if ( !$app->param('offset') ) {
1459                    my $data = $panel_params->{object_loop} ||= [];
1460                    unshift @$data, $pseudo_user_row;
1461                }
1462            }
1463            if (
1464                !$panel_params->{object_loop}
1465                || ( $panel_params->{object_loop}
1466                    && @{ $panel_params->{object_loop} } < 1 )
1467              )
1468            {
1469                $params->{"missing_$source"} = 1;
1470                $params->{"missing_data"}    = 1;
1471            }
1472            push @{ $params->{panel_loop} }, $panel_params;
1473        }
1474
1475        # save the arguments from whence we came...
1476        $params->{return_args} = $app->return_args;
1477        $app->load_tmpl( 'dialog/create_association.tmpl', $params );
1478    }
1479}
1480
1481sub remove_userpic {
1482    my $app = shift;
1483    $app->validate_magic() or return;
1484    my $q  = $app->param;
1485    my $user_id = $q->param('user_id');
1486    my $user = $app->model('author')->load( { id => $user_id } )
1487        or return;
1488    if ($user->userpic_asset_id) {
1489        my $old_file = $user->userpic_file();
1490        my $fmgr = MT::FileMgr->new('Local');
1491        if ($fmgr->exists($old_file)) {
1492            $fmgr->delete($old_file);
1493        }
1494        $user->userpic_asset_id(0);
1495        $user->save;
1496    }
1497    return 'success';
1498}
1499
1500sub can_delete_association {
1501    my ( $eh, $app, $obj ) = @_;
1502
1503    my $blog_id = $app->param('blog_id');
1504    my $user    = $app->user;
1505    if ( !$user->is_superuser ) {
1506        if ( !$blog_id || !$user->permissions($blog_id)->can_administer_blog ) {
1507            return $eh->error( MT->translate("Permission denied.") );
1508        }
1509        if ( $obj->author_id == $user->id ) {
1510            return $eh->error(
1511                MT->translate("You cannot delete your own association.") );
1512        }
1513    }
1514    1;
1515}
1516
1517sub can_view {
1518    my ( $eh, $app, $id ) = @_;
1519    return $id && ( $app->user->id == $id );
1520}
1521
1522sub can_save {
1523    my ( $eh, $app, $id ) = @_;
1524    my $author = $app->user;
1525    if ( !$id ) {
1526        return $author->is_superuser;
1527    }
1528    else {
1529        return $author->id == $id;
1530    }
1531}
1532
1533sub can_delete {
1534    my ( $eh, $app, $obj ) = @_;
1535    my $author = $app->user;
1536    if ( $author->id == $obj->id ) {
1537        return $eh->error(
1538            MT->translate("You cannot delete your own user record.") );
1539    }
1540    return 1 if $author->is_superuser();
1541    if ( !( $obj->created_by && $obj->created_by == $author->id ) ) {
1542        return $eh->error(
1543            MT->translate(
1544                "You have no permission to delete the user [_1].", $obj->name
1545            )
1546        );
1547    }
1548}
1549
1550sub save_filter {
1551    my ( $eh, $app ) = @_;
1552
1553    my $status = $app->param('status');
1554    return 1 if $status and $status == MT::Author::INACTIVE();
1555
1556    require MT::Auth;
1557    my $auth_mode = $app->config('AuthenticationModule');
1558    my ($pref) = split /\s+/, $auth_mode;
1559
1560    my $name = $app->param('name');
1561    if ( $pref eq 'MT' ) {
1562        if ( defined $name ) {
1563            $name =~ s/(^\s+|\s+$)//g;
1564            $app->param( 'name', $name );
1565        }
1566        return $eh->error( $app->translate("User requires username") )
1567          if ( !$name );
1568    }
1569
1570    require MT::Author;
1571    my $existing = MT::Author->load(
1572        {
1573            name => $name,
1574            type => MT::Author::AUTHOR()
1575        }
1576    );
1577    my $id = $app->param('id');
1578    if ( $existing && ( ( $id && $existing->id ne $id ) || !$id ) ) {
1579        return $eh->error(
1580            $app->translate("A user with the same name already exists.") );
1581    }
1582
1583    return 1 if ( $pref ne 'MT' );
1584    if ( !$app->param('id') ) {    # it's a new object
1585        return $eh->error( $app->translate("User requires password") )
1586          if ( !$app->param('pass') );
1587        return $eh->error(
1588            $app->translate("User requires password recovery word/phrase") )
1589          if ( !$app->param('hint') );
1590    }
1591    return $eh->error(
1592        MT->translate("Email Address is required for password recovery") )
1593      unless $app->param('email');
1594    if ( $app->param('url') ) {
1595        my $url = $app->param('url');
1596        return $eh->error( MT->translate("Website URL is imperfect") )
1597          unless is_url($url);
1598    }
1599    1;
1600}
1601
1602sub pre_save {
1603    my $eh = shift;
1604    my ( $app, $obj, $original ) = @_;
1605
1606    # Authors should only be of type AUTHOR when created from
1607    # the CMS app; COMMENTERs are created from the Comments app.
1608    $obj->type( MT::Author::AUTHOR() );
1609
1610    my $pass = $app->param('pass');
1611    if ($pass) {
1612        $obj->set_password($pass);
1613    }
1614    elsif ( !$obj->id ) {
1615        $obj->password('(none)');
1616    }
1617
1618    my ( $delim, $delim2 ) = $app->param('tag_delim');
1619    $delim = $delim ? $delim : $delim2;
1620    if ( $delim =~ m/comma/i ) {
1621        $delim = ord(',');
1622    }
1623    elsif ( $delim =~ m/space/i ) {
1624        $delim = ord(' ');
1625    }
1626    else {
1627        $delim = ord(',');
1628    }
1629    $obj->entry_prefs( 'tag_delim' => $delim );
1630
1631    unless ( $obj->id ) {
1632        $obj->created_by( $app->user->id );
1633    }
1634    1;
1635}
1636
1637sub post_save {
1638    my $eh = shift;
1639    my ( $app, $obj, $original ) = @_;
1640
1641    if ( !$original->id ) {
1642        $app->log(
1643            {
1644                message => $app->translate(
1645                    "User '[_1]' (ID:[_2]) created by '[_3]'",
1646                    $obj->name, $obj->id, $app->user->name
1647                ),
1648                level    => MT::Log::INFO(),
1649                class    => 'author',
1650                category => 'new',
1651            }
1652        );
1653        $obj->add_default_roles;
1654
1655        my $author_id = $obj->id;
1656        if ( $app->param('create_personal_weblog') ) {
1657
1658            # provision new user with a personal blog
1659            $app->run_callbacks( 'new_user_provisioning', $obj );
1660        }
1661    }
1662    else {
1663        if ( $app->user->id == $obj->id ) {
1664            ## If this is a user editing his/her profile, $id will be
1665            ## some defined value; if so we should update the user's
1666            ## cookie to reflect any changes made to username and password.
1667            ## Otherwise, this is a new user, and we shouldn't update the
1668            ## cookie.
1669            $app->user($obj);
1670            if (   ( $obj->name ne $original->name )
1671                || ( $app->param('pass') ) )
1672            {
1673                $app->start_session();
1674            }
1675        }
1676    }
1677    1;
1678}
1679
1680sub post_delete {
1681    my ( $eh, $app, $obj ) = @_;
1682
1683    $app->log(
1684        {
1685            message => $app->translate(
1686                "User '[_1]' (ID:[_2]) deleted by '[_3]'",
1687                $obj->name, $obj->id, $app->user->name
1688            ),
1689            level    => MT::Log::INFO(),
1690            class    => 'system',
1691            category => 'delete'
1692        }
1693    );
1694}
1695
1696sub _merge_default_assignments {
1697    my $app = shift;
1698    my ( $data, $hasher, $type, $id ) = @_;
1699
1700    if ( my $def = MT->config->DefaultAssignments ) {
1701        my @def = split ',', $def;
1702        while ( my $role_id = shift @def ) {
1703            my $blog_id = shift @def;
1704            next unless $role_id && $blog_id;
1705            next if ( $type eq 'role' ) && ( $id != $role_id );
1706            next if ( $type eq 'blog' ) && ( $id != $blog_id );
1707            my $obj = MT::Association->new;
1708            $obj->role_id($role_id);
1709            $obj->blog_id($blog_id);
1710            $obj->id( 'PSEUDO-' . $role_id . '-' . $blog_id );
1711            my $row = $obj->column_values();
1712            $hasher->( $obj, $row ) if $hasher;
1713            $row->{user_id}   = 'PSEUDO';
1714            $row->{user_name} = MT->translate('(newly created user)');
1715            push @$data, $row;
1716        }
1717    }
1718}
1719
1720sub build_author_table {
1721    my $app = shift;
1722    my (%args) = @_;
1723
1724    my $i = 1;
1725    my @author;
1726    my $iter;
1727    if ( $args{load_args} ) {
1728        my $class = $app->model('author');
1729        $iter = $class->load_iter( @{ $args{load_args} } );
1730    }
1731    elsif ( $args{iter} ) {
1732        $iter = $args{iter};
1733    }
1734    elsif ( $args{items} ) {
1735        $iter = sub { pop @{ $args{items} } };
1736    }
1737    return [] unless $iter;
1738    my $blog_id = $app->param('blog_id');
1739    my $param = $args{param};
1740    $param->{has_edit_access}  = $app->user->is_superuser();
1741    $param->{is_administrator} = $app->user->is_superuser();
1742    my ( %blogs, %entry_count_refs );
1743    while ( my $author = $iter->() ) {
1744        my $row = {
1745            name           => $author->name,
1746            nickname       => $author->nickname,
1747            email          => $author->email,
1748            url            => $author->url,
1749            status_enabled => $author->is_active,
1750            status_pending => ( $author->status == MT::Author::PENDING() )
1751            ? 1
1752            : 0,
1753            id          => $author->id,
1754            entry_count => 0,
1755            is_me => ( $app->user->id == $author->id ? 1 : 0 )
1756        };
1757        $entry_count_refs{ $author->id } = \$row->{entry_count};
1758        if ( $author->created_by ) {
1759            if ( my $parent_author =
1760                $app->model('author')->load( $author->created_by ) )
1761            {
1762                $row->{created_by} = $parent_author->name;
1763            }
1764            else {
1765                $row->{created_by} = $app->translate('(user deleted)');
1766            }
1767        }
1768        $row->{object} = $author;
1769        $row->{usertype_author} = 1 if $author->type == MT::Author::AUTHOR();
1770        if ( $author->type == MT::Author::COMMENTER() ) {
1771            $row->{usertype_commenter} = 1;
1772            $row->{status_trusted} = 1 if $blog_id && $author->is_trusted($blog_id);
1773            if ($row->{name} =~ m/^[a-f0-9]{32}$/) {
1774                $row->{name} = $row->{nickname} || $row->{url};
1775            }
1776        }
1777        $row->{auth_icon_url} = $author->auth_icon_url;
1778        push @author, $row;
1779    }
1780    return [] unless @author;
1781    my $type = $app->param('entry_type') || 'entry';
1782    my $entry_class = $app->model($type);
1783    my $author_entry_count_iter =
1784      $entry_class->count_group_by( { author_id => [ keys %entry_count_refs ] },
1785        { group => ['author_id'] } );
1786    while ( my ( $count, $author_id ) = $author_entry_count_iter->() ) {
1787        ${ $entry_count_refs{$author_id} } = $count;
1788    }
1789    $param->{author_table}[0]{object_loop} = \@author;
1790
1791    $app->load_list_actions( 'author', $param->{author_table}[0] );
1792    $param->{page_actions} = $param->{author_table}[0]{page_actions} =
1793      $app->page_actions('list_authors');
1794    $param->{object_loop} = $param->{author_table}[0]{object_loop};
1795
1796    \@author;
1797}
1798
1799sub _delete_pseudo_association {
1800    my $app = shift;
1801    my ($pid, $bid) = @_;
1802    my $rid;
1803    if ($pid) {
1804        ( my $pseudo, $rid, $bid ) = split '-', $pid;
1805    }
1806    my @newdef;
1807    if ( my $def = $app->config->DefaultAssignments ) {
1808        my @def = split ',', $def;
1809        while ( my $role_id = shift @def ) {
1810            my $blog_id = shift @def;
1811            next unless $role_id && $blog_id;
1812            next
1813              if ( $rid && ( $role_id == $rid ) && ( $blog_id == $bid ) )
1814                || ( !defined($rid) && ( $blog_id == $bid ) );
1815            push @newdef, "$role_id,$blog_id";
1816        }
1817    }
1818    if (@newdef) {
1819        $app->config( 'DefaultAssignments', join( ',', @newdef ), 1 );
1820    }
1821    else {
1822        $app->config( 'DefaultAssignments', undef, 1 );
1823    }
1824    $app->config->save_config;
1825}
1826
18271;
Note: See TracBrowser for help on using the browser.