csv_file_process.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. <?php
  2. if (!class_exists('csv_file_process')) {
  3. class csv_file_process {
  4. private $csv_file;
  5. private $csv_fields_order;
  6. private $is_generate_vm_pass;
  7. private $is_generate_ext_pass;
  8. private $is_add_device_profile;
  9. private $is_add_extension_user;
  10. private $is_add_device;
  11. private $vm_password_length;
  12. private $db;
  13. private $domain_uuid;
  14. private $domain_name;
  15. private $line_sip_transport;
  16. private $line_sip_port;
  17. private $line_register_expires;
  18. public function __construct($file_path, $csv_delimiter = False) {
  19. if (!file_exists($file_path)) {
  20. $this->csv_file = False;
  21. return;
  22. }
  23. $this->is_import_devices = False;
  24. $this->csv_fields_order = False;
  25. $this->is_generate_vm_pass = False;
  26. $this->is_generate_ext_pass = False;
  27. $this->is_is_add_device_profile = False;
  28. //$this->numerical_fields = ['number_alias', 'call_timeout', 'voicemail_id'];
  29. $this->csv_file = new SplFileObject($file_path);
  30. if ($csv_delimiter) {
  31. $this->csv_file->setCsvControl($csv_delimiter);
  32. return;
  33. }
  34. // Guessing CSV delimiter
  35. if (count($this->csv_file->fgetcsv()) != 1) {
  36. return;
  37. }
  38. // Trying ';'
  39. $this->csv_file->rewind();
  40. $this->csv_file->setCsvControl(";");
  41. if (count($this->csv_file->fgetcsv()) != 1) {
  42. return;
  43. }
  44. // Trying 'tab'
  45. $this->csv_file->rewind();
  46. $this->csv_file->setCsvControl("\t");
  47. if (count($this->csv_file->fgetcsv()) != 1) {
  48. return;
  49. }
  50. // Trying 'space'
  51. $this->csv_file->rewind();
  52. $this->csv_file->setCsvControl(" ");
  53. if (count($this->csv_file->fgetcsv()) != 1) {
  54. return;
  55. }
  56. // Trying ':'
  57. $this->csv_file->rewind();
  58. $this->csv_file->setCsvControl(":");
  59. if (count($this->csv_file->fgetcsv()) != 1) {
  60. return;
  61. }
  62. // Cannot get csv file delimiter. Unsetting file
  63. unset($this->csv_file);
  64. }
  65. public function __destruct() {
  66. unset($this->csv_file);
  67. }
  68. // Creation part end
  69. // Private functions start
  70. private function starts_with($haystack, $needle) {
  71. return strncmp($haystack, $needle, strlen($needle)) === 0;
  72. }
  73. private function ends_with($haystack, $needle) {
  74. return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
  75. }
  76. // Funcion to add missing fields if any and check if some of actual fields are correct.
  77. private function normalize_line($csv_line) {
  78. $result = array();
  79. foreach ($this->csv_fields_order as $index => $value) {
  80. $csv_field = (isset($csv_line[$index])) ? $csv_line[$index] : '';
  81. switch ($value) {
  82. case 'extension':
  83. if (strlen($csv_field) == 0) {
  84. // Immediate exit if extension field is empty
  85. return False;
  86. }
  87. $result['voicemail_id'] = (is_numeric($csv_field)) ? (int) $csv_field : False;
  88. break;
  89. case 'number_alias':
  90. case 'voicemail_password':
  91. $csv_field = (is_numeric($csv_field)) ? (int) $csv_field : '';
  92. break;
  93. case 'limit_max':
  94. $csv_field = (is_numeric($csv_field)) ? (int) $csv_field : 5;
  95. break;
  96. case 'call_timeout':
  97. $csv_field = (is_numeric($csv_field)) ? (int) $csv_field : 30;
  98. break;
  99. case 'voicemail_enabled':
  100. $csv_field = (filter_var($csv_field, FILTER_VALIDATE_BOOLEAN)) ? 'true' : 'false';
  101. break;
  102. }
  103. $result[$value] = $csv_field;
  104. }
  105. if ($this->is_generate_vm_pass) {
  106. $result['voicemail_password'] = generate_password($this->vm_password_length, 1);
  107. }
  108. if ($this->is_generate_ext_pass) {
  109. $result['password'] = generate_password();
  110. }
  111. // Set defaults that might be set before
  112. $result['limit_max'] = isset($result['limit_max']) ? $result['limit_max'] : 5;
  113. $result['call_timeout'] = isset($result['call_timeout']) ? $result['call_timeout'] : 30;
  114. $result['directory_visible'] = isset($result['directory_visible']) ? $result['directory_visible'] : 'true';
  115. $result['directory_exten_visible'] = $result['directory_visible'];
  116. $result['description'] = isset($result['description']) ? $result['description'] : '';
  117. $result['voicemail_mail_to'] = isset($result['voicemail_mail_to']) ? $result['voicemail_mail_to'] : '';
  118. $result['voicemail_enabled'] = isset($result['voicemail_enabled']) ? $result['voicemail_enabled'] : 'true';
  119. $result['voicemail_description'] = $result['description'];
  120. // Set various defaults that is not controlled by user
  121. $result['limit_destination'] = 'error/user_busy';
  122. $result['call_screen_enabled'] = 'false';
  123. $result['user_context'] = $this->domain_name;
  124. $result['enabled'] = 'true';
  125. $result['voicemail_file'] = 'attach';
  126. $result['voicemail_local_after_email'] = 'true';
  127. // Device section
  128. if ($this->is_add_device) {
  129. // Normalize mac address
  130. $result['device_mac_address'] = strtolower($result['device_mac_address']);
  131. $result['device_mac_address'] = preg_replace("/[^a-f0-9]+/", "", $result['device_mac_address']);
  132. $result['device_label'] = isset($result['device_label']) ? $result['device_label'] : $result['extension'];
  133. $result['device_template'] = isset($result['device_template']) ? strtolower($result['device_template']) : '';
  134. $result['device_enabled'] = 'true';
  135. $result['device_vendor'] = explode('/', $result['device_template'])[0];
  136. }
  137. return $result;
  138. }
  139. private function get_one_result($sql) {
  140. $prep_statement = $this->db->prepare(check_sql($sql));
  141. $prep_statement->execute();
  142. $result = $prep_statement->fetch(PDO::FETCH_NUM);
  143. $result_count = count($result);
  144. if ($result_count == 0) {
  145. return False;
  146. }
  147. return $result[0];
  148. }
  149. private function form_prepare_insert_statement($csv_line) {
  150. // Funcion to form part of INSERT statement with PDO prepared form
  151. // like '(extension_uuid, domain_uuid) VALUES (?, ?)
  152. // ? symbols replaced on execute() stage.
  153. $sql = '';
  154. $keys = array_keys($csv_line);
  155. $sql .= "(" . implode(',', $keys) . ") VALUES (";
  156. $sql .= str_repeat('?,', count($keys));
  157. $sql = rtrim($sql, ", ") . ")";
  158. return $sql;
  159. }
  160. private function form_prepare_update_statement($csv_line) {
  161. // Function to form part of UPDATE statement with PDO prepared form
  162. // like 'extension_uuid = ?, domain_uuid = ?'
  163. // ? symbols replaced on execute() stage.
  164. $sql = '';
  165. $keys = array_keys($csv_line);
  166. $sql .= implode(' = ?,', $keys);
  167. $sql .= " = ?";
  168. return $sql;
  169. }
  170. private function prepare_and_execute_statement($sql, $insert_array = NULL) {
  171. // Function for prepare end execute statements. Mostly done for extensive logging errors.
  172. $prep_statement = $this->db->prepare(check_sql($sql));
  173. if (!$prep_statement) {
  174. // Not that efficient logging for errors, but better than nothing
  175. echo $sql . "\n";
  176. echo "Prepare error: ". json_encode($this->db->errorInfo()) . "\n" . json_encode($prep_statement->errorInfo()) . "\n";
  177. return;
  178. }
  179. if (!$prep_statement->execute(array_values($insert_array))) {
  180. echo "Execute error: ". json_encode($prep_statement->errorInfo()) . "\n";
  181. echo $sql . "\n" . json_encode(array_values($insert_array)) . "\n";
  182. }
  183. }
  184. private function add_extension($csv_line) {
  185. // Check if extension is there
  186. $sql = "SELECT extension_uuid FROM v_extensions";
  187. $sql .= " WHERE domain_uuid = '" . $this->domain_uuid . "'";
  188. $sql .= " AND extension = '" . $csv_line['extension'] . "'";
  189. $sql .= " LIMIT 1";
  190. // Getting UUID if exists
  191. $extension_csv_line = array();
  192. $extension_csv_line['extension_uuid'] = $this->get_one_result($sql);
  193. // Remove non-needed entires from CSV
  194. foreach ($csv_line as $key => $value) {
  195. if ($this->starts_with($key, 'device_') or $this->starts_with($key, 'voicemail_') or $this->starts_with($key, 'extension_')) {
  196. continue;
  197. }
  198. $extension_csv_line[$key] = $value;
  199. }
  200. if ($extension_csv_line['extension_uuid']) {
  201. // Update existing extension
  202. $sql = "UPDATE v_extensions SET ";
  203. $sql .= $this->form_prepare_update_statement($extension_csv_line);
  204. $sql .= " WHERE extension_uuid = '" . $extension_csv_line['extension_uuid'] . "'";
  205. $sql .= " AND domain_uuid = '" . $this->domain_uuid . "'";
  206. } else {
  207. // Insert new extension
  208. $extension_csv_line['domain_uuid'] = $this->domain_uuid;
  209. $extension_csv_line['extension_uuid'] = uuid();
  210. $sql = "INSERT INTO v_extensions ";
  211. $sql .= $this->form_prepare_insert_statement($extension_csv_line);
  212. }
  213. $this->prepare_and_execute_statement($sql, $extension_csv_line);
  214. if ($this->is_add_extension_user) {
  215. // Get user id by name
  216. $sql = "SELECT user_uuid FROM v_users";
  217. $sql .= " WHERE domain_uuid = '" . $this->domain_uuid . "'";
  218. $sql .= " AND username = '" . $csv_line['extension_user'] . "'";
  219. $sql .= " LIMIT 1";
  220. $user_uuid = $this->get_one_result($sql);
  221. if (!$user_uuid) {
  222. // Cannot find this user_uuid. So, can't add user to extension
  223. return;
  224. }
  225. // Check if this link is already exists
  226. $sql = "SELECT extension_user_uuid FROM v_extension_users";
  227. $sql .= " WHERE domain_uuid = '" . $this->domain_uuid . "'";
  228. $sql .= " AND extension_uuid = '" . $extension_csv_line['extension_uuid'] . "'";
  229. $sql .= " AND user_uuid = '" . $user_uuid . "'";
  230. $sql .= " LIMIT 1";
  231. if ($this->get_one_result($sql)) {
  232. // Link is already exists
  233. return;
  234. }
  235. // Prepare data to insert into database
  236. $extension_to_user = array(
  237. 'extension_user_uuid' => uuid(),
  238. 'domain_uuid' => $this->domain_uuid,
  239. 'extension_uuid' => $extension_csv_line['extension_uuid'],
  240. 'user_uuid' => $user_uuid,
  241. );
  242. $sql = "INSERT INTO v_extension_users";
  243. $sql .= $this->form_prepare_insert_statement($extension_to_user);
  244. $this->prepare_and_execute_statement($sql, $extension_to_user);
  245. }
  246. }
  247. private function add_voicemail($csv_line) {
  248. if (strlen($csv_line['voicemail_id']) == 0) {
  249. return;
  250. }
  251. // Check if VM id is there
  252. $sql = "SELECT voicemail_uuid FROM v_voicemails";
  253. $sql .= " WHERE domain_uuid = '" . $this->domain_uuid . "'";
  254. $sql .= " AND voicemail_id = '" . $csv_line['voicemail_id'] . "'";
  255. $sql .= " LIMIT 1";
  256. $voicemail_csv_line = array();
  257. // Getting UUID if exists
  258. $voicemail_csv_line['voicemail_uuid'] = $this->get_one_result($sql);
  259. // Leaving only necessary data
  260. foreach ($csv_line as $key => $value) {
  261. if ($this->starts_with($key, 'voicemail_')) {
  262. $voicemail_csv_line[$key] = $value;
  263. }
  264. }
  265. if ($voicemail_csv_line['voicemail_uuid']) {
  266. $sql = "UPDATE v_voicemails SET ";
  267. $sql .= $this->form_prepare_update_statement($voicemail_csv_line);
  268. $sql .= " WHERE voicemail_uuid = '" . $voicemail_csv_line['voicemail_uuid'] . "'";
  269. $sql .= " AND domain_uuid = '" . $this->domain_uuid . "'";
  270. } else {
  271. $voicemail_csv_line['domain_uuid'] = $this->domain_uuid;
  272. $voicemail_csv_line['voicemail_uuid'] = uuid();
  273. $sql = "INSERT INTO v_voicemails";
  274. $sql .= $this->form_prepare_insert_statement($voicemail_csv_line);
  275. }
  276. $this->prepare_and_execute_statement($sql, $voicemail_csv_line);
  277. }
  278. private function add_device($csv_line) {
  279. if (!$this->is_add_device) {
  280. return;
  281. }
  282. // Check if it is MAC address actually
  283. if (strlen($csv_line['device_mac_address']) != 12) {
  284. return;
  285. }
  286. // First - check if device exists
  287. $sql = "SELECT device_uuid FROM v_devices";
  288. $sql .= " WHERE domain_uuid = '" . $this->domain_uuid . "'";
  289. $sql .= " AND device_mac_address = '" . $csv_line['device_mac_address'] . "'";
  290. $sql .= " LIMIT 1";
  291. $device_csv_line = array();
  292. $device_csv_line['device_uuid'] = $this->get_one_result($sql);
  293. // Cleanup CSV file
  294. foreach ($csv_line as $key => $value) {
  295. if ($this->starts_with($key, 'device_')) {
  296. $device_csv_line[$key] = $value;
  297. }
  298. }
  299. // Check for profile UUID
  300. if (isset($device_csv_line['device_profile'])) {
  301. $sql = "SELECT device_profile_uuid FROM v_device_profiles";
  302. $sql .= " WHERE domain_uuid = '" . $this->domain_uuid . "'";
  303. $sql .= " AND device_profile_name = '" . $device_csv_line['device_profile'] . "'";
  304. $sql .= " AND device_profile_enabled = 'true'";
  305. $sql .= " LIMIT 1";
  306. $device_csv_line['device_profile_uuid'] = $this->get_one_result($sql);
  307. if (!$device_csv_line['device_profile_uuid']) {
  308. unset($device_csv_line['device_profile_uuid']);
  309. }
  310. unset($device_csv_line['device_profile']);
  311. }
  312. if ($device_csv_line['device_uuid']) {
  313. $sql = "UPDATE v_devices SET ";
  314. $sql .= $this->form_prepare_update_statement($device_csv_line);
  315. $sql .= " WHERE device_uuid = '" . $device_csv_line['device_uuid'] . "'";
  316. $sql .= " AND domain_uuid = '" . $this->domain_uuid . "'";
  317. } else {
  318. $device_csv_line['domain_uuid'] = $this->domain_uuid;
  319. $device_csv_line['device_uuid'] = uuid();
  320. $sql = "INSERT INTO v_devices ";
  321. $sql .= $this->form_prepare_insert_statement($device_csv_line);
  322. }
  323. $this->prepare_and_execute_statement($sql, $device_csv_line);
  324. // Add line 1 for extension
  325. $device_line_array = array(
  326. 'line_number' => '1',
  327. 'server_address' => $this->domain_name,
  328. 'display_name' => $csv_line['extension'],
  329. 'user_id' => $csv_line['extension'],
  330. 'auth_id' => $csv_line['extension'],
  331. 'password' => $csv_line['password'],
  332. 'sip_port' => $this->line_sip_port,
  333. 'sip_transport' => $this->line_sip_transport,
  334. 'register_expires' => $this->line_register_expires,
  335. 'enabled' => 'true',
  336. 'device_uuid' => $device_csv_line['device_uuid'],
  337. );
  338. // Check if line 1 for this device already exists
  339. $sql = "SELECT device_line_uuid FROM v_device_lines";
  340. $sql .= " WHERE domain_uuid = '" . $this->domain_uuid . "'";
  341. $sql .= " AND device_uuid = '" . $device_csv_line['device_uuid'] . "'";
  342. $sql .= " LIMIT 1";
  343. $device_line_array['device_line_uuid'] = $this->get_one_result($sql);
  344. if ($device_line_array['device_line_uuid']) {
  345. $sql = "UPDATE v_device_lines SET ";
  346. $sql .= $this->form_prepare_update_statement($device_line_array);
  347. $sql .= " WHERE device_line_uuid = '" . $device_line_array['device_line_uuid'] . "'";
  348. $sql .= " AND domain_uuid = '" . $this->domain_uuid . "'";
  349. } else {
  350. $device_line_array['domain_uuid'] = $this->domain_uuid;
  351. $device_line_array['device_line_uuid'] = uuid();
  352. $sql = "INSERT INTO v_device_lines";
  353. $sql .= $this->form_prepare_insert_statement($device_line_array);
  354. }
  355. $this->prepare_and_execute_statement($sql, $device_line_array);
  356. }
  357. // Private funcions end
  358. // Public functions start
  359. public function is_valid() {
  360. if ($this->csv_file) {
  361. return True;
  362. }
  363. return False;
  364. }
  365. public function read_first($number_to_read = 4) {
  366. $this->csv_file->rewind();
  367. if (!$this->csv_file->valid()) {
  368. return False;
  369. }
  370. $result = array();
  371. for ($i = 1; $i <= $number_to_read; $i++) {
  372. if (!$this->csv_file->valid()) {
  373. break;
  374. }
  375. $result[] = array_map('escape',$this->csv_file->fgetcsv());
  376. //$result[] = $this->csv_file->fgetcsv();
  377. }
  378. $this->csv_file->rewind();
  379. return $result;
  380. }
  381. public function set_csv_fields_order($csv_fields_order) {
  382. $this->csv_fields_order = $csv_fields_order;
  383. $this->is_add_device = in_array('device_mac_address', $csv_fields_order);
  384. $this->is_generate_ext_pass = !in_array('password', $csv_fields_order);
  385. $this->is_generate_vm_pass = !in_array('voicemail_password', $csv_fields_order);
  386. $this->is_add_extension_user = in_array('extension_user', $csv_fields_order);
  387. if ($this->is_import_devices) {
  388. $this->is_add_device_profile = in_array('device_profile', $csv_fields_order);
  389. }
  390. }
  391. public function process_csv_file($options) {
  392. // Increase running time to 5 min
  393. set_time_limit(5 * 60);
  394. $result_message = '';
  395. $text = (new text)->get();
  396. $this->vm_password_length = $options['vm_password_length'];
  397. $this->db = $options['db'];
  398. $this->domain_uuid = $options['domain_uuid'];
  399. $this->domain_name = $options['domain_name'];
  400. $this->line_sip_transport = $options['line_sip_transport'];
  401. $this->line_sip_port = $options['line_sip_port'];
  402. $this->line_register_expires = $options['line_register_expires'];
  403. $skip_first_line = $options['skip_first_line'];
  404. if (!$this->csv_fields_order) {
  405. $result_message .= $text['message-csv_info_missing'] . "\n";
  406. return $result_message;
  407. }
  408. // Read file line by line
  409. $this->csv_file->rewind();
  410. $result_message .= $text['message-process_csv_file_start'] . "\n";
  411. // Skip first line if applied
  412. if ($skip_first_line) {
  413. $result_message .= $text['message-process_csv_file_skip_first_line'] . "\n";
  414. $this->csv_file->current();
  415. $this->csv_file->next();
  416. }
  417. $added_lines_count = 0;
  418. $skipped_lines_count = 0;
  419. $skipped_lines_array = array();
  420. while (!$this->csv_file->eof()) {
  421. // Read CSV line and sterialize it
  422. $csv_line = array_map('check_str', $this->csv_file->fgetcsv());
  423. $csv_line = $this->normalize_line($csv_line);
  424. if (array_key_exists('ignore', $csv_line)) {
  425. unset($csv_line['ignore']);
  426. }
  427. if ($csv_line) { // CSV line is correct and extension is present
  428. $this->add_extension($csv_line);
  429. $this->add_voicemail($csv_line);
  430. $this->add_device($csv_line);
  431. $added_lines_count += 1;
  432. } else {
  433. $skipped_lines_count += 1;
  434. $skipped_lines_array[] = $csv_line;
  435. }
  436. }
  437. $result_message .= $text['message-process_csv_file_end'] . "\n\n";
  438. // Add statistics to result message
  439. $result_message .= $text['message-process_csv_file_stats'] . "\n";
  440. $result_message .= " " . $text['message-process_csv_file_added_lines'] . " " . $added_lines_count . "\n";
  441. $result_message .= " " . $text['message-process_csv_file_skipped_lines'] . " " . $skipped_lines_count . "\n";
  442. if ($skipped_lines > 0) {
  443. foreach ($skipped_lines_array as $skipped_line) {
  444. $result_message .= " " . implode(',', $skipped_line) . "\n";
  445. }
  446. }
  447. return $result_message;
  448. }
  449. }
  450. }
  451. ?>