ucb_ldap 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +127 -0
- data/Manifest +25 -0
- data/README +80 -0
- data/Rakefile +22 -0
- data/TODO +2 -0
- data/init.rb +1 -0
- data/lib/person/adv_con_person.rb +0 -0
- data/lib/person/affiliation_methods.rb +212 -0
- data/lib/person/generic_attributes.rb +68 -0
- data/lib/ucb_ldap.rb +177 -0
- data/lib/ucb_ldap_address.rb +106 -0
- data/lib/ucb_ldap_affiliation.rb +84 -0
- data/lib/ucb_ldap_entry.rb +398 -0
- data/lib/ucb_ldap_exceptions.rb +27 -0
- data/lib/ucb_ldap_namespace.rb +42 -0
- data/lib/ucb_ldap_org.rb +369 -0
- data/lib/ucb_ldap_person.rb +135 -0
- data/lib/ucb_ldap_person_job_appointment.rb +79 -0
- data/lib/ucb_ldap_schema.rb +115 -0
- data/lib/ucb_ldap_schema_attribute.rb +153 -0
- data/lib/ucb_ldap_service.rb +109 -0
- data/lib/ucb_ldap_student_term.rb +101 -0
- data/lib/ucb_simple_ldap_entry.rb +201 -0
- data/schema/schema.yml +2954 -0
- data/ucb_ldap.gemspec +40 -0
- data/version.yml +1 -0
- metadata +124 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
= UCB::LDAP Changelog
|
2
|
+
|
3
|
+
== Version 1.2.1, January 22, 2008
|
4
|
+
|
5
|
+
* fixed bug: include UCB::LDAP call at top level that corrupted namespace
|
6
|
+
* fixed rspec tests to use production ldap server to verify Org structure
|
7
|
+
|
8
|
+
|
9
|
+
== Version 1.2.0, September 20, 2007
|
10
|
+
|
11
|
+
* fixed UCB::LDAP::Person.student_not_registered? which was broken
|
12
|
+
* added support for new entities
|
13
|
+
* Affiliation
|
14
|
+
* Service
|
15
|
+
* StudentTerm
|
16
|
+
* added handling of timestamp attributes
|
17
|
+
* various additions and clean-up to the various employee_*, student_*, affiliate_* methods
|
18
|
+
including handling of expiration
|
19
|
+
* fetches schema from url rather than file packaged with gem
|
20
|
+
* made UCB::LDAP::Person searches exclude test entries by default
|
21
|
+
* Rails applications can use UCB::LDAP.bind_for_rails() to get environment-specific binds
|
22
|
+
|
23
|
+
== Version 1.1.1, August 2, 2007
|
24
|
+
|
25
|
+
* fixed bug around deleting LDAP entries.
|
26
|
+
|
27
|
+
== Version 1.1.0, August 1, 2007
|
28
|
+
|
29
|
+
* added Org#level_<n>_code and Org#level_<n>_name where <n> is 1-6. Returns the
|
30
|
+
org node's level "n" code/name
|
31
|
+
* added option to Org.flattened_tree() to restrict levels returned.
|
32
|
+
|
33
|
+
== Version 1.0.1, July 25, 2007
|
34
|
+
|
35
|
+
* do a better job trapping dropped connections in UCB::LDAP.connection_open?
|
36
|
+
* added Person#test? to check for test entries
|
37
|
+
|
38
|
+
== Version 1.0.0
|
39
|
+
|
40
|
+
* got complete schema info for attributes
|
41
|
+
* replaced attribute synonyms in favor of explicit methods
|
42
|
+
* added support for ldap updates
|
43
|
+
* added support for new entities (if your bind supports it):
|
44
|
+
* appointment
|
45
|
+
* address
|
46
|
+
|
47
|
+
== Version 0.8.1, May 18, 2007
|
48
|
+
|
49
|
+
* added Person#dept_org (synonym for Person#deptid)
|
50
|
+
* added Person#dept_name
|
51
|
+
|
52
|
+
== Version 0.8.0, April 27, 2007
|
53
|
+
|
54
|
+
* added support for privileged binds via UCB::LDAP::authenticate()
|
55
|
+
|
56
|
+
== Version 0.7.0, January 16, 2007
|
57
|
+
|
58
|
+
* updated: UCB::LDAP::Entry.search to use Net::LDAP::Filter objects: this fixed the
|
59
|
+
problem with embedded whitespace
|
60
|
+
* updated UCB::LDAP::Person.person_by_uid() to work with an Integer or a String
|
61
|
+
and UCB::LDAP::Person.persons_by_uids() to work with an Array of Integers or Strings.
|
62
|
+
* updated: UCB::LDAP::Person.student? to utilize the new LDAP v2 ou structure
|
63
|
+
* added: UCB::LDAP::Person.student_registered?, UCB::LDAP::Person.student_not_registered?
|
64
|
+
* deprecated: UCB::LDAP::Person.student_summer?, UCB::LDAP::Person.student_summer_only?,
|
65
|
+
UCB::LDAP::Person.spring?, UCB::LDAP::Person.fall?. Except for UCB::LDAP::Person.student_summer_only?,
|
66
|
+
these methods may be added in later versions of UCB::LDAP::Person. Note: accessing these
|
67
|
+
attributes will required a privileged bind.
|
68
|
+
* changed UCB::LDAP::Entry to only allow filter options in hash form: :filter => {:uid => 12345}
|
69
|
+
* updated documentations
|
70
|
+
* updated unit tests.
|
71
|
+
|
72
|
+
== Version 0.6.0, January 10, 2007
|
73
|
+
|
74
|
+
* Added loading/caching of all nodes
|
75
|
+
* Added calculation of all child nodes
|
76
|
+
* Added UCB::LDAP::Org.flattened_tree()
|
77
|
+
* Added UCB::LDAP::Person.org_node()
|
78
|
+
|
79
|
+
== Version 0.5.0, December 20, 2006
|
80
|
+
|
81
|
+
* Added following methods to Org:
|
82
|
+
* child_nodes
|
83
|
+
* parent_node
|
84
|
+
* parent_nodes
|
85
|
+
* persons
|
86
|
+
|
87
|
+
== Version 0.4.0, December 14, 2006
|
88
|
+
|
89
|
+
* Added Person.persons_by_uids to return array of Person for given
|
90
|
+
array of uids.
|
91
|
+
* Changed implementation of Person.person_by_uid to use new Person.persons_by_uid.
|
92
|
+
|
93
|
+
== Version 0.3.1, December 13, 2006
|
94
|
+
|
95
|
+
* Added SchemaAttribute class and methods to load schema
|
96
|
+
attributes in UCB::LDAP. Did not make use of the schema
|
97
|
+
attributes.
|
98
|
+
|
99
|
+
== Version 0.3.0, October 20, 2006
|
100
|
+
|
101
|
+
* Moved to SVN
|
102
|
+
* Reorganized doc directory
|
103
|
+
|
104
|
+
== Version 0.2.2, October 12, 2006
|
105
|
+
|
106
|
+
* Made more "booleans" actually return <tt>true</tt> and <tt>false</tt>
|
107
|
+
rather than expressions that evaluate to <tt>true</tt> and <tt>false</tt>.
|
108
|
+
|
109
|
+
* Added TODO[link:files/TODO.html].
|
110
|
+
|
111
|
+
* Some documentation updates.
|
112
|
+
|
113
|
+
== Version 0.2.1, October 10, 2006
|
114
|
+
|
115
|
+
* Changed module name and namespace from UcbLdap to UCB::LDAP.
|
116
|
+
* Fixed so UCB::LDAP::Entry and subclasses can be marshalled,
|
117
|
+
otherwise can't store in a Rails session.
|
118
|
+
|
119
|
+
== Version 0.2.0, October 9, 2006
|
120
|
+
|
121
|
+
* Packaged as a Ruby Gem.
|
122
|
+
* Converted underlying code to use Net::LDAP instead of LDAP.
|
123
|
+
* Added support for searching the org unit tree.
|
124
|
+
|
125
|
+
== Version 0.1.0, October 5, 2006
|
126
|
+
|
127
|
+
* Initial release.
|
data/Manifest
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
Manifest
|
3
|
+
README
|
4
|
+
Rakefile
|
5
|
+
TODO
|
6
|
+
init.rb
|
7
|
+
lib/person/adv_con_person.rb
|
8
|
+
lib/person/affiliation_methods.rb
|
9
|
+
lib/person/generic_attributes.rb
|
10
|
+
lib/ucb_ldap.rb
|
11
|
+
lib/ucb_ldap_address.rb
|
12
|
+
lib/ucb_ldap_affiliation.rb
|
13
|
+
lib/ucb_ldap_entry.rb
|
14
|
+
lib/ucb_ldap_exceptions.rb
|
15
|
+
lib/ucb_ldap_namespace.rb
|
16
|
+
lib/ucb_ldap_org.rb
|
17
|
+
lib/ucb_ldap_person.rb
|
18
|
+
lib/ucb_ldap_person_job_appointment.rb
|
19
|
+
lib/ucb_ldap_schema.rb
|
20
|
+
lib/ucb_ldap_schema_attribute.rb
|
21
|
+
lib/ucb_ldap_service.rb
|
22
|
+
lib/ucb_ldap_student_term.rb
|
23
|
+
lib/ucb_simple_ldap_entry.rb
|
24
|
+
schema/schema.yml
|
25
|
+
version.yml
|
data/README
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
=UC Berkeley LDAP
|
2
|
+
|
3
|
+
UCB::LDAP is a wrapper module around Net::LDAP intended to simplify searching the UC Berkeley
|
4
|
+
LDAP directory: http://directory.berkeley.edu
|
5
|
+
|
6
|
+
==Introduction to LDAP
|
7
|
+
If you are blissfully ignorant of LDAP, you should familiarize yourself with some of the basics.
|
8
|
+
Here is a great online resource: http://www.zytrax.com/books/ldap.
|
9
|
+
|
10
|
+
The RDoc for the ruby-net-ldap Gem (http://rubyfurnace.com/docs/ruby-net-ldap-0.0.4/classes/Net/LDAP.html) also has a good introduction to LDAP.
|
11
|
+
|
12
|
+
|
13
|
+
==Examples
|
14
|
+
|
15
|
+
===General Search
|
16
|
+
|
17
|
+
Search the directory specifying tree base and
|
18
|
+
filter, getting back generic UCB::LDAP::Entry instances:
|
19
|
+
|
20
|
+
entries = UCB::LDAP.search(:base => "ou=people,dc=berkeley,dc=edu", :filter => {:uid => 123}
|
21
|
+
|
22
|
+
entry.uid #=> '123'
|
23
|
+
entry.givenname #=> 'John'
|
24
|
+
entry.sn #=> 'Doe'
|
25
|
+
|
26
|
+
See UCB::LDAP::Entry for more information.
|
27
|
+
|
28
|
+
===Person Search
|
29
|
+
|
30
|
+
Search the Person tree getting back UCB::LDAP::Person instances:
|
31
|
+
|
32
|
+
person = UCB::LDAP::Person.person_by_uid "123"
|
33
|
+
|
34
|
+
person.firstname #=> "John"
|
35
|
+
person.affiliations #=> ['EMPLOYEE-TYPE-STAFF']
|
36
|
+
person.employee? #=> true
|
37
|
+
person.employee_staff? #=> true
|
38
|
+
person.employee_academic? #=> false
|
39
|
+
person.student? #=> false
|
40
|
+
|
41
|
+
See UCB::LDAP::Person for more information.
|
42
|
+
|
43
|
+
===Org Unit Search
|
44
|
+
|
45
|
+
Search the Org Unit tree getting back UCB::LDAP::Org instances:
|
46
|
+
|
47
|
+
dept = UCB::LDAP::Org.org_by_ou 'jkasd'
|
48
|
+
|
49
|
+
dept.deptid #=> "JKASD"
|
50
|
+
dept.name #=> "Administrative Systems Dept"
|
51
|
+
|
52
|
+
See UCB::LDAP::Org for more information.
|
53
|
+
|
54
|
+
===Privileged Binds
|
55
|
+
|
56
|
+
If you want access the directory anonomously, no credentials are required.
|
57
|
+
If you want to access via a privileged bind, authenticate before querying:
|
58
|
+
|
59
|
+
p = UCB::LDAP::Person.person_by_uid("123")
|
60
|
+
p.non_public_attr #=> NoMethodError
|
61
|
+
|
62
|
+
UCB::LDAP.authenticate("mybind", "mypassword")
|
63
|
+
p = UCB::LDAP::Person.person_by_uid("123")
|
64
|
+
p.non_public_attr #=> "some value"
|
65
|
+
|
66
|
+
=== Privileged Binds and Rails
|
67
|
+
|
68
|
+
See UCB::LDAP.bind_for_rails()
|
69
|
+
|
70
|
+
==Dependencies
|
71
|
+
|
72
|
+
* Net::LDAP
|
73
|
+
* Ruby 1.8.2 or better
|
74
|
+
|
75
|
+
==Authors
|
76
|
+
|
77
|
+
Steven Hansen runner@berkeley.edu
|
78
|
+
Lucas Rockwell
|
79
|
+
Steve Downey
|
80
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
require 'hanna/rdoctask'
|
5
|
+
|
6
|
+
Echoe.new('ucb_ldap', '1.3.0') do |p|
|
7
|
+
p.description = "Convenience classes for interacing with UCB's LDAP directory"
|
8
|
+
p.url = "http://ucbrb.rubyforge.org/ucb_ldap"
|
9
|
+
p.author = "Steven Hansen, Steve Downey, Lucas Rockwell"
|
10
|
+
p.email = "runner@berkeley.edu"
|
11
|
+
p.ignore_pattern = ["svn_user.yml", "tasks/ucb_ldap.rake", "spec/**/**", "test/**/**"]
|
12
|
+
p.project = "ucbrb"
|
13
|
+
p.runtime_dependencies = ["ruby-net-ldap", ">= 0.0.4"]
|
14
|
+
p.rdoc_options = "-o doc --inline-source -T hanna lib/*.rb"
|
15
|
+
p.rdoc_pattern = ["README", "lib/**/**"]
|
16
|
+
end
|
17
|
+
|
18
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ucb_ldap'
|
File without changes
|
@@ -0,0 +1,212 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module UCB::LDAP
|
4
|
+
module AffiliationMethods
|
5
|
+
|
6
|
+
# Returns an <tt>Array</tt> of Person's affiliations.
|
7
|
+
def affiliations
|
8
|
+
@affiliations ||= berkeleyEduAffiliations.map { |a| a.upcase }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns <tt>true</tt> if entry's affiliations contain _affiliation_.
|
12
|
+
#
|
13
|
+
# Case-insensitive.
|
14
|
+
def has_affiliation?(affiliation)
|
15
|
+
affiliations.include?(affiliation.upcase)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns <tt>true</tt> if Person's affiliations contain at least one affiliation of a particular type.
|
19
|
+
#
|
20
|
+
# p = Person.find_by_uid ...
|
21
|
+
# p.affiliations #=> ['EMPLOYEE-TYPE-STAFF']
|
22
|
+
# p.has_affiliation_of_type?(:employee) #=> true
|
23
|
+
# p.has_affiliation_of_type?(:student) #=> false
|
24
|
+
def has_affiliation_of_type?(affiliation_type)
|
25
|
+
aff_type_string = affiliation_type.to_s.upcase
|
26
|
+
affiliations.find{|a| a =~ /^#{aff_type_string}-TYPE-/} ? true : false
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
############################################
|
31
|
+
# Determine if the person is an EMPLOYEE #
|
32
|
+
############################################
|
33
|
+
|
34
|
+
# Returns <tt>true</tt> if entry has the "staff" affiliation.
|
35
|
+
def employee_staff?
|
36
|
+
has_affiliation? 'EMPLOYEE-TYPE-STAFF'
|
37
|
+
end
|
38
|
+
|
39
|
+
def employee_academic?
|
40
|
+
has_affiliation? 'EMPLOYEE-TYPE-ACADEMIC'
|
41
|
+
end
|
42
|
+
|
43
|
+
def employee_expired?
|
44
|
+
has_affiliation? 'EMPLOYEE-STATUS-EXPIRED'
|
45
|
+
end
|
46
|
+
|
47
|
+
def employee_expiration_date
|
48
|
+
Date.parse(berkeleyEduEmpExpDate.to_s)
|
49
|
+
end
|
50
|
+
|
51
|
+
def employee?
|
52
|
+
has_affiliation_of_type?(:employee) && !employee_expired?
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
##########################################
|
57
|
+
# Determine if the person is a STUDENT #
|
58
|
+
##########################################
|
59
|
+
|
60
|
+
# Returns +true+ if is an expired student.
|
61
|
+
def student_expired?
|
62
|
+
has_affiliation? 'STUDENT-STATUS-EXPIRED'
|
63
|
+
end
|
64
|
+
|
65
|
+
def student_expiration_date
|
66
|
+
Date.parse(berkeleyEduStuExpDate.to_s)
|
67
|
+
end
|
68
|
+
|
69
|
+
def student_registered?
|
70
|
+
has_affiliation? 'STUDENT-TYPE-REGISTERED'
|
71
|
+
end
|
72
|
+
|
73
|
+
def student_not_registered?
|
74
|
+
has_affiliation? 'STUDENT-TYPE-NOT REGISTERED'
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns <tt>true</tt> if entry has a studend affiliation and
|
78
|
+
# is not expired.
|
79
|
+
def student?
|
80
|
+
has_affiliation_of_type?(:student) && !student_expired?
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
##############################################
|
85
|
+
# Determine if the persone is an AFFILIATE #
|
86
|
+
##############################################
|
87
|
+
|
88
|
+
def affiliate_aws_only?
|
89
|
+
has_affiliation? 'AFFILIATE-TYPE-AWS ONLY'
|
90
|
+
end
|
91
|
+
|
92
|
+
def affiliate_staff_retiree?
|
93
|
+
has_affiliation? 'AFFILIATE-TYPE-STAFF RETIREE'
|
94
|
+
end
|
95
|
+
|
96
|
+
def affiliate_emeritus?
|
97
|
+
has_affiliation? 'AFFILIATE-TYPE-EMERITUS'
|
98
|
+
end
|
99
|
+
|
100
|
+
def affiliate_directory_only?
|
101
|
+
has_affiliation? 'AFFILIATE-TYPE-DIRECTORY ONLY'
|
102
|
+
end
|
103
|
+
|
104
|
+
# Note: there are actually 2 types of visting affiliaties, visiting student and
|
105
|
+
# visiting scholars. But for now we will generalize.
|
106
|
+
def affiliate_visiting?
|
107
|
+
has_affiliation? 'AFFILIATE-TYPE-VISITING'
|
108
|
+
end
|
109
|
+
|
110
|
+
def affiliate_contractor?
|
111
|
+
has_affiliation? 'AFFILIATE-TYPE-CONTRACTOR'
|
112
|
+
end
|
113
|
+
|
114
|
+
def affiliate_lbl_doe_postdoc?
|
115
|
+
has_affiliation? 'AFFILIATE-TYPE-LBL/DOE POSTDOC'
|
116
|
+
end
|
117
|
+
|
118
|
+
def affiliate_lbl_op_staff?
|
119
|
+
has_affiliation? 'AFFILIATE-TYPE-LBLOP STAFF'
|
120
|
+
end
|
121
|
+
|
122
|
+
def affiliate_committee_member?
|
123
|
+
has_affiliation? 'AFFILIATE-TYPE-COMMITTEE MEMBER'
|
124
|
+
end
|
125
|
+
|
126
|
+
def affiliate_consultant?
|
127
|
+
has_affiliation? 'AFFILIATE-TYPE-CONSULTANT'
|
128
|
+
end
|
129
|
+
|
130
|
+
def affiliate_volunteer?
|
131
|
+
has_affiliation? 'AFFILIATE-TYPE-VOLUNTEER'
|
132
|
+
end
|
133
|
+
|
134
|
+
def affiliate_hhmi_researcher?
|
135
|
+
has_affiliation? 'AFFILIATE-TYPE-HHMI RESEARCHER'
|
136
|
+
end
|
137
|
+
|
138
|
+
def affiliate_concurrent_enrollment?
|
139
|
+
has_affiliation? 'AFFILIATE-TYPE-CONCURR ENROLL'
|
140
|
+
end
|
141
|
+
|
142
|
+
def affiliate_temp_agency?
|
143
|
+
has_affiliation? 'AFFILIATE-TYPE-TEMP AGENCY'
|
144
|
+
end
|
145
|
+
|
146
|
+
def affiliate_departmental?
|
147
|
+
has_affiliation? 'AFFILIATE-TYPE-DEPARTMENTAL'
|
148
|
+
end
|
149
|
+
|
150
|
+
def affiliate_test?
|
151
|
+
has_affiliation? 'AFFILIATE-TYPE-TEST'
|
152
|
+
end
|
153
|
+
|
154
|
+
def affiliate_staff_affiliate?
|
155
|
+
has_affiliation? 'AFFILIATE-TYPE-STAFF-AFFILIATE'
|
156
|
+
end
|
157
|
+
|
158
|
+
def affiliate_academic_case_tracking?
|
159
|
+
has_affiliation? 'AFFILIATE-TYPE-ACADEMIC CASE TRACKING'
|
160
|
+
end
|
161
|
+
|
162
|
+
def affiliate_maintenance?
|
163
|
+
has_affiliation? 'AFFILIATE-TYPE-MAINTENANCE'
|
164
|
+
end
|
165
|
+
|
166
|
+
def affiliate_billing_only?
|
167
|
+
has_affiliation? 'AFFILIATE-TYPE-BILLING ONLY'
|
168
|
+
end
|
169
|
+
|
170
|
+
def affiliate_advcon_trustee?
|
171
|
+
has_affiliation? 'AFFILIATE-TYPE-ADVCON-TRUSTEE'
|
172
|
+
end
|
173
|
+
|
174
|
+
def affiliate_advcon_friend?
|
175
|
+
has_affiliation? 'AFFILIATE-TYPE-ADVCON-FRIEND'
|
176
|
+
end
|
177
|
+
|
178
|
+
def affiliate_advcon_alumnus?
|
179
|
+
has_affiliation? 'AFFILIATE-TYPE-ADVCON-ALUMNUS'
|
180
|
+
end
|
181
|
+
|
182
|
+
def affiliate_advcon_caa_member?
|
183
|
+
has_affiliation? 'AFFILIATE-TYPE-ADVCON-CAA-MEMBER'
|
184
|
+
end
|
185
|
+
|
186
|
+
def affiliate_advcon_i_house_resident?
|
187
|
+
has_affiliation? 'AFFILIATE-TYPE-ADVCON-I-HOUSE-RESIDENT'
|
188
|
+
end
|
189
|
+
|
190
|
+
def affiliate_advcon_student?
|
191
|
+
has_affiliation? 'AFFILIATE-TYPE-ADVCON-STUDENT'
|
192
|
+
end
|
193
|
+
|
194
|
+
def affiliate_advcon_attendee?
|
195
|
+
has_affiliation? 'AFFILIATE-TYPE-ADVCON-ATTENDEE'
|
196
|
+
end
|
197
|
+
|
198
|
+
def affiliate_expired?
|
199
|
+
has_affiliation? 'AFFILIATE-STATUS-EXPIRED'
|
200
|
+
end
|
201
|
+
|
202
|
+
def affiliate?
|
203
|
+
has_affiliation_of_type?(:affiliate) && !affiliate_expired?
|
204
|
+
end
|
205
|
+
|
206
|
+
def affiliate_expiration_date
|
207
|
+
Date.parse(berkeleyEduAffExpDate.to_s)
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|