diff --git a/modules/contacts/modules.php b/modules/contacts/modules.php
index 6a6334e185..ddb70162f6 100644
--- a/modules/contacts/modules.php
+++ b/modules/contacts/modules.php
@@ -85,6 +85,33 @@ public function process() {
}
}
+/**
+ * @subpackage contacts/handler
+ */
+class Hm_Handler_process_export_contacts extends Hm_Handler_Module {
+ public function process() {
+ if (array_key_exists('contact_source', $this->request->get)) {
+ $source = $this->request->get['contact_source'];
+ $contacts = $this->get('contact_store');
+ $contact_list = $contacts->getAll();
+ if ($source != 'all') {
+ $contact_list = $contacts->export($source);
+ }
+
+ Hm_Functions::header('Content-Type: text/csv');
+ Hm_Functions::header('Content-Disposition: attachment; filename="'.$source.'_contacts.csv"');
+ $output = fopen('php://output', 'w');
+ fputcsv($output, array('display_name', 'email_address', 'phone_number'));
+ foreach ($contact_list as $contact) {
+ $contact_data = is_array($contact) ? $contact : $contact->export();
+ fputcsv($output, array($contact_data['display_name'], $contact_data['email_address'], $contact_data['phone_number']));
+ }
+ fclose($output);
+ exit;
+ }
+ }
+}
+
/**
* @subpackage contacts/output
*/
@@ -107,7 +134,16 @@ protected function output() {
*/
class Hm_Output_contacts_content_start extends Hm_Output_Module {
protected function output() {
- return '
'.$this->trans('Contacts').'
';
+ $contact_source_list = $this->get('contact_sources', array());
+ $actions = '
'.$this->trans('Export Contacts as CSV').'
';
+ $actions .= '
';
+ foreach ($contact_source_list as $value) {
+ $actions .= '
';
+ }
+
+ return '
'.$this->trans('Contacts'). '
'.
+ '
'.$actions.'
';
}
}
@@ -148,11 +184,25 @@ protected function output() {
}
}
+/**
+ * @subpackage contacts/handler
+ */
+class Hm_Handler_check_imported_contacts extends Hm_Handler_Module
+{
+ public function process()
+ {
+ $imported_contact = $this->session->get('imported_contact', array());
+ $this->session->del('imported_contact');
+ $this->out('imported_contact', $imported_contact);
+ }
+}
+
/**
* @subpackage contacts/output
*/
class Hm_Output_contacts_list extends Hm_Output_Module {
protected function output() {
+ $imported_contact = $this->get('imported_contact', array());
if (count($this->get('contact_sources', array())) == 0) {
return '
'.$this->trans('No contact backends are enabled!').
' '.$this->trans('At least one backend must be enabled in the config/app.php file to use contacts.').'
';
@@ -160,6 +210,13 @@ protected function output() {
$per_page = 25;
$current_page = $this->get('contact_page', 1);
$res = '
';
+ $res .= ''.$modal.'
';
return $res;
}
}
@@ -334,3 +391,79 @@ function name_map($val) {
}
return $val;
}}
+
+
+/**
+ * @subpackage contacts/functions
+ */
+if (!hm_exists('get_import_detail_modal_content')) {
+function get_import_detail_modal_content($output_mod, $imported_contacts) {
+ $per_page = 10;
+ $page = 1;
+ $total_contacts = count($imported_contacts);
+ $total_pages = ceil($total_contacts / $per_page);
+ $res = '
+
+
+ #
+ Display Name
+ E-mail Address
+ Telephone Number
+ Status
+
+
+ ';
+
+ for ($i = 0; $i < $total_contacts; $i++) {
+ $contact = $imported_contacts[$i];
+ $status = $contact['status'] == "invalid email" ? "danger" : "success";
+ $res .= '
+ '.($i + 1).'
+ '.$output_mod->html_safe($contact['display_name']).'
+ '.$output_mod->html_safe($contact['email_address']).'
+ '.$output_mod->html_safe($contact['phone_number']).'
+ '.$output_mod->html_safe($contact['status']).'
+ ';
+ }
+
+ $res .= '
';
+
+ if ($total_pages > 1) {
+ $res .= '
+
+ ';
+ }
+
+ $res .= '
';
+
+ return '
';
+}}
diff --git a/modules/contacts/setup.php b/modules/contacts/setup.php
index 2410c7dd0d..48e84b8d5d 100644
--- a/modules/contacts/setup.php
+++ b/modules/contacts/setup.php
@@ -10,6 +10,7 @@
setup_base_page('contacts', 'core');
add_handler('contacts', 'load_contacts', true, 'contacts', 'load_user_data', 'after');
+add_handler('contacts', 'check_imported_contacts', true, 'contacts', 'load_user_data', 'after');
add_output('contacts', 'contacts_content_start', true, 'contacts', 'content_section_start', 'after');
add_output('contacts', 'contacts_list', true, 'contacts', 'contacts_content_start', 'after');
add_output('contacts', 'contacts_content_end', true, 'contacts', 'contacts_list', 'after');
@@ -37,11 +38,16 @@
add_handler('ajax_delete_contact', 'load_contacts', true, 'contacts', 'load_user_data', 'after');
add_handler('ajax_delete_contact', 'save_user_data', true, 'core', 'language', 'after');
+setup_base_page('export_contact', 'core');
+add_handler('export_contact', 'load_contacts', true, 'contacts', 'load_user_data', 'after');
+add_handler('export_contact', 'process_export_contacts', true, 'contacts', 'load_contacts', 'after');
+
return array(
'allowed_pages' => array(
'contacts',
'ajax_add_contact',
'ajax_delete_contact',
+ 'export_contact',
'ajax_autocomplete_contact'
),
'allowed_post' => array(
@@ -53,16 +59,19 @@
'edit_contact' => FILTER_DEFAULT,
'add_contact' => FILTER_DEFAULT,
'contact_source' => FILTER_DEFAULT,
- 'contact_type' => FILTER_DEFAULT
+ 'contact_type' => FILTER_DEFAULT,
+ 'import_contact' => FILTER_DEFAULT,
),
'allowed_get' => array(
'contact_id' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
'contact_page' => FILTER_VALIDATE_INT,
'contact_type' => FILTER_DEFAULT,
'contact_source' => FILTER_DEFAULT,
+ 'import_contact' => FILTER_DEFAULT,
),
'allowed_output' => array(
'contact_deleted' => array(FILTER_VALIDATE_INT, false),
+ 'imported_contact' => array(FILTER_DEFAULT, FILTER_REQUIRE_ARRAY),
'contact_suggestions' => array(FILTER_DEFAULT, FILTER_REQUIRE_ARRAY)
),
);
diff --git a/modules/contacts/site.css b/modules/contacts/site.css
index 6d60236732..32e77aa8bb 100644
--- a/modules/contacts/site.css
+++ b/modules/contacts/site.css
@@ -11,6 +11,12 @@
.show_contact { margin-right: 15px; }
.contact_detail th { font-weight: normal; text-align: left; padding-right: 20px; }
.contact_fld { max-width: 300px; overflow-x: hidden; text-overflow: ellipsis; }
+#contact_csv { width: 80%; }
+.list_actions { z-index: 100; border-left: solid 1px #ede8e6; border-bottom: solid 1px #ede8e6; position: absolute; right: 0px; top: 54px; background-color: #fafafa; font-size: 85%; padding: 30px; padding-top: 10px; display: none;}
+.src_title { color: #666; font-size: 110%; padding: 5px; margin-bottom: 10px; }
+.contact_import_detail td {
+ border-bottom: none !important;
+}
.mobile .contact_list { margin-left: 0px; font-size: 125%; width: 100%; }
.mobile .contact_controls img { width: 20px; height: 20px; }
diff --git a/modules/contacts/site.js b/modules/contacts/site.js
index 61e89f6444..b9a12b7976 100644
--- a/modules/contacts/site.js
+++ b/modules/contacts/site.js
@@ -207,6 +207,40 @@ var add_autocomplete = function(event, class_name, list_div, fld_val) {
return false;
};
+var showPage = function(selected_page, total_pages) {
+ $('.import_body tr').hide();
+ $('.page_' + selected_page).show();
+ $('.page_link_selector').removeClass('active');
+ $('.page_item_' + selected_page).addClass('active');
+ $('.prev_page').toggleClass('disabled', selected_page === 1);
+ $('.next_page').toggleClass('disabled', selected_page === total_pages);
+};
+
+var contact_import_pagination = function() {
+ var selected_page = 1;
+ var total_pages = $('#totalPages').val();
+ showPage(selected_page, total_pages);
+
+ $('.page_link_selector').on('click', function () {
+ selected_page = $(this).data('page');
+ showPage(selected_page, total_pages);
+ });
+
+ $('.prev_page').on('click', function () {
+ if (selected_page > 1) {
+ selected_page--;
+ showPage(selected_page, total_pages);
+ }
+ });
+
+ $('.next_page').on('click', function () {
+ if (selected_page < total_pages) {
+ selected_page++;
+ showPage(selected_page, total_pages);
+ }
+ });
+};
+
if (hm_page_name() == 'contacts') {
$('.delete_contact').on("click", function() {
delete_contact($(this).data('id'), $(this).data('source'), $(this).data('type'));
@@ -234,6 +268,11 @@ if (hm_page_name() == 'contacts') {
}
});
+ $('.source_link').on("click", function () {
+ $('.list_actions').toggle(); $('#list_controls_menu').hide();
+ return false;
+ });
+ contact_import_pagination();
}
else if (hm_page_name() == 'compose') {
$('.compose_to').on('keyup', function(e) { autocomplete_contact(e, '.compose_to', '#to_contacts'); });
diff --git a/modules/local_contacts/assets/data/contact_sample.csv b/modules/local_contacts/assets/data/contact_sample.csv
new file mode 100644
index 0000000000..83b3e012d5
--- /dev/null
+++ b/modules/local_contacts/assets/data/contact_sample.csv
@@ -0,0 +1,2 @@
+display_name,email_address,phone_number
+Thomas Tester,test@example.org,1234567890
\ No newline at end of file
diff --git a/modules/local_contacts/modules.php b/modules/local_contacts/modules.php
index e7761062d4..e83331b6ec 100644
--- a/modules/local_contacts/modules.php
+++ b/modules/local_contacts/modules.php
@@ -65,6 +65,90 @@ public function process() {
}
}
+/**
+ * @subpackage local_contacts/handler
+ */
+class Hm_Handler_process_import_contact extends Hm_Handler_Module {
+ public function process() {
+ list($success, $form) = $this->process_form(array('contact_source', 'import_contact'));
+ if ($success && $form['contact_source'] == 'csv') {
+ $file = $this->request->files['contact_csv'];
+ $csv = fopen($file['tmp_name'], 'r');
+ if ($csv) {
+ $contacts = $this->get('contact_store');
+ $header = fgetcsv($csv);
+ $expectedHeader = array('display_name', 'email_address', 'phone_number');
+
+ if ($header !== $expectedHeader) {
+ fclose($csv);
+ Hm_Msgs::add('ERRInvalid CSV file, please use a valid header: '.implode(', ', $expectedHeader));
+ return;
+ }
+
+ $contact_list = $contacts->getAll();
+ $message = '';
+ $update_count = 0;
+ $create_count = 0;
+ $invalid_mail_count = 0;
+ $import_result = [];
+
+
+ while (($data = fgetcsv($csv)) !== FALSE) {
+ $single_contact = [
+ 'display_name' => $data[0],
+ 'email_address' => $data[1],
+ 'phone_number' => $data[2] ?? ''
+ ];
+ $email = $data[1];
+ if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
+ $single_contact['status'] = 'invalid email';
+ array_push($import_result, $single_contact);
+ $invalid_mail_count++;
+ continue;
+ }
+
+ $details = array('source' => 'local', 'display_name' => $data[0], 'email_address' => $email);
+ if (array_key_exists(2, $data) && $data[2]) {
+ $details['phone_number'] = $data[2];
+ }
+
+ $contactUpdated = false;
+ foreach ($contact_list as $key => $contact) {
+ if ($contact->value('email_address') == $email) {
+ $contacts->update_contact($key, $details);
+ $single_contact['status'] = 'update';
+ array_push($import_result, $single_contact);
+ $update_count++;
+ $contactUpdated = true;
+ continue 2;
+ }
+ }
+
+ if (!$contactUpdated) {
+ $contacts->add_contact($details);
+ $single_contact['status'] = 'new';
+ array_push($import_result, $single_contact);
+ $create_count++;
+ }
+ }
+ fclose($csv);
+ $contacts->save();
+ $this->session->record_unsaved('Contact Created');
+ if (isset($import_result) && (!$create_count && !$update_count)) {
+ $message = 'ERR'.$create_count.' contacts created, '.$update_count.' contacts updated, '.$invalid_mail_count.' Invalid email address';
+ } elseif (isset($import_result) && ($create_count || $update_count)) {
+ $message = $create_count.' contacts created, '.$update_count.' contacts updated, '.$invalid_mail_count.' Invalid email address';
+ } else {
+ $message = 'ERRAn error occured';
+ }
+
+ $this->session->set('imported_contact', $import_result);
+ Hm_Msgs::add($message);
+ }
+ }
+ }
+}
+
/**
* @subpackage local_contacts/handler
*/
@@ -159,3 +243,26 @@ protected function output() {
$this->trans('Cancel').'" />
';
}
}
+
+/**
+ * @subpackage import_local_contacts/output
+ */
+class Hm_Output_import_contacts_form extends Hm_Output_Module {
+ protected function output() {
+ $form_class = 'contact_form';
+ $button = '