root/branches/release-41/lib/MT/CMS/User.pm @ 2820

Revision 2820, 59.5 kB (checked in by bchoate, 17 months ago)

Make the display name a required field for all authentication methods. BugId:80675

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