+}
+
+=item agentnum
+
+Returns true if the user can view the specified agent.
+
+=cut
+
+sub agentnum {
+ my( $self, $agentnum ) = @_;
+ my $sth = dbh->prepare(
+ "SELECT COUNT(*) FROM access_usergroup
+ JOIN access_groupagent USING ( groupnum )
+ WHERE usernum = ? AND agentnum = ?"
+ ) or die dbh->errstr;
+ $sth->execute($self->usernum, $agentnum) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item agents [ HASHREF | OPTION => VALUE ... ]
+
+Returns the list of agents this user can view (via group membership), as
+FS::agent objects. Accepts the same options as the agentnums_sql method.
+
+=cut
+
+sub agents {
+ my $self = shift;
+ qsearch({
+ 'table' => 'agent',
+ 'hashref' => { disabled=>'' },
+ 'extra_sql' => ' AND '. $self->agentnums_sql(@_),
+ 'order_by' => 'ORDER BY agent',
+ });
+}
+
+=item access_right RIGHTNAME | LISTREF
+
+Given a right name or a list reference of right names, returns true if this
+user has this right, or, for a list, one of the rights (currently via group
+membership, eventually also via user overrides).
+
+=cut
+
+sub access_right {
+ my( $self, $rightname ) = @_;
+
+ $rightname = [ $rightname ] unless ref($rightname);
+
+ warn "$me access_right called on ". join(', ', @$rightname). "\n"
+ if $DEBUG;
+
+ #some caching of ACL requests for low-hanging fruit perf improvement
+ #since we get a new $CurrentUser object each page view there shouldn't be any
+ #issues with stickiness
+ if ( $self->{_ACLcache} ) {
+
+ unless ( grep !exists($self->{_ACLcache}{$_}), @$rightname ) {
+ warn "$me ACL cache hit for ". join(', ', @$rightname). "\n"
+ if $DEBUG;
+ return scalar( grep $self->{_ACLcache}{$_}, @$rightname );
+ }
+
+ warn "$me ACL cache miss for ". join(', ', @$rightname). "\n"
+ if $DEBUG;
+
+ } else {
+
+ warn "initializing ACL cache\n"
+ if $DEBUG;
+ $self->{_ACLcache} = {};
+
+ }
+
+ my $has_right = ' rightname IN ('. join(',', map '?', @$rightname ). ') ';
+
+ my $sth = dbh->prepare("
+ SELECT groupnum FROM access_usergroup
+ LEFT JOIN access_group USING ( groupnum )
+ LEFT JOIN access_right
+ ON ( access_group.groupnum = access_right.rightobjnum )
+ WHERE usernum = ?
+ AND righttype = 'FS::access_group'
+ AND $has_right
+ LIMIT 1
+ ") or die dbh->errstr;
+ $sth->execute($self->usernum, @$rightname) or die $sth->errstr;
+ my $row = $sth->fetchrow_arrayref;
+
+ my $return = $row ? $row->[0] : '';
+
+ #just caching the single-rightname hits should be enough of a win for now
+ if ( scalar(@$rightname) == 1 ) {
+ $self->{_ACLcache}{${$rightname}[0]} = $return;
+ }
+
+ $return;
+
+}
+
+=item default_customer_view
+
+Returns the default customer view for this user, from the
+"default_customer_view" user preference, the "cust_main-default_view" config,
+or the hardcoded default, "basics" (formerly "jumbo" prior to 3.0).
+
+=cut
+
+sub default_customer_view {
+ my $self = shift;
+
+ $self->option('default_customer_view')
+ || FS::Conf->new->config('cust_main-default_view')
+ || 'basics'; #s/jumbo/basics/ starting with 3.0
+
+}
+
+=item spreadsheet_format [ OVERRIDE ]
+
+Returns a hashref of this user's Excel spreadsheet download settings:
+'extension' (xls or xlsx), 'class' (Spreadsheet::WriteExcel or
+Excel::Writer::XLSX), and 'mime_type'. If OVERRIDE is 'XLS' or 'XLSX',
+use that instead of the user's setting.
+
+=cut
+
+# is there a better place to put this?
+my %formats = (
+ XLS => {
+ extension => '.xls',
+ class => 'Spreadsheet::WriteExcel',
+ mime_type => 'application/vnd.ms-excel',
+ },
+ XLSX => {
+ extension => '.xlsx',
+ class => 'Excel::Writer::XLSX',
+ mime_type => # it's on wikipedia, it must be true
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ }
+);
+
+sub spreadsheet_format {
+ my $self = shift;
+ my $override = shift;
+
+ my $f = $override
+ || $self->option('spreadsheet_format')
+ || FS::Conf->new->config('spreadsheet_format')
+ || 'XLS';
+
+ $formats{$f};
+}
+
+=item is_system_user
+
+Returns true if this user has the name of a known system account. These
+users cannot log into the web interface and can't have passwords set.
+
+=cut
+
+sub is_system_user {
+ my $self = shift;
+ return grep { $_ eq $self->username } ( qw(
+ fs_queue
+ fs_daily
+ fs_selfservice
+ fs_signup
+ fs_bootstrap
+ fs_selfserv
+ fs_api
+ ) );
+}
+
+=item change_password NEW_PASSWORD
+
+=cut
+
+sub change_password {
+ #my( $self, $password ) = @_;
+ #FS::Auth->auth_class->change_password( $self, $password );
+ FS::Auth->auth_class->change_password( @_ );
+}
+
+=item change_password_fields NEW_PASSWORD
+
+=cut
+
+sub change_password_fields {
+ #my( $self, $password ) = @_;
+ #FS::Auth->auth_class->change_password_fields( $self, $password );
+ FS::Auth->auth_class->change_password_fields( @_ );
+}
+
+=back
+
+=head1 BUGS