From: Vincent Bernat Date: Fri, 23 Dec 2011 21:03:08 +0000 (+0100) Subject: Imported Upstream version 0.7 X-Git-Url: https://git.donarmstrong.com/?p=roundcube.git;a=commitdiff_plain;h=76507f7c63a660742e76889ad6e3919f3dde3bb0 Imported Upstream version 0.7 --- diff --git a/CHANGELOG b/CHANGELOG index f4daec4..c872df3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,96 +1,80 @@ CHANGELOG Roundcube Webmail =========================== -- Fix bug where the last identity is used on reply (#1488101) -- Fix locked folder rename option on servers supporting RFC2086 only (#1488089) -- Fix encoding of LDAP contacts identifiers (#1488079) -- Fix session race conditions when composing new messages -- jQuery 1.6.4 -- Fix handling of binary attachments encoded with quoted-printable (#1488065) -- Fix text-overflow:ellipsis issues on messages list in FF7 and Webkit (#1488061) -- Fix handling of links with IP address -- Fix bug where message list filter was reset on folder compacting (#1488076) +RELEASE 0.7 +----------- +- Make Roundcube render the Email Standards Project Acid Test correctly +- Replace prompt() with jQuery UI dialog (#1485135) +- Fix navigation in messages search results +- Improved handling of some malformed values encoded with quoted-printable (#1488232) +- Add possibility to do LDAP bind before searching for bind DN +- Fix handling of empty tags in HTML messages (#1488225) +- Add content filter for embedded attachments to protect from XSS on IE (#1487895) +- Use strpos() instead of strstr() when possible (#1488211) +- Fix handling HTML entities when converting HTML to text (#1488212) +- Fix fit_string_to_size() renders browser and ui unresponsive (#1488207) +- Fix handling of invalid characters in request (#1488124) +- Fix merging some configuration options in update.sh script (#1485864) +- Fix so TEXT key will remove all HEADER keys in IMAP SEARCH (#1488208) +- Fix handling contact photo url with https:// prefix (#1488202) +- Fix possible infinite redirect on attachment preview (#1488199) +- Improved clickjacking protection for browsers which don't support X-Frame-Options headers +- Fixed bug where similiar folder names were highlighted wrong (#1487860) +- Fixed bug in handling link with '!' character in it (#1488195) +- Fixed bug where session ID's length was limited to 40 characters (#1488196) +- TinyMCE security issue: removed moxieplayer (embedding flv and mp4 is not supported anymore) -RELEASE 0.6-RC --------------- -- jQuery 1.6.3 -- Fallback to mail_domain in LDAP variable replacements; added 'host' to 'user_create' hook arguments (#1488024) -- Fixed wrong vCard type parameter mobile (#1488067) -- Fixed vCard WORKFAX issue (#1488046) -- Add vCard's Profile URL support (#1488062) -- Fix imap_cache setting to values other than 'db' (#1488060) -- Fix handling of attachments inside message/rfc822 parts (#1488026) -- Make list of mimetypes that open in preview window configurable (#1487625) -- Added plugin hook 'message_part_get' for attachment downloads -- Fixed selecting identity on reply/forward (#1487981) -- Fix image type check for contact photo uploads - -RELEASE 0.6-beta +RELEASE 0.7-beta ---------------- -- Added unique connection identifier to IMAP debug messages -- Add option to hide selected LDAP addressbook on the list -- Add client-side checking of uploaded files size -- Add newlines between organization, department, jobtitle (#1488028) -- Recalculate date when replying to a message and localize the cite header (#1487675) -- Fix handling of email addresses with quoted local part (#1487939) -- Fix EOL character in vCard exports (#1487873) -- Added optional "multithreading" autocomplete feature -- Plugin API: Added 'config_get' hook -- Fixed new_user_identity plugin to work with updated rcube_ldap class (#1487994) -- Plugin API: added folder_delete and folder_rename hooks -- Added possibility to undo last contact delete operation -- Fix sorting of contact groups after group create (#1487747) -- Add optional textual upload progress indicator (#1486039) -- Fix parsing URLs containing commas (#1487970) -- Added vertical splitter for books/groups list in addressbook (#1487923) -- Improved namespace roots handling in folder manager -- Added searching in all addressbook sources -- Added addressbook source selection in contacts import -- Implement LDAPv3 Virtual List View (VLV) for paged results listing -- Use 'address_template' config option when adding a new address block (#1487944) -- Added addressbook advanced search -- Add popup with basic fields selection for addressbook search -- Case-insensitive matching in autocompletion (#1487933) -- Added option to force spellchecking before sending a message (#1485458) -- Fix handling of "<" character in contact data, search fields and folder names (#1487864) -- Fix saving "<" character in identity name and organization fields (#1487864) -- Added option to specify to which address book add new contacts -- Added plugin hook for keep-alive requests -- Store user preferences in session when write-master is not available and session is stored in memcache, write them later -- Improve performence of folder manager operations -- Fix default_port option handling in Installer when config.inc.php file exists (#1487925) -- Removed option focus_on_new_message, added newmail_notifier plugin -- Added general rcube_cache class with Memcache and APC support -- Improved caching performance by skipping writes of unchanged data -- Option enable_caching replaced by imap_cache and messages_cache options -- Fix WORKFAX saving in address book (#1487910) -- Add forward-as-attachment feature -- jQuery-1.6.2 (#1487913, #1487144) -- Improve display name composition when saving contacts (#1487143) -- Fix problems with subfolders of INBOX folder on some IMAP servers (#1487725) -- Fix handling of folders that doesn't belong to any namespace (#1487637) -- Enable multiselection for attachments uploading in capable browsers (#1485969) -- Add possibility to change HTML editor configuration by skin -- Fix a bug where selecting too many contacts would produce too large URI request (#1487892) -- Improve performance by including files with absolute path (#1487849) -- Move folder name truncation to client/skin (#1485412) -- Added plugin hook for request token creation -- Replace LDAP vars in group queries (#1487837) -- Fix vcard folding with uncode characters (#1487868) -- Keep all submitted data if contact form validation fails (#1487865) -- Handle uncode strings in rcube_addressbook::normalize_string() (#1487866) -- Fix handling of debug_level=4 in ajax requests (#1487831) -- Enable TinyMCE's contextmenu (#1487014) -- Allow multiple concurrent compose sessions -- New config option for custom logo -- Allow skins to define/override texts with -- Add simple ACL rights/namespace handling in folder manager -- Force IE to send referers (#1487806) -- Better display of vcard import results (#1485457) -- Improved vcard import -- Interactive update script with improved DB schema check -- Fix problem with contactgroupmembers table creation on MySQL 4.x, add index on contact_id column -- Add LDAP SASL bind and proxy authentication (#1486692) -- Replying to a sent message puts the old recipient as the new recipient (#1487074) -- Fulltext search over (almost) all data for contacts -- Extend address book with rich contact information +- Fix handling of HTML form elements in messages (#1485137) +- Fix regression in setting recipient to self when replying to a Sent message (#1487074) +- Fix listing of folders in hidden namespaces (#1486796) +- Don't consider \Noselect flag when building folders tree (#1488004) +- Fix sorting autocomplete results (#1488084) +- Add option to set session name (#1486433) +- Add option to skip alternative email addresses in autocompletion +- Fix inconsistent behaviour of Compose button in Drafts folder, add Edit button for drafts +- Fix problem with parsing HTML message body with non-unicode characters (#1487813) +- Add option to define matching method for addressbook search (#1486564, #1487907) +- Make email recipients separator configurable +- Fix so folders with \Noinferiors attribute aren't listed in parent selector +- Fix handling of curly brackets in URLs (#1488168) +- Fix handling of dates (birthday/anniversary) in contact data (#1488147) +- Fix error on opening searched LDAP contact (#1488144) +- Fix redundant line break in flowed format (#1488146) +- Fix IDN address validation issue (#1488137) +- Fix JS error when dst_active checkbox doesn't exist (#1488133) +- Autocomplete LDAP records when adding contacts from mail (#1488073) +- Plugin API: added 'ready' hook (#1488063) +- Ignore DSN request when it isn't supported by SMTP server (#1487800) +- Make sure LDAP name fields aren't arrays (#1488108) +- Fixed imap test to non-default port when using ssl (#1488118) +- Force all files to be overwritten when updating (#1488117) +- Fix issue where it wasn't possible to change list view mode in folder manager for INBOX (#1488107) +- Fix namespace handling in special folders settings (#1488112) +- Disable time limit for CLI scripts (#1488109) +- Fix misleading display when chaning editor type (#1488104) +- Add loading indicator on contact delete +- Fix bug where after delete message rows can be added to the list of another folder (#1487752) +- Add notice on autocompletion that not all records were displayed +- Add option 'searchonly' for LDAP address books +- Add Priority filter to the messages list +- Cache synchronization using QRESYNC/CONDSTORE +- Trigger 'new_messages' hook for all checked folders (#1488083) +- Make date/time format user configurable; drop 'date_today' config option +- Fix setting title for truncated subject in IE (#1487128) +- Fix displaying multipart/alternative messages with only one part (#1487938) +- Rewritten messages caching: + Indexes are stored in a separate table, so there's no need to store all messages in a folder + Added threads data caching + Flags are stored separately, so flag change doesn't cause DELETE+INSERT, just UPDATE +- Improved FETCH response handling +- Improvements in response tokenization method +- Use 'From' and 'To' labels instead of 'Sender' and 'Recipient' +- Fix username case-insensitivity issue in MySQL (#1488021) +- Addressbook Saved Searches +- Added spellchecker exceptions dictionary (shared or per-user) +- Added possibility to ignore words containing caps, numbers, symbols (spellcheck_ignore_* options) +- Added 'priority' column on messages list (#1486782) +- Localize forwarded message header (#1488058) + diff --git a/INSTALL b/INSTALL index 9b07b2b..3fc6f5d 100644 --- a/INSTALL +++ b/INSTALL @@ -9,7 +9,7 @@ wiki page at http://trac.roundcube.net/wiki REQUIREMENTS ============ -* The Apache or Lighttpd Webserver +* The Apache, Lighttpd, Cherokee or Hiawatha web server * .htaccess support allowing overrides for DirectoryIndex * PHP Version 5.2.1 or greater including - PCRE, DOM, JSON, XML, Session, Sockets (required) diff --git a/INSTALL.orig b/INSTALL.orig new file mode 100644 index 0000000..493baf7 --- /dev/null +++ b/INSTALL.orig @@ -0,0 +1,233 @@ +INTRODUCTION +============ + +This file describes the basic steps to install Roundcube Webmail on your +web server. For additional information, please also consult the project's +wiki page at http://trac.roundcube.net/wiki + + +REQUIREMENTS +============ + +* The Apache, Lighttpd, Cherokee or Hiawatha web server +* .htaccess support allowing overrides for DirectoryIndex +* PHP Version 5.2.1 or greater including + - PCRE, DOM, JSON, XML, Session, Sockets (required) + - libiconv (recommended) + - mbstring, fileinfo, mcrypt (optional) +* PEAR packages distributed with Roundcube or external: + - MDB2 2.5.0 or newer + - Mail_Mime 1.8.1 or newer + - Net_SMTP 1.4.2 or newer + - Net_IDNA2 0.1.1 or newer + - Auth_SASL 1.0.3 or newer +* php.ini options (see .htaccess file): + - error_reporting E_ALL & ~E_NOTICE (or lower) + - memory_limit > 16MB (increase as suitable to support large attachments) + - file_uploads enabled (for attachment upload features) + - session.auto_start disabled + - zend.ze1_compatibility_mode disabled + - suhosin.session.encrypt disabled + - mbstring.func_overload disabled + - magic_quotes_runtime disabled +* PHP compiled with OpenSSL to connect to IMAPS and to use the spell checker +* A MySQL (4.0.8 or newer), PostgreSQL, MSSQL database engine + or the SQLite extension for PHP +* One of the above databases with permission to create tables +* An SMTP server (recommended) or PHP configured for mail delivery + + +INSTALLATION +============ + +1. Decompress and put this folder somewhere inside your document root +2. Make sure that the following directories (and the files within) + are writable by the webserver + - /temp + - /logs +3. Create a new database and a database user for Roundcube (see DATABASE SETUP) +4. Point your browser to http://url-to-roundcube/installer/ +5. Follow the instructions of the install script (or see MANUAL CONFIGURATION) +6. After creating and testing the configuration, remove the installer directory +7. Done! + + +CONFIGURATION HINTS +=================== + +Roundcube writes internal errors to the 'errors' log file located in the logs +directory which can be configured in config/main.inc.php. If you want ordinary +PHP errors to be logged there as well, enable the 'php_value error_log' line +in the .htaccess file and set the path to the log file accordingly. + +By default the session_path settings of PHP are not modified by Roundcube. +However if you want to limit the session cookies to the directory where +Roundcube resides you can uncomment and configure the according line +in the .htaccess file. + + +DATABASE SETUP +============== + +Note: Database for Roundcube must use UTF-8 character set. + +* MySQL +------- +Setting up the mysql database can be done by creating an empty database, +importing the table layout and granting the proper permissions to the +roundcube user. Here is an example of that procedure: + +# mysql +> CREATE DATABASE roundcubemail /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; +> GRANT ALL PRIVILEGES ON roundcubemail.* TO roundcube@localhost + IDENTIFIED BY 'password'; +> quit + +# mysql roundcubemail < SQL/mysql.initial.sql + +Note 1: 'password' is the master password for the roundcube user. It is strongly +recommended you replace this with a more secure password. Please keep in +mind: You need to specify this password later in 'config/db.inc.php'. + + +* SQLite +-------- +You need sqlite 2 (preferably 2.8) to setup the sqlite db +(sqlite 3.x also doesn't work at the moment). Here is +an example how you can setup the sqlite.db for roundcube: + +# sqlite -init SQL/sqlite.initial.sql sqlite.db +Loading resources from SQL/sqlite.initial.sql +SQLite version 2.8.16 +Enter ".help" for instructions +sqlite> .exit +# chmod o+rw sqlite.db + +Make sure your configuration points to the sqlite.db file and that the +webserver can write to the file and the directory containing the file. + + +* PostgreSQL +------------ +To use Roundcube with PostgreSQL support you have to follow these +simple steps, which have to be done as the postgres system user (or +which ever is the database superuser): + +$ createuser roundcube +$ createdb -O roundcube -E UNICODE roundcubemail +$ psql roundcubemail + +roundcubemail =# ALTER USER roundcube WITH PASSWORD 'the_new_password'; +roundcubemail =# \c - roundcube +roundcubemail => \i SQL/postgres.initial.sql + +All this has been tested with PostgreSQL 8.x and 7.4.x. Older +versions don't have a -O option for the createdb, so if you are +using that version you'll have to change ownership of the DB later. + + +Database cleaning +----------------- +Do keep your database slick and clean we recommend to periodically execute +bin/cleandb.sh which finally removes all records that are marked as deleted. +Best solution is to install a cronjob running this script daily. + + + +MANUAL CONFIGURATION +==================== + +First of all, rename the files config/*.inc.php.dist to config/*.inc.php. +You can then change these files according to your environment and your needs. +Details about the config parameters can be found in the config files. +See http://trac.roundcube.net/wiki/Howto_Install for even more guidance. + +You can also modify the default .htaccess file. This is necessary to +increase the allowed size of file attachments, for example: + php_value upload_max_filesize 2M + + +UPGRADING +========= + +If you already have a previous version of Roundcube installed, +please refer to the instructions in UPGRADING guide. + + +OPTIMISING +========== + +There are two forms of optimisation here, compression and caching, both aimed +at increasing an end user's experience using Roundcube Webmail. Compression +allows the static web pages to be delivered with less bandwidth. The index.php +of Roundcube Webmail already enables compression on its output. The settings +below allow compression to occur for all static files. Caching sets HTTP +response headers that enable a user's web client to understand what is static +and how to cache it. + +The caching directives used are: + * Etags - sets at tag so the client can request is the page has changed + * Cache-control - defines the age of the page and that the page is 'public' + This enables clients to cache javascript files that don't have private + information between sessions even if using HTTPS. It also allows proxies + to share the same cached page between users. + * Expires - provides another hint to increase the lifetime of static pages. + +For more information refer to RFC 2616. + +Side effects: +------------- +These directives are designed for production use. If you are using this in +a development environment you may get horribly confused if your webclient +is caching stuff that you changed on the server. Disabling the expires +parts below should save you some grief. + +If you are changing the skins, it is recommended that you copy content to +a different directory apart from 'default'. + +Apache: +------- +To enable these features in apache the following modules need to be enabled: + * mod_deflate + * mod_expires + * mod_headers + +The optimisation is already included in the .htaccess file in the top +directory of your installation. + +If you are using Apache version 2.2.9 and later, in the .htaccess file +change the 'append' word to 'merge' for a more correct response. Keeping +as 'append' shouldn't cause any problems though changing to merge will +eliminate the possibility of duplicate 'public' headers in Cache-control. + +Lighttpd: +--------- +With Lightty the addition of Expire: tags by mod_expire is incompatible with +the addition of "Cache-control: public". Using Cache-control 'public' is +used below as it is assumed to give a better caching result. + +Enable modules in server.modules: + "mod_setenv" + "mod_compress" + +Mod_compress is a server side cache of compressed files to improve its performance. + +$HTTP["host"] == "www.example.com" { + + static-file.etags = "enable" + # http://redmine.lighttpd.net/projects/lighttpd/wiki/Etag.use-mtimeDetails + etag.use-mtime = "enable" + + # http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModSetEnv + $HTTP["url"] =~ "^/roundcubemail/(plugins|skins|program)" { + setenv.add-response-header = ( "Cache-Control" => "public, max-age=2592000") + } + + # http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModCompress + # set compress.cache-dir to somewhere outside the docroot. + compress.cache-dir = var.statedir + "/cache/compress" + + compress.filetype = ("text/plain", "text/html", "text/javascript", "text/css", "text/xml", "image/gif", "image/png") +} + + diff --git a/SQL/mssql.initial.sql b/SQL/mssql.initial.sql index 4aa6fc9..c141141 100644 --- a/SQL/mssql.initial.sql +++ b/SQL/mssql.initial.sql @@ -7,6 +7,33 @@ CREATE TABLE [dbo].[cache] ( ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO +CREATE TABLE [dbo].[cache_index] ( + [user_id] [int] NOT NULL , + [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [changed] [datetime] NOT NULL , + [valid] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL , + [data] [text] COLLATE Latin1_General_CI_AI NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[cache_thread] ( + [user_id] [int] NOT NULL , + [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [changed] [datetime] NOT NULL , + [data] [text] COLLATE Latin1_General_CI_AI NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[cache_messages] ( + [user_id] [int] NOT NULL , + [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [uid] [int] NOT NULL , + [changed] [datetime] NOT NULL , + [data] [text] COLLATE Latin1_General_CI_AI NOT NULL + [flags] [int](1) NOT NULL , +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + CREATE TABLE [dbo].[contacts] ( [contact_id] [int] IDENTITY (1, 1) NOT NULL , [user_id] [int] NOT NULL , @@ -53,27 +80,8 @@ CREATE TABLE [dbo].[identities] ( ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO -CREATE TABLE [dbo].[messages] ( - [message_id] [int] IDENTITY (1, 1) NOT NULL , - [user_id] [int] NOT NULL , - [del] [tinyint] NOT NULL , - [cache_key] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , - [created] [datetime] NOT NULL , - [idx] [int] NOT NULL , - [uid] [int] NOT NULL , - [subject] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL , - [from] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL , - [to] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL , - [cc] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL , - [date] [datetime] NOT NULL , - [size] [int] NOT NULL , - [headers] [text] COLLATE Latin1_General_CI_AI NOT NULL , - [structure] [text] COLLATE Latin1_General_CI_AI NULL -) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] -GO - CREATE TABLE [dbo].[session] ( - [sess_id] [varchar] (32) COLLATE Latin1_General_CI_AI NOT NULL , + [sess_id] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , [created] [datetime] NOT NULL , [changed] [datetime] NULL , [ip] [varchar] (40) COLLATE Latin1_General_CI_AI NOT NULL , @@ -93,6 +101,22 @@ CREATE TABLE [dbo].[users] ( ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO +CREATE TABLE [dbo].[dictionary] ( + [user_id] [int] , + [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL , + [data] [text] COLLATE Latin1_General_CI_AI NOT NULL +) 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 ( @@ -100,6 +124,27 @@ ALTER TABLE [dbo].[cache] WITH NOCHECK ADD ) ON [PRIMARY] GO +ALTER TABLE [dbo].[cache_index] WITH NOCHECK ADD + PRIMARY KEY CLUSTERED + ( + [user_id],[mailbox] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_thread] WITH NOCHECK ADD + PRIMARY KEY CLUSTERED + ( + [user_id],[mailbox] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_messages] WITH NOCHECK ADD + PRIMARY KEY CLUSTERED + ( + [user_id],[mailbox],[uid] + ) ON [PRIMARY] +GO + ALTER TABLE [dbo].[contacts] WITH NOCHECK ADD CONSTRAINT [PK_contacts_contact_id] PRIMARY KEY CLUSTERED ( @@ -128,13 +173,6 @@ ALTER TABLE [dbo].[identities] WITH NOCHECK ADD ) ON [PRIMARY] GO -ALTER TABLE [dbo].[messages] WITH NOCHECK ADD - PRIMARY KEY CLUSTERED - ( - [message_id] - ) ON [PRIMARY] -GO - ALTER TABLE [dbo].[session] WITH NOCHECK ADD CONSTRAINT [PK_session_sess_id] PRIMARY KEY CLUSTERED ( @@ -149,6 +187,13 @@ ALTER TABLE [dbo].[users] WITH NOCHECK ADD ) 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 CONSTRAINT [DF_cache_user_id] DEFAULT ('0') FOR [user_id], CONSTRAINT [DF_cache_cache_key] DEFAULT ('') FOR [cache_key], @@ -164,6 +209,29 @@ GO CREATE INDEX [IX_cache_created] ON [dbo].[cache]([created]) ON [PRIMARY] GO +ALTER TABLE [dbo].[cache_index] ADD + CONSTRAINT [DF_cache_index_changed] DEFAULT (getdate()) FOR [changed], + CONSTRAINT [DF_cache_index_valid] DEFAULT ('0') FOR [valid] +GO + +CREATE INDEX [IX_cache_index_user_id] ON [dbo].[cache_index]([user_id]) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_thread] ADD + CONSTRAINT [DF_cache_thread_changed] DEFAULT (getdate()) FOR [changed] +GO + +CREATE INDEX [IX_cache_thread_user_id] ON [dbo].[cache_thread]([user_id]) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_messages] ADD + CONSTRAINT [DF_cache_messages_changed] DEFAULT (getdate()) FOR [changed], + CONSTRAINT [DF_cache_messages_flags] DEFAULT (0) FOR [flags], +GO + +CREATE INDEX [IX_cache_messages_user_id] ON [dbo].[cache_messages]([user_id]) ON [PRIMARY] +GO + ALTER TABLE [dbo].[contacts] ADD CONSTRAINT [DF_contacts_user_id] DEFAULT (0) FOR [user_id], CONSTRAINT [DF_contacts_changed] DEFAULT (getdate()) FOR [changed], @@ -215,33 +283,6 @@ GO CREATE INDEX [IX_identities_user_id] ON [dbo].[identities]([user_id]) ON [PRIMARY] GO -ALTER TABLE [dbo].[messages] ADD - CONSTRAINT [DF_messages_user_id] DEFAULT (0) FOR [user_id], - CONSTRAINT [DF_messages_del] DEFAULT (0) FOR [del], - CONSTRAINT [DF_messages_cache_key] DEFAULT ('') FOR [cache_key], - CONSTRAINT [DF_messages_created] DEFAULT (getdate()) FOR [created], - CONSTRAINT [DF_messages_idx] DEFAULT (0) FOR [idx], - CONSTRAINT [DF_messages_uid] DEFAULT (0) FOR [uid], - CONSTRAINT [DF_messages_subject] DEFAULT ('') FOR [subject], - CONSTRAINT [DF_messages_from] DEFAULT ('') FOR [from], - CONSTRAINT [DF_messages_to] DEFAULT ('') FOR [to], - CONSTRAINT [DF_messages_cc] DEFAULT ('') FOR [cc], - CONSTRAINT [DF_messages_date] DEFAULT (getdate()) FOR [date], - CONSTRAINT [DF_messages_size] DEFAULT (0) FOR [size] -GO - -CREATE INDEX [IX_messages_user_id] ON [dbo].[messages]([user_id]) ON [PRIMARY] -GO - -CREATE INDEX [IX_messages_cache_key] ON [dbo].[messages]([cache_key]) ON [PRIMARY] -GO - -CREATE INDEX [IX_messages_uid] ON [dbo].[messages]([uid]) ON [PRIMARY] -GO - -CREATE INDEX [IX_messages_created] ON [dbo].[messages]([created]) ON [PRIMARY] -GO - ALTER TABLE [dbo].[session] ADD CONSTRAINT [DF_session_sess_id] DEFAULT ('') FOR [sess_id], CONSTRAINT [DF_session_created] DEFAULT (getdate()) FOR [created], @@ -264,6 +305,17 @@ GO CREATE INDEX [IX_users_alias] ON [dbo].[users]([alias]) ON [PRIMARY] GO +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 @@ -284,7 +336,17 @@ ALTER TABLE [dbo].[cache] ADD CONSTRAINT [FK_cache_user_id] ON DELETE CASCADE ON UPDATE CASCADE GO -ALTER TABLE [dbo].[messages] ADD CONSTRAINT [FK_messages_user_id] +ALTER TABLE [dbo].[cache_index] ADD CONSTRAINT [FK_cache_index_user_id] + FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id]) + ON DELETE CASCADE ON UPDATE CASCADE +GO + +ALTER TABLE [dbo].[cache_thread] ADD CONSTRAINT [FK_cache_thread_user_id] + FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id]) + ON DELETE CASCADE ON UPDATE CASCADE +GO + +ALTER TABLE [dbo].[cache_messages] ADD CONSTRAINT [FK_cache_messages_user_id] FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id]) ON DELETE CASCADE ON UPDATE CASCADE GO @@ -294,6 +356,11 @@ ALTER TABLE [dbo].[contactgroupmembers] ADD CONSTRAINT [FK_contactgroupmembers_c 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] diff --git a/SQL/mssql.upgrade.sql b/SQL/mssql.upgrade.sql index 606db60..eee5ae5 100644 --- a/SQL/mssql.upgrade.sql +++ b/SQL/mssql.upgrade.sql @@ -110,3 +110,137 @@ DELETE FROM [dbo].[messages] GO DELETE FROM [dbo].[cache] GO + +-- Updates from version 0.6 + +CREATE TABLE [dbo].[dictionary] ( + [user_id] [int] , + [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL , + [data] [text] COLLATE Latin1_General_CI_AI NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +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 + +DROP TABLE [dbo].[messages] +GO +CREATE TABLE [dbo].[cache_index] ( + [user_id] [int] NOT NULL , + [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [changed] [datetime] NOT NULL , + [valid] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL , + [data] [text] COLLATE Latin1_General_CI_AI NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[cache_thread] ( + [user_id] [int] NOT NULL , + [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [changed] [datetime] NOT NULL , + [data] [text] COLLATE Latin1_General_CI_AI NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[cache_messages] ( + [user_id] [int] NOT NULL , + [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [uid] [int] NOT NULL , + [changed] [datetime] NOT NULL , + [data] [text] COLLATE Latin1_General_CI_AI NOT NULL + [flags] [int] NOT NULL , +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_index] WITH NOCHECK ADD + PRIMARY KEY CLUSTERED + ( + [user_id],[mailbox] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_thread] WITH NOCHECK ADD + PRIMARY KEY CLUSTERED + ( + [user_id],[mailbox] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_messages] WITH NOCHECK ADD + PRIMARY KEY CLUSTERED + ( + [user_id],[mailbox],[uid] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_index] ADD + CONSTRAINT [DF_cache_index_changed] DEFAULT (getdate()) FOR [changed], + CONSTRAINT [DF_cache_index_valid] DEFAULT ('0') FOR [valid] +GO + +CREATE INDEX [IX_cache_index_user_id] ON [dbo].[cache_index]([user_id]) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_thread] ADD + CONSTRAINT [DF_cache_thread_changed] DEFAULT (getdate()) FOR [changed] +GO + +CREATE INDEX [IX_cache_thread_user_id] ON [dbo].[cache_thread]([user_id]) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_messages] ADD + CONSTRAINT [DF_cache_messages_changed] DEFAULT (getdate()) FOR [changed], + CONSTRAINT [DF_cache_messages_flags] DEFAULT (0) FOR [flags] +GO + +CREATE INDEX [IX_cache_messages_user_id] ON [dbo].[cache_messages]([user_id]) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache_index] ADD CONSTRAINT [FK_cache_index_user_id] + FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id]) + ON DELETE CASCADE ON UPDATE CASCADE +GO + +ALTER TABLE [dbo].[cache_thread] ADD CONSTRAINT [FK_cache_thread_user_id] + FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id]) + ON DELETE CASCADE ON UPDATE CASCADE +GO + +ALTER TABLE [dbo].[cache_messages] ADD CONSTRAINT [FK_cache_messages_user_id] + FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id]) + ON DELETE CASCADE ON UPDATE CASCADE +GO + +-- Updates from version 0.7-beta + +ALTER TABLE [dbo].[session] ALTER COLUMN [sess_id] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL +GO + diff --git a/SQL/mysql.initial.sql b/SQL/mysql.initial.sql index 14bbb96..94679f2 100644 --- a/SQL/mysql.initial.sql +++ b/SQL/mysql.initial.sql @@ -6,7 +6,7 @@ -- Table structure for table `session` CREATE TABLE `session` ( - `sess_id` varchar(40) NOT NULL, + `sess_id` varchar(128) NOT NULL, `created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', `ip` varchar(40) NOT NULL, @@ -20,9 +20,9 @@ CREATE TABLE `session` ( CREATE TABLE `users` ( `user_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `username` varchar(128) NOT NULL, + `username` varchar(128) BINARY NOT NULL, `mail_host` varchar(128) NOT NULL, - `alias` varchar(128) NOT NULL, + `alias` varchar(128) BINARY NOT NULL, `created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', `last_login` datetime DEFAULT NULL, `language` varchar(5), @@ -33,33 +33,6 @@ CREATE TABLE `users` ( ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; --- Table structure for table `messages` - -CREATE TABLE `messages` ( - `message_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, - `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', - `del` tinyint(1) NOT NULL DEFAULT '0', - `cache_key` varchar(128) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL, - `created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', - `idx` int(11) UNSIGNED NOT NULL DEFAULT '0', - `uid` int(11) UNSIGNED NOT NULL DEFAULT '0', - `subject` varchar(255) NOT NULL, - `from` varchar(255) NOT NULL, - `to` varchar(255) NOT NULL, - `cc` varchar(255) NOT NULL, - `date` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', - `size` int(11) UNSIGNED NOT NULL DEFAULT '0', - `headers` text NOT NULL, - `structure` text, - PRIMARY KEY(`message_id`), - CONSTRAINT `user_id_fk_messages` FOREIGN KEY (`user_id`) - REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, - INDEX `created_index` (`created`), - INDEX `index_index` (`user_id`, `cache_key`, `idx`), - UNIQUE `uniqueness` (`user_id`, `cache_key`, `uid`) -) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; - - -- Table structure for table `cache` CREATE TABLE `cache` ( @@ -76,6 +49,51 @@ CREATE TABLE `cache` ( ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; +-- Table structure for table `cache_index` + +CREATE TABLE `cache_index` ( + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + `mailbox` varchar(255) BINARY NOT NULL, + `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', + `valid` tinyint(1) NOT NULL DEFAULT '0', + `data` longtext NOT NULL, + CONSTRAINT `user_id_fk_cache_index` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, + INDEX `changed_index` (`changed`), + PRIMARY KEY (`user_id`, `mailbox`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + + +-- Table structure for table `cache_thread` + +CREATE TABLE `cache_thread` ( + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + `mailbox` varchar(255) BINARY NOT NULL, + `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', + `data` longtext NOT NULL, + CONSTRAINT `user_id_fk_cache_thread` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, + INDEX `changed_index` (`changed`), + PRIMARY KEY (`user_id`, `mailbox`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + + +-- Table structure for table `cache_messages` + +CREATE TABLE `cache_messages` ( + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + `mailbox` varchar(255) BINARY NOT NULL, + `uid` int(11) UNSIGNED NOT NULL DEFAULT '0', + `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', + `data` longtext NOT NULL, + `flags` int(11) NOT NULL DEFAULT '0', + CONSTRAINT `user_id_fk_cache_messages` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, + INDEX `changed_index` (`changed`), + PRIMARY KEY (`user_id`, `mailbox`, `uid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + + -- Table structure for table `contacts` CREATE TABLE `contacts` ( @@ -144,4 +162,31 @@ CREATE TABLE `identities` ( ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; +-- Table structure for table `dictionary` + +CREATE TABLE `dictionary` ( + `user_id` int(10) UNSIGNED DEFAULT NULL, + `language` varchar(5) NOT NULL, + `data` longtext NOT NULL, + CONSTRAINT `user_id_fk_dictionary` FOREIGN KEY (`user_id`) + 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 */; + + +-- 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 */; diff --git a/SQL/mysql.update.sql b/SQL/mysql.update.sql index ed21bda..0761731 100644 --- a/SQL/mysql.update.sql +++ b/SQL/mysql.update.sql @@ -144,3 +144,71 @@ ALTER TABLE `contactgroupmembers` ADD INDEX `contactgroupmembers_contact_index` TRUNCATE TABLE `messages`; TRUNCATE TABLE `cache`; + +-- Updates from version 0.6 + +ALTER TABLE `users` CHANGE `alias` `alias` varchar(128) BINARY NOT NULL; +ALTER TABLE `users` CHANGE `username` `username` varchar(128) BINARY NOT NULL; + +CREATE TABLE `dictionary` ( + `user_id` int(10) UNSIGNED DEFAULT NULL, + `language` varchar(5) NOT NULL, + `data` longtext NOT NULL, + CONSTRAINT `user_id_fk_dictionary` FOREIGN KEY (`user_id`) + 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 */; + +DROP TABLE `messages`; + +CREATE TABLE `cache_index` ( + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + `mailbox` varchar(255) BINARY NOT NULL, + `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', + `valid` tinyint(1) NOT NULL DEFAULT '0', + `data` longtext NOT NULL, + CONSTRAINT `user_id_fk_cache_index` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, + INDEX `changed_index` (`changed`), + PRIMARY KEY (`user_id`, `mailbox`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `cache_thread` ( + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + `mailbox` varchar(255) BINARY NOT NULL, + `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', + `data` longtext NOT NULL, + CONSTRAINT `user_id_fk_cache_thread` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, + INDEX `changed_index` (`changed`), + PRIMARY KEY (`user_id`, `mailbox`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `cache_messages` ( + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + `mailbox` varchar(255) BINARY NOT NULL, + `uid` int(11) UNSIGNED NOT NULL DEFAULT '0', + `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', + `data` longtext NOT NULL, + `flags` int(11) NOT NULL DEFAULT '0', + CONSTRAINT `user_id_fk_cache_messages` FOREIGN KEY (`user_id`) + REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, + INDEX `changed_index` (`changed`), + PRIMARY KEY (`user_id`, `mailbox`, `uid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +-- Updates from version 0.7-beta + +ALTER TABLE `session` CHANGE `sess_id` `sess_id` varchar(128) NOT NULL; diff --git a/SQL/postgres.initial.sql b/SQL/postgres.initial.sql index 5350e79..3710dac 100644 --- a/SQL/postgres.initial.sql +++ b/SQL/postgres.initial.sql @@ -37,7 +37,7 @@ CREATE INDEX users_alias_id_idx ON users (alias); -- CREATE TABLE "session" ( - sess_id varchar(40) DEFAULT '' PRIMARY KEY, + sess_id varchar(128) DEFAULT '' PRIMARY KEY, created timestamp with time zone DEFAULT now() NOT NULL, changed timestamp with time zone DEFAULT now() NOT NULL, ip varchar(41) NOT NULL, @@ -67,7 +67,7 @@ CREATE SEQUENCE identity_ids CREATE TABLE identities ( identity_id integer DEFAULT nextval('identity_ids'::text) PRIMARY KEY, user_id integer NOT NULL - REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, changed timestamp with time zone DEFAULT now() NOT NULL, del smallint DEFAULT 0 NOT NULL, standard smallint DEFAULT 0 NOT NULL, @@ -178,7 +178,7 @@ CREATE SEQUENCE cache_ids CREATE TABLE "cache" ( cache_id integer DEFAULT nextval('cache_ids'::text) PRIMARY KEY, user_id integer NOT NULL - REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, cache_key varchar(128) DEFAULT '' NOT NULL, created timestamp with time zone DEFAULT now() NOT NULL, data text NOT NULL @@ -188,40 +188,91 @@ CREATE INDEX cache_user_id_idx ON "cache" (user_id, cache_key); CREATE INDEX cache_created_idx ON "cache" (created); -- --- Sequence "message_ids" --- Name: message_ids; Type: SEQUENCE; Schema: public; Owner: postgres +-- Table "cache_index" +-- Name: cache_index; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE cache_index ( + user_id integer NOT NULL + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + mailbox varchar(255) NOT NULL, + changed timestamp with time zone DEFAULT now() NOT NULL, + valid smallint NOT NULL DEFAULT 0, + data text NOT NULL, + PRIMARY KEY (user_id, mailbox) +); + +CREATE INDEX cache_index_changed_idx ON cache_index (changed); + +-- +-- Table "cache_thread" +-- Name: cache_thread; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE cache_thread ( + user_id integer NOT NULL + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + mailbox varchar(255) NOT NULL, + changed timestamp with time zone DEFAULT now() NOT NULL, + data text NOT NULL, + PRIMARY KEY (user_id, mailbox) +); + +CREATE INDEX cache_thread_changed_idx ON cache_thread (changed); + +-- +-- Table "cache_messages" +-- Name: cache_messages; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE cache_messages ( + user_id integer NOT NULL + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + mailbox varchar(255) NOT NULL, + uid integer NOT NULL, + changed timestamp with time zone DEFAULT now() NOT NULL, + data text NOT NULL, + flags integer NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, mailbox, uid) +); + +CREATE INDEX cache_messages_changed_idx ON cache_messages (changed); + +-- +-- Table "dictionary" +-- Name: dictionary; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE dictionary ( + user_id integer DEFAULT NULL + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + "language" varchar(5) NOT NULL, + 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 message_ids +CREATE SEQUENCE search_ids INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; -- --- Table "messages" --- Name: messages; Type: TABLE; Schema: public; Owner: postgres +-- Table "searches" +-- Name: searches; Type: TABLE; Schema: public; Owner: postgres -- -CREATE TABLE messages ( - message_id integer DEFAULT nextval('message_ids'::text) PRIMARY KEY, +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, - del smallint DEFAULT 0 NOT NULL, - cache_key varchar(128) DEFAULT '' NOT NULL, - created timestamp with time zone DEFAULT now() NOT NULL, - idx integer DEFAULT 0 NOT NULL, - uid integer DEFAULT 0 NOT NULL, - subject varchar(128) DEFAULT '' NOT NULL, - "from" varchar(128) DEFAULT '' NOT NULL, - "to" varchar(128) DEFAULT '' NOT NULL, - cc varchar(128) DEFAULT '' NOT NULL, - date timestamp with time zone NOT NULL, - size integer DEFAULT 0 NOT NULL, - headers text NOT NULL, - structure text, - CONSTRAINT messages_user_id_key UNIQUE (user_id, cache_key, uid) + 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 INDEX messages_index_idx ON messages (user_id, cache_key, idx); -CREATE INDEX messages_created_idx ON messages (created); diff --git a/SQL/postgres.update.sql b/SQL/postgres.update.sql index 94513c5..c96669d 100644 --- a/SQL/postgres.update.sql +++ b/SQL/postgres.update.sql @@ -100,3 +100,72 @@ CREATE INDEX contactgroupmembers_contact_id_idx ON contactgroupmembers (contact_ TRUNCATE messages; TRUNCATE cache; + +-- Updates from version 0.6 + +CREATE TABLE dictionary ( + user_id integer DEFAULT NULL + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + "language" varchar(5) NOT NULL, + 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) +); + +DROP SEQUENCE messages_ids; +DROP TABLE messages; + +CREATE TABLE cache_index ( + user_id integer NOT NULL + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + mailbox varchar(255) NOT NULL, + changed timestamp with time zone DEFAULT now() NOT NULL, + valid smallint NOT NULL DEFAULT 0, + data text NOT NULL, + PRIMARY KEY (user_id, mailbox) +); + +CREATE INDEX cache_index_changed_idx ON cache_index (changed); + +CREATE TABLE cache_thread ( + user_id integer NOT NULL + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + mailbox varchar(255) NOT NULL, + changed timestamp with time zone DEFAULT now() NOT NULL, + data text NOT NULL, + PRIMARY KEY (user_id, mailbox) +); + +CREATE INDEX cache_thread_changed_idx ON cache_thread (changed); + +CREATE TABLE cache_messages ( + user_id integer NOT NULL + REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, + mailbox varchar(255) NOT NULL, + uid integer NOT NULL, + changed timestamp with time zone DEFAULT now() NOT NULL, + data text NOT NULL, + flags integer NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, mailbox, uid) +); + +CREATE INDEX cache_messages_changed_idx ON cache_messages (changed); + +-- Updates from version 0.7-beta + +ALTER TABLE "session" ALTER sess_id TYPE varchar(128); diff --git a/SQL/sqlite.initial.sql b/SQL/sqlite.initial.sql index d2885e9..8c8da5c 100644 --- a/SQL/sqlite.initial.sql +++ b/SQL/sqlite.initial.sql @@ -1,7 +1,7 @@ -- Roundcube Webmail initial database structure -- --- Table structure for table `cache` +-- Table structure for table cache -- CREATE TABLE cache ( @@ -9,7 +9,7 @@ CREATE TABLE cache ( user_id integer NOT NULL default 0, cache_key varchar(128) NOT NULL default '', created datetime NOT NULL default '0000-00-00 00:00:00', - data longtext NOT NULL + data text NOT NULL ); CREATE INDEX ix_cache_user_cache_key ON cache(user_id, cache_key); @@ -110,7 +110,7 @@ CREATE INDEX ix_users_alias ON users(alias); -- CREATE TABLE session ( - sess_id varchar(40) NOT NULL PRIMARY KEY, + sess_id varchar(128) NOT NULL PRIMARY KEY, created datetime NOT NULL default '0000-00-00 00:00:00', changed datetime NOT NULL default '0000-00-00 00:00:00', ip varchar(40) NOT NULL default '', @@ -121,28 +121,81 @@ CREATE INDEX ix_session_changed ON session (changed); -- -------------------------------------------------------- --- --- Table structure for table messages --- +-- +-- Table structure for table dictionary +-- -CREATE TABLE messages ( - message_id integer NOT NULL PRIMARY KEY, - user_id integer NOT NULL default '0', - del tinyint NOT NULL default '0', - cache_key varchar(128) NOT NULL default '', - created datetime NOT NULL default '0000-00-00 00:00:00', - idx integer NOT NULL default '0', - uid integer NOT NULL default '0', - subject varchar(255) NOT NULL default '', - "from" varchar(255) NOT NULL default '', - "to" varchar(255) NOT NULL default '', - "cc" varchar(255) NOT NULL default '', - "date" datetime NOT NULL default '0000-00-00 00:00:00', - size integer NOT NULL default '0', - headers text NOT NULL, - structure text +CREATE TABLE dictionary ( + user_id integer DEFAULT NULL, + "language" varchar(5) NOT NULL, + data text NOT NULL +); + +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); + +-- -------------------------------------------------------- + +-- +-- Table structure for table cache_index +-- + +CREATE TABLE cache_index ( + user_id integer NOT NULL, + mailbox varchar(255) NOT NULL, + changed datetime NOT NULL default '0000-00-00 00:00:00', + valid smallint NOT NULL DEFAULT '0', + data text NOT NULL, + PRIMARY KEY (user_id, mailbox) +); + +CREATE INDEX ix_cache_index_changed ON cache_index (changed); + +-- -------------------------------------------------------- + +-- +-- Table structure for table cache_thread +-- + +CREATE TABLE cache_thread ( + user_id integer NOT NULL, + mailbox varchar(255) NOT NULL, + changed datetime NOT NULL default '0000-00-00 00:00:00', + data text NOT NULL, + PRIMARY KEY (user_id, mailbox) +); + +CREATE INDEX ix_cache_thread_changed ON cache_thread (changed); + +-- -------------------------------------------------------- + +-- +-- Table structure for table cache_messages +-- + +CREATE TABLE cache_messages ( + user_id integer NOT NULL, + mailbox varchar(255) NOT NULL, + uid integer NOT NULL, + changed datetime NOT NULL default '0000-00-00 00:00:00', + data text NOT NULL, + flags integer NOT NULL DEFAULT '0', + PRIMARY KEY (user_id, mailbox, uid) ); -CREATE UNIQUE INDEX ix_messages_user_cache_uid ON messages (user_id,cache_key,uid); -CREATE INDEX ix_messages_index ON messages (user_id,cache_key,idx); -CREATE INDEX ix_messages_created ON messages (created); +CREATE INDEX ix_cache_messages_changed ON cache_messages (changed); diff --git a/SQL/sqlite.update.sql b/SQL/sqlite.update.sql index 30c3ae9..92f6da1 100644 --- a/SQL/sqlite.update.sql +++ b/SQL/sqlite.update.sql @@ -223,6 +223,75 @@ INSERT INTO contacts (contact_id, user_id, changed, del, name, email, firstname, CREATE INDEX ix_contacts_user_id ON contacts(user_id, email); DROP TABLE contacts_tmp; + DELETE FROM messages; DELETE FROM cache; CREATE INDEX ix_contactgroupmembers_contact_id ON contactgroupmembers (contact_id); + +-- Updates from version 0.6 + +CREATE TABLE dictionary ( + user_id integer DEFAULT NULL, + "language" varchar(5) NOT NULL, + data text NOT NULL +); + +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); + +DROP TABLE messages; + +CREATE TABLE cache_index ( + user_id integer NOT NULL, + mailbox varchar(255) NOT NULL, + changed datetime NOT NULL default '0000-00-00 00:00:00', + valid smallint NOT NULL DEFAULT '0', + data text NOT NULL, + PRIMARY KEY (user_id, mailbox) +); + +CREATE INDEX ix_cache_index_changed ON cache_index (changed); + +CREATE TABLE cache_thread ( + user_id integer NOT NULL, + mailbox varchar(255) NOT NULL, + changed datetime NOT NULL default '0000-00-00 00:00:00', + data text NOT NULL, + PRIMARY KEY (user_id, mailbox) +); + +CREATE INDEX ix_cache_thread_changed ON cache_thread (changed); + +CREATE TABLE cache_messages ( + user_id integer NOT NULL, + mailbox varchar(255) NOT NULL, + uid integer NOT NULL, + changed datetime NOT NULL default '0000-00-00 00:00:00', + data text NOT NULL, + flags integer NOT NULL DEFAULT '0', + PRIMARY KEY (user_id, mailbox, uid) +); + +CREATE INDEX ix_cache_messages_changed ON cache_messages (changed); + +-- Updates from version 0.7-beta + +DROP TABLE session; +CREATE TABLE session ( + sess_id varchar(128) NOT NULL PRIMARY KEY, + created datetime NOT NULL default '0000-00-00 00:00:00', + changed datetime NOT NULL default '0000-00-00 00:00:00', + ip varchar(40) NOT NULL default '', + vars text NOT NULL +); + +CREATE INDEX ix_session_changed ON session (changed); diff --git a/bin/indexcontacts.sh b/bin/indexcontacts.sh index d552be6..cbeffe9 100755 --- a/bin/indexcontacts.sh +++ b/bin/indexcontacts.sh @@ -16,14 +16,14 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: indexcontacts.sh 4623 2011-03-28 06:49:02Z alec $ + $Id: indexcontacts.sh 5307 2011-10-05 09:28:25Z alec $ */ define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/' ); require_once INSTALL_PATH.'program/include/clisetup.php'; - +ini_set('memory_limit', -1); // connect to DB $RCMAIL = rcmail::get_instance(); @@ -47,7 +47,7 @@ while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) { unset($row['words']); $contacts->update($row['ID'], $row); } - + echo "done.\n"; } diff --git a/bin/installto.sh b/bin/installto.sh index 33652dc..bcba57c 100755 --- a/bin/installto.sh +++ b/bin/installto.sh @@ -15,7 +15,7 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: installto.sh 4677 2011-04-20 13:10:45Z alec $ + $Id: installto.sh 5311 2011-10-06 08:20:11Z thomasb $ */ @@ -45,13 +45,13 @@ if (strtolower($input) == 'y') { $err = false; echo "Copying files to target location..."; foreach (array('program','installer','bin','SQL','plugins','skins/default') as $dir) { - if (!system("rsync -avuC " . INSTALL_PATH . "$dir/* $target_dir/$dir/")) { + if (!system("rsync -avC " . INSTALL_PATH . "$dir/* $target_dir/$dir/")) { $err = true; break; } } foreach (array('index.php','.htaccess','config/main.inc.php.dist','config/db.inc.php.dist','CHANGELOG','README','UPGRADING') as $file) { - if (!system("rsync -avu " . INSTALL_PATH . "$file $target_dir/$file")) { + if (!system("rsync -av " . INSTALL_PATH . "$file $target_dir/$file")) { $err = true; break; } diff --git a/bin/jsshrink.sh b/bin/jsshrink.sh index be5aad1..9cfd660 100755 --- a/bin/jsshrink.sh +++ b/bin/jsshrink.sh @@ -1,10 +1,11 @@ #!/bin/sh JS_DIR=`dirname "$0"`/../program/js +JAR_DIR='/tmp' CLOSURE_COMPILER_URL='http://closure-compiler.googlecode.com/files/compiler-latest.zip' do_shrink() { rm -f "$2" - java -jar compiler.jar --compilation_level=SIMPLE_OPTIMIZATIONS --js="$1" --js_output_file="$2" + java -jar $JAR_DIR/compiler.jar --compilation_level=SIMPLE_OPTIMIZATIONS --js="$1" --js_output_file="$2" } if [ ! -d "$JS_DIR" ]; then @@ -12,6 +13,10 @@ if [ ! -d "$JS_DIR" ]; then exit 1 fi +if [ ! -w "$JAR_DIR" ]; then + JAR_DIR=`dirname "$0"` +fi + if java -version >/dev/null 2>&1; then : else @@ -19,16 +24,16 @@ else exit 1 fi -if [ ! -r "compiler.jar" ]; then +if [ ! -r "$JAR_DIR/compiler.jar" ]; then if which wget >/dev/null 2>&1 && which unzip >/dev/null 2>&1; then wget "$CLOSURE_COMPILER_URL" -O "/tmp/$$.zip" elif which curl >/dev/null 2>&1 && which unzip >/dev/null 2>&1; then curl "$CLOSURE_COMPILER_URL" -o "/tmp/$$.zip" else - echo "Please download $CLOSURE_COMPILER_URL and extract compiler.jar to this directory." + echo "Please download $CLOSURE_COMPILER_URL and extract compiler.jar to $JAR_DIR/." exit 1 fi - unzip "/tmp/$$.zip" "compiler.jar" + (cd $JAR_DIR && unzip "/tmp/$$.zip" "compiler.jar") rm -f "/tmp/$$.zip" fi diff --git a/config/db.inc.php.dist b/config/db.inc.php.dist index 78cd968..c1464f9 100644 --- a/config/db.inc.php.dist +++ b/config/db.inc.php.dist @@ -39,34 +39,24 @@ $rcmail_config['db_persistent'] = FALSE; // you can define specific table names used to store webmail data $rcmail_config['db_table_users'] = 'users'; - $rcmail_config['db_table_identities'] = 'identities'; - $rcmail_config['db_table_contacts'] = 'contacts'; - $rcmail_config['db_table_contactgroups'] = 'contactgroups'; - $rcmail_config['db_table_contactgroupmembers'] = 'contactgroupmembers'; - $rcmail_config['db_table_session'] = 'session'; - $rcmail_config['db_table_cache'] = 'cache'; - -$rcmail_config['db_table_messages'] = 'messages'; +$rcmail_config['db_table_cache_index'] = 'cache_index'; +$rcmail_config['db_table_cache_thread'] = 'cache_thread'; +$rcmail_config['db_table_cache_messages'] = 'cache_messages'; // you can define specific sequence names used in PostgreSQL $rcmail_config['db_sequence_users'] = 'user_ids'; - $rcmail_config['db_sequence_identities'] = 'identity_ids'; - $rcmail_config['db_sequence_contacts'] = 'contact_ids'; - $rcmail_config['db_sequence_contactgroups'] = 'contactgroups_ids'; - $rcmail_config['db_sequence_cache'] = 'cache_ids'; - -$rcmail_config['db_sequence_messages'] = 'message_ids'; +$rcmail_config['db_sequence_searches'] = 'search_ids'; // end db config file diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 824085c..6957577 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -5,7 +5,7 @@ | Main configuration file | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2010, The Roundcube Dev Team | + | Copyright (C) 2005-2011, The Roundcube Dev Team | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ @@ -222,6 +222,9 @@ $rcmail_config['session_lifetime'] = 10; // session domain: .example.org $rcmail_config['session_domain'] = ''; +// session name. Default: 'roundcube_sessid' +$rcmail_config['session_name'] = null; + // Backend to use for session storage. Can either be 'db' (default) or 'memcache' // If set to memcache, a list of servers need to be specified in 'memcache_hosts' // Make sure the Memcache extension (http://pecl.php.net/package/memcache) version >= 2.0.0 is installed @@ -369,25 +372,30 @@ $rcmail_config['message_sort_col'] = ''; $rcmail_config['message_sort_order'] = 'DESC'; // These cols are shown in the message list. Available cols are: -// subject, from, to, cc, replyto, date, size, status, flag, attachment +// subject, from, to, cc, replyto, date, size, status, flag, attachment, 'priority' $rcmail_config['list_cols'] = array('subject', 'status', 'from', 'date', 'size', 'flag', 'attachment'); // the default locale setting (leave empty for auto-detection) // RFC1766 formatted language name like en_US, de_DE, de_CH, fr_FR, pt_BR $rcmail_config['language'] = null; -// use this format for short date display (date or strftime format) -$rcmail_config['date_short'] = 'D H:i'; +// use this format for date display (date or strftime format) +$rcmail_config['date_format'] = 'Y-m-d'; -// use this format for detailed date/time formatting (date or strftime format) -$rcmail_config['date_long'] = 'd.m.Y H:i'; +// give this choice of date formats to the user to select from +$rcmail_config['date_formats'] = array('Y-m-d', 'd-m-Y', 'Y/m/d', 'm/d/Y', 'd/m/Y', 'd.m.Y', 'j.n.Y'); -// use this format for today's date display (date or strftime format) -// Note: $ character will be replaced with 'Today' label -$rcmail_config['date_today'] = 'H:i'; +// use this format for time display (date or strftime format) +$rcmail_config['time_format'] = 'H:i'; -// use this format for date display without time (date or strftime format) -$rcmail_config['date_format'] = 'Y-m-d'; +// give this choice of time formats to the user to select from +$rcmail_config['time_formats'] = array('G:i', 'H:i', 'g:i a', 'h:i A'); + +// use this format for short date display (derived from date_format and time_format) +$rcmail_config['date_short'] = 'D H:i'; + +// use this format for detailed date/time formatting (derived from date_format and time_format) +$rcmail_config['date_long'] = 'Y-m-d H:i'; // store draft message is this mailbox // leave blank if draft messages should not be stored @@ -413,7 +421,7 @@ $rcmail_config['trash_mbox'] = 'Trash'; // NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP) $rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash'); -// automatically create the above listed default folders on login +// automatically create the above listed default folders on first login $rcmail_config['create_default_folders'] = false; // protect the default folders from renames, deletes, and subscription changes @@ -427,6 +435,10 @@ $rcmail_config['quota_zero_as_unlimited'] = false; // requires to be compiled with Open SSL support $rcmail_config['enable_spellcheck'] = true; +// Enables spellchecker exceptions dictionary. +// Setting it to 'shared' will make the dictionary shared by all users. +$rcmail_config['spellcheck_dictionary'] = false; + // Set the spell checking engine. 'googie' is the default. 'pspell' is also available, // but requires the Pspell extensions. When using Nox Spell Server, also set 'googie' here. $rcmail_config['spellcheck_engine'] = 'googie'; @@ -442,6 +454,18 @@ $rcmail_config['spellcheck_uri'] = ''; // Leave empty for default set of available language. $rcmail_config['spellcheck_languages'] = NULL; +// Makes that words with all letters capitalized will be ignored (e.g. GOOGLE) +$rcmail_config['spellcheck_ignore_caps'] = false; + +// Makes that words with numbers will be ignored (e.g. g00gle) +$rcmail_config['spellcheck_ignore_nums'] = false; + +// Makes that words with symbols will be ignored (e.g. g@@gle) +$rcmail_config['spellcheck_ignore_syms'] = false; + +// Use this char/string to separate recipients when composing a new message +$rcmail_config['recipients_separator'] = ','; + // don't let users set pagesize to more than this value if set $rcmail_config['max_pagesize'] = 200; @@ -517,14 +541,21 @@ $rcmail_config['ldap_public']['Verisign'] = array( // The login name is used to search for the DN to bind with 'search_base_dn' => '', 'search_filter' => '', // e.g. '(&(objectClass=posixAccount)(uid=%u))' + // DN and password to bind as before searching for bind DN, if anonymous search is not allowed + 'search_bind_dn' => '', + 'search_bind_pw' => '', + // Default for %dn variable if search doesn't return DN value + 'search_dn_default' => '', // Optional authentication identifier to be used as SASL authorization proxy // bind_dn need to be empty 'auth_cid' => '', // SASL authentication method (for proxy auth), e.g. DIGEST-MD5 'auth_method' => '', - // Indicates if the addressbook shall be displayed on the list. + // Indicates if the addressbook shall be hidden from the list. // With this option enabled you can still search/view contacts. 'hidden' => false, + // Indicates if the addressbook shall not list contacts but only allows searching. + 'searchonly' => false, // Indicates if we can write to the LDAP directory or not. // If writable is true then these fields need to be populated: // LDAP_Object_Classes, required_fields, LDAP_rdn @@ -564,6 +595,7 @@ $rcmail_config['ldap_public']['Verisign'] = array( 'numsub_filter' => '(objectClass=organizationalUnit)', // with VLV, we also use numSubOrdinates to query the total number of records. Set this filter to get all numSubOrdinates attributes for counting 'sizelimit' => '0', // Enables you to limit the count of entries fetched. Setting this to 0 means no limit. 'timelimit' => '0', // Sets the number of seconds how long is spend on the search. Setting this to 0 means no limit. + 'referrals' => true|false, // Sets the LDAP_OPT_REFERRALS option. Mostly used in multi-domain Active Directory setups // definition for contact groups (uncomment if no groups are supported) // for the groups base_dn, the user replacements %fu, %u, $d and %dc work as for base_dn (see above) @@ -573,8 +605,8 @@ $rcmail_config['ldap_public']['Verisign'] = array( 'base_dn' => '', 'filter' => '(objectClass=groupOfNames)', 'object_classes' => array("top", "groupOfNames"), - // name of the member attribute, e.g. uniqueMember - 'member_attr' => 'member', + 'member_attr' => 'member', // name of the member attribute, e.g. uniqueMember + 'name_attr' => 'cn', // attribute to be used as group name ), ); */ @@ -601,6 +633,13 @@ $rcmail_config['autocomplete_max'] = 15; // available placeholders: {street}, {locality}, {zipcode}, {country}, {region} $rcmail_config['address_template'] = '{street}
{locality} {zipcode}
{country} {region}'; +// Matching mode for addressbook search (including autocompletion) +// 0 - partial (*abc*), default +// 1 - strict (abc) +// 2 - prefix (abc*) +// Note: For LDAP sources fuzzy_search must be enabled to use 'partial' or 'prefix' mode +$rcmail_config['addressbook_search_mode'] = 0; + // ---------------------------------- // USER PREFERENCES // ---------------------------------- @@ -617,8 +656,8 @@ $rcmail_config['pagesize'] = 40; // use this timezone to display date/time $rcmail_config['timezone'] = 'auto'; -// is daylight saving On? -$rcmail_config['dst_active'] = (bool)date('I'); +// is daylight saving On? Default: (bool)date('I'); +$rcmail_config['dst_active'] = null; // prefer displaying HTML messages $rcmail_config['prefer_html'] = true; @@ -748,4 +787,7 @@ $rcmail_config['default_addressbook'] = null; // Enables spell checking before sending a message. $rcmail_config['spellcheck_before_send'] = false; +// Skip alternative email addresses in autocompletion (show one address per contact) +$rcmail_config['autocomplete_single'] = false; + // end of config file diff --git a/index.php b/index.php index e3e55d7..1a7fceb 100644 --- a/index.php +++ b/index.php @@ -2,7 +2,7 @@ /* +-------------------------------------------------------------------------+ | Roundcube Webmail IMAP Client | - | Version 0.6 | + | Version 0.7 | | | | Copyright (C) 2005-2011, The Roundcube Dev Team | | | @@ -23,7 +23,7 @@ | Author: Thomas Bruederli | +-------------------------------------------------------------------------+ - $Id: index.php 5292 2011-09-28 19:16:41Z thomasb $ + $Id: index.php 5582 2011-12-09 08:55:40Z thomasb $ */ @@ -33,6 +33,9 @@ require_once 'program/include/iniset.php'; // init application, start session, init output class, etc. $RCMAIL = rcmail::get_instance(); +// Make the whole PHP output non-cacheable (#1487797) +send_nocacheing_headers(); + // turn on output buffering ob_start(); @@ -177,7 +180,7 @@ if (empty($RCMAIL->user->ID)) { ) ); } - + if ($session_error || $_REQUEST['_err'] == 'session') $OUTPUT->show_message('sessionerror', 'error', null, true, -1); @@ -192,7 +195,7 @@ else { // check client X-header to verify request origin if ($OUTPUT->ajax_call) { if (rc_request_header('X-Roundcube-Request') != $RCMAIL->get_request_token() && !$RCMAIL->config->get('devel_mode')) { - header('HTTP/1.1 404 Not Found'); + header('HTTP/1.1 403 Forbidden'); die("Invalid Request"); } } @@ -211,6 +214,12 @@ else { } } +// we're ready, user is authenticated and the request is safe +$plugin = $RCMAIL->plugins->exec_hook('ready', array('task' => $RCMAIL->task, 'action' => $RCMAIL->action)); +$RCMAIL->set_task($plugin['task']); +$RCMAIL->action = $plugin['action']; + + // handle special actions if ($RCMAIL->action == 'keep-alive') { $OUTPUT->reset(); diff --git a/installer/rcube_install.php b/installer/rcube_install.php index ff3f7a4..dbe662a 100644 --- a/installer/rcube_install.php +++ b/installer/rcube_install.php @@ -142,20 +142,22 @@ class rcube_install foreach ($this->config as $prop => $default) { - $value = (isset($_POST["_$prop"]) || $this->bool_config_props[$prop]) ? $_POST["_$prop"] : $default; + $is_default = !isset($_POST["_$prop"]); + $value = !$is_default || $this->bool_config_props[$prop] ? $_POST["_$prop"] : $default; // convert some form data - if ($prop == 'debug_level') { - $val = 0; - if (is_array($value)) + if ($prop == 'debug_level' && !$is_default) { + if (is_array($value)) { + $val = 0; foreach ($value as $dbgval) $val += intval($dbgval); - $value = $val; + $value = $val; + } } else if ($which == 'db' && $prop == 'db_dsnw' && !empty($_POST['_dbtype'])) { if ($_POST['_dbtype'] == 'sqlite') $value = sprintf('%s://%s?mode=0646', $_POST['_dbtype'], $_POST['_dbname']{0} == '/' ? '/' . $_POST['_dbname'] : $_POST['_dbname']); - else + else if ($_POST['_dbtype']) $value = sprintf('%s://%s:%s@%s/%s', $_POST['_dbtype'], rawurlencode($_POST['_dbuser']), rawurlencode($_POST['_dbpass']), $_POST['_dbhost'], $_POST['_dbname']); } @@ -177,9 +179,9 @@ class rcube_install $value = '%p'; } else if ($prop == 'default_imap_folders') { - $value = Array(); + $value = array(); foreach ($this->config['default_imap_folders'] as $_folder) { - switch($_folder) { + switch ($_folder) { case 'Drafts': $_folder = $this->config['drafts_mbox']; break; case 'Sent': $_folder = $this->config['sent_mbox']; break; case 'Junk': $_folder = $this->config['junk_mbox']; break; @@ -206,7 +208,7 @@ class rcube_install // replace the matching line in config file $out = preg_replace( '/(\$rcmail_config\[\''.preg_quote($prop).'\'\])\s+=\s+(.+);/Uie', - "'\\1 = ' . rcube_install::_dump_var(\$value) . ';'", + "'\\1 = ' . rcube_install::_dump_var(\$value, \$prop) . ';'", $out); } @@ -299,7 +301,7 @@ class rcube_install $current = $this->config; $this->config = array(); $this->load_defaults(); - + foreach ($this->replaced_config as $prop => $replacement) { if (isset($current[$prop])) { if ($prop == 'skin_path') @@ -328,9 +330,9 @@ class rcube_install if ($current['keep_alive'] && $current['session_lifetime'] < $current['keep_alive']) $current['session_lifetime'] = max(10, ceil($current['keep_alive'] / 60) * 2); - + $this->config = array_merge($this->config, $current); - + foreach ((array)$current['ldap_public'] as $key => $values) { $this->config['ldap_public'][$key] = $current['ldap_public'][$key]; } @@ -614,7 +616,22 @@ class rcube_install } - static function _dump_var($var) { + static function _dump_var($var, $name=null) { + // special values + switch ($name) { + case 'syslog_facility': + $list = array(32 => 'LOG_AUTH', 80 => 'LOG_AUTHPRIV', 72 => ' LOG_CRON', + 24 => 'LOG_DAEMON', 0 => 'LOG_KERN', 128 => 'LOG_LOCAL0', + 136 => 'LOG_LOCAL1', 144 => 'LOG_LOCAL2', 152 => 'LOG_LOCAL3', + 160 => 'LOG_LOCAL4', 168 => 'LOG_LOCAL5', 176 => 'LOG_LOCAL6', + 184 => 'LOG_LOCAL7', 48 => 'LOG_LPR', 16 => 'LOG_MAIL', + 56 => 'LOG_NEWS', 40 => 'LOG_SYSLOG', 8 => 'LOG_USER', 64 => 'LOG_UUCP'); + if ($val = $list[$var]) + return $val; + break; + } + + if (is_array($var)) { if (empty($var)) { return 'array()'; diff --git a/installer/test.php b/installer/test.php index 02a1ceb..2dd3305 100644 --- a/installer/test.php +++ b/installer/test.php @@ -382,18 +382,20 @@ $pass_field = new html_passwordfield(array('name' => '_pass', 'id' => 'imappass' Connecting to ' . Q($_POST['_host']) . '...
'; - - $a_host = parse_url($_POST['_host']); + + $imap_host = trim($_POST['_host']); + $imap_port = $RCI->getprop('default_port'); + $a_host = parse_url($imap_host); + if ($a_host['host']) { $imap_host = $a_host['host']; - $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null; - $imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : $CONFIG['default_port']); - } - else { - $imap_host = trim($_POST['_host']); - $imap_port = $RCI->getprop('default_port'); + $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null; + if (isset($a_host['port'])) + $imap_port = $a_host['port']; + else if ($imap_ssl && $imap_ssl != 'tls' && (!$imap_port || $imap_port == 143)) + $imap_port = 993; } $imap_host = idn_to_ascii($imap_host); diff --git a/plugins/acl/acl.js b/plugins/acl/acl.js index 4b1431a..c4011a8 100644 --- a/plugins/acl/acl.js +++ b/plugins/acl/acl.js @@ -1,7 +1,7 @@ /** * ACL plugin script * - * @version 0.6.1 + * @version 0.6.3 * @author Aleksander Machniak */ @@ -315,6 +315,9 @@ rcube_webmail.prototype.acl_init_form = function(id) this.acl_form.show(); if (type == 'user') name_input.focus(); + + // unfocus the list, make backspace key in name input field working + this.acl_list.blur(); } // Returns class name according to ACL comparision result diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php index 976b362..3a5fd1a 100644 --- a/plugins/acl/acl.php +++ b/plugins/acl/acl.php @@ -3,7 +3,7 @@ /** * Folders Access Control Lists Management (RFC4314, RFC2086) * - * @version 0.6.1 + * @version @package_version@ * @author Aleksander Machniak * * @@ -87,11 +87,15 @@ class acl extends rcube_plugin $this->load_config(); $search = get_input_value('_search', RCUBE_INPUT_GPC, true); + $sid = get_input_value('_id', RCUBE_INPUT_GPC); $users = array(); if ($this->init_ldap()) { - $this->ldap->set_pagesize(15); - $result = $this->ldap->search('*', $search); + $max = (int) $this->rc->config->get('autocomplete_max', 15); + $mode = (int) $this->rc->config->get('addressbook_search_mode'); + + $this->ldap->set_pagesize($max); + $result = $this->ldap->search('*', $search, $mode); foreach ($result->records as $record) { $user = $record['uid']; @@ -112,7 +116,7 @@ class acl extends rcube_plugin sort($users, SORT_LOCALE_STRING); - $this->rc->output->command('ksearch_query_results', $users, $search); + $this->rc->output->command('ksearch_query_results', $users, $search, $sid); $this->rc->output->send(); } @@ -186,6 +190,10 @@ class acl extends rcube_plugin 'aclrights' => array($this, 'templ_rights'), )); + $this->rc->output->set_env('autocomplete_max', (int)$this->rc->config->get('autocomplete_max', 15)); + $this->rc->output->set_env('autocomplete_min_length', $this->rc->config->get('autocomplete_min_length')); + $this->rc->output->add_label('autocompletechars', 'autocompletemore'); + $args['form']['sharing'] = array( 'name' => Q($this->gettext('sharing')), 'content' => $this->rc->output->parse('acl.table', false, false), @@ -619,7 +627,6 @@ class acl extends rcube_plugin $acl = $this->rc->imap->get_acl('INBOX'); if (is_array($acl)) { $regexp = '/^' . preg_quote($_SESSION['username'], '/') . '@(.*)$/'; - $regexp = '/^' . preg_quote('aleksander.machniak', '/') . '@(.*)$/'; foreach (array_keys($acl) as $name) { if (preg_match($regexp, $name, $matches)) { $domain = $matches[1]; diff --git a/plugins/acl/skins/default/acl.css b/plugins/acl/skins/default/acl.css index e46a1d0..cf3391f 100644 --- a/plugins/acl/skins/default/acl.css +++ b/plugins/acl/skins/default/acl.css @@ -61,6 +61,12 @@ background-color: #CC3333; } +#acltable tr.unfocused td +{ + color: #FFFFFF; + background-color: #929292; +} + #acladvswitch { position: absolute; diff --git a/plugins/archive/archive.js b/plugins/archive/archive.js index a837508..5c576e1 100644 --- a/plugins/archive/archive.js +++ b/plugins/archive/archive.js @@ -27,7 +27,9 @@ if (window.rcmail) { // set css style for archive folder var li; - if (rcmail.env.archive_folder && rcmail.env.archive_folder_icon && (li = rcmail.get_folder_li(rcmail.env.archive_folder))) + if (rcmail.env.archive_folder && rcmail.env.archive_folder_icon + && (li = rcmail.get_folder_li(rcmail.env.archive_folder, '', true)) + ) $(li).css('background-image', 'url(' + rcmail.env.archive_folder_icon + ')'); }) } diff --git a/plugins/archive/archive.php b/plugins/archive/archive.php index 843b612..a568062 100644 --- a/plugins/archive/archive.php +++ b/plugins/archive/archive.php @@ -100,7 +100,7 @@ class archive extends rcube_plugin // load folders list when needed if ($CURR_SECTION) $select = rcmail_mailbox_select(array('noselection' => '---', 'realnames' => true, - 'maxlength' => 30, 'exceptions' => array('INBOX'))); + 'maxlength' => 30, 'exceptions' => array('INBOX'), 'folder_filter' => 'mail', 'folder_rights' => 'w')); else $select = new html_select(); diff --git a/plugins/archive/localization/fr_FR.inc b/plugins/archive/localization/fr_FR.inc index f44f30f..498a091 100644 --- a/plugins/archive/localization/fr_FR.inc +++ b/plugins/archive/localization/fr_FR.inc @@ -2,7 +2,7 @@ $labels = array(); $labels['buttontitle'] = 'Archiver ce message'; -$labels['archived'] = 'Message archivé avec success'; +$labels['archived'] = 'Message archivé avec success'; $labels['archivefolder'] = 'Archive'; ?> diff --git a/plugins/archive/package.xml b/plugins/archive/package.xml index c442a5c..c549fc9 100644 --- a/plugins/archive/package.xml +++ b/plugins/archive/package.xml @@ -13,10 +13,9 @@ roundcube@gmail.com yes - 2010-02-06 - + 2011-11-23 - 1.4 + 1.5 1.4 @@ -35,14 +34,21 @@ - + + + + + + + + diff --git a/plugins/enigma/README b/plugins/enigma/README index afb2322..22d6e51 100644 --- a/plugins/enigma/README +++ b/plugins/enigma/README @@ -18,7 +18,7 @@ Enigma Plugin Status: - Parsing of decrypted messages into array (see rcube_mime_struct) and then into rcube_message_part structure (create core class rcube_mime_parser or take over PEAR::Mail_mimeDecode package and improve it) - Sending encrypted/signed messages (probably some changes in core will be needed) -- Per-Identity settings (including keys/certs) (+ split Identities details page into tabs) +- Per-Identity settings (including keys/certs) - Handling big messages with temp files (including changes in Roundcube core) - Performance improvements (some caching, code review) - better (and more) icons diff --git a/plugins/enigma/config.inc.php b/plugins/enigma/config.inc.php deleted file mode 100644 index ca841d0..0000000 --- a/plugins/enigma/config.inc.php +++ /dev/null @@ -1,14 +0,0 @@ -enigma->add_button(array( diff --git a/plugins/http_authentication/http_authentication.php b/plugins/http_authentication/http_authentication.php index fa074f0..be49b52 100644 --- a/plugins/http_authentication/http_authentication.php +++ b/plugins/http_authentication/http_authentication.php @@ -53,7 +53,7 @@ class http_authentication extends rcube_plugin return $args; } - + function logout($args) { // redirect to configured URL in order to clear HTTP auth credentials diff --git a/plugins/managesieve/Changelog b/plugins/managesieve/Changelog index e354064..0550612 100644 --- a/plugins/managesieve/Changelog +++ b/plugins/managesieve/Changelog @@ -1,4 +1,36 @@ +- Fixed setting test type to :is when none is specified +- Fixed javascript error in IE8 + +* version 5.0-rc1 [2011-11-17] +----------------------------------------------------------- +- Fixed sorting of scripts, scripts including aware of the sort order +- Fixed import of rules with unsupported tests +- Added 'address' and 'envelope' tests support +- Added 'body' extension support (RFC5173) +- Added 'subaddress' extension support (RFC5233) +- Added comparators support +- Changed Sender/Recipient labels to From/To +- Fixed importing rule names from Ingo +- Fixed handling of extensions disabled in config + +* version 5.0-beta [2011-10-17] +----------------------------------------------------------- +- Added possibility to create a filter based on selected message "in-place" - Fixed import from Horde-INGO (#1488064) +- Add managesieve_script_name option for default name of the script (#1487956) +- Fixed handling of enabled magic_quotes_gpc setting +- Fixed PHP warning on connection error when submitting filter form +- Fixed bug where new action row with flags wasn't handled properly +- Added managesieve_connect hook for plugins +- Fixed doubled Filter tab on page refresh +- Added filters set selector in filter form when invoked in mail task +- Improved script parser, added support for include and variables extensions +- Added Kolab's KEP:14 support (http://wiki.kolab.org/User:Greve/Drafts/KEP:14) +- Use smaller action/rule buttons +- UI redesign: added possibility to move filter to any place using drag&drop + (instead of up/down buttons), added filter sets list object, added more + 'loading' messages +- Added option to hide some scripts (managesieve_filename_exceptions) * version 4.3 [2011-07-28] ----------------------------------------------------------- diff --git a/plugins/managesieve/config.inc.php.dist b/plugins/managesieve/config.inc.php.dist index 905cfef..cb9b2a9 100644 --- a/plugins/managesieve/config.inc.php.dist +++ b/plugins/managesieve/config.inc.php.dist @@ -31,6 +31,9 @@ $rcmail_config['managesieve_usetls'] = false; // default contents of filters script (eg. default spam filter) $rcmail_config['managesieve_default'] = '/etc/dovecot/sieve/global'; +// The name of the script which will be used when there's no user script +$rcmail_config['managesieve_script_name'] = 'managesieve'; + // Sieve RFC says that we should use UTF-8 endcoding for mailbox names, // but some implementations does not covert UTF-8 to modified UTF-7. // Defaults to UTF7-IMAP @@ -50,4 +53,15 @@ $rcmail_config['managesieve_disabled_extensions'] = array(); // Enables debugging of conversation with sieve server. Logs it into /sieve $rcmail_config['managesieve_debug'] = false; +// Enables features described in http://wiki.kolab.org/KEP:14 +$rcmail_config['managesieve_kolab_master'] = false; + +// Script name extension used for scripts including. Dovecot uses '.sieve', +// Cyrus uses '.siv'. Doesn't matter if you have managesieve_kolab_master disabled. +$rcmail_config['managesieve_filename_extension'] = '.sieve'; + +// List of reserved script names (without extension). +// Scripts listed here will be not presented to the user. +$rcmail_config['managesieve_filename_exceptions'] = array(); + ?> diff --git a/plugins/managesieve/lib/rcube_sieve.php b/plugins/managesieve/lib/rcube_sieve.php index 3e52809..7c4f0aa 100644 --- a/plugins/managesieve/lib/rcube_sieve.php +++ b/plugins/managesieve/lib/rcube_sieve.php @@ -1,13 +1,27 @@ - - $Id: rcube_sieve.php 5203 2011-09-12 06:44:56Z alec $ - -*/ + * Classes for managesieve operations (using PEAR::Net_Sieve) + * + * Copyright (C) 2008-2011, The Roundcube Dev Team + * Copyright (C) 2011, Kolab Systems AG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: rcube_sieve.php 5452 2011-11-18 14:44:48Z alec $ + * + */ // Managesieve Protocol: RFC5804 @@ -30,7 +44,6 @@ class rcube_sieve public $script; // rcube_sieve_script object public $current; // name of currently loaded script - private $disabled; // array of disabled extensions private $exts; // array of supported extensions @@ -75,7 +88,17 @@ class rcube_sieve } $this->exts = $this->get_extensions(); - $this->disabled = $disabled; + + // disable features by config + if (!empty($disabled)) { + // we're working on lower-cased names + $disabled = array_map('strtolower', (array) $disabled); + foreach ($disabled as $ext) { + if (($idx = array_search($ext, $this->exts)) !== false) { + unset($this->exts[$idx]); + } + } + } } public function __destruct() { @@ -195,7 +218,7 @@ class rcube_sieve { if ($this->exts) return $this->exts; - + if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); @@ -286,23 +309,28 @@ class rcube_sieve */ private function _parse($txt) { - // try to parse from Roundcube format - $script = new rcube_sieve_script($txt, $this->disabled, $this->exts); - - // ... else try to import from different formats - if (empty($script->content)) { - $script = $this->_import_rules($txt); - $script = new rcube_sieve_script($script, $this->disabled, $this->exts); + // parse + $script = new rcube_sieve_script($txt, $this->exts); + // fix/convert to Roundcube format + if (!empty($script->content)) { // replace all elsif with if+stop, we support only ifs foreach ($script->content as $idx => $rule) { + if (empty($rule['type']) || !preg_match('/^(if|elsif|else)$/', $rule['type'])) { + continue; + } + + $script->content[$idx]['type'] = 'if'; + // 'stop' not found? foreach ($rule['actions'] as $action) { if (preg_match('/^(stop|vacation)$/', $action['type'])) { continue 2; } } - $script->content[$idx]['actions'][] = array('type' => 'stop'); + if (empty($script->content[$idx+1]) || $script->content[$idx+1]['type'] != 'if') { + $script->content[$idx]['actions'][] = array('type' => 'stop'); + } } } @@ -343,48 +371,6 @@ class rcube_sieve return $this->save_script($name, $content); } - private function _import_rules($script) - { - $i = 0; - $name = array(); - - // Squirrelmail (Avelsieve) - if (preg_match('/(#START_SIEVE_RULE.*END_SIEVE_RULE)\r?\n/', $script)) { - $tokens = preg_split('/(#START_SIEVE_RULE.*END_SIEVE_RULE)\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE); - foreach ($tokens as $token) { - if (preg_match('/^#START_SIEVE_RULE.*/', $token, $matches)) { - $name[$i] = "unnamed rule ".($i+1); - $content .= "# rule:[".$name[$i]."]\n"; - } - elseif (isset($name[$i])) { - // This preg_replace is added because I've found some Avelsieve scripts - // with rules containing "if" here. I'm not sure it was working - // before without this or not. - $token = preg_replace('/^if\s+/', '', trim($token)); - $content .= "if $token\n"; - $i++; - } - } - } - // Horde (INGO) - else if (preg_match('/(# .+)\r?\n/', $script)) { - $tokens = preg_split('/(# .+)\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE); - foreach($tokens as $token) { - if (preg_match('/^# (.+)/', $token, $matches)) { - $name[$i] = $matches[1]; - $content .= "# rule:[" . $name[$i] . "]\n"; - } - elseif (isset($name[$i])) { - $token = str_replace(":comparator \"i;ascii-casemap\" ", "", $token); - $content .= $token . "\n"; - $i++; - } - } - } - - return $content; - } - private function _set_error($error) { $this->error = $error; diff --git a/plugins/managesieve/lib/rcube_sieve_script.php b/plugins/managesieve/lib/rcube_sieve_script.php index 871fb14..04bcc4c 100644 --- a/plugins/managesieve/lib/rcube_sieve_script.php +++ b/plugins/managesieve/lib/rcube_sieve_script.php @@ -1,20 +1,37 @@ - - $Id: rcube_sieve_script.php 4806 2011-05-24 08:32:01Z alec $ - -*/ + * Class for operations on Sieve scripts + * + * Copyright (C) 2008-2011, The Roundcube Dev Team + * Copyright (C) 2011, Kolab Systems AG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: rcube_sieve_script.php 5452 2011-11-18 14:44:48Z alec $ + * + */ class rcube_sieve_script { public $content = array(); // script rules array - private $supported = array( // extensions supported by class - 'fileinto', // RFC3028 + private $vars = array(); // "global" variables + private $prefix = ''; // script header (comments) + private $supported = array( // Sieve extensions supported by class + 'fileinto', // RFC5228 + 'envelope', // RFC5228 'reject', // RFC5429 'ereject', // RFC5429 'copy', // RFC3894 @@ -23,57 +40,34 @@ class rcube_sieve_script 'regex', // draft-ietf-sieve-regex-01 'imapflags', // draft-melnikov-sieve-imapflags-06 'imap4flags', // RFC5232 - // TODO: body, notify + 'include', // draft-ietf-sieve-include-12 + 'variables', // RFC5229 + 'body', // RFC5173 + 'subaddress', // RFC5233 + // @TODO: enotify/notify, spamtest+virustest, mailbox, date ); - private $capabilities; - /** * Object constructor * * @param string Script's text content - * @param array List of disabled extensions * @param array List of capabilities supported by server */ - public function __construct($script, $disabled=null, $capabilities=null) + public function __construct($script, $capabilities=array()) { - if (!empty($disabled)) { - // we're working on lower-cased names - $disabled = array_map('strtolower', (array) $disabled); - foreach ($disabled as $ext) { - if (($idx = array_search($ext, $this->supported)) !== false) { + $capabilities = array_map('strtolower', (array) $capabilities); + + // disable features by server capabilities + if (!empty($capabilities)) { + foreach ($this->supported as $idx => $ext) { + if (!in_array($ext, $capabilities)) { unset($this->supported[$idx]); } } } - $this->capabilities = $capabilities; - $this->content = $this->_parse_text($script); - } - - /** - * Adds script contents as text to the script array (at the end) - * - * @param string Text script contents - */ - public function add_text($script) - { - $content = $this->_parse_text($script); - $result = false; - - // check existsing script rules names - foreach ($this->content as $idx => $elem) { - $names[$elem['name']] = $idx; - } - - foreach ($content as $elem) { - if (!isset($names[$elem['name']])) { - array_push($this->content, $elem); - $result = true; - } - } - - return $result; + // Parse text content of the script + $this->_parse_text($script); } /** @@ -81,6 +75,8 @@ class rcube_sieve_script * * @param string Rule name * @param array Rule content (as array) + * + * @return int The index of the new rule */ public function add_rule($content) { @@ -113,162 +109,344 @@ class rcube_sieve_script return false; } + /** + * Sets "global" variable + * + * @param string $name Variable name + * @param string $value Variable value + * @param array $mods Variable modifiers + */ + public function set_var($name, $value, $mods = array()) + { + // Check if variable exists + for ($i=0, $len=count($this->vars); $i<$len; $i++) { + if ($this->vars[$i]['name'] == $name) { + break; + } + } + + $var = array_merge($mods, array('name' => $name, 'value' => $value)); + $this->vars[$i] = $var; + } + + /** + * Unsets "global" variable + * + * @param string $name Variable name + */ + public function unset_var($name) + { + // Check if variable exists + foreach ($this->vars as $idx => $var) { + if ($var['name'] == $name) { + unset($this->vars[$idx]); + break; + } + } + } + + /** + * Gets the value of "global" variable + * + * @param string $name Variable name + * + * @return string Variable value + */ + public function get_var($name) + { + // Check if variable exists + for ($i=0, $len=count($this->vars); $i<$len; $i++) { + if ($this->vars[$i]['name'] == $name) { + return $this->vars[$i]['name']; + } + } + } + + /** + * Sets script header content + * + * @param string $text Header content + */ + public function set_prefix($text) + { + $this->prefix = $text; + } + /** * Returns script as text */ public function as_text() { - $script = ''; - $exts = array(); - $idx = 0; + $output = ''; + $exts = array(); + $idx = 0; + + if (!empty($this->vars)) { + if (in_array('variables', (array)$this->supported)) { + $has_vars = true; + array_push($exts, 'variables'); + } + foreach ($this->vars as $var) { + if (empty($has_vars)) { + // 'variables' extension not supported, put vars in comments + $output .= sprintf("# %s %s\n", $var['name'], $var['value']); + } + else { + $output .= 'set '; + foreach (array_diff(array_keys($var), array('name', 'value')) as $opt) { + $output .= ":$opt "; + } + $output .= self::escape_string($var['name']) . ' ' . self::escape_string($var['value']) . ";\n"; + } + } + } // rules foreach ($this->content as $rule) { $extension = ''; - $tests = array(); - $i = 0; + $script = ''; + $tests = array(); + $i = 0; // header - $script .= '# rule:[' . $rule['name'] . "]\n"; + if (!empty($rule['name']) && strlen($rule['name'])) { + $script .= '# rule:[' . $rule['name'] . "]\n"; + } // constraints expressions - foreach ($rule['tests'] as $test) { - $tests[$i] = ''; - switch ($test['test']) { - case 'size': - $tests[$i] .= ($test['not'] ? 'not ' : ''); - $tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg']; - break; - case 'true': - $tests[$i] .= ($test['not'] ? 'false' : 'true'); - break; - case 'exists': - $tests[$i] .= ($test['not'] ? 'not ' : ''); - $tests[$i] .= 'exists ' . self::escape_string($test['arg']); - break; - case 'header': - $tests[$i] .= ($test['not'] ? 'not ' : ''); + if (!empty($rule['tests'])) { + foreach ($rule['tests'] as $test) { + $tests[$i] = ''; + switch ($test['test']) { + case 'size': + $tests[$i] .= ($test['not'] ? 'not ' : ''); + $tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg']; + break; - // relational operator + comparator - if (preg_match('/^(value|count)-([gteqnl]{2})/', $test['type'], $m)) { - array_push($exts, 'relational'); - array_push($exts, 'comparator-i;ascii-numeric'); + case 'true': + $tests[$i] .= ($test['not'] ? 'false' : 'true'); + break; - $tests[$i] .= 'header :' . $m[1] . ' "' . $m[2] . '" :comparator "i;ascii-numeric"'; - } - else { - if ($test['type'] == 'regex') { - array_push($exts, 'regex'); + case 'exists': + $tests[$i] .= ($test['not'] ? 'not ' : ''); + $tests[$i] .= 'exists ' . self::escape_string($test['arg']); + break; + + case 'header': + $tests[$i] .= ($test['not'] ? 'not ' : ''); + $tests[$i] .= 'header'; + + if (!empty($test['type'])) { + // relational operator + comparator + if (preg_match('/^(value|count)-([gteqnl]{2})/', $test['type'], $m)) { + array_push($exts, 'relational'); + array_push($exts, 'comparator-i;ascii-numeric'); + + $tests[$i] .= ' :' . $m[1] . ' "' . $m[2] . '" :comparator "i;ascii-numeric"'; + } + else { + $this->add_comparator($test, $tests[$i], $exts); + + if ($test['type'] == 'regex') { + array_push($exts, 'regex'); + } + + $tests[$i] .= ' :' . $test['type']; + } } - $tests[$i] .= 'header :' . $test['type']; - } + $tests[$i] .= ' ' . self::escape_string($test['arg1']); + $tests[$i] .= ' ' . self::escape_string($test['arg2']); + break; - $tests[$i] .= ' ' . self::escape_string($test['arg1']); - $tests[$i] .= ' ' . self::escape_string($test['arg2']); - break; + case 'address': + case 'envelope': + if ($test['test'] == 'envelope') { + array_push($exts, 'envelope'); + } + + $tests[$i] .= ($test['not'] ? 'not ' : ''); + $tests[$i] .= $test['test']; + + if (!empty($test['part'])) { + $tests[$i] .= ' :' . $test['part']; + if ($test['part'] == 'user' || $test['part'] == 'detail') { + array_push($exts, 'subaddress'); + } + } + + $this->add_comparator($test, $tests[$i], $exts); + + if (!empty($test['type'])) { + if ($test['type'] == 'regex') { + array_push($exts, 'regex'); + } + $tests[$i] .= ' :' . $test['type']; + } + + $tests[$i] .= ' ' . self::escape_string($test['arg1']); + $tests[$i] .= ' ' . self::escape_string($test['arg2']); + break; + + case 'body': + array_push($exts, 'body'); + + $tests[$i] .= ($test['not'] ? 'not ' : '') . 'body'; + + $this->add_comparator($test, $tests[$i], $exts); + + if (!empty($test['part'])) { + $tests[$i] .= ' :' . $test['part']; + + if (!empty($test['content']) && $test['part'] == 'content') { + $tests[$i] .= ' ' . self::escape_string($test['content']); + } + } + + if (!empty($test['type'])) { + if ($test['type'] == 'regex') { + array_push($exts, 'regex'); + } + $tests[$i] .= ' :' . $test['type']; + } + + $tests[$i] .= ' ' . self::escape_string($test['arg']); + break; + } + $i++; } - $i++; } // disabled rule: if false #.... - $script .= 'if ' . ($rule['disabled'] ? 'false # ' : ''); + if (!empty($tests)) { + $script .= 'if ' . ($rule['disabled'] ? 'false # ' : ''); - if (empty($tests)) { - $tests_str = 'true'; - } - else if (count($tests) > 1) { - $tests_str = implode(', ', $tests); - } - else { - $tests_str = $tests[0]; - } + if (count($tests) > 1) { + $tests_str = implode(', ', $tests); + } + else { + $tests_str = $tests[0]; + } - if ($rule['join'] || count($tests) > 1) { - $script .= sprintf('%s (%s)', $rule['join'] ? 'allof' : 'anyof', $tests_str); - } - else { - $script .= $tests_str; + if ($rule['join'] || count($tests) > 1) { + $script .= sprintf('%s (%s)', $rule['join'] ? 'allof' : 'anyof', $tests_str); + } + else { + $script .= $tests_str; + } + $script .= "\n{\n"; } - $script .= "\n{\n"; // action(s) - foreach ($rule['actions'] as $action) { - switch ($action['type']) { + if (!empty($rule['actions'])) { + foreach ($rule['actions'] as $action) { + $action_script = ''; + + switch ($action['type']) { + + case 'fileinto': + array_push($exts, 'fileinto'); + $action_script .= 'fileinto '; + if ($action['copy']) { + $action_script .= ':copy '; + array_push($exts, 'copy'); + } + $action_script .= self::escape_string($action['target']); + break; - case 'fileinto': - array_push($exts, 'fileinto'); - $script .= "\tfileinto "; - if ($action['copy']) { - $script .= ':copy '; - array_push($exts, 'copy'); - } - $script .= self::escape_string($action['target']) . ";\n"; - break; + case 'redirect': + $action_script .= 'redirect '; + if ($action['copy']) { + $action_script .= ':copy '; + array_push($exts, 'copy'); + } + $action_script .= self::escape_string($action['target']); + break; - case 'redirect': - $script .= "\tredirect "; - if ($action['copy']) { - $script .= ':copy '; - array_push($exts, 'copy'); - } - $script .= self::escape_string($action['target']) . ";\n"; - break; + case 'reject': + case 'ereject': + array_push($exts, $action['type']); + $action_script .= $action['type'].' ' + . self::escape_string($action['target']); + break; - case 'reject': - case 'ereject': - array_push($exts, $action['type']); - $script .= "\t".$action['type']." " - . self::escape_string($action['target']) . ";\n"; - break; + case 'addflag': + case 'setflag': + case 'removeflag': + if (in_array('imap4flags', $this->supported)) + array_push($exts, 'imap4flags'); + else + array_push($exts, 'imapflags'); - case 'addflag': - case 'setflag': - case 'removeflag': - if (is_array($this->capabilities) && in_array('imap4flags', $this->capabilities)) - array_push($exts, 'imap4flags'); - else - array_push($exts, 'imapflags'); + $action_script .= $action['type'].' ' + . self::escape_string($action['target']); + break; - $script .= "\t".$action['type']." " - . self::escape_string($action['target']) . ";\n"; - break; + case 'keep': + case 'discard': + case 'stop': + $action_script .= $action['type']; + break; - case 'keep': - case 'discard': - case 'stop': - $script .= "\t" . $action['type'] .";\n"; - break; + case 'include': + array_push($exts, 'include'); + $action_script .= 'include '; + foreach (array_diff(array_keys($action), array('target', 'type')) as $opt) { + $action_script .= ":$opt "; + } + $action_script .= self::escape_string($action['target']); + break; - case 'vacation': - array_push($exts, 'vacation'); - $script .= "\tvacation"; - if (!empty($action['days'])) - $script .= " :days " . $action['days']; - if (!empty($action['addresses'])) - $script .= " :addresses " . self::escape_string($action['addresses']); - if (!empty($action['subject'])) - $script .= " :subject " . self::escape_string($action['subject']); - if (!empty($action['handle'])) - $script .= " :handle " . self::escape_string($action['handle']); - if (!empty($action['from'])) - $script .= " :from " . self::escape_string($action['from']); - if (!empty($action['mime'])) - $script .= " :mime"; - $script .= " " . self::escape_string($action['reason']) . ";\n"; - break; + case 'set': + array_push($exts, 'variables'); + $action_script .= 'set '; + foreach (array_diff(array_keys($action), array('name', 'value', 'type')) as $opt) { + $action_script .= ":$opt "; + } + $action_script .= self::escape_string($action['name']) . ' ' . self::escape_string($action['value']); + break; + + case 'vacation': + array_push($exts, 'vacation'); + $action_script .= 'vacation'; + if (!empty($action['days'])) + $action_script .= " :days " . $action['days']; + if (!empty($action['addresses'])) + $action_script .= " :addresses " . self::escape_string($action['addresses']); + if (!empty($action['subject'])) + $action_script .= " :subject " . self::escape_string($action['subject']); + if (!empty($action['handle'])) + $action_script .= " :handle " . self::escape_string($action['handle']); + if (!empty($action['from'])) + $action_script .= " :from " . self::escape_string($action['from']); + if (!empty($action['mime'])) + $action_script .= " :mime"; + $action_script .= " " . self::escape_string($action['reason']); + break; + } + + if ($action_script) { + $script .= !empty($tests) ? "\t" : ''; + $script .= $action_script . ";\n"; + } } } - $script .= "}\n"; - $idx++; + if ($script) { + $output .= $script . (!empty($tests) ? "}\n" : ''); + $idx++; + } } // requires if (!empty($exts)) - $script = 'require ["' . implode('","', array_unique($exts)) . "\"];\n" . $script; + $output = 'require ["' . implode('","', array_unique($exts)) . "\"];\n" . $output; - return $script; + if (!empty($this->prefix)) { + $output = $this->prefix . "\n\n" . $output; + } + + return $output; } /** @@ -296,35 +474,89 @@ class rcube_sieve_script */ private function _parse_text($script) { - $i = 0; - $content = array(); + $prefix = ''; + $options = array(); + + while ($script) { + $script = trim($script); + $rule = array(); + + // Comments + while (!empty($script) && $script[0] == '#') { + $endl = strpos($script, "\n"); + $line = $endl ? substr($script, 0, $endl) : $script; + + // Roundcube format + if (preg_match('/^# rule:\[(.*)\]/', $line, $matches)) { + $rulename = $matches[1]; + } + // KEP:14 variables + else if (preg_match('/^# (EDITOR|EDITOR_VERSION) (.+)$/', $line, $matches)) { + $this->set_var($matches[1], $matches[2]); + } + // Horde-Ingo format + else if (!empty($options['format']) && $options['format'] == 'INGO' + && preg_match('/^# (.*)/', $line, $matches) + ) { + $rulename = $matches[1]; + } + else if (empty($options['prefix'])) { + $prefix .= $line . "\n"; + } + + $script = ltrim(substr($script, strlen($line) + 1)); + } + + // handle script header + if (empty($options['prefix'])) { + $options['prefix'] = true; + if ($prefix && strpos($prefix, 'horde.org/ingo')) { + $options['format'] = 'INGO'; + } + } - // tokenize rules - if ($tokens = preg_split('/(# rule:\[.*\])\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) { - foreach($tokens as $token) { - if (preg_match('/^# rule:\[(.*)\]/', $token, $matches)) { - $content[$i]['name'] = $matches[1]; + // Control structures/blocks + if (preg_match('/^(if|else|elsif)/i', $script)) { + $rule = $this->_tokenize_rule($script); + if (strlen($rulename) && !empty($rule)) { + $rule['name'] = $rulename; } - else if (isset($content[$i]['name']) && sizeof($content[$i]) == 1) { - if ($rule = $this->_tokenize_rule($token)) { - $content[$i] = array_merge($content[$i], $rule); - $i++; + } + // Simple commands + else { + $rule = $this->_parse_actions($script, ';'); + if (!empty($rule[0]) && is_array($rule)) { + // set "global" variables + if ($rule[0]['type'] == 'set') { + unset($rule[0]['type']); + $this->vars[] = $rule[0]; + } + else { + $rule = array('actions' => $rule); } - else // unknown rule format - unset($content[$i]); } } + + $rulename = ''; + + if (!empty($rule)) { + $this->content[] = $rule; + } } - return $content; + if (!empty($prefix)) { + $this->prefix = trim($prefix); + } } /** * Convert text script fragment to rule object * * @param string Text rule + * + * @return array Rule data */ - private function _tokenize_rule($content) + private function _tokenize_rule(&$content) { $cond = strtolower(self::tokenize($content, 1)); @@ -389,7 +621,7 @@ class rcube_sieve_script $header = array('test' => 'header', 'not' => $not, 'arg1' => '', 'arg2' => ''); for ($i=0, $len=count($tokens); $i<$len; $i++) { if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) { - $i++; + $header['comparator'] = $tokens[++$i]; } else if (!is_array($tokens[$i]) && preg_match('/^:(count|value)$/i', $tokens[$i])) { $header['type'] = strtolower(substr($tokens[$i], 1)) . '-' . $tokens[++$i]; @@ -406,6 +638,52 @@ class rcube_sieve_script $tests[] = $header; break; + case 'address': + case 'envelope': + $header = array('test' => $token, 'not' => $not, 'arg1' => '', 'arg2' => ''); + for ($i=0, $len=count($tokens); $i<$len; $i++) { + if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) { + $header['comparator'] = $tokens[++$i]; + } + else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) { + $header['type'] = strtolower(substr($tokens[$i], 1)); + } + else if (!is_array($tokens[$i]) && preg_match('/^:(localpart|domain|all|user|detail)$/i', $tokens[$i])) { + $header['part'] = strtolower(substr($tokens[$i], 1)); + } + else { + $header['arg1'] = $header['arg2']; + $header['arg2'] = $tokens[$i]; + } + } + + $tests[] = $header; + break; + + case 'body': + $header = array('test' => 'body', 'not' => $not, 'arg' => ''); + for ($i=0, $len=count($tokens); $i<$len; $i++) { + if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) { + $header['comparator'] = $tokens[++$i]; + } + else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) { + $header['type'] = strtolower(substr($tokens[$i], 1)); + } + else if (!is_array($tokens[$i]) && preg_match('/^:(raw|content|text)$/i', $tokens[$i])) { + $header['part'] = strtolower(substr($tokens[$i], 1)); + + if ($header['part'] == 'content') { + $header['content'] = $tokens[++$i]; + } + } + else { + $header['arg'] = $tokens[$i]; + } + } + + $tests[] = $header; + break; + case 'exists': $tests[] = array('test' => 'exists', 'not' => $not, 'arg' => array_pop($tokens)); @@ -427,9 +705,7 @@ class rcube_sieve_script } // ...and actions block - if ($tests) { - $actions = $this->_parse_actions($content); - } + $actions = $this->_parse_actions($content); if ($tests && $actions) { $result = array( @@ -447,10 +723,12 @@ class rcube_sieve_script /** * Parse body of actions section * - * @param string Text body + * @param string $content Text body + * @param string $end End of text separator + * * @return array Array of parsed action type/target pairs */ - private function _parse_actions($content) + private function _parse_actions(&$content, $end = '}') { $result = null; @@ -531,12 +809,72 @@ class rcube_sieve_script 'target' => $tokens[count($tokens)-1] ); break; + + case 'include': + $include = array('type' => 'include', 'target' => array_pop($tokens)); + + // Parameters: :once, :optional, :global, :personal + for ($i=0, $len=count($tokens); $i<$len; $i++) { + $tok = strtolower($tokens[$i]); + if ($tok[0] == ':') { + $include[substr($tok, 1)] = true; + } + } + + $result[] = $include; + break; + + case 'set': + $set = array('type' => 'set', 'value' => array_pop($tokens), 'name' => array_pop($tokens)); + + // Parameters: :lower :upper :lowerfirst :upperfirst :quotewildcard :length + for ($i=0, $len=count($tokens); $i<$len; $i++) { + $tok = strtolower($tokens[$i]); + if ($tok[0] == ':') { + $set[substr($tok, 1)] = true; + } + } + + $result[] = $set; + break; + + case 'require': + // skip, will be build according to used commands + // $result[] = array('type' => 'require', 'target' => $tokens); + break; + } + + if ($separator == $end) + break; } return $result; } + /** + * + */ + private function add_comparator($test, &$out, &$exts) + { + if (empty($test['comparator'])) { + return; + } + + if ($test['comparator'] == 'i;ascii-numeric') { + array_push($exts, 'relational'); + array_push($exts, 'comparator-i;ascii-numeric'); + } + else if (!in_array($test['comparator'], array('i;octet', 'i;ascii-casemap'))) { + array_push($exts, 'comparator-' . $test['comparator']); + } + + // skip default comparator + if ($test['comparator'] != 'i;ascii-casemap') { + $out .= ' :comparator ' . self::escape_string($test['comparator']); + } + } + /** * Escape special chars into quoted string value or multi-line string * or list of strings @@ -595,7 +933,7 @@ class rcube_sieve_script * @param mixed $num Number of tokens to return, 0 for all * or True for all tokens until separator is found. * Separator will be returned as last token. - * @param int $in_list Enable to called recursively inside a list + * @param int $in_list Enable to call recursively inside a list * * @return mixed Tokens array or string if $num=1 */ @@ -654,7 +992,7 @@ class rcube_sieve_script $str = substr($str, 1); if ($num === true) { $result[] = $sep; - break 2; + break 2; } break; @@ -684,7 +1022,7 @@ class rcube_sieve_script // String atom default: // empty or one character - if ($str === '') { + if ($str === '' || $str === null) { break 2; } if (strlen($str) < 2) { diff --git a/plugins/managesieve/localization/de_CH.inc b/plugins/managesieve/localization/de_CH.inc index c0fe389..1fcef1e 100644 --- a/plugins/managesieve/localization/de_CH.inc +++ b/plugins/managesieve/localization/de_CH.inc @@ -1,5 +1,20 @@ | + +-----------------------------------------------------------------------+ + @version : importgettext.sh 5558 2011-12-07 08:51:01Z thomasb $ +*/ + +$labels = array(); $labels['filters'] = 'Filter'; $labels['managefilters'] = 'Verwalte eingehende Nachrichtenfilter'; $labels['filtername'] = 'Filtername'; @@ -17,12 +32,18 @@ $labels['filteris'] = 'ist gleich'; $labels['filterisnot'] = 'ist ungleich'; $labels['filterexists'] = 'ist vorhanden'; $labels['filternotexists'] = 'nicht vorhanden'; +$labels['filtermatches'] = 'entspricht Ausdruck'; +$labels['filternotmatches'] = 'entspricht nicht Ausdruck'; +$labels['filterregex'] = 'trifft regulären Ausdruck'; +$labels['filternotregex'] = 'entspricht regulärem Ausdruck'; $labels['filterunder'] = 'unter'; $labels['filterover'] = 'über'; $labels['addrule'] = 'Regel hinzufügen'; $labels['delrule'] = 'Regel löschen'; $labels['messagemoveto'] = 'Verschiebe Nachricht nach'; $labels['messageredirect'] = 'Leite Nachricht um nach'; +$labels['messagecopyto'] = 'Kopiere Nachricht nach'; +$labels['messagesendcopy'] = 'Sende Kopie an'; $labels['messagereply'] = 'Antworte mit Nachricht'; $labels['messagedelete'] = 'Nachricht löschen'; $labels['messagediscard'] = 'Discard with message'; @@ -35,18 +56,64 @@ $labels['recipient'] = 'Empfänger'; $labels['vacationaddresses'] = 'Zusätzliche Liste von Empfängern (Komma getrennt):'; $labels['vacationdays'] = 'Antwort wird erneut gesendet nach (in Tagen):'; $labels['vacationreason'] = 'Inhalt der Nachricht (Abwesenheitsgrund):'; +$labels['vacationsubject'] = 'Betreff'; $labels['rulestop'] = 'Regelauswertung anhalten'; +$labels['enable'] = 'Aktivieren/Deaktivieren'; +$labels['filterset'] = 'Filtersätze'; +$labels['filtersets'] = 'Filtersätze'; +$labels['filtersetadd'] = 'Filtersatz anlegen'; +$labels['filtersetdel'] = 'Aktuellen Filtersatz löschen'; +$labels['filtersetact'] = 'Aktuellen Filtersatz aktivieren'; +$labels['filtersetdeact'] = 'Aktuellen Filtersatz deaktivieren'; +$labels['filterdef'] = 'Filterdefinition'; +$labels['filtersetname'] = 'Filtersatzname'; +$labels['newfilterset'] = 'Neuer Filtersatz'; +$labels['active'] = 'aktiv'; +$labels['none'] = 'keine'; +$labels['fromset'] = 'aus Filtersatz'; +$labels['fromfile'] = 'aus Datei'; +$labels['filterdisabled'] = 'Filter deaktiviert'; +$labels['countisgreaterthan'] = 'Anzahl ist grösser als'; +$labels['countisgreaterthanequal'] = 'Anzahl ist gleich oder grösser als'; +$labels['countislessthan'] = 'Anzahl ist kleiner als'; +$labels['countislessthanequal'] = 'Anzahl ist gleich oder kleiner als'; +$labels['countequals'] = 'Anzahl ist gleich'; +$labels['countnotequals'] = 'Anzahl ist ungleich'; +$labels['valueisgreaterthan'] = 'Wert ist grösser als'; +$labels['valueisgreaterthanequal'] = 'Wert ist gleich oder grösser als'; +$labels['valueislessthan'] = 'Wert ist kleiner'; +$labels['valueislessthanequal'] = 'Wert ist gleich oder kleiner als'; +$labels['valueequals'] = 'Wert ist gleich'; +$labels['valuenotequals'] = 'Wert ist ungleich'; +$labels['setflags'] = 'Setze Markierungen'; +$labels['addflags'] = 'Füge Markierung hinzu'; +$labels['removeflags'] = 'Entferne Markierung'; +$labels['flagread'] = 'gelesen'; +$labels['flagdeleted'] = 'Gelöscht'; +$labels['flaganswered'] = 'Beantwortet'; +$labels['flagflagged'] = 'Markiert'; +$labels['flagdraft'] = 'Entwurf'; +$labels['filtercreate'] = 'Filter erstellen'; +$labels['usedata'] = 'Die folgenden Daten im Filter benutzen:'; +$labels['nextstep'] = 'Nächster Schritt'; +$labels['...'] = ''; +$labels['advancedopts'] = 'Erweiterte Optionen'; +$labels['body'] = 'Inhalt'; +$labels['address'] = 'Adresse'; +$labels['envelope'] = 'Umschlag'; +$labels['modifier'] = 'Wandler'; +$labels['text'] = 'Text'; +$labels['undecoded'] = 'kodiert (roh)'; +$labels['contenttype'] = 'Inhaltstyp'; +$labels['modtype'] = 'Typ:'; +$labels['allparts'] = 'alle'; +$labels['domain'] = 'Domain'; +$labels['localpart'] = 'lokaler Teil'; +$labels['user'] = 'Benutzer'; +$labels['detail'] = 'Detail'; +$labels['comparator'] = 'Komparator'; +$labels['default'] = 'Vorgabewert'; +$labels['octet'] = 'strikt (Oktet)'; +$labels['asciicasemap'] = 'Gross-/Kleinschreibung ignorieren'; +$labels['asciinumeric'] = 'numerisch (ascii-numeric)'; -$messages['filterunknownerror'] = 'Unbekannter Serverfehler'; -$messages['filterconnerror'] = 'Kann nicht zum Sieve-Server verbinden'; -$messages['filterdeleteerror'] = 'Fehler beim des löschen Filters. Serverfehler'; -$messages['filterdeleted'] = 'Filter erfolgreich gelöscht'; -$messages['filterdeleteconfirm'] = 'Möchten Sie den Filter löschen ?'; -$messages['filtersaved'] = 'Filter gespeichert'; -$messages['filtersaveerror'] = 'Serverfehler, konnte den Filter nicht speichern.'; -$messages['ruledeleteconfirm'] = 'Sicher, dass Sie die Regel löschen wollen?'; -$messages['actiondeleteconfirm'] = 'Sicher, dass Sie die ausgewaehlte Aktion löschen wollen?'; -$messages['forbiddenchars'] = 'Unerlaubte Zeichen im Feld'; -$messages['cannotbeempty'] = 'Feld darf nicht leer sein'; - -?> diff --git a/plugins/managesieve/localization/de_DE.inc b/plugins/managesieve/localization/de_DE.inc index e71d7e0..d78220c 100644 --- a/plugins/managesieve/localization/de_DE.inc +++ b/plugins/managesieve/localization/de_DE.inc @@ -1,9 +1,24 @@ | + +-----------------------------------------------------------------------+ + @version : importgettext.sh 5558 2011-12-07 08:51:01Z thomasb $ +*/ + +$labels = array(); $labels['filters'] = 'Filter'; -$labels['managefilters'] = 'Posteingangs-Filter verwalten'; +$labels['managefilters'] = 'Filter für eingehende Nachrichten verwalten'; $labels['filtername'] = 'Filtername'; -$labels['newfilter'] = 'Filter anlegen'; +$labels['newfilter'] = 'Neuer Filter'; $labels['filteradd'] = 'Filter hinzufügen'; $labels['filterdel'] = 'Filter löschen'; $labels['moveup'] = 'Nach oben'; @@ -17,17 +32,21 @@ $labels['filteris'] = 'ist gleich'; $labels['filterisnot'] = 'ist ungleich'; $labels['filterexists'] = 'existiert'; $labels['filternotexists'] = 'existiert nicht'; +$labels['filtermatches'] = 'trifft auf Ausdruck zu'; +$labels['filternotmatches'] = 'trifft nicht auf Ausdruck zu'; +$labels['filterregex'] = 'trifft auf regulären Ausdruck zu'; +$labels['filternotregex'] = 'trifft nicht auf regulären Ausdruck zu'; $labels['filterunder'] = 'unter'; $labels['filterover'] = 'über'; $labels['addrule'] = 'Regel hinzufügen'; $labels['delrule'] = 'Regel löschen'; -$labels['messagemoveto'] = 'Verschiebe Nachricht nach'; -$labels['messageredirect'] = 'Leite Nachricht um an'; -$labels['messagecopyto'] = 'Kopiere Nachricht nach'; -$labels['messagesendcopy'] = 'Sende Kopie an'; -$labels['messagereply'] = 'Antworte mit Nachricht'; -$labels['messagedelete'] = 'Lösche Nachricht'; -$labels['messagediscard'] = 'Weise ab mit Nachricht'; +$labels['messagemoveto'] = 'Nachricht verschieben nach'; +$labels['messageredirect'] = 'Nachricht umleiten an'; +$labels['messagecopyto'] = 'Nachricht kopieren nach'; +$labels['messagesendcopy'] = 'Kopie senden an'; +$labels['messagereply'] = 'Mit Nachricht antworten'; +$labels['messagedelete'] = 'Nachricht löschen'; +$labels['messagediscard'] = 'Abweisen mit Nachricht'; $labels['messagesrules'] = 'Für eingehende Nachrichten:'; $labels['messagesactions'] = '...führende folgende Aktionen aus:'; $labels['add'] = 'Hinzufügen'; @@ -37,13 +56,15 @@ $labels['recipient'] = 'Empfänger'; $labels['vacationaddresses'] = 'Zusätzliche Liste von E-Mail Empfängern (Komma getrennt):'; $labels['vacationdays'] = 'Wie oft sollen Nachrichten gesendet werden (in Tagen):'; $labels['vacationreason'] = 'Nachrichteninhalt (Abwesenheitsgrund):'; +$labels['vacationsubject'] = 'Nachrichtenbetreff'; $labels['rulestop'] = 'Regelauswertung anhalten'; +$labels['enable'] = 'Aktivieren/Deaktivieren'; $labels['filterset'] = 'Filtersätze'; +$labels['filtersets'] = 'Filtersätze'; $labels['filtersetadd'] = 'Filtersatz anlegen'; $labels['filtersetdel'] = 'Aktuellen Filtersatz löschen'; $labels['filtersetact'] = 'Aktuellen Filtersatz aktivieren'; $labels['filtersetdeact'] = 'Aktuellen Filtersatz deaktivieren'; -$labels['filtersetget'] = 'Filtersatz im Textformat herunterladen'; $labels['filterdef'] = 'Filterdefinition'; $labels['filtersetname'] = 'Filtersatzname'; $labels['newfilterset'] = 'Neuer Filtersatz'; @@ -64,29 +85,35 @@ $labels['valueislessthan'] = 'Wert ist kleiner'; $labels['valueislessthanequal'] = 'Wert ist gleich oder kleiner als'; $labels['valueequals'] = 'Wert ist gleich'; $labels['valuenotequals'] = 'Wert ist ungleich'; +$labels['setflags'] = 'Markierung an der Nachricht setzen'; +$labels['addflags'] = 'Markierung zur Nachricht hinzufügen'; +$labels['removeflags'] = 'Markierungen von der Nachricht entfernen'; +$labels['flagread'] = 'Gelesen'; +$labels['flagdeleted'] = 'Gelöscht'; +$labels['flaganswered'] = 'Beantwortet'; +$labels['flagflagged'] = 'Markiert'; +$labels['flagdraft'] = 'Entwurf'; +$labels['filtercreate'] = 'Filter erstellen'; +$labels['usedata'] = 'Die folgenden Daten im Filter benutzen:'; +$labels['nextstep'] = 'Nächster Schritt'; +$labels['...'] = '...'; +$labels['advancedopts'] = 'Erweiterte Optionen'; +$labels['body'] = 'Textkörper'; +$labels['address'] = 'Adresse'; +$labels['envelope'] = 'Umschlag'; +$labels['modifier'] = 'Modifikator:'; +$labels['text'] = 'Text'; +$labels['undecoded'] = 'Nicht dekodiert'; +$labels['contenttype'] = 'Inhaltstyp'; +$labels['modtype'] = 'Typ:'; +$labels['allparts'] = 'Alle'; +$labels['domain'] = 'Domäne'; +$labels['localpart'] = 'lokaler Teil'; +$labels['user'] = 'Benutzer'; +$labels['detail'] = 'Detail'; +$labels['comparator'] = 'Komperator:'; +$labels['default'] = 'Vorgabewert'; +$labels['octet'] = 'strikt (Oktett)'; +$labels['asciicasemap'] = 'Groß-/Kleinschreibung ignorieren'; +$labels['asciinumeric'] = 'numerisch (ascii-numeric)'; -$messages = array(); -$messages['filterunknownerror'] = 'Unbekannter Serverfehler'; -$messages['filterconnerror'] = 'Kann keine Verbindung mit Managesieve-Server herstellen'; -$messages['filterdeleteerror'] = 'Fehler beim Löschen des Filters. Serverfehler'; -$messages['filterdeleted'] = 'Filter erfolgreich gelöscht'; -$messages['filtersaved'] = 'Filter erfolgreich gespeichert'; -$messages['filtersaveerror'] = 'Fehler beim Speichern des Filters. Serverfehler'; -$messages['filterdeleteconfirm'] = 'Möchten Sie den ausgewählten Filter wirklich löschen?'; -$messages['ruledeleteconfirm'] = 'Sind Sie sicher, dass Sie die ausgewählte Regel löschen möchten?'; -$messages['actiondeleteconfirm'] = 'Sind Sie sicher, dass Sie die ausgewählte Aktion löschen möchten?'; -$messages['forbiddenchars'] = 'Unzulässige Zeichen im Eingabefeld'; -$messages['cannotbeempty'] = 'Eingabefeld darf nicht leer sein'; -$messages['setactivateerror'] = 'Kann ausgewählten Filtersatz nicht aktivieren. Serverfehler'; -$messages['setdeactivateerror'] = 'Kann ausgewählten Filtersatz nicht deaktivieren. Serverfehler'; -$messages['setdeleteerror'] = 'Kann ausgewählten Filtersatz nicht löschen. Serverfehler'; -$messages['setactivated'] = 'Filtersatz wurde erfolgreich aktiviert'; -$messages['setdeactivated'] = 'Filtersatz wurde erfolgreich deaktiviert'; -$messages['setdeleted'] = 'Filtersatz wurde erfolgreich gelöscht'; -$messages['setdeleteconfirm'] = 'Sind Sie sicher, dass Sie den ausgewählten Filtersatz löschen möchten?'; -$messages['setcreateerror'] = 'Kann Filtersatz nicht erstellen. Serverfehler'; -$messages['setcreated'] = 'Filtersatz wurde erfolgreich erstellt'; -$messages['emptyname'] = 'Kann Filtersatz nicht erstellen. Kein Name vergeben'; -$messages['nametoolong'] = 'Kann Filtersatz nicht erstellen. Name zu lang' - -?> diff --git a/plugins/managesieve/localization/en_US.inc b/plugins/managesieve/localization/en_US.inc index f08357e..94e0ba6 100644 --- a/plugins/managesieve/localization/en_US.inc +++ b/plugins/managesieve/localization/en_US.inc @@ -43,12 +43,13 @@ $labels['vacationdays'] = 'How often send messages (in days):'; $labels['vacationreason'] = 'Message body (vacation reason):'; $labels['vacationsubject'] = 'Message subject:'; $labels['rulestop'] = 'Stop evaluating rules'; +$labels['enable'] = 'Enable/Disable'; $labels['filterset'] = 'Filters set'; +$labels['filtersets'] = 'Filter sets'; $labels['filtersetadd'] = 'Add filters set'; $labels['filtersetdel'] = 'Delete current filters set'; $labels['filtersetact'] = 'Activate current filters set'; $labels['filtersetdeact'] = 'Deactivate current filters set'; -$labels['filtersetget'] = 'Download filters set in text format'; $labels['filterdef'] = 'Filter definition'; $labels['filtersetname'] = 'Filters set name'; $labels['newfilterset'] = 'New filters set'; @@ -77,29 +78,61 @@ $labels['flagdeleted'] = 'Deleted'; $labels['flaganswered'] = 'Answered'; $labels['flagflagged'] = 'Flagged'; $labels['flagdraft'] = 'Draft'; +$labels['filtercreate'] = 'Create filter'; +$labels['usedata'] = 'Use following data in the filter:'; +$labels['nextstep'] = 'Next Step'; +$labels['...'] = '...'; +$labels['advancedopts'] = 'Advanced options'; +$labels['body'] = 'Body'; +$labels['address'] = 'address'; +$labels['envelope'] = 'envelope'; +$labels['modifier'] = 'modifier:'; +$labels['text'] = 'text'; +$labels['undecoded'] = 'undecoded (raw)'; +$labels['contenttype'] = 'content type'; +$labels['modtype'] = 'type:'; +$labels['allparts'] = 'all'; +$labels['domain'] = 'domain'; +$labels['localpart'] = 'local part'; +$labels['user'] = 'user'; +$labels['detail'] = 'detail'; +$labels['comparator'] = 'comparator:'; +$labels['default'] = 'default'; +$labels['octet'] = 'strict (octet)'; +$labels['asciicasemap'] = 'case insensitive (ascii-casemap)'; +$labels['asciinumeric'] = 'numeric (ascii-numeric)'; $messages = array(); -$messages['filterunknownerror'] = 'Unknown server error'; -$messages['filterconnerror'] = 'Unable to connect to managesieve server'; -$messages['filterdeleteerror'] = 'Unable to delete filter. Server error occured'; -$messages['filterdeleted'] = 'Filter deleted successfully'; -$messages['filtersaved'] = 'Filter saved successfully'; -$messages['filtersaveerror'] = 'Unable to save filter. Server error occured'; +$messages['filterunknownerror'] = 'Unknown server error.'; +$messages['filterconnerror'] = 'Unable to connect to server.'; +$messages['filterdeleteerror'] = 'Unable to delete filter. Server error occured.'; +$messages['filterdeleted'] = 'Filter deleted successfully.'; +$messages['filtersaved'] = 'Filter saved successfully.'; +$messages['filtersaveerror'] = 'Unable to save filter. Server error occured.'; $messages['filterdeleteconfirm'] = 'Do you really want to delete selected filter?'; $messages['ruledeleteconfirm'] = 'Are you sure, you want to delete selected rule?'; $messages['actiondeleteconfirm'] = 'Are you sure, you want to delete selected action?'; -$messages['forbiddenchars'] = 'Forbidden characters in field'; -$messages['cannotbeempty'] = 'Field cannot be empty'; -$messages['setactivateerror'] = 'Unable to activate selected filters set. Server error occured'; -$messages['setdeactivateerror'] = 'Unable to deactivate selected filters set. Server error occured'; -$messages['setdeleteerror'] = 'Unable to delete selected filters set. Server error occured'; -$messages['setactivated'] = 'Filters set activated successfully'; -$messages['setdeactivated'] = 'Filters set deactivated successfully'; -$messages['setdeleted'] = 'Filters set deleted successfully'; +$messages['forbiddenchars'] = 'Forbidden characters in field.'; +$messages['cannotbeempty'] = 'Field cannot be empty.'; +$messages['ruleexist'] = 'Filter with specified name already exists.'; +$messages['setactivateerror'] = 'Unable to activate selected filters set. Server error occured.'; +$messages['setdeactivateerror'] = 'Unable to deactivate selected filters set. Server error occured.'; +$messages['setdeleteerror'] = 'Unable to delete selected filters set. Server error occured.'; +$messages['setactivated'] = 'Filters set activated successfully.'; +$messages['setdeactivated'] = 'Filters set deactivated successfully.'; +$messages['setdeleted'] = 'Filters set deleted successfully.'; $messages['setdeleteconfirm'] = 'Are you sure, you want to delete selected filters set?'; -$messages['setcreateerror'] = 'Unable to create filters set. Server error occured'; -$messages['setcreated'] = 'Filters set created successfully'; -$messages['emptyname'] = 'Unable to create filters set. Empty set name'; -$messages['nametoolong'] = 'Unable to create filters set. Name too long' +$messages['setcreateerror'] = 'Unable to create filters set. Server error occured.'; +$messages['setcreated'] = 'Filters set created successfully.'; +$messages['activateerror'] = 'Unable to enable selected filter(s). Server error occured.'; +$messages['deactivateerror'] = 'Unable to disable selected filter(s). Server error occured.'; +$messages['activated'] = 'Filter(s) disabled successfully.'; +$messages['deactivated'] = 'Filter(s) enabled successfully.'; +$messages['moved'] = 'Filter moved successfully.'; +$messages['moveerror'] = 'Unable to move selected filter. Server error occured.'; +$messages['nametoolong'] = 'Name too long.'; +$messages['namereserved'] = 'Reserved name.'; +$messages['setexist'] = 'Set already exists.'; +$messages['nodata'] = 'At least one position must be selected!'; ?> diff --git a/plugins/managesieve/localization/es_ES.inc b/plugins/managesieve/localization/es_ES.inc index 1dad18d..b6dbf16 100644 --- a/plugins/managesieve/localization/es_ES.inc +++ b/plugins/managesieve/localization/es_ES.inc @@ -53,6 +53,31 @@ $labels['none'] = 'ninguno'; $labels['fromset'] = 'de conjunto '; $labels['fromfile'] = 'de archivo'; $labels['filterdisabled'] = 'Filtro desactivado'; +$labels['filtermatches'] = 'coincide con la expresión'; +$labels['filternotmatches'] = 'no coincide con la expresión'; +$labels['filterregex'] = 'coincide con la expresión regular'; +$labels['filternotregex'] = 'no coincide con la expresión regular'; +$labels['vacationsubject'] = 'Asunto del Mensaje:'; +$labels['countisgreaterthan'] = 'contiene más que'; +$labels['countisgreaterthanequal'] = 'contiene más o igual que'; +$labels['countislessthan'] = 'contiene menos que'; +$labels['countislessthanequal'] = 'contiene menos o igual que'; +$labels['countequals'] = 'contiene igual que'; +$labels['countnotequals'] = 'contiene distinto que'; +$labels['valueisgreaterthan'] = 'el valor es mayor que'; +$labels['valueisgreaterthanequal'] = 'el valor es mayor o igual que'; +$labels['valueislessthan'] = 'el valor es menor que'; +$labels['valueislessthanequal'] = 'el valor es menor o igual que'; +$labels['valueequals'] = 'el valor es igual que'; +$labels['valuenotequals'] = 'el valor es distinto que'; +$labels['setflags'] = 'Etiquetar el mensaje'; +$labels['addflags'] = 'Agregar etiqueta al mensaje'; +$labels['removeflags'] = 'Eliminar etiquetas al mensaje'; +$labels['flagread'] = 'Leido'; +$labels['flagdeleted'] = 'Eliminado'; +$labels['flaganswered'] = 'Respondido'; +$labels['flagflagged'] = 'Marcado'; +$labels['flagdraft'] = 'Borrador'; $messages = array(); $messages['filterunknownerror'] = 'Error desconocido de servidor'; @@ -76,6 +101,7 @@ $messages['setdeleteconfirm'] = '¿Está seguro de que desea borrar el conjunto $messages['setcreateerror'] = 'Imposible crear el conjunto de filtros. Ha ocurrido un error en el servidor'; $messages['setcreated'] = 'Conjunto de filtros creado satisfactoriamente'; $messages['emptyname'] = 'Imposible crear el conjunto de filtros. Sin nombre'; -$messages['nametoolong'] = 'Imposible crear el conjunto de filtros. Nombre demasiado largo' +$messages['nametoolong'] = 'Imposible crear el conjunto de filtros. Nombre demasiado largo'; +$messages['setdeactivateerror'] = 'Imposible desactivar el conjunto de filtros seleccionado. Ha ocurrido un error en el servidor'; ?> diff --git a/plugins/managesieve/localization/lv_LV.inc b/plugins/managesieve/localization/lv_LV.inc new file mode 100644 index 0000000..b394588 --- /dev/null +++ b/plugins/managesieve/localization/lv_LV.inc @@ -0,0 +1,92 @@ + diff --git a/plugins/managesieve/localization/pl_PL.inc b/plugins/managesieve/localization/pl_PL.inc index 290dd1a..c0ec2ed 100644 --- a/plugins/managesieve/localization/pl_PL.inc +++ b/plugins/managesieve/localization/pl_PL.inc @@ -7,8 +7,8 @@ $labels['filtername'] = 'Nazwa filtru'; $labels['newfilter'] = 'Nowy filtr'; $labels['filteradd'] = 'Dodaj filtr'; $labels['filterdel'] = 'Usuń filtr'; -$labels['moveup'] = 'Przenieś wyżej'; -$labels['movedown'] = 'Przenieś niżej'; +$labels['enable'] = 'Włącz/Wyłącz'; +$labels['filtersets'] = 'Zbiory fitrów'; $labels['filterallof'] = 'spełniających wszystkie poniższe kryteria'; $labels['filteranyof'] = 'spełniających dowolne z poniższych kryteriów'; $labels['filterany'] = 'wszystkich'; @@ -49,7 +49,6 @@ $labels['filtersetadd'] = 'Dodaj zbiór filtrów'; $labels['filtersetdel'] = 'Usuń bieżący zbiór filtrów'; $labels['filtersetact'] = 'Aktywuj bieżący zbiór filtrów'; $labels['filtersetdeact'] = 'Deaktywuj bieżący zbiór filtrów'; -$labels['filtersetget'] = 'Pobierz bieżący zbiór filtrów w formacie tekstowym'; $labels['filterdef'] = 'Definicja filtra'; $labels['filtersetname'] = 'Nazwa zbioru'; $labels['newfilterset'] = 'Nowy zbiór filtrów'; @@ -78,29 +77,61 @@ $labels['flagdeleted'] = 'Usunięta'; $labels['flaganswered'] = 'Z odpowiedzią'; $labels['flagflagged'] = 'Oflagowana'; $labels['flagdraft'] = 'Szkic'; +$labels['filtercreate'] = 'Utwóż filtr'; +$labels['usedata'] = 'Użyj następujących danych do utworzenia filtra:'; +$labels['nextstep'] = 'Następny krok'; +$labels['...'] = '...'; +$labels['advancedopts'] = 'Zaawansowane opcje'; +$labels['body'] = 'Treść'; +$labels['address'] = 'adres'; +$labels['envelope'] = 'koperta (envelope)'; +$labels['modifier'] = 'modyfikator:'; +$labels['text'] = 'tekst'; +$labels['undecoded'] = 'nie (raw)'; +$labels['contenttype'] = 'typ części (content type)'; +$labels['modtype'] = 'typ:'; +$labels['allparts'] = 'wszystkie'; +$labels['domain'] = 'domena'; +$labels['localpart'] = 'część lokalna'; +$labels['user'] = 'użytkownik'; +$labels['detail'] = 'detal'; +$labels['comparator'] = 'komparator:'; +$labels['default'] = 'domyślny'; +$labels['octet'] = 'dokładny (octet)'; +$labels['asciicasemap'] = 'nierozróżniający wielkości liter (ascii-casemap)'; +$labels['asciinumeric'] = 'numeryczny (ascii-numeric)'; $messages = array(); -$messages['filterunknownerror'] = 'Nieznany błąd serwera'; -$messages['filterconnerror'] = 'Nie można nawiązać połączenia z serwerem managesieve'; -$messages['filterdeleteerror'] = 'Nie można usunąć filtra. Wystąpił błąd serwera'; -$messages['filterdeleted'] = 'Filtr został usunięty pomyślnie'; +$messages['filterunknownerror'] = 'Nieznany błąd serwera.'; +$messages['filterconnerror'] = 'Nie można nawiązać połączenia z serwerem.'; +$messages['filterdeleteerror'] = 'Nie można usunąć filtra. Błąd serwera.'; +$messages['filterdeleted'] = 'Filtr został usunięty pomyślnie.'; $messages['filterdeleteconfirm'] = 'Czy na pewno chcesz usunąć wybrany filtr?'; -$messages['filtersaved'] = 'Filtr został zapisany pomyślnie'; +$messages['filtersaved'] = 'Filtr został zapisany pomyślnie.'; $messages['filtersaveerror'] = 'Nie można zapisać filtra. Wystąpił błąd serwera.'; $messages['ruledeleteconfirm'] = 'Czy na pewno chcesz usunąć wybraną regułę?'; $messages['actiondeleteconfirm'] = 'Czy na pewno usunąć wybraną akcję?'; -$messages['forbiddenchars'] = 'Pole zawiera niedozwolone znaki'; -$messages['cannotbeempty'] = 'Pole nie może być puste'; -$messages['setactivateerror'] = 'Nie można aktywować wybranego zbioru filtrów. Błąd serwera'; -$messages['setdeactivateerror'] = 'Nie można deaktywować wybranego zbioru filtrów. Błąd serwera'; -$messages['setdeleteerror'] = 'Nie można usunąć wybranego zbioru filtrów. Błąd serwera'; -$messages['setactivated'] = 'Zbiór filtrów został aktywowany pomyślnie'; -$messages['setdeactivated'] = 'Zbiór filtrów został deaktywowany pomyślnie'; -$messages['setdeleted'] = 'Zbiór filtrów został usunięty pomyślnie'; +$messages['forbiddenchars'] = 'Pole zawiera niedozwolone znaki.'; +$messages['cannotbeempty'] = 'Pole nie może być puste.'; +$messages['setactivateerror'] = 'Nie można aktywować wybranego zbioru filtrów. Błąd serwera.'; +$messages['setdeactivateerror'] = 'Nie można deaktywować wybranego zbioru filtrów. Błąd serwera.'; +$messages['setdeleteerror'] = 'Nie można usunąć wybranego zbioru filtrów. Błąd serwera.'; +$messages['setactivated'] = 'Zbiór filtrów został aktywowany pomyślnie.'; +$messages['setdeactivated'] = 'Zbiór filtrów został deaktywowany pomyślnie.'; +$messages['setdeleted'] = 'Zbiór filtrów został usunięty pomyślnie.'; $messages['setdeleteconfirm'] = 'Czy na pewno chcesz usunąć wybrany zbiór filtrów?'; -$messages['setcreateerror'] = 'Nie można utworzyć zbioru filtrów. Błąd serwera'; -$messages['setcreated'] = 'Zbiór filtrów został utworzony pomyślnie'; -$messages['emptyname'] = 'Nie można utworzyć zbioru filtrów. Pusta nazwa zbioru'; -$messages['nametoolong'] = 'Nie można utworzyć zbioru filtrów. Nazwa zbyt długa' +$messages['setcreateerror'] = 'Nie można utworzyć zbioru filtrów. Błąd serwera.'; +$messages['setcreated'] = 'Zbiór filtrów został utworzony pomyślnie.'; +$messages['nodata'] = 'Należy wybrać co najmniej jedną pozycję!'; +$messages['ruleexist'] = 'Filtr o podanej nazwie już istnieje.'; +$messages['activateerror'] = 'Nie można włączyć wybranych filtrów. Błąd serwera.'; +$messages['deactivateerror'] = 'Nie można wyłączyć wybranych filtrów. Błąd serwera.'; +$messages['activated'] = 'Filtr(y) wyłączono pomyślnie.'; +$messages['deactivated'] = 'Filtr(y) włączono pomyślnie.'; +$messages['moved'] = 'Filter został przeniesiony pomyślnie.'; +$messages['moveerror'] = 'Nie można przenieść wybranego filtra. Błąd serwera.'; +$messages['nametoolong'] = 'Zbyt długa nazwa.'; +$messages['namereserved'] = 'Nazwa zarezerwowana.'; +$messages['setexist'] = 'Zbiór już istnieje.'; ?> diff --git a/plugins/managesieve/localization/pt_BR.inc b/plugins/managesieve/localization/pt_BR.inc index b48774e..af3f05a 100644 --- a/plugins/managesieve/localization/pt_BR.inc +++ b/plugins/managesieve/localization/pt_BR.inc @@ -32,7 +32,7 @@ $labels['add'] = 'Adicionar'; $labels['del'] = 'Excluir'; $labels['sender'] = 'Remetente'; $labels['recipient'] = 'Destinatário'; -$labels['vacationaddresses'] = 'Lista adicional de e-mails de remetente (separado por vírgula):'; +$labels['vacationaddresses'] = 'Lista adicional de e-mails destinatários (separado por vírgula):'; $labels['vacationdays'] = 'Enviar mensagens com que frequência (em dias):'; $labels['vacationreason'] = 'Corpo da mensagem (motivo de férias):'; $labels['rulestop'] = 'Parar de avaliar regras'; diff --git a/plugins/managesieve/managesieve.js b/plugins/managesieve/managesieve.js index ec6247a..a8bfaf2 100644 --- a/plugins/managesieve/managesieve.js +++ b/plugins/managesieve/managesieve.js @@ -1,56 +1,97 @@ -/* Sieve Filters (tab) */ +/* (Manage)Sieve Filters */ if (window.rcmail) { rcmail.addEventListener('init', function(evt) { + // add managesieve-create command to message_commands array, + // so it's state will be updated on message selection/unselection + if (rcmail.env.task == 'mail') { + if (rcmail.env.action != 'show') + rcmail.env.message_commands.push('managesieve-create'); + else + rcmail.enable_command('managesieve-create', true); + } + else { + var tab = $('').attr('id', 'settingstabpluginmanagesieve').addClass('tablink'), + button = $('').attr('href', rcmail.env.comm_path+'&_action=plugin.managesieve') + .attr('title', rcmail.gettext('managesieve.managefilters')) + .html(rcmail.gettext('managesieve.filters')) + .appendTo(tab); + + // add tab + rcmail.add_element(tab, 'tabs'); + } + + if (rcmail.env.task == 'mail' || rcmail.env.action.indexOf('plugin.managesieve') != -1) { + // Create layer for form tips + if (!rcmail.env.framed) { + rcmail.env.ms_tip_layer = $('
'); + rcmail.env.ms_tip_layer.appendTo(document.body); + } + } - var tab = $('').attr('id', 'settingstabpluginmanagesieve').addClass('tablink'); - var button = $('
').attr('href', rcmail.env.comm_path+'&_action=plugin.managesieve') - .attr('title', rcmail.gettext('managesieve.managefilters')) - .html(rcmail.gettext('managesieve.filters')) - .appendTo(tab); - - // add button and register commands - rcmail.add_element(tab, 'tabs'); - rcmail.register_command('plugin.managesieve-save', function() { rcmail.managesieve_save() }, true); - rcmail.register_command('plugin.managesieve-add', function() { rcmail.managesieve_add() }, true); - rcmail.register_command('plugin.managesieve-del', function() { rcmail.managesieve_del() }, true); - rcmail.register_command('plugin.managesieve-up', function() { rcmail.managesieve_up() }, true); - rcmail.register_command('plugin.managesieve-down', function() { rcmail.managesieve_down() }, true); - rcmail.register_command('plugin.managesieve-set', function() { rcmail.managesieve_set() }, true); - rcmail.register_command('plugin.managesieve-setadd', function() { rcmail.managesieve_setadd() }, true); - rcmail.register_command('plugin.managesieve-setdel', function() { rcmail.managesieve_setdel() }, true); - rcmail.register_command('plugin.managesieve-setact', function() { rcmail.managesieve_setact() }, true); - rcmail.register_command('plugin.managesieve-setget', function() { rcmail.managesieve_setget() }, true); - - if (rcmail.env.action == 'plugin.managesieve') { + // register commands + rcmail.register_command('plugin.managesieve-save', function() { rcmail.managesieve_save() }); + rcmail.register_command('plugin.managesieve-act', function() { rcmail.managesieve_act() }); + rcmail.register_command('plugin.managesieve-add', function() { rcmail.managesieve_add() }); + rcmail.register_command('plugin.managesieve-del', function() { rcmail.managesieve_del() }); + rcmail.register_command('plugin.managesieve-move', function() { rcmail.managesieve_move() }); + rcmail.register_command('plugin.managesieve-setadd', function() { rcmail.managesieve_setadd() }); + rcmail.register_command('plugin.managesieve-setdel', function() { rcmail.managesieve_setdel() }); + rcmail.register_command('plugin.managesieve-setact', function() { rcmail.managesieve_setact() }); + rcmail.register_command('plugin.managesieve-setget', function() { rcmail.managesieve_setget() }); + + if (rcmail.env.action == 'plugin.managesieve' || rcmail.env.action == 'plugin.managesieve-save') { if (rcmail.gui_objects.sieveform) { rcmail.enable_command('plugin.managesieve-save', true); + + // small resize for header element + $('select[name="_header[]"]', rcmail.gui_objects.sieveform).each(function() { + if (this.value == '...') this.style.width = '40px'; + }); + + // resize dialog window + if (rcmail.env.action == 'plugin.managesieve' && rcmail.env.task == 'mail') { + parent.rcmail.managesieve_dialog_resize(rcmail.gui_objects.sieveform); + } + + $('input[type="text"]:first', rcmail.gui_objects.sieveform).focus(); } else { - rcmail.enable_command('plugin.managesieve-del', 'plugin.managesieve-up', - 'plugin.managesieve-down', false); rcmail.enable_command('plugin.managesieve-add', 'plugin.managesieve-setadd', !rcmail.env.sieveconnerror); } - // Create layer for form tips - if (!rcmail.env.framed) { - rcmail.env.ms_tip_layer = $('
'); - rcmail.env.ms_tip_layer.appendTo(document.body); - } + var i, p = rcmail, setcnt, set = rcmail.env.currentset; if (rcmail.gui_objects.filterslist) { - var p = rcmail; - rcmail.filters_list = new rcube_list_widget(rcmail.gui_objects.filterslist, {multiselect:false, draggable:false, keyboard:false}); + rcmail.filters_list = new rcube_list_widget(rcmail.gui_objects.filterslist, + {multiselect:false, draggable:true, keyboard:false}); rcmail.filters_list.addEventListener('select', function(o){ p.managesieve_select(o); }); + rcmail.filters_list.addEventListener('dragstart', function(o){ p.managesieve_dragstart(o); }); + rcmail.filters_list.addEventListener('dragend', function(e){ p.managesieve_dragend(e); }); + rcmail.filters_list.row_init = function (row) { + row.obj.onmouseover = function() { p.managesieve_focus_filter(row); }; + row.obj.onmouseout = function() { p.managesieve_unfocus_filter(row); }; + }; rcmail.filters_list.init(); rcmail.filters_list.focus(); + } - rcmail.enable_command('plugin.managesieve-set', true); - rcmail.enable_command('plugin.managesieve-setact', 'plugin.managesieve-setget', rcmail.gui_objects.filtersetslist.length); - rcmail.enable_command('plugin.managesieve-setdel', rcmail.gui_objects.filtersetslist.length > 1); + if (rcmail.gui_objects.filtersetslist) { + rcmail.filtersets_list = new rcube_list_widget(rcmail.gui_objects.filtersetslist, {multiselect:false, draggable:false, keyboard:false}); + rcmail.filtersets_list.addEventListener('select', function(o){ p.managesieve_setselect(o); }); + rcmail.filtersets_list.init(); + rcmail.filtersets_list.focus(); - $('#'+rcmail.buttons['plugin.managesieve-setact'][0].id).attr('title', rcmail.gettext('managesieve.filterset' - + (rcmail.gui_objects.filtersetslist.value == rcmail.env.active_set ? 'deact' : 'act'))); + if (set != null) { + set = rcmail.managesieve_setid(set); + rcmail.filtersets_list.shift_start = set; + rcmail.filtersets_list.highlight_row(set, false); + } + + setcnt = rcmail.filtersets_list.rowcount; + rcmail.enable_command('plugin.managesieve-set', true); + rcmail.enable_command('plugin.managesieve-setact', 'plugin.managesieve-setget', setcnt); + rcmail.enable_command('plugin.managesieve-setdel', setcnt > 1); } } if (rcmail.gui_objects.sieveform && rcmail.env.rule_disabled) @@ -59,7 +100,7 @@ if (window.rcmail) { }; /*********************************************************/ -/********* Managesieve filters methods *********/ +/********* Managesieve UI methods *********/ /*********************************************************/ rcube_webmail.prototype.managesieve_add = function() @@ -71,23 +112,41 @@ rcube_webmail.prototype.managesieve_add = function() rcube_webmail.prototype.managesieve_del = function() { var id = this.filters_list.get_single_selection(); - if (confirm(this.get_label('managesieve.filterdeleteconfirm'))) - this.http_request('plugin.managesieve', - '_act=delete&_fid='+this.filters_list.rows[id].uid, true); + if (confirm(this.get_label('managesieve.filterdeleteconfirm'))) { + var lock = this.set_busy(true, 'loading'); + this.http_post('plugin.managesieve', + '_act=delete&_fid='+this.filters_list.rows[id].uid, lock); + } }; -rcube_webmail.prototype.managesieve_up = function() +rcube_webmail.prototype.managesieve_act = function() { - var id = this.filters_list.get_single_selection(); - this.http_request('plugin.managesieve', - '_act=up&_fid='+this.filters_list.rows[id].uid, true); + var id = this.filters_list.get_single_selection(), + lock = this.set_busy(true, 'loading'); + + this.http_post('plugin.managesieve', + '_act=act&_fid='+this.filters_list.rows[id].uid, lock); }; -rcube_webmail.prototype.managesieve_down = function() +// Filter selection +rcube_webmail.prototype.managesieve_select = function(list) { - var id = this.filters_list.get_single_selection(); - this.http_request('plugin.managesieve', - '_act=down&_fid='+this.filters_list.rows[id].uid, true); + var id = list.get_single_selection(); + if (id != null) + this.load_managesieveframe(list.rows[id].uid); +}; + +// Set selection +rcube_webmail.prototype.managesieve_setselect = function(list) +{ + this.show_contentframe(false); + this.filters_list.clear(true); + this.enable_command('plugin.managesieve-setdel', list.rowcount > 1); + this.enable_command( 'plugin.managesieve-setact', 'plugin.managesieve-setget', true); + + var id = list.get_single_selection(); + if (id != null) + this.managesieve_list(this.env.filtersets[id]); }; rcube_webmail.prototype.managesieve_rowid = function(id) @@ -99,194 +158,275 @@ rcube_webmail.prototype.managesieve_rowid = function(id) return i; }; -rcube_webmail.prototype.managesieve_updatelist = function(action, name, id, disabled) +// Returns set's identifier +rcube_webmail.prototype.managesieve_setid = function(name) +{ + for (var i in this.env.filtersets) + if (this.env.filtersets[i] == name) + return i; +}; + +// Filters listing request +rcube_webmail.prototype.managesieve_list = function(script) +{ + var lock = this.set_busy(true, 'loading'); + + this.http_post('plugin.managesieve', '_act=list&_set='+urlencode(script), lock); +}; + +// Script download request +rcube_webmail.prototype.managesieve_setget = function() +{ + var id = this.filtersets_list.get_single_selection(), + script = this.env.filtersets[id]; + + location.href = this.env.comm_path+'&_action=plugin.managesieve&_act=setget&_set='+urlencode(script); +}; + +// Set activate/deactivate request +rcube_webmail.prototype.managesieve_setact = function() +{ + var id = this.filtersets_list.get_single_selection(), + lock = this.set_busy(true, 'loading'), + script = this.env.filtersets[id], + action = $('#rcmrow'+id).hasClass('disabled') ? 'setact' : 'deact'; + + this.http_post('plugin.managesieve', '_act='+action+'&_set='+urlencode(script), lock); +}; + +// Set delete request +rcube_webmail.prototype.managesieve_setdel = function() +{ + if (!confirm(this.get_label('managesieve.setdeleteconfirm'))) + return false; + + var id = this.filtersets_list.get_single_selection(), + lock = this.set_busy(true, 'loading'), + script = this.env.filtersets[id]; + + this.http_post('plugin.managesieve', '_act=setdel&_set='+urlencode(script), lock); +}; + +// Set add request +rcube_webmail.prototype.managesieve_setadd = function() +{ + this.filters_list.clear_selection(); + this.enable_command('plugin.managesieve-act', 'plugin.managesieve-del', false); + + if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { + var lock = this.set_busy(true, 'loading'); + target = window.frames[this.env.contentframe]; + target.location.href = this.env.comm_path+'&_action=plugin.managesieve&_framed=1&_newset=1&_unlock='+lock; + } +}; + +rcube_webmail.prototype.managesieve_updatelist = function(action, o) { this.set_busy(true); switch (action) { - case 'delete': - this.filters_list.remove_row(this.managesieve_rowid(id)); - this.filters_list.clear_selection(); - this.enable_command('plugin.managesieve-del', 'plugin.managesieve-up', 'plugin.managesieve-down', false); + + // Delete filter row + case 'del': + var i, list = this.filters_list, rows = list.rows; + + list.remove_row(this.managesieve_rowid(o.id)); + list.clear_selection(); this.show_contentframe(false); + this.enable_command('plugin.managesieve-del', 'plugin.managesieve-act', false); // re-numbering filters - var i, rows = this.filters_list.rows; for (i=0; i id) + if (rows[i] != null && rows[i].uid > o.id) rows[i].uid = rows[i].uid-1; } + break; - case 'down': - var from, fromstatus, status, rows = this.filters_list.rows; + // Update filter row + case 'update': + var i, row = $('#rcmrow'+o.id); + + if (o.name) + $('td', row).html(o.name); + if (o.disabled) + row.addClass('disabled'); + else + row.removeClass('disabled'); + + $('#disabled', $('iframe').contents()).prop('checked', o.disabled); - // we need only to replace filter names... - for (var i=0; i'); + + $('td', row).html(o.name); + row.attr('id', 'rcmrow'+o.id); + if (o.disabled) + row.addClass('disabled'); + + list.insert_row(row.get(0)); + list.highlight_row(o.id); + + this.enable_command('plugin.managesieve-del', 'plugin.managesieve-act', true); - // we need only to replace filter names... - for (var i=0; i'); + + $('td', row).html(o.name); + row.attr('id', 'rcmrow'+id); + + this.env.filtersets[id] = o.name; + list.insert_row(row.get(0)); + + // move row into its position on the list + if (o.index != list.rowcount-1) { + row.detach(); + var elem = $('tr:visible', list.list).get(o.index); + row.insertBefore(elem); + } + + list.select(id); + + break; } - this.gui_objects.sieveform.submit(); + + this.set_busy(false); }; // load filter frame rcube_webmail.prototype.load_managesieveframe = function(id) { - if (typeof(id) != 'undefined' && id != null) { - this.enable_command('plugin.managesieve-del', true); - this.filters_listbuttons(); - } - else - this.enable_command('plugin.managesieve-up', 'plugin.managesieve-down', 'plugin.managesieve-del', false); + var has_id = typeof(id) != 'undefined' && id != null; + this.enable_command('plugin.managesieve-act', 'plugin.managesieve-del', has_id); if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { target = window.frames[this.env.contentframe]; var msgid = this.set_busy(true, 'loading'); - target.location.href = this.env.comm_path+'&_action=plugin.managesieve&_framed=1&_fid='+id+'&_unlock='+msgid; + target.location.href = this.env.comm_path+'&_action=plugin.managesieve&_framed=1' + +(id ? '&_fid='+id : '')+'&_unlock='+msgid; } }; -// enable/disable Up/Down buttons -rcube_webmail.prototype.filters_listbuttons = function() +// load filter frame +rcube_webmail.prototype.managesieve_dragstart = function(list) { - var id = this.filters_list.get_single_selection(), - rows = this.filters_list.rows; + var id = this.filters_list.get_single_selection(); - for (var i=0; i0 || buttons.length>1) { $(button).removeClass('disabled'); - button.removeAttribute('disabled'); } else { $(button).addClass('disabled'); - button.setAttribute('disabled', true); } } }; -// Set change -rcube_webmail.prototype.managesieve_set = function() -{ - var script = $(this.gui_objects.filtersetslist).val(); - location.href = this.env.comm_path+'&_action=plugin.managesieve&_set='+script; -}; - -// Script download -rcube_webmail.prototype.managesieve_setget = function() -{ - var script = $(this.gui_objects.filtersetslist).val(); - location.href = this.env.comm_path+'&_action=plugin.managesieve&_act=setget&_set='+script; -}; - -// Set activate -rcube_webmail.prototype.managesieve_setact = function() -{ - if (!this.gui_objects.filtersetslist) - return false; - - var script = this.gui_objects.filtersetslist.value, - action = (script == rcmail.env.active_set ? 'deact' : 'setact'); - - this.http_post('plugin.managesieve', '_act='+action+'&_set='+script); -}; - -// Set activate flag in sets list after set activation -rcube_webmail.prototype.managesieve_reset = function() -{ - if (!this.gui_objects.filtersetslist) - return false; - - var list = this.gui_objects.filtersetslist, - opts = list.getElementsByTagName('option'), - label = ' (' + this.get_label('managesieve.active') + ')', - regx = new RegExp(RegExp.escape(label)+'$'); - - for (var x=0; x iframe' : '#filter-box'), parent.document).offset(); + top += offset.top; + left += offset.left; + } + + tip.html(e.data.str) + top -= tip.height(); + + tip.css({left: left, top: top}).show(); + }) + .bind('mouseleave', function(e) { tip.hide(); }); + } +}; + +/*********************************************************/ +/********* Mail UI methods *********/ +/*********************************************************/ + +rcube_webmail.prototype.managesieve_create = function() +{ + if (!rcmail.env.sieve_headers || !rcmail.env.sieve_headers.length) + return; + + var i, html, buttons = {}, dialog = $("#sievefilterform"); + + // create dialog window + if (!dialog.length) { + dialog = $('
'); + $('body').append(dialog); + } + + // build dialog window content + html = '
'+this.gettext('managesieve.usedata')+'
    '; + for (i in rcmail.env.sieve_headers) + html += '
  • ' + +' '+rcmail.env.sieve_headers[i][1]+'
  • '; + html += '
'; + + dialog.html(html); + + // [Next Step] button action + buttons[this.gettext('managesieve.nextstep')] = function () { + // check if there's at least one checkbox checked + var hdrs = $('input[name="headers[]"]:checked', dialog); + if (!hdrs.length) { + alert(rcmail.gettext('managesieve.nodata')); + return; + } + + // build frame URL + var url = rcmail.get_task_url('mail'); + url = rcmail.add_url(url, '_action', 'plugin.managesieve'); + url = rcmail.add_url(url, '_framed', 1); + + hdrs.map(function() { + var val = rcmail.env.sieve_headers[this.value]; + url = rcmail.add_url(url, 'r['+this.value+']', val[0]+':'+val[1]); + }); + + // load form in the iframe + var frame = $(''): -(g=document.createElement("iframe"),g.name=f,g.style.border="none",g.style.width=0,g.style.height=0,g.style.visibility="hidden",document.body.appendChild(g));$(f).bind("load",{ts:e},d);$(a).attr({target:f,action:this.url(b,{_id:this.env.compose_id||"",_uploadid:e}),method:"POST"}).attr(a.encoding?"encoding":"enctype","multipart/form-data").submit();return f};this.start_keepalive=function(){this._int&&clearInterval(this._int);if(this.env.keep_alive&&!this.env.framed&&this.task=="mail"&&this.gui_objects.mailboxlist)this._int= -setInterval(function(){l.check_for_recent(!1)},this.env.keep_alive*1E3);else if(this.env.keep_alive&&!this.env.framed&&this.task!="login"&&this.env.action!="print")this._int=setInterval(function(){l.keep_alive()},this.env.keep_alive*1E3)};this.keep_alive=function(){this.busy||this.http_request("keep-alive")};this.check_for_recent=function(a){if(!this.busy){var b,d="_mbox="+urlencode(this.env.mailbox);a&&(b=this.set_busy(!0,"checkingmail"),d+="&_refresh=1",this.start_keepalive());this.gui_objects.messagelist&& -(d+="&_list=1");this.gui_objects.quotadisplay&&(d+="&_quota=1");this.env.search_request&&(d+="&_search="+this.env.search_request);this.http_request("check-recent",d,b)}};this.get_single_uid=function(){return this.env.uid?this.env.uid:this.message_list?this.message_list.get_single_selection():null};this.get_single_cid=function(){return this.env.cid?this.env.cid:this.contact_list?this.contact_list.get_single_selection():null};this.get_caret_pos=function(a){if(a.selectionEnd!==void 0)return a.selectionEnd; -else if(document.selection&&document.selection.createRange){var b=document.selection.createRange();if(b.parentElement()!=a)return 0;var d=b.duplicate();a.tagName=="TEXTAREA"?d.moveToElementText(a):d.expand("textedit");d.setEndPoint("EndToStart",b);b=d.text.length;return b<=a.value.length?b:-1}else return a.value.length};this.set_caret_pos=function(a,b){if(a.setSelectionRange)a.setSelectionRange(b,b);else if(a.createTextRange){var d=a.createTextRange();d.collapse(!0);d.moveEnd("character",b);d.moveStart("character", -b);d.select()}};this.lock_form=function(a,b){if(a&&a.elements){var d,e,f;if(b)this.disabled_form_elements=[];for(d=0,e=a.elements.length;da.parent().width())l.title=a.html()}};rcube_webmail.prototype.addEventListener=rcube_event_engine.prototype.addEventListener; -rcube_webmail.prototype.removeEventListener=rcube_event_engine.prototype.removeEventListener;rcube_webmail.prototype.triggerEvent=rcube_event_engine.prototype.triggerEvent; +function rcube_webmail(){this.env={recipients_separator:",",recipients_delimiter:", "};this.labels={};this.buttons={};this.buttons_sel={};this.gui_objects={};this.gui_containers={};this.commands={};this.command_handlers={};this.onloads=[];this.messages={};this.ref="rcmail";var i=this;this.dblclick_time=500;this.message_time=4E3;this.identifier_expr=RegExp("[^0-9a-z-_]","gi");this.env.keep_alive=60;this.env.request_timeout=180;this.env.draft_autosave=0;this.env.comm_path="./";this.env.blankpage="program/blank.gif"; +$.ajaxSetup({cache:!1,error:function(a,b,d){i.http_error(a,b,d)},beforeSend:function(a){a.setRequestHeader("X-Roundcube-Request",i.env.request_token)}});this.set_env=function(a,b){if(null!=a&&"object"===typeof a&&!b)for(var d in a)this.env[d]=a[d];else this.env[a]=b};this.add_label=function(a,b){"string"==typeof a?this.labels[a]=b:"object"==typeof a&&$.extend(this.labels,a)};this.register_button=function(a,b,d,e,f,h){this.buttons[a]||(this.buttons[a]=[]);b={id:b,type:d};if(e)b.act=e;if(f)b.sel=f; +if(h)b.over=h;this.buttons[a].push(b);this.loaded&&s(a,b)};this.gui_object=function(a,b){this.gui_objects[a]=this.loaded?rcube_find_object(b):b};this.gui_container=function(a,b){this.gui_containers[a]=b};this.add_element=function(a,b){this.gui_containers[b]&&this.gui_containers[b].jquery&&this.gui_containers[b].append(a)};this.register_command=function(a,b,d){this.command_handlers[a]=b;d&&this.enable_command(a,!0)};this.add_onload=function(a){this.onloads.push(a)};this.init=function(){var a,b=this; +this.task=this.env.task;if(!bw.dom||!bw.xmlhttp_test())this.goto_url("error","_code=0x199");else{for(a in this.gui_containers)this.gui_containers[a]=$("#"+this.gui_containers[a]);for(a in this.gui_objects)this.gui_objects[a]=rcube_find_object(this.gui_objects[a]);if(this.env.x_frame_options)try{if("deny"==this.env.x_frame_options&&top.location.href!=self.location.href)top.location.href=self.location.href;else if(top.location.hostname!=self.location.hostname)throw 1;}catch(d){$("form").each(function(){i.lock_form(this, +!0)});this.display_message("Blocked: possible clickjacking attack!","error");return}this.init_buttons();if(this.is_framed())parent.rcmail.set_busy(!1,null,parent.rcmail.env.frame_lock),parent.rcmail.env.frame_lock=null;this.enable_command("logout","mail","addressbook","settings","save-pref","compose","undo",!0);this.env.permaurl&&this.enable_command("permaurl",!0);switch(this.task){case "mail":this.enable_command("list","checkmail","add-contact","search","reset-search","collapse-folder",!0);if(this.gui_objects.messagelist)this.message_list= +new rcube_list_widget(this.gui_objects.messagelist,{multiselect:!0,multiexpand:!0,draggable:!0,keyboard:!0,column_movable:this.env.col_movable,dblclick_time:this.dblclick_time}),this.message_list.row_init=function(a){b.init_message_row(a)},this.message_list.addEventListener("dblclick",function(a){b.msglist_dbl_click(a)}),this.message_list.addEventListener("click",function(a){b.msglist_click(a)}),this.message_list.addEventListener("keypress",function(a){b.msglist_keypress(a)}),this.message_list.addEventListener("select", +function(a){b.msglist_select(a)}),this.message_list.addEventListener("dragstart",function(a){b.drag_start(a)}),this.message_list.addEventListener("dragmove",function(a){b.drag_move(a)}),this.message_list.addEventListener("dragend",function(a){b.drag_end(a)}),this.message_list.addEventListener("expandcollapse",function(a){b.msglist_expand(a)}),this.message_list.addEventListener("column_replace",function(a){b.msglist_set_coltypes(a)}),document.onmouseup=function(a){return b.doc_mouse_up(a)},this.gui_objects.messagelist.parentNode.onmousedown= +function(a){return b.click_on_list(a)},this.message_list.init(),this.enable_command("toggle_status","toggle_flag","menu-open","menu-save",!0),this.command("list");if(this.gui_objects.qsearchbox){if(null!=this.env.search_text)this.gui_objects.qsearchbox.value=this.env.search_text;$(this.gui_objects.qsearchbox).focusin(function(){rcmail.message_list.blur()})}!this.env.flag_for_deletion&&this.env.trash_mailbox&&this.env.mailbox!=this.env.trash_mailbox&&this.set_alttext("delete","movemessagetotrash"); +this.env.message_commands="show,reply,reply-all,reply-list,forward,moveto,copy,delete,open,mark,edit,viewsource,download,print,load-attachment,load-headers,forward-attachment".split(",");if("show"==this.env.action||"preview"==this.env.action){this.enable_command(this.env.message_commands,this.env.uid);this.enable_command("reply-list",this.env.list_post);"show"==this.env.action&&this.http_request("pagenav","_uid="+this.env.uid+"&_mbox="+urlencode(this.env.mailbox)+(this.env.search_request?"&_search="+ +this.env.search_request:""),this.display_message("","loading"));if(this.env.blockedobjects){if(this.gui_objects.remoteobjectsmsg)this.gui_objects.remoteobjectsmsg.style.display="block";this.enable_command("load-images","always-load",!0)}"preview"==this.env.action&&this.is_framed()&&(this.enable_command("compose","add-contact",!1),parent.rcmail.show_contentframe(!0))}else if("compose"==this.env.action){this.env.compose_commands=["send-attachment","remove-attachment","send","cancel","toggle-editor"]; +this.env.drafts_mailbox&&this.env.compose_commands.push("savedraft");this.enable_command(this.env.compose_commands,"identities",!0);if(this.env.spellcheck)this.env.spellcheck.spelling_state_observer=function(a){i.set_spellcheck_state(a)},this.env.compose_commands.push("spellcheck"),this.set_spellcheck_state("ready"),"1"==$("input[name='_is_html']").val()&&this.display_spellcheck_controls(!1);document.onmouseup=function(a){return b.doc_mouse_up(a)};this.init_messageform()}else"print"==this.env.action&& +this.env.uid&&(bw.safari?window.setTimeout("window.print()",10):window.print());if(this.gui_objects.mailboxlist)this.env.unread_counts={},this.gui_objects.folderlist=this.gui_objects.mailboxlist,this.http_request("getunread","");this.env.mdn_request&&this.env.uid&&(a="_uid="+this.env.uid+"&_mbox="+urlencode(this.env.mailbox),confirm(this.get_label("mdnrequest"))?this.http_post("sendmdn",a):this.http_post("mark",a+"&_flag=mdnsent"));break;case "addressbook":if(this.gui_objects.folderlist)this.env.contactfolders= +$.extend($.extend({},this.env.address_sources),this.env.contactgroups);this.enable_command("add","import",this.env.writable_source);this.enable_command("list","listgroup","listsearch","advanced-search",!0);if(this.gui_objects.contactslist)this.contact_list=new rcube_list_widget(this.gui_objects.contactslist,{multiselect:!0,draggable:this.gui_objects.folderlist?!0:!1,keyboard:!0}),this.contact_list.row_init=function(a){b.triggerEvent("insertrow",{cid:a.uid,row:a})},this.contact_list.addEventListener("keypress", +function(a){b.contactlist_keypress(a)}),this.contact_list.addEventListener("select",function(a){b.contactlist_select(a)}),this.contact_list.addEventListener("dragstart",function(a){b.drag_start(a)}),this.contact_list.addEventListener("dragmove",function(a){b.drag_move(a)}),this.contact_list.addEventListener("dragend",function(a){b.drag_end(a)}),this.contact_list.init(),this.env.cid&&this.contact_list.highlight_row(this.env.cid),this.gui_objects.contactslist.parentNode.onmousedown=function(a){return b.click_on_list(a)}, +document.onmouseup=function(a){return b.doc_mouse_up(a)},this.gui_objects.qsearchbox&&$(this.gui_objects.qsearchbox).focusin(function(){rcmail.contact_list.blur()}),this.update_group_commands(),this.command("list");this.set_page_buttons();this.env.cid&&(this.enable_command("show","edit",!0),this.gui_objects.editform&&$("input.groupmember").change(function(){i.group_member_change(this.checked?"add":"del",i.env.cid,i.env.source,this.value)}));this.gui_objects.editform&&(this.enable_command("save",!0), +("add"==this.env.action||"edit"==this.env.action)&&this.init_contact_form());this.gui_objects.qsearchbox&&this.enable_command("search","reset-search","moveto",!0);break;case "settings":this.enable_command("preferences","identities","save","folders",!0);if("identities"==this.env.action)this.enable_command("add",2>this.env.identities_level);else if("edit-identity"==this.env.action||"add-identity"==this.env.action)this.enable_command("add",2>this.env.identities_level),this.enable_command("save","delete", +"edit","toggle-editor",!0);else if("folders"==this.env.action)this.enable_command("subscribe","unsubscribe","create-folder","rename-folder",!0);else if("edit-folder"==this.env.action&&this.gui_objects.editform)this.enable_command("save","folder-size",!0),parent.rcmail.env.messagecount=this.env.messagecount,parent.rcmail.enable_command("purge",this.env.messagecount),$("input[type='text']").first().select();this.gui_objects.identitieslist?(this.identity_list=new rcube_list_widget(this.gui_objects.identitieslist, +{multiselect:!1,draggable:!1,keyboard:!1}),this.identity_list.addEventListener("select",function(a){b.identity_select(a)}),this.identity_list.init(),this.identity_list.focus(),this.env.iid&&this.identity_list.highlight_row(this.env.iid)):this.gui_objects.sectionslist?(this.sections_list=new rcube_list_widget(this.gui_objects.sectionslist,{multiselect:!1,draggable:!1,keyboard:!1}),this.sections_list.addEventListener("select",function(a){b.section_select(a)}),this.sections_list.init(),this.sections_list.focus()): +this.gui_objects.subscriptionlist&&this.init_subscription_list();break;case "login":a=$("#rcmloginuser");a.bind("keyup",function(a){return rcmail.login_user_keyup(a)});""==a.val()?a.focus():$("#rcmloginpwd").focus();var e=new Date;a=e.getTimezoneOffset()/-60;e=e.getStdTimezoneOffset()/-60;$("#rcmlogintz").val(e);$("#rcmlogindst").val(a>e?1:0);$("form").submit(function(){$("input[type=submit]",this).prop("disabled",!0);rcmail.display_message("","loading")});this.enable_command("login",!0)}bw.ie&&$("input[type=file]").keydown(function(a){"13"== +a.keyCode&&a.preventDefault()});this.loaded=!0;this.pending_message&&this.display_message(this.pending_message[0],this.pending_message[1],this.pending_message[2]);if(this.gui_objects.folderlist)this.gui_containers.foldertray=$(this.gui_objects.folderlist);this.triggerEvent("init",{task:this.task,action:this.env.action});for(var f in this.onloads)if("string"===typeof this.onloads[f])eval(this.onloads[f]);else if("function"===typeof this.onloads[f])this.onloads[f]();this.start_keepalive()}};this.log= +function(a){window.console&&console.log&&console.log(a)};this.command=function(a,b,d){var e,f,h,g;d&&d.blur&&d.blur();if(this.busy)return!1;if(!this.commands[a])return this.is_framed()&&parent.rcmail.command(a,b),!1;if("mail"==this.task&&"compose"==this.env.action&&0>$.inArray(a,this.env.compose_commands)&&this.cmp_hash!=this.compose_field_hash()&&!confirm(this.get_label("notsentwarning")))return!1;if("function"===typeof this.command_handlers[a])return e=this.command_handlers[a](b,d),void 0!==e?e: +d?!1:!0;if("string"===typeof this.command_handlers[a])return e=window[this.command_handlers[a]](b,d),void 0!==e?e:d?!1:!0;this.triggerEvent("actionbefore",{props:b,action:a});e=this.triggerEvent("before"+a,b);if(void 0!==e){if(!1===e)return!1;b=e}e=void 0;switch(a){case "login":this.gui_objects.loginform&&this.gui_objects.loginform.submit();break;case "mail":case "addressbook":case "settings":case "logout":this.switch_task(a);break;case "permaurl":if(d&&d.href&&d.target)return!0;if(this.env.permaurl)parent.location.href= +this.env.permaurl;break;case "menu-open":case "menu-save":return this.triggerEvent(a,{props:b}),!1;case "open":if(f=this.get_single_uid())return d.href="?_task="+this.env.task+"&_action=show&_mbox="+urlencode(this.env.mailbox)+"&_uid="+f,!0;break;case "list":this.reset_qsearch();"mail"==this.task?(this.list_mailbox(b),this.env.trash_mailbox&&!this.env.flag_for_deletion&&this.set_alttext("delete",this.env.mailbox!=this.env.trash_mailbox?"movemessagetotrash":"deletemessage")):"addressbook"==this.task&& +this.list_contacts(b);break;case "load-headers":this.load_headers(d);break;case "sort":f=b;g=this.env.sort_col==f?"ASC"==this.env.sort_order?"DESC":"ASC":"ASC";this.set_list_sorting(f,g);this.list_mailbox("","",f+"_"+g);break;case "nextpage":this.list_page("next");break;case "lastpage":this.list_page("last");break;case "previouspage":this.list_page("prev");break;case "firstpage":this.list_page("first");break;case "expunge":this.env.messagecount&&this.expunge_mailbox(this.env.mailbox);break;case "purge":case "empty-mailbox":this.env.messagecount&& +this.purge_mailbox(this.env.mailbox);break;case "show":if("mail"==this.task){if((f=this.get_single_uid())&&(!this.env.uid||f!=this.env.uid))this.env.mailbox==this.env.drafts_mailbox?this.goto_url("compose","_draft_uid="+f+"&_mbox="+urlencode(this.env.mailbox),!0):this.show_message(f)}else"addressbook"==this.task&&(h=b?b:this.get_single_cid())&&!("show"==this.env.action&&h==this.env.cid)&&this.load_contact(h,"show");break;case "add":"addressbook"==this.task?this.load_contact(0,"add"):"settings"==this.task&& +(this.identity_list.clear_selection(),this.load_identity(0,"add-identity"));break;case "edit":if("addressbook"==this.task&&(h=this.get_single_cid()))this.load_contact(h,"edit");else if("settings"==this.task&&b)this.load_identity(b,"edit-identity");else if("mail"==this.task&&(h=this.get_single_uid()))g=this.env.mailbox==this.env.drafts_mailbox?"_draft_uid=":"_uid=",this.goto_url("compose",g+h+"&_mbox="+urlencode(this.env.mailbox),!0);break;case "save":var k;if(g=this.gui_objects.editform){if("search"!= +this.env.action)if((k=$("input[name='_pagesize']",g))&&k.length&&isNaN(parseInt(k.val()))){alert(this.get_label("nopagesizewarning"));k.focus();break}else{if("reload"==b)g.action+="?_reload=1";else if("settings"==this.task&&0==this.env.identities_level%2&&(k=$("input[name='_email']",g))&&k.length&&!rcube_check_email(k.val())){alert(this.get_label("noemailwarning"));k.focus();break}$("input.placeholder").each(function(){if(this.value==this._placeholder)this.value=""})}if(parent.rcmail&&parent.rcmail.env.source)g.action= +this.add_url(g.action,"_orig_source",parent.rcmail.env.source);g.submit()}break;case "delete":"mail"==this.task?this.delete_messages():"addressbook"==this.task?this.delete_contacts():"settings"==this.task&&this.delete_identity();break;case "move":case "moveto":"mail"==this.task?this.move_messages(b):"addressbook"==this.task&&this.drag_active&&this.copy_contact(null,b);break;case "copy":"mail"==this.task&&this.copy_messages(b);break;case "mark":b&&this.mark_message(b);break;case "toggle_status":if(b&& +!b._row)break;g="read";if(b._row.uid)f=b._row.uid,this.message_list.rows[f].deleted?g="undelete":this.message_list.rows[f].unread||(g="unread");this.mark_message(g,f);break;case "toggle_flag":if(b&&!b._row)break;g="flagged";if(b._row.uid)f=b._row.uid,this.message_list.rows[f].flagged&&(g="unflagged");this.mark_message(g,f);break;case "always-load":if(this.env.uid&&this.env.sender){this.add_contact(urlencode(this.env.sender));window.setTimeout(function(){i.command("load-images")},300);break}case "load-images":this.env.uid&& +this.show_message(this.env.uid,!0,"preview"==this.env.action);break;case "load-attachment":g="_mbox="+urlencode(this.env.mailbox)+"&_uid="+this.env.uid+"&_part="+b.part;if(this.env.uid&&b.mimetype&&this.env.mimetypes&&0<=$.inArray(b.mimetype,this.env.mimetypes)&&("text/html"==b.mimetype&&(g+="&_safe=1"),this.attachment_win=window.open(this.env.comm_path+"&_action=get&"+g+"&_frame=1","rcubemailattachment"))){window.setTimeout(function(){i.attachment_win.focus()},10);break}this.goto_url("get",g+"&_download=1", +!1);break;case "select-all":this.select_all_mode=b?!1:!0;this.dummy_select=!0;"invert"==b?this.message_list.invert_selection():this.message_list.select_all("page"==b?"":b);this.dummy_select=null;break;case "select-none":this.select_all_mode=!1;this.message_list.clear_selection();break;case "expand-all":this.env.autoexpand_threads=1;this.message_list.expand_all();break;case "expand-unread":this.env.autoexpand_threads=2;this.message_list.collapse_all();this.expand_unread();break;case "collapse-all":this.env.autoexpand_threads= +0;this.message_list.collapse_all();break;case "nextmessage":this.env.next_uid&&this.show_message(this.env.next_uid,!1,"preview"==this.env.action);break;case "lastmessage":this.env.last_uid&&this.show_message(this.env.last_uid);break;case "previousmessage":this.env.prev_uid&&this.show_message(this.env.prev_uid,!1,"preview"==this.env.action);break;case "firstmessage":this.env.first_uid&&this.show_message(this.env.first_uid);break;case "checkmail":this.check_for_recent(!0);break;case "compose":g=this.url("mail/compose"); +if("mail"==this.task)g+="&_mbox="+urlencode(this.env.mailbox),b&&(g+="&_to="+urlencode(b));else if("addressbook"==this.task){if(b&&0=f.x2||a.y=f.y2){if(this.env.last_folder_target)$(this.get_folder_li(this.env.last_folder_target)).removeClass("droptarget"), +this.env.folder_coords[this.env.last_folder_target].on=0,this.env.last_folder_target=null}else for(b in this.env.folder_coords)if(f=this.env.folder_coords[b],a.x>=f.x1&&a.x=f.y1&&a.y  
',g[i.parent_uid]&&!1===g[i.parent_uid].expanded||(0==this.env.autoexpand_threads||2==this.env.autoexpand_threads)&& +(!g[i.parent_uid]||!g[i.parent_uid].expanded)?(m.style.display="none",i.expanded=!1):i.expanded=!0;else if(i.has_children){if(void 0===i.expanded&&(1==this.env.autoexpand_threads||2==this.env.autoexpand_threads&&i.unread_children))i.expanded=!0;j='
  
'}k+=' ';if(!bw.ie&&b.subject)g=d.mbox==this.env.drafts_mailbox?"_draft_uid":"_uid",b.subject='
'+b.subject+"";for(h in this.env.coltypes){f=this.env.coltypes[h];g=document.createElement("td");g.className=(""+f).toLowerCase();if("flag"==f)f=d.flagged?"flagged":"unflagged",f=' ';else if("attachment"==f)f=/application\/|multipart\/m/.test(d.ctype)? +' ':/multipart\/report/.test(d.ctype)?' ':" ";else if("status"==f)f=d.deleted?"deleted":d.seen?0 ';else if("threads"==f)f=j;else if("subject"==f){if(bw.ie)g.onmouseover=function(){rcube_webmail.long_subject_title_ie(this,i.depth+1)};f=k+b[f]}else f="priority"==f?0d.prio?' ': +" ":b[f];g.innerHTML=f;m.appendChild(g)}l.insert_row(m,e);e&&this.env.pagesize&&l.rowcount>this.env.pagesize&&(a=l.get_last_row(),l.remove_row(a),l.clear_selection(a))};this.set_list_sorting=function(a,b){$("#rcm"+this.env.sort_col).removeClass("sorted"+this.env.sort_order.toUpperCase());a&&$("#rcm"+a).addClass("sorted"+b);this.env.sort_col=a;this.env.sort_order=b};this.set_list_options=function(a,b,d,e){var f,h="";if(void 0===b)b=this.env.sort_col;if(!d)d=this.env.sort_order;if(this.env.sort_col!= +b||this.env.sort_order!=d)f=1,this.set_list_sorting(b,d);this.env.threading!=e&&(f=1,h+="&_threads="+e);if(a&&a.length){for(var g,k,j=[],i=this.env.coltypes,e=0;ed.location.href.indexOf(this.env.blankpage))d.location.href=this.env.blankpage}else if(!bw.safari&&!bw.konq)b[a?"show":"hide"]();!a&&this.busy&&this.set_busy(!1,null,this.env.frame_lock)};this.lock_frame=function(){if(!this.env.frame_lock)(this.is_framed()?parent.rcmail:this).env.frame_lock=this.set_busy(!0,"loading")};this.list_page=function(a){"next"==a?a=this.env.current_page+1:"last"==a?a=this.env.pagecount:"prev"==a&&1d.depth?(f=e-d.depth,h[b][f]&2||(h[b][f]=h[b][f]?h[b][f]+2:2)):e==d.depth&&(h[b][0]&2||(h[b][0]+=2)),d.depth>e);b--);h.push(Array(d.depth));h[h.length-1][0]=1;g.push(d.uid)}else{if(h.length){for(b in h)this.set_tree_icons(g[b],h[b]);h=[];g=[]}if(a&&k!=j[a].obj)break}k=k.nextSibling}if(h.length)for(b in h)this.set_tree_icons(g[b],h[b])};this.set_tree_icons= +function(a,b){var d,e=[],f="",h=b.length;for(d=0;d'):f+('
');f&&$("#rcmtab"+a).html(f)};this.update_thread_root=function(a,b){if(this.env.threading){var d=this.message_list.find_root(a); +if(a!=d){var e=this.message_list.rows[d];if("read"==b&&e.unread_children)e.unread_children--;else if("unread"==b&&e.has_children)e.unread_children=e.unread_children?e.unread_children+1:1;else return;this.set_message_icon(d);this.set_unread_children(d)}}};this.update_thread=function(a){if(!this.env.threading)return 0;var b,d=0,e=this.message_list.rows,f=e[a],h=e[a].depth,g=[];f.depth?f.unread&&(a=this.message_list.find_root(a),e[a].unread_children--,this.set_unread_children(a)):d--;a=f.parent_uid; +for(f=f.obj.nextSibling;f;){if(1==f.nodeType&&(b=e[f.uid])){if(!b.depth||b.depth<=h)break;b.depth--;$("#rcmtab"+b.uid).width(15*b.depth).html("");if(b.depth){if(b.depth==h)b.parent_uid=a;b.unread&&g.length&&g[g.length-1].unread_children++}else{d++;b.parent_uid=0;if(b.has_children)$("#rcmrow"+b.uid+" .leaf:first").attr("id","rcmexpando"+b.uid).attr("class","none"!=b.obj.style.display?"expanded":"collapsed").bind("mousedown",{uid:b.uid,p:this},function(a){return a.data.p.expand_message_row(a,a.data.uid)}), +b.unread_children=0,g.push(b);"none"==b.obj.style.display&&$(b.obj).show()}}f=f.nextSibling}for(b=0;b$.inArray(k,j)&&j.push(k)),this.message_list.remove_row(g,this.env.display_next&&h==i.length-1);this.env.display_next||this.message_list.clear_selection();for(h=0,len= +j.length;hf?d+="&_count="+-1*f:0e||0').html('
'+this.get_label("nosubjectwarning")+"
").appendTo(document.body);var l=$("").attr("type", +"text").attr("size",30).appendTo(b).val(this.get_label("nosubject")),d={};d[this.get_label("cancel")]=function(){g.focus();$(this).dialog("close")};d[this.get_label("sendmessage")]=function(){g.val(l.val());$(this).dialog("close");i.command(a,{nocheck:!0})};b.dialog({modal:!0,resizable:!1,buttons:d,close:function(){$(this).remove()}});l.select();return!1}this.stop_spellchecking();window.tinyMCE&&(b=tinyMCE.get(this.env.composebody));if(!b&&""==k.val()&&!confirm(this.get_label("nobodywarning")))return k.focus(), +!1;if(b){if(!b.getContent()&&!confirm(this.get_label("nobodywarning")))return b.focus(),!1;tinyMCE.triggerSave()}return!0};this.toggle_editor=function(a){if("html"==a.mode)this.display_spellcheck_controls(!1),this.plain2html($("#"+a.id).val(),a.id),tinyMCE.execCommand("mceAddControl",!1,a.id);else{var b=tinyMCE.get(a.id);b.plugins.spellchecker&&b.plugins.spellchecker.active&&b.execCommand("mceSpellCheck",!1);if(b=b.getContent()){if(!confirm(this.get_label("editorwarning")))return!1;this.html2plain(b, +a.id)}tinyMCE.execCommand("mceRemoveControl",!1,a.id);this.display_spellcheck_controls(!0)}return!0};this.stop_spellchecking=function(){var a;if(window.tinyMCE&&(a=tinyMCE.get(this.env.composebody)))a.plugins.spellchecker&&a.plugins.spellchecker.active&&a.execCommand("mceSpellCheck");else if((a=this.env.spellcheck)&&!this.spellcheck_ready)$(a.spell_span).trigger("click"),this.set_spellcheck_state("ready")};this.display_spellcheck_controls=function(a){this.env.spellcheck&&(a||this.stop_spellchecking(), +$(this.env.spellcheck.spell_container).css("visibility",a?"visible":"hidden"))};this.set_spellcheck_state=function(a){this.spellcheck_ready="ready"==a||"no_error_found"==a;this.enable_command("spellcheck",this.spellcheck_ready)};this.spellcheck_lang=function(){var a;if(window.tinyMCE&&(a=tinyMCE.get(this.env.composebody))&&a.plugins.spellchecker)return a.plugins.spellchecker.selectedLang;if(this.env.spellcheck)return GOOGIE_CUR_LANG};this.spellcheck_resume=function(a,b){if(a){var d=tinyMCE.get(this.env.composebody), +e=d.plugins.spellchecker;e.active=1;e._markWords(b);d.nodeChanged()}else{var e=this.env.spellcheck;e.prepare(!1,!0);e.processData(b)}};this.set_draft_id=function(a){$("input[name='_draft_saveid']").val(a)};this.auto_save_start=function(){if(this.env.draft_autosave)this.save_timer=self.setTimeout(function(){i.command("savedraft")},1E3*this.env.draft_autosave);this.busy=!1};this.compose_field_hash=function(a){var b,d="",e=$("[name='_to']").val(),f=$("[name='_cc']").val(),h=$("[name='_bcc']").val(), +g=$("[name='_subject']").val();e&&(d+=e+":");f&&(d+=f+":");h&&(d+=h+":");g&&(d+=g+":");d=window.tinyMCE&&(b=tinyMCE.get(this.env.composebody))?d+b.getContent():d+$("[name='_message']").val();if(this.env.attachments)for(var k in this.env.attachments)d+=k;if(a)this.cmp_hash=d;return d};this.change_identity=function(a,b){if(!a||!a.options)return!1;if(!b)b=this.env.show_sig;var d,e=-1,f=a.options[a.selectedIndex].value,h=$("[name='_message']"),g=h.val(),k="1"==$("input[name='_is_html']").val(),j=this.env.identity; +d=this.env.sig_above&&("reply"==this.env.compose_mode||"forward"==this.env.compose_mode)?"---":"-- ";this.env.signatures&&this.env.signatures[f]?(this.enable_command("insert-sig",!0),this.env.compose_commands.push("insert-sig")):this.enable_command("insert-sig",!1);if(k){if(b&&this.env.signatures&&(e=tinyMCE.get(this.env.composebody),h=e.dom.get("_rc_sig"),h||(j=e.getBody(),g=e.getDoc(),h=g.createElement("div"),h.setAttribute("id","_rc_sig"),this.env.sig_above?(e.getWin().focus(),e=e.selection.getNode(), +"BODY"==e.nodeName?(j.insertBefore(h,j.firstChild),j.insertBefore(g.createElement("br"),j.firstChild)):(j.insertBefore(h,e.nextSibling),j.insertBefore(g.createElement("br"),e.nextSibling))):(bw.ie&&j.appendChild(g.createElement("br")),j.appendChild(h))),this.env.signatures[f]))this.env.signatures[f].is_html?(j=this.env.signatures[f].text,this.env.signatures[f].plain_text.match(/^--[ -]\r?\n/m)||(j=d+"
"+j)):(j=this.env.signatures[f].text,j.match(/^--[ -]\r?\n/m)||(j=d+"\n"+j),j="
"+j+"
"), +h.innerHTML=j}else b&&j&&this.env.signatures&&this.env.signatures[j]&&(j=this.env.signatures[j].is_html?this.env.signatures[j].plain_text:this.env.signatures[j].text,j=j.replace(/\r\n/g,"\n"),j.match(/^--[ -]\n/m)||(j=d+"\n"+j),e=this.env.sig_above?g.indexOf(j):g.lastIndexOf(j),0<=e&&(g=g.substring(0,e)+g.substring(e+j.length,g.length))),b&&this.env.signatures&&this.env.signatures[f]?(j=this.env.signatures[f].is_html?this.env.signatures[f].plain_text:this.env.signatures[f].text,j=j.replace(/\r\n/g, +"\n"),j.match(/^--[ -]\n/m)||(j=d+"\n"+j),this.env.sig_above?0<=e?(g=g.substring(0,e)+j+g.substring(e,g.length),d=e-1):(pos=this.get_caret_pos(h.get(0)))?(g=g.substring(0,pos)+"\n"+j+"\n\n"+g.substring(pos,g.length),d=pos):(d=0,g="\n\n"+j+"\n\n"+g.replace(/^[\r\n]+/,"")):(g=g.replace(/[\r\n]+$/,""),d=!this.env.top_posting&&g.length?g.length+1:0,g+="\n\n"+j)):d=this.env.top_posting?0:g.length,h.val(g),this.set_caret_pos(h.get(0),d);this.env.identity=f;return!0};this.upload_file=function(a){if(!a)return!1; +var b,d=0,e=$("input[type=file]",a).get(0),f=e.files?e.files.length:e.value?1:0;if(f){if(e.files&&this.env.max_filesize&&this.env.filesizeerror){for(b=0;bthis.env.max_filesize){this.display_message(this.env.filesizeerror,"error");return}}b=this.async_upload_form(a,"upload",function(a){var b,d="";try{if(this.contentDocument)b=this.contentDocument;else if(this.contentWindow)b=this.contentWindow.document;d=b.childNodes[0].innerHTML}catch(e){}if(!d.match(/add2attachment/)&& +(!bw.opera||rcmail.env.uploadframe&&rcmail.env.uploadframe==a.data.ts))d.match(/display_message/)||rcmail.display_message(rcmail.get_label("fileuploaderror"),"error"),rcmail.remove_from_attachment_list(a.data.ts);if(bw.opera)rcmail.env.uploadframe=a.data.ts});f=""+this.get_label("uploading"+(1";d=b.replace(/^rcmupload/,"");this.env.loadingicon&&(f=''+f);this.env.cancelicon&&(f=''+f);this.add2attachment_list(d,{name:"",html:f,complete:!1});this.env.upload_progress_time&&this.upload_progress_start("upload",d)}this.gui_objects.attachmentform=a;return!0};this.add2attachment_list=function(a,b,d){if(!this.gui_objects.attachmentlist)return!1;var e,f=$("
  • ").attr("id",a).html(b.html);d&&(e=document.getElementById(d))?f.replaceAll(e):f.appendTo(this.gui_objects.attachmentlist);d&&this.env.attachments[d]&& +delete this.env.attachments[d];this.env.attachments[a]=b;return!0};this.remove_from_attachment_list=function(a){delete this.env.attachments[a];$("#"+a).remove()};this.remove_attachment=function(a){a&&this.env.attachments[a]&&this.http_post("remove-attachment",{_id:this.env.compose_id,_file:a});return!0};this.cancel_attachment_upload=function(a,b){if(!a||!b)return!1;this.remove_from_attachment_list(a);$("iframe[name='"+b+"']").remove();return!1};this.upload_progress_start=function(a,b){window.setTimeout(function(){rcmail.http_request(a, +{_progress:b})},1E3*this.env.upload_progress_time)};this.upload_progress_update=function(a){var b=$("#"+a.name+"> span");b.length&&a.text&&(b.text(a.text),a.done||this.upload_progress_start(a.action,a.name))};this.add_contact=function(a){a&&this.http_post("addcontact","_address="+a);return!0};this.qsearch=function(a){if(""!=a){var b=this.set_busy(!0,"searching");this.message_list?this.clear_message_list():this.contact_list&&this.list_contacts_clear();this.env.current_page=1;r=this.http_request("search", +this.search_params(a)+(this.env.source?"&_source="+urlencode(this.env.source):"")+(this.env.group?"&_gid="+urlencode(this.env.group):""),b);this.env.qsearch={lock:b,request:r}}};this.search_params=function(a,b){var d,e=[],f=[],h=this.env.search_mods,g=this.env.mailbox;if(!b&&this.gui_objects.search_filter)b=this.gui_objects.search_filter.value;if(!a&&this.gui_objects.qsearchbox)a=this.gui_objects.qsearchbox.value;b&&e.push("_filter="+urlencode(b));if(a&&(e.push("_q="+urlencode(a)),h&&this.message_list&& +(h=h[g]?h[g]:h["*"]),h)){for(d in h)f.push(d);e.push("_headers="+f.join(","))}g&&e.push("_mbox="+urlencode(g));return e.join("&")};this.reset_qsearch=function(){if(this.gui_objects.qsearchbox)this.gui_objects.qsearchbox.value="";this.env.qsearch&&this.abort_request(this.env.qsearch);this.env.qsearch=null;this.env.search_request=null;this.env.search_id=null};this.sent_successfully=function(a,b){this.display_message(b,a);window.setTimeout(function(){i.list_mailbox()},500)};this.ksearch_keydown=function(a, +b,d){this.ksearch_timer&&clearTimeout(this.ksearch_timer);var e=rcube_event.get_keycode(a),f=rcube_event.get_modifier(a);switch(e){case 38:case 40:if(!this.ksearch_pane)break;e=38==e?1:0;b=document.getElementById("rcmksearchSelected");if(!b)b=this.ksearch_pane.__ul.firstChild;b&&this.ksearch_select(e?b.previousSibling:b.nextSibling);return rcube_event.cancel(a);case 9:if(f==SHIFT_KEY||!this.ksearch_visible()){this.ksearch_hide();return}case 13:if(!this.ksearch_visible())return!1;this.insert_recipient(this.ksearch_selected); +this.ksearch_hide();return rcube_event.cancel(a);case 27:this.ksearch_hide();return;case 37:case 39:if(f!=SHIFT_KEY)return}this.ksearch_timer=window.setTimeout(function(){i.ksearch_get_results(d)},200);this.ksearch_input=b;return!0};this.ksearch_visible=function(){return null!==this.ksearch_selected&&void 0!==this.ksearch_selected&&this.ksearch_value};this.ksearch_select=function(a){var b=$("#rcmksearchSelected");b[0]&&a&&b.removeAttr("id").removeClass("selected");if(a)$(a).attr("id","rcmksearchSelected").addClass("selected"), +this.ksearch_selected=a._rcm_id};this.insert_recipient=function(a){if(!(null===a||!this.env.contacts[a]||!this.ksearch_input)){var b=this.ksearch_input.value,d=this.get_caret_pos(this.ksearch_input),d=b.lastIndexOf(this.ksearch_value,d),e=!1,f="",h=b.substring(0,d),b=b.substring(d+this.ksearch_value.length,b.length);this.ksearch_destroy();"object"===typeof this.env.contacts[a]&&this.env.contacts[a].id?(f+=this.env.contacts[a].name+this.env.recipients_delimiter,this.group2expand=$.extend({},this.env.contacts[a]), +this.group2expand.input=this.ksearch_input,this.http_request("mail/group-expand","_source="+urlencode(this.env.contacts[a].source)+"&_gid="+urlencode(this.env.contacts[a].id),!1)):"string"===typeof this.env.contacts[a]&&(f=this.env.contacts[a]+this.env.recipients_delimiter,e=!0);this.ksearch_input.value=h+f+b;d+=f.length;this.ksearch_input.setSelectionRange&&this.ksearch_input.setSelectionRange(d,d);e&&this.triggerEvent("autocomplete_insert",{field:this.ksearch_input,insert:f})}};this.replace_group_recipients= +function(a,b){if(this.group2expand&&this.group2expand.id==a)this.group2expand.input.value=this.group2expand.input.value.replace(this.group2expand.name,b),this.triggerEvent("autocomplete_insert",{field:this.group2expand.input,insert:b}),this.group2expand=null};this.ksearch_get_results=function(a){var b=this.ksearch_input?this.ksearch_input.value:null;if(null!==b){this.ksearch_pane&&this.ksearch_pane.is(":visible")&&this.ksearch_pane.hide();var d=this.get_caret_pos(this.ksearch_input),e=b.lastIndexOf(this.env.recipients_separator, +d-1),b=b.substring(e+1,d),e=this.env.autocomplete_min_length,d=this.ksearch_data,b=$.trim(b);if(b!=this.ksearch_value)if(this.ksearch_destroy(),b.length&&b.length"),this.ksearch_pane=$("
    ").attr("id","rcmKSearchpane").css({position:"absolute","z-index":3E4}).append(h).appendTo(document.body),this.ksearch_pane.__ul=h[0];h=this.ksearch_pane.__ul;d&&this.ksearch_pane.data("reqid")==d?l-=h.childNodes.length:(this.ksearch_pane.data("reqid",d),h.innerHTML="",this.env.contacts= +[],e=$(this.ksearch_input).offset(),this.ksearch_pane.css({left:e.left+"px",top:e.top+this.ksearch_input.offsetHeight+"px",display:"none"}));if(a&&(f=a.length))for(e=0;e/g,">").replace(/##([^%]+)%%/g,"$1"),g.onmouseover=function(){i.ksearch_select(this)},g.onmouseup=function(){i.ksearch_click(this)},g._rcm_id= +this.env.contacts.length+e,h.appendChild(g),l-=1;if(h.childNodes.length&&(this.ksearch_pane.show(),!this.env.contacts.length))$("li:first",h).attr("id","rcmksearchSelected").addClass("selected"),this.ksearch_selected=0;if(f)this.env.contacts=this.env.contacts.concat(a);if(j.id==d)if(j.num--,0").attr("type","text").val(this.env.contactgroups["G"+ +this.env.source+this.env.group].name);this.name_input.bind("keydown",function(a){return rcmail.add_input_keydown(a)});this.env.group_renaming=!0;var a,b=this.get_folder_li(this.env.source+this.env.group,"rcmliG");b&&(a=b.firstChild)&&$(a).hide().before(this.name_input)}this.name_input.select().focus()}};this.group_delete=function(){if(this.env.group&&confirm(this.get_label("deletegroupconfirm"))){var a=this.set_busy(!0,"groupdeleting");this.http_post("group-delete","_source="+urlencode(this.env.source)+ +"&_gid="+urlencode(this.env.group),a)}};this.remove_group_item=function(a){var b,d="G"+a.source+a.id;if(b=this.get_folder_li(d))this.triggerEvent("group_delete",{source:a.source,id:a.id,li:b}),b.parentNode.removeChild(b),delete this.env.contactfolders[d],delete this.env.contactgroups[d];this.list_contacts(a.source,0)};this.add_input_row=function(a){if(this.gui_objects.folderlist){if(!this.name_input)this.name_input=$("").attr("type","text").data("tt",a),this.name_input.bind("keydown",function(a){return rcmail.add_input_keydown(a)}), +this.name_input_li=$("
  • ").addClass(a).append(this.name_input),this.name_input_li.insertAfter("contactsearch"==a?$("li:last",this.gui_objects.folderlist):this.get_folder_li(this.env.source));this.name_input.select().focus()}};this.add_input_keydown=function(a){var b=rcube_event.get_keycode(a),d=$(a.target),a=d.data("tt");if(13==b){if(b=d.val())d=this.set_busy(!0,"loading"),"contactsearch"==a?this.http_post("search-create","_search="+urlencode(this.env.search_request)+"&_name="+urlencode(b),d):this.env.group_renaming? +this.http_post("group-rename","_source="+urlencode(this.env.source)+"&_gid="+urlencode(this.env.group)+"&_name="+urlencode(b),d):this.http_post("group-create","_source="+urlencode(this.env.source)+"&_name="+urlencode(b),d);return!1}27==b&&this.reset_add_input();return!0};this.reset_add_input=function(){if(this.name_input){if(this.env.group_renaming)this.name_input.parent().children().last().show(),this.env.group_renaming=!1;this.name_input.remove();this.name_input_li&&this.name_input_li.remove(); +this.name_input=this.name_input_li=null}this.enable_command("list","listgroup",!0)};this.insert_contact_group=function(a){this.reset_add_input();a.type="group";var b="G"+a.source+a.id,d=$("").attr("href","#").attr("rel",a.source+":"+a.id).click(function(){return rcmail.command("listgroup",a,this)}).html(a.name),d=$("
  • ").attr({id:"rcmli"+this.html_identifier(b),"class":"contactgroup"}).append(d);this.env.contactfolders[b]=this.env.contactgroups[b]=a;this.add_contact_group_row(a,d);this.triggerEvent("group_insert", +{id:a.id,source:a.source,name:a.name,li:d[0]})};this.update_contact_group=function(a){this.reset_add_input();var b="G"+a.source+a.id,d=this.get_folder_li(b),e;if(d&&a.newid){e="G"+a.source+a.newid;var f=$.extend({},a);d.id="rcmli"+this.html_identifier(e);this.env.contactfolders[e]=this.env.contactfolders[b];this.env.contactfolders[e].id=a.newid;this.env.group=a.newid;delete this.env.contactfolders[b];delete this.env.contactgroups[b];f.id=a.newid;f.type="group";e=$("").attr("href","#").attr("rel", +a.source+":"+a.newid).click(function(){return rcmail.command("listgroup",f,this)}).html(a.name);$(d).children().replaceWith(e)}else if(d&&(e=d.firstChild)&&"a"==e.tagName.toLowerCase())e.innerHTML=a.name;this.env.contactfolders[b].name=this.env.contactgroups[b].name=a.name;this.add_contact_group_row(a,$(d),!0);this.triggerEvent("group_update",{id:a.id,source:a.source,name:a.name,li:d[0],newid:a.newid})};this.add_contact_group_row=function(a,b,d){var e=a.name.toUpperCase(),f=this.get_folder_li(a.source), +a="rcmliG"+this.html_identifier(a.source);d?(d=b.clone(!0),b.remove()):d=b;$('li[id^="'+a+'"]',this.gui_objects.folderlist).each(function(a,b){if(e>=$(this).text().toUpperCase())f=b;else return!1});d.insertAfter(f)};this.update_group_commands=function(){var a=""!=this.env.source?this.env.address_sources[this.env.source]:null;this.enable_command("group-create",a&&a.groups&&!a.readonly);this.enable_command("group-rename","group-delete",a&&a.groups&&this.env.group&&!a.readonly)};this.init_edit_field= +function(a,b){b||(b=$(".ff_"+a));b.focus(function(){i.focus_textfield(this)}).blur(function(){i.blur_textfield(this)}).each(function(){this._placeholder=this.title=i.env.coltypes[a].label;i.blur_textfield(this)})};this.insert_edit_field=function(a,b,d){var e=$("#ff_"+a);if(e.length)e.show().focus(),$(d).children('option[value="'+a+'"]').prop("disabled",!0);else if($(".ff_"+a),e=$("#contactsection"+b+" .contactcontroller"+a),e.length||(e=$("
    ").addClass("contactfieldgroup contactcontroller"+ +a).insertAfter($("#contactsection"+b+" .contactfieldgroup").last())),e.length&&"FIELDSET"==e.get(0).nodeName){var f,b=this.env.coltypes[a],h=$("
    ").addClass("row"),g=$("
    ").addClass("contactfieldcontent data"),k=$("
    ").addClass("contactfieldlabel label");b.subtypes_select?k.html(b.subtypes_select):k.html(b.label);var j=1!=b.limit?"[]":"";if("text"==b.type||"date"==b.type)f=$("").addClass("ff_"+a).attr({type:"text",name:"_"+a+j,size:b.size}).appendTo(g),this.init_edit_field(a,f), +"date"==b.type&&$.datepicker&&f.datepicker();else if("composite"==b.type){var l,n,m=[],o=[];if(f=this.env[a+"_template"])for(l=0;l").addClass("ff_"+l).attr({type:"text",name:"_"+l+j,size:f.size}).appendTo(g),g.append(o[p]||" "),this.init_edit_field(l,f),n||(n=f);f=n}else if("select"==b.type){f=$("').attr('type', 'text').attr('size', 30).appendTo(myprompt).val(this.get_label('nosubject')); - // user hit cancel, so don't send - if (!subject && subject !== '') { + var buttons = {}; + buttons[this.get_label('cancel')] = function(){ input_subject.focus(); - return false; - } - else - input_subject.val((subject ? subject : this.get_label('nosubject'))); + $(this).dialog('close'); + }; + buttons[this.get_label('sendmessage')] = function(){ + input_subject.val(prompt_value.val()); + $(this).dialog('close'); + ref.command(cmd, { nocheck:true }); // repeat command which triggered this + }; + + myprompt.dialog({ + modal: true, + resizable: false, + buttons: buttons, + close: function(event, ui) { $(this).remove() } + }); + prompt_value.select(); + return false; } // Apply spellcheck changes if spell checker is active @@ -3151,7 +3200,7 @@ function rcube_webmail() sig = this.env.signatures[sig].is_html ? this.env.signatures[sig].plain_text : this.env.signatures[sig].text; sig = sig.replace(/\r\n/g, '\n'); - if (!sig.match(/^--[ -]\n/)) + if (!sig.match(/^--[ -]\n/m)) sig = sig_separator + '\n' + sig; p = this.env.sig_above ? message.indexOf(sig) : message.lastIndexOf(sig); @@ -3163,7 +3212,7 @@ function rcube_webmail() sig = this.env.signatures[id]['is_html'] ? this.env.signatures[id]['plain_text'] : this.env.signatures[id]['text']; sig = sig.replace(/\r\n/g, '\n'); - if (!sig.match(/^--[ -]\n/)) + if (!sig.match(/^--[ -]\n/m)) sig = sig_separator + '\n' + sig; if (this.env.sig_above) { @@ -3232,12 +3281,12 @@ function rcube_webmail() if (this.env.signatures[id]) { if (this.env.signatures[id].is_html) { sig = this.env.signatures[id].text; - if (!this.env.signatures[id].plain_text.match(/^--[ -]\r?\n/)) + if (!this.env.signatures[id].plain_text.match(/^--[ -]\r?\n/m)) sig = sig_separator + '
    ' + sig; } else { sig = this.env.signatures[id].text; - if (!sig.match(/^--[ -]\r?\n/)) + if (!sig.match(/^--[ -]\r?\n/m)) sig = sig_separator + '\n' + sig; sig = '
    ' + sig + '
    '; } @@ -3341,16 +3390,8 @@ function rcube_webmail() this.remove_from_attachment_list = function(name) { - if (this.env.attachments[name]) - delete this.env.attachments[name]; - - if (!this.gui_objects.attachmentlist) - return false; - - var list = this.gui_objects.attachmentlist.getElementsByTagName("li"); - for (i=0; i 0; i++) { + if (results && (len = results.length)) { + for (i=0; i < len && maxlen > 0; i++) { text = typeof results[i] === 'object' ? results[i].name : results[i]; li = document.createElement('LI'); - li.innerHTML = text.replace(new RegExp('('+RegExp.escape(s_val)+')', 'ig'), '##$1%%').replace(//g, '>').replace(/##([^%]+)%%/g, '$1'); + li.innerHTML = text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%').replace(//g, '>').replace(/##([^%]+)%%/g, '$1'); li.onmouseover = function(){ ref.ksearch_select(this); }; li.onmouseup = function(){ ref.ksearch_click(this) }; li._rcm_id = this.env.contacts.length + i; @@ -3722,20 +3781,28 @@ function rcube_webmail() } } - if (results && results.length) + if (len) this.env.contacts = this.env.contacts.concat(results); // run next parallel search - if (maxlen > 0 && this.ksearch_data.id == reqid && this.ksearch_data.sources.length) { - var lock, xhr, props = this.ksearch_data, source = props.sources.shift(); - if (source) { + if (data.id == reqid) { data.num--; - lock = this.display_message(this.get_label('searching'), 'loading'); - xhr = this.http_post(props.action, '_search='+urlencode(s_val)+'&_id='+reqid - +'&_source='+urlencode(source), lock); - - this.ksearch_data.locks.push(lock); - this.ksearch_data.requests.push(xhr); + if (maxlen > 0 && data.sources.length) { + var lock, xhr, source = data.sources.shift(); + if (source) { + lock = this.display_message(this.get_label('searching'), 'loading'); + xhr = this.http_post(data.action, '_search='+urlencode(value)+'&_id='+reqid + +'&_source='+urlencode(source), lock); + + this.ksearch_data.locks.push(lock); + this.ksearch_data.requests.push(xhr); + } + } + else if (!maxlen) { + if (!this.ksearch_msg) + this.ksearch_msg = this.display_message(this.get_label('autocompletemore')); + // abort pending searches + this.ksearch_abort(); } } }; @@ -3769,8 +3836,24 @@ function rcube_webmail() this.ksearch_destroy(); }; - // Aborts pending autocomplete requests + // Clears autocomplete data/requests this.ksearch_destroy = function() + { + this.ksearch_abort(); + + if (this.ksearch_info) + this.hide_message(this.ksearch_info); + + if (this.ksearch_msg) + this.hide_message(this.ksearch_msg); + + this.ksearch_data = null; + this.ksearch_info = null; + this.ksearch_msg = null; + } + + // Aborts pending autocomplete requests + this.ksearch_abort = function() { var i, len, ac = this.ksearch_data; @@ -3779,9 +3862,8 @@ function rcube_webmail() for (i=0, len=ac.locks.length; i 0); + this.enable_command('compose', this.env.group || list.selection.length > 0); this.enable_command('edit', id && writable); this.enable_command('delete', list.selection.length && writable); @@ -3832,7 +3914,7 @@ function rcube_webmail() this.list_contacts = function(src, group, page) { - var add_url = '', + var folder, add_url = '', target = window; if (!src) @@ -3848,7 +3930,12 @@ function rcube_webmail() 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; @@ -3893,8 +3980,8 @@ function rcube_webmail() 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); @@ -3904,7 +3991,8 @@ function rcube_webmail() { this.contact_list.clear(true); this.show_contentframe(false); - this.enable_command('delete', 'compose', false); + this.enable_command('delete', false); + this.enable_command('compose', this.env.group ? true : false); }; // load contact record @@ -3976,9 +4064,11 @@ function rcube_webmail() this.delete_contacts = function() { + var selection = this.contact_list.get_selection(), + undelete = this.env.source && this.env.address_sources[this.env.source].undelete; + // exit if no mailbox specified or if selection is empty - var selection = this.contact_list.get_selection(); - if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm'))) + if (!(selection.length || this.env.cid) || (!undelete && !confirm(this.get_label('deletecontactconfirm')))) return; var id, n, a_cids = [], qs = ''; @@ -4005,7 +4095,10 @@ function rcube_webmail() qs += '&_search='+this.env.search_request; // send request to server - this.http_post('delete', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source)+'&_from='+(this.env.action ? this.env.action : '')+qs); + this.http_post('delete', '_cid='+urlencode(a_cids.join(',')) + +'&_source='+urlencode(this.env.source) + +'&_from='+(this.env.action ? this.env.action : '')+qs, + this.display_message(this.get_label('contactdeleting'), 'loading')); return true; }; @@ -4015,7 +4108,7 @@ function rcube_webmail() { var c, row, list = this.contact_list; - cid = String(cid).replace(this.identifier_expr, '_'); + cid = this.html_identifier(cid); // when in searching mode, concat cid with the source name if (!list.rows[cid]) { @@ -4031,7 +4124,7 @@ function rcube_webmail() // cid change if (newcid) { - newcid = String(newcid).replace(this.identifier_expr, '_'); + newcid = this.html_identifier(newcid); row.id = 'rcmrow' + newcid; list.remove_row(cid); list.init_row(row); @@ -4044,31 +4137,29 @@ function rcube_webmail() // add row to contacts list this.add_contact_row = function(cid, cols, select) { - if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0]) + if (!this.gui_objects.contactslist) return false; - var tbody = this.gui_objects.contactslist.tBodies[0], - rowcount = tbody.rows.length, - even = rowcount%2, + var c, list = this.contact_list, row = document.createElement('tr'); - row.id = 'rcmrow'+String(cid).replace(this.identifier_expr, '_'); - row.className = 'contact '+(even ? 'even' : 'odd'); + row.id = 'rcmrow'+this.html_identifier(cid); + row.className = 'contact'; - if (this.contact_list.in_selection(cid)) + if (list.in_selection(cid)) row.className += ' selected'; // add each submitted col - for (var c in cols) { + for (c in cols) { col = document.createElement('td'); col.className = String(c).toLowerCase(); col.innerHTML = cols[c]; row.appendChild(col); } - this.contact_list.insert_row(row); + list.insert_row(row); - this.enable_command('export', (this.contact_list.rowcount > 0)); + this.enable_command('export', list.rowcount > 0); }; this.init_contact_form = function() @@ -4090,24 +4181,27 @@ function rcube_webmail() this.selectedIndex = 0; }); + // enable date pickers on date fields + if ($.datepicker && this.env.date_format) { + $.datepicker.setDefaults({ + dateFormat: this.env.date_format, + changeMonth: true, + changeYear: true, + yearRange: '-100:+10', + showOtherMonths: true, + selectOtherMonths: true, + monthNamesShort: this.env.month_names, + onSelect: function(dateText) { $(this).focus().val(dateText) } + }); + $('input.datepicker').datepicker(); + } + $("input[type='text']:visible").first().focus(); }; this.group_create = function() { - if (!this.gui_objects.folderlist) - return; - - if (!this.name_input) { - this.name_input = $('').attr('type', 'text'); - this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); }); - this.name_input_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() @@ -4153,18 +4247,40 @@ function rcube_webmail() 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 = $('').attr('type', 'text').data('tt', type); + this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); }); + this.name_input_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); @@ -4209,7 +4325,7 @@ function rcube_webmail() .attr('rel', prop.source+':'+prop.id) .click(function() { return rcmail.command('listgroup', prop, this); }) .html(prop.name), - li = $('
  • ').attr({id: 'rcmli'+key.replace(this.identifier_expr, '_'), 'class': 'contactgroup'}) + li = $('
  • ').attr({id: 'rcmli'+this.html_identifier(key), 'class': 'contactgroup'}) .append(link); this.env.contactfolders[key] = this.env.contactgroups[key] = prop; @@ -4232,7 +4348,7 @@ function rcube_webmail() var newkey = 'G'+prop.source+prop.newid, newprop = $.extend({}, prop);; - li.id = String('rcmli'+newkey).replace(this.identifier_expr, '_'); + li.id = 'rcmli' + this.html_identifier(newkey); this.env.contactfolders[newkey] = this.env.contactfolders[key]; this.env.contactfolders[newkey].id = prop.newid; this.env.group = prop.newid; @@ -4264,7 +4380,7 @@ function rcube_webmail() { var row, name = prop.name.toUpperCase(), sibling = this.get_folder_li(prop.source), - prefix = 'rcmliG'+(prop.source).replace(this.identifier_expr, '_'); + prefix = 'rcmliG' + this.html_identifier(prop.source); // When renaming groups, we need to remove it from DOM and insert it in the proper place if (reloc) { @@ -4335,6 +4451,9 @@ function rcube_webmail() .appendTo(cell); this.init_edit_field(col, input); + + if (colprop.type == 'date' && $.datepicker) + input.datepicker(); } else if (colprop.type == 'composite') { var childcol, cp, first, templ, cols = [], suffices = []; @@ -4480,11 +4599,106 @@ function rcube_webmail() // 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 = $('').attr('href', '#') + .attr('rel', id) + .click(function() { return rcmail.command('listsearch', id, this); }) + .html(name), + li = $('
  • ').attr({id: 'rcmli' + this.html_identifier(key), '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); }; @@ -4970,17 +5184,18 @@ function rcube_webmail() init_button(cmd, this.buttons[cmd][i]); } } + + // set active task button + this.set_button(this.task, 'sel'); }; // set button to a specific state this.set_button = function(command, state) { - var button, obj, a_buttons = this.buttons[command]; + var n, button, obj, a_buttons = this.buttons[command], + len = a_buttons ? a_buttons.length : 0; - if (!a_buttons || !a_buttons.length) - return false; - - for (var n=0; n'+plainText+''); + + plain = plain.replace(/&/g, '&').replace(//g, '>'); + $('#'+id).val(plain ? '
    '+plain+'
    ' : ''); + this.set_busy(false, null, lock); }; @@ -5580,7 +5790,7 @@ function rcube_webmail() var base = this.env.comm_path; // overwrite task name - if (query._action.match(/([a-z]+)\/([a-z-_.]+)/)) { + if (query._action.match(/([a-z]+)\/([a-z0-9-_.]+)/)) { query._action = RegExp.$2; base = base.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); } @@ -5794,6 +6004,8 @@ function rcube_webmail() 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 }); } @@ -6008,6 +6220,23 @@ rcube_webmail.long_subject_title = function(elem, indent) } }; +rcube_webmail.long_subject_title_ie = function(elem, indent) +{ + if (!elem.title) { + var $elem = $(elem), + txt = $.trim($elem.text()), + tmp = $('').text(txt) + .css({'position': 'absolute', 'float': 'left', 'visibility': 'hidden', + 'font-size': $elem.css('font-size'), 'font-weight': $elem.css('font-weight')}) + .appendTo($('body')), + w = tmp.width(); + + tmp.remove(); + if (w + indent * 15 > $elem.width()) + elem.title = txt; + } +}; + // copy event engine prototype rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener; rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener; diff --git a/program/js/common.js b/program/js/common.js index a9b6f53..7d1f173 100644 --- a/program/js/common.js +++ b/program/js/common.js @@ -1,25 +1,28 @@ var CONTROL_KEY=1,SHIFT_KEY=2,CONTROL_SHIFT_KEY=3; -function roundcube_browser(){var a=navigator;this.ver=parseFloat(a.appVersion);this.appver=a.appVersion;this.agent=a.userAgent;this.agent_lc=a.userAgent.toLowerCase();this.name=a.appName;this.vendor=a.vendor?a.vendor:"";this.vendver=a.vendorSub?parseFloat(a.vendorSub):0;this.product=a.product?a.product:"";this.platform=String(a.platform).toLowerCase();this.lang=a.language?a.language.substring(0,2):a.browserLanguage?a.browserLanguage.substring(0,2):a.systemLanguage?a.systemLanguage.substring(0,2): -"en";this.win=this.platform.indexOf("win")>=0;this.mac=this.platform.indexOf("mac")>=0;this.linux=this.platform.indexOf("linux")>=0;this.unix=this.platform.indexOf("unix")>=0;this.dom=document.getElementById?!0:!1;this.dom2=document.addEventListener&&document.removeEventListener;this.ie4=(this.ie=document.all&&!window.opera)&&!this.dom;this.ie5=this.dom&&this.appver.indexOf("MSIE 5")>0;this.ie8=this.dom&&this.appver.indexOf("MSIE 8")>0;this.ie7=this.dom&&this.appver.indexOf("MSIE 7")>0;this.ie6=this.dom&& -!this.ie8&&!this.ie7&&this.appver.indexOf("MSIE 6")>0;this.ns=this.ver<5&&this.name=="Netscape"||this.ver>=5&&this.vendor.indexOf("Netscape")>=0;this.chrome=this.agent_lc.indexOf("chrome")>0;this.safari=!this.chrome&&(this.agent_lc.indexOf("safari")>0||this.agent_lc.indexOf("applewebkit")>0);this.mz=this.dom&&!this.ie&&!this.ns&&!this.chrome&&!this.safari&&this.agent.indexOf("Mozilla")>=0;this.konq=this.agent_lc.indexOf("konqueror")>0;this.iphone=this.safari&&this.agent_lc.indexOf("iphone")>0;this.ipad= -this.safari&&this.agent_lc.indexOf("ipad")>0;if((this.opera=window.opera?!0:!1)&&window.RegExp)this.vendver=/opera(\s|\/)([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$2):-1;else if(this.chrome&&window.RegExp)this.vendver=/chrome\/([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$1):0;else if(!this.vendver&&this.safari)this.vendver=/(safari|applewebkit)\/([0-9]+)/.test(this.agent_lc)?parseInt(RegExp.$2):0;else if(!this.vendver&&this.mz||this.agent.indexOf("Camino")>0)this.vendver=/rv:([0-9\.]+)/.test(this.agent)? -parseFloat(RegExp.$1):0;else if(this.ie&&window.RegExp)this.vendver=/msie\s+([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$1):0;else if(this.konq&&window.RegExp)this.vendver=/khtml\/([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$1):0;if(this.safari&&/;\s+([a-z]{2})-[a-z]{2}\)/.test(this.agent_lc))this.lang=RegExp.$1;this.dhtml=this.ie4&&this.win||this.ie5||this.ie6||this.ns4||this.mz;this.vml=this.win&&this.ie&&this.dom&&!this.opera;this.pngalpha=this.mz||this.opera&&this.vendver>=6||this.ie&& -this.mac&&this.vendver>=5||this.ie&&this.win&&this.vendver>=5.5||this.safari;this.opacity=this.mz||this.ie&&this.vendver>=5.5&&!this.opera||this.safari&&this.vendver>=100;this.cookies=a.cookieEnabled;this.xmlhttp_test=function(){var a=new Function("try{var o=new ActiveXObject('Microsoft.XMLHTTP');return true;}catch(err){return false;}");return this.xmlhttp=window.XMLHttpRequest||window.ActiveXObject&&a()};this.set_html_class=function(){var a=" js";this.ie?(a+=" ie",this.ie5?a+=" ie5":this.ie6?a+= +function roundcube_browser(){var a=navigator;this.ver=parseFloat(a.appVersion);this.appver=a.appVersion;this.agent=a.userAgent;this.agent_lc=a.userAgent.toLowerCase();this.name=a.appName;this.vendor=a.vendor?a.vendor:"";this.vendver=a.vendorSub?parseFloat(a.vendorSub):0;this.product=a.product?a.product:"";this.platform=(""+a.platform).toLowerCase();this.lang=a.language?a.language.substring(0,2):a.browserLanguage?a.browserLanguage.substring(0,2):a.systemLanguage?a.systemLanguage.substring(0,2):"en"; +this.win=0<=this.platform.indexOf("win");this.mac=0<=this.platform.indexOf("mac");this.linux=0<=this.platform.indexOf("linux");this.unix=0<=this.platform.indexOf("unix");this.dom=document.getElementById?!0:!1;this.dom2=document.addEventListener&&document.removeEventListener;this.ie4=(this.ie=document.all&&!window.opera)&&!this.dom;this.ie5=this.dom&&0this.ver&&"Netscape"==this.name||5<=this.ver&&0<=this.vendor.indexOf("Netscape");this.chrome=0|[,;s\n])","i"): RegExp("^((([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22))*\\x40([^@\\x2e]+\\x2e)+([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))|(mailtest\\x40(\\u0645\\u062b\\u0627\\u0644\\x2e\\u0625\\u062e\\u062a\\u0628\\u0627\\u0631|\\u4f8b\\u5b50\\x2e\\u6d4b\\u8bd5|\\u4f8b\\u5b50\\x2e\\u6e2c\\u8a66|\\u03c0\\u03b1\\u03c1\\u03ac\\u03b4\\u03b5\\u03b9\\u03b3\\u03bc\\u03b1\\x2e\\u03b4\\u03bf\\u03ba\\u03b9\\u03bc\\u03ae|\\u0909\\u0926\\u093e\\u0939\\u0930\\u0923\\x2e\\u092a\\u0930\\u0940\\u0915\\u094d\\u0937\\u093e|\\u4f8b\\u3048\\x2e\\u30c6\\u30b9\\u30c8|\\uc2e4\\ub840\\x2e\\ud14c\\uc2a4\\ud2b8|\\u0645\\u062b\\u0627\\u0644\\x2e\\u0622\\u0632\\u0645\\u0627\\u06cc\\u0634\u06cc|\\u043f\\u0440\\u0438\\u043c\\u0435\\u0440\\x2e\\u0438\\u0441\\u043f\\u044b\\u0442\\u0430\\u043d\\u0438\\u0435|\\u0b89\\u0ba4\\u0bbe\\u0bb0\\u0ba3\\u0bae\\u0bcd\\x2e\\u0baa\\u0bb0\\u0bbf\\u0b9f\\u0bcd\\u0b9a\\u0bc8|\\u05d1\\u05f2\\u05b7\\u05e9\\u05e4\\u05bc\\u05d9\\u05dc\\x2e\\u05d8\\u05e2\\u05e1\\u05d8)))$", -"i")).test(a)?!0:!1:!1}function rcube_clone_object(a){var b={},c;for(c in a)b[c]=a[c]&&typeof a[c]==="object"?clone_object(a[c]):a[c];return b}function urlencode(a){return window.encodeURIComponent?encodeURIComponent(a):escape(a)} +"i")).test(a)?!0:!1:!1}function rcube_clone_object(a){var b={},c;for(c in a)b[c]=a[c]&&"object"===typeof a[c]?clone_object(a[c]):a[c];return b}function urlencode(a){return window.encodeURIComponent?encodeURIComponent(a).replace("*","%2A"):escape(a).replace("+","%2B").replace("*","%2A").replace("/","%2F").replace("@","%40")} function rcube_find_object(a,b){var c,d;b||(b=document);if(b.getElementsByName&&(c=b.getElementsByName(a)))d=c[0];!d&&b.getElementById&&(d=b.getElementById(a));!d&&b.all&&(d=b.all[a]);!d&&b.images.length&&(d=b.images[a]);if(!d&&b.forms.length)for(c=0;c=d.left&&c.x=d.top&&c.y=d.left&&c.x=d.top&&c.y>2,b=(b&3)<<4|c>>4,f=(c&15)<<2|d>>6,h=d&63,isNaN(c)?f=h=64:isNaN(d)&&(h=64),g=g+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(e)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(b)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(f)+ +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(h);while(i>4,c=(c&15)<<4|e>>2,d=(e&3)<<6|f,g+=String.fromCharCode(b),64!=e&&(g+=String.fromCharCode(c)),64!=f&&(g+=String.fromCharCode(d));while(i | +-----------------------------------------------------------------------+ - $Id: common.js 5281 2011-09-27 07:29:49Z alec $ + $Id: common.js 5481 2011-11-24 07:53:00Z alec $ */ // Constants @@ -171,14 +171,12 @@ get_modifier: function(e) var opcode = 0; e = e || window.event; - if (bw.mac && e) { + if (bw.mac && e) opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); - return opcode; - } - if (e) { + else if (e) opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); - return opcode; - } + + return opcode; }, /** @@ -544,10 +542,17 @@ function rcube_clone_object(obj) return out; }; -// make a string URL safe +// make a string URL safe (and compatible with PHP's rawurlencode()) function urlencode(str) { - return window.encodeURIComponent ? encodeURIComponent(str) : escape(str); + if (window.encodeURIComponent) + return encodeURIComponent(str).replace('*', '%2A'); + + return escape(str) + .replace('+', '%2B') + .replace('*', '%2A') + .replace('/', '%2F') + .replace('@', '%40'); }; @@ -673,6 +678,23 @@ RegExp.escape = function(str) return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); }; +// Extend Date prototype to detect Standard timezone without DST +// from http://www.michaelapproved.com/articles/timezone-detect-and-ignore-daylight-saving-time-dst/ +Date.prototype.getStdTimezoneOffset = function() +{ + var m = 12, + d = new Date(null, m, 1), + tzo = d.getTimezoneOffset(); + + while (--m) { + d.setUTCMonth(m); + if (tzo != d.getTimezoneOffset()) { + return Math.max(tzo, d.getTimezoneOffset()); + } + } + + return tzo; +} // Make getElementById() case-sensitive on IE if (bw.ie) @@ -689,3 +711,82 @@ if (bw.ie) return obj; } } + +// This code was written by Tyler Akins and has been placed in the +// public domain. It would be nice if you left this header intact. +// Base64 code from Tyler Akins -- http://rumkin.com +var Base64 = (function () { + var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + var obj = { + /** + * Encodes a string in base64 + * @param {String} input The string to encode in base64. + */ + encode: function (input) { + if (typeof(window.btoa) === 'function') + return btoa(input); + + var chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0, output = '', len = input.length; + + do { + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) + enc3 = enc4 = 64; + else if (isNaN(chr3)) + enc4 = 64; + + output = output + + keyStr.charAt(enc1) + keyStr.charAt(enc2) + + keyStr.charAt(enc3) + keyStr.charAt(enc4); + } while (i < len); + + return output; + }, + + /** + * Decodes a base64 string. + * @param {String} input The string to decode. + */ + decode: function (input) { + if (typeof(window.atob) === 'function') + return atob(input); + + var chr1, chr2, chr3, enc1, enc2, enc3, enc4, len, i = 0, output = ''; + + // remove all characters that are not A-Z, a-z, 0-9, +, /, or = + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + len = input.length; + + do { + enc1 = keyStr.indexOf(input.charAt(i++)); + enc2 = keyStr.indexOf(input.charAt(i++)); + enc3 = keyStr.indexOf(input.charAt(i++)); + enc4 = keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) + output = output + String.fromCharCode(chr2); + if (enc4 != 64) + output = output + String.fromCharCode(chr3); + } while (i < len); + + return output; + } + }; + + return obj; +})(); diff --git a/program/js/editor.js b/program/js/editor.js index a3aef72..63186fb 100644 --- a/program/js/editor.js +++ b/program/js/editor.js @@ -14,15 +14,15 @@ */ // Initialize HTML editor -function rcmail_editor_init(skin_path, editor_lang, spellcheck, mode) +function rcmail_editor_init(config) { var ret, conf = { mode: 'textareas', editor_selector: 'mce_editor', apply_source_formatting: true, theme: 'advanced', - language: editor_lang, - content_css: skin_path + '/editor_content.css', + language: config.lang, + content_css: config.skin_path + '/editor_content.css', theme_advanced_toolbar_location: 'top', theme_advanced_toolbar_align: 'left', theme_advanced_buttons3: '', @@ -35,7 +35,7 @@ function rcmail_editor_init(skin_path, editor_lang, spellcheck, mode) rc_client: rcmail }; - if (mode == 'identity') + if (config.mode == 'identity') $.extend(conf, { plugins: 'paste,tabfocus', theme_advanced_buttons1: 'bold,italic,underline,strikethrough,justifyleft,justifycenter,justifyright,justifyfull,separator,outdent,indent,charmap,hr,link,unlink,code,forecolor', @@ -43,11 +43,12 @@ function rcmail_editor_init(skin_path, editor_lang, spellcheck, mode) }); else // mail compose $.extend(conf, { - plugins: 'paste,emotions,media,nonbreaking,table,searchreplace,visualchars,directionality,tabfocus' + (spellcheck ? ',spellchecker' : ''), + plugins: 'paste,emotions,media,nonbreaking,table,searchreplace,visualchars,directionality,tabfocus' + (config.spellcheck ? ',spellchecker' : ''), theme_advanced_buttons1: 'bold,italic,underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,bullist,numlist,outdent,indent,ltr,rtl,blockquote,|,forecolor,backcolor,fontselect,fontsizeselect', - theme_advanced_buttons2: 'link,unlink,table,|,emotions,charmap,image,media,|,code,search' + (spellcheck ? ',spellchecker' : '') + ',undo,redo', + theme_advanced_buttons2: 'link,unlink,table,|,emotions,charmap,image,media,|,code,search' + (config.spellcheck ? ',spellchecker' : '') + ',undo,redo', spellchecker_languages: (rcmail.env.spellcheck_langs ? rcmail.env.spellcheck_langs : 'Dansk=da,Deutsch=de,+English=en,Espanol=es,Francais=fr,Italiano=it,Nederlands=nl,Polski=pl,Portugues=pt,Suomi=fi,Svenska=sv'), spellchecker_rpc_url: '?_task=utils&_action=spell_html', + spellchecker_enable_learn_rpc: config.spelldict, accessibility_focus: false, oninit: 'rcmail_editor_callback' }); @@ -114,13 +115,17 @@ function rcmail_toggle_editor(select, textAreaId, flagElement) if (flagElement && (flag = rcube_find_object(flagElement))) flag.value = '1'; } - else { - if (!res && select.tagName == 'SELECT') - select.value = 'html'; + else if (res) { if (flagElement && (flag = rcube_find_object(flagElement))) flag.value = '0'; if (rcmail.env.composebody) rcube_find_object(rcmail.env.composebody).focus(); } + else { // !res + if (select.tagName == 'SELECT') + select.value = 'html'; + else if (select.tagName == 'INPUT') + select.checked = true; + } } diff --git a/program/js/googiespell.js b/program/js/googiespell.js index 411740f..3618fe1 100644 --- a/program/js/googiespell.js +++ b/program/js/googiespell.js @@ -1,38 +1,39 @@ var GOOGIE_CUR_LANG,GOOGIE_DEFAULT_LANG="en"; -function GoogieSpell(s,t){var l=this,r=getCookie("language");GOOGIE_CUR_LANG=r!=null?r:GOOGIE_DEFAULT_LANG;this.array_keys=function(a){var b=[],c;for(c in a)b.push([c]);return b};this.img_dir=s;this.server_url=t;this.lang_to_word=this.org_lang_to_word={da:"Dansk",de:"Deutsch",en:"English",es:"Español",fr:"Français",it:"Italiano",nl:"Nederlands",pl:"Polski",pt:"Português",fi:"Suomi",sv:"Svenska"};this.langlist_codes=this.array_keys(this.lang_to_word);this.show_change_lang_pic=!0;this.change_lang_pic_placement= -"right";this.report_state_change=!0;this.el_scroll_top=this.ta_scroll_top=0;this.lang_chck_spell="Check spelling";this.lang_revert="Revert to";this.lang_close="Close";this.lang_rsm_edt="Resume editing";this.lang_no_error_found="No spelling errors found";this.lang_no_suggestions="No suggestions";this.show_spell_img=!1;this.decoration=!0;this.use_close_btn=!1;this.report_ta_not_found=this.edit_layer_dbl_click=!0;this.custom_no_spelling_error=this.custom_ajax_error=null;this.custom_menu_builder=[];this.custom_item_evaulator= -null;this.extra_menu_items=[];this.custom_spellcheck_starter=null;this.main_controller=!0;this.all_errors_fixed_observer=this.show_menu_observer=this.spelling_state_observer=this.lang_state_observer=null;this.use_focus=!1;this.focus_link_b=this.focus_link_t=null;this.cnt_errors_fixed=this.cnt_errors=0;$(document).bind("click",function(a){a=$(a.target);a.attr("googie_action_btn")!="1"&&l.isLangWindowShown()&&l.hideLangWindow();a.attr("googie_action_btn")!="1"&&l.isErrorWindowShown()&&l.hideErrorWindow()}); -this.decorateTextarea=function(a){if(this.text_area=typeof a==="string"?document.getElementById(a):a){if(!this.spell_container&&this.decoration){var a=document.createElement("table"),b=document.createElement("tbody"),c=document.createElement("tr"),d=document.createElement("td"),e=this.isDefined(this.force_width)?this.force_width:this.text_area.offsetWidth,f=this.isDefined(this.force_height)?this.force_height:16;c.appendChild(d);b.appendChild(c);$(a).append(b).insertBefore(this.text_area).width("100%").height(f); -$(d).height(f).width(e).css("text-align","right");this.spell_container=d}this.checkSpellingState()}else this.report_ta_not_found&&alert("Text area not found")};this.setSpellContainer=function(a){this.spell_container=typeof a==="string"?document.getElementById(a):a};this.setLanguages=function(a){this.lang_to_word=a;this.langlist_codes=this.array_keys(a)};this.setCurrentLanguage=function(a){GOOGIE_CUR_LANG=a;var b=new Date;b.setTime(b.getTime()+31536E6);setCookie("language",a,b)};this.setForceWidthHeight= -function(a,b){this.force_width=a;this.force_height=b};this.setDecoration=function(a){this.decoration=a};this.dontUseCloseButtons=function(){this.use_close_btn=!1};this.appendNewMenuItem=function(a,b,c){this.extra_menu_items.push([a,b,c])};this.appendCustomMenuBuilder=function(a,b){this.custom_menu_builder.push([a,b])};this.setFocus=function(){try{return this.focus_link_b.focus(),this.focus_link_t.focus(),!0}catch(a){return!1}};this.setStateChanged=function(a){this.state=a;this.spelling_state_observer!= -null&&this.report_state_change&&this.spelling_state_observer(a,this)};this.setReportStateChange=function(a){this.report_state_change=a};this.getUrl=function(){return this.server_url+GOOGIE_CUR_LANG};this.escapeSpecial=function(a){return a?a.replace(/&/g,"&").replace(//g,">"):""};this.createXMLReq=function(a){return''+a+""}; -this.spellCheck=function(a){this.prepare(a);var a=this.escapeSpecial(this.orginal_text),b=this;$.ajax({type:"POST",url:this.getUrl(),data:this.createXMLReq(a),dataType:"text",error:function(){b.custom_ajax_error?b.custom_ajax_error(b):alert("An error was encountered on the server. Please try again later.");b.main_controller&&($(b.spell_span).remove(),b.removeIndicator());b.checkSpellingState()},success:function(a){b.processData(a);b.results.length||(b.custom_no_spelling_error?b.custom_no_spelling_error(b): -b.flashNoSpellingErrorState());b.removeIndicator()}})};this.prepare=function(a,b){this.cnt_errors=this.cnt_errors_fixed=0;this.setStateChanged("checking_spell");!b&&this.main_controller&&this.appendIndicator(this.spell_span);this.error_links=[];this.ta_scroll_top=this.text_area.scrollTop;this.ignore=a;this.hideLangWindow();if($(this.text_area).val()==""||a)this.custom_no_spelling_error?this.custom_no_spelling_error(this):this.flashNoSpellingErrorState(),this.removeIndicator();else{this.createEditLayer(this.text_area.offsetWidth, -this.text_area.offsetHeight);this.createErrorWindow();$("body").append(this.error_window);try{netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead")}catch(c){}this.main_controller&&$(this.spell_span).unbind("click");this.orginal_text=$(this.text_area).val()}};this.parseResult=function(a){var b=/\w+="(\d+|true)"/g,c=/\t/g,a=a.match(/]*>[^<]*<\/c>/g),d=[];if(a==null)return d;for(var e=0,f=a.length;e]*>/g,"").split(c);for(h=0;hd&&(this.results[c].attrs.o+=b)};this.saveOldValue=function(a,b){a.is_changed= -!0;a.old_value=b};this.createListSeparator=function(){var a=document.createElement("td"),b=document.createElement("tr");$(a).html(" ").attr("googie_action_btn","1").css({cursor:"default","font-size":"3px","border-top":"1px solid #ccc","padding-top":"3px"});b.appendChild(a);return b};this.correctError=function(a,b,c,d){var e=b.innerHTML,c=c.nodeType==3?c.nodeValue:c.innerHTML,f=this.results[a].attrs.o;if(d)d=b.previousSibling.innerHTML,b.previousSibling.innerHTML=d.slice(0,d.length-1),e=" "+e,f--; -this.hideErrorWindow();this.updateOrginalText(f,e,c,a);$(b).html(c).css("color","green").attr("is_corrected",!0);this.results[a].attrs.l=c.length;this.isDefined(b.old_value)||this.saveOldValue(b,e);this.errorFixed()};this.showErrorWindow=function(a,b){this.show_menu_observer&&this.show_menu_observer(this);var c=this,d=$(a).offset(),e=document.createElement("table"),f=document.createElement("tbody");$(this.error_window).html("");$(e).addClass("googie_list").attr("googie_action_btn","1");for(var j= -!1,g=0;g0&&f.appendChild(this.createListSeparator());var q=function(b){if(b/g,">"):""};this.createXMLReq=function(a){return''+ +a+""};this.spellCheck=function(a){this.prepare(a);var a=this.escapeSpecial(this.orginal_text),b=this;$.ajax({type:"POST",url:this.getUrl(),data:this.createXMLReq(a),dataType:"text",error:function(){b.custom_ajax_error?b.custom_ajax_error(b):alert("An error was encountered on the server. Please try again later.");b.main_controller&&($(b.spell_span).remove(),b.removeIndicator());b.checkSpellingState()},success:function(a){b.processData(a);b.results.length||(b.custom_no_spelling_error? +b.custom_no_spelling_error(b):b.flashNoSpellingErrorState());b.removeIndicator()}})};this.learnWord=function(a){var a=this.escapeSpecial(a.innerHTML),b=this,a=''+a+"";$.ajax({type:"POST",url:this.getUrl(),data:a,dataType:"text",error:function(){b.custom_ajax_error?b.custom_ajax_error(b):alert("An error was encountered on the server. Please try again later.")},success:function(){}})};this.prepare=function(a,b){this.cnt_errors= +this.cnt_errors_fixed=0;this.setStateChanged("checking_spell");!b&&this.main_controller&&this.appendIndicator(this.spell_span);this.error_links=[];this.ta_scroll_top=this.text_area.scrollTop;this.ignore=a;this.hideLangWindow();if(""==$(this.text_area).val()||a)this.custom_no_spelling_error?this.custom_no_spelling_error(this):this.flashNoSpellingErrorState(),this.removeIndicator();else{this.createEditLayer(this.text_area.offsetWidth,this.text_area.offsetHeight);this.createErrorWindow();$("body").append(this.error_window); +try{netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead")}catch(c){}this.main_controller&&$(this.spell_span).unbind("click");this.orginal_text=$(this.text_area).val()}};this.parseResult=function(a){var b=/\w+="(\d+|true)"/g,c=/\t/g,a=a.match(/]*>[^<]*<\/c>/g),d=[];if(null==a)return d;for(var e=0,f=a.length;e]*>/g,"").split(c);for(h=0;hd&&(this.results[c].attrs.o+=b)};this.saveOldValue=function(a,b){a.is_changed=!0;a.old_value=b};this.createListSeparator=function(){var a=document.createElement("td"), +b=document.createElement("tr");$(a).html(" ").attr("googie_action_btn","1").css({cursor:"default","font-size":"3px","border-top":"1px solid #ccc","padding-top":"3px"});b.appendChild(a);return b};this.correctError=function(a,b,c,d){var e=b.innerHTML,c=3==c.nodeType?c.nodeValue:c.innerHTML,f=this.results[a].attrs.o;if(d)d=b.previousSibling.innerHTML,b.previousSibling.innerHTML=d.slice(0,d.length-1),e=" "+e,f--;this.hideErrorWindow();this.updateOrginalText(f,e,c,a);$(b).html(c).css("color","green").attr("is_corrected", +!0);this.results[a].attrs.l=c.length;this.isDefined(b.old_value)||this.saveOldValue(b,e);this.errorFixed()};this.ignoreError=function(a){$(a).removeAttr("class").css("color","").unbind();this.hideErrorWindow()};this.showErrorWindow=function(a,b){this.show_menu_observer&&this.show_menu_observer(this);var c=this,d=$(a).offset(),e=document.createElement("table"),f=document.createElement("tbody");$(this.error_window).html("");$(e).addClass("googie_list").attr("googie_action_btn","1");for(var j=!1,g=0;g< +this.custom_menu_builder.length;g++){var h=this.custom_menu_builder[g];if(h[0](this.results[b])){j=h[1](this,f,a);break}}if(!j){var j=this.results[b].suggestions,l=this.results[b].attrs.o,g=this.results[b].attrs.l,k,m;this.has_dictionary&&!$(a).attr("is_corrected")&&(h=document.createElement("tr"),k=document.createElement("td"),m=document.createElement("span"),$(m).text(this.lang_learn_word),$(k).attr("googie_action_btn","1").css("cursor","default").mouseover(c.item_onmouseover).mouseout(c.item_onmouseout).click(function(){c.learnWord(a, +b);c.ignoreError(a,b)}),k.appendChild(m),h.appendChild(k),f.appendChild(h));for(var o=0,g=j.length;o").css({position:"absolute", -"z-index":-1}),$("body").append(d),this.error_window_iframe=d;$(this.error_window_iframe).css({top:this.error_window.offsetTop,left:this.error_window.offsetLeft,width:this.error_window.offsetWidth,height:this.error_window.offsetHeight}).show()}};this.createEditLayer=function(a,b){this.edit_layer=document.createElement("div");$(this.edit_layer).addClass("googie_edit_layer").attr("id","googie_edit_layer").width("auto").height(b);this.text_area.nodeName.toLowerCase()!="input"||$(this.text_area).val()== -""?$(this.edit_layer).css("overflow","auto").height(b-4):$(this.edit_layer).css("overflow","hidden");var c=this;this.edit_layer_dbl_click&&$(this.edit_layer).dblclick(function(a){if(a.target.className!="googie_link"&&!c.isErrorWindowShown()){c.resumeEditing();var b=function(){$(c.text_area).focus();b=null};window.setTimeout(b,10)}return!1})};this.resumeEditing=function(){this.setStateChanged("ready");if(this.edit_layer)this.el_scroll_top=this.edit_layer.scrollTop;this.hideErrorWindow();this.main_controller&& -$(this.spell_span).removeClass().addClass("googie_no_style");if(!this.ignore&&(this.use_focus&&($(this.focus_link_t).remove(),$(this.focus_link_b).remove()),$(this.edit_layer).remove(),$(this.text_area).show(),this.el_scroll_top!=void 0))this.text_area.scrollTop=this.el_scroll_top;this.checkSpellingState(!1)};this.createErrorLink=function(a,b){var c=document.createElement("span"),d=this,e=function(){d.showErrorWindow(c,b);e=null;return!1};$(c).html(a).addClass("googie_link").bind("click",e).attr({googie_action_btn:"1", -g_id:b,is_corrected:!1});return c};this.createPart=function(a){if(a==" ")return document.createTextNode(" ");var a=this.escapeSpecial(a),a=a.replace(/\n/g,"
    "),a=a.replace(/ /g,"  "),a=a.replace(/^ /g," "),a=a.replace(/ $/g," "),b=document.createElement("span");$(b).html(a);return b};this.showErrorsInIframe=function(){var a=document.createElement("div"),b=0,c=this.results;if(c.length>0){for(var d=0,e=c.length;d"),a=a.replace(/ /g,"  "),a=a.replace(/^ /g," "),a=a.replace(/ $/g," "),b=document.createElement("span");$(b).html(a);return b};this.showErrorsInIframe=function(){var a=document.createElement("div"),b=0,c=this.results;if(0").attr({src:this.img_dir+"change_lang.gif",alt:"Change language",googie_action_btn:"1"}),b=document.createElement("span");l= -this;$(b).addClass("googie_lang_3d_on").append(a).bind("click",function(){var a=this.tagName.toLowerCase()=="img"?this.parentNode:this;$(a).hasClass("googie_lang_3d_click")?(a.className="googie_lang_3d_on",l.hideLangWindow()):(a.className="googie_lang_3d_click",l.showLangWindow(a))});return b};this.createSpellDiv=function(){var a=document.createElement("span");$(a).addClass("googie_check_spelling_link").text(this.lang_chck_spell);this.show_spell_img&&$(a).append(" ").append($("").attr("src", -this.img_dir+"spellc.gif"));return a};this.flashNoSpellingErrorState=function(a){this.setStateChanged("no_error_found");var b=this;if(this.main_controller){var c;c=a?function(){a();b.checkSpellingState()}:function(){b.checkSpellingState()};var d=$("").text(this.lang_no_error_found);$(this.switch_lan_pic).hide();$(this.spell_span).empty().append(d).removeClass().addClass("googie_check_spelling_ok");window.setTimeout(c,1E3)}};this.resumeEditingState=function(){this.setStateChanged("resume_editing"); -if(this.main_controller){var a=$("").text(this.lang_rsm_edt),b=this;$(this.switch_lan_pic).hide();$(this.spell_span).empty().unbind().append(a).bind("click",function(){b.resumeEditing()}).removeClass().addClass("googie_resume_editing")}try{this.edit_layer.scrollTop=this.ta_scroll_top}catch(c){}};this.checkSpellingState=function(a){a&&this.setStateChanged("ready");this.switch_lan_pic=this.show_change_lang_pic?this.createChangeLangPic():document.createElement("span");var a=this.createSpellDiv(), -b=this;this.custom_spellcheck_starter?$(a).bind("click",function(){b.custom_spellcheck_starter()}):$(a).bind("click",function(){b.spellCheck()});this.main_controller&&(this.change_lang_pic_placement=="left"?$(this.spell_container).empty().append(this.switch_lan_pic).append(" ").append(a):$(this.spell_container).empty().append(a).append(" ").append(this.switch_lan_pic));this.spell_span=a};this.isDefined=function(a){return a!==void 0&&a!==null};this.errorFixed=function(){this.cnt_errors_fixed++;this.all_errors_fixed_observer&& -this.cnt_errors_fixed==this.cnt_errors&&(this.hideErrorWindow(),this.all_errors_fixed_observer())};this.errorFound=function(){this.cnt_errors++};this.createCloseButton=function(a){return this.createButton(this.lang_close,"googie_list_close",a)};this.createButton=function(a,b,c){var d=document.createElement("tr"),e=document.createElement("td"),f;b?(f=document.createElement("span"),$(f).addClass(b).html(a)):f=document.createTextNode(a);$(e).bind("click",c).bind("mouseover",this.item_onmouseover).bind("mouseout", -this.item_onmouseout);e.appendChild(f);d.appendChild(e);return d};this.removeIndicator=function(){window.rcmail&&rcmail.set_busy(!1,null,this.rc_msg_id)};this.appendIndicator=function(){if(window.rcmail)this.rc_msg_id=rcmail.set_busy(!0,"checking")};this.createFocusLink=function(a){var b=document.createElement("a");$(b).attr({href:"javascript:;",name:a});return b};this.item_onmouseover=function(){this.className!="googie_list_revert"&&this.className!="googie_list_close"?this.className="googie_list_onhover": -this.parentNode.className="googie_list_onhover"};this.item_onmouseout=function(){this.className!="googie_list_revert"&&this.className!="googie_list_close"?this.className="googie_list_onout":this.parentNode.className="googie_list_onout"}}; +[];for(i=0;i").attr({src:this.img_dir+ +"change_lang.gif",alt:"Change language",googie_action_btn:"1"}),b=document.createElement("span");l=this;$(b).addClass("googie_lang_3d_on").append(a).bind("click",function(){var a="img"==this.tagName.toLowerCase()?this.parentNode:this;$(a).hasClass("googie_lang_3d_click")?(a.className="googie_lang_3d_on",l.hideLangWindow()):(a.className="googie_lang_3d_click",l.showLangWindow(a))});return b};this.createSpellDiv=function(){var a=document.createElement("span");$(a).addClass("googie_check_spelling_link").text(this.lang_chck_spell); +this.show_spell_img&&$(a).append(" ").append($("").attr("src",this.img_dir+"spellc.gif"));return a};this.flashNoSpellingErrorState=function(a){this.setStateChanged("no_error_found");var b=this;if(this.main_controller){var c;c=a?function(){a();b.checkSpellingState()}:function(){b.checkSpellingState()};var d=$("").text(this.lang_no_error_found);$(this.switch_lan_pic).hide();$(this.spell_span).empty().append(d).removeClass().addClass("googie_check_spelling_ok");window.setTimeout(c,1E3)}}; +this.resumeEditingState=function(){this.setStateChanged("resume_editing");if(this.main_controller){var a=$("").text(this.lang_rsm_edt),b=this;$(this.switch_lan_pic).hide();$(this.spell_span).empty().unbind().append(a).bind("click",function(){b.resumeEditing()}).removeClass().addClass("googie_resume_editing")}try{this.edit_layer.scrollTop=this.ta_scroll_top}catch(c){}};this.checkSpellingState=function(a){a&&this.setStateChanged("ready");this.switch_lan_pic=this.show_change_lang_pic?this.createChangeLangPic(): +document.createElement("span");var a=this.createSpellDiv(),b=this;this.custom_spellcheck_starter?$(a).bind("click",function(){b.custom_spellcheck_starter()}):$(a).bind("click",function(){b.spellCheck()});this.main_controller&&("left"==this.change_lang_pic_placement?$(this.spell_container).empty().append(this.switch_lan_pic).append(" ").append(a):$(this.spell_container).empty().append(a).append(" ").append(this.switch_lan_pic));this.spell_span=a};this.isDefined=function(a){return void 0!==a&&null!== +a};this.errorFixed=function(){this.cnt_errors_fixed++;this.all_errors_fixed_observer&&this.cnt_errors_fixed==this.cnt_errors&&(this.hideErrorWindow(),this.all_errors_fixed_observer())};this.errorFound=function(){this.cnt_errors++};this.createCloseButton=function(a){return this.createButton(this.lang_close,"googie_list_close",a)};this.createButton=function(a,b,c){var d=document.createElement("tr"),e=document.createElement("td"),f;b?(f=document.createElement("span"),$(f).addClass(b).html(a)):f=document.createTextNode(a); +$(e).bind("click",c).bind("mouseover",this.item_onmouseover).bind("mouseout",this.item_onmouseout);e.appendChild(f);d.appendChild(e);return d};this.removeIndicator=function(){window.rcmail&&rcmail.set_busy(!1,null,this.rc_msg_id)};this.appendIndicator=function(){if(window.rcmail)this.rc_msg_id=rcmail.set_busy(!0,"checking")};this.createFocusLink=function(a){var b=document.createElement("a");$(b).attr({href:"javascript:;",name:a});return b};this.item_onmouseover=function(){"googie_list_revert"!=this.className&& +"googie_list_close"!=this.className?this.className="googie_list_onhover":this.parentNode.className="googie_list_onhover"};this.item_onmouseout=function(){"googie_list_revert"!=this.className&&"googie_list_close"!=this.className?this.className="googie_list_onout":this.parentNode.className="googie_list_onout"}}; diff --git a/program/js/googiespell.js.src b/program/js/googiespell.js.src index de8890f..96d612c 100644 --- a/program/js/googiespell.js.src +++ b/program/js/googiespell.js.src @@ -1,8 +1,11 @@ /* SpellCheck jQuery'fied spell checker based on GoogieSpell 4.0 - Copyright Amir Salihefendic 2006 - Copyright Aleksander Machniak 2009 + (which was published under GPL "version 2 or any later version") + + Copyright (C) 2006 Amir Salihefendic + Copyright (C) 2009 Aleksander Machniak + Copyright (C) 2011 Kolab Systems AG LICENSE GPL AUTHORS @@ -13,7 +16,8 @@ var GOOGIE_CUR_LANG, GOOGIE_DEFAULT_LANG = 'en'; -function GoogieSpell(img_dir, server_url) { +function GoogieSpell(img_dir, server_url, has_dict) +{ var ref = this, cookie_value = getCookie('language'); @@ -49,6 +53,7 @@ function GoogieSpell(img_dir, server_url) { this.lang_rsm_edt = "Resume editing"; this.lang_no_error_found = "No spelling errors found"; this.lang_no_suggestions = "No suggestions"; + this.lang_learn_word = "Add to dictionary"; this.show_spell_img = false; // roundcube mod. this.decoration = true; @@ -64,6 +69,7 @@ function GoogieSpell(img_dir, server_url) { this.extra_menu_items = []; this.custom_spellcheck_starter = null; this.main_controller = true; + this.has_dictionary = has_dict; // Observers this.lang_state_observer = null; @@ -90,7 +96,8 @@ function GoogieSpell(img_dir, server_url) { }); -this.decorateTextarea = function(id) { +this.decorateTextarea = function(id) +{ this.text_area = typeof id === 'string' ? document.getElementById(id) : id; if (this.text_area) { @@ -119,16 +126,19 @@ this.decorateTextarea = function(id) { ////// // API Functions (the ones that you can call) ///// -this.setSpellContainer = function(id) { +this.setSpellContainer = function(id) +{ this.spell_container = typeof id === 'string' ? document.getElementById(id) : id; }; -this.setLanguages = function(lang_dict) { +this.setLanguages = function(lang_dict) +{ this.lang_to_word = lang_dict; this.langlist_codes = this.array_keys(lang_dict); }; -this.setCurrentLanguage = function(lan_code) { +this.setCurrentLanguage = function(lan_code) +{ GOOGIE_CUR_LANG = lan_code; //Set cookie @@ -137,29 +147,35 @@ this.setCurrentLanguage = function(lan_code) { setCookie('language', lan_code, now); }; -this.setForceWidthHeight = function(width, height) { +this.setForceWidthHeight = function(width, height) +{ // Set to null if you want to use one of them this.force_width = width; this.force_height = height; }; -this.setDecoration = function(bool) { +this.setDecoration = function(bool) +{ this.decoration = bool; }; -this.dontUseCloseButtons = function() { +this.dontUseCloseButtons = function() +{ this.use_close_btn = false; }; -this.appendNewMenuItem = function(name, call_back_fn, checker) { +this.appendNewMenuItem = function(name, call_back_fn, checker) +{ this.extra_menu_items.push([name, call_back_fn, checker]); }; -this.appendCustomMenuBuilder = function(eval_fn, builder) { +this.appendCustomMenuBuilder = function(eval_fn, builder) +{ this.custom_menu_builder.push([eval_fn, builder]); }; -this.setFocus = function() { +this.setFocus = function() +{ try { this.focus_link_b.focus(); this.focus_link_t.focus(); @@ -174,13 +190,15 @@ this.setFocus = function() { ////// // Set functions (internal) ///// -this.setStateChanged = function(current_state) { +this.setStateChanged = function(current_state) +{ this.state = current_state; if (this.spelling_state_observer != null && this.report_state_change) this.spelling_state_observer(current_state, this); }; -this.setReportStateChange = function(bool) { +this.setReportStateChange = function(bool) +{ this.report_state_change = bool; }; @@ -188,28 +206,31 @@ this.setReportStateChange = function(bool) { ////// // Request functions ///// -this.getUrl = function() { +this.getUrl = function() +{ return this.server_url + GOOGIE_CUR_LANG; }; -this.escapeSpecial = function(val) { +this.escapeSpecial = function(val) +{ return val ? val.replace(/&/g, "&").replace(//g, ">") : ''; }; -this.createXMLReq = function (text) { +this.createXMLReq = function (text) +{ return '' + '' + '' + text + ''; }; -this.spellCheck = function(ignore) { +this.spellCheck = function(ignore) +{ this.prepare(ignore); var req_text = this.escapeSpecial(this.orginal_text), ref = this; - $.ajax({ type: 'POST', url: this.getUrl(), - data: this.createXMLReq(req_text), dataType: 'text', + $.ajax({ type: 'POST', url: this.getUrl(), data: this.createXMLReq(req_text), dataType: 'text', error: function(o) { if (ref.custom_ajax_error) ref.custom_ajax_error(ref); @@ -234,6 +255,25 @@ this.spellCheck = function(ignore) { }); }; +this.learnWord = function(word, id) +{ + word = this.escapeSpecial(word.innerHTML); + + var ref = this, + req_text = '' + word + ''; + + $.ajax({ type: 'POST', url: this.getUrl(), data: req_text, dataType: 'text', + error: function(o) { + if (ref.custom_ajax_error) + ref.custom_ajax_error(ref); + else + alert('An error was encountered on the server. Please try again later.'); + }, + success: function(data) { + } + }); +}; + ////// // Spell checking functions @@ -274,7 +314,8 @@ this.prepare = function(ignore, no_indicator) this.orginal_text = $(this.text_area).val(); }; -this.parseResult = function(r_text) { +this.parseResult = function(r_text) +{ // Returns an array: result[item] -> ['attrs'], ['suggestions'] var re_split_attr_c = /\w+="(\d+|true)"/g, re_split_text = /\t/g, @@ -324,21 +365,25 @@ this.processData = function(data) ////// // Error menu functions ///// -this.createErrorWindow = function() { +this.createErrorWindow = function() +{ this.error_window = document.createElement('div'); $(this.error_window).addClass('googie_window popupmenu').attr('googie_action_btn', '1'); }; -this.isErrorWindowShown = function() { +this.isErrorWindowShown = function() +{ return $(this.error_window).is(':visible'); }; -this.hideErrorWindow = function() { +this.hideErrorWindow = function() +{ $(this.error_window).hide(); $(this.error_window_iframe).hide(); }; -this.updateOrginalText = function(offset, old_value, new_value, id) { +this.updateOrginalText = function(offset, old_value, new_value, id) +{ var part_1 = this.orginal_text.substring(0, offset), part_2 = this.orginal_text.substring(offset+old_value.length), add_2_offset = new_value.length - old_value.length; @@ -357,18 +402,20 @@ this.saveOldValue = function(elm, old_value) { elm.old_value = old_value; }; -this.createListSeparator = function() { +this.createListSeparator = function() +{ var td = document.createElement('td'), tr = document.createElement('tr'); $(td).html(' ').attr('googie_action_btn', '1') - .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'}); + .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'}); tr.appendChild(td); return tr; }; -this.correctError = function(id, elm, l_elm, rm_pre_space) { +this.correctError = function(id, elm, l_elm, rm_pre_space) +{ var old_value = elm.innerHTML, new_value = l_elm.nodeType == 3 ? l_elm.nodeValue : l_elm.innerHTML, offset = this.results[id]['attrs']['o']; @@ -393,7 +440,15 @@ this.correctError = function(id, elm, l_elm, rm_pre_space) { this.errorFixed(); }; -this.showErrorWindow = function(elm, id) { +this.ignoreError = function(elm, id) +{ + // @TODO: ignore all same words + $(elm).removeAttr('class').css('color', '').unbind(); + this.hideErrorWindow(); +}; + +this.showErrorWindow = function(elm, id) +{ if (this.show_menu_observer) this.show_menu_observer(this); @@ -414,6 +469,7 @@ this.showErrorWindow = function(elm, id) { break; } } + if (!changed) { // Build up the result list var suggestions = this.results[id]['suggestions'], @@ -421,6 +477,26 @@ this.showErrorWindow = function(elm, id) { len = this.results[id]['attrs']['l'], row, item, dummy; + // [Add to dictionary] button + if (this.has_dictionary && !$(elm).attr('is_corrected')) { + row = document.createElement('tr'), + item = document.createElement('td'), + dummy = document.createElement('span'); + + $(dummy).text(this.lang_learn_word); + $(item).attr('googie_action_btn', '1').css('cursor', 'default') + .mouseover(ref.item_onmouseover) + .mouseout(ref.item_onmouseout) + .click(function(e) { + ref.learnWord(elm, id); + ref.ignoreError(elm, id); + }); + + item.appendChild(dummy); + row.appendChild(item); + list.appendChild(row); + } +/* if (suggestions.length == 0) { row = document.createElement('tr'), item = document.createElement('td'), @@ -433,7 +509,7 @@ this.showErrorWindow = function(elm, id) { row.appendChild(item); list.appendChild(row); } - +*/ for (var i=0, len=suggestions.length; i < len; i++) { row = document.createElement('tr'), item = document.createElement('td'), @@ -441,16 +517,15 @@ this.showErrorWindow = function(elm, id) { $(dummy).html(suggestions[i]); - $(item).bind('mouseover', this.item_onmouseover) - .bind('mouseout', this.item_onmouseout) - .bind('click', function(e) { ref.correctError(id, elm, e.target.firstChild) }); + $(item).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout) + .click(function(e) { ref.correctError(id, elm, e.target.firstChild) }); item.appendChild(dummy); row.appendChild(item); list.appendChild(row); } - //The element is changed, append the revert + // The element is changed, append the revert if (elm.is_changed && elm.innerHTML != elm.old_value) { var old_value = elm.old_value, revert_row = document.createElement('tr'), @@ -459,11 +534,10 @@ this.showErrorWindow = function(elm, id) { $(rev_span).addClass('googie_list_revert').html(this.lang_revert + ' ' + old_value); - $(revert).bind('mouseover', this.item_onmouseover) - .bind('mouseout', this.item_onmouseout) - .bind('click', function(e) { + $(revert).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout) + .click(function(e) { ref.updateOrginalText(offset, elm.innerHTML, old_value, id); - $(elm).attr('is_corrected', true).css('color', '#b91414').html(old_value); + $(elm).removeAttr('is_corrected').css('color', '#b91414').html(old_value); ref.hideErrorWindow(); }); @@ -498,11 +572,11 @@ this.showErrorWindow = function(elm, id) { $(ok_pic).attr('src', this.img_dir + 'ok.gif') .width(32).height(16) .css({'cursor': 'pointer', 'margin-left': '2px', 'margin-right': '2px'}) - .bind('click', onsub); + .click(onsub); $(edit_form).attr('googie_action_btn', '1') .css({'margin': 0, 'padding': 0, 'cursor': 'default', 'white-space': 'nowrap'}) - .bind('submit', onsub); + .submit(onsub); edit_form.appendChild(edit_input); edit_form.appendChild(ok_pic); @@ -523,9 +597,9 @@ this.showErrorWindow = function(elm, id) { e_col = document.createElement('td'); $(e_col).html(e_elm[0]) - .bind('mouseover', ref.item_onmouseover) - .bind('mouseout', ref.item_onmouseout) - .bind('click', function() { return e_elm[1](elm, ref) }); + .mouseover(ref.item_onmouseover) + .mouseout(ref.item_onmouseout) + .click(function() { return e_elm[1](elm, ref) }); e_row.appendChild(e_col); list.appendChild(e_row); @@ -575,7 +649,8 @@ this.showErrorWindow = function(elm, id) { ////// // Edit layer (the layer where the suggestions are stored) ////// -this.createEditLayer = function(width, height) { +this.createEditLayer = function(width, height) +{ this.edit_layer = document.createElement('div'); $(this.edit_layer).addClass('googie_edit_layer').attr('id', 'googie_edit_layer') .width('auto').height(height); @@ -603,7 +678,8 @@ this.createEditLayer = function(width, height) { } }; -this.resumeEditing = function() { +this.resumeEditing = function() +{ this.setStateChanged('ready'); if (this.edit_layer) @@ -629,7 +705,8 @@ this.resumeEditing = function() { this.checkSpellingState(false); }; -this.createErrorLink = function(text, id) { +this.createErrorLink = function(text, id) +{ var elm = document.createElement('span'), ref = this, d = function (e) { @@ -638,13 +715,14 @@ this.createErrorLink = function(text, id) { return false; }; - $(elm).html(text).addClass('googie_link').bind('click', d) - .attr({'googie_action_btn' : '1', 'g_id' : id, 'is_corrected' : false}); + $(elm).html(text).addClass('googie_link').click(d).removeAttr('is_corrected') + .attr({'googie_action_btn' : '1', 'g_id' : id}); return elm; }; -this.createPart = function(txt_part) { +this.createPart = function(txt_part) +{ if (txt_part == " ") return document.createTextNode(" "); @@ -659,7 +737,8 @@ this.createPart = function(txt_part) { return span; }; -this.showErrorsInIframe = function() { +this.showErrorsInIframe = function() +{ var output = document.createElement('div'), pointer = 0, results = this.results; @@ -717,7 +796,8 @@ this.showErrorsInIframe = function() { ////// // Choose language menu ////// -this.createLangWindow = function() { +this.createLangWindow = function() +{ this.language_window = document.createElement('div'); $(this.language_window).addClass('googie_window popupmenu') .width(100).attr('googie_action_btn', '1'); @@ -776,16 +856,19 @@ this.createLangWindow = function() { this.language_window.appendChild(table); }; -this.isLangWindowShown = function() { +this.isLangWindowShown = function() +{ return $(this.language_window).is(':visible'); }; -this.hideLangWindow = function() { +this.hideLangWindow = function() +{ $(this.language_window).hide(); $(this.switch_lan_pic).removeClass().addClass('googie_lang_3d_on'); }; -this.showLangWindow = function(elm) { +this.showLangWindow = function(elm) +{ if (this.show_menu_observer) this.show_menu_observer(this); @@ -806,11 +889,13 @@ this.showLangWindow = function(elm) { this.highlightCurSel(); }; -this.deHighlightCurSel = function() { +this.deHighlightCurSel = function() +{ $(this.lang_cur_elm).removeClass().addClass('googie_list_onout'); }; -this.highlightCurSel = function() { +this.highlightCurSel = function() +{ if (GOOGIE_CUR_LANG == null) GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG; for (var i=0; i < this.lang_elms.length; i++) { @@ -824,7 +909,8 @@ this.highlightCurSel = function() { } }; -this.createChangeLangPic = function() { +this.createChangeLangPic = function() +{ var img = $('') .attr({src: this.img_dir + 'change_lang.gif', 'alt': 'Change language', 'googie_action_btn': '1'}), switch_lan = document.createElement('span'); @@ -847,7 +933,8 @@ this.createChangeLangPic = function() { return switch_lan; }; -this.createSpellDiv = function() { +this.createSpellDiv = function() +{ var span = document.createElement('span'); $(span).addClass('googie_check_spelling_link').text(this.lang_chck_spell); @@ -862,7 +949,8 @@ this.createSpellDiv = function() { ////// // State functions ///// -this.flashNoSpellingErrorState = function(on_finish) { +this.flashNoSpellingErrorState = function(on_finish) +{ this.setStateChanged('no_error_found'); var ref = this; @@ -888,7 +976,8 @@ this.flashNoSpellingErrorState = function(on_finish) { } }; -this.resumeEditingState = function() { +this.resumeEditingState = function() +{ this.setStateChanged('resume_editing'); //Change link text to resume @@ -906,7 +995,8 @@ this.resumeEditingState = function() { catch (e) {}; }; -this.checkSpellingState = function(fire) { +this.checkSpellingState = function(fire) +{ if (fire) this.setStateChanged('ready'); @@ -939,12 +1029,14 @@ this.checkSpellingState = function(fire) { ////// // Misc. functions ///// -this.isDefined = function(o) { +this.isDefined = function(o) +{ return (o !== undefined && o !== null) }; -this.errorFixed = function() { - this.cnt_errors_fixed++; +this.errorFixed = function() +{ + this.cnt_errors_fixed++; if (this.all_errors_fixed_observer) if (this.cnt_errors_fixed == this.cnt_errors) { this.hideErrorWindow(); @@ -952,15 +1044,18 @@ this.errorFixed = function() { } }; -this.errorFound = function() { +this.errorFound = function() +{ this.cnt_errors++; }; -this.createCloseButton = function(c_fn) { +this.createCloseButton = function(c_fn) +{ return this.createButton(this.lang_close, 'googie_list_close', c_fn); }; -this.createButton = function(name, css_class, c_fn) { +this.createButton = function(name, css_class, c_fn) +{ var btn_row = document.createElement('tr'), btn = document.createElement('td'), spn_btn; @@ -982,14 +1077,16 @@ this.createButton = function(name, css_class, c_fn) { return btn_row; }; -this.removeIndicator = function(elm) { +this.removeIndicator = function(elm) +{ //$(this.indicator).remove(); // roundcube mod. if (window.rcmail) rcmail.set_busy(false, null, this.rc_msg_id); }; -this.appendIndicator = function(elm) { +this.appendIndicator = function(elm) +{ // modified by roundcube if (window.rcmail) this.rc_msg_id = rcmail.set_busy(true, 'checking'); @@ -1005,19 +1102,23 @@ this.appendIndicator = function(elm) { */ } -this.createFocusLink = function(name) { +this.createFocusLink = function(name) +{ var link = document.createElement('a'); $(link).attr({'href': 'javascript:;', 'name': name}); return link; }; -this.item_onmouseover = function(e) { +this.item_onmouseover = function(e) +{ if (this.className != 'googie_list_revert' && this.className != 'googie_list_close') this.className = 'googie_list_onhover'; else this.parentNode.className = 'googie_list_onhover'; }; -this.item_onmouseout = function(e) { + +this.item_onmouseout = function(e) +{ if (this.className != 'googie_list_revert' && this.className != 'googie_list_close') this.className = 'googie_list_onout'; else diff --git a/program/js/list.js b/program/js/list.js index 1e326e2..5116df7 100644 --- a/program/js/list.js +++ b/program/js/list.js @@ -1,39 +1,39 @@ -function rcube_list_widget(a,b){this.ENTER_KEY=13;this.DELETE_KEY=46;this.BACKSPACE_KEY=8;this.list=a?a:null;this.frame=null;this.rows=[];this.selection=[];this.colcount=this.rowcount=0;this.subject_col=-1;this.col_drag_active=this.drag_active=this.dont_select=this.toggleselect=this.keyboard=this.column_movable=this.draggable=this.multi_selecting=this.multiexpand=this.multiselect=this.shiftkey=!1;this.column_fixed=null;this.shift_start=this.last_selected=0;this.focused=this.in_selection_before=!1; -this.drag_mouse_start=null;this.dblclick_time=600;this.row_init=function(){};if(b&&typeof b==="object")for(var c in b)this[c]=b[c]} -rcube_list_widget.prototype={init:function(){if(this.list&&this.list.tBodies[0]){this.rows=[];this.rowcount=0;var a,b,c=this.list.tBodies[0].rows;for(a=0,b=c.length;a1){this.drag_start=!0;this.drag_mouse_start=rcube_event.get_mouse_pos(a); -rcube_event.add_listener({event:"mousemove",object:this,method:"column_drag_mouse_move"});rcube_event.add_listener({event:"mouseup",object:this,method:"column_drag_mouse_up"});this.add_dragfix();for(var c=0;c=c.depth-1)f=c.depth,$(e).css("display",""),b.expanded=!0,this.triggerEvent("expandcollapse",{uid:b.uid,expanded:b.expanded})}else if(a&&(!c||c.depth<=d))break}e=e.nextSibling}return!1}, -collapse_all:function(a){var b,c,d;if(a){if(a.expanded=!1,b=a.depth,c=a.obj.nextSibling,this.update_expando(a.uid),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded}),b&&this.multiexpand)return!1}else c=this.list.tBodies[0].firstChild,b=0;for(;c;){if(c.nodeType==1&&(d=this.rows[c.uid])){if(a&&(!d.depth||d.depth<=b))break;(a||d.depth)&&$(c).css("display","none");if(d.has_children&&d.expanded)d.expanded=!1,this.update_expando(d.uid,!1),this.triggerEvent("expandcollapse",{uid:d.uid,expanded:d.expanded})}c= -c.nextSibling}return!1},expand_all:function(a){var b,c,d;a?(a.expanded=!0,b=a.depth,c=a.obj.nextSibling,this.update_expando(a.uid,!0),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded})):(c=this.list.tBodies[0].firstChild,b=0);for(;c;){if(c.nodeType==1&&(d=this.rows[c.uid])){if(a&&d.depth<=b)break;$(c).css("display","");if(d.has_children&&!d.expanded)d.expanded=!0,this.update_expando(d.uid,!0),this.triggerEvent("expandcollapse",{uid:d.uid,expanded:d.expanded})}c=c.nextSibling}return!1}, -update_expando:function(a,b){var c=document.getElementById("rcmexpando"+a);if(c)c.className=b?"expanded":"collapsed"},get_next_row:function(){if(!this.rows)return!1;for(var a=this.rows[this.last_selected],a=a?a.obj.nextSibling:null;a&&(a.nodeType!=1||a.style.display=="none");)a=a.nextSibling;return a},get_prev_row:function(){if(!this.rows)return!1;for(var a=this.rows[this.last_selected],a=a?a.obj.previousSibling:null;a&&(a.nodeType!=1||a.style.display=="none");)a=a.previousSibling;return a},get_first_row:function(){if(this.rowcount){var a, -b,c=this.list.tBodies[0].rows;for(a=0,b=c.length-1;a=0;a--)if(b[a].id&&String(b[a].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i)&&this.rows[RegExp.$1]!=null)return RegExp.$1}return null},select_row:function(a,b,c){var d=this.selection.join(",");this.multiselect||(b=0);if(!this.shift_start)this.shift_start= -a;if(b){switch(b){case SHIFT_KEY:this.shift_select(a,!1);break;case CONTROL_KEY:c||this.highlight_row(a,!0);break;case CONTROL_SHIFT_KEY:this.shift_select(a,!0);break;default:this.highlight_row(a,!1)}this.multi_selecting=!0}else this.shift_start=a,this.highlight_row(a,!1),this.multi_selecting=!1;this.selection.join(",")!=d&&this.triggerEvent("select");this.last_selected!=0&&this.rows[this.last_selected]&&$(this.rows[this.last_selected].obj).removeClass("focused");this.toggleselect&&this.last_selected== -a?(this.clear_selection(),a=null):$(this.rows[a].obj).addClass("focused");if(!this.selection.length)this.shift_start=null;this.last_selected=a},select:function(a){this.select_row(a,!1);this.scrollto(a)},select_next:function(){var a=this.get_next_row(),b=this.get_prev_row();(a=a?a:b)&&this.select_row(a.uid,!1,!1)},select_first:function(a){var b=this.get_first_row();b&&(a?(this.shift_select(b,a),this.triggerEvent("select"),this.scrollto(b)):this.select(b))},select_last:function(a){var b=this.get_last_row(); -b&&(a?(this.shift_select(b,a),this.triggerEvent("select"),this.scrollto(b)):this.select(b))},select_childs:function(a){if(this.rows[a]&&this.rows[a].has_children)for(var b=this.rows[a].depth,a=this.rows[a].obj.nextSibling;a;){if(a.nodeType==1&&(r=this.rows[a.uid])){if(!r.depth||r.depth<=b)break;this.in_selection(r.uid)||this.select_row(r.uid,CONTROL_KEY)}a=a.nextSibling}},shift_select:function(a,b){if(!this.rows[this.shift_start]||!this.selection.length)this.shift_start=a;var c,d=this.rows[this.shift_start].obj.rowIndex, -e=this.rows[a].obj.rowIndex,f=de?d:e;for(c in this.rows)this.rows[c].obj.rowIndex>=f&&this.rows[c].obj.rowIndex<=d?this.in_selection(c)||this.highlight_row(c,!0):this.in_selection(c)&&!b&&this.highlight_row(c,!0)},in_selection:function(a){for(var b in this.selection)if(this.selection[b]==a)return!0;return!1},select_all:function(a){if(!this.rows||!this.rows.length)return!1;var b,c=this.selection.join(",");this.selection=[];for(b in this.rows)!a||this.rows[b][a]==!0?(this.last_selected=b, -this.highlight_row(b,!0)):$(this.rows[b].obj).removeClass("selected").removeClass("unfocused");this.selection.join(",")!=c&&this.triggerEvent("select");this.focus();return!0},invert_selection:function(){if(!this.rows||!this.rows.length)return!1;var a,b=this.selection.join(",");for(a in this.rows)this.highlight_row(a,!0);this.selection.join(",")!=b&&this.triggerEvent("select");this.focus();return!0},clear_selection:function(a){var b,c=this.selection.length;if(a)for(b in this.selection){if(this.selection[b]== -a){this.selection.splice(b,1);break}}else{for(b in this.selection)this.rows[this.selection[b]]&&$(this.rows[this.selection[b]].obj).removeClass("selected").removeClass("unfocused");this.selection=[]}c&&!this.selection.length&&this.triggerEvent("select")},get_selection:function(){return this.selection},get_single_selection:function(){return this.selection.length==1?this.selection[0]:null},highlight_row:function(a,b){if(this.rows[a]&&!b){if(this.selection.length>1||!this.in_selection(a))this.clear_selection(), -this.selection[0]=a,$(this.rows[a].obj).addClass("selected")}else if(this.rows[a])if(this.in_selection(a)){var c=$.inArray(a,this.selection),d=this.selection.slice(0,c),c=this.selection.slice(c+1,this.selection.length);this.selection=d.concat(c);$(this.rows[a].obj).removeClass("selected").removeClass("unfocused")}else this.selection[this.selection.length]=a,$(this.rows[a].obj).addClass("selected")},key_press:function(a){if(this.focused!=!0)return!0;var b=rcube_event.get_keycode(a),c=rcube_event.get_modifier(a); -switch(b){case 40:case 38:case 63233:case 63232:return rcube_event.cancel(a),this.use_arrow_key(b,c);case 61:case 107:case 109:case 32:return rcube_event.cancel(a),a=this.use_plusminus_key(b,c),this.key_pressed=b,this.triggerEvent("keypress"),a;case 36:return this.select_first(c),rcube_event.cancel(a);case 35:return this.select_last(c),rcube_event.cancel(a);default:if(this.shiftkey=a.shiftKey,this.key_pressed=b,this.triggerEvent("keypress"),this.shiftkey=!1,this.key_pressed==this.BACKSPACE_KEY)return rcube_event.cancel(a)}return!0}, -key_down:function(a){switch(rcube_event.get_keycode(a)){case 27:if(this.drag_active)return this.drag_mouse_up(a);if(this.col_drag_active)return this.selected_column=null,this.column_drag_mouse_up(a);case 40:case 38:case 63233:case 63232:case 61:case 107:case 109:case 32:if(!rcube_event.get_modifier(a)&&this.focused)return rcube_event.cancel(a)}return!0},use_arrow_key:function(a,b){var c;if(a==40||a==63233)c=this.get_next_row();else if(a==38||a==63232)c=this.get_prev_row();c&&(this.select_row(c.uid, -b,!0),this.scrollto(c.uid));return!1},use_plusminus_key:function(a,b){var c=this.rows[this.last_selected];if(c)return a==32&&(a=c.expanded?109:61),a==61||a==107?b==CONTROL_KEY||this.multiexpand?this.expand_all(c):this.expand(c):b==CONTROL_KEY||this.multiexpand?this.collapse_all(c):this.collapse(c),this.update_expando(c.uid,c.expanded),!1},scrollto:function(a){var b=this.rows[a].obj;if(b&&this.frame){var c=Number(b.offsetTop);!c&&this.rows[a].parent_uid&&(this.expand_all(this.rows[this.find_root(this.rows[a].uid)]), -c=Number(b.offsetTop));if(cNumber(this.frame.scrollTop)+Number(this.frame.offsetHeight))this.frame.scrollTop=c+Number(b.offsetHeight)-Number(this.frame.offsetHeight)}},drag_mouse_move:function(a){if(a.type=="touchmove")if(a.changedTouches.length==1)a=rcube_event.touchevent(a.changedTouches[0]);else return rcube_event.cancel(a);if(this.drag_start){var b=rcube_event.get_mouse_pos(a);if(!this.drag_mouse_start||Math.abs(b.x- -this.drag_mouse_start.x)<3&&Math.abs(b.y-this.drag_mouse_start.y)<3)return!1;if(!this.draglayer)this.draglayer=$("
    ").attr("id","rcmdraglayer").css({position:"absolute",display:"none","z-index":2E3}).appendTo(document.body);var c,d,e=$.merge([],this.selection);for(c in e)d=e[c],this.rows[d].has_children&&!this.rows[d].expanded&&this.select_childs(d);this.draglayer.html("");for(c=0;c12){this.draglayer.append("...");break}if(e=this.rows[this.selection[c]].obj)for(b= -0,d=0;d=0&&this.subject_col==b){for(var f,g,h=e.childNodes[d].childNodes,b=0;b50?d.substring(0,50)+"...":d;this.draglayer.append($("
    ").text(d));break}b++}}this.draglayer.show();this.drag_active=!0;this.triggerEvent("dragstart")}this.drag_active&& -this.draglayer&&(c=rcube_event.get_mouse_pos(a),this.draglayer.css({left:c.x+20+"px",top:c.y-5+(bw.ie?document.documentElement.scrollTop:0)+"px"}),this.triggerEvent("dragmove",a?a:window.event));return this.drag_start=!1},drag_mouse_up:function(a){document.onmousemove=null;if(a.type=="touchend"&&a.changedTouches.length!=1)return rcube_event.cancel(a);this.draglayer&&this.draglayer.is(":visible")&&(this.drag_start_pos?this.draglayer.animate(this.drag_start_pos,300,"swing").hide(20):this.draglayer.hide()); -this.drag_active&&this.focus();this.drag_active=!1;rcube_event.remove_listener({event:"mousemove",object:this,method:"drag_mouse_move"});rcube_event.remove_listener({event:"mouseup",object:this,method:"drag_mouse_up"});if(bw.iphone||bw.ipad)rcube_event.remove_listener({event:"touchmove",object:this,method:"drag_mouse_move"}),rcube_event.remove_listener({event:"touchend",object:this,method:"drag_mouse_up"});this.del_dragfix();this.triggerEvent("dragend");return rcube_event.cancel(a)},column_drag_mouse_move:function(a){if(this.drag_start){var b; -b=rcube_event.get_mouse_pos(a);if(!this.drag_mouse_start||Math.abs(b.x-this.drag_mouse_start.x)<3&&Math.abs(b.y-this.drag_mouse_start.y)<3)return!1;if(!this.col_draglayer){b=$(this.list).offset();var c=this.list.tHead.rows[0].cells;this.col_draglayer=$("
    ").attr("id","rcmcoldraglayer").css(b).css({position:"absolute","z-index":2001,"background-color":"white",opacity:0.75,height:this.frame.offsetHeight-2+"px",width:this.frame.offsetWidth-2+"px"}).appendTo(document.body).append($("
    ").attr("id", -"rcmcolumnindicator").css({position:"absolute","border-right":"2px dotted #555","z-index":2002,height:this.frame.offsetHeight-2+"px"}));this.cols=[];this.list_pos=this.list_min_pos=b.left;for(b=0;b=this.cols[b]/2+this.list_pos+c)c+=this.cols[b];else break;b==0&&this.list_min_pos>d.x?c=this.list_min_pos-this.list_pos:!this.list.rowcount&&b==this.cols.length&&(c-=2);$("#rcmcolumnindicator").css({width:c+"px"});this.triggerEvent("column_dragmove",a?a:window.event)}return this.drag_start=!1},column_drag_mouse_up:function(a){document.onmousemove=null;if(this.col_draglayer)this.col_draglayer.remove(),this.col_draglayer=null;this.col_drag_active&&this.focus();this.col_drag_active= -!1;rcube_event.remove_listener({event:"mousemove",object:this,method:"column_drag_mouse_move"});rcube_event.remove_listener({event:"mouseup",object:this,method:"column_drag_mouse_up"});this.del_dragfix();if(this.selected_column!==null&&this.cols&&this.cols.length){var b,c=0,d=rcube_event.get_mouse_pos(a);for(b=0;b=this.cols[b]/2+this.list_pos+c)c+=this.cols[b];else break;b!=this.selected_column&&b!=this.selected_column+1&&this.column_replace(this.selected_column,b)}this.triggerEvent("column_dragend"); -return rcube_event.cancel(a)},add_dragfix:function(){$("iframe").each(function(){$('
    ').css({background:"#fff",width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css($(this).offset()).appendTo(document.body)})},del_dragfix:function(){$("div.iframe-dragdrop-fix").each(function(){this.parentNode.removeChild(this)})},column_replace:function(a,b){var c;c=this.list.tHead.rows[0].cells;var d=c[a],e=c[b],f=document.createElement("td"); -e?c[0].parentNode.insertBefore(f,e):c[0].parentNode.appendChild(f);c[0].parentNode.replaceChild(d,f);for(r=0,c=this.list.tBodies[0].rows.length;ra?b-1:b:this.subject_cola&&b>=this.subject_col&&this.subject_col--;this.triggerEvent("column_replace")}}; -rcube_list_widget.prototype.addEventListener=rcube_event_engine.prototype.addEventListener;rcube_list_widget.prototype.removeEventListener=rcube_event_engine.prototype.removeEventListener;rcube_list_widget.prototype.triggerEvent=rcube_event_engine.prototype.triggerEvent; +this.rows[c]&&this.rows[c].obj&&$(this.rows[c].obj).addClass("selected").removeClass("unfocused");$("*:focus",window).blur();$("iframe").each(function(){this.blur()});(a||(a=window.event))&&rcube_event.cancel(a)},blur:function(){var a,b;this.focused=!1;for(a in this.selection)b=this.selection[a],this.rows[b]&&this.rows[b].obj&&$(this.rows[b].obj).removeClass("selected").addClass("unfocused")},drag_column:function(a,b){if(1=c.depth-1)f=c.depth,$(e).css("display",""),b.expanded=!0,this.triggerEvent("expandcollapse",{uid:b.uid,expanded:b.expanded})}else if(a&&(!c||c.depth<=d))break}e=e.nextSibling}return!1},collapse_all:function(a){var b,c,d;if(a){if(a.expanded= +!1,b=a.depth,c=a.obj.nextSibling,this.update_expando(a.uid),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded}),b&&this.multiexpand)return!1}else c=this.list.tBodies[0].firstChild,b=0;for(;c;){if(1==c.nodeType&&(d=this.rows[c.uid])){if(a&&(!d.depth||d.depth<=b))break;(a||d.depth)&&$(c).css("display","none");if(d.has_children&&d.expanded)d.expanded=!1,this.update_expando(d.uid,!1),this.triggerEvent("expandcollapse",{uid:d.uid,expanded:d.expanded})}c=c.nextSibling}return!1},expand_all:function(a){var b, +c,d;a?(a.expanded=!0,b=a.depth,c=a.obj.nextSibling,this.update_expando(a.uid,!0),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded})):(c=this.list.tBodies[0].firstChild,b=0);for(;c;){if(1==c.nodeType&&(d=this.rows[c.uid])){if(a&&d.depth<=b)break;$(c).css("display","");if(d.has_children&&!d.expanded)d.expanded=!0,this.update_expando(d.uid,!0),this.triggerEvent("expandcollapse",{uid:d.uid,expanded:d.expanded})}c=c.nextSibling}return!1},update_expando:function(a,b){var c=document.getElementById("rcmexpando"+ +a);if(c)c.className=b?"expanded":"collapsed"},get_next_row:function(){if(!this.rows)return!1;for(var a=this.rows[this.last_selected],a=a?a.obj.nextSibling:null;a&&(1!=a.nodeType||"none"==a.style.display);)a=a.nextSibling;return a},get_prev_row:function(){if(!this.rows)return!1;for(var a=this.rows[this.last_selected],a=a?a.obj.previousSibling:null;a&&(1!=a.nodeType||"none"==a.style.display);)a=a.previousSibling;return a},get_first_row:function(){if(this.rowcount){var a,b,c=this.list.tBodies[0].rows; +for(a=0,b=c.length-1;ae?d:e;for(c in this.rows)this.rows[c].obj.rowIndex>= +f&&this.rows[c].obj.rowIndex<=d?this.in_selection(c)||this.highlight_row(c,!0):this.in_selection(c)&&!b&&this.highlight_row(c,!0)},in_selection:function(a){for(var b in this.selection)if(this.selection[b]==a)return!0;return!1},select_all:function(a){if(!this.rows||!this.rows.length)return!1;var b,c=this.selection.join(",");this.selection=[];for(b in this.rows)!a||!0==this.rows[b][a]?(this.last_selected=b,this.highlight_row(b,!0)):$(this.rows[b].obj).removeClass("selected").removeClass("unfocused"); +this.selection.join(",")!=c&&this.triggerEvent("select");this.focus();return!0},invert_selection:function(){if(!this.rows||!this.rows.length)return!1;var a,b=this.selection.join(",");for(a in this.rows)this.highlight_row(a,!0);this.selection.join(",")!=b&&this.triggerEvent("select");this.focus();return!0},clear_selection:function(a){var b,c=this.selection.length;if(a)for(b in this.selection){if(this.selection[b]==a){this.selection.splice(b,1);break}}else{for(b in this.selection)this.rows[this.selection[b]]&& +$(this.rows[this.selection[b]].obj).removeClass("selected").removeClass("unfocused");this.selection=[]}c&&!this.selection.length&&this.triggerEvent("select")},get_selection:function(){return this.selection},get_single_selection:function(){return 1==this.selection.length?this.selection[0]:null},highlight_row:function(a,b){if(this.rows[a]&&!b){if(1Number(this.frame.scrollTop)+Number(this.frame.offsetHeight))this.frame.scrollTop=c+Number(b.offsetHeight)-Number(this.frame.offsetHeight)}},drag_mouse_move:function(a){if("touchmove"==a.type)if(1==a.changedTouches.length)a=rcube_event.touchevent(a.changedTouches[0]);else return rcube_event.cancel(a); +if(this.drag_start){var b=rcube_event.get_mouse_pos(a);if(!this.drag_mouse_start||3>Math.abs(b.x-this.drag_mouse_start.x)&&3>Math.abs(b.y-this.drag_mouse_start.y))return!1;if(!this.draglayer)this.draglayer=$("
    ").attr("id","rcmdraglayer").css({position:"absolute",display:"none","z-index":2E3}).appendTo(document.body);var c,d,e=$.merge([],this.selection);for(c in e)d=e[c],this.rows[d].has_children&&!this.rows[d].expanded&&this.select_childs(d);this.draglayer.html("");for(c=0;cthis.subject_col||0<=this.subject_col&&this.subject_col==b){for(var f,g,h=e.childNodes[d].childNodes,b=0;b").text(d)); +break}b++}}this.draglayer.show();this.drag_active=!0;this.triggerEvent("dragstart")}this.drag_active&&this.draglayer&&(c=rcube_event.get_mouse_pos(a),this.draglayer.css({left:c.x+20+"px",top:c.y-5+(bw.ie?document.documentElement.scrollTop:0)+"px"}),this.triggerEvent("dragmove",a?a:window.event));return this.drag_start=!1},drag_mouse_up:function(a){document.onmousemove=null;if("touchend"==a.type&&1!=a.changedTouches.length)return rcube_event.cancel(a);this.draglayer&&this.draglayer.is(":visible")&& +(this.drag_start_pos?this.draglayer.animate(this.drag_start_pos,300,"swing").hide(20):this.draglayer.hide());this.drag_active&&this.focus();this.drag_active=!1;rcube_event.remove_listener({event:"mousemove",object:this,method:"drag_mouse_move"});rcube_event.remove_listener({event:"mouseup",object:this,method:"drag_mouse_up"});if(bw.iphone||bw.ipad)rcube_event.remove_listener({event:"touchmove",object:this,method:"drag_mouse_move"}),rcube_event.remove_listener({event:"touchend",object:this,method:"drag_mouse_up"}); +this.del_dragfix();this.triggerEvent("dragend");return rcube_event.cancel(a)},column_drag_mouse_move:function(a){if(this.drag_start){var b;b=rcube_event.get_mouse_pos(a);if(!this.drag_mouse_start||3>Math.abs(b.x-this.drag_mouse_start.x)&&3>Math.abs(b.y-this.drag_mouse_start.y))return!1;if(!this.col_draglayer){b=$(this.list).offset();var c=this.list.tHead.rows[0].cells;this.col_draglayer=$("
    ").attr("id","rcmcoldraglayer").css(b).css({position:"absolute","z-index":2001,"background-color":"white", +opacity:0.75,height:this.frame.offsetHeight-2+"px",width:this.frame.offsetWidth-2+"px"}).appendTo(document.body).append($("
    ").attr("id","rcmcolumnindicator").css({position:"absolute","border-right":"2px dotted #555","z-index":2002,height:this.frame.offsetHeight-2+"px"}));this.cols=[];this.list_pos=this.list_min_pos=b.left;for(b=0;b=this.cols[b]/2+this.list_pos+c)c+=this.cols[b];else break;0==b&&this.list_min_pos>d.x?c=this.list_min_pos-this.list_pos:!this.list.rowcount&&b==this.cols.length&&(c-=2);$("#rcmcolumnindicator").css({width:c+"px"});this.triggerEvent("column_dragmove",a?a:window.event)}return this.drag_start=!1},column_drag_mouse_up:function(a){document.onmousemove= +null;if(this.col_draglayer)this.col_draglayer.remove(),this.col_draglayer=null;this.col_drag_active&&this.focus();this.col_drag_active=!1;rcube_event.remove_listener({event:"mousemove",object:this,method:"column_drag_mouse_move"});rcube_event.remove_listener({event:"mouseup",object:this,method:"column_drag_mouse_up"});this.del_dragfix();if(null!==this.selected_column&&this.cols&&this.cols.length){var b,c=0,d=rcube_event.get_mouse_pos(a);for(b=0;b=this.cols[b]/2+this.list_pos+ +c)c+=this.cols[b];else break;b!=this.selected_column&&b!=this.selected_column+1&&this.column_replace(this.selected_column,b)}this.triggerEvent("column_dragend");return rcube_event.cancel(a)},add_dragfix:function(){$("iframe").each(function(){$('
    ').css({background:"#fff",width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css($(this).offset()).appendTo(document.body)})},del_dragfix:function(){$("div.iframe-dragdrop-fix").each(function(){this.parentNode.removeChild(this)})}, +column_replace:function(a,b){var c;c=this.list.tHead.rows[0].cells;var d=c[a],e=c[b],f=document.createElement("td");e?c[0].parentNode.insertBefore(f,e):c[0].parentNode.appendChild(f);c[0].parentNode.replaceChild(d,f);for(r=0,c=this.list.tBodies[0].rows.length;ra?b-1:b:this.subject_cola&&b>=this.subject_col&&this.subject_col--;this.triggerEvent("column_replace")}};rcube_list_widget.prototype.addEventListener=rcube_event_engine.prototype.addEventListener;rcube_list_widget.prototype.removeEventListener=rcube_event_engine.prototype.removeEventListener;rcube_list_widget.prototype.triggerEvent=rcube_event_engine.prototype.triggerEvent; diff --git a/program/js/list.js.src b/program/js/list.js.src index e47b547..c8864c3 100644 --- a/program/js/list.js.src +++ b/program/js/list.js.src @@ -13,7 +13,7 @@ | Requires: common.js | +-----------------------------------------------------------------------+ - $Id: list.js 4750 2011-05-12 09:27:17Z alec $ + $Id: list.js 5271 2011-09-22 20:51:42Z thomasb $ */ @@ -36,7 +36,7 @@ function rcube_list_widget(list, p) this.colcount = 0; this.subject_col = -1; - this.shiftkey = false; + this.modkey = 0; this.multiselect = false; this.multiexpand = false; this.multi_selecting = false; @@ -648,7 +648,7 @@ select_row: function(id, mod_key, with_mouse) case CONTROL_KEY: if (!with_mouse) this.highlight_row(id, true); - break; + break; case CONTROL_SHIFT_KEY: this.shift_select(id, true); @@ -954,7 +954,8 @@ highlight_row: function(id, multiple) */ key_press: function(e) { - if (this.focused != true) + var target = e.target || {}; + if (this.focused != true || target.nodeName == 'INPUT' || target.nodeName == 'TEXTAREA' || target.nodeName == 'SELECT') return true; var keyCode = rcube_event.get_keycode(e), @@ -962,7 +963,7 @@ key_press: function(e) switch (keyCode) { case 40: - case 38: + case 38: case 63233: // "down", in safari keypress case 63232: // "up", in safari keypress // Stop propagation so that the browser doesn't scroll @@ -976,7 +977,9 @@ key_press: function(e) rcube_event.cancel(e); var ret = this.use_plusminus_key(keyCode, mod_key); this.key_pressed = keyCode; + this.modkey = mod_key; this.triggerEvent('keypress'); + this.modkey = 0; return ret; case 36: // Home this.select_first(mod_key); @@ -985,11 +988,10 @@ key_press: function(e) this.select_last(mod_key); return rcube_event.cancel(e); default: - this.shiftkey = e.shiftKey; this.key_pressed = keyCode; + this.modkey = mod_key; this.triggerEvent('keypress'); - // reset shiftkey flag, we need it only for registered events - this.shiftkey = false; + this.modkey = 0; if (this.key_pressed == this.BACKSPACE_KEY) return rcube_event.cancel(e); @@ -1003,13 +1005,17 @@ key_press: function(e) */ key_down: function(e) { + var target = e.target || {}; + if (this.focused != true || target.nodeName == 'INPUT' || target.nodeName == 'TEXTAREA' || target.nodeName == 'SELECT') + return true; + switch (rcube_event.get_keycode(e)) { case 27: if (this.drag_active) - return this.drag_mouse_up(e); + return this.drag_mouse_up(e); if (this.col_drag_active) { this.selected_column = null; - return this.column_drag_mouse_up(e); + return this.column_drag_mouse_up(e); } case 40: @@ -1044,7 +1050,7 @@ use_arrow_key: function(keyCode, mod_key) new_row = this.get_prev_row(); if (new_row) { - this.select_row(new_row.uid, mod_key, true); + this.select_row(new_row.uid, mod_key, false); this.scrollto(new_row.uid); } diff --git a/program/lib/html2text.php b/program/lib/html2text.php index 1ab1605..9fc96ea 100644 --- a/program/lib/html2text.php +++ b/program/lib/html2text.php @@ -145,7 +145,6 @@ class html2text var $search = array( "/\r/", // Non-legal carriage return "/[\n\t]+/", // Newlines and tabs - '/[ ]{2,}/', // Runs of spaces, pre-handling '/]*>.*?<\/script>/i', //