gestion 1.9.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +48 -0
- data/.project +14 -0
- data/Binaries/adduser_to_buzz +15 -0
- data/Binaries/backup +7 -0
- data/Binaries/check_gestion +8 -0
- data/Binaries/gestion.gnumail +22 -0
- data/Binaries/gestion.logrotate +34 -0
- data/Binaries/gestion.service +12 -0
- data/Binaries/gestion_update.rb +183 -0
- data/Binaries/gestion_update.service +10 -0
- data/Binaries/get_compta +11 -0
- data/Binaries/kill_gestion +16 -0
- data/Binaries/ldap/add_indexes +51 -0
- data/Binaries/ldap/backup +2 -0
- data/Binaries/ldap/install_ldap +92 -0
- data/Binaries/ldap/restore +7 -0
- data/Binaries/lib_backup +5 -0
- data/Binaries/log_scan_errors +8 -0
- data/Binaries/loop_gestion +64 -0
- data/Binaries/onetimers/sync_courses_from_compta.rb +74 -0
- data/Binaries/onetimers/transfer_cash_from_ldap_to_csv +26 -0
- data/Binaries/reboot +5 -0
- data/Binaries/restore +3 -0
- data/Binaries/restore_do +22 -0
- data/Binaries/sort_events +31 -0
- data/Binaries/start_gestion +18 -0
- data/Binaries/swipe_gestion +18 -0
- data/Binaries/update_africompta +21 -0
- data/Binaries/update_users +3 -0
- data/Diplomas.src/accredited.odg +0 -0
- data/Diplomas.src/diploma.odg +0 -0
- data/Diplomas.src/label.odg +0 -0
- data/Diplomas.src/presence_sheet.ods +0 -0
- data/Diplomas.src/presence_sheet_small.ods +0 -0
- data/Diplomas.src/student_card.odg +0 -0
- data/Doc/130514-it-ideas.odt +0 -0
- data/Doc/Compta-cash.mm +179 -0
- data/Doc/General.odt +0 -0
- data/Entities/AccessGroups.rb +117 -0
- data/Entities/Activity.rb +178 -0
- data/Entities/ChatMsg.rb +142 -0
- data/Entities/Classroom.rb +11 -0
- data/Entities/Client.rb +19 -0
- data/Entities/Computer.rb +21 -0
- data/Entities/ConfigBase.rb +280 -0
- data/Entities/Course.rb +1588 -0
- data/Entities/CourseType.rb +171 -0
- data/Entities/DFiles.rb +466 -0
- data/Entities/FilesManage.rb +226 -0
- data/Entities/Grade.rb +186 -0
- data/Entities/Internet.rb +300 -0
- data/Entities/Netdev.rb +10 -0
- data/Entities/Person.rb +1175 -0
- data/Entities/Plug.rb +98 -0
- data/Entities/Quiz.rb +33 -0
- data/Entities/Recharges.rb +37 -0
- data/Entities/Report.rb +136 -0
- data/Entities/Room.rb +12 -0
- data/Entities/SMS.rb +30 -0
- data/Entities/ScheduleType.rb +33 -0
- data/Entities/Share.rb +120 -0
- data/Entities/Task.rb +51 -0
- data/Entities/Ticket.rb +72 -0
- data/Entities/Usage.rb +143 -0
- data/Entities/Worker.rb +29 -0
- data/Files/apache-profeda.conf +36 -0
- data/Files/label.erb +121 -0
- data/Files/label_notfound.erb +64 -0
- data/Files/label_notpassed.erb +84 -0
- data/Files/mobileinfo.erb +115 -0
- data/Files/smb.conf +333 -0
- data/Files/timetable.html +36 -0
- data/Files/timetable.js +239 -0
- data/Gemfile +12 -0
- data/Gemfile.dev +12 -0
- data/Gemfile.dev.lock +127 -0
- data/Gemfile.lock +127 -0
- data/Gemfile.prod +8 -0
- data/Gestion +35 -0
- data/Gestion.rb +220 -0
- data/INSTALL +40 -0
- data/Images/connection.xcf +0 -0
- data/Images/connection_no.png +0 -0
- data/Images/connection_wait.png +0 -0
- data/Images/connection_yes.png +0 -0
- data/Paths/Exas.rb +13 -0
- data/Paths/Files.rb +19 -0
- data/Paths/GetDiplomas.rb +20 -0
- data/Paths/Info.rb +114 -0
- data/Paths/Label.rb +187 -0
- data/Paths/MobileInfo.rb +19 -0
- data/Paths/internetCash.rb +34 -0
- data/Paths/internetWifi.rb +54 -0
- data/README.md +60 -0
- data/Rakefile +13 -0
- data/TODO +1391 -0
- data/Test/.gitignore +3 -0
- data/Test/Diplomas/base_gestion.odt +0 -0
- data/Test/Diplomas/base_report.odt +0 -0
- data/Test/Diplomas/carte_etudiant.odg +0 -0
- data/Test/Diplomas/exam_language.odt +0 -0
- data/Test/Diplomas/label.odg +0 -0
- data/Test/Diplomas/presence_sheet.ods +0 -0
- data/Test/Diplomas/presence_sheet_small.ods +0 -0
- data/Test/Diplomas/student_card.odg +0 -0
- data/Test/Manual/testMerge +18 -0
- data/Test/config_test.yaml +26 -0
- data/Test/db.testGestion +0 -0
- data/Test/dfiles/descs/avg-rescue.desc +10 -0
- data/Test/dfiles/descs/avg.desc +8 -0
- data/Test/dfiles/descs/driver.desc +8 -0
- data/Test/dfiles/descs/linuxmint.desc +7 -0
- data/Test/dfiles/files/avg-160203.exe +1 -0
- data/Test/dfiles/files/avg.iso +1 -0
- data/Test/dfiles/files/driver.exe +1 -0
- data/Test/dfiles/index_post.html +3 -0
- data/Test/dfiles/index_pre.html +8 -0
- data/Test/dfiles/priorities +5 -0
- data/Test/ge_activity.rb +124 -0
- data/Test/ge_chat.rb +106 -0
- data/Test/ge_compta.rb +67 -0
- data/Test/ge_configbase.rb +54 -0
- data/Test/ge_course.rb +1114 -0
- data/Test/ge_dfiles.rb +121 -0
- data/Test/ge_filesmanage.rb +180 -0
- data/Test/ge_info.rb +27 -0
- data/Test/ge_internet.rb +246 -0
- data/Test/ge_login.rb +55 -0
- data/Test/ge_person.rb +373 -0
- data/Test/ge_qvinfo.rb +28 -0
- data/Test/ge_report.rb +97 -0
- data/Test/ge_share.rb +27 -0
- data/Test/ge_sms.rb +34 -0
- data/Test/ge_tasks.rb +19 -0
- data/Test/ge_usage.rb +168 -0
- data/Test/ge_view.rb +46 -0
- data/Test/multiconf-captive +29 -0
- data/Test/test.conf +7 -0
- data/Test/test.rb +49 -0
- data/Test/test_bytes.png +0 -0
- data/VERSION +140 -0
- data/Views/Admin/Backup.rb +91 -0
- data/Views/Admin/Configuration.rb +44 -0
- data/Views/Admin/Credit.rb +32 -0
- data/Views/Admin/FilesManage.rb +219 -0
- data/Views/Admin/Function.rb +39 -0
- data/Views/Admin/Power.rb +49 -0
- data/Views/Admin/Printer.rb +37 -0
- data/Views/Admin/Server.rb +252 -0
- data/Views/Admin/Tabs.rb +5 -0
- data/Views/Admin/Update.rb +73 -0
- data/Views/Admin/UpdateSystem.rb +26 -0
- data/Views/Cashbox/Activity.rb +191 -0
- data/Views/Cashbox/Course.rb +141 -0
- data/Views/Cashbox/Credit.rb +79 -0
- data/Views/Cashbox/Report.rb +115 -0
- data/Views/Cashbox/Service.rb +105 -0
- data/Views/Cashbox/Tabs.rb +10 -0
- data/Views/Compta/Accounts.rb +36 -0
- data/Views/Compta/Course.rb +96 -0
- data/Views/Compta/Show.rb +6 -0
- data/Views/Compta/Transfer.rb +66 -0
- data/Views/Course/Diploma.rb +203 -0
- data/Views/Course/Grade.rb +401 -0
- data/Views/Course/Modify.rb +447 -0
- data/Views/Course/Print.rb +94 -0
- data/Views/Course/Responsible.rb +44 -0
- data/Views/Course/Stats.rb +76 -0
- data/Views/Course/Students.rb +92 -0
- data/Views/Course/Tabs.rb +220 -0
- data/Views/Internet/Access.rb +134 -0
- data/Views/Internet/ClassEdit.rb +24 -0
- data/Views/Internet/ClassUsers.rb +81 -0
- data/Views/Internet/Config.rb +32 -0
- data/Views/Internet/Mobile.rb +213 -0
- data/Views/Internet/Recharges.rb +49 -0
- data/Views/Internet/Tabs.rb +6 -0
- data/Views/Inventory/Computer.rb +24 -0
- data/Views/Inventory/Room.rb +18 -0
- data/Views/Inventory/Tabs.rb +9 -0
- data/Views/Inventory/TicketClosed.rb +7 -0
- data/Views/Inventory/TicketOpen.rb +23 -0
- data/Views/Library/Person.rb +36 -0
- data/Views/Library/Tabs.rb +7 -0
- data/Views/Network/Block.rb +87 -0
- data/Views/Network/Netdevs.rb +21 -0
- data/Views/Network/Restriction.rb +37 -0
- data/Views/Network/Share.rb +167 -0
- data/Views/Network/Tables.rb +28 -0
- data/Views/Network/Tabs.rb +6 -0
- data/Views/Person/Admin.rb +99 -0
- data/Views/Person/Center.rb +48 -0
- data/Views/Person/Course.rb +72 -0
- data/Views/Person/Modify.rb +153 -0
- data/Views/Person/Tabs.rb +162 -0
- data/Views/Report/ComptaExecutive.rb +221 -0
- data/Views/Report/ComptaFlat.rb +79 -0
- data/Views/Report/ReportCourse.rb +47 -0
- data/Views/Report/Tabs.rb +8 -0
- data/Views/Report/Usage.rb +52 -0
- data/Views/Report/UsageCases.rb +59 -0
- data/Views/Self/Cash.rb +67 -0
- data/Views/Self/Chat.rb +55 -0
- data/Views/Self/Concours.rb +109 -0
- data/Views/Self/Email.rb +34 -0
- data/Views/Self/Internet.rb +255 -0
- data/Views/Self/Results.rb +17 -0
- data/Views/Self/Services.rb +85 -0
- data/Views/Self/Show.rb +47 -0
- data/Views/Self/Tabs.rb +5 -0
- data/Views/Special/DFileEdit.rb +13 -0
- data/Views/Special/PlugEdit.rb +56 -0
- data/Views/Special/Tabs.rb +6 -0
- data/Views/Special/Vnc.rb +39 -0
- data/Views/Task/Client.rb +21 -0
- data/Views/Task/Edit.rb +33 -0
- data/Views/Task/List.rb +55 -0
- data/Views/Task/Tabs.rb +9 -0
- data/Views/Task/Worker.rb +30 -0
- data/Views/Template/Activity.rb +33 -0
- data/Views/Template/CourseType.rb +63 -0
- data/Views/Template/ScheduleType.rb +29 -0
- data/Views/Template/Tabs.rb +5 -0
- data/Views/Welcome.rb +121 -0
- data/config.yaml.default +36 -0
- data/po/Gestion-ar.po +2356 -0
- data/po/Gestion-en.mo +0 -0
- data/po/Gestion-en.po +4363 -0
- data/po/Gestion-fr.mo +0 -0
- data/po/Gestion-fr.po +4345 -0
- data/po/traduction-ar.rtf +76 -0
- metadata +381 -0
data/Entities/Person.rb
ADDED
|
@@ -0,0 +1,1175 @@
|
|
|
1
|
+
# Holds the data available for a person. The permission is a regexp
|
|
2
|
+
# giving access to different views and it's blocks
|
|
3
|
+
#
|
|
4
|
+
# Configuration:
|
|
5
|
+
# adduser_cmd - is called after ldapadduser returns with the username as argument
|
|
6
|
+
|
|
7
|
+
class String
|
|
8
|
+
def capitalize_all
|
|
9
|
+
self.split(' ').collect { |s| s.capitalize }.join(' ')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def capitalize_all!
|
|
13
|
+
self.replace self.capitalize_all
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class IsNecessary < Exception
|
|
18
|
+
attr :for_course
|
|
19
|
+
|
|
20
|
+
def initialize(course)
|
|
21
|
+
@for_course = course
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Persons < Entities
|
|
27
|
+
attr_accessor :print_card_student, :print_card_responsible, :resps
|
|
28
|
+
self.needs :Courses
|
|
29
|
+
|
|
30
|
+
def setup_data
|
|
31
|
+
add_new_storage :LDAP
|
|
32
|
+
|
|
33
|
+
value_block :address
|
|
34
|
+
value_str_LDAP :first_name, :ldap_name => 'sn'
|
|
35
|
+
value_str_LDAP :family_name, :ldap_name => 'givenname'
|
|
36
|
+
value_list_drop :gender, '%w( male female n/a )'
|
|
37
|
+
value_date :birthday
|
|
38
|
+
value_str :address
|
|
39
|
+
value_str_LDAP :phone, :ldap_name => 'mobile'
|
|
40
|
+
value_str_LDAP :email, :ldap_name => 'mail'
|
|
41
|
+
value_str_LDAP :town, :ldap_name => 'l'
|
|
42
|
+
value_str_LDAP :country, :ldap_name => 'st'
|
|
43
|
+
value_str :profession
|
|
44
|
+
value_str :school_grade
|
|
45
|
+
|
|
46
|
+
value_block :admin
|
|
47
|
+
#value_str :account_name_due
|
|
48
|
+
#value_str :account_name_cash
|
|
49
|
+
value_str :role_diploma
|
|
50
|
+
value_list :permissions, 'Permission.list.sort'
|
|
51
|
+
|
|
52
|
+
value_block :accounts
|
|
53
|
+
value_entity_account :account_due
|
|
54
|
+
value_entity_account :account_due_paid
|
|
55
|
+
value_entity_account :account_cash
|
|
56
|
+
|
|
57
|
+
value_block :internet
|
|
58
|
+
value_list :groups, '%w( freesurf sudo print localonly share ).sort'
|
|
59
|
+
value_list_single :internet_none, '[]'
|
|
60
|
+
|
|
61
|
+
value_block :read_only
|
|
62
|
+
value_str_ro_LDAP :login_name, :ldap_name => 'uid'
|
|
63
|
+
# credit -> internet_credit
|
|
64
|
+
# credit_due -> account_total_due
|
|
65
|
+
value_int_ro :internet_credit
|
|
66
|
+
value_int_ro :account_total_due
|
|
67
|
+
|
|
68
|
+
value_block :email_account
|
|
69
|
+
value_str :acc_remote
|
|
70
|
+
value_str :acc_pass
|
|
71
|
+
value_list_drop :acc_proto, '%w( POP3 IMAP )'
|
|
72
|
+
value_list_drop :acc_port, '%w( 110 993 995 )'
|
|
73
|
+
value_list_drop :acc_supp, '["ssl keep", "ssl", "keep", ""]'
|
|
74
|
+
|
|
75
|
+
value_block :hidden
|
|
76
|
+
value_str :session_id
|
|
77
|
+
value_str_LDAP :password, :ldap_name => 'userPassword'
|
|
78
|
+
value_str :password_plain
|
|
79
|
+
value_int_LDAP :person_id, :ldap_name => 'uidnumber'
|
|
80
|
+
|
|
81
|
+
@resps = []
|
|
82
|
+
@resps_course = []
|
|
83
|
+
ConfigBases.add_observer(self, :update_config)
|
|
84
|
+
update_config(nil, nil, nil)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.admin_users
|
|
88
|
+
ConfigBase.persons_add_del_users == %w(true) &&
|
|
89
|
+
!get_config(false, :Entities, :Persons, :user_simulation)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def update_config(action, value, old)
|
|
93
|
+
dputs(3) { "Updating #{action} with #{value.inspect}" }
|
|
94
|
+
case action
|
|
95
|
+
when :function_add
|
|
96
|
+
if value.index :accounting_courses
|
|
97
|
+
search_all_.each { |p|
|
|
98
|
+
dputs(3) { "Searching if #{p.login_name} needs new accounts" }
|
|
99
|
+
p.update_accounts
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
when :function_del
|
|
103
|
+
else
|
|
104
|
+
ddir = Courses.dir_diplomas
|
|
105
|
+
cdir = "#{ddir}/cartes"
|
|
106
|
+
if !File.exist? cdir
|
|
107
|
+
FileUtils::mkdir_p(cdir)
|
|
108
|
+
end
|
|
109
|
+
@print_card_student = OpenPrint.new(
|
|
110
|
+
ConfigBase.template_path(:card_student), cdir)
|
|
111
|
+
@print_card_responsible = OpenPrint.new(
|
|
112
|
+
ConfigBase.template_path(:card_responsible), cdir)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def migration_1(p)
|
|
117
|
+
if p.person_id == 0
|
|
118
|
+
dputs(0) { 'Error: Oups, found person with id 0 - trying to change this' }
|
|
119
|
+
p.person_id = Persons.new_id[:person_id]
|
|
120
|
+
dputs(2) { "Putting person-id to #{p.person_id}" }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def migration_2_raw(p)
|
|
125
|
+
dputs(2) { "p is #{p.class}" }
|
|
126
|
+
p._internet_credit = p._credit
|
|
127
|
+
p._account_total_due = p._credit_due
|
|
128
|
+
p._account_name_due = p._account_due
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def migration_3(p)
|
|
132
|
+
if p.permissions.class != Array
|
|
133
|
+
p.permissions = []
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def migration_4(p)
|
|
138
|
+
p.gender = %w( n/a )
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def fetch_account(r, a)
|
|
142
|
+
Accounts.get_by_path_or_create("#{r}::#{a}")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def migration_5_raw(d)
|
|
146
|
+
#dp d.inspect
|
|
147
|
+
if d._account_name_due
|
|
148
|
+
r = get_config('Root::Lending', :Accounting, :lending)
|
|
149
|
+
d._account_due = fetch_account(r, d._account_name_due).id
|
|
150
|
+
d._account_due_paid = fetch_account(r, "#{d._account_name_due}::Paid").id
|
|
151
|
+
end
|
|
152
|
+
if d._account_name_cash
|
|
153
|
+
r = get_config('Root::Cash', :Accounting, :cash)
|
|
154
|
+
d._account_cash = fetch_account(r, d._account_name_cash).id
|
|
155
|
+
end
|
|
156
|
+
#dp d.inspect
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Searches for an empty name starting with "login", adding 2, 3, 4, ...
|
|
160
|
+
def find_empty_login_name(login)
|
|
161
|
+
suffix = ''
|
|
162
|
+
login = accents_replace(login)
|
|
163
|
+
while match_by_login_name(login + suffix.to_s)
|
|
164
|
+
dputs(2) { "Found #{login + suffix.to_s} to already exist" }
|
|
165
|
+
suffix = suffix.to_i + 1
|
|
166
|
+
suffix = 2 if suffix == 1
|
|
167
|
+
end
|
|
168
|
+
login + suffix.to_s
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def create_first_family(fullname)
|
|
172
|
+
# Trying to be intelligent about splitting up of names
|
|
173
|
+
if fullname.split(' ').length > 1
|
|
174
|
+
names = fullname.split(' ')
|
|
175
|
+
s = names.length < 4 ? 0 : 1
|
|
176
|
+
first = names[0..s].join(' ')
|
|
177
|
+
family = names[(s+1)..-1].join(' ')
|
|
178
|
+
dputs(2) { "Creating user #{names.inspect} as #{first} - #{family}" }
|
|
179
|
+
[first, family]
|
|
180
|
+
else
|
|
181
|
+
[fullname, '']
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def accents_replace(login)
|
|
186
|
+
login = login.downcase.gsub(/ /, '_')
|
|
187
|
+
accents = Hash[*%w( a àáâä e éèêë i ìíîï o òóôöœ u ùúûü c ç ss ß )]
|
|
188
|
+
dputs(2) { "Login was #{login}" }
|
|
189
|
+
accents.each { |k, v|
|
|
190
|
+
login.gsub!(/[#{v}]/, k)
|
|
191
|
+
}
|
|
192
|
+
login.gsub!(/[^a-z0-9_-]/, '_')
|
|
193
|
+
dputs(2) { "Login is #{login}" }
|
|
194
|
+
login
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Creates a login-name out of "first" and "family" name - should work nicely in
|
|
198
|
+
# Africa, perhaps a bit bizarre in Western-countries (like "tlinus" instead of
|
|
199
|
+
# "ltorvalds")
|
|
200
|
+
# if "family" is empty, it tries to generate some sensible values for "first" and
|
|
201
|
+
# "family" by itself
|
|
202
|
+
def create_login_name(first, family = '')
|
|
203
|
+
if family.length == 0
|
|
204
|
+
first, family = create_first_family(first)
|
|
205
|
+
end
|
|
206
|
+
if family.length > 0
|
|
207
|
+
dputs(2) { 'Family name + First name' }
|
|
208
|
+
login = family.chars.first + first.split.first
|
|
209
|
+
else
|
|
210
|
+
login = first.split.first
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
accents_replace(login)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def create(d)
|
|
217
|
+
# Sanitize first and family-name
|
|
218
|
+
if d.has_key? :complete_name
|
|
219
|
+
d[:first_name] = d[:complete_name]
|
|
220
|
+
end
|
|
221
|
+
if d.has_key? :first_name
|
|
222
|
+
if not d.has_key? :family_name or d[:family_name].length == 0
|
|
223
|
+
d[:first_name], d[:family_name] = create_first_family(d[:first_name])
|
|
224
|
+
end
|
|
225
|
+
d[:first_name].capitalize_all!
|
|
226
|
+
d[:family_name].capitalize_all!
|
|
227
|
+
elsif d.has_key? :login_name
|
|
228
|
+
d[:first_name] = d[:login_name]
|
|
229
|
+
else
|
|
230
|
+
dputs(0) { "Error: Trying to create Person with missing names: #{d.inspect}" }
|
|
231
|
+
return nil
|
|
232
|
+
end
|
|
233
|
+
if !d[:login_name] or d[:login_name].length == 0
|
|
234
|
+
d[:login_name] = create_login_name(d[:first_name], d[:family_name])
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
if d.has_key? :login_name_prefix
|
|
238
|
+
d[:login_name] = d[:login_name_prefix] + d[:login_name]
|
|
239
|
+
end
|
|
240
|
+
d[:login_name] = find_empty_login_name(d[:login_name])
|
|
241
|
+
d[:person_id] = nil
|
|
242
|
+
log_msg :person, "Creating Person #{d.inspect}"
|
|
243
|
+
|
|
244
|
+
person = super(d, true)
|
|
245
|
+
|
|
246
|
+
person.password_plain = d.has_key?(:password) ? d[:password] : rand(10000).to_s.rjust(4, '0')
|
|
247
|
+
person.password = person.password_plain
|
|
248
|
+
|
|
249
|
+
if (cmd = ConfigBase.persons_addeduser_cmd).to_s.length > 0
|
|
250
|
+
dputs(2) { "Going to call #{cmd} #{person.login_name} #{person.password_plain}" }
|
|
251
|
+
System.run_bool("#{cmd} #{person.login_name} #{person.password_plain}")
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
return person
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def self.create_empty
|
|
258
|
+
Entities.Persons.create([])
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def create_person(full_name, creator = nil, login_prop = nil)
|
|
262
|
+
new_data = {:login_name => login_prop,
|
|
263
|
+
:complete_name => full_name}
|
|
264
|
+
perms = ['internet']
|
|
265
|
+
if creator and creator.permissions.index('center')
|
|
266
|
+
new_data.merge!({:login_name_prefix => "#{creator.login_name}_"})
|
|
267
|
+
perms.push('teacher')
|
|
268
|
+
end
|
|
269
|
+
Persons.create(new_data.merge(:permissions => perms))
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def update(session)
|
|
273
|
+
super(session).merge({:account_total_due => session.owner.internet_credit})
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Adds cash to a persons account. The hash "data" should contain the following
|
|
277
|
+
# fields:
|
|
278
|
+
# - credit_add : how much CFA to add
|
|
279
|
+
# - person_id : the id of the person to receive the credit
|
|
280
|
+
def add_internet_credit(session, data)
|
|
281
|
+
dputs(5) { "data is #{data.inspect}" }
|
|
282
|
+
client = match_by_login_name(data['login_name'].to_s)
|
|
283
|
+
if data._credit_add and client
|
|
284
|
+
actor = session.owner
|
|
285
|
+
dputs(3) { "Adding cash to #{client.full_name} from #{actor.full_name}" }
|
|
286
|
+
if client.login_name.to_s.length > 0
|
|
287
|
+
actor.add_internet_credit(client, data._credit_add)
|
|
288
|
+
else
|
|
289
|
+
dputs(0) { "Bizarre client: #{client.inspect} - #{session.inspect} - #{data.inspect}" }
|
|
290
|
+
end
|
|
291
|
+
return client
|
|
292
|
+
end
|
|
293
|
+
return nil
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def get_login_with_permission(perm)
|
|
297
|
+
#persons = Entities.Persons.search_by_permissions(perm)
|
|
298
|
+
persons = Persons.data.select { |k, v|
|
|
299
|
+
v._permissions && v._permissions.index(perm) }.
|
|
300
|
+
collect { |k, v| Persons.get_data_instance(k) }
|
|
301
|
+
if persons
|
|
302
|
+
# The "select" at the end removes empty entries
|
|
303
|
+
persons.collect { |p|
|
|
304
|
+
p.login_name
|
|
305
|
+
}.select { |s| s }
|
|
306
|
+
else
|
|
307
|
+
[]
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def list_teachers
|
|
312
|
+
get_login_with_permission('teacher').sort
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def list_assistants
|
|
316
|
+
get_login_with_permission('assistant').sort
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def list_students
|
|
320
|
+
get_login_with_permission('student').sort
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def listp_account_due
|
|
324
|
+
search_by_account_due('.+').select { |p|
|
|
325
|
+
# CashboxCredit-permission is the least for everybody who handles money.
|
|
326
|
+
p.login_name != 'admin' && p.has_permission?(:CashboxCredit)
|
|
327
|
+
}.collect { |p|
|
|
328
|
+
if p.account_due != nil
|
|
329
|
+
dputs(4) { "p is #{p.full_name}" }
|
|
330
|
+
dputs(4) { "account is #{p.account_due.get_path}" }
|
|
331
|
+
amount = (p.account_due.total.to_f * 1000).to_i.separator
|
|
332
|
+
name = p.full_name
|
|
333
|
+
if name.length == 0
|
|
334
|
+
name = p.login_name
|
|
335
|
+
end
|
|
336
|
+
[p.person_id, "#{amount.to_s.rjust(6)} - #{name}"]
|
|
337
|
+
else
|
|
338
|
+
[p.person_id, "0 - #{name}"]
|
|
339
|
+
end
|
|
340
|
+
}.sort { |a, b|
|
|
341
|
+
a[1] <=> b[1]
|
|
342
|
+
}.reverse
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def save_data(d)
|
|
346
|
+
d = d.to_sym
|
|
347
|
+
dputs(3) { "d is #{d.inspect}" }
|
|
348
|
+
|
|
349
|
+
if !d[:first_name] and !d[:person_id]
|
|
350
|
+
return {:first_name => 'user'}
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
[:first_name, :family_name].each { |n|
|
|
354
|
+
d[n] && d[n].capitalize_all!
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
super(d)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def data_create(data)
|
|
361
|
+
dputs(2) { "Creating new data #{data.inspect}" }
|
|
362
|
+
if has_storage? :LDAP
|
|
363
|
+
user = data[:login_name]
|
|
364
|
+
if Kernel.system("ldapadduser #{user} plugdev")
|
|
365
|
+
if (cmd = ConfigBase.persons_adduser_cmd).to_s.length > 0
|
|
366
|
+
dputs(2) { "Going to call #{cmd} #{user.inspect}" }
|
|
367
|
+
System.run_bool("#{cmd} #{user}")
|
|
368
|
+
end
|
|
369
|
+
else
|
|
370
|
+
dputs(0) { "Error: Couldn't create #{user}" }
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def find_full_name(name)
|
|
376
|
+
dputs(2) { "Searching for #{name}" }
|
|
377
|
+
@data.each_key { |k|
|
|
378
|
+
if @data[k]
|
|
379
|
+
fn = "#{data[k][:first_name]} #{data[k][:family_name]}"
|
|
380
|
+
dputs(2) { "Searching in #{fn}" }
|
|
381
|
+
if fn =~ /#{name}/i
|
|
382
|
+
dputs(2) { 'Found it' }
|
|
383
|
+
return get_data_instance(k)
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
}
|
|
387
|
+
return nil
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def find_name_or_create(name)
|
|
391
|
+
first, last = name.split(' ', 2)
|
|
392
|
+
find_full_name(name) or
|
|
393
|
+
create(:first_name => first, :family_name => last)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def login_to_full(login)
|
|
397
|
+
p = match_by_login_name(login)
|
|
398
|
+
p ? p.full_name : ''
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def listp_responsible(session = nil)
|
|
402
|
+
list = search_by_permissions('teacher')
|
|
403
|
+
if session
|
|
404
|
+
list = list.select { |p|
|
|
405
|
+
p.login_name =~ /^#{session.owner.login_name}_/
|
|
406
|
+
}.push(session.owner)
|
|
407
|
+
end
|
|
408
|
+
list.collect { |p|
|
|
409
|
+
[p.person_id, p.full_name]
|
|
410
|
+
}
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def self.responsibles_raw
|
|
414
|
+
return Persons.data.select { |k, v|
|
|
415
|
+
v._permissions &&
|
|
416
|
+
Permission.can_view(v._permissions.reject { |perm| perm.to_s == 'admin' },
|
|
417
|
+
'FlagResponsible')
|
|
418
|
+
}.collect { |k, v| Persons.find_by_person_id(k) }
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def self.responsibles_sort(resps)
|
|
422
|
+
resps.collect { |p|
|
|
423
|
+
[p.person_id, p.full_name]
|
|
424
|
+
}.sort { |a, b| a.last <=> b.last }
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def responsibles(force_update = false)
|
|
428
|
+
#dputs_func
|
|
429
|
+
if force_update || @resps.size == 0
|
|
430
|
+
dputs(3) { "Making responsible-cache with #{@data.size} entities" }
|
|
431
|
+
@resps = Persons.responsibles_raw
|
|
432
|
+
@resps = Persons.responsibles_sort(@resps)
|
|
433
|
+
else
|
|
434
|
+
dputs(3) { 'Lazily using responsible-cache' }
|
|
435
|
+
end
|
|
436
|
+
if force_update || @resps_course.size == 0
|
|
437
|
+
@resps_course = Courses.search_all_.collect { |c| [c.teacher, c.responsible, c.assistant] }.
|
|
438
|
+
flatten.compact.uniq
|
|
439
|
+
@resps_course = Persons.responsibles_sort(@resps_course)
|
|
440
|
+
end
|
|
441
|
+
(@resps + @resps_course).uniq.sort_by { |p| p[1] }
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def responsibles_add(p)
|
|
445
|
+
e = [[p.person_id, p.full_name]]
|
|
446
|
+
if not @resps.index(e)
|
|
447
|
+
@resps = (@resps + e).sort { |a, b| a.last <=> b.last }
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def responsibles_del(p)
|
|
452
|
+
e = [[p.person_id, p.full_name]]
|
|
453
|
+
if @resps.index(e)
|
|
454
|
+
@resps = (@resps - e).sort { |a, b| a.last <=> b.last }
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def create_add_course(student, owner, course, check_double = false)
|
|
459
|
+
prefix = ConfigBase.has_function?(:course_server) ?
|
|
460
|
+
"#{owner.login_name}_" : ''
|
|
461
|
+
login_name = Persons.create_login_name(student)
|
|
462
|
+
if not (person = Persons.match_by_login_name(prefix + student))
|
|
463
|
+
if check_double and
|
|
464
|
+
Persons.search_by_login_name("^#{prefix}#{login_name}[0-9]*$").length > 0
|
|
465
|
+
return nil
|
|
466
|
+
else
|
|
467
|
+
person = Persons.create({:first_name => student,
|
|
468
|
+
:login_name_prefix => prefix,
|
|
469
|
+
:permissions => %w( student ), :town => @town, :country => @country})
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
#person.email = "#{person.login_name}@ndjair.net"
|
|
473
|
+
person and course.students_add person
|
|
474
|
+
person
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def delete_all(local_only = false)
|
|
478
|
+
super(local_only)
|
|
479
|
+
@resps = []
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def self.update_fetchmailrc
|
|
483
|
+
begin
|
|
484
|
+
File.open('/etc/fetchmailrc', 'w') { |f|
|
|
485
|
+
f.write <<-start
|
|
486
|
+
set daemon 600
|
|
487
|
+
|
|
488
|
+
start
|
|
489
|
+
Persons.search_by_permissions(:email).each { |p|
|
|
490
|
+
if p.acc_proto.class == Array &&
|
|
491
|
+
p.acc_port.class == Array &&
|
|
492
|
+
p.acc_supp.class == Array &&
|
|
493
|
+
p.email && p.acc_pass
|
|
494
|
+
proto, port, supp =
|
|
495
|
+
p.acc_proto.join, p.acc_port.join, p.acc_supp.join
|
|
496
|
+
if proto.length * port.length * p.email.length * p.acc_pass.length > 0
|
|
497
|
+
dputs(2) { "Adding #{p.login_name} to fetchmailrc" }
|
|
498
|
+
f.write <<-person
|
|
499
|
+
poll #{p.acc_remote} uidl with proto #{p.acc_proto.join}
|
|
500
|
+
auth password port #{p.acc_port.join}
|
|
501
|
+
user '#{p.email}' there with password '#{p.acc_pass}'
|
|
502
|
+
is #{p.login_name} here
|
|
503
|
+
mimedecode #{p.acc_supp.join}
|
|
504
|
+
option limit 1000000 batchlimit 10 fetchlimit 10 fetchsizelimit 20
|
|
505
|
+
|
|
506
|
+
person
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
f.chmod 0700
|
|
512
|
+
}
|
|
513
|
+
FileUtils.chown 'fetchmail', 'nobody', '/etc/fetchmailrc'
|
|
514
|
+
rescue Errno::EACCES => e
|
|
515
|
+
dputs(0) { "Can't write fetchmailrc here..." }
|
|
516
|
+
self.permissions = permissions - %w(email)
|
|
517
|
+
rescue ArgumentError
|
|
518
|
+
dputs(0) { "Didn't find fetchmail-user" }
|
|
519
|
+
self.permissions = permissions - %w(email)
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def Persons.center
|
|
524
|
+
Persons.find_by_permissions(:center)
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
def Persons.centers
|
|
528
|
+
Persons.search_by_permissions(:center)
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def Persons.master_center
|
|
532
|
+
Persons.centers.find { |c| c.has_permission?(:center_director) }
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def Persons.master_center_login_name
|
|
536
|
+
(c = Persons.master_center) ? c.login_name : 'master'
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
def Persons.search_in(str, field = nil, center: nil, max: 20)
|
|
540
|
+
# Don't search if there are few caracters and lots of Persons
|
|
541
|
+
dputs(3) { "search_in - _#{str}_ - #{Persons.data.length}" }
|
|
542
|
+
if (!str || str.length < 3) && (Persons.data.length > max)
|
|
543
|
+
return field ? View.reply(:empty, field) : []
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
result = Persons.data.select { |k, v|
|
|
547
|
+
#dp v
|
|
548
|
+
str.split(/ /).collect { |s|
|
|
549
|
+
ret = false
|
|
550
|
+
%i( login_name family_name first_name
|
|
551
|
+
permissions person_id email phone groups ).each { |i|
|
|
552
|
+
#dp "#{i} - #{v[i]} - #{v[i].to_s =~ /#{str}/}"
|
|
553
|
+
ret ||= !!(v[i].to_s =~ /#{s}/i)
|
|
554
|
+
}
|
|
555
|
+
ret
|
|
556
|
+
}.compact.inject(:&)
|
|
557
|
+
}.sort { |a, b|
|
|
558
|
+
if a[1]._login_name.to_s == str
|
|
559
|
+
-1
|
|
560
|
+
elsif b[1]._login_name.to_s == str
|
|
561
|
+
1
|
|
562
|
+
else
|
|
563
|
+
"#{a[1]._first_name} #{a[1]._family_name}" <=>
|
|
564
|
+
"#{b[1]._first_name} #{b[1]._family_name}"
|
|
565
|
+
end
|
|
566
|
+
}.first(max).collect { |k, v| Persons.get_data_instance(k) }
|
|
567
|
+
|
|
568
|
+
dputs(3) { "Result is: #{result.collect { |r| r.login_name }}" }
|
|
569
|
+
not result and result = []
|
|
570
|
+
|
|
571
|
+
result
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def Persons.check_login(login, password)
|
|
575
|
+
return nil unless p = Persons.match_by_login_name(login)
|
|
576
|
+
p.check_pass(password) ? p : nil
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
def icc_get(tr)
|
|
580
|
+
c = tr._center
|
|
581
|
+
return "Error: Didn't find center #{c.inspect}}" unless center = Persons.match_by_login_name(c._login_name)
|
|
582
|
+
return "Error: Passwords do not match for #{c.inspect}" unless center.password_plain == c._password_plain
|
|
583
|
+
login_name = "#{c._login_name}_#{tr._name.first}"
|
|
584
|
+
log_msg :Persons, "ICC-get for #{login_name.inspect}"
|
|
585
|
+
if p = Persons.match_by_login_name(login_name)
|
|
586
|
+
p = p.to_hash
|
|
587
|
+
p._login_name = tr._name.first
|
|
588
|
+
p
|
|
589
|
+
else
|
|
590
|
+
"Error: Didn't find #{login_name}"
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
#
|
|
596
|
+
### One person only
|
|
597
|
+
#
|
|
598
|
+
|
|
599
|
+
class Person < Entity
|
|
600
|
+
def setup_instance
|
|
601
|
+
dputs(3) { "Data is #{@proxy.data[@id].inspect}" }
|
|
602
|
+
|
|
603
|
+
self.internet_credit = internet_credit.to_i
|
|
604
|
+
update_accounts
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def update_accounts
|
|
608
|
+
return unless ConfigBase.has_function? :accounting
|
|
609
|
+
update_account_cash
|
|
610
|
+
update_account_due
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def update_account_due
|
|
614
|
+
# dputs_func
|
|
615
|
+
return unless ConfigBase.has_function? :accounting
|
|
616
|
+
if can_view :FlagAddInternet and login_name != 'admin'
|
|
617
|
+
dputs(3) { "Adding account_due to -#{login_name.inspect}-" }
|
|
618
|
+
if login_name.to_s == ''
|
|
619
|
+
dputs(0) { "Error: account_name_due empty and no login_name #{self.inspect}" }
|
|
620
|
+
return
|
|
621
|
+
end
|
|
622
|
+
acc = (full_name || login_name).capitalize_all
|
|
623
|
+
return unless ConfigBase.account_lending
|
|
624
|
+
lending = "#{ConfigBase.account_lending.get_path}::#{acc}"
|
|
625
|
+
service = ConfigBase.account_services.get_path
|
|
626
|
+
dputs(2) { "Preparing accounts for #{full_name} - #{acc}" }
|
|
627
|
+
if !self.account_due
|
|
628
|
+
dputs(2) { "Creating account #{lending}" }
|
|
629
|
+
self.account_due = Accounts.get_by_path_or_create(lending,
|
|
630
|
+
acc, false, -1, true)
|
|
631
|
+
end
|
|
632
|
+
if !self.account_due_paid
|
|
633
|
+
dputs(2) { "Creating account #{lending}::Paid" }
|
|
634
|
+
self.account_due_paid = Accounts.get_by_path_or_create("#{lending}::Paid",
|
|
635
|
+
acc, false, -1, true)
|
|
636
|
+
end
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
def update_account_cash
|
|
641
|
+
return unless ConfigBase.has_function?(:accounting)
|
|
642
|
+
if can_view(:FlagAccounting) && login_name != 'admin'
|
|
643
|
+
acc = (first_name || login_name).capitalize
|
|
644
|
+
dputs(3) { "Getting cash account #{acc}" }
|
|
645
|
+
return unless ConfigBase.account_cash
|
|
646
|
+
cc = "#{ConfigBase.account_cash.get_path}::#{acc}"
|
|
647
|
+
dputs(3){"Account will be #{cc}"}
|
|
648
|
+
self.account_cash = Accounts.get_by_path_or_create(cc, cc, false, -1, true)
|
|
649
|
+
dputs(3) { "Account is #{account_cash.inspect}" }
|
|
650
|
+
end
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
def total_cash
|
|
654
|
+
if account_cash
|
|
655
|
+
(account_cash.total.to_f * 1000).to_i
|
|
656
|
+
else
|
|
657
|
+
0
|
|
658
|
+
end
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
def groups=(g)
|
|
662
|
+
self._groups = g
|
|
663
|
+
update_smb_passwd
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def permissions=(p)
|
|
667
|
+
has_teacher = self._permissions and self._permissions.concat(p).index('teacher')
|
|
668
|
+
dputs(3) { "#{self.login_name}: has_teacher is #{has_teacher} - permissions are #{p.inspect}" }
|
|
669
|
+
old_permissions = self._permissions
|
|
670
|
+
self._permissions = p.uniq
|
|
671
|
+
if has_teacher
|
|
672
|
+
if Permission.can_view(p.reject { |perm| perm.to_s == 'admin' },
|
|
673
|
+
'FlagResponsible')
|
|
674
|
+
Persons.responsibles_add(self)
|
|
675
|
+
else
|
|
676
|
+
Persons.responsibles_del(self)
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
if permissions.index 'email'
|
|
680
|
+
add_local_email
|
|
681
|
+
end
|
|
682
|
+
if (old_permissions || self._permissions).index('email')
|
|
683
|
+
Persons.update_fetchmailrc
|
|
684
|
+
end
|
|
685
|
+
update_accounts
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
def update_local_passwd(pass)
|
|
689
|
+
if permissions and permissions.index 'email'
|
|
690
|
+
dputs(2) { "Updating password #{pass} for #{login_name}" }
|
|
691
|
+
System.run_bool("echo -e '#{pass}\n#{pass}' | passwd #{login_name}")
|
|
692
|
+
end
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
def add_local_email
|
|
696
|
+
add_user_account
|
|
697
|
+
dir = "/home/#{login_name}/Maildir"
|
|
698
|
+
if !File.exists? dir
|
|
699
|
+
Dir.mkdir dir
|
|
700
|
+
%w( new cur tmp ).each { |d|
|
|
701
|
+
Dir.mkdir File.join(dir, d)
|
|
702
|
+
}
|
|
703
|
+
FileUtils.chown login_name, login_name, Dir.glob("/home/#{login_name}/**/**")
|
|
704
|
+
end
|
|
705
|
+
squirrel_base = '/srv/http/squirrelmail/config/var/data'
|
|
706
|
+
if Files.exists? squirrel_base
|
|
707
|
+
squirrel_pref = "#{squirrel_base}/#{login_name}.pref"
|
|
708
|
+
if !File.exists? squirrel_pref
|
|
709
|
+
IO.write(squirrel_pref, "full_name=#{full_name}\nemail_address=#{email}")
|
|
710
|
+
FileUtils.chown 'http', 'http', squirrel_pref
|
|
711
|
+
end
|
|
712
|
+
end
|
|
713
|
+
update_local_passwd(password)
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
def add_user_account
|
|
717
|
+
if not @proxy.has_storage? :LDAP
|
|
718
|
+
if Persons.admin_users
|
|
719
|
+
if !File.exist? "/home/#{login_name}"
|
|
720
|
+
log_msg :Person, "Adding user-account for #{login_name} with #{permissions.inspect}"
|
|
721
|
+
System.run_str("if which adduser; then adduser --disabled-password --gecos '#{self.full_name}' #{self.login_name};
|
|
722
|
+
else useradd -m #{self.login_name}; fi")
|
|
723
|
+
end
|
|
724
|
+
end
|
|
725
|
+
end
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
def update_smb_passwd(pass = password_plain)
|
|
729
|
+
if ConfigBase.has_function?(:share) && (groups && groups.index('share'))
|
|
730
|
+
add_user_account
|
|
731
|
+
if Persons.admin_users && pass
|
|
732
|
+
p = pass.chomp
|
|
733
|
+
log_msg :person, "Changing password in Samba to #{p.inspect}"
|
|
734
|
+
pwd_change = "/bin/echo -e '#{p}\\n#{p}' | smbpasswd -s -a #{self.login_name} "
|
|
735
|
+
dputs(3) { pwd_change.inspect }
|
|
736
|
+
System.run_str pwd_change
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
def account_total_due
|
|
742
|
+
if account_due
|
|
743
|
+
dputs(3) { "account_due is #{account_due.total.inspect}" }
|
|
744
|
+
(account_due.total.to_f * 1000.0).round.to_i
|
|
745
|
+
else
|
|
746
|
+
_account_total_due
|
|
747
|
+
end
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
def add_internet_credit(client, internet_credit)
|
|
751
|
+
dputs(3) { "Adding #{internet_credit}CFA to #{client.internet_credit} for #{client.login_name}" }
|
|
752
|
+
internet_credit_before = client.internet_credit
|
|
753
|
+
if internet_credit.to_i < 0 and internet_credit.to_i.abs > client.internet_credit.to_i
|
|
754
|
+
internet_credit = -client.internet_credit.to_i
|
|
755
|
+
end
|
|
756
|
+
if pay_service(internet_credit, "internet_credit pour -#{client.login_name}:#{internet_credit}-")
|
|
757
|
+
client.data_set(:_internet_credit, (client.internet_credit.to_i + internet_credit.to_i).to_s)
|
|
758
|
+
log_msg(:AddCash, "#{self.login_name} added #{internet_credit} for #{client.login_name}: " +
|
|
759
|
+
"#{internet_credit_before} + #{internet_credit} = #{client.internet_credit}")
|
|
760
|
+
log_msg(:AddCash, "Total due: #{account_total_due}")
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
def pay_service(credit, msg, date = nil)
|
|
765
|
+
if account_due
|
|
766
|
+
date = date ? Date.parse(date) : Date.today
|
|
767
|
+
|
|
768
|
+
Movements.create("#{msg}", date.strftime('%Y-%m-%d'),
|
|
769
|
+
credit.to_i / 1000.0, account_due, ConfigBase.account_services)
|
|
770
|
+
self.account_total_due = (account_due.total.to_f * 1000.0).round.to_i
|
|
771
|
+
else
|
|
772
|
+
return false
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
def check_pass(pass)
|
|
777
|
+
if @proxy.has_storage? :LDAP
|
|
778
|
+
# We have to try to bind to the LDAP
|
|
779
|
+
dputs(2) { 'Trying LDAP' }
|
|
780
|
+
#return @proxy.storage[:LDAP].check_login( data_get(:login_name), pass )
|
|
781
|
+
return @proxy.storage[:LDAP].check_login(login_name, pass)
|
|
782
|
+
else
|
|
783
|
+
#dputs( 0 ){ "is #{pass} equal to #{data_get( :password ) }" }
|
|
784
|
+
dputs(2) { "is #{pass} equal to #{password}" }
|
|
785
|
+
#return pass == data_get( :password )
|
|
786
|
+
return pass == password
|
|
787
|
+
end
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
def password=(pass)
|
|
791
|
+
(@pre_init || @loading) and return
|
|
792
|
+
|
|
793
|
+
p = pass.to_s.chomp
|
|
794
|
+
if @proxy.has_storage? :LDAP
|
|
795
|
+
dputs(2) { "Changing password for #{self.login_name}: #{pass}" }
|
|
796
|
+
p = System.run_str("slappasswd -s #{pass}").chomp
|
|
797
|
+
dputs(2) { "Hashed password for #{self.login_name} is: #{pass}" }
|
|
798
|
+
end
|
|
799
|
+
update_smb_passwd(pass)
|
|
800
|
+
update_local_passwd(pass)
|
|
801
|
+
if self._password != p
|
|
802
|
+
log_msg :person, "Setting password (#{pass}) for #{self.login_name} to #{p}"
|
|
803
|
+
self._password = p
|
|
804
|
+
end
|
|
805
|
+
if (permissions and permissions.index('center')) or
|
|
806
|
+
(groups and groups.index('share')) or
|
|
807
|
+
(not self.password_plain or self.password_plain == '' or
|
|
808
|
+
self.password_plain == pass)
|
|
809
|
+
self.password_plain = pass
|
|
810
|
+
else
|
|
811
|
+
dputs(2) { self.password_plain.inspect }
|
|
812
|
+
self.password_plain = '****'
|
|
813
|
+
end
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
def full_name
|
|
817
|
+
ret = []
|
|
818
|
+
first_name and ret.push first_name
|
|
819
|
+
family_name and ret.push family_name
|
|
820
|
+
ret.length == 0 and ret.push login_name
|
|
821
|
+
ret.join(' ')
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
def full_login
|
|
825
|
+
"#{full_name} (#{login_name})"
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
def replace(orig, field, str)
|
|
829
|
+
fields.each { |f|
|
|
830
|
+
orig.gsub!(f[0], f[1].to_s)
|
|
831
|
+
}
|
|
832
|
+
orig
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
def lp_cmd=(v)
|
|
836
|
+
@proxy.print_card_student.lp_cmd = v
|
|
837
|
+
@proxy.print_card_responsible.lp_cmd = v
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
def print(card = :student)
|
|
841
|
+
ctype = 'Visiteur'
|
|
842
|
+
courses = Courses.list_courses_for_person(self)
|
|
843
|
+
if courses and courses.length > 0
|
|
844
|
+
dputs(3) { "Courses is #{courses.inspect}" }
|
|
845
|
+
ctype = Courses.match_by_course_id(courses[0][0]).description
|
|
846
|
+
end
|
|
847
|
+
fname = "#{person_id.to_s.rjust(6, '0')}-#{full_name.gsub(/ /, '_')}"
|
|
848
|
+
courses = ['', '']
|
|
849
|
+
Courses.list_courses_for_person(self).each { |c|
|
|
850
|
+
dputs(3) { "Course #{c.inspect}" }
|
|
851
|
+
courses.unshift(Courses.match_by_course_id(c.first).description)
|
|
852
|
+
}
|
|
853
|
+
replace = [[/--NAME1--/, first_name],
|
|
854
|
+
[/--NAME2--/, family_name],
|
|
855
|
+
[/--GENDER--/, gender_i18n(ConfigBase.locale_force)],
|
|
856
|
+
[/--BDAY--/, birthday],
|
|
857
|
+
[/--TDAY--/, System.run_str('LC_ALL=fr_FR.UTF-8 date +"%d %B %Y"')],
|
|
858
|
+
[/--TOWN--/, town],
|
|
859
|
+
[/--TEL--/, phone],
|
|
860
|
+
[/--UNAME--/, login_name],
|
|
861
|
+
[/--EMAIL--/, email],
|
|
862
|
+
[/--CTYPE--/, ctype],
|
|
863
|
+
[/--COURSE1--/, courses[0]],
|
|
864
|
+
[/--COURSE2--/, courses[1]],
|
|
865
|
+
[/--PASS--/, password_plain]]
|
|
866
|
+
if center = Persons.center
|
|
867
|
+
url, email = center.email.to_s.split('::')
|
|
868
|
+
replace.concat([[/--CENTER_NAME--/, center.full_name],
|
|
869
|
+
[/--CENTER_ADDRESS--/, center.address],
|
|
870
|
+
[/--CENTER_TOWN--/, center.town],
|
|
871
|
+
[/--CENTER_COUNTRY--/, center.country],
|
|
872
|
+
[/--CENTER_TEL--/, center.phone],
|
|
873
|
+
[/--CENTER_URL--/, url],
|
|
874
|
+
[/--CENTER_EMAIL--/, email]])
|
|
875
|
+
end
|
|
876
|
+
dputs(3) { "Replace is #{replace.inspect}" }
|
|
877
|
+
case card
|
|
878
|
+
when /student/
|
|
879
|
+
@proxy.print_card_student.print(replace, nil, fname)
|
|
880
|
+
when /responsible/
|
|
881
|
+
@proxy.print_card_responsible.print(replace, nil, fname)
|
|
882
|
+
end
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
def simple
|
|
886
|
+
return true if !permissions
|
|
887
|
+
(permissions-%w(internet student)).length == 0
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
def to_list(user = nil)
|
|
891
|
+
[login_name, "#{full_name} - #{login_name}" +
|
|
892
|
+
(show_password?(user) ? ":#{password_plain}" : '')]
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
def to_list_id(user = nil)
|
|
896
|
+
[person_id, "#{full_name} - #{login_name}" +
|
|
897
|
+
(show_password?(user) ? ":#{password_plain}" : '')]
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
def session
|
|
901
|
+
Sessions.match_by_sid(self.session_id)
|
|
902
|
+
end
|
|
903
|
+
|
|
904
|
+
def first_name=(v)
|
|
905
|
+
self._first_name = v.to_s.capitalize_all
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
def family_name=(v)
|
|
909
|
+
self._family_name = v.to_s.capitalize_all
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
def get_cash(person, amount)
|
|
913
|
+
dputs(3) { "Amount is #{amount.inspect} and #{person.inspect} will receive it" }
|
|
914
|
+
amount = amount.to_i
|
|
915
|
+
if amount < 0
|
|
916
|
+
dputs(0) { "Error: Can't transfer a negative amount here" }
|
|
917
|
+
return false
|
|
918
|
+
end
|
|
919
|
+
if not person.account_due
|
|
920
|
+
dputs(0) { "Error: #{person.login_name}::#{person.full_name} has no account_due" }
|
|
921
|
+
return false
|
|
922
|
+
end
|
|
923
|
+
if not account_cash
|
|
924
|
+
dputs(0) { "Error: #{self.inspect} has no account_cash" }
|
|
925
|
+
return false
|
|
926
|
+
end
|
|
927
|
+
dputs(3) { "Transferring #{amount} from #{account_cash.get_path} to " +
|
|
928
|
+
"#{person.account_due.get_path}"
|
|
929
|
+
}
|
|
930
|
+
Movements.create('Transfert au comptable', Date.today,
|
|
931
|
+
amount / 1000.0, account_cash, person.account_due)
|
|
932
|
+
return true
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
def get_all_due(person, date = Date.today)
|
|
936
|
+
if person.account_due && account_cash
|
|
937
|
+
value = 0
|
|
938
|
+
person.account_due.movements.each { |m|
|
|
939
|
+
if m.date <= date
|
|
940
|
+
dputs(3) { "Moving #{m.inspect}" }
|
|
941
|
+
value += m.get_value(person.account_due)
|
|
942
|
+
m.move_from_to(person.account_due, person.account_due_paid)
|
|
943
|
+
end
|
|
944
|
+
}
|
|
945
|
+
dputs(3) { "Value is #{value}" }
|
|
946
|
+
Movements.create('Transfert au comptable', date,
|
|
947
|
+
value, account_cash, person.account_due_paid)
|
|
948
|
+
end
|
|
949
|
+
end
|
|
950
|
+
|
|
951
|
+
def can_view(v)
|
|
952
|
+
#Permission.can_view( data_get(:permissions), v )
|
|
953
|
+
Permission.can_view(permissions, v)
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
def has_role(r)
|
|
957
|
+
Permission.has_role(permissions, r)
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
def has_all_rights_of(person)
|
|
961
|
+
dputs(4) { "#{person.permissions} - #{permissions}" }
|
|
962
|
+
pv1 = Permission.views(permissions)
|
|
963
|
+
Permission.views(person.permissions).each { |p|
|
|
964
|
+
found = false
|
|
965
|
+
pv1.each { |p1|
|
|
966
|
+
if p =~ /^#{p1}$/
|
|
967
|
+
found = true
|
|
968
|
+
dputs(4) { "Found my #{p1} matches his #{p}" }
|
|
969
|
+
end
|
|
970
|
+
}
|
|
971
|
+
not found and return false
|
|
972
|
+
}
|
|
973
|
+
return true
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
def delete
|
|
977
|
+
Courses.search_all_.each { |course|
|
|
978
|
+
dputs(3) { "Checking course #{course.name}" }
|
|
979
|
+
[:teacher, :assistant, :responsible, :center].each { |role|
|
|
980
|
+
begin
|
|
981
|
+
r = course.data_get("_#{role}")
|
|
982
|
+
rescue Exception => e
|
|
983
|
+
if e.message == 'WrongIndex'
|
|
984
|
+
dputs(0) { "Error: Role :#{role} is not well defined - resetting to nil" }
|
|
985
|
+
course.data_set("_#{role}", nil)
|
|
986
|
+
end
|
|
987
|
+
end
|
|
988
|
+
dputs(3) { "Role #{role} is #{r.inspect}" }
|
|
989
|
+
if r and r.login_name == login_name
|
|
990
|
+
raise IsNecessary.new(course)
|
|
991
|
+
end
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
Courses.data.values.each { |d|
|
|
996
|
+
if d[:students] and d[:students].index(login_name)
|
|
997
|
+
d[:students] -= [login_name]
|
|
998
|
+
end
|
|
999
|
+
}
|
|
1000
|
+
Shares.search_all_.each { |s|
|
|
1001
|
+
s.acl.delete login_name
|
|
1002
|
+
}
|
|
1003
|
+
AccessGroups.search_all_.each { |ag|
|
|
1004
|
+
ag.members and ag.members.delete login_name
|
|
1005
|
+
}
|
|
1006
|
+
Grades.search_by_student(self).each { |g|
|
|
1007
|
+
g.delete
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
if @proxy.has_storage? :LDAP
|
|
1011
|
+
if !Kernel.system("ldapdeleteuser #{self.login_name}")
|
|
1012
|
+
dputs(0) { "Error: couldn't delete user #{self.inspect}" }
|
|
1013
|
+
end
|
|
1014
|
+
elsif Persons.admin_users
|
|
1015
|
+
System.run_bool("if which deluser; then deluser #{self.login_name}; else
|
|
1016
|
+
userdel #{self.login_name}; fi")
|
|
1017
|
+
end
|
|
1018
|
+
if ConfigBase.has_function?(:share) && Persons.admin_users
|
|
1019
|
+
System.run_bool("smbpasswd -x #{self.login_name}")
|
|
1020
|
+
end
|
|
1021
|
+
super
|
|
1022
|
+
|
|
1023
|
+
@proxy.resps = []
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
def get_unique
|
|
1027
|
+
login_name
|
|
1028
|
+
end
|
|
1029
|
+
|
|
1030
|
+
def has_permission?(perm)
|
|
1031
|
+
#dputs_func
|
|
1032
|
+
dputs(4) { "Checking #{perm.inspect} in #{permissions.inspect}" }
|
|
1033
|
+
dputs(4) { "Which is #{Permission.views(permissions).inspect }" }
|
|
1034
|
+
return false unless permissions
|
|
1035
|
+
return true if permissions.index(perm.to_s)
|
|
1036
|
+
Permission.views(permissions).select { |p|
|
|
1037
|
+
dputs(4) { "Checking permission #{p.inspect} of #{perm.inspect}" }
|
|
1038
|
+
dputs(4) { "Result is #{perm.to_s =~ /^#{p.to_s}$/}" }
|
|
1039
|
+
perm.to_s =~ /^#{p.to_s}$/
|
|
1040
|
+
}.length > 0
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
def courses
|
|
1044
|
+
Courses.search_all.select { |c|
|
|
1045
|
+
c[:students] and c[:students].index(login_name)
|
|
1046
|
+
}
|
|
1047
|
+
end
|
|
1048
|
+
|
|
1049
|
+
def report_list_movements(from = nil, to = from, account = account_due)
|
|
1050
|
+
total = 0
|
|
1051
|
+
account or return [[]]
|
|
1052
|
+
if from
|
|
1053
|
+
account.movements.select { |m|
|
|
1054
|
+
dputs(3) { "Date is #{m.date.inspect}" }
|
|
1055
|
+
(from..to).include? m.date
|
|
1056
|
+
}
|
|
1057
|
+
elsif to
|
|
1058
|
+
dputs(3) { "Fetching only dates upto #{to}" }
|
|
1059
|
+
account.movements.select { |m|
|
|
1060
|
+
dputs(3) { "Date is #{m.date.inspect}" }
|
|
1061
|
+
m.date <= to
|
|
1062
|
+
}
|
|
1063
|
+
else
|
|
1064
|
+
account.movements
|
|
1065
|
+
end.reverse.collect { |m|
|
|
1066
|
+
v = m.get_value(account)
|
|
1067
|
+
[m.global_id,
|
|
1068
|
+
[m.date,
|
|
1069
|
+
"#{m.get_other_account(account).name}: #{m.desc}",
|
|
1070
|
+
Account.total_form(v),
|
|
1071
|
+
Account.total_form(total += v)]
|
|
1072
|
+
]
|
|
1073
|
+
}.reverse
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
def report_list(report, date = nil)
|
|
1077
|
+
date ||= Date.today
|
|
1078
|
+
case report
|
|
1079
|
+
when :daily, 1
|
|
1080
|
+
report_list_movements(date)
|
|
1081
|
+
when :weekly, 2
|
|
1082
|
+
week = Date.commercial(date.year, date.cweek, 1)
|
|
1083
|
+
report_list_movements(week, week + 6)
|
|
1084
|
+
when :monthly, 3
|
|
1085
|
+
report_list_movements(
|
|
1086
|
+
Date.new(date.year, date.month, 1),
|
|
1087
|
+
Date.new(date.year, date.month, -1))
|
|
1088
|
+
when :all, 4
|
|
1089
|
+
report_list_movements(nil, date)
|
|
1090
|
+
when :all_paid, 5
|
|
1091
|
+
report_list_movements(nil, nil, account_due_paid)
|
|
1092
|
+
end
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1095
|
+
def report_pdf(report, date = nil)
|
|
1096
|
+
file = "/tmp/cash_#{login_name}.pdf"
|
|
1097
|
+
Prawn::Document.generate(file,
|
|
1098
|
+
:page_size => 'A4',
|
|
1099
|
+
:page_layout => :portrait,
|
|
1100
|
+
:bottom_margin => 2.cm) do |pdf|
|
|
1101
|
+
|
|
1102
|
+
sum = 0
|
|
1103
|
+
movs = report_list(report, date).reverse
|
|
1104
|
+
pdf.text "Report for #{full_name}", :align => :center, :size => 20
|
|
1105
|
+
pdf.font_size 10
|
|
1106
|
+
if movs.length > 0
|
|
1107
|
+
pdf.text "From #{movs.first[1][0]} to #{movs.last[1][0]}"
|
|
1108
|
+
end
|
|
1109
|
+
pdf.text "Account: #{account_due.path}"
|
|
1110
|
+
pdf.move_down 1.cm
|
|
1111
|
+
|
|
1112
|
+
if movs.length > 0
|
|
1113
|
+
header = [['Date', 'Description', 'Value', 'Sum'].collect { |ch|
|
|
1114
|
+
{:content => ch, :align => :center} }]
|
|
1115
|
+
dputs(3) { "Movs is #{movs.inspect}" }
|
|
1116
|
+
pdf.table(header + movs.collect { |m_id, m|
|
|
1117
|
+
[{:content => "#{m[0]}", :align => :center},
|
|
1118
|
+
m[1],
|
|
1119
|
+
{:content => "#{m[2]}", :align => :right},
|
|
1120
|
+
{:content => "#{Account.total_form(
|
|
1121
|
+
sum += m[2].gsub(',', '').to_f / 1000)}",
|
|
1122
|
+
:align => :right}]
|
|
1123
|
+
}, :header => true, :column_widths => [70, 300, 75, 75])
|
|
1124
|
+
pdf.move_down(2.cm)
|
|
1125
|
+
end
|
|
1126
|
+
|
|
1127
|
+
pdf.repeat(:all, :dynamic => true) do
|
|
1128
|
+
pdf.draw_text "#{Date.today} - #{account_due.path}",
|
|
1129
|
+
:at => [0, -20], :size => 10
|
|
1130
|
+
pdf.draw_text pdf.page_number, :at => [19.cm, -20]
|
|
1131
|
+
end
|
|
1132
|
+
end
|
|
1133
|
+
file
|
|
1134
|
+
end
|
|
1135
|
+
|
|
1136
|
+
def to_frontend(owner)
|
|
1137
|
+
to_list_id(owner)
|
|
1138
|
+
end
|
|
1139
|
+
|
|
1140
|
+
def is_responsible?
|
|
1141
|
+
%w( director teacher assistant center_director ).each { |p|
|
|
1142
|
+
return true if has_permission? p
|
|
1143
|
+
}
|
|
1144
|
+
return false
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
def is_staff?
|
|
1148
|
+
(permissions.to_a - %w(student internet)).length > 0
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
def show_password?(user = nil)
|
|
1152
|
+
case ConfigBase.show_passwords.first
|
|
1153
|
+
when 'always'
|
|
1154
|
+
return true
|
|
1155
|
+
when 'never'
|
|
1156
|
+
return false
|
|
1157
|
+
when 'students'
|
|
1158
|
+
return !is_staff?
|
|
1159
|
+
when 'lesser'
|
|
1160
|
+
return user ? user.has_all_rights_of(self) : false
|
|
1161
|
+
end
|
|
1162
|
+
end
|
|
1163
|
+
|
|
1164
|
+
def gender_i18n(lang)
|
|
1165
|
+
g = (gender ? gender.first : '').to_sym
|
|
1166
|
+
case lang
|
|
1167
|
+
when /en/
|
|
1168
|
+
{male: 'Mister', female: 'Madam'}[g]
|
|
1169
|
+
when /fr/
|
|
1170
|
+
{male: 'Monsieur', female: 'Madame'}[g]
|
|
1171
|
+
else
|
|
1172
|
+
''
|
|
1173
|
+
end
|
|
1174
|
+
end
|
|
1175
|
+
end
|