gestion 1.9.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (233) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +48 -0
  3. data/.project +14 -0
  4. data/Binaries/adduser_to_buzz +15 -0
  5. data/Binaries/backup +7 -0
  6. data/Binaries/check_gestion +8 -0
  7. data/Binaries/gestion.gnumail +22 -0
  8. data/Binaries/gestion.logrotate +34 -0
  9. data/Binaries/gestion.service +12 -0
  10. data/Binaries/gestion_update.rb +183 -0
  11. data/Binaries/gestion_update.service +10 -0
  12. data/Binaries/get_compta +11 -0
  13. data/Binaries/kill_gestion +16 -0
  14. data/Binaries/ldap/add_indexes +51 -0
  15. data/Binaries/ldap/backup +2 -0
  16. data/Binaries/ldap/install_ldap +92 -0
  17. data/Binaries/ldap/restore +7 -0
  18. data/Binaries/lib_backup +5 -0
  19. data/Binaries/log_scan_errors +8 -0
  20. data/Binaries/loop_gestion +64 -0
  21. data/Binaries/onetimers/sync_courses_from_compta.rb +74 -0
  22. data/Binaries/onetimers/transfer_cash_from_ldap_to_csv +26 -0
  23. data/Binaries/reboot +5 -0
  24. data/Binaries/restore +3 -0
  25. data/Binaries/restore_do +22 -0
  26. data/Binaries/sort_events +31 -0
  27. data/Binaries/start_gestion +18 -0
  28. data/Binaries/swipe_gestion +18 -0
  29. data/Binaries/update_africompta +21 -0
  30. data/Binaries/update_users +3 -0
  31. data/Diplomas.src/accredited.odg +0 -0
  32. data/Diplomas.src/diploma.odg +0 -0
  33. data/Diplomas.src/label.odg +0 -0
  34. data/Diplomas.src/presence_sheet.ods +0 -0
  35. data/Diplomas.src/presence_sheet_small.ods +0 -0
  36. data/Diplomas.src/student_card.odg +0 -0
  37. data/Doc/130514-it-ideas.odt +0 -0
  38. data/Doc/Compta-cash.mm +179 -0
  39. data/Doc/General.odt +0 -0
  40. data/Entities/AccessGroups.rb +117 -0
  41. data/Entities/Activity.rb +178 -0
  42. data/Entities/ChatMsg.rb +142 -0
  43. data/Entities/Classroom.rb +11 -0
  44. data/Entities/Client.rb +19 -0
  45. data/Entities/Computer.rb +21 -0
  46. data/Entities/ConfigBase.rb +280 -0
  47. data/Entities/Course.rb +1588 -0
  48. data/Entities/CourseType.rb +171 -0
  49. data/Entities/DFiles.rb +466 -0
  50. data/Entities/FilesManage.rb +226 -0
  51. data/Entities/Grade.rb +186 -0
  52. data/Entities/Internet.rb +300 -0
  53. data/Entities/Netdev.rb +10 -0
  54. data/Entities/Person.rb +1175 -0
  55. data/Entities/Plug.rb +98 -0
  56. data/Entities/Quiz.rb +33 -0
  57. data/Entities/Recharges.rb +37 -0
  58. data/Entities/Report.rb +136 -0
  59. data/Entities/Room.rb +12 -0
  60. data/Entities/SMS.rb +30 -0
  61. data/Entities/ScheduleType.rb +33 -0
  62. data/Entities/Share.rb +120 -0
  63. data/Entities/Task.rb +51 -0
  64. data/Entities/Ticket.rb +72 -0
  65. data/Entities/Usage.rb +143 -0
  66. data/Entities/Worker.rb +29 -0
  67. data/Files/apache-profeda.conf +36 -0
  68. data/Files/label.erb +121 -0
  69. data/Files/label_notfound.erb +64 -0
  70. data/Files/label_notpassed.erb +84 -0
  71. data/Files/mobileinfo.erb +115 -0
  72. data/Files/smb.conf +333 -0
  73. data/Files/timetable.html +36 -0
  74. data/Files/timetable.js +239 -0
  75. data/Gemfile +12 -0
  76. data/Gemfile.dev +12 -0
  77. data/Gemfile.dev.lock +127 -0
  78. data/Gemfile.lock +127 -0
  79. data/Gemfile.prod +8 -0
  80. data/Gestion +35 -0
  81. data/Gestion.rb +220 -0
  82. data/INSTALL +40 -0
  83. data/Images/connection.xcf +0 -0
  84. data/Images/connection_no.png +0 -0
  85. data/Images/connection_wait.png +0 -0
  86. data/Images/connection_yes.png +0 -0
  87. data/Paths/Exas.rb +13 -0
  88. data/Paths/Files.rb +19 -0
  89. data/Paths/GetDiplomas.rb +20 -0
  90. data/Paths/Info.rb +114 -0
  91. data/Paths/Label.rb +187 -0
  92. data/Paths/MobileInfo.rb +19 -0
  93. data/Paths/internetCash.rb +34 -0
  94. data/Paths/internetWifi.rb +54 -0
  95. data/README.md +60 -0
  96. data/Rakefile +13 -0
  97. data/TODO +1391 -0
  98. data/Test/.gitignore +3 -0
  99. data/Test/Diplomas/base_gestion.odt +0 -0
  100. data/Test/Diplomas/base_report.odt +0 -0
  101. data/Test/Diplomas/carte_etudiant.odg +0 -0
  102. data/Test/Diplomas/exam_language.odt +0 -0
  103. data/Test/Diplomas/label.odg +0 -0
  104. data/Test/Diplomas/presence_sheet.ods +0 -0
  105. data/Test/Diplomas/presence_sheet_small.ods +0 -0
  106. data/Test/Diplomas/student_card.odg +0 -0
  107. data/Test/Manual/testMerge +18 -0
  108. data/Test/config_test.yaml +26 -0
  109. data/Test/db.testGestion +0 -0
  110. data/Test/dfiles/descs/avg-rescue.desc +10 -0
  111. data/Test/dfiles/descs/avg.desc +8 -0
  112. data/Test/dfiles/descs/driver.desc +8 -0
  113. data/Test/dfiles/descs/linuxmint.desc +7 -0
  114. data/Test/dfiles/files/avg-160203.exe +1 -0
  115. data/Test/dfiles/files/avg.iso +1 -0
  116. data/Test/dfiles/files/driver.exe +1 -0
  117. data/Test/dfiles/index_post.html +3 -0
  118. data/Test/dfiles/index_pre.html +8 -0
  119. data/Test/dfiles/priorities +5 -0
  120. data/Test/ge_activity.rb +124 -0
  121. data/Test/ge_chat.rb +106 -0
  122. data/Test/ge_compta.rb +67 -0
  123. data/Test/ge_configbase.rb +54 -0
  124. data/Test/ge_course.rb +1114 -0
  125. data/Test/ge_dfiles.rb +121 -0
  126. data/Test/ge_filesmanage.rb +180 -0
  127. data/Test/ge_info.rb +27 -0
  128. data/Test/ge_internet.rb +246 -0
  129. data/Test/ge_login.rb +55 -0
  130. data/Test/ge_person.rb +373 -0
  131. data/Test/ge_qvinfo.rb +28 -0
  132. data/Test/ge_report.rb +97 -0
  133. data/Test/ge_share.rb +27 -0
  134. data/Test/ge_sms.rb +34 -0
  135. data/Test/ge_tasks.rb +19 -0
  136. data/Test/ge_usage.rb +168 -0
  137. data/Test/ge_view.rb +46 -0
  138. data/Test/multiconf-captive +29 -0
  139. data/Test/test.conf +7 -0
  140. data/Test/test.rb +49 -0
  141. data/Test/test_bytes.png +0 -0
  142. data/VERSION +140 -0
  143. data/Views/Admin/Backup.rb +91 -0
  144. data/Views/Admin/Configuration.rb +44 -0
  145. data/Views/Admin/Credit.rb +32 -0
  146. data/Views/Admin/FilesManage.rb +219 -0
  147. data/Views/Admin/Function.rb +39 -0
  148. data/Views/Admin/Power.rb +49 -0
  149. data/Views/Admin/Printer.rb +37 -0
  150. data/Views/Admin/Server.rb +252 -0
  151. data/Views/Admin/Tabs.rb +5 -0
  152. data/Views/Admin/Update.rb +73 -0
  153. data/Views/Admin/UpdateSystem.rb +26 -0
  154. data/Views/Cashbox/Activity.rb +191 -0
  155. data/Views/Cashbox/Course.rb +141 -0
  156. data/Views/Cashbox/Credit.rb +79 -0
  157. data/Views/Cashbox/Report.rb +115 -0
  158. data/Views/Cashbox/Service.rb +105 -0
  159. data/Views/Cashbox/Tabs.rb +10 -0
  160. data/Views/Compta/Accounts.rb +36 -0
  161. data/Views/Compta/Course.rb +96 -0
  162. data/Views/Compta/Show.rb +6 -0
  163. data/Views/Compta/Transfer.rb +66 -0
  164. data/Views/Course/Diploma.rb +203 -0
  165. data/Views/Course/Grade.rb +401 -0
  166. data/Views/Course/Modify.rb +447 -0
  167. data/Views/Course/Print.rb +94 -0
  168. data/Views/Course/Responsible.rb +44 -0
  169. data/Views/Course/Stats.rb +76 -0
  170. data/Views/Course/Students.rb +92 -0
  171. data/Views/Course/Tabs.rb +220 -0
  172. data/Views/Internet/Access.rb +134 -0
  173. data/Views/Internet/ClassEdit.rb +24 -0
  174. data/Views/Internet/ClassUsers.rb +81 -0
  175. data/Views/Internet/Config.rb +32 -0
  176. data/Views/Internet/Mobile.rb +213 -0
  177. data/Views/Internet/Recharges.rb +49 -0
  178. data/Views/Internet/Tabs.rb +6 -0
  179. data/Views/Inventory/Computer.rb +24 -0
  180. data/Views/Inventory/Room.rb +18 -0
  181. data/Views/Inventory/Tabs.rb +9 -0
  182. data/Views/Inventory/TicketClosed.rb +7 -0
  183. data/Views/Inventory/TicketOpen.rb +23 -0
  184. data/Views/Library/Person.rb +36 -0
  185. data/Views/Library/Tabs.rb +7 -0
  186. data/Views/Network/Block.rb +87 -0
  187. data/Views/Network/Netdevs.rb +21 -0
  188. data/Views/Network/Restriction.rb +37 -0
  189. data/Views/Network/Share.rb +167 -0
  190. data/Views/Network/Tables.rb +28 -0
  191. data/Views/Network/Tabs.rb +6 -0
  192. data/Views/Person/Admin.rb +99 -0
  193. data/Views/Person/Center.rb +48 -0
  194. data/Views/Person/Course.rb +72 -0
  195. data/Views/Person/Modify.rb +153 -0
  196. data/Views/Person/Tabs.rb +162 -0
  197. data/Views/Report/ComptaExecutive.rb +221 -0
  198. data/Views/Report/ComptaFlat.rb +79 -0
  199. data/Views/Report/ReportCourse.rb +47 -0
  200. data/Views/Report/Tabs.rb +8 -0
  201. data/Views/Report/Usage.rb +52 -0
  202. data/Views/Report/UsageCases.rb +59 -0
  203. data/Views/Self/Cash.rb +67 -0
  204. data/Views/Self/Chat.rb +55 -0
  205. data/Views/Self/Concours.rb +109 -0
  206. data/Views/Self/Email.rb +34 -0
  207. data/Views/Self/Internet.rb +255 -0
  208. data/Views/Self/Results.rb +17 -0
  209. data/Views/Self/Services.rb +85 -0
  210. data/Views/Self/Show.rb +47 -0
  211. data/Views/Self/Tabs.rb +5 -0
  212. data/Views/Special/DFileEdit.rb +13 -0
  213. data/Views/Special/PlugEdit.rb +56 -0
  214. data/Views/Special/Tabs.rb +6 -0
  215. data/Views/Special/Vnc.rb +39 -0
  216. data/Views/Task/Client.rb +21 -0
  217. data/Views/Task/Edit.rb +33 -0
  218. data/Views/Task/List.rb +55 -0
  219. data/Views/Task/Tabs.rb +9 -0
  220. data/Views/Task/Worker.rb +30 -0
  221. data/Views/Template/Activity.rb +33 -0
  222. data/Views/Template/CourseType.rb +63 -0
  223. data/Views/Template/ScheduleType.rb +29 -0
  224. data/Views/Template/Tabs.rb +5 -0
  225. data/Views/Welcome.rb +121 -0
  226. data/config.yaml.default +36 -0
  227. data/po/Gestion-ar.po +2356 -0
  228. data/po/Gestion-en.mo +0 -0
  229. data/po/Gestion-en.po +4363 -0
  230. data/po/Gestion-fr.mo +0 -0
  231. data/po/Gestion-fr.po +4345 -0
  232. data/po/traduction-ar.rtf +76 -0
  233. metadata +381 -0
@@ -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