- Merge devel-saved_search branch (Addressbook Saved Searches)
| | |
| | | CHANGELOG Roundcube Webmail |
| | | =========================== |
| | | |
| | | - Addressbook Saved Searches |
| | | - Added spellchecker exceptions dictionary (shared or per-user) |
| | | - Added possibility to ignore words containing caps, numbers, symbols (spellcheck_ignore_* options) |
| | | |
| | |
| | | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | CREATE TABLE [dbo].[searches] (
|
| | | [search_id] [int] IDENTITY (1, 1) NOT NULL ,
|
| | | [user_id] [int] NOT NULL ,
|
| | | [type] [tinyint] NOT NULL ,
|
| | | [name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [data] [text] COLLATE Latin1_General_CI_AI NOT NULL |
| | | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[cache] WITH NOCHECK ADD
|
| | | PRIMARY KEY CLUSTERED
|
| | | (
|
| | |
| | | (
|
| | | [user_id]
|
| | | ) ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[searches] WITH NOCHECK ADD |
| | | CONSTRAINT [PK_searches_search_id] PRIMARY KEY CLUSTERED |
| | | (
|
| | | [search_id]
|
| | | ) ON [PRIMARY] |
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[cache] ADD
|
| | |
| | | CREATE UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[searches] ADD |
| | | CONSTRAINT [DF_searches_user] DEFAULT (0) FOR [user_id],
|
| | | CONSTRAINT [DF_searches_type] DEFAULT (0) FOR [type],
|
| | | GO
|
| | |
|
| | | CREATE UNIQUE INDEX [IX_searches_user_type_name] ON [dbo].[searches]([user_id],[type],[name]) ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[identities] ADD CONSTRAINT [FK_identities_user_id]
|
| | | FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
|
| | | ON DELETE CASCADE ON UPDATE CASCADE
|
| | |
| | | ON DELETE CASCADE ON UPDATE CASCADE
|
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[searches] ADD CONSTRAINT [FK_searches_user_id]
|
| | | FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
|
| | | ON DELETE CASCADE ON UPDATE CASCADE
|
| | | GO
|
| | |
|
| | | -- Use trigger instead of foreign key (#1487112)
|
| | | -- "Introducing FOREIGN KEY constraint ... may cause cycles or multiple cascade paths."
|
| | | CREATE TRIGGER [contact_delete_member] ON [dbo].[contacts]
|
| | |
| | | GO
|
| | | CREATE UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | CREATE TABLE [dbo].[searches] (
|
| | | [search_id] [int] IDENTITY (1, 1) NOT NULL ,
|
| | | [user_id] [int] NOT NULL ,
|
| | | [type] [tinyint] NOT NULL ,
|
| | | [name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [data] [text] COLLATE Latin1_General_CI_AI NOT NULL |
| | | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[searches] WITH NOCHECK ADD |
| | | CONSTRAINT [PK_searches_search_id] PRIMARY KEY CLUSTERED |
| | | (
|
| | | [search_id]
|
| | | ) ON [PRIMARY] |
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[searches] ADD |
| | | CONSTRAINT [DF_searches_user] DEFAULT (0) FOR [user_id],
|
| | | CONSTRAINT [DF_searches_type] DEFAULT (0) FOR [type],
|
| | | GO
|
| | |
|
| | | CREATE UNIQUE INDEX [IX_searches_user_type_name] ON [dbo].[searches]([user_id],[type],[name]) ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[searches] ADD CONSTRAINT [FK_searches_user_id]
|
| | | FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
|
| | | ON DELETE CASCADE ON UPDATE CASCADE
|
| | | GO
|
| | |
|
| | |
| | | UNIQUE `uniqueness` (`user_id`, `language`) |
| | | ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; |
| | | |
| | | |
| | | -- Table structure for table `searches` |
| | | |
| | | CREATE TABLE `searches` ( |
| | | `search_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, |
| | | `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', |
| | | `type` int(3) NOT NULL DEFAULT '0', |
| | | `name` varchar(128) NOT NULL, |
| | | `data` text, |
| | | PRIMARY KEY(`search_id`), |
| | | CONSTRAINT `user_id_fk_searches` FOREIGN KEY (`user_id`) |
| | | REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | UNIQUE `uniqueness` (`user_id`, `type`, `name`) |
| | | ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; |
| | | |
| | | |
| | | /*!40014 SET FOREIGN_KEY_CHECKS=1 */; |
| | |
| | | REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | UNIQUE `uniqueness` (`user_id`, `language`) |
| | | ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; |
| | | |
| | | CREATE TABLE `searches` ( |
| | | `search_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, |
| | | `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', |
| | | `type` int(3) NOT NULL DEFAULT '0', |
| | | `name` varchar(128) NOT NULL, |
| | | `data` text, |
| | | PRIMARY KEY(`search_id`), |
| | | CONSTRAINT `user_id_fk_searches` FOREIGN KEY (`user_id`) |
| | | REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | UNIQUE `uniqueness` (`user_id`, `type`, `name`) |
| | | ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; |
| | |
| | | data text NOT NULL, |
| | | CONSTRAINT dictionary_user_id_language_key UNIQUE (user_id, "language") |
| | | ); |
| | | |
| | | -- |
| | | -- Sequence "searches_ids" |
| | | -- Name: searches_ids; Type: SEQUENCE; Schema: public; Owner: postgres |
| | | -- |
| | | |
| | | CREATE SEQUENCE search_ids |
| | | INCREMENT BY 1 |
| | | NO MAXVALUE |
| | | NO MINVALUE |
| | | CACHE 1; |
| | | |
| | | -- |
| | | -- Table "searches" |
| | | -- Name: searches; Type: TABLE; Schema: public; Owner: postgres |
| | | -- |
| | | |
| | | CREATE TABLE searches ( |
| | | search_id integer DEFAULT nextval('search_ids'::text) PRIMARY KEY, |
| | | user_id integer NOT NULL |
| | | REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | "type" smallint DEFAULT 0 NOT NULL, |
| | | name varchar(128) NOT NULL, |
| | | data text NOT NULL, |
| | | CONSTRAINT searches_user_id_key UNIQUE (user_id, "type", name) |
| | | ); |
| | |
| | | data text NOT NULL, |
| | | CONSTRAINT dictionary_user_id_language_key UNIQUE (user_id, "language") |
| | | ); |
| | | |
| | | CREATE SEQUENCE search_ids |
| | | INCREMENT BY 1 |
| | | NO MAXVALUE |
| | | NO MINVALUE |
| | | CACHE 1; |
| | | |
| | | CREATE TABLE searches ( |
| | | search_id integer DEFAULT nextval('search_ids'::text) PRIMARY KEY, |
| | | user_id integer NOT NULL |
| | | REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | "type" smallint DEFAULT 0 NOT NULL, |
| | | name varchar(128) NOT NULL, |
| | | data text NOT NULL, |
| | | CONSTRAINT searches_user_id_key UNIQUE (user_id, "type", name) |
| | | ); |
| | |
| | | |
| | | CREATE UNIQUE INDEX ix_dictionary_user_language ON dictionary (user_id, "language"); |
| | | |
| | | -- -------------------------------------------------------- |
| | | |
| | | -- |
| | | -- Table structure for table searches |
| | | -- |
| | | |
| | | CREATE TABLE searches ( |
| | | search_id integer NOT NULL PRIMARY KEY, |
| | | user_id integer NOT NULL DEFAULT '0', |
| | | "type" smallint NOT NULL DEFAULT '0', |
| | | name varchar(128) NOT NULL, |
| | | data text NOT NULL |
| | | ); |
| | | |
| | | CREATE UNIQUE INDEX ix_searches_user_type_name (user_id, type, name); |
| | |
| | | DELETE FROM cache; |
| | | CREATE INDEX ix_contactgroupmembers_contact_id ON contactgroupmembers (contact_id); |
| | | |
| | | |
| | | -- Updates from version 0.6-stable |
| | | |
| | | CREATE TABLE dictionary ( |
| | |
| | | ); |
| | | |
| | | CREATE UNIQUE INDEX ix_dictionary_user_language ON dictionary (user_id, "language"); |
| | | |
| | | CREATE TABLE searches ( |
| | | search_id integer NOT NULL PRIMARY KEY, |
| | | user_id integer NOT NULL DEFAULT '0', |
| | | "type" smallint NOT NULL DEFAULT '0', |
| | | name varchar(128) NOT NULL, |
| | | data text NOT NULL |
| | | ); |
| | | |
| | | CREATE UNIQUE INDEX ix_searches_user_type_name (user_id, type, name); |
| | |
| | | |
| | | $rcmail_config['db_sequence_messages'] = 'message_ids'; |
| | | |
| | | $rcmail_config['db_sequence_searches'] = 'search_ids'; |
| | | |
| | | |
| | | // end db config file |
| | | |
| | |
| | | */ |
| | | private $rc; |
| | | |
| | | const SEARCH_ADDRESSBOOK = 1; |
| | | const SEARCH_MAIL = 2; |
| | | |
| | | /** |
| | | * Object constructor |
| | |
| | | return empty($plugin['email']) ? NULL : $plugin['email']; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return a list of saved searches linked with this user |
| | | * |
| | | * @param int $type Search type |
| | | * |
| | | * @return array List of saved searches indexed by search ID |
| | | */ |
| | | function list_searches($type) |
| | | { |
| | | $plugin = $this->rc->plugins->exec_hook('saved_search_list', array('type' => $type)); |
| | | |
| | | if ($plugin['abort']) { |
| | | return (array) $plugin['result']; |
| | | } |
| | | |
| | | $result = array(); |
| | | |
| | | $sql_result = $this->db->query( |
| | | "SELECT search_id AS id, ".$this->db->quoteIdentifier('name') |
| | | ." FROM ".get_table_name('searches') |
| | | ." WHERE user_id = ?" |
| | | ." AND ".$this->db->quoteIdentifier('type')." = ?" |
| | | ." ORDER BY ".$this->db->quoteIdentifier('name'), |
| | | (int) $this->ID, (int) $type); |
| | | |
| | | while ($sql_arr = $this->db->fetch_assoc($sql_result)) { |
| | | $sql_arr['data'] = unserialize($sql_arr['data']); |
| | | $result[$sql_arr['id']] = $sql_arr; |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return saved search data. |
| | | * |
| | | * @param int $id Row identifier |
| | | * |
| | | * @return array Data |
| | | */ |
| | | function get_search($id) |
| | | { |
| | | $plugin = $this->rc->plugins->exec_hook('saved_search_get', array('id' => $id)); |
| | | |
| | | if ($plugin['abort']) { |
| | | return $plugin['result']; |
| | | } |
| | | |
| | | $sql_result = $this->db->query( |
| | | "SELECT ".$this->db->quoteIdentifier('name') |
| | | .", ".$this->db->quoteIdentifier('data') |
| | | .", ".$this->db->quoteIdentifier('type') |
| | | ." FROM ".get_table_name('searches') |
| | | ." WHERE user_id = ?" |
| | | ." AND search_id = ?", |
| | | (int) $this->ID, (int) $id); |
| | | |
| | | while ($sql_arr = $this->db->fetch_assoc($sql_result)) { |
| | | return array( |
| | | 'id' => $id, |
| | | 'name' => $sql_arr['name'], |
| | | 'type' => $sql_arr['type'], |
| | | 'data' => unserialize($sql_arr['data']), |
| | | ); |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Deletes given saved search record |
| | | * |
| | | * @param int $sid Search ID |
| | | * |
| | | * @return boolean True if deleted successfully, false if nothing changed |
| | | */ |
| | | function delete_search($sid) |
| | | { |
| | | if (!$this->ID) |
| | | return false; |
| | | |
| | | $this->db->query( |
| | | "DELETE FROM ".get_table_name('searches') |
| | | ." WHERE user_id = ?" |
| | | ." AND search_id = ?", |
| | | (int) $this->ID, $sid); |
| | | |
| | | return $this->db->affected_rows(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a new saved search record linked with this user |
| | | * |
| | | * @param array $data Hash array with col->value pairs to save |
| | | * |
| | | * @return int The inserted search ID or false on error |
| | | */ |
| | | function insert_search($data) |
| | | { |
| | | if (!$this->ID) |
| | | return false; |
| | | |
| | | $insert_cols[] = 'user_id'; |
| | | $insert_values[] = (int) $this->ID; |
| | | $insert_cols[] = $this->db->quoteIdentifier('type'); |
| | | $insert_values[] = (int) $data['type']; |
| | | $insert_cols[] = $this->db->quoteIdentifier('name'); |
| | | $insert_values[] = $data['name']; |
| | | $insert_cols[] = $this->db->quoteIdentifier('data'); |
| | | $insert_values[] = serialize($data['data']); |
| | | |
| | | $sql = "INSERT INTO ".get_table_name('searches') |
| | | ." (".join(', ', $insert_cols).")" |
| | | ." VALUES (".join(', ', array_pad(array(), sizeof($insert_values), '?')).")"; |
| | | |
| | | call_user_func_array(array($this->db, 'query'), |
| | | array_merge(array($sql), $insert_values)); |
| | | |
| | | return $this->db->insert_id('searches'); |
| | | } |
| | | |
| | | } |
| | |
| | | this.enable_command('export', true); |
| | | |
| | | this.enable_command('add', 'import', this.env.writable_source); |
| | | this.enable_command('list', 'listgroup', 'advanced-search', true); |
| | | this.enable_command('list', 'listgroup', 'listsearch', 'advanced-search', true); |
| | | |
| | | // load contacts of selected source |
| | | if (!this.env.action) |
| | |
| | | break; |
| | | |
| | | case 'list': |
| | | if (this.task=='mail') { |
| | | if (!this.env.search_request || (props && props != this.env.mailbox)) |
| | | this.reset_qsearch(); |
| | | |
| | | this.reset_qsearch(); |
| | | if (this.task == 'mail') { |
| | | this.list_mailbox(props); |
| | | |
| | | if (this.env.trash_mailbox && !this.env.flag_for_deletion) |
| | | this.set_alttext('delete', this.env.mailbox != this.env.trash_mailbox ? 'movemessagetotrash' : 'deletemessage'); |
| | | } |
| | | else if (this.task == 'addressbook') { |
| | | if (!this.env.search_request || (props != this.env.source)) |
| | | this.reset_qsearch(); |
| | | |
| | | this.list_contacts(props); |
| | | this.enable_command('add', 'import', this.env.writable_source); |
| | | } |
| | | break; |
| | | |
| | |
| | | break; |
| | | |
| | | case 'listgroup': |
| | | this.reset_qsearch(); |
| | | this.list_contacts(props.source, props.id); |
| | | break; |
| | | |
| | |
| | | if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort)) |
| | | url += '&_refresh=1'; |
| | | |
| | | this.select_folder(mbox, this.env.mailbox); |
| | | this.select_folder(mbox); |
| | | this.env.mailbox = mbox; |
| | | |
| | | // load message list remotely |
| | |
| | | |
| | | this.env.qsearch = null; |
| | | this.env.search_request = null; |
| | | this.env.search_id = null; |
| | | }; |
| | | |
| | | this.sent_successfully = function(type, msg) |
| | |
| | | |
| | | this.list_contacts = function(src, group, page) |
| | | { |
| | | var add_url = '', |
| | | var folder, add_url = '', |
| | | target = window; |
| | | |
| | | if (!src) |
| | |
| | | else if (group != this.env.group) |
| | | page = this.env.current_page = 1; |
| | | |
| | | this.select_folder((group ? 'G'+src+group : src), (this.env.group ? 'G'+this.env.source+this.env.group : this.env.source)); |
| | | if (this.env.search_id) |
| | | folder = 'S'+this.env.search_id; |
| | | else |
| | | folder = group ? 'G'+src+group : src; |
| | | |
| | | this.select_folder(folder); |
| | | |
| | | this.env.source = src; |
| | | this.env.group = group; |
| | |
| | | if (group) |
| | | url += '&_gid='+group; |
| | | |
| | | // also send search request to get the right messages |
| | | if (this.env.search_request) |
| | | // also send search request to get the right messages |
| | | if (this.env.search_request) |
| | | url += '&_search='+this.env.search_request; |
| | | |
| | | this.http_request('list', url, lock); |
| | |
| | | |
| | | this.group_create = function() |
| | | { |
| | | if (!this.gui_objects.folderlist) |
| | | return; |
| | | |
| | | if (!this.name_input) { |
| | | this.name_input = $('<input>').attr('type', 'text'); |
| | | this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); }); |
| | | this.name_input_li = $('<li>').addClass('contactgroup').append(this.name_input); |
| | | |
| | | var li = this.get_folder_li(this.env.source) |
| | | this.name_input_li.insertAfter(li); |
| | | } |
| | | |
| | | this.name_input.select().focus(); |
| | | this.add_input_row('contactgroup'); |
| | | }; |
| | | |
| | | this.group_rename = function() |
| | |
| | | this.list_contacts(prop.source, 0); |
| | | }; |
| | | |
| | | // @TODO: maybe it would be better to use popup instead of inserting input to the list? |
| | | this.add_input_row = function(type) |
| | | { |
| | | if (!this.gui_objects.folderlist) |
| | | return; |
| | | |
| | | if (!this.name_input) { |
| | | this.name_input = $('<input>').attr('type', 'text').data('tt', type); |
| | | this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); }); |
| | | this.name_input_li = $('<li>').addClass(type).append(this.name_input); |
| | | |
| | | var li = type == 'contactsearch' ? $('li:last', this.gui_objects.folderlist) : this.get_folder_li(this.env.source); |
| | | this.name_input_li.insertAfter(li); |
| | | } |
| | | |
| | | this.name_input.select().focus(); |
| | | }; |
| | | |
| | | // handler for keyboard events on the input field |
| | | this.add_input_keydown = function(e) |
| | | { |
| | | var key = rcube_event.get_keycode(e); |
| | | var key = rcube_event.get_keycode(e), |
| | | input = $(e.target), itype = input.data('tt'); |
| | | |
| | | // enter |
| | | if (key == 13) { |
| | | var newname = this.name_input.val(); |
| | | var newname = input.val(); |
| | | |
| | | if (newname) { |
| | | var lock = this.set_busy(true, 'loading'); |
| | | if (this.env.group_renaming) |
| | | |
| | | if (itype == 'contactsearch') |
| | | this.http_post('search-create', '_search='+urlencode(this.env.search_request)+'&_name='+urlencode(newname), lock); |
| | | else if (this.env.group_renaming) |
| | | this.http_post('group-rename', '_source='+urlencode(this.env.source)+'&_gid='+urlencode(this.env.group)+'&_name='+urlencode(newname), lock); |
| | | else |
| | | this.http_post('group-create', '_source='+urlencode(this.env.source)+'&_name='+urlencode(newname), lock); |
| | |
| | | // unselect directory/group |
| | | this.unselect_directory = function() |
| | | { |
| | | if (this.env.address_sources.length > 1 || this.env.group != '') { |
| | | this.select_folder('', (this.env.group ? 'G'+this.env.source+this.env.group : this.env.source)); |
| | | this.env.group = ''; |
| | | this.env.source = ''; |
| | | this.select_folder(''); |
| | | this.enable_command('search-delete', false); |
| | | }; |
| | | |
| | | // callback for creating a new saved search record |
| | | this.insert_saved_search = function(name, id) |
| | | { |
| | | this.reset_add_input(); |
| | | |
| | | var key = 'S'+id, |
| | | link = $('<a>').attr('href', '#') |
| | | .attr('rel', id) |
| | | .click(function() { return rcmail.command('listsearch', id, this); }) |
| | | .html(name), |
| | | li = $('<li>').attr({id: 'rcmli'+key.replace(this.identifier_expr, '_'), 'class': 'contactsearch'}) |
| | | .append(link), |
| | | prop = {name:name, id:id, li:li[0]}; |
| | | |
| | | this.add_saved_search_row(prop, li); |
| | | this.select_folder('S'+id); |
| | | this.enable_command('search-delete', true); |
| | | this.env.search_id = id; |
| | | |
| | | this.triggerEvent('abook_search_insert', prop); |
| | | }; |
| | | |
| | | // add saved search row to the list, with sorting |
| | | this.add_saved_search_row = function(prop, li, reloc) |
| | | { |
| | | var row, sibling, name = prop.name.toUpperCase(); |
| | | |
| | | // When renaming groups, we need to remove it from DOM and insert it in the proper place |
| | | if (reloc) { |
| | | row = li.clone(true); |
| | | li.remove(); |
| | | } |
| | | else |
| | | row = li; |
| | | |
| | | $('li[class~="contactsearch"]', this.gui_objects.folderlist).each(function(i, elem) { |
| | | if (!sibling) |
| | | sibling = this.previousSibling; |
| | | |
| | | if (name >= $(this).text().toUpperCase()) |
| | | sibling = elem; |
| | | else |
| | | return false; |
| | | }); |
| | | |
| | | if (sibling) |
| | | row.insertAfter(sibling); |
| | | else |
| | | row.appendTo(this.gui_objects.folderlist); |
| | | }; |
| | | |
| | | // creates an input for saved search name |
| | | this.search_create = function() |
| | | { |
| | | this.add_input_row('contactsearch'); |
| | | }; |
| | | |
| | | this.search_delete = function() |
| | | { |
| | | if (this.env.search_request) { |
| | | var lock = this.set_busy(true, 'savedsearchdeleting'); |
| | | this.http_post('search-delete', '_sid='+urlencode(this.env.search_id), lock); |
| | | } |
| | | }; |
| | | |
| | | // callback from server upon search-delete command |
| | | this.remove_search_item = function(id) |
| | | { |
| | | var li, key = 'S'+id; |
| | | if ((li = this.get_folder_li(key))) { |
| | | this.triggerEvent('search_delete', { id:id, li:li }); |
| | | |
| | | li.parentNode.removeChild(li); |
| | | } |
| | | |
| | | this.env.search_id = null; |
| | | this.env.search_request = null; |
| | | this.list_contacts_clear(); |
| | | this.reset_qsearch(); |
| | | this.enable_command('search-delete', 'search-create', false); |
| | | }; |
| | | |
| | | this.listsearch = function(id) |
| | | { |
| | | var folder, lock = this.set_busy(true, 'searching'); |
| | | |
| | | if (this.contact_list) { |
| | | this.list_contacts_clear(); |
| | | } |
| | | |
| | | this.reset_qsearch(); |
| | | this.select_folder('S'+id); |
| | | |
| | | // reset vars |
| | | this.env.current_page = 1; |
| | | this.http_request('search', '_sid='+urlencode(id), lock); |
| | | }; |
| | | |
| | | |
| | |
| | | }; |
| | | |
| | | // mark a mailbox as selected and set environment variable |
| | | this.select_folder = function(name, old, prefix) |
| | | this.select_folder = function(name, prefix) |
| | | { |
| | | if (this.gui_objects.folderlist) { |
| | | var current_li, target_li; |
| | | |
| | | if ((current_li = this.get_folder_li(old, prefix))) { |
| | | $(current_li).removeClass('selected').addClass('unfocused'); |
| | | if ((current_li = $('li.selected', this.gui_objects.folderlist))) { |
| | | current_li.removeClass('selected').addClass('unfocused'); |
| | | } |
| | | if ((target_li = this.get_folder_li(name, prefix))) { |
| | | $(target_li).removeClass('unfocused').addClass('selected'); |
| | | } |
| | | |
| | | // trigger event hook |
| | | this.triggerEvent('selectfolder', { folder:name, old:old, prefix:prefix }); |
| | | this.triggerEvent('selectfolder', { folder:name, prefix:prefix }); |
| | | } |
| | | }; |
| | | |
| | |
| | | this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0)); |
| | | |
| | | if (response.action == 'list' || response.action == 'search') { |
| | | this.enable_command('search-create', this.env.source == ''); |
| | | this.enable_command('search-delete', this.env.search_id); |
| | | this.update_group_commands(); |
| | | this.triggerEvent('listupdate', { folder:this.env.source, rowcount:this.contact_list.rowcount }); |
| | | } |
| | |
| | | $labels['markunread'] = 'As unread'; |
| | | $labels['markflagged'] = 'As flagged'; |
| | | $labels['markunflagged'] = 'As unflagged'; |
| | | $labels['messageactions'] = 'More actions...'; |
| | | $labels['moreactions'] = 'More actions...'; |
| | | |
| | | $labels['select'] = 'Select'; |
| | | $labels['all'] = 'All'; |
| | |
| | | $labels['export'] = 'Export'; |
| | | $labels['exportvcards'] = 'Export contacts in vCard format'; |
| | | $labels['newcontactgroup'] = 'Create new contact group'; |
| | | $labels['groupactions'] = 'Actions for contact groups...'; |
| | | $labels['grouprename'] = 'Rename group'; |
| | | $labels['groupdelete'] = 'Delete group'; |
| | | |
| | |
| | | $labels['groups'] = 'Groups'; |
| | | $labels['personaladrbook'] = 'Personal Addresses'; |
| | | |
| | | $labels['searchsave'] = 'Save search'; |
| | | $labels['searchdelete'] = 'Delete search'; |
| | | |
| | | $labels['import'] = 'Import'; |
| | | $labels['importcontacts'] = 'Import contacts'; |
| | | $labels['importfromfile'] = 'Import from file:'; |
| | |
| | | $messages['nobodywarning'] = 'Send this message without text?'; |
| | | $messages['notsentwarning'] = 'Message has not been sent. Do you want to discard your message?'; |
| | | $messages['noldapserver'] = 'Please select an ldap server to search.'; |
| | | $messages['nocontactsreturned'] = 'No contacts were found.'; |
| | | $messages['nosearchname'] = 'Please enter a contact name or email address.'; |
| | | $messages['notuploadedwarning'] = 'Not all attachments have been uploaded yet. Please wait or cancel the upload.'; |
| | | $messages['searchsuccessful'] = '$nr messages found.'; |
| | | $messages['contactsearchsuccessful'] = '$nr contacts found.'; |
| | | $messages['searchnomatch'] = 'Search returned no matches.'; |
| | | $messages['searching'] = 'Searching...'; |
| | | $messages['checking'] = 'Checking...'; |
| | |
| | | $messages['groupdeleted'] = 'Group deleted successfully.'; |
| | | $messages['grouprenamed'] = 'Group renamed successfully.'; |
| | | $messages['groupcreated'] = 'Group created successfully.'; |
| | | $messages['savedsearchdeleted'] = 'Saved search deleted successfully.'; |
| | | $messages['savedsearchdeleteerror'] = 'Could not delete saved search.'; |
| | | $messages['savedsearchcreated'] = 'Saved search created successfully.'; |
| | | $messages['savedsearchcreateerror'] = 'Could not create saved search.'; |
| | | $messages['messagedeleted'] = 'Message(s) deleted successfully.'; |
| | | $messages['messagemoved'] = 'Message(s) moved successfully.'; |
| | | $messages['messagecopied'] = 'Message(s) copied successfully.'; |
| | |
| | | $out = $groupdata['out']; |
| | | } |
| | | |
| | | $OUTPUT->set_env('contactgroups', $jsdata); |
| | | $line_templ = html::tag('li', array( |
| | | 'id' => 'rcmliS%s', 'class' => '%s'), |
| | | html::a(array('href' => '#', 'rel' => 'S%s', |
| | | 'onclick' => "return ".JS_OBJECT_NAME.".command('listsearch', '%s', this)"), '%s')); |
| | | |
| | | // Saved searches |
| | | $sources = $RCMAIL->user->list_searches(rcube_user::SEARCH_ADDRESSBOOK); |
| | | foreach ($sources as $j => $source) { |
| | | $id = $source['id']; |
| | | $js_id = JQ($id); |
| | | |
| | | // set class name(s) |
| | | $class_name = 'contactsearch'; |
| | | if ($current === $id) |
| | | $class_name .= ' selected'; |
| | | if ($source['class_name']) |
| | | $class_name .= ' ' . $source['class_name']; |
| | | |
| | | $out .= sprintf($line_templ, |
| | | html_identifier($id), |
| | | $class_name, |
| | | $id, |
| | | $js_id, (!empty($source['name']) ? Q($source['name']) : Q($id))); |
| | | } |
| | | |
| | | $OUTPUT->set_env('contactgroups', $jsdata); |
| | | $OUTPUT->add_gui_object('folderlist', $attrib['id']); |
| | | // add some labels to client |
| | | $OUTPUT->add_label('deletegroupconfirm', 'groupdeleting', 'addingmember', 'removingmember'); |
| | |
| | | 'group-delete' => 'groups.inc', |
| | | 'group-addmembers' => 'groups.inc', |
| | | 'group-delmembers' => 'groups.inc', |
| | | 'search-create' => 'search.inc', |
| | | 'search-delete' => 'search.inc', |
| | | )); |
| | |
| | | |
| | | */ |
| | | |
| | | if ($RCMAIL->action == 'search-create') { |
| | | $id = get_input_value('_search', RCUBE_INPUT_POST); |
| | | $name = get_input_value('_name', RCUBE_INPUT_POST, true); |
| | | |
| | | if (($params = $_SESSION['search_params']) && $params['id'] == $id) { |
| | | |
| | | $data = array( |
| | | 'type' => rcube_user::SEARCH_ADDRESSBOOK, |
| | | 'name' => $name, |
| | | 'data' => array( |
| | | 'fields' => $params['data'][0], |
| | | 'search' => $params['data'][1], |
| | | ), |
| | | ); |
| | | |
| | | $plugin = $RCMAIL->plugins->exec_hook('saved_search_create', array('data' => $data)); |
| | | |
| | | if (!$plugin['abort']) |
| | | $result = $RCMAIL->user->insert_search($plugin['data']); |
| | | else |
| | | $result = $plugin['result']; |
| | | } |
| | | |
| | | if ($result) { |
| | | $OUTPUT->show_message('savedsearchcreated', 'confirmation'); |
| | | $OUTPUT->command('insert_saved_search', Q($name), Q($result)); |
| | | } |
| | | else |
| | | $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'savedsearchcreateerror', 'error'); |
| | | |
| | | $OUTPUT->send(); |
| | | } |
| | | |
| | | if ($RCMAIL->action == 'search-delete') { |
| | | $id = get_input_value('_sid', RCUBE_INPUT_POST); |
| | | |
| | | $plugin = $RCMAIL->plugins->exec_hook('saved_search_delete', array('id' => $id)); |
| | | |
| | | if (!$plugin['abort']) |
| | | $result = $RCMAIL->user->delete_search($id); |
| | | else |
| | | $result = $plugin['result']; |
| | | |
| | | if ($result) { |
| | | $OUTPUT->show_message('savedsearchdeleted', 'confirmation'); |
| | | $OUTPUT->command('remove_search_item', Q($id)); |
| | | } |
| | | else |
| | | $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'savedsearchdeleteerror', 'error'); |
| | | |
| | | $OUTPUT->send(); |
| | | } |
| | | |
| | | |
| | | if (!isset($_GET['_form'])) { |
| | | rcmail_contact_search(); |
| | | } |
| | |
| | | global $RCMAIL, $OUTPUT, $CONFIG, $SEARCH_MODS_DEFAULT; |
| | | |
| | | $adv = isset($_POST['_adv']); |
| | | $sid = get_input_value('_sid', RCUBE_INPUT_GET); |
| | | |
| | | // get search criteria from saved search |
| | | if ($sid && ($search = $RCMAIL->user->get_search($sid))) { |
| | | $fields = $search['data']['fields']; |
| | | $search = $search['data']['search']; |
| | | } |
| | | // get fields/values from advanced search form |
| | | if ($adv) { |
| | | else if ($adv) { |
| | | foreach (array_keys($_POST) as $key) { |
| | | $s = trim(get_input_value($key, RCUBE_INPUT_POST, true)); |
| | | if (strlen($s) && preg_match('/^_search_([a-zA-Z0-9_-]+)$/', $key, $m)) { |
| | |
| | | |
| | | // save search settings in session |
| | | $_SESSION['search'][$search_request] = $search_set; |
| | | $_SESSION['search_params'] = array('id' => $search_request, 'data' => array($fields, $search)); |
| | | $_SESSION['page'] = 1; |
| | | |
| | | if ($adv) |
| | |
| | | if ($result->count > 0) { |
| | | // create javascript list |
| | | rcmail_js_contacts_list($result); |
| | | $OUTPUT->show_message('contactsearchsuccessful', 'confirmation', array('nr' => $result->count)); |
| | | } |
| | | else { |
| | | $OUTPUT->show_message('nocontactsfound', 'notice'); |
| | |
| | | $OUTPUT->command('set_env', 'search_request', $search_request); |
| | | $OUTPUT->command('set_env', 'pagecount', ceil($result->count / $CONFIG['pagesize'])); |
| | | $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($result)); |
| | | // Re-set current source |
| | | $OUTPUT->command('set_env', 'search_id', $sid); |
| | | $OUTPUT->command('set_env', 'source', ''); |
| | | $OUTPUT->command('set_env', 'group', ''); |
| | | |
| | | // unselect currently selected directory/group |
| | | $OUTPUT->command('unselect_directory'); |
| | | if (!$sid) |
| | | $OUTPUT->command('unselect_directory'); |
| | | $OUTPUT->command('update_group_commands'); |
| | | |
| | | // send response |
| | |
| | | |
| | | #directorylistbox input |
| | | { |
| | | margin: 2px; |
| | | margin: 0px; |
| | | font-size: 11px; |
| | | width: 90%; |
| | | } |
| | | |
| | |
| | | #directorylist li.contactgroup |
| | | { |
| | | padding-left: 15px; |
| | | background-position: 20px -144px; |
| | | background-position: 20px -143px; |
| | | } |
| | | |
| | | #directorylist li.contactsearch |
| | | { |
| | | background-position: 6px -162px; |
| | | } |
| | | |
| | | #directorylist li.selected |
| | |
| | | <roundcube:if condition="template:name == 'mail'" /> |
| | | <roundcube:button name="markmenulink" id="markmenulink" type="link" class="button markmessage" title="markmessages" onclick="rcmail_ui.show_popup('markmenu');return false" content=" " /> |
| | | <roundcube:endif /> |
| | | <roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button messagemenu" title="messageactions" onclick="rcmail_ui.show_popup('messagemenu');return false" content=" " /> |
| | | <roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button messagemenu" title="moreactions" onclick="rcmail_ui.show_popup('messagemenu');return false" content=" " /> |
| | | <roundcube:if condition="template:name == 'message'" /> |
| | | <roundcube:object name="mailboxlist" type="select" noSelection="moveto" maxlength="25" onchange="rcmail.command('moveto', this.options[this.selectedIndex].value)" class="mboxlist" folder_filter="mail" /> |
| | | <roundcube:endif /> |
| | |
| | | </div> |
| | | <div class="boxfooter"> |
| | | <roundcube:button command="group-create" type="link" title="newcontactgroup" class="buttonPas addgroup" classAct="button addgroup" content=" " /> |
| | | <roundcube:button name="groupmenulink" id="groupmenulink" type="link" title="groupactions" class="button groupactions" onclick="rcmail_ui.show_popup('groupmenu');return false" content=" " /> |
| | | <roundcube:button name="groupmenulink" id="groupmenulink" type="link" title="moreactions" class="button groupactions" onclick="rcmail_ui.show_popup('groupmenu');return false" content=" " /> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | <ul> |
| | | <li><roundcube:button command="group-rename" label="grouprename" classAct="active" /></li> |
| | | <li><roundcube:button command="group-delete" label="groupdelete" classAct="active" /></li> |
| | | <li class="separator_above"><roundcube:button command="search-create" label="searchsave" classAct="active" /></li> |
| | | <li><roundcube:button command="search-delete" label="searchdelete" classAct="active" /></li> |
| | | <roundcube:container name="groupoptions" id="groupoptionsmenu" /> |
| | | </ul> |
| | | </div> |