APIDatabaseError class for SQL error, which send debug info to slim\log
[dolibarr-api.git] / adherents.php
1 <?php
2
3 define('ADHERENT_LOGIN_REGEX', '[a-zA-Z][a-zA-Z0-9-_]{2,11}');
4 function adherent_status_tostring($status) {
5     if($status == -1)
6         return 'draft';
7     elseif($status == 0)
8         return 'terminated';
9     elseif($status >= 1)
10         return 'valid';
11     else
12         return '?';
13 }
14
15 function adherent_status_fromstring($statusstr) {
16     $arr=array(
17         'draft' => -1,
18         'terminated' => 0,
19         'valid' => 1,
20         '?' => -1
21     );
22     if(isset($arr[$statusstr]))
23         return $arr[$statusstr];
24     else
25         return false;
26 }
27
28 function adherents_response($adh) {
29     return array(
30         'id' => $adh->id,
31         'ref' => $adh->ref,
32         'honorific' => $adh->civilite_id,
33         'firstname' => $adh->firstname,
34         'lastname' => $adh->lastname,
35         'login' => $adh->login,
36         'pass' => $adh->pass,
37         'company' => $adh->societe,
38         'address' => $adh->address,
39         'zip' => $adh->zip,
40         'town' => $adh->town,
41
42         'state_id' => $adh->state_id,              // Id of department
43         'state_code' => $adh->state_code,            // Code of department
44         'state' => $adh->state,                 // Label of department
45
46         'country_id' => $adh->country_id,
47         'country_code' => $adh->country_code,
48         'country' => $adh->country,
49
50         'email' => $adh->email,
51         'phone' => $adh->phone,
52         'phone_perso' => $adh->phone_perso,
53         'phone_mobile' => $adh->phone_mobile,
54
55         'morphy' => $adh->morphy,
56         'public' => $adh->public,
57         'note' => $adh->note,                // Private note
58         'status' => adherent_status_tostring($adh->statut),
59         'photo' => $adh->photo,
60
61         'datec' => $adh->datec,
62         'datem' => $adh->datem,
63         'datefin' => $adh->datefin,
64         'datevalid' => $adh->datevalid,
65         'birth' => $adh->naiss,
66
67         'typeid' => $adh->typeid,            // Id type adherent
68         'type' => $adh->type,                // Libelle type adherent
69         'need_subscription' => $adh->need_subscription,
70
71         'user_id' => $adh->user_id,
72         'user_login' => $adh->user_login,
73
74         'fk_soc' => $adh->fk_soc,
75
76         // Fields loaded by fetch_subscriptions()
77         'first_subscription_date' => $adh->first_subscription_date,
78         'first_subscription_amount' => $adh->first_subscription_amount,
79         'last_subscription_date' => $adh->last_subscription_date,
80         'last_subscription_amount' => $adh->last_subscription_amount,
81 //        'subscriptions' => $adh->subscriptions,
82
83         'array_options' => $adh->array_options,
84     );
85 }
86
87 function adherent_populate($data, $adh=null) {
88     global $db;
89
90     $ex=new APIException(array());
91
92     $ret=check_keys($data, array('login', 'pass', 'email', 'firstname', 'lastname',
93                                  'societe', 'morphy', 'address', 'zip', 'town', 'phone',
94                                  'phone_perso', 'phone_mobile', 'birth', 'honorific', 'status'),
95                     function($a, $k) { return !(array_key_exists($k, $a) && !is_string($a[$k])); });
96     foreach($ret as $v)
97         $ex->addError(new APIInvalidParamError($v, 'string expected for parameter "'.$v.'"'));
98
99     $ret=check_keys($data, array('state_id'),
100                     function($a, $k) { return !(array_key_exists($k, $a) && !is_int($a[$k])); });
101     foreach($ret as $v)
102         $ex->addError(new APIInvalidParamError($v, 'int expected for parameter "'.$v.'"'));
103
104     if($ex->hasErrors())
105         throw $ex;
106
107     if(array_key_exists('birth', $data)) {
108         $data['birth']=DateTime::createFromFormat('Y-m-d', $data['birth']);
109         if($data['birth'] === false) {
110             $ex->addError(new APIInvalidParamError('birth', 'Invalid birth date format'));
111         }
112         $data['birth']=$data['birth']->format('Y-m-d');
113     }
114
115     if(array_key_exists('status', $data)) {
116         $data['status']=adherent_status_fromstring($data['status']);
117         if($data['status'] === false)
118             $ex->addError(new APIInvalidParamError('status', 'Invalid status'));
119     }
120
121     $data_f=filter_var_array($data, array(
122         'login'    => array(
123             'filter'  => FILTER_VALIDATE_REGEXP,
124             'options' => array('regexp' => '/^'.ADHERENT_LOGIN_REGEX.'$/')
125         ),
126         'email'    => array(
127             'filter'  => FILTER_VALIDATE_EMAIL,
128         ),
129         'morphy'   => array(
130             'filter'  => FILTER_VALIDATE_REGEXP,
131             'options' => array('regexp' => '/^(mor|phy)$/')
132         ),
133     ));
134     foreach($data_f as $k => $v) {
135         if($v === false)
136             $ex->addError(new APIInvalidParamError($k, 'Could not validate parameter "'.$k.'"'));
137     }
138
139     if(array_key_exists('adhtype', $data)) {
140         $typeid = adhtype_byname($data['adhtype']);
141         if(is_null($typeid)) {
142             $ex->addError(new APIInvalidParamError('adhtype', 'Invalid adherent type'));
143         } elseif($typeid < 1) {
144             throw new APIException(array(new APIDatabaseError('Could not fetch adherent type', $db)));
145         }
146         $data['adhtype_id']=$typeid;
147     } else {
148         unset($data['adhtype_id']);
149     }
150
151     if(array_key_exists('honorific', $data)) {
152         $honorific = honorific_bycode($data['honorific']);
153         if(is_null($honorific)) {
154             $ex->addError(new APIInvalidParamError('honorific', 'Invalid honorific'));
155         } elseif($honorific < 1) {
156             throw new APIException(array(new APIDatabaseError('Could not fetch honorific', $db)));
157         }
158     }
159
160     if(array_key_exists('country', $data)) {
161         $countryid = country_byname($data['country']);
162         if(is_null($countryid)) {
163             $ex->addError(new APIInvalidParamError('country', 'Invalid country'));
164         } elseif($countryid < 1) {
165             throw new APIException(array(new APIDatabaseError('Could not fetch country', $db)));
166         }
167         $data['country_id']=$countryid;
168     } else {
169         unset($data['country_id']);
170     }
171
172     if($ex->hasErrors())
173         throw $ex;
174
175     /* data key => adh attribute */
176     $data_to_attr = array(
177         'login'        => 'login',
178         'pass'         => 'pass',
179         'email'        => 'email',
180         'honorific'    => 'civilite_id',
181         'firstname'    => 'firstname',
182         'lastname'     => 'lastname',
183         'company'      => 'societe',
184         'morphy'       => 'morphy',
185         'address'      => 'address',
186         'zip'          => 'zip',
187         'town'         => 'town',
188         'birth'        => 'naiss',
189         'phone'        => 'phone',
190         'phone_perso'  => 'phone_perso',
191         'phone_mobile' => 'phone_mobile',
192         'note'         => 'note',
193         'adhtype_id'   => 'typeid',
194         'country_id'   => 'country_id',
195         'state_id'     => 'state_id',
196         'status'       => 'statut',
197     );
198
199     if(is_null($adh))
200         $adh=new Adherent($db);
201
202     foreach($data as $k => $v) {
203         if(array_key_exists($k, $data_to_attr)) {
204             $adh->{$data_to_attr[$k]} = $v;
205         }
206     }
207
208     return $adh;
209 }
210
211 function adherent_type_response($adhtype) {
212     return array(
213         'id'                 => $adhtype->rowid,
214         'ref'                => $adhtype->rowid,
215         'name'               => $adhtype->libelle,
216 //        'status'             => $adhtype->statut,
217         'need_subscription'  => $adhtype->cotisation,
218         'welcome_message'    => $adhtype->mail_valid,
219         'note'               => $adhtype->note,
220         'can_vote'           => $adhtype->vote,
221     );
222 }
223
224 function adhtype_byname($name) {
225     global $db;
226
227     $sql = "SELECT d.rowid";
228     $sql .= " FROM ".MAIN_DB_PREFIX."adherent_type as d";
229     $sql .= " WHERE d.libelle = '".$db->escape($name)."'";
230
231     $resql=$db->query($sql);
232     if ($resql) {
233         if($db->num_rows($resql)) {
234             $obj = $db->fetch_object($resql);
235             return $obj->rowid;
236         }
237         return null;
238     } else {
239         return -1;
240     }
241 }
242
243 /* gen thirtparty name from adherent */
244 function gen_thirdparty_name($adh) {
245     global $langs;
246
247     $name = $adh->getFullName($langs);
248     if (!empty($name))
249         if ($adh->societe) $name.=' ('.$object->societe.')';
250     else
251         $name=$adh->societe;
252     return $name;
253 }
254
255
256
257 $app->map('/v1/adherents/:id/', add_allow('GET', 'HEAD', 'PATCH'), $return405)
258         ->via('POST', 'PUT')->conditions(array('id' => '[0-9]+'));
259 $app->options('/v1/adherents/:id/', add_allow('GET', 'HEAD', 'PATCH'), function() {})
260         ->conditions(array('id' => '[0-9]+'));
261
262 $app->get('/v1/adherents/:id/', function($id) use ($app, $db) {
263     $adh=new Adherent($db);
264     $r=$adh->fetch($id);
265     if($r < 0) {
266         halt_error(500, new APIDatabaseError('Adherent could not be fetched', $db));
267     } elseif($r === 0) {
268         halt_error(404, new APIObjectNotFoundError('Adherent not found'));
269     }
270
271     $app->lastModified($adh->datem);
272     $app->expires('+1 day');
273
274     if($app->request()->isHead()) {
275         $app->halt(200);
276     }
277
278     $resp=adherents_response($adh);
279
280     $app->response()->body(
281         marsh_response(array('adherent' => $resp))
282     );
283
284 })->conditions(array('id' => '[0-9]+'));
285
286
287 $app->map('/v1/adherents/:id/', function($id) use ($app, $db) {
288     $data=$app->request()->getBody();
289
290     $adh=new Adherent($db);
291     $r=$adh->fetch((int)$id);
292     if($r < 0) {
293         halt_error(500, new APIDatabaseError('Adherent could not be fetched', $db));
294     } elseif($r === 0) {
295         halt_error(404, new APIObjectNotFoundError('Adherent not found'));
296     }
297
298     if(!check_unmodified_since($adh->datem)) {
299         halt_error(412, new APIError('Adherent was modified, If-Unmodified-Since precondition failed'));
300     }
301
302     try {
303         $adh=adherent_populate($data, $adh);
304     } catch(APIException $e) {
305         $app->halt(400, marsh_response($e));
306     }
307
308     $ret=$adh->update(new User($db));
309     if($ret < 1) {
310         halt_error(500, new APIDatabaseError('Adherent could not be updated', $db, $adh->error, $ret));
311     }
312
313     $adh=new Adherent($db);
314     $ret=$adh->fetch((int)$id);
315     if($ret < 0) {
316         halt_error(500, new APIDatabaseError('The created adherent could not be fetched back from database', $db));
317     } elseif($r === 0) {
318         halt_error(500, new APIObjectNotFoundError('Adherent not found... but we just created it ?'));
319     }
320
321     $app->response()->status(201);
322     $app->lastModified($adh->datem);
323     $app->expires('+1 day');
324     $app->response()['Content-Location'] = buildPath('/v1/adherents/'.$adh->id.'/');
325
326     $app->response()->body(
327         marsh_response(array('adherent' => adherents_response($adh)))
328     );
329 })->via('PATCH')->conditions(array('id' => '[0-9]+'));
330
331
332 $app->map('/v1/adherents/', add_allow('POST'), $return405)->via('GET', 'HEAD', 'PUT', 'PATCH');
333 $app->options('/v1/adherents/', add_allow('POST'), function() {});
334
335 $app->post('/v1/adherents/', function() use ($app, $db) {
336     $data=$app->request()->getBody();
337
338     $adh=new Adherent($db);
339     $adh->fetch_login($data['login']);
340     if($adh->error) {
341         halt_error(500, new APIDatabaseError('Could not verify login uniqueness', $db));
342     } elseif($adh->id != NULL) {
343         halt_error(409, new APIError('Login already taken'));
344     }
345
346     $errs=array();
347     $ret=check_keys($data, array('login', 'pass', 'email', 'morphy', 'adhtype'),
348                     function($a, $k) { return array_key_exists($k, $a); });
349     foreach($ret as $v)
350         $errs[] = new APIMissingParamError($v, 'Missing required parameter "'.$v.'"');
351
352     if(count($errs))
353         $app->halt(400, marsh_response(array('errors' => $errs)));
354
355
356     try {
357         $adh=adherent_populate($data);
358     } catch(APIException $e) {
359         $app->halt(400, marsh_response($e));
360     }
361
362     $ret=$adh->create(new User($db));
363     if($ret < 1) {
364         halt_error(500, new APIDatabaseError('Adherent could not be created', $db, $adh->error, $ret));
365     }
366
367     $rowid=$adh->id;
368     $adh=new Adherent($db);
369     $ret=$adh->fetch($rowid);
370     if($ret < 1) {
371         halt_error(500, new APIDatabaseError('The created adherent could not be fetched back from database ?!', $db));
372     }
373
374     $soc=new Societe($db);
375     $ret=$soc->create_from_member($adh, gen_thirdparty_name($adh));
376     if($ret < 1) {
377         halt_error(500, new APIDatabaseError('Unable to create a third-party linked to the adherent', $db, $adh->error, $ret));
378     }
379
380     $app->response()->status(201);
381     $app->lastModified($adh->datem);
382     $app->expires('+1 day');
383     $app->response()['Content-Location'] = buildPath('/v1/adherents/'.$adh->id.'/');
384
385     $app->response()->body(
386         marsh_response(array('adherent' => adherents_response($adh)))
387     );
388 });
389
390
391 $app->map('/v1/adherents/login/:login/', add_allow('GET', 'HEAD'), $return405)->via('POST', 'PUT', 'PATCH');
392 $app->options('/v1/adherents/login/:login/', add_allow('GET', 'HEAD'), function() {});
393
394 $app->get('/v1/adherents/login/:login/', function($login) use ($app, $db) {
395     $adh=new Adherent($db);
396     $r=$adh->fetch_login($login);
397     if($adh->error) {
398         halt_error(500, new APIDatabaseError('Adherent could not be fetched', $db));
399     } elseif($adh->id == NULL) {
400         halt_error(404, new APIObjectNotFoundError('Adherent not found'));
401     }
402
403     $app->lastModified($adh->datem);
404     $app->expires('+1 day');
405     $app->response()['Content-Location'] = buildPath('/v1/adherents/'.$adh->id.'/');
406
407     if($app->request()->isHead()) {
408         $app->halt(200);
409     }
410
411     $resp=adherents_response($adh);
412
413     $app->response()->body(
414         marsh_response(array('adherent' => $resp))
415     );
416
417 })->conditions(array('login' => ADHERENT_LOGIN_REGEX));
418
419
420 $app->map('/v1/adherents/types/', add_allow('GET'), $return405)->via('HEAD', 'POST', 'PUT', 'PATCH');
421 $app->options('/v1/adherents/types/', add_allow('GET'), function() {});
422
423 $app->get('/v1/adherents/types/', function() use ($app, $db) {
424     /* Yuck, raw SQL :[ */
425     $sql = "SELECT d.rowid, d.libelle, d.statut, d.cotisation, d.mail_valid, d.note, d.vote";
426     $sql .= " FROM ".MAIN_DB_PREFIX."adherent_type as d";
427     $sql .= " ORDER BY rowid ASC";
428
429     $res=array();
430
431     $resql=$db->query($sql);
432     if ($resql) {
433         for($i = 0; $i < $db->num_rows($resql); $i++) {
434             $obj = $db->fetch_object($resql);
435             $res[] = adherent_type_response($obj);
436         }
437     } else {
438         halt_error(500, new APIDatabaseError('Adherent types could not be fetched', $db));
439     }
440
441     $app->response()->body(
442         marsh_response(array('types' => $res))
443     );
444 });
445