You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

automatic_addressbook.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Automatic address book
  5. *
  6. *
  7. * Simple plugin to register to "collect" all recipients of sent mail
  8. * to a dedicated address book (usefull for autocompleting email you
  9. * already used). User can choose in preferences (compose group) to
  10. * enable or disable the feature of this plugin.
  11. * Aims to reproduce the similar features of thunderbird or gmail.
  12. *
  13. * @version 0.4
  14. * @author Jocelyn Delalande (slightly modified by Roland 'rosali' Liebl)
  15. * @author Sebastien Blaisot <sebastien@blaisot.org>
  16. * @website https://github.com/sblaisot/automatic_addressbook
  17. * @licence http://www.gnu.org/licenses/gpl-3.0.html GNU GPLv3+
  18. *
  19. */
  20. /*
  21. * Skeletton based on "example_addressbook" plugin.
  22. * Contact adding code inspired by addcontact.inc by Thomas Bruederli
  23. */
  24. class automatic_addressbook extends rcube_plugin
  25. {
  26. public $task = 'mail|addressbook|settings';
  27. private $abook_id = 'collected';
  28. /**
  29. * Initialize plugin
  30. */
  31. public function init()
  32. {
  33. $this->add_hook('addressbooks_list', array($this, 'address_sources'));
  34. $this->add_hook('addressbook_get', array($this, 'get_address_book'));
  35. $this->add_hook('message_sent', array($this, 'register_recipients'));
  36. $this->add_hook('preferences_list', array($this, 'settings_table'));
  37. $this->add_hook('preferences_save', array($this, 'save_prefs'));
  38. $this->add_hook('contact_update', array($this, 'handle_doubles'));
  39. $this->add_hook('contact_create', array($this, 'handle_doubles'));
  40. $this->add_texts('localization/', false);
  41. $this->load_config('config/config.inc.php.dist');
  42. if (file_exists("./plugins/automatic_addressbook/config/config.inc.php")) {
  43. $this->load_config('config/config.inc.php');
  44. }
  45. // Adds an address-book category in rc <= 0.5, retro-compatibility code,
  46. // not needed for rc 0.6
  47. $this->add_hook('preferences_sections_list', array($this, 'add_addressbook_category'));
  48. // use this address book for autocompletion queries
  49. $config = rcmail::get_instance()->config;
  50. $sources = $config->get('autocomplete_addressbooks', array('sql'));
  51. if (!in_array($this->abook_id, $sources) &&
  52. $config->get('use_auto_abook', true) &&
  53. $config->get('use_auto_abook_for_completion', true)) {
  54. $sources[] = $this->abook_id;
  55. $config->set('autocomplete_addressbooks', $sources);
  56. }
  57. }
  58. /**
  59. * Register automatic_addressbook as address source
  60. *
  61. * @param array $p Hash array with list of available address books
  62. * @return array $p Hash array with list of available address books
  63. */
  64. public function address_sources($p)
  65. {
  66. $rcmail = rcmail::get_instance();
  67. if ($rcmail->config->get('use_auto_abook', true)) {
  68. $p['sources'][$this->abook_id] =
  69. array('id' => $this->abook_id,
  70. 'name' => rcube_utils::rep_specialchars_output($this->gettext('automaticallycollected')),
  71. 'readonly' => FALSE, 'groups' => false);
  72. }
  73. return $p;
  74. }
  75. /**
  76. * Requests automatic_addressbook instance
  77. *
  78. * @param array $p Hash array containing the id of the requested abook
  79. * @return array $p Hash array containing instance of the requested abook
  80. */
  81. public function get_address_book($p)
  82. {
  83. $rcmail = rcmail::get_instance();
  84. if (($p['id'] === $this->abook_id) && $rcmail->config->get('use_auto_abook', true)) {
  85. require_once dirname(__FILE__) . '/automatic_addressbook_backend.php';
  86. $p['instance'] = new automatic_addressbook_backend($rcmail->db, $rcmail->user->ID);
  87. $p['instance']->groups = false;
  88. }
  89. return $p;
  90. }
  91. /**
  92. * Collect the email address of a just-sent email recipients into
  93. * the automatic addressbook (if it's not already in another
  94. * addressbook).
  95. *
  96. * @param array $p Hash array containing header and body of sent mail
  97. * @return nothing
  98. */
  99. public function register_recipients($p)
  100. {
  101. $rcmail = rcmail::get_instance();
  102. if (!$rcmail->config->get('use_auto_abook', true)) {
  103. return;
  104. }
  105. $headers = $p['headers'];
  106. if (!class_exists('rcube_mime')) { // RC < 0.8 compatibility code
  107. $IMAP = new rcube_imap(null);
  108. $all_recipients = array_merge(
  109. $IMAP->decode_address_list($headers['To'], null, true, $headers['charset']),
  110. $IMAP->decode_address_list($headers['Cc'], null, true, $headers['charset']),
  111. $IMAP->decode_address_list($headers['Bcc'], null, true, $headers['charset'])
  112. );
  113. } else {
  114. $all_recipients = array_merge(
  115. rcube_mime::decode_address_list($headers['To'], null, true, $headers['charset']),
  116. rcube_mime::decode_address_list($headers['Cc'], null, true, $headers['charset']),
  117. rcube_mime::decode_address_list($headers['Bcc'], null, true, $headers['charset'])
  118. );
  119. }
  120. require_once dirname(__FILE__) . '/automatic_addressbook_backend.php';
  121. $CONTACTS = new automatic_addressbook_backend($rcmail->db, $rcmail->user->ID);
  122. foreach ($all_recipients as $recipient) {
  123. // Bcc and Cc can be empty
  124. if ($recipient['mailto'] != '') {
  125. $contact = array(
  126. 'email' => $recipient['mailto'],
  127. 'name' => $recipient['name']
  128. );
  129. // use email address part for name
  130. if (empty($contact['name']) || $contact['name'] == $contact['email']) {
  131. $contact['name'] = ucfirst(preg_replace('/[\.\-]/', ' ',
  132. substr($contact['email'], 0, strpos($contact['email'], '@'))));
  133. }
  134. /* We only want to add the contact to the collected contacts
  135. * address book if it is not already in an addressbook, so we
  136. * first lookup in every address source.
  137. */
  138. $book_types = (array)$rcmail->config->get('autocomplete_addressbooks', 'sql');
  139. foreach ($book_types as $id) {
  140. $abook = $rcmail->get_address_book($id);
  141. $previous_entries = $abook->search('email', $contact['email'], false, false);
  142. if ($previous_entries->count) {
  143. break;
  144. }
  145. }
  146. if (!$previous_entries->count) {
  147. $plugin = $rcmail->plugins->exec_hook('contact_create', array('record' => $contact,
  148. 'source' => $this->abook_id));
  149. if (!$plugin['abort']) {
  150. $CONTACTS->insert($contact, false);
  151. }
  152. }
  153. }
  154. }
  155. }
  156. /**
  157. * Adds an address-book settings category in rc <= 0.5, not needed for rc >= 0.6
  158. *
  159. * @param array $args Hash array with list (hash array) of sections and list (hash array) of sections
  160. * @return array Hash array with list (hash array) of sections and list (hash array) of sections
  161. */
  162. public function add_addressbook_category($args)
  163. {
  164. $temp = $args['list']['server'];
  165. unset($args['list']['server']);
  166. $args['list']['addressbook']['id'] = 'addressbook';
  167. $args['list']['addressbook']['section'] = $this->gettext('addressbook');
  168. $args['list']['server'] = $temp;
  169. return $args;
  170. }
  171. /**
  172. * Adds a check-box to enable/disable automatic address collection.
  173. *
  174. * @param array $args Hash array containing section and preference blocks
  175. * @return array Hash array containing preference blocks with addressbook preferences
  176. */
  177. public function settings_table($args)
  178. {
  179. if ($args['section'] == 'addressbook') {
  180. $use_auto_abook = rcmail::get_instance()->config->get('use_auto_abook', true);
  181. $field_id = 'rcmfd_use_auto_abook';
  182. $checkbox = new html_checkbox(array(
  183. 'name' => '_use_auto_abook',
  184. 'id' => $field_id, 'value' => 1
  185. ));
  186. $args['blocks']['automaticallycollected']['name'] = $this->gettext('automaticallycollected');
  187. $args['blocks']['automaticallycollected']['options']['use_subscriptions'] = array(
  188. 'title' => html::label($field_id, rcube_utils::rep_specialchars_output($this->gettext('useautoabook'))),
  189. 'content' => $checkbox->show($use_auto_abook ? 1 : 0),
  190. );
  191. $use_auto_abook_for_completion = rcmail::get_instance()->config->get('use_auto_abook_for_completion', true);
  192. $field_id2 = 'rcmfd_use_auto_abook_for_completion';
  193. $checkbox2 = new html_checkbox(array(
  194. 'name' => '_use_auto_abook_for_completion',
  195. 'id' => $field_id2, 'value' => 1
  196. ));
  197. $args['blocks']['automaticallycollected']['name'] = $this->gettext('automaticallycollected');
  198. $args['blocks']['automaticallycollected']['options']['use_autocompletion'] = array(
  199. 'title' => html::label($field_id2, rcube_utils::rep_specialchars_output($this->gettext('useforcompletion'))),
  200. 'content' => $checkbox2->show($use_auto_abook_for_completion ? 1 : 0),
  201. );
  202. }
  203. return $args;
  204. }
  205. /**
  206. * Save preferences
  207. *
  208. * @param array $args Hash array with prefs to be saved
  209. * @return array $args Hash array with result: boolean, abort: boolean, prefs: array
  210. */
  211. public function save_prefs($args)
  212. {
  213. if ($args['section'] == 'addressbook') {
  214. $rcmail = rcmail::get_instance();
  215. $use_auto_abook = $rcmail->config->get('use_auto_abook');
  216. $args['prefs']['use_auto_abook'] = isset($_POST['_use_auto_abook']) ? true : false;
  217. $use_auto_abook_for_completion = $rcmail->config->get('use_auto_abook_for_completion');
  218. $args['prefs']['use_auto_abook_for_completion'] = isset($_POST['_use_auto_abook_for_completion']) ? true : false;
  219. }
  220. return $args;
  221. }
  222. /**
  223. * When a contact is added to a "regular" addressbook, take care to
  224. * delete it from collected addressbook if it was in.
  225. *
  226. * @param array $args Hash array with contact details
  227. * @return array record updated or abort
  228. */
  229. public function handle_doubles($args)
  230. {
  231. $rcmail = rcmail::get_instance();
  232. if (!$rcmail->config->get('use_auto_abook', true)) {
  233. return $args;
  234. }
  235. $moveto = $rcmail->config->get('on_edit_move_to_default');
  236. if ($args['source'] == $this->abook_id && !empty($args['id']) && $moveto) {
  237. $args['source'] = $rcmail->config->get('default_addressbook');
  238. $plugin = $rcmail->plugins->exec_hook('contact_create', array('record' => $args['record'],
  239. 'source' => $args['source']));
  240. if (!$plugin['abort']) {
  241. $CONTACTS = $rcmail->get_address_book($args['source']);
  242. $insert_id = $CONTACTS->insert($args['record'], false);
  243. } else {
  244. $insert_id = $plugin['result'];
  245. }
  246. $rcmail->output->show_message('automatic_addressbook.contactmoved', 'confirmation');
  247. $rcmail->output->command('parent.list_contacts');
  248. return array('abort' => true, 'result' => $insert_id);
  249. }
  250. if ($args['source'] !== $this->abook_id) {
  251. foreach (array('email:home', 'email:work', 'email:other') as $email_field) {
  252. // Would trigger a warning with rc 0.5 without this if
  253. if ($args['record'][$email_field]) {
  254. foreach ($args['record'][$email_field] as $email) {
  255. $contact_emails[] = $email;
  256. }
  257. }
  258. }
  259. // rc <= 0.5, retro-compatibility code, not needed for rc 0.6
  260. $contact_emails[] = $args['record']['email'];
  261. //
  262. foreach ($contact_emails as $contact_email) {
  263. if (!empty($contact_email)) {
  264. $auto_abook = $rcmail->get_address_book($this->abook_id);
  265. $auto_abook->reset();
  266. $collected_contact = $auto_abook->search('email', $contact_email, false, true);
  267. while ($record = $collected_contact->iterate()) {
  268. $plugin = $rcmail->plugins->exec_hook('contact_delete', array('id' => $record['contact_id'],
  269. 'source' => $this->abook_id));
  270. if (!$plugin['abort']) {
  271. $auto_abook->delete($record['contact_id']);
  272. $rcmail->output->show_message('automatic_addressbook.contactremoved', 'confirmation');
  273. }
  274. }
  275. }
  276. }
  277. if ($rcmail->task == "addressbook" &&
  278. $rcmail->action == "copy" &&
  279. $_REQUEST['_source'] == $this->abook_id) {
  280. $rcmail->output->command('parent.list_contacts');
  281. }
  282. }
  283. return $args;
  284. }
  285. }