BAOBAB
co_things_basalt_plugin.class.php
Go to the documentation of this file.
1 <?php
2 /***************************************************************************************************************************/
27 defined( 'LGV_BASALT_CATCHER' ) or die ( 'Cannot Execute Directly' ); // Makes sure that this file is in the correct context.
28 
29 /****************************************************************************************************************************/
34  /***********************/
38  protected function _get_xsd() {
39  return $this->_process_xsd(dirname(__FILE__).'/schema.xsd');
40  }
41 
42  /***********************/
48  protected function _get_long_thing_description( $in_object,
49  $in_show_parents = false
50  ) {
51  $ret = parent::_get_long_description($in_object, $in_show_parents);
52 
53  if (isset($in_object->tags()[0]) && trim($in_object->tags()[0])) {
54  $ret['key'] = trim($in_object->tags()[0]);
55  }
56 
57  if (isset($in_object->tags()[1]) && trim($in_object->tags()[1])) {
58  $ret['description'] = trim($in_object->tags()[1]);
59  }
60 
61  for ($tag = 2; $tag < 10; $tag++) {
62  if (isset($in_object->tags()[$tag]) && (NULL != $in_object->tags()[$tag])) {
63  $ret['tag'.$tag] = trim($in_object->tags()[$tag]);
64  }
65  }
66 
67  return $ret;
68  }
69 
70  /***********************/
76  protected function _process_parameters( $in_andisol_instance,
77  $in_query
78  ) {
79  $ret = parent::_process_parameters($in_andisol_instance, $in_query);
80 
81  if (isset($in_query) && is_array($in_query)) {
82  if (isset($in_query['key'])) {
83  $ret['key'] = trim(strval($in_query['key']));
84  }
85 
86  if (isset($in_query['description'])) {
87  $ret['description'] = trim(strval($in_query['description']));
88  }
89 
90  for ($tag = 2; $tag < 10; $tag++) {
91  if (isset($in_query['tag'.$tag])) {
92  $ret['tag'.$tag] = trim(strval($in_query['tag'.$tag]));
93  }
94  }
95  }
96 
97  return $ret;
98  }
99 
100  /***********************/
106  protected function _process_thing_post( $in_andisol_instance,
107  $in_path = [],
108  $in_query = []
109  ) {
110  $ret = [];
111  // Extract any valid key from the resource specification. Commas not allowed.
112  $thing_key = (isset($in_path) && is_array($in_path) && count($in_path) && (false === strpos(',', $in_path[0])) && !ctype_digit($in_path[0])) ? trim($in_path[0]) : NULL;
113 
114  $parameters = $this->_process_parameters($in_andisol_instance, $in_query);
115 
116  if ($thing_key) { // Not allowed to have any numerical value.
117  $parameters['key'] = $thing_key; // The resource specification always beats anything passed in.
118  }
119 
120  if ($in_andisol_instance->logged_in() && isset($parameters['payload']) && isset($parameters['key']) && $parameters['key'] && (false === strpos($parameters['key'], ','))) { // Must be logged in to POST, and we can't have commas in the key.
121  if (!$in_andisol_instance->key_is_unique($parameters['key'])) {
122  header('HTTP/1.1 400 Invalid Key');
123  exit();
124  }
125 
126  if ($in_andisol_instance->set_value_for_key($parameters['key'], $parameters['payload'])) {
127  $new_record = $in_andisol_instance->get_object_for_key($parameters['key']);
128  if ($new_record instanceof CO_KeyValue_CO_Collection) {
129  unset($parameters['payload']);
130  $result = $this->_process_thing_put($in_andisol_instance, [$new_record], $in_path, $in_query);
131 
132  $ret['new_thing'] = $result['changed_things'][0]['after'];
133  } else {
134  header('HTTP/1.1 400 Resource Not Created');
135  exit();
136  }
137  } else {
138  header('HTTP/1.1 400 Resource Not Created');
139  exit();
140  }
141  } elseif ($in_andisol_instance->logged_in() && isset($parameters['key']) && !isset($parameters['payload'])) {
142  header('HTTP/1.1 400 Invalid Payload');
143  exit();
144  } elseif ($in_andisol_instance->logged_in() && isset($parameters['key'])) {
145  header('HTTP/1.1 400 Invalid Resource Key');
146  exit();
147  } else {
148  header('HTTP/1.1 403 Forbidden');
149  exit();
150  }
151 
152  return $ret;
153  }
154 
155  /***********************/
161  protected function _process_thing_get( $in_andisol_instance,
162  $in_object_list = [],
163  $in_data_only = false,
164  $in_show_details = false,
165  $in_show_parents = false,
166  $in_search_count_only = false,
167  $in_search_ids_only = false,
168  $in_path = [],
169  $in_query = []
170  ) {
171  $ret = [];
172 
173  if ($in_search_count_only) {
174  $ret['count'] = intval($in_object_list);
175  } elseif (isset($in_object_list) && is_array($in_object_list) && (0 < count($in_object_list))) {
176  if ($in_search_ids_only) {
177  $ret['ids'] = $in_object_list;
178  } else {
179  foreach ($in_object_list as $thing) {
180  if ($in_show_details) {
181  $ret[] = $this->_get_long_thing_description($thing, $in_show_parents);
182  } elseif ($in_data_only) {
183  $payload = $thing->get_payload();
184  $temp_file = tempnam(sys_get_temp_dir(), 'RVP');
185  file_put_contents($temp_file , $payload);
186  $finfo = finfo_open(FILEINFO_MIME_TYPE);
187  $content_type = finfo_file($finfo, $temp_file);
188  $payload_type = $content_type.';base64,';
189  $ret = $payload_type.base64_encode($payload);
190  } else {
191  $ret[] = $this->_get_short_description($thing);
192  }
193  }
194  }
195  }
196 
197  return $ret;
198  }
199 
200  /***********************/
206  protected function _process_thing_delete( $in_andisol_instance,
207  $in_object_list = [],
208  $in_show_parents = false
209  ) {
210  $ret = [];
211 
212  if ($in_andisol_instance->logged_in()) { // Must be logged in to DELETE.
213  if (isset($in_object_list) && is_array($in_object_list) && (0 < count($in_object_list))) {
214  foreach ($in_object_list as $thing) {
215  $to_be_deleted = $this->_get_long_thing_description($thing, $in_show_parents);
216  if ($thing->user_can_write() && $thing->delete_from_db()) {
217  $ret['deleted_things'][] = $to_be_deleted;
218  }
219  }
220  }
221  } else {
222  header('HTTP/1.1 403 Forbidden');
223  exit();
224  }
225 
226  return $ret;
227  }
228 
229  /***********************/
235  protected function _process_thing_put( $in_andisol_instance,
236  $in_object_list = [],
237  $in_path = [],
238  $in_query = [],
239  $in_show_parents = false
240  ) {
241  if ($in_andisol_instance->logged_in()) { // Must be logged in to PUT.
242  $ret = [];
243  $fuzz_factor = isset($in_query) && is_array($in_query) && isset($in_query['fuzz_factor']) ? floatval($in_query['fuzz_factor']) : 0; // Set any fuzz factor.
244 
245  $parameters = $this->_process_parameters($in_andisol_instance, $in_query);
246  if (isset($parameters) && is_array($parameters) && count($parameters) && isset($in_object_list) && is_array($in_object_list) && count($in_object_list)) {
247  /*
248  What we are doing here, is using the "batch mode" for each record object to set the changes in thing without doing a DB update.
249  We generate a change report, but don't add the report to the final report yet, as the change isn't "set" yet.
250  After we make all the changes, we cycle through the records, clearing the "batch mode" for each record object, which sends it to the DB.
251  If the clear works, then we set it into the final report.
252  This makes the process work much better in a multiuser environment, where other clients could be querying the DB.
253  */
254  $change_reports = []; // We will keep our interin reports here.
255 
256  foreach ($in_object_list as $thing) {
257  if ($thing->user_can_write()) { // Belt and suspenders. Make sure we can write.
258  $thing->set_batch_mode();
259  // Take a "before" snapshot.
260  $changed_thing = ['before' => $this->_get_long_thing_description($thing, $in_show_parents)];
261  $result = true;
262 
263  // No commas allowed in the key.
264  if ($result && isset($parameters['key']) && (false === strpos(',', $parameters['key']))) {
265  $result = $thing->set_key($parameters['key']);
266  unset($parameters['key']); // There can only be one...
267  }
268 
269  if ($result && isset($parameters['name'])) {
270  $result = $thing->set_name($parameters['name']);
271  }
272 
273  if ($result && isset($parameters['write_token'])) {
274  $result = $thing->set_write_security_id($parameters['write_token']);
275  }
276 
277  if ($result && isset($parameters['lang'])) {
278  $result = $thing->set_lang($parameters['lang']);
279  }
280 
281  if ($result && isset($parameters['longitude'])) {
282  $result = $thing->set_longitude($parameters['longitude']);
283  }
284 
285  if ($result && isset($parameters['latitude'])) {
286  $result = $thing->set_latitude($parameters['latitude']);
287  }
288 
289  if ($result && isset($parameters['fuzz_factor'])) {
290  $result = $thing->set_fuzz_factor($parameters['fuzz_factor']);
291  }
292 
293  if ($result && isset($parameters['can_see_through_the_fuzz'])) {
294  $result = $thing->set_can_see_through_the_fuzz($parameters['can_see_through_the_fuzz']);
295  }
296 
297  if ($result && isset($parameters['description'])) {
298  $result = $thing->set_tag(1, $parameters['description']);
299  }
300 
301  if ($result && isset($parameters['tag2'])) {
302  $result = $thing->set_tag(2, $parameters['tag2']);
303  }
304 
305  if ($result && isset($parameters['tag3'])) {
306  $result = $thing->set_tag(3, $parameters['tag3']);
307  }
308 
309  if ($result && isset($parameters['tag4'])) {
310  $result = $thing->set_tag(4, $parameters['tag4']);
311  }
312 
313  if ($result && isset($parameters['tag5'])) {
314  $result = $thing->set_tag(5, $parameters['tag5']);
315  }
316 
317  if ($result && isset($parameters['tag6'])) {
318  $result = $thing->set_tag(6, $parameters['tag6']);
319  }
320 
321  if ($result && isset($parameters['tag7'])) {
322  $result = $thing->set_tag(7, $parameters['tag7']);
323  }
324 
325  if ($result && isset($parameters['tag8'])) {
326  $result = $thing->set_tag(8, $parameters['tag8']);
327  }
328 
329  if ($result && isset($parameters['tag9'])) {
330  $result = $thing->set_tag(9, $parameters['tag9']);
331  }
332 
333  if ($result && isset($parameters['remove_payload'])) {
334  $result = $thing->set_payload(NULL);
335  } elseif ($result && isset($parameters['payload'])) {
336  $result = $thing->set_payload($parameters['payload']);
337  }
338 
339  // We have previously split into "add" and "remove" lists (dictated by the sign of the integer).
340  if ($result && isset($parameters['child_ids'])) {
341  $add = $parameters['child_ids']['add'];
342  $remove = $parameters['child_ids']['remove'];
343 
344  foreach ($remove as $id) {
345  if ($id != $thing->id()) {
346  $child = $in_andisol_instance->get_single_data_record_by_id($id);
347  if (isset($child)) {
348  $result = $thing->deleteThisElement($child);
349  }
350 
351  if (!$result) {
352  break;
353  }
354  }
355  }
356 
357  if ($result) {
358  foreach ($add as $id) {
359  if ($id != $thing->id()) {
360  $child = $in_andisol_instance->get_single_data_record_by_id($id);
361  if (isset($child)) {
362  $result = $thing->appendElement($child);
363 
364  if (!$result) {
365  break;
366  }
367  }
368  }
369  }
370  }
371  }
372 
373  // We do this last, so we have the option of doing a "lock" (which isn't necessary in "batch mode").
374  if ($result && isset($parameters['read_token'])) {
375  $result = $thing->set_read_security_id($parameters['read_token']);
376  }
377 
378  if ($result) { // Assuming all went well to this point, we take an "after" snapshot, and save the object and interim report in our "final clear" list.
379  $changed_thing['after'] = $this->_get_long_thing_description($thing, $in_show_parents);
380  $change_reports[] = ['object' => $thing, 'report' => $changed_thing];
381  }
382  }
383  }
384 
385  // Here's where we actually set each record into the DB, and generate the full final report.
386  if ($result && count($change_reports)) {
387  $ret['changed_things'] = [];
388  foreach ($change_reports as $value) {
389  $result = $value['object']->clear_batch_mode();
390  if ($result) { // We only report the ones that work.
391  $ret['changed_things'][] = $value['report'];
392  } else {
393  break;
394  }
395  }
396  }
397  }
398  } else {
399  header('HTTP/1.1 403 Forbidden');
400  exit();
401  }
402  return $ret;
403  }
404 
405  /***********************/
411  static public function classes_managed() {
412  return ['CO_Collection', 'CO_KeyValue_CO_Collection'];
413  }
414 
415  /***********************/
419  public function plugin_name() {
420  return 'things';
421  }
422 
423  /***********************/
429  public function process_command( $in_andisol_instance,
430  $in_http_method,
431  $in_response_type,
432  $in_path = [],
433  $in_query = []
434  ) {
435  $ret = [];
436  $data_only = false; // Return only base64 data. If this is sent, the return type is ignored. Multiple responses are concatenated by spaces.
437 
438  if ('POST' == $in_http_method) { // We handle POST directly.
439  $ret = $this->_process_thing_post($in_andisol_instance, $in_path, $in_query);
440  } else {
441  $show_parents = isset($in_query) && is_array($in_query) && isset($in_query['show_parents']); // Show all things in detail, as well as the parents (applies only to GET).
442  $show_details = $show_parents || (isset($in_query) && is_array($in_query) && isset($in_query['show_details'])); // Show all things in detail (applies only to GET).
443  $data_only = (isset($in_query) && is_array($in_query) && isset($in_query['data_only']));
444  $writeable = isset($in_query) && is_array($in_query) && isset($in_query['writeable']); // Show/list only things this user can modify.
445  $search_count_only = isset($in_query) && is_array($in_query) && isset($in_query['search_count_only']); // Ignored for discrete IDs. If true, then a simple "count" result is returned as an integer.
446  $search_ids_only = isset($in_query) && is_array($in_query) && isset($in_query['search_ids_only']); // Ignored for discrete IDs. If true, then the response will be an array of integers, denoting resource IDs.
447  $search_page_size = isset($in_query) && is_array($in_query) && isset($in_query['search_page_size']) ? abs(intval($in_query['search_page_size'])) : 0; // Ignored for discrete IDs. This is the size of a page of results (1-based result count. 0 is no page size).
448  $search_page_number = isset($in_query) && is_array($in_query) && isset($in_query['search_page_number']) ? abs(intval($in_query['search_page_number'])) : 0; // Ignored for discrete IDs, or if search_page_size is 0. The page we are interested in (0-based. 0 is the first page).
449 
450  $thinglist = [];
451 
452  // For the default (no thing ID), we simply act on a list of all available things (or filtered by some search criteria).
453  if (0 == count($in_path)) {
454  $radius = isset($in_query) && is_array($in_query) && isset($in_query['search_radius']) && (0.0 < floatval($in_query['search_radius'])) ? floatval($in_query['search_radius']) : NULL;
455  $longitude = isset($in_query) && is_array($in_query) && isset($in_query['search_longitude']) ? floatval($in_query['search_longitude']) : NULL;
456  $latitude = isset($in_query) && is_array($in_query) && isset($in_query['search_latitude']) ? floatval($in_query['search_latitude']) : NULL;
457 
458  $location_search = NULL;
459 
460  if (isset($radius) && isset($longitude) && isset($latitude)) {
461  $location_search = Array('radius' => $radius, 'longitude' => $longitude, 'latitude' => $latitude);
462  }
463 
464  $class_search = Array('%_KeyValue_CO_Collection', 'use_like' => 1);
465 
466  $search_array = ['access_class' => $class_search];
467 
468  if (isset($location_search)) {
469  $search_array['location'] = $location_search;
470  }
471 
472  $search_name = isset($in_query) && is_array($in_query) && isset($in_query['search_name']) ? trim($in_query['search_name']) : NULL; // Search in the object name.
473 
474  if (isset($search_name)) {
475  $search_array['name'] = Array($search_name, 'use_like' => 1);
476  }
477 
478  $tags = [NULL];
479 
480  $in_query['search_tag1'] = isset($in_query) && is_array($in_query) && isset($in_query['search_description']) ? trim($in_query['search_description']) : NULL;
481  $has_tag = false;
482 
483  for ($tag = 1; $tag < 10; $tag++) {
484  $tag_string = 'search_tag'.$tag;
485  $tag_value = isset($in_query) && is_array($in_query) && isset($in_query[$tag_string]) ? trim($in_query[$tag_string]) : NULL;
486  if ($tag_value !== NULL) {
487  $has_tag = true;
488  }
489  $tags[] = $tag_value;
490  }
491 
492  // If there were any specified tags, we search by tag. Otherwise, we don't bother.
493  if ($has_tag) {
494  $search_array['tags'] = $tags;
495  $search_array['tags']['use_like'] = 1;
496  }
497 
498  $thinglist = $in_andisol_instance->generic_search($search_array, false, $search_page_size, $search_page_number, $writeable, $search_count_only, $search_ids_only);
499  } else {
500  $thing_id_list = array_unique(explode(',', $in_path[0]));
501 
502  if ($data_only) { // We only do one at a time for data only.
503  $thing_id_list = [$thing_id_list[0]];
504  }
505 
506  foreach ($thing_id_list as $id) {
507  $thing = NULL;
508 
509  if (ctype_digit($id) && (0 < intval($id))) { // Numerical, we go by ID
510  $thing = $in_andisol_instance->get_single_data_record_by_id($id);
511  }
512 
513  if (!$thing) {
514  $id = urldecode($id);
515  $thing = $in_andisol_instance->get_object_for_key($id);
516  }
517 
518  if (isset($thing) && ($thing instanceof CO_Collection)) {
519  $thinglist[] = $thing;
520  }
521 
522  if ($data_only) { // We only allow one thing for data only.
523  break;
524  }
525  }
526  }
527 
528  if ('GET' == $in_http_method) {
529  $ret = $this->_process_thing_get($in_andisol_instance, $thinglist, $data_only, $show_details, $show_parents, $search_count_only, $search_ids_only, $in_path, $in_query);
530  } elseif ('DELETE' == $in_http_method) {
531  $ret = $this->_process_thing_delete($in_andisol_instance, $thinglist, $show_parents);
532  } else {
533  $ret = $this->_process_thing_put($in_andisol_instance, $thinglist, $in_path, $in_query, $show_parents);
534  }
535  }
536 
537  return $data_only ? $ret : $this->_condition_response($in_response_type, $ret);
538  }
539 }
_get_long_thing_description( $in_object, $in_show_parents=false)
process_command( $in_andisol_instance, $in_http_method, $in_response_type, $in_path=[], $in_query=[])
_process_thing_get( $in_andisol_instance, $in_object_list=[], $in_data_only=false, $in_show_details=false, $in_show_parents=false, $in_search_count_only=false, $in_search_ids_only=false, $in_path=[], $in_query=[])
_process_thing_post( $in_andisol_instance, $in_path=[], $in_query=[])
_process_thing_put( $in_andisol_instance, $in_object_list=[], $in_path=[], $in_query=[], $in_show_parents=false)
_process_thing_delete( $in_andisol_instance, $in_object_list=[], $in_show_parents=false)
_process_parameters( $in_andisol_instance, $in_query)