| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Net::LDAP::Class; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 10 |  |  | 10 |  | 32350 | use strict; | 
|  | 10 |  |  |  |  | 9 |  | 
|  | 10 |  |  |  |  | 213 |  | 
| 4 | 10 |  |  | 10 |  | 26 | use warnings; | 
|  | 10 |  |  |  |  | 8 |  | 
|  | 10 |  |  |  |  | 191 |  | 
| 5 | 10 |  |  | 10 |  | 44 | use base qw( Rose::Object ); | 
|  | 10 |  |  |  |  | 12 |  | 
|  | 10 |  |  |  |  | 3544 |  | 
| 6 | 10 |  |  | 10 |  | 1451 | use Carp; | 
|  | 10 |  |  |  |  | 13 |  | 
|  | 10 |  |  |  |  | 414 |  | 
| 7 | 10 |  |  | 10 |  | 449 | use Data::Dump (); | 
|  | 10 |  |  |  |  | 5176 |  | 
|  | 10 |  |  |  |  | 113 |  | 
| 8 | 10 |  |  | 10 |  | 964 | use Net::LDAP; | 
|  | 10 |  |  |  |  | 216731 |  | 
|  | 10 |  |  |  |  | 53 |  | 
| 9 | 10 |  |  | 10 |  | 1375 | use Net::LDAP::Entry; | 
|  | 10 |  |  |  |  | 3791 |  | 
|  | 10 |  |  |  |  | 157 |  | 
| 10 | 10 |  |  | 10 |  | 3905 | use Net::LDAP::Control::Paged; | 
|  | 10 |  |  |  |  | 5201 |  | 
|  | 10 |  |  |  |  | 221 |  | 
| 11 | 10 |  |  | 10 |  | 41 | use Net::LDAP::Constant qw(LDAP_CONTROL_PAGED); | 
|  | 10 |  |  |  |  | 11 |  | 
|  | 10 |  |  |  |  | 339 |  | 
| 12 | 10 |  |  | 10 |  | 3881 | use Net::LDAP::Batch; | 
|  | 10 |  |  |  |  | 73408 |  | 
|  | 10 |  |  |  |  | 48 |  | 
| 13 | 10 |  |  | 10 |  | 4518 | use Net::LDAP::Class::Metadata; | 
|  | 10 |  |  |  |  | 22 |  | 
|  | 10 |  |  |  |  | 203 |  | 
| 14 | 10 |  |  | 10 |  | 3627 | use Net::LDAP::Class::Iterator; | 
|  | 10 |  |  |  |  | 20 |  | 
|  | 10 |  |  |  |  | 184 |  | 
| 15 | 10 |  |  | 10 |  | 3479 | use Net::LDAP::Class::MultiIterator; | 
|  | 10 |  |  |  |  | 16 |  | 
|  | 10 |  |  |  |  | 186 |  | 
| 16 | 10 |  |  | 10 |  | 3316 | use Net::LDAP::Class::SimpleIterator; | 
|  | 10 |  |  |  |  | 17 |  | 
|  | 10 |  |  |  |  | 303 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | use Net::LDAP::Class::MethodMaker ( | 
| 19 | 10 |  |  |  |  | 42 | 'scalar --get_set_init' => [qw( ldap ldap_entry debug error )], | 
| 20 |  |  |  |  |  |  | 'scalar'                => [qw( batch prev_batch )], | 
| 21 |  |  |  |  |  |  | 'object_or_class_meta'  => [qw( attributes unique_attributes base_dn )], | 
| 22 | 10 |  |  | 10 |  | 43 | ); | 
|  | 10 |  |  |  |  | 10 |  | 
| 23 |  |  |  |  |  |  |  | 
| 24 | 10 |  |  | 10 |  | 527 | use overload '""' => 'stringify', 'bool' => sub {1}, 'fallback' => 1; | 
|  | 10 |  |  | 1308 |  | 10 |  | 
|  | 10 |  |  |  |  | 77 |  | 
|  | 1308 |  |  |  |  | 6294 |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | our $VERSION = '0.27'; | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | =head1 NAME | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | Net::LDAP::Class - object-relational mapper for Net::LDAP | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | # define your class | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | package MyLDAPClass; | 
| 37 |  |  |  |  |  |  | use base qw( Net::LDAP::Class ); | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | __PACKAGE__->metadata->setup( | 
| 40 |  |  |  |  |  |  | attributes          => [qw( name address phone email )], | 
| 41 |  |  |  |  |  |  | unique_attributes   => [qw( email )], | 
| 42 |  |  |  |  |  |  | base_dn             => 'dc=mycompany,dc=com', | 
| 43 |  |  |  |  |  |  | ); | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | 1; | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | # then use your class | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | use MyLDAPClass; | 
| 50 |  |  |  |  |  |  | use Net::LDAP; | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | my $ldap = create_Net_LDAP_object_and_bind();  # you write this | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | # create an instance of your class | 
| 55 |  |  |  |  |  |  | my $person = MyLDAPClass->new( ldap => $ldap, email => 'foo@bar.com' ); | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | # load from LDAP or write if not yet existing | 
| 58 |  |  |  |  |  |  | $person->read or $person->create; | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | # set the 'name' attribute | 
| 61 |  |  |  |  |  |  | $person->name( 'Joe Foo' ); | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | # write your changes | 
| 64 |  |  |  |  |  |  | $person->update; | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | # change your mind? | 
| 67 |  |  |  |  |  |  | $person->delete; | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | Net::LDAP::Class (NLC) is an object-relational mapping for LDAP. | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | I know, it's all wrong to confuse the ORM model with LDAP | 
| 75 |  |  |  |  |  |  | since LDAP is not relational in the same way that a RDBMS is. But the ORM | 
| 76 |  |  |  |  |  |  | APIs of projects like DBIx::Class and Rose::DB::Object are so fun and easy to use, | 
| 77 |  |  |  |  |  |  | it seemed like LDAP management should be just as fun and easy. | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | The architecture of this package is based on Rose::DB::Object, which the author | 
| 80 |  |  |  |  |  |  | uses to great effect for RDBMS management. | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | =head1 METHODS | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | NLC uses the Rose::Object package to create methods and handle the mundane get/set features. | 
| 85 |  |  |  |  |  |  | In addition, Net::LDAP::Class::MethodMaker implements a new method type called B | 
| 86 |  |  |  |  |  |  | which handles the get/set/fetch of NLC objects related to a given NLC object. Typically these | 
| 87 |  |  |  |  |  |  | are Users and Groups. A User is typically related to one or more Groups, and a Group is typically | 
| 88 |  |  |  |  |  |  | related to one or more Users. See Net::LDAP::Class::User and Net::LDAP::Class::Group for | 
| 89 |  |  |  |  |  |  | examples. | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | There are some methods which every NLC subclass must implement. See L for details. | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | =head2 init | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | Override this in a subclass. Be sure to call SUPER::init in your subclass. | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | =cut | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | sub init { | 
| 100 | 472 |  |  | 472 | 1 | 502 | my $self = shift; | 
| 101 | 472 |  |  |  |  | 1079 | $self->SUPER::init(@_); | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 472 |  |  |  |  | 5063 | my $meta = $self->metadata; | 
| 104 | 472 | 50 | 33 |  |  | 1870 | if ( !$meta or !$meta->is_initialized ) { | 
| 105 | 0 |  |  |  |  | 0 | croak | 
| 106 |  |  |  |  |  |  | "must initialize Metadata class before instantiating a new object"; | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  |  | 
| 109 | 472 |  | 66 |  |  | 2932 | $self->{ldap} ||= $self->init_ldap; | 
| 110 |  |  |  |  |  |  |  | 
| 111 | 472 | 50 |  |  |  | 880 | if ( !$self->ldap->isa('Net::LDAP') ) { | 
| 112 | 0 |  |  |  |  | 0 | croak "ldap value is not a Net::LDAP-derived object"; | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 472 |  |  |  |  | 2872 | return $self; | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | =head2 metadata_class | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | Returns 'Net::LDAP::Class::Metadata' by default. | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | =cut | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 480 |  |  | 480 | 1 | 2112 | sub metadata_class {'Net::LDAP::Class::Metadata'} | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | =head2 metadata | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | Returns an instance of the metadata_class() containing all the metadata for | 
| 129 |  |  |  |  |  |  | the NLC class. May be called as a class or object method. | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | =cut | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | sub metadata { | 
| 134 | 1276 |  |  | 1276 | 1 | 25097 | my ($self) = shift; | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | # object method | 
| 137 | 1276 | 100 |  |  |  | 2315 | if ( ref $self ) { | 
| 138 |  |  |  |  |  |  | return $self->{_meta} | 
| 139 | 1119 |  | 66 |  |  | 5158 | ||= $self->metadata_class->for_class( ref $self ); | 
| 140 |  |  |  |  |  |  | } | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | # class method | 
| 143 | 157 |  | 66 |  |  | 990 | return $Net::LDAP::Class::Metadata::Objects{$self} | 
| 144 |  |  |  |  |  |  | || $self->metadata_class->for_class($self); | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | =head2 init_ldap | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | If you do not pass a Net::LDAP object to new(), you may instead | 
| 150 |  |  |  |  |  |  | set the ldap_uri() class method to a URI string and | 
| 151 |  |  |  |  |  |  | init_ldap() will create a Net::LDAP object and bind() it for you. | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | Returns a Net::LDAP object. | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | =cut | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | sub init_ldap { | 
| 158 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 159 | 0 | 0 |  |  |  | 0 | if ( !$self->ldap_uri ) { | 
| 160 | 0 |  |  |  |  | 0 | croak "must set ldap_uri() or override init_ldap()"; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 | 0 | 0 |  |  |  | 0 | my $ldap = Net::LDAP->new( $self->ldap_uri ) | 
| 164 |  |  |  |  |  |  | or croak "can't create new Net::LDAP: $!"; | 
| 165 | 0 | 0 |  |  |  | 0 | my $msg = $ldap->bind() or croak "can't do anonymous LDAP bind: $!"; | 
| 166 | 0 | 0 |  |  |  | 0 | if ( $msg->code ) { | 
| 167 | 0 |  |  |  |  | 0 | croak "LDAP bind failed: " . $self->get_ldap_error($msg); | 
| 168 |  |  |  |  |  |  | } | 
| 169 | 0 |  |  |  |  | 0 | return $ldap; | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | =head2 init_debug | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | Sets the default debug flag to whatever the PERL_DEBUG or LDAP_DEBUG | 
| 175 |  |  |  |  |  |  | env variable is set to. | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | =cut | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 649 | 50 |  | 649 | 1 | 6974 | sub init_debug { $ENV{PERL_DEBUG} || $ENV{LDAP_DEBUG} } | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | =head2 init_ldap_entry | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | Returns undef by default. | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =cut | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 1416 |  |  | 1416 | 1 | 7623 | sub init_ldap_entry { return undef } | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =head2 get_ldap_error( I ) | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | Stringify the error message for the I object. | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | =cut | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | sub get_ldap_error { | 
| 196 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 197 | 0 | 0 |  |  |  | 0 | my $msg  = shift or croak "ldap_msg required"; | 
| 198 | 0 |  |  |  |  | 0 | my $str  = "\n" | 
| 199 |  |  |  |  |  |  | . join( "\n", | 
| 200 |  |  |  |  |  |  | "Return code: " . $msg->code, | 
| 201 |  |  |  |  |  |  | "Message: " . $msg->error_name, | 
| 202 |  |  |  |  |  |  | " :" . $msg->error_text, | 
| 203 |  |  |  |  |  |  | "MessageID: " . $msg->mesg_id, | 
| 204 |  |  |  |  |  |  | "DN: " . $msg->dn, | 
| 205 |  |  |  |  |  |  | ) . "\n"; | 
| 206 | 0 |  |  |  |  | 0 | return $str; | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | =head2 stringify | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | Returns the first unique attribute value that is not undef. If no | 
| 212 |  |  |  |  |  |  | such value is found, returns the object. | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | By default all NLC-derived objects are overloaded with this method. | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | =cut | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | sub stringify { | 
| 219 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 220 | 0 |  |  |  |  | 0 | for my $key ( @{ $self->unique_attributes } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 221 | 0 |  |  |  |  | 0 | my $val = $self->$key; | 
| 222 | 0 | 0 |  |  |  | 0 | return $val if defined $val; | 
| 223 |  |  |  |  |  |  | } | 
| 224 | 0 |  |  |  |  | 0 | return $self; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | =head2 find( I ) | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | Returns array (or array ref if called in scalar context) | 
| 230 |  |  |  |  |  |  | of objects matching I. | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | I may include: | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | =over | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | =item ldap | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | If not present, the ldap() method is called instead. | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | =item base_dn | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | If not present, the base_dn() method is called instead. | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | =back | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | Any other I are passed directly to the Net::LDAP search() | 
| 247 |  |  |  |  |  |  | method. | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | Returns undef if no results matching I are found. | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | =cut | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | sub find { | 
| 254 | 86 |  |  | 86 | 1 | 172 | my $self  = shift; | 
| 255 | 86 |  | 66 |  |  | 358 | my $class = ref($self) || $self; | 
| 256 | 86 |  |  |  |  | 363 | my %opts  = @_; | 
| 257 | 86 |  | 66 |  |  | 360 | my $ldap  = delete $opts{ldap} || $self->ldap; | 
| 258 |  |  |  |  |  |  |  | 
| 259 | 86 | 50 |  |  |  | 450 | if ( !$ldap ) { | 
| 260 | 0 |  |  |  |  | 0 | croak "Net::LDAP object required"; | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 | 86 |  | 33 |  |  | 258 | my $base = delete $opts{base_dn} || $self->base_dn; | 
| 264 |  |  |  |  |  |  |  | 
| 265 | 86 | 50 |  |  |  | 220 | if ( !$base ) { | 
| 266 | 0 |  |  |  |  | 0 | croak "must indicate base_dn in opts or call as object method"; | 
| 267 |  |  |  |  |  |  | } | 
| 268 |  |  |  |  |  |  |  | 
| 269 | 86 |  | 33 |  |  | 447 | my $attr = delete $opts{attrs} || $self->attributes; | 
| 270 |  |  |  |  |  |  |  | 
| 271 | 86 |  |  |  |  | 416 | my $msg = $ldap->search( | 
| 272 |  |  |  |  |  |  | base  => $base, | 
| 273 |  |  |  |  |  |  | attrs => $attr, | 
| 274 |  |  |  |  |  |  | %opts, | 
| 275 |  |  |  |  |  |  | ); | 
| 276 |  |  |  |  |  |  |  | 
| 277 | 86 | 50 |  |  |  | 2165438 | if ( $msg->code ) { | 
| 278 | 0 |  |  |  |  | 0 | croak $self->get_ldap_error($msg); | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  |  | 
| 281 | 86 |  |  |  |  | 713 | my @results; | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 86 |  |  |  |  | 322 | for my $entry ( $msg->entries() ) { | 
| 284 | 52 |  |  |  |  | 710 | push( | 
| 285 |  |  |  |  |  |  | @results, | 
| 286 |  |  |  |  |  |  | $class->new( | 
| 287 |  |  |  |  |  |  | ldap       => $ldap, | 
| 288 |  |  |  |  |  |  | ldap_entry => $entry, | 
| 289 |  |  |  |  |  |  | ) | 
| 290 |  |  |  |  |  |  | ); | 
| 291 |  |  |  |  |  |  | } | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 86 | 100 |  |  |  | 731 | return unless @results; | 
| 294 | 50 | 100 |  |  |  | 525 | return wantarray ? @results : \@results; | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | =head2 create | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | Write a new object to the database. Calls the action_for_create() method -- see L. | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | =cut | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | sub create { | 
| 304 | 59 |  |  | 59 | 1 | 16744 | my $self = shift; | 
| 305 | 59 | 50 |  |  |  | 221 | unless ( $self->check_unique_attributes_set ) { | 
| 306 | 0 |  |  |  |  | 0 | croak | 
| 307 |  |  |  |  |  |  | "at least one unique attribute must be set in order to create()"; | 
| 308 |  |  |  |  |  |  | } | 
| 309 | 59 | 50 |  |  |  | 329 | my @action = $self->action_for_create(@_) or return; | 
| 310 | 59 |  |  |  |  | 314 | $self->do_batch(@action); | 
| 311 | 59 | 50 |  |  |  | 306 | $self->read or croak "cannot read newly created $self"; | 
| 312 | 59 |  |  |  |  | 606 | return $self; | 
| 313 |  |  |  |  |  |  | } | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | =head2 read | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | Read an object's attribute values from the database. You must have | 
| 318 |  |  |  |  |  |  | previously set at least one unique attribute in the object | 
| 319 |  |  |  |  |  |  | in order for the read() to work. | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | Returns the object on success, undef if the object was not found. | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | =cut | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | sub read { | 
| 326 | 183 |  |  | 183 | 1 | 1485 | my $self = shift; | 
| 327 | 183 |  |  |  |  | 354 | my %opts = @_; | 
| 328 |  |  |  |  |  |  |  | 
| 329 | 183 |  |  |  |  | 234 | my ( $filter, $value ); | 
| 330 |  |  |  |  |  |  |  | 
| 331 | 183 | 50 | 33 |  |  | 1016 | if ( !$opts{filter} && !$opts{value} ) { | 
| 332 |  |  |  |  |  |  |  | 
| 333 | 183 | 50 |  |  |  | 451 | unless ( $self->check_unique_attributes_set ) { | 
| 334 |  |  |  |  |  |  | croak "cannot read() without unique attribute set. " | 
| 335 |  |  |  |  |  |  | . "Unique attributes include: " | 
| 336 | 0 |  |  |  |  | 0 | . join( ', ', @{ $self->unique_attributes } ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | # get first unique key set for filter | 
| 340 |  |  |  |  |  |  |  | 
| 341 | 183 |  |  |  |  | 277 | for my $key ( @{ $self->unique_attributes } ) { | 
|  | 183 |  |  |  |  | 422 |  | 
| 342 | 197 | 100 |  |  |  | 471 | if ( defined $self->$key ) { | 
| 343 | 183 |  |  |  |  | 250 | $filter = $key; | 
| 344 | 183 |  |  |  |  | 419 | $value  = $self->$key; | 
| 345 | 183 |  |  |  |  | 313 | last; | 
| 346 |  |  |  |  |  |  | } | 
| 347 |  |  |  |  |  |  | } | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | } | 
| 350 |  |  |  |  |  |  | else { | 
| 351 | 0 |  |  |  |  | 0 | $filter = delete $opts{filter}; | 
| 352 | 0 |  |  |  |  | 0 | $value  = delete $opts{value}; | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 183 | 50 |  |  |  | 431 | if ( !defined $filter ) { | 
| 356 | 0 |  |  |  |  | 0 | croak "could not find a unique filter to read() on"; | 
| 357 |  |  |  |  |  |  | } | 
| 358 | 183 | 50 |  |  |  | 443 | if ( !defined $value ) { | 
| 359 | 0 |  |  |  |  | 0 | croak "could not find a unique value to read() on"; | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  |  | 
| 362 | 183 |  | 66 |  |  | 698 | my $base_dn = delete $opts{base_dn} || $self->base_dn; | 
| 363 |  |  |  |  |  |  |  | 
| 364 | 183 | 50 |  |  |  | 466 | $self->debug && warn "read() within $base_dn : $filter=$value\n"; | 
| 365 |  |  |  |  |  |  |  | 
| 366 | 183 |  |  |  |  | 467 | my $msg = $self->ldap->search( | 
| 367 |  |  |  |  |  |  | base   => $base_dn, | 
| 368 |  |  |  |  |  |  | scope  => "sub", | 
| 369 |  |  |  |  |  |  | filter => "($filter=$value)", | 
| 370 |  |  |  |  |  |  | attrs  => $self->attributes, | 
| 371 |  |  |  |  |  |  | ); | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 183 | 50 |  |  |  | 7700371 | if ( $msg->count() > 0 ) { | 
| 374 | 183 | 50 |  |  |  | 2980 | carp "$filter $value exists" if $self->debug; | 
| 375 |  |  |  |  |  |  |  | 
| 376 | 183 |  |  |  |  | 663 | my $entry = $msg->entry(0); | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | # set any entry attributes we've got cached in $self | 
| 379 | 183 |  |  |  |  | 4484 | for my $attr ( keys %{ $self->{_not_yet_set} } ) { | 
|  | 183 |  |  |  |  | 770 |  | 
| 380 |  |  |  |  |  |  |  | 
| 381 | 204 |  |  |  |  | 384 | my $new = $self->{_not_yet_set}->{$attr}; | 
| 382 | 204 |  | 50 |  |  | 763 | my $old = $entry->get_value($attr) || ''; | 
| 383 | 204 | 100 |  |  |  | 5628 | if ( $new ne $old ) { | 
| 384 |  |  |  |  |  |  | $entry->replace( $attr, | 
| 385 | 21 |  |  |  |  | 94 | delete $self->{_not_yet_set}->{$attr} ); | 
| 386 | 21 |  |  |  |  | 513 | $self->{_was_set}->{$attr}->{new} = $new; | 
| 387 | 21 |  |  |  |  | 60 | $self->{_was_set}->{$attr}->{old} = $old; | 
| 388 |  |  |  |  |  |  | } | 
| 389 |  |  |  |  |  |  | else { | 
| 390 | 183 |  |  |  |  | 387 | delete $self->{_not_yet_set}->{$attr}; | 
| 391 |  |  |  |  |  |  | } | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | } | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | # this will cause any existing entry to be DESTROYed | 
| 396 | 183 |  |  |  |  | 673 | $self->ldap_entry($entry); | 
| 397 |  |  |  |  |  |  |  | 
| 398 | 183 |  |  |  |  | 2736 | return $self; | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  | else { | 
| 401 | 0 |  |  |  |  | 0 | return; | 
| 402 |  |  |  |  |  |  | } | 
| 403 |  |  |  |  |  |  | } | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | =head2 update | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | Write changes to the database. Calls action_for_update() -- see L. | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | If no changes are detected, aborts and returns undef. | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | On successful write, returns the value of read(). | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | =cut | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | sub update { | 
| 416 | 8 |  |  | 8 | 1 | 580 | my $self = shift; | 
| 417 | 8 |  |  |  |  | 26 | $self->check_unique_attributes_set; | 
| 418 | 8 | 50 |  |  |  | 20 | unless ( $self->ldap_entry ) { | 
| 419 | 0 |  |  |  |  | 0 | croak "can't update() without first having a Net::LDAP::Entry loaded"; | 
| 420 |  |  |  |  |  |  | } | 
| 421 | 8 | 50 |  |  |  | 81 | my @action = $self->action_for_update(@_) or return; | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | # clear, since action_for_update() has already used them. | 
| 424 | 8 |  |  |  |  | 20 | $self->{_was_set} = {}; | 
| 425 |  |  |  |  |  |  |  | 
| 426 | 8 |  |  |  |  | 38 | $self->do_batch(@action); | 
| 427 |  |  |  |  |  |  |  | 
| 428 | 8 |  |  |  |  | 78 | return $self->read; | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | =head2 delete | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | Remove the object from the database. You must call read() first. | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | Returns the value of do_batch(). | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  | =cut | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | sub delete { | 
| 440 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 441 | 0 |  |  |  |  | 0 | $self->check_unique_attributes_set; | 
| 442 | 0 | 0 |  |  |  | 0 | unless ( $self->ldap_entry ) { | 
| 443 | 0 |  |  |  |  | 0 | croak "can't delete() without having a Net::LDAP::Entry loaded"; | 
| 444 |  |  |  |  |  |  | } | 
| 445 | 0 | 0 |  |  |  | 0 | my @action = $self->action_for_delete or return; | 
| 446 | 0 |  |  |  |  | 0 | return $self->do_batch(@action); | 
| 447 |  |  |  |  |  |  | } | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | =head2 read_or_create | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | Convenience method. If read() returns undef, create() is called. | 
| 452 |  |  |  |  |  |  | Returns the object in any case. | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | =cut | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | sub read_or_create { | 
| 457 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 458 | 0 | 0 |  |  |  | 0 | if ( !$self->read(@_) ) { | 
| 459 | 0 |  |  |  |  | 0 | $self->create(@_); | 
| 460 |  |  |  |  |  |  | } | 
| 461 | 0 |  |  |  |  | 0 | return $self; | 
| 462 |  |  |  |  |  |  | } | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | =head2 save | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | Convenience method. If ldap_entry() is set, update() is called. | 
| 467 |  |  |  |  |  |  | Otherwise, read_or_create() is called. The NLC object is returned | 
| 468 |  |  |  |  |  |  | in any case. | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | =cut | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | sub save { | 
| 473 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 474 | 0 | 0 |  |  |  | 0 | if ( $self->ldap_entry ) { | 
| 475 | 0 |  |  |  |  | 0 | $self->update; | 
| 476 |  |  |  |  |  |  | } | 
| 477 |  |  |  |  |  |  | else { | 
| 478 | 0 |  |  |  |  | 0 | $self->read_or_create; | 
| 479 |  |  |  |  |  |  | } | 
| 480 | 0 |  |  |  |  | 0 | return $self; | 
| 481 |  |  |  |  |  |  | } | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | =head2 validate( I, I ) | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | Called by MethodMaker every time an attribute is set with | 
| 486 |  |  |  |  |  |  | a MethodMaker-created method. | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | If validate() returns true, I is set. If validate() | 
| 489 |  |  |  |  |  |  | returns false, a fatal error is thrown and error() set. | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | This method should be overriden in your subclass to provide | 
| 492 |  |  |  |  |  |  | schema-specific validation. The default behaviour is a no-op | 
| 493 |  |  |  |  |  |  | (always returns true). | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | =cut | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | sub validate { | 
| 498 | 105 |  |  | 105 | 1 | 165 | my ( $self, $attr, $value ) = @_; | 
| 499 | 105 | 50 |  |  |  | 221 | if ( $self->debug ) { | 
| 500 | 0 |  |  |  |  | 0 | warn "validate $attr: $value\n"; | 
| 501 |  |  |  |  |  |  | } | 
| 502 | 105 |  |  |  |  | 266 | return 1; | 
| 503 |  |  |  |  |  |  | } | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | =head2 do_batch( I ) | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | Creates (if batch() is not already set) | 
| 508 |  |  |  |  |  |  | and runs a Net::LDAP::Batch object, passing it | 
| 509 |  |  |  |  |  |  | the I to run. Will croak on any error. | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | Returns the Net::LDAP::Batch object on success. | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | =cut | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | sub do_batch { | 
| 516 | 67 |  |  | 67 | 1 | 117 | my $self    = shift; | 
| 517 | 67 |  |  |  |  | 119 | my @actions = @_; | 
| 518 | 67 | 50 |  |  |  | 173 | if ( !@actions ) { | 
| 519 | 0 |  |  |  |  | 0 | warn "no actions to execute\n"; | 
| 520 | 0 |  |  |  |  | 0 | return; | 
| 521 |  |  |  |  |  |  | } | 
| 522 | 67 |  | 33 |  |  | 466 | my $batch = $self->batch || Net::LDAP::Batch->new( | 
| 523 |  |  |  |  |  |  | ldap  => $self->ldap, | 
| 524 |  |  |  |  |  |  | debug => $self->debug, | 
| 525 |  |  |  |  |  |  | ); | 
| 526 | 67 | 50 |  |  |  | 2636 | if ( $self->debug ) { | 
| 527 | 0 |  |  |  |  | 0 | warn "Batch: " . Data::Dump::dump($batch); | 
| 528 |  |  |  |  |  |  | } | 
| 529 | 67 |  |  |  |  | 270 | $batch->add_actions(@actions); | 
| 530 | 67 |  |  |  |  | 8613 | $self->prev_batch($batch); | 
| 531 | 67 | 50 |  |  |  | 205 | $batch->do or croak $batch->error; | 
| 532 | 67 |  |  |  |  | 2879206 | return $batch; | 
| 533 |  |  |  |  |  |  | } | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | =head2 add_to_batch( I ) | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | Initializes (if necessary) and adds I | 
| 538 |  |  |  |  |  |  | to the Net::LDAP::Batch object in batch(). | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | =cut | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  | sub add_to_batch { | 
| 543 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 544 | 0 |  | 0 |  |  | 0 | my $batch = $self->batch || Net::LDAP::Batch->new( | 
| 545 |  |  |  |  |  |  | ldap  => $self->ldap, | 
| 546 |  |  |  |  |  |  | debug => $self->debug | 
| 547 |  |  |  |  |  |  | ); | 
| 548 | 0 |  |  |  |  | 0 | $batch->add_actions(@_); | 
| 549 | 0 |  |  |  |  | 0 | $self->batch($batch); | 
| 550 |  |  |  |  |  |  | } | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | =head2 rollback | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | Will call the rollback() method on the Net::LDAP::Batch object returned | 
| 555 |  |  |  |  |  |  | by batch(). If there is not batch() set, will croak. | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | =cut | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | sub rollback { | 
| 560 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 561 | 0 | 0 |  |  |  | 0 | if ( $self->prev_batch ) { | 
| 562 | 0 | 0 |  |  |  | 0 | $self->prev_batch->rollback or croak $self->prev_batch->error; | 
| 563 |  |  |  |  |  |  | } | 
| 564 |  |  |  |  |  |  | else { | 
| 565 | 0 |  |  |  |  | 0 | croak "no prev_batch to rollback"; | 
| 566 |  |  |  |  |  |  | } | 
| 567 | 0 |  |  |  |  | 0 | return 1; | 
| 568 |  |  |  |  |  |  | } | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | =head2 action_for_create | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | See L. | 
| 573 |  |  |  |  |  |  |  | 
| 574 |  |  |  |  |  |  | =cut | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | sub action_for_create { | 
| 577 | 0 |  |  | 0 | 1 | 0 | croak "must override action_for_create()"; | 
| 578 |  |  |  |  |  |  | } | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | =head2 action_for_update | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | See L. | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | =cut | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | sub action_for_update { | 
| 587 | 0 |  |  | 0 | 1 | 0 | croak "must override action_for_update()"; | 
| 588 |  |  |  |  |  |  | } | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | =head2 action_for_delete | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | See L. | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | =cut | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | sub action_for_delete { | 
| 597 | 0 |  |  | 0 | 1 | 0 | croak "must override action_for_delete()"; | 
| 598 |  |  |  |  |  |  | } | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | =head2 check_unique_attributes_set | 
| 601 |  |  |  |  |  |  |  | 
| 602 |  |  |  |  |  |  | Returns true (1) if any unique attribute is set | 
| 603 |  |  |  |  |  |  | with a defined value. | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | Returns false (0) if no unique attributes are set. | 
| 606 |  |  |  |  |  |  |  | 
| 607 |  |  |  |  |  |  | =cut | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | sub check_unique_attributes_set { | 
| 610 | 250 |  |  | 250 | 1 | 317 | my $self = shift; | 
| 611 | 250 |  |  |  |  | 756 | my $uk   = $self->unique_attributes; | 
| 612 | 250 | 50 |  |  |  | 711 | if ( !ref($uk) eq 'ARRAY' ) { | 
| 613 | 0 |  |  |  |  | 0 | croak "unique_attributes must be an ARRAY ref"; | 
| 614 |  |  |  |  |  |  | } | 
| 615 | 250 |  |  |  |  | 494 | for my $key (@$uk) { | 
| 616 | 264 | 100 |  |  |  | 785 | if ( defined $self->$key ) { | 
| 617 | 250 |  |  |  |  | 767 | return 1; | 
| 618 |  |  |  |  |  |  | } | 
| 619 |  |  |  |  |  |  | } | 
| 620 | 0 |  |  |  |  | 0 | return 0; | 
| 621 |  |  |  |  |  |  | } | 
| 622 |  |  |  |  |  |  |  | 
| 623 |  |  |  |  |  |  | =head2 AUTOLOAD | 
| 624 |  |  |  |  |  |  |  | 
| 625 |  |  |  |  |  |  | Will croak() with a helpful message if you call a method that does | 
| 626 |  |  |  |  |  |  | not exist. Mostly useful for catching cases where you forget to predefine | 
| 627 |  |  |  |  |  |  | an attribute. | 
| 628 |  |  |  |  |  |  |  | 
| 629 |  |  |  |  |  |  | =cut | 
| 630 |  |  |  |  |  |  |  | 
| 631 |  |  |  |  |  |  | sub AUTOLOAD { | 
| 632 | 0 |  |  | 0 |  | 0 | my ( $self, @args ) = @_; | 
| 633 |  |  |  |  |  |  |  | 
| 634 | 0 |  |  |  |  | 0 | my ($attribute) = ( our $AUTOLOAD =~ /([^:]+)$/ ); | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | #    carp "AUTOLOAD called for " | 
| 637 |  |  |  |  |  |  | #        . ref($self) | 
| 638 |  |  |  |  |  |  | #        . " -> $attribute " | 
| 639 |  |  |  |  |  |  | #        . Data::Dump::dump( \@args ); | 
| 640 |  |  |  |  |  |  |  | 
| 641 | 0 | 0 |  |  |  | 0 | if ( $attribute eq 'DESTROY' ) { | 
| 642 |  |  |  |  |  |  |  | 
| 643 |  |  |  |  |  |  | #Data::Dump::dump($self); | 
| 644 | 0 |  |  |  |  | 0 | return; | 
| 645 |  |  |  |  |  |  | } | 
| 646 |  |  |  |  |  |  |  | 
| 647 | 0 |  |  |  |  | 0 | croak qq[no such attribute or method "$attribute" defined for package "] | 
| 648 |  |  |  |  |  |  | . ref($self) | 
| 649 |  |  |  |  |  |  | . qq[ -- do you need to add '$attribute' to your setup() call?"]; | 
| 650 |  |  |  |  |  |  | } | 
| 651 |  |  |  |  |  |  |  | 
| 652 |  |  |  |  |  |  | =head2 dump | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | Returns Data::Dump::dump output for the NLC object. Useful for debugging. | 
| 655 |  |  |  |  |  |  | See also the Net::LDAP::Entry dump() method which can be called on the ldap_entry | 
| 656 |  |  |  |  |  |  | value. | 
| 657 |  |  |  |  |  |  |  | 
| 658 |  |  |  |  |  |  | $nlc->dump;                # same as Data::Dump::dump( $nlc ) | 
| 659 |  |  |  |  |  |  | $nlc->ldap_entry->dump;    # see Net::LDAP::Entry dump() method | 
| 660 |  |  |  |  |  |  |  | 
| 661 |  |  |  |  |  |  | =cut | 
| 662 |  |  |  |  |  |  |  | 
| 663 |  |  |  |  |  |  | sub dump { | 
| 664 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 665 | 0 |  |  |  |  | 0 | return Data::Dump::dump($self); | 
| 666 |  |  |  |  |  |  | } | 
| 667 |  |  |  |  |  |  |  | 
| 668 |  |  |  |  |  |  | =head2 has_local_changes | 
| 669 |  |  |  |  |  |  |  | 
| 670 |  |  |  |  |  |  | Convenience method. Returns true if the object has had any values | 
| 671 |  |  |  |  |  |  | set since the last time it was written to the server. | 
| 672 |  |  |  |  |  |  |  | 
| 673 |  |  |  |  |  |  | =cut | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | sub has_local_changes { | 
| 676 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 677 | 0 |  |  |  |  | 0 | return scalar keys %{ $self->{_was_set} }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 678 |  |  |  |  |  |  | } | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | =head2 batch | 
| 681 |  |  |  |  |  |  |  | 
| 682 |  |  |  |  |  |  | Get/set the Net::LDAP::Batch object for the current transaction. Typically you | 
| 683 |  |  |  |  |  |  | don't want to mess with this but documented for completeness. | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | =head2 prev_batch | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | Get/set the Net::LDAP::Batch object for the just-completed transaction. | 
| 688 |  |  |  |  |  |  | Typically you don't want to mess with this but documented for completeness. | 
| 689 |  |  |  |  |  |  |  | 
| 690 |  |  |  |  |  |  | =cut | 
| 691 |  |  |  |  |  |  |  | 
| 692 |  |  |  |  |  |  | =head2 act_on_all( I [, I] ) | 
| 693 |  |  |  |  |  |  |  | 
| 694 |  |  |  |  |  |  | Performs I sub reference on all records in LDAP. | 
| 695 |  |  |  |  |  |  | The I should expect one argument: a Net::LDAP::Class-derived | 
| 696 |  |  |  |  |  |  | object. | 
| 697 |  |  |  |  |  |  |  | 
| 698 |  |  |  |  |  |  | act_on_all() operates using Net::LDAP::Control::Paged, performing | 
| 699 |  |  |  |  |  |  | a search() using a filter based on unique_attributes() and iterating | 
| 700 |  |  |  |  |  |  | over all matches in groups of (by default) 500. You may set the | 
| 701 |  |  |  |  |  |  | pager size in I. I should be a hash ref. The following | 
| 702 |  |  |  |  |  |  | key/value pairs are supported: | 
| 703 |  |  |  |  |  |  |  | 
| 704 |  |  |  |  |  |  | =over | 
| 705 |  |  |  |  |  |  |  | 
| 706 |  |  |  |  |  |  | =item page_size | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | Default: 500. Sets the Net::LDAP::Control::Paged size. | 
| 709 |  |  |  |  |  |  |  | 
| 710 |  |  |  |  |  |  | =item filter | 
| 711 |  |  |  |  |  |  |  | 
| 712 |  |  |  |  |  |  | Default: unique_atttributes->[0] = '*' | 
| 713 |  |  |  |  |  |  |  | 
| 714 |  |  |  |  |  |  | Set the filter for the search. | 
| 715 |  |  |  |  |  |  |  | 
| 716 |  |  |  |  |  |  | =item ldap | 
| 717 |  |  |  |  |  |  |  | 
| 718 |  |  |  |  |  |  | A Net::LDAP object. B | 
| 719 |  |  |  |  |  |  |  | 
| 720 |  |  |  |  |  |  | =back | 
| 721 |  |  |  |  |  |  |  | 
| 722 |  |  |  |  |  |  | Returns the number of Net::LDAP::Class results acted upon. | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  | =cut | 
| 725 |  |  |  |  |  |  |  | 
| 726 |  |  |  |  |  |  | sub act_on_all { | 
| 727 | 1 |  |  | 1 | 1 | 78 | my $self    = shift; | 
| 728 | 1 | 50 |  |  |  | 8 | my $coderef = shift or croak "coderef required"; | 
| 729 | 1 |  | 50 |  |  | 14 | my $opts    = shift || {}; | 
| 730 | 1 |  | 33 |  |  | 5 | my $class   = ref($self) || $self; | 
| 731 |  |  |  |  |  |  |  | 
| 732 | 1 | 50 |  |  |  | 5 | if ( ref $coderef ne 'CODE' ) { | 
| 733 | 0 |  |  |  |  | 0 | croak "coderef is not a CODE reference"; | 
| 734 |  |  |  |  |  |  | } | 
| 735 |  |  |  |  |  |  |  | 
| 736 |  |  |  |  |  |  | my $filter = $opts->{filter} | 
| 737 | 1 |  | 33 |  |  | 16 | || $self->metadata->unique_attributes->[0] . '=*'; | 
| 738 | 1 |  | 50 |  |  | 9 | my $page_size = $opts->{page_size} || 500; | 
| 739 |  |  |  |  |  |  |  | 
| 740 | 1 |  | 33 |  |  | 9 | my $ldap = $opts->{ldap} || $self->ldap; | 
| 741 | 1 |  |  |  |  | 56 | my $page = Net::LDAP::Control::Paged->new( size => $page_size ); | 
| 742 | 1 |  |  |  |  | 79 | my $cookie; | 
| 743 | 1 |  |  |  |  | 6 | my @args = ( | 
| 744 |  |  |  |  |  |  | 'base'    => $self->metadata->base_dn, | 
| 745 |  |  |  |  |  |  | 'filter'  => "($filter)", | 
| 746 |  |  |  |  |  |  | 'attrs'   => $self->metadata->attributes, | 
| 747 |  |  |  |  |  |  | 'control' => [$page], | 
| 748 |  |  |  |  |  |  | ); | 
| 749 |  |  |  |  |  |  |  | 
| 750 | 1 |  |  |  |  | 3 | my $count = 0; | 
| 751 |  |  |  |  |  |  |  | 
| 752 | 1 |  |  |  |  | 7 | PAGE: while ( my $ldap_search = $ldap->search(@args) ) { | 
| 753 |  |  |  |  |  |  |  | 
| 754 |  |  |  |  |  |  | # fatal on search error | 
| 755 | 2 | 50 |  |  |  | 64358 | croak "error searching ldap: ", $self->get_ldap_error($ldap_search) | 
| 756 |  |  |  |  |  |  | if ( $ldap_search->code ); | 
| 757 |  |  |  |  |  |  |  | 
| 758 | 2 |  |  |  |  | 22 | ENTRY: while ( my $ldap_entry = $ldap_search->shift_entry ) { | 
| 759 |  |  |  |  |  |  |  | 
| 760 | 7 |  |  |  |  | 179 | $count++; | 
| 761 |  |  |  |  |  |  |  | 
| 762 | 7 |  |  |  |  | 17 | my $nlc = $class->new( | 
| 763 |  |  |  |  |  |  | ldap       => $ldap, | 
| 764 |  |  |  |  |  |  | ldap_entry => $ldap_entry | 
| 765 |  |  |  |  |  |  | ); | 
| 766 |  |  |  |  |  |  |  | 
| 767 | 7 | 50 |  |  |  | 17 | $self->debug and warn sprintf( "%6d %s\n", $count, $nlc ); | 
| 768 |  |  |  |  |  |  |  | 
| 769 | 7 |  |  |  |  | 11 | $coderef->($nlc); | 
| 770 |  |  |  |  |  |  | } | 
| 771 |  |  |  |  |  |  |  | 
| 772 |  |  |  |  |  |  | # handle next search page | 
| 773 | 2 | 50 |  |  |  | 77 | my ($resp) = $ldap_search->control(LDAP_CONTROL_PAGED) or last PAGE; | 
| 774 | 2 |  |  |  |  | 373 | $cookie = $resp->cookie; | 
| 775 | 2 | 100 |  |  |  | 231 | if ( !$cookie ) { | 
| 776 | 1 |  |  |  |  | 3 | last PAGE; | 
| 777 |  |  |  |  |  |  | } | 
| 778 | 1 |  |  |  |  | 3 | $page->cookie($cookie); | 
| 779 |  |  |  |  |  |  |  | 
| 780 |  |  |  |  |  |  | } | 
| 781 |  |  |  |  |  |  |  | 
| 782 |  |  |  |  |  |  | # be nice to the server and stop the search if we still have a cookie | 
| 783 | 1 | 50 |  |  |  | 7 | if ($cookie) { | 
| 784 | 0 |  |  |  |  | 0 | $page->cookie($cookie); | 
| 785 | 0 |  |  |  |  | 0 | $page->size(0); | 
| 786 | 0 |  |  |  |  | 0 | $ldap->ldap->search(@args); | 
| 787 | 0 |  |  |  |  | 0 | croak "LDAP seach ended prematurely."; | 
| 788 |  |  |  |  |  |  | } | 
| 789 |  |  |  |  |  |  |  | 
| 790 | 1 |  |  |  |  | 14 | return $count; | 
| 791 |  |  |  |  |  |  |  | 
| 792 |  |  |  |  |  |  | } | 
| 793 |  |  |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | =head2 isa_user | 
| 795 |  |  |  |  |  |  |  | 
| 796 |  |  |  |  |  |  | Convenience method. Just returns shift->isa('Net::LDAP::Class::User'). | 
| 797 |  |  |  |  |  |  |  | 
| 798 |  |  |  |  |  |  | =cut | 
| 799 |  |  |  |  |  |  |  | 
| 800 |  |  |  |  |  |  | sub isa_user { | 
| 801 | 0 |  |  | 0 | 1 |  | return shift->isa('Net::LDAP::Class::User'); | 
| 802 |  |  |  |  |  |  | } | 
| 803 |  |  |  |  |  |  |  | 
| 804 |  |  |  |  |  |  | =head2 isa_group | 
| 805 |  |  |  |  |  |  |  | 
| 806 |  |  |  |  |  |  | Just like isa_user() but checks the Net::LDAP::Class::Group. | 
| 807 |  |  |  |  |  |  |  | 
| 808 |  |  |  |  |  |  | =cut | 
| 809 |  |  |  |  |  |  |  | 
| 810 |  |  |  |  |  |  | sub isa_group { | 
| 811 | 0 |  |  | 0 | 1 |  | return shift->isa('Net::LDAP::Class::Group'); | 
| 812 |  |  |  |  |  |  | } | 
| 813 |  |  |  |  |  |  |  | 
| 814 |  |  |  |  |  |  | 1; | 
| 815 |  |  |  |  |  |  |  | 
| 816 |  |  |  |  |  |  | __END__ |