summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/main.inc.php.dist8
-rw-r--r--index.php48
-rw-r--r--plugins/additional_message_headers/additional_message_headers.php42
-rw-r--r--plugins/autologon/autologon.php44
-rw-r--r--plugins/database_attachments/database_attachments.php152
-rw-r--r--plugins/debug_logger/debug_logger.php146
-rw-r--r--plugins/debug_logger/runlog/runlog.php227
-rw-r--r--plugins/emoticons/emoticons.php39
-rw-r--r--plugins/example_addressbook/example_addressbook.php42
-rw-r--r--plugins/example_addressbook/example_addressbook_backend.php72
-rw-r--r--plugins/filesystem_attachments/filesystem_attachments.php144
-rw-r--r--plugins/http_authentication/http_authentication.php41
-rw-r--r--plugins/markasjunk/junk_act.pngbin0 -> 1995 bytes
-rw-r--r--plugins/markasjunk/junk_pas.pngbin0 -> 1988 bytes
-rw-r--r--plugins/markasjunk/localization/en_US.inc7
-rw-r--r--plugins/markasjunk/markasjunk.js28
-rw-r--r--plugins/markasjunk/markasjunk.php47
-rw-r--r--plugins/new_user_identity/new_user_identity.php49
-rw-r--r--plugins/password/localization/en_US.inc15
-rw-r--r--plugins/password/localization/pl_PL.inc15
-rw-r--r--plugins/password/password.js44
-rw-r--r--plugins/password/password.php160
-rw-r--r--plugins/show_additional_headers/show_additional_headers.php49
-rw-r--r--plugins/subscriptions_option/localization/en_US.inc6
-rw-r--r--plugins/subscriptions_option/subscriptions_option.php84
-rw-r--r--plugins/userinfo/localization/de_CH.inc9
-rw-r--r--plugins/userinfo/localization/en_US.inc9
-rw-r--r--plugins/userinfo/userinfo.js16
-rw-r--r--plugins/userinfo/userinfo.php53
-rw-r--r--plugins/vcard_attachments/vcard_attachments.php115
-rw-r--r--plugins/vcard_attachments/vcardattach.js10
-rw-r--r--program/include/html.php30
-rwxr-xr-xprogram/include/iniset.php2
-rw-r--r--program/include/main.inc25
-rw-r--r--program/include/rcmail.php44
-rw-r--r--program/include/rcube_addressbook.php169
-rw-r--r--program/include/rcube_config.php1
-rw-r--r--program/include/rcube_contacts.php35
-rw-r--r--program/include/rcube_html_page.php11
-rw-r--r--program/include/rcube_imap.php31
-rw-r--r--program/include/rcube_json_output.php26
-rw-r--r--program/include/rcube_ldap.php2
-rw-r--r--program/include/rcube_message.php3
-rw-r--r--program/include/rcube_plugin.php196
-rw-r--r--program/include/rcube_plugin_api.php312
-rwxr-xr-xprogram/include/rcube_template.php112
-rw-r--r--program/include/rcube_user.php26
-rw-r--r--program/include/rcube_vcard.php4
-rw-r--r--program/js/app.js997
-rw-r--r--program/js/common.js230
-rw-r--r--program/js/editor.js4
-rw-r--r--program/js/list.js172
-rw-r--r--program/lib/imap.inc18
-rw-r--r--program/steps/addressbook/func.inc36
-rw-r--r--program/steps/mail/attachments.inc74
-rw-r--r--program/steps/mail/compose.inc50
-rw-r--r--program/steps/mail/func.inc176
-rw-r--r--program/steps/mail/sendmail.inc87
-rw-r--r--program/steps/mail/show.inc4
-rw-r--r--program/steps/settings/func.inc12
-rw-r--r--program/steps/settings/manage_folders.inc1
-rw-r--r--program/steps/settings/save_prefs.inc3
-rw-r--r--skins/default/common.css7
-rw-r--r--skins/default/functions.js30
-rw-r--r--skins/default/includes/settingstabs.html2
-rw-r--r--skins/default/includes/taskbar.html1
-rw-r--r--skins/default/mail.css2
-rw-r--r--skins/default/splitter.js40
-rw-r--r--skins/default/templates/addressbook.html4
-rw-r--r--skins/default/templates/identities.html2
-rw-r--r--skins/default/templates/mail.html1
-rw-r--r--skins/default/templates/managefolders.html2
-rw-r--r--skins/default/templates/message.html1
-rw-r--r--skins/default/templates/plugin.html24
-rw-r--r--skins/default/templates/settings.html6
75 files changed, 3466 insertions, 1270 deletions
diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist
index 850b2ba6c..9e3a25ef7 100644
--- a/config/main.inc.php.dist
+++ b/config/main.inc.php.dist
@@ -35,6 +35,12 @@ $rcmail_config['log_dir'] = 'logs/';
// use this folder to store temp files (must be writeable for apache user)
$rcmail_config['temp_dir'] = 'temp/';
+// use this folder to search for plugin sources
+$rcmail_config['plugins_dir'] = 'plugins/';
+
+// List of active plugins. Add the name of a directory found in 'plugins_dir'
+$rcmail_config['plugins'] = array();
+
// enable caching of messages and mailbox data in the local database.
// this is recommended if the IMAP server does not run on the same machine
$rcmail_config['enable_caching'] = TRUE;
@@ -152,7 +158,7 @@ $rcmail_config['date_long'] = 'd.m.Y H:i';
$rcmail_config['date_today'] = 'H:i';
// add this user-agent to message headers when sending
-$rcmail_config['useragent'] = 'RoundCube Webmail/0.2-beta';
+$rcmail_config['useragent'] = 'RoundCube Webmail/0.3-beta';
// use this name to compose page titles
$rcmail_config['product_name'] = 'RoundCube Webmail';
diff --git a/index.php b/index.php
index 172d57c88..7c2d23032 100644
--- a/index.php
+++ b/index.php
@@ -2,7 +2,7 @@
/*
+-------------------------------------------------------------------------+
| RoundCube Webmail IMAP Client |
- | Version 0.2-20080829 |
+ | Version 0.3-20090419 |
| |
| Copyright (C) 2005-2009, RoundCube Dev. - Switzerland |
| |
@@ -36,6 +36,9 @@ $RCMAIL = rcmail::get_instance();
// init output class
$OUTPUT = !empty($_REQUEST['_remote']) ? $RCMAIL->init_json() : $RCMAIL->load_gui(!empty($_REQUEST['_framed']));
+// init plugin API
+$RCMAIL->plugins->init();
+
// set output buffering
if ($RCMAIL->action != 'get' && $RCMAIL->action != 'viewsource') {
// use gzip compression if supported
@@ -70,21 +73,29 @@ if ($RCMAIL->action=='error' && !empty($_GET['_code'])) {
raise_error(array('code' => hexdec($_GET['_code'])), FALSE, TRUE);
}
+
+// trigger startup plugin hook
+$startup = $RCMAIL->plugins->exec_hook('startup', array('task' => $RCMAIL->task, 'action' => $RCMAIL->action));
+$RCMAIL->set_task($startup['task']);
+$RCMAIL->action = $startup['action'];
+
+
// try to log in
if ($RCMAIL->action=='login' && $RCMAIL->task=='mail') {
// purge the session in case of new login when a session already exists
- $RCMAIL->kill_session();
-
- // set IMAP host
- $host = $RCMAIL->autoselect_host();
+ $RCMAIL->kill_session();
+ $auth = $RCMAIL->plugins->exec_hook('authenticate', array(
+ 'host' => $RCMAIL->autoselect_host(),
+ 'user' => trim(get_input_value('_user', RCUBE_INPUT_POST)),
+ )) + array('pass' => get_input_value('_pass', RCUBE_INPUT_POST, true, 'ISO-8859-1'));
+
// check if client supports cookies
if (empty($_COOKIE)) {
$OUTPUT->show_message("cookiesdisabled", 'warning');
}
- else if ($_SESSION['temp'] && !empty($_POST['_user']) && !empty($_POST['_pass']) &&
- $RCMAIL->login(trim(get_input_value('_user', RCUBE_INPUT_POST), ' '),
- get_input_value('_pass', RCUBE_INPUT_POST, true, 'ISO-8859-1'), $host)) {
+ else if ($_SESSION['temp'] && !empty($auth['user']) && !empty($auth['host']) && isset($auth['pass']) &&
+ $RCMAIL->login($auth['user'], $auth['pass'], $auth['host'])) {
// create new session ID
unset($_SESSION['temp']);
rcube_sess_regenerate_id();
@@ -99,12 +110,22 @@ if ($RCMAIL->action=='login' && $RCMAIL->task=='mail') {
$RCMAIL->user->ID,
$_SERVER['REMOTE_ADDR']));
}
+
+ // restore original request parameters
+ $query = array();
+ if ($url = get_input_value('_url', RCUBE_INPUT_POST))
+ parse_str($url, $query);
+
+ // allow plugins to control the redirect url after login success
+ $redir = $RCMAIL->plugins->exec_hook('login_after', $query + array('task' => $RCMAIL->task));
+ unset($redir['abort']);
// send redirect
- $OUTPUT->redirect();
+ $OUTPUT->redirect($redir);
}
else {
$OUTPUT->show_message($IMAP->error_code < -1 ? 'imaperror' : 'loginfailed', 'warning');
+ $RCMAIL->plugins->exec_hook('login_failed', array('code' => $IMAP->error_code, 'host' => $auth['host'], 'user' => $auth['user']));
$RCMAIL->kill_session();
}
}
@@ -208,9 +229,14 @@ $redirects = 0; $incstep = null;
while ($redirects < 5) {
$stepfile = !empty($action_map[$RCMAIL->task][$RCMAIL->action]) ?
$action_map[$RCMAIL->task][$RCMAIL->action] : strtr($RCMAIL->action, '-', '_') . '.inc';
-
+
+ // execute a plugin action
+ if (eregi('^plugin.', $RCMAIL->action)) {
+ $RCMAIL->plugins->exec_action($RCMAIL->action);
+ break;
+ }
// try to include the step file
- if (is_file(($incfile = 'program/steps/'.$RCMAIL->task.'/'.$stepfile))) {
+ else if (is_file(($incfile = 'program/steps/'.$RCMAIL->task.'/'.$stepfile))) {
include($incfile);
$redirects++;
}
diff --git a/plugins/additional_message_headers/additional_message_headers.php b/plugins/additional_message_headers/additional_message_headers.php
new file mode 100644
index 000000000..92471384e
--- /dev/null
+++ b/plugins/additional_message_headers/additional_message_headers.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * Additional Message Headers
+ *
+ * Very simple plugin which will read additional headers for outgoing messages from the config file.
+ *
+ * Enable the plugin in config/main.inc.php and add your desired headers.
+ *
+ * @version 1.0
+ * @author Ziba Scott
+ * @website http://roundcube.net
+ *
+ * Example:
+ *
+ * $rcmail_config['additional_message_headers']['X-Remote-Browser'] = $_SERVER['HTTP_USER_AGENT'];
+ * $rcmail_config['additional_message_headers']['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
+ * $rcmail_config['additional_message_headers']['X-RoundCube-Server'] = $_SERVER['SERVER_ADDR'];
+ * if( isset( $_SERVER['MACHINE_NAME'] )) {
+ * $rcmail_config['additional_message_headers']['X-RoundCube-Server'] .= ' (' . $_SERVER['MACHINE_NAME'] . ')';
+ * }
+ */
+class additional_message_headers extends rcube_plugin
+{
+ public $task = 'mail';
+
+ function init()
+ {
+ $this->add_hook('outgoing_message_headers', array($this, 'message_headers'));
+ }
+
+ function message_headers($args){
+
+ // additional email headers
+ $additional_headers = rcmail::get_instance()->config->get('additional_message_headers',array());
+ foreach($additional_headers as $header=>$value){
+ $args['headers'][$header] = $value;
+ }
+
+ return $args;
+ }
+}
diff --git a/plugins/autologon/autologon.php b/plugins/autologon/autologon.php
new file mode 100644
index 000000000..c40f2d4eb
--- /dev/null
+++ b/plugins/autologon/autologon.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * Sample plugin to try out some hooks.
+ * This performs an automatic login if accessed from localhost
+ */
+class autologon extends rcube_plugin
+{
+
+ function init()
+ {
+ $this->add_hook('startup', array($this, 'startup'));
+ $this->add_hook('authenticate', array($this, 'authenticate'));
+ }
+
+ function startup($args)
+ {
+ $rcmail = rcmail::get_instance();
+
+ // change action to login
+ if ($args['task'] == 'mail' && empty($args['action']) && empty($_SESSION['user_id']) && !empty($_GET['_autologin']) && $this->is_localhost())
+ $args['action'] = 'login';
+
+ return $args;
+ }
+
+ function authenticate($args)
+ {
+ if (!empty($_GET['_autologin']) && $this->is_localhost()) {
+ $args['user'] = 'me';
+ $args['pass'] = '******';
+ $args['host'] = 'localhost';
+ }
+
+ return $args;
+ }
+
+ function is_localhost()
+ {
+ return $_SERVER['REMOTE_ADDR'] == '::1' || $_SERVER['REMOTE_ADDR'] == '127.0.0.1';
+ }
+
+}
+
diff --git a/plugins/database_attachments/database_attachments.php b/plugins/database_attachments/database_attachments.php
new file mode 100644
index 000000000..28ccde4b3
--- /dev/null
+++ b/plugins/database_attachments/database_attachments.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Filesystem Attachments
+ *
+ * This plugin which provides database backed storage for temporary
+ * attachment file handling. The primary advantage of this plugin
+ * is its compatibility with round-robin dns multi-server roundcube
+ * installations.
+ *
+ * This plugin relies on the core filesystem_attachments plugin
+ *
+ * @author Ziba Scott <ziba@umich.edu>
+ *
+ */
+require_once('plugins/filesystem_attachments/filesystem_attachments.php');
+class database_attachments extends filesystem_attachments
+{
+
+ // A prefix for the cache key used in the session and in the key field of the cache table
+ private $cache_prefix = "db_attach";
+
+ /**
+ * Helper method to generate a unique key for the given attachment file
+ */
+ private function _key($filepath)
+ {
+ return $this->cache_prefix.md5(mktime().$filepath.$_SESSION['user_id']);
+ }
+
+ /**
+ * Save a newly uploaded attachment
+ */
+ function upload($args)
+ {
+ $args['status'] = false;
+ $rcmail = rcmail::get_instance();
+ $key = $this->_key($args['path']);
+ $data = base64_encode(file_get_contents($args['path']));
+
+ $status = $rcmail->db->query(
+ "INSERT INTO ".get_table_name('cache')."
+ (created, user_id, cache_key, data)
+ VALUES (".$rcmail->db->now().", ?, ?, ?)",
+ $_SESSION['user_id'],
+ $key,
+ $data);
+
+ if ($status) {
+ $args['id'] = $key;
+ $args['status'] = true;
+ unset($args['path']);
+ }
+
+ return $args;
+ }
+
+ /**
+ * Save an attachment from a non-upload source (draft or forward)
+ */
+ function save($args)
+ {
+ $args['status'] = false;
+ $rcmail = rcmail::get_instance();
+
+ $key = $this->_key($args['name']);
+ $data = base64_encode($args['data']);
+
+ $status = $rcmail->db->query(
+ "INSERT INTO ".get_table_name('cache')."
+ (created, user_id, cache_key, data)
+ VALUES (".$rcmail->db->now().", ?, ?, ?)",
+ $_SESSION['user_id'],
+ $key,
+ $data);
+
+ if ($status) {
+ $args['id'] = $key;
+ $args['status'] = true;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Remove an attachment from storage
+ * This is triggered by the remove attachment button on the compose screen
+ */
+ function remove($args)
+ {
+ $args['status'] = false;
+ $rcmail = rcmail::get_instance();
+ $status = $rcmail->db->query(
+ "DELETE FROM ".get_table_name('cache')."
+ WHERE user_id=?
+ AND cache_key=?",
+ $_SESSION['user_id'],
+ $args['id']);
+
+ if ($status) {
+ $args['status'] = true;
+ }
+
+ return $args;
+ }
+
+ /**
+ * When composing an html message, image attachments may be shown
+ * For this plugin, $this->get_attachment will check the file and
+ * return it's contents
+ */
+ function display($args)
+ {
+ return $this->get_attachment($args);
+ }
+
+ /**
+ * When displaying or sending the attachment the file contents are fetched
+ * using this method. This is also called by the display_attachment hook.
+ */
+ function get_attachment($args)
+ {
+ $rcmail = rcmail::get_instance();
+
+ $sql_result = $rcmail->db->query(
+ "SELECT cache_id, data
+ FROM ".get_table_name('cache')."
+ WHERE user_id=?
+ AND cache_key=?",
+ $_SESSION['user_id'],
+ $args['id']);
+
+ if ($sql_arr = $rcmail->db->fetch_assoc($sql_result)) {
+ $args['data'] = base64_decode($sql_arr['data']);
+ $args['status'] = true;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Delete all temp files associated with this user
+ */
+ function cleanup($args)
+ {
+ $rcmail = rcmail::get_instance();
+ $rcmail->db->query(
+ "DELETE FROM ".get_table_name('cache')."
+ WHERE user_id=?
+ AND cache_key like '{$this->cache_prefix}%'",
+ $_SESSION['user_id']);
+ }
+}
diff --git a/plugins/debug_logger/debug_logger.php b/plugins/debug_logger/debug_logger.php
new file mode 100644
index 000000000..8cd335187
--- /dev/null
+++ b/plugins/debug_logger/debug_logger.php
@@ -0,0 +1,146 @@
+<?php
+
+/**
+ * Debug Logger
+ *
+ * Enhanced logging for debugging purposes. It is not recommened
+ * to be enabled on production systems without testing because of
+ * the somewhat increased memory, cpu and disk i/o overhead.
+ *
+ * Debug Logger listens for existing console("message") calls and
+ * introduces start and end tags as well as free form tagging
+ * which can redirect messages to files. The resulting log files
+ * provide timing and tag quantity results.
+ *
+ * Enable the plugin in config/main.inc.php and add your desired
+ * log types and files.
+ *
+ * @version 1.0
+ * @author Ziba Scott
+ * @website http://roundcube.net
+ *
+ * Example:
+ *
+ * config/main.inc.php:
+ *
+ * // $rcmail_config['debug_logger'][type of logging] = name of file in log_dir
+ * // The 'master' log includes timing information
+ * $rcmail_config['debug_logger']['master'] = 'master';
+ * // If you want sql messages to also go into a separate file
+ * $rcmail_config['debug_logger']['sql'] = 'sql';
+ *
+ * index.php (just after $RCMAIL->plugins->init()):
+ *
+ * console("my test","start");
+ * console("my message");
+ * console("my sql calls","start");
+ * console("cp -r * /dev/null","shell exec");
+ * console("select * from example","sql");
+ * console("select * from example","sql");
+ * console("select * from example","sql");
+ * console("end");
+ * console("end");
+ *
+ *
+ * logs/master (after reloading the main page):
+ *
+ * [17-Feb-2009 16:51:37 -0500] start: Task: mail.
+ * [17-Feb-2009 16:51:37 -0500] start: my test
+ * [17-Feb-2009 16:51:37 -0500] my message
+ * [17-Feb-2009 16:51:37 -0500] shell exec: cp -r * /dev/null
+ * [17-Feb-2009 16:51:37 -0500] start: my sql calls
+ * [17-Feb-2009 16:51:37 -0500] sql: select * from example
+ * [17-Feb-2009 16:51:37 -0500] sql: select * from example
+ * [17-Feb-2009 16:51:37 -0500] sql: select * from example
+ * [17-Feb-2009 16:51:37 -0500] end: my sql calls - 0.0018 seconds shell exec: 1, sql: 3,
+ * [17-Feb-2009 16:51:37 -0500] end: my test - 0.0055 seconds shell exec: 1, sql: 3,
+ * [17-Feb-2009 16:51:38 -0500] end: Task: mail. - 0.8854 seconds shell exec: 1, sql: 3,
+ *
+ * logs/sql (after reloading the main page):
+ *
+ * [17-Feb-2009 16:51:37 -0500] sql: select * from example
+ * [17-Feb-2009 16:51:37 -0500] sql: select * from example
+ * [17-Feb-2009 16:51:37 -0500] sql: select * from example
+ */
+class debug_logger extends rcube_plugin
+{
+ function init()
+ {
+ require_once(dirname(__FILE__).'/runlog/runlog.php');
+ $this->runlog = new runlog();
+
+ if(!rcmail::get_instance()->config->get('log_dir')){
+ rcmail::get_instance()->config->set('log_dir',INSTALL_PATH.'logs');
+ }
+
+ $log_config = rcmail::get_instance()->config->get('debug_logger',array());
+
+ foreach($log_config as $type=>$file){
+ $this->runlog->set_file(rcmail::get_instance()->config->get('log_dir').'/'.$file, $type);
+ }
+
+ $start_string = "";
+ $action = rcmail::get_instance()->action;
+ $task = rcmail::get_instance()->task;
+ if($action){
+ $start_string .= "Action: ".$action.". ";
+ }
+ if($task){
+ $start_string .= "Task: ".$task.". ";
+ }
+ $this->runlog->start($start_string);
+
+ $this->add_hook('console', array($this, 'console'));
+ $this->add_hook('authenticate', array($this, 'authenticate'));
+ }
+
+ function authenticate($args){
+ $this->runlog->note('Authenticating '.$args['user'].'@'.$args['host']);
+ return $args;
+ }
+
+ function console($args){
+ $note = $args[0];
+ $type = $args[1];
+
+
+ if(!isset($args[1])){
+ // This could be extended to detect types based on the
+ // file which called console. For now only rcube_imap.inc is supported
+ $bt = debug_backtrace(true);
+ $file = $bt[3]['file'];
+ switch(basename($file)){
+ case 'rcube_imap.php':
+ $type = 'imap';
+ break;
+ default:
+ $type = FALSE;
+ break;
+ }
+ }
+ switch($note){
+ case 'end':
+ $type = 'end';
+ break;
+ }
+
+
+ switch($type){
+ case 'start':
+ $this->runlog->start($note);
+ break;
+ case 'end':
+ $this->runlog->end();
+ break;
+ default:
+ $this->runlog->note($note, $type);
+ break;
+ }
+ return $args;
+ }
+
+ function __destruct(){
+ $this->runlog->end();
+ }
+}
+?>
diff --git a/plugins/debug_logger/runlog/runlog.php b/plugins/debug_logger/runlog/runlog.php
new file mode 100644
index 000000000..c9f672615
--- /dev/null
+++ b/plugins/debug_logger/runlog/runlog.php
@@ -0,0 +1,227 @@
+<?php
+
+/**
+ * runlog
+ *
+ * @author Ziba Scott <ziba@umich.edu>
+ */
+class runlog {
+
+ private $start_time = FALSE;
+
+ private $parent_stack = array();
+
+ public $print_to_console = FALSE;
+
+ private $file_handles = array();
+
+ private $indent = 0;
+
+ public $threshold = 0;
+
+ public $tag_count = array();
+
+ public $timestamp = "d-M-Y H:i:s O";
+
+ public $max_line_size = 150;
+
+ private $run_log = array();
+
+ function runlog()
+ {
+ $this->start_time = microtime( TRUE );
+ }
+
+ public function start( $name, $tag = FALSE )
+ {
+ $this->run_log[] = array( 'type' => 'start',
+ 'tag' => $tag,
+ 'index' => count($this->run_log),
+ 'value' => $name,
+ 'time' => microtime( TRUE ),
+ 'parents' => $this->parent_stack,
+ 'ended' => false,
+ );
+ $this->parent_stack[] = $name;
+
+ $this->print_to_console("start: ".$name, $tag, 'start');
+ $this->print_to_file("start: ".$name, $tag, 'start');
+ $this->indent++;
+ }
+
+ public function end()
+ {
+ $name = array_pop( $this->parent_stack );
+ foreach ( $this->run_log as $k => $entry ) {
+ if ( $entry['value'] == $name && $entry['type'] == 'start' && $entry['ended'] == false) {
+ $lastk = $k;
+ }
+ }
+ $start = $this->run_log[$lastk]['time'];
+ $this->run_log[$lastk]['duration'] = microtime( TRUE ) - $start;
+ $this->run_log[$lastk]['ended'] = true;
+
+ $this->run_log[] = array( 'type' => 'end',
+ 'tag' => $this->run_log[$lastk]['tag'],
+ 'index' => $lastk,
+ 'value' => $name,
+ 'time' => microtime( TRUE ),
+ 'duration' => microtime( TRUE ) - $start,
+ 'parents' => $this->parent_stack,
+ );
+ $this->indent--;
+ if($this->run_log[$lastk]['duration'] >= $this->threshold){
+ $tag_report = "";
+ foreach($this->tag_count as $tag=>$count){
+ $tag_report .= "$tag: $count, ";
+ }
+ if(!empty($tag_report)){
+// $tag_report = "\n$tag_report\n";
+ }
+ $end_txt = sprintf("end: $name - %0.4f seconds $tag_report", $this->run_log[$lastk]['duration'] );
+ $this->print_to_console($end_txt, $this->run_log[$lastk]['tag'] , 'end');
+ $this->print_to_file($end_txt, $this->run_log[$lastk]['tag'], 'end');
+ }
+ }
+
+ public function increase_tag_count($tag){
+ if(!isset($this->tag_count[$tag])){
+ $this->tag_count[$tag] = 0;
+ }
+ $this->tag_count[$tag]++;
+ }
+
+ public function get_text(){
+ $text = "";
+ foreach($this->run_log as $entry){
+ $text .= str_repeat(" ",count($entry['parents']));
+ if($entry['tag'] != 'text'){
+ $text .= $entry['tag'].': ';
+ }
+ $text .= $entry['value'];
+
+ if($entry['tag'] == 'end'){
+ $text .= sprintf(" - %0.4f seconds", $entry['duration'] );
+ }
+
+ $text .= "\n";
+ }
+ return $text;
+ }
+
+ public function set_file($filename, $tag = 'master'){
+ if(!isset($this->file_handle[$tag])){
+ $this->file_handles[$tag] = fopen($filename, 'a');
+ if(!$this->file_handles[$tag]){
+ trigger_error('Could not open file for writing: '.$filename);
+ }
+ }
+ }
+
+ public function note( $msg, $tag = FALSE )
+ {
+ if($tag){
+ $this->increase_tag_count($tag);
+ }
+ if ( is_array( $msg )) {
+ $msg = '<pre>' . print_r( $msg, TRUE ) . '</pre>';
+ }
+ $this->debug_messages[] = $msg;
+ $this->run_log[] = array( 'type' => 'note',
+ 'tag' => $tag ? $tag:"text",
+ 'value' => htmlentities($msg),
+ 'time' => microtime( TRUE ),
+ 'parents' => $this->parent_stack,
+ );
+
+ $this->print_to_file($msg, $tag);
+ $this->print_to_console($msg, $tag);
+
+ }
+
+ public function print_to_file($msg, $tag = FALSE, $type = FALSE){
+ if(!$tag){
+ $file_handle_tag = 'master';
+ }
+ else{
+ $file_handle_tag = $tag;
+ }
+ if($file_handle_tag != 'master' && isset($this->file_handles[$file_handle_tag])){
+ $buffer = $this->get_indent();
+ $buffer .= "$msg\n";
+ if(!empty($this->timestamp)){
+ $buffer = sprintf("[%s] %s",date($this->timestamp, mktime()), $buffer);
+ }
+ fwrite($this->file_handles[$file_handle_tag], wordwrap($buffer, $this->max_line_size, "\n "));
+ }
+ if(isset($this->file_handles['master']) && $this->file_handles['master']){
+ $buffer = $this->get_indent();
+ if($tag){
+ $buffer .= "$tag: ";
+ }
+ $msg = str_replace("\n","",$msg);
+ $buffer .= "$msg";
+ if(!empty($this->timestamp)){
+ $buffer = sprintf("[%s] %s",date($this->timestamp, mktime()), $buffer);
+ }
+ if(strlen($buffer) > $this->max_line_size){
+ $buffer = substr($buffer,0,$this->max_line_size - 3)."...";
+ }
+ fwrite($this->file_handles['master'], $buffer."\n");
+ }
+ }
+
+ public function print_to_console($msg, $tag=FALSE){
+ if($this->print_to_console){
+ if(is_array($this->print_to_console)){
+ if(in_array($tag, $this->print_to_console)){
+ echo $this->get_indent();
+ if($tag){
+ echo "$tag: ";
+ }
+ echo "$msg\n";
+ }
+ }
+ else{
+ echo $this->get_indent();
+ if($tag){
+ echo "$tag: ";
+ }
+ echo "$msg\n";
+ }
+ }
+ }
+
+ public function print_totals(){
+ $totals = array();
+ foreach ( $this->run_log as $k => $entry ) {
+ if ( $entry['type'] == 'start' && $entry['ended'] == true) {
+ $totals[$entry['value']]['duration'] += $entry['duration'];
+ $totals[$entry['value']]['count'] += 1;
+ }
+ }
+ if($this->file_handle){
+ foreach($totals as $name=>$details){
+ fwrite($this->file_handle,$name.": ".number_format($details['duration'],4)."sec, ".$details['count']." calls \n");
+ }
+ }
+ }
+
+ private function get_indent(){
+ $buf = "";
+ for($i = 0; $i < $this->indent; $i++){
+ $buf .= " ";
+ }
+ return $buf;
+ }
+
+
+ function __destruct(){
+ foreach($this->file_handles as $handle){
+ fclose($handle);
+ }
+ }
+
+}
+
+?>
diff --git a/plugins/emoticons/emoticons.php b/plugins/emoticons/emoticons.php
new file mode 100644
index 000000000..be736b625
--- /dev/null
+++ b/plugins/emoticons/emoticons.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * Display Emoticons
+ *
+ * Sample plugin to replace emoticons in plain text message body with real icons
+ *
+ * @version 1.0.1
+ * @author Thomas Bruederli
+ * @website http://roundcube.net
+ */
+class emoticons extends rcube_plugin
+{
+ public $task = 'mail';
+ private $map;
+
+ function init()
+ {
+ $this->task = 'mail';
+ $this->add_hook('message_part_after', array($this, 'replace'));
+
+ $this->map = array(
+ ':)' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-smile.gif', 'alt' => ':)')),
+ ':-)' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-smile.gif', 'alt' => ':-)')),
+ ':(' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-cry.gif', 'alt' => ':(')),
+ ':-(' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-cry.gif', 'alt' => ':-(')),
+ );
+ }
+
+ function replace($args)
+ {
+ if ($args['type'] == 'plain')
+ return array('body' => strtr($args['body'], $this->map));
+
+ return null;
+ }
+
+}
+
diff --git a/plugins/example_addressbook/example_addressbook.php b/plugins/example_addressbook/example_addressbook.php
new file mode 100644
index 000000000..081efcb13
--- /dev/null
+++ b/plugins/example_addressbook/example_addressbook.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * Sample plugin to add a new address book
+ * with just a static list of contacts
+ */
+class example_addressbook extends rcube_plugin
+{
+ private $abook_id = 'static';
+
+ public function init()
+ {
+ $this->add_hook('address_sources', array($this, 'address_sources'));
+ $this->add_hook('get_address_book', array($this, 'get_address_book'));
+
+ // use this address book for autocompletion queries
+ // (maybe this should be configurable by the user?)
+ $config = rcmail::get_instance()->config;
+ $sources = $config->get('autocomplete_addressbooks', array('sql'));
+ if (!in_array($this->abook_id, $sources)) {
+ $sources[] = $this->abook_id;
+ $config->set('autocomplete_addressbooks', $sources);
+ }
+ }
+
+ public function address_sources($p)
+ {
+ $p['sources'][$this->abook_id] = array('id' => $this->abook_id, 'name' => 'Static List', 'readonly' => true);
+ return $p;
+ }
+
+ public function get_address_book($p)
+ {
+ if ($p['id'] == $this->abook_id) {
+ require_once(dirname(__FILE__) . '/example_addressbook_backend.php');
+ $p['instance'] = new example_addressbook_backend;
+ }
+
+ return $p;
+ }
+
+}
diff --git a/plugins/example_addressbook/example_addressbook_backend.php b/plugins/example_addressbook/example_addressbook_backend.php
new file mode 100644
index 000000000..ad6b89d67
--- /dev/null
+++ b/plugins/example_addressbook/example_addressbook_backend.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Example backend class for a custom address book
+ *
+ * This one just holds a static list of address records
+ *
+ * @author Thomas Bruederli
+ */
+class example_addressbook_backend extends rcube_addressbook
+{
+ public $primary_key = 'ID';
+ public $readonly = true;
+
+ private $filter;
+ private $result;
+
+ public function __construct()
+ {
+ $this->ready = true;
+ }
+
+ public function set_search_set($filter)
+ {
+ $this->filter = $filter;
+ }
+
+ public function get_search_set()
+ {
+ return $this->filter;
+ }
+
+ public function reset()
+ {
+ $this->result = null;
+ $this->filter = null;
+ }
+
+ public function list_records($cols=null, $subset=0)
+ {
+ $this->result = $this->count();
+ $this->result->add(array('ID' => '111', 'name' => "Example Contact", 'firstname' => "Example", 'surname' => "Contact", 'email' => "example@roundcube.net"));
+
+ return $this->result;
+ }
+
+ public function search($fields, $value, $strict=false, $select=true)
+ {
+ // no search implemented, just list all records
+ return $this->list_records();
+ }
+
+ public function count()
+ {
+ return new rcube_result_set(1, ($this->list_page-1) * $this->page_size);
+ }
+
+ public function get_result()
+ {
+ return $this->result;
+ }
+
+ public function get_record($id, $assoc=false)
+ {
+ $this->list_records();
+ $first = $this->result->first();
+ $sql_arr = $first['ID'] == $id ? $first : null;
+
+ return $assoc && $sql_arr ? $sql_arr : $this->result;
+ }
+
+}
diff --git a/plugins/filesystem_attachments/filesystem_attachments.php b/plugins/filesystem_attachments/filesystem_attachments.php
new file mode 100644
index 000000000..9a6c0a81d
--- /dev/null
+++ b/plugins/filesystem_attachments/filesystem_attachments.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * Filesystem Attachments
+ *
+ * This is a core plugin which provides basic, filesystem based
+ * attachment temporary file handling. This includes storing
+ * attachments of messages currently being composed, writing attachments
+ * to disk when drafts with attachments are re-opened and writing
+ * attachments to disk for inline display in current html compositions.
+ *
+ * Developers may wish to extend this class when creating attachment
+ * handler plugins:
+ * require_once('plugins/filesystem_attachments/filesystem_attachments.php');
+ * class myCustom_attachments extends filesystem_attachments
+ *
+ * @author Ziba Scott <ziba@umich.edu>
+ * @author Thomas Bruederli <roundcube@gmail.com>
+ *
+ */
+class filesystem_attachments extends rcube_plugin
+{
+ public $task = 'mail';
+
+ function init()
+ {
+ // Save a newly uploaded attachment
+ $this->add_hook('upload_attachment', array($this, 'upload'));
+
+ // Save an attachment from a non-upload source (draft or forward)
+ $this->add_hook('save_attachment', array($this, 'save'));
+
+ // Remove an attachment from storage
+ $this->add_hook('remove_attachment', array($this, 'remove'));
+
+ // When composing an html message, image attachments may be shown
+ $this->add_hook('display_attachment', array($this, 'display'));
+
+ // Get the attachment from storage and place it on disk to be sent
+ $this->add_hook('get_attachment', array($this, 'get_attachment'));
+
+ // Delete all temp files associated with this user
+ $this->add_hook('cleanup_attachments', array($this, 'cleanup'));
+ }
+
+ /**
+ * Save a newly uploaded attachment
+ */
+ function upload($args)
+ {
+ $args['status'] = false;
+ $rcmail = rcmail::get_instance();
+
+ // use common temp dir for file uploads
+ // #1484529: we need absolute path on Windows for move_uploaded_file()
+ $temp_dir = realpath($rcmail->config->get('temp_dir'));
+ $tmpfname = tempnam($temp_dir, 'rcmAttmnt');
+
+ if (move_uploaded_file($args['path'], $tmpfname) && file_exists($tmpfname)) {
+ $args['id'] = count($_SESSION['plugins']['filesystem_attachments']['tmp_files'])+1;
+ $args['path'] = $tmpfname;
+ $args['status'] = true;
+
+ // Note the file for later cleanup
+ $_SESSION['plugins']['filesystem_attachments']['tmp_files'][] = $tmpfname;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Save an attachment from a non-upload source (draft or forward)
+ */
+ function save($args)
+ {
+ $args['status'] = false;
+ $rcmail = rcmail::get_instance();
+ $temp_dir = unslashify($rcmail->config->get('temp_dir'));
+ $tmp_path = tempnam($temp_dir, 'rcmAttmnt');
+
+ if ($fp = fopen($tmp_path, 'w')) {
+ fwrite($fp, $args['data']);
+ fclose($fp);
+
+ $args['id'] = count($_SESSION['plugins']['filesystem_attachments']['tmp_files'])+1;
+ $args['path'] = $tmp_path;
+ $args['status'] = true;
+
+ // Note the file for later cleanup
+ $_SESSION['plugins']['filesystem_attachments']['tmp_files'][] = $tmp_path;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Remove an attachment from storage
+ * This is triggered by the remove attachment button on the compose screen
+ */
+ function remove($args)
+ {
+ $args['status'] = @unlink($args['path']);
+ return $args;
+ }
+
+ /**
+ * When composing an html message, image attachments may be shown
+ * For this plugin, the file is already in place, just check for
+ * the existance of the proper metadata
+ */
+ function display($args)
+ {
+ $args['status'] = file_exists($args['path']);
+ return $args;
+ }
+
+ /**
+ * This attachment plugin doesn't require any steps to put the file
+ * on disk for use. This stub function is kept here to make this
+ * class handy as a parent class for other plugins which may need it.
+ */
+ function get_attachment($args)
+ {
+ return $args;
+ }
+
+ /**
+ * Delete all temp files associated with this user
+ */
+ function cleanup($args)
+ {
+ // $_SESSION['compose']['attachments'] is not a complete record of
+ // temporary files because loading a draft or starting a forward copies
+ // the file to disk, but does not make an entry in that array
+ if (is_array($_SESSION['plugins']['filesystem_attachments']['tmp_files'])){
+ foreach ($_SESSION['plugins']['filesystem_attachments']['tmp_files'] as $filename){
+ if(file_exists($filename)){
+ unlink($filename);
+ }
+ }
+ unset($_SESSION['plugins']['filesystem_attachments']['tmp_files']);
+ }
+ return $args;
+ }
+}
diff --git a/plugins/http_authentication/http_authentication.php b/plugins/http_authentication/http_authentication.php
new file mode 100644
index 000000000..57422a74d
--- /dev/null
+++ b/plugins/http_authentication/http_authentication.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * HTTP Basic Authentication
+ *
+ * Make use of an existing HTTP authentication and perform login with the existing user credentials
+ *
+ * @version 1.0
+ * @author Thomas Bruederli
+ */
+class http_authentication extends rcube_plugin
+{
+
+ function init()
+ {
+ $this->add_hook('startup', array($this, 'startup'));
+ $this->add_hook('authenticate', array($this, 'authenticate'));
+ }
+
+ function startup($args)
+ {
+ // change action to login
+ if ($args['task'] == 'mail' && empty($args['action']) && empty($_SESSION['user_id'])
+ && !empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW']))
+ $args['action'] = 'login';
+
+ return $args;
+ }
+
+ function authenticate($args)
+ {
+ if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
+ $args['user'] = $_SERVER['PHP_AUTH_USER'];
+ $args['pass'] = $_SERVER['PHP_AUTH_PW'];
+ }
+
+ return $args;
+ }
+
+}
+
diff --git a/plugins/markasjunk/junk_act.png b/plugins/markasjunk/junk_act.png
new file mode 100644
index 000000000..b5a84f604
--- /dev/null
+++ b/plugins/markasjunk/junk_act.png
Binary files differ
diff --git a/plugins/markasjunk/junk_pas.png b/plugins/markasjunk/junk_pas.png
new file mode 100644
index 000000000..b88a561a4
--- /dev/null
+++ b/plugins/markasjunk/junk_pas.png
Binary files differ
diff --git a/plugins/markasjunk/localization/en_US.inc b/plugins/markasjunk/localization/en_US.inc
new file mode 100644
index 000000000..6f63e161a
--- /dev/null
+++ b/plugins/markasjunk/localization/en_US.inc
@@ -0,0 +1,7 @@
+<?php
+
+$labels = array();
+$labels['buttontitle'] = 'Mark as Junk';
+$labels['reportedasjunk'] = 'Successfully reported as Junk';
+
+?> \ No newline at end of file
diff --git a/plugins/markasjunk/markasjunk.js b/plugins/markasjunk/markasjunk.js
new file mode 100644
index 000000000..8b02d7438
--- /dev/null
+++ b/plugins/markasjunk/markasjunk.js
@@ -0,0 +1,28 @@
+/* Mark-as-Junk plugin script */
+
+function rcmail_markasjunk(prop)
+{
+ if (!rcmail.env.uid && (!rcmail.message_list || !rcmail.message_list.get_selection().length))
+ return;
+
+ var uids = rcmail.env.uid ? rcmail.env.uid : rcmail.message_list.get_selection().join(',');
+
+ rcmail.set_busy(true, 'loading');
+ rcmail.http_post('plugin.markasjunk', '_uid='+uids+'&_mbox='+urlencode(rcmail.env.mailbox), true);
+}
+
+// callback for app-onload event
+if (window.rcmail) {
+ rcmail.addEventListener('init', function(evt) {
+
+ // register command (directly enable in message view mode)
+ rcmail.register_command('plugin.markasjunk', rcmail_markasjunk, rcmail.env.uid);
+
+ // add event-listener to message list
+ if (rcmail.message_list)
+ rcmail.message_list.addEventListener('select', function(list){
+ rcmail.enable_command('plugin.markasjunk', list.get_selection().length > 0);
+ });
+ })
+}
+
diff --git a/plugins/markasjunk/markasjunk.php b/plugins/markasjunk/markasjunk.php
new file mode 100644
index 000000000..959111d84
--- /dev/null
+++ b/plugins/markasjunk/markasjunk.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * Mark as Junk
+ *
+ * Sample plugin that adds a new button to the mailbox toolbar
+ * to mark the selected messages as Junk and move them to the Junk folder
+ *
+ * @version 1.0
+ * @author Thomas Bruederli
+ */
+class markasjunk extends rcube_plugin
+{
+ public $task = 'mail';
+
+ function init()
+ {
+ $this->register_action('plugin.markasjunk', array($this, 'request_action'));
+ $GLOBALS['IMAP_FLAGS']['JUNK'] = 'Junk';
+
+ $rcmail = rcmail::get_instance();
+ if ($rcmail->action == '' || $rcmail->action == 'show') {
+ $this->include_script('markasjunk.js');
+ $this->add_texts('localization', true);
+ $this->add_button(array('command' => 'plugin.markasjunk', 'imagepas' => 'junk_pas.png', 'imageact' => 'junk_act.png'), 'toolbar');
+ }
+ }
+
+ function request_action()
+ {
+ $this->add_texts('localization');
+
+ $uids = get_input_value('_uid', RCUBE_INPUT_POST);
+ $mbox = get_input_value('_mbox', RCUBE_INPUT_POST);
+
+ $rcmail = rcmail::get_instance();
+ $rcmail->imap->set_flag($uids, 'JUNK');
+
+ if (($junk_mbox = $rcmail->config->get('junk_mbox')) && $mbox != $junk_mbox) {
+ $rcmail->output->command('move_messages', $junk_mbox);
+ }
+
+ $rcmail->output->command('display_message', $this->gettext('reportedasjunk'), 'confirmation');
+ $rcmail->output->send();
+ }
+
+} \ No newline at end of file
diff --git a/plugins/new_user_identity/new_user_identity.php b/plugins/new_user_identity/new_user_identity.php
new file mode 100644
index 000000000..75595693c
--- /dev/null
+++ b/plugins/new_user_identity/new_user_identity.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * New user identity
+ *
+ * Populates a new user's default identity from LDAP on their first visit.
+ *
+ * This plugin requires that a working public_ldap directory be configured.
+ *
+ * @version 1.0
+ * @author Kris Steinhoff
+ *
+ * Example configuration:
+ *
+ * // The id of the address book to use to automatically set a new
+ * // user's full name in their new identity. (This should be an
+ * // string, which refers to the $rcmail_config['ldap_public'] array.)
+ * $rcmail_config['new_user_identity_addressbook'] = 'People';
+ *
+ * // When automatically setting a new users's full name in their
+ * // new identity, match the user's login name against this field.
+ * $rcmail_config['new_user_identity_match'] = 'uid';
+ *
+ * // Use the value in this field to automatically set a new users's
+ * // full name in their new identity.
+ * $rcmail_config['new_user_identity_field'] = 'name';
+ */
+class new_user_identity extends rcube_plugin
+{
+ function init()
+ {
+ $this->add_hook('create_user', array($this, 'lookup_user_name'));
+ }
+
+ function lookup_user_name($args)
+ {
+ $rcmail = rcmail::get_instance();
+ if ($addressbook = $rcmail->config->get('new_user_identity_addressbook')) {
+ $match = $rcmail->config->get('new_user_identity_match');
+ $ldap = $rcmail->get_address_book($addressbook);
+ $ldap->prop['search_fields'] = array($match);
+ $results = $ldap->search($match, $args['user'], TRUE);
+ if (count($results->records) == 1) {
+ $args['user_name'] = $results->records[0][$rcmail->config->get('new_user_identity_field')];
+ }
+ }
+ return $args;
+ }
+}
+?>
diff --git a/plugins/password/localization/en_US.inc b/plugins/password/localization/en_US.inc
new file mode 100644
index 000000000..b54bcd4c9
--- /dev/null
+++ b/plugins/password/localization/en_US.inc
@@ -0,0 +1,15 @@
+<?php
+
+$labels = array();
+$labels['changepasswd'] = 'Change Password';
+$labels['curpasswd'] = 'Current Password:';
+$labels['newpasswd'] = 'New Password:';
+$labels['confpasswd'] = 'Confirm New Password:';
+
+$messages = array();
+$messages['nopassword'] = "Please input new password.";
+$messages['nocurpassword'] = "Please input current password.";
+$messages['passwordincorrectly'] = "Current password incorrectly.";
+$messages['passwordinconsistency'] = "Inconsistency of password, please try again.";
+
+?> \ No newline at end of file
diff --git a/plugins/password/localization/pl_PL.inc b/plugins/password/localization/pl_PL.inc
new file mode 100644
index 000000000..197999531
--- /dev/null
+++ b/plugins/password/localization/pl_PL.inc
@@ -0,0 +1,15 @@
+<?php
+
+$labels = array();
+$labels['changepasswd'] = 'Zmiana hasła';
+$labels['curpasswd'] = 'Aktualne hasło:';
+$labels['newpasswd'] = 'Nowe hasło:';
+$labels['confpasswd'] = 'Potwierdź hasło:';
+
+$messages = array();
+$messages['nopassword'] = 'Wprowadź nowe hasło.';
+$messages['nocurpassword'] = 'Wprowadź aktualne hasło.';
+$messages['passwordincorrect'] = 'Błędne aktualne hasło, spróbuj ponownie.';
+$messages['passwordinconsistency'] = 'Hasła nie pasują, spróbuj ponownie.';
+
+?>
diff --git a/plugins/password/password.js b/plugins/password/password.js
new file mode 100644
index 000000000..3d05b622b
--- /dev/null
+++ b/plugins/password/password.js
@@ -0,0 +1,44 @@
+/* Password change interface (tab) */
+
+if (window.rcmail) {
+ rcmail.addEventListener('init', function(evt) {
+ // <span id="settingstabdefault" class="tablink"><roundcube:button command="preferences" type="link" label="preferences" title="editpreferences" /></span>
+ var tab = $('<span>').attr('id', 'settingstabpluginpassword').addClass('tablink');
+
+ var button = $('<a>').attr('href', rcmail.env.comm_path+'&_action=plugin.password').html(rcmail.gettext('password')).appendTo(tab);
+ button.bind('click', function(e){ return rcmail.command('plugin.password', this) });
+
+ // add button and register commands
+ rcmail.add_element(tab, 'tabs');
+ rcmail.register_command('plugin.password', function() { rcmail.goto_url('plugin.password') }, true);
+ rcmail.register_command('plugin.password-save', function() {
+ var input_curpasswd = rcube_find_object('_curpasswd');
+ var input_newpasswd = rcube_find_object('_newpasswd');
+ var input_confpasswd = rcube_find_object('_confpasswd');
+
+ if (input_curpasswd && input_curpasswd.value=='') {
+ alert(rcmail.gettext('nocurpassword', 'password'));
+ input_curpasswd.focus();
+ } else if (input_newpasswd && input_newpasswd.value=='') {
+ alert(rcmail.gettext('nopassword', 'password'));
+ input_newpasswd.focus();
+ } else if (input_confpasswd && input_confpasswd.value=='') {
+ alert(rcmail.gettext('nopassword', 'password'));
+ input_confpasswd.focus();
+ } else if ((input_newpasswd && input_confpasswd) && (input_newpasswd.value != input_confpasswd.value)) {
+ alert(rcmail.gettext('passwordinconsistency', 'password'));
+ input_newpasswd.focus();
+ } else {
+ rcmail.gui_objects.passform.submit();
+ }
+ }, true);
+ })
+
+ // set page title
+ if (rcmail.env.action == 'plugin.password' && rcmail.env.task == 'settings') {
+ var title = rcmail.gettext('changepasswd','password')
+ if (rcmail.env.product_name)
+ title = rcmail.env.product_name + ' :: ' + title;
+ rcmail.set_pagetitle(title);
+ }
+}
diff --git a/plugins/password/password.php b/plugins/password/password.php
new file mode 100644
index 000000000..4a35da119
--- /dev/null
+++ b/plugins/password/password.php
@@ -0,0 +1,160 @@
+<?php
+
+/**
+ * Change Password
+ *
+ * Sample plugin that adds a possibility to change password
+ * (Settings -> Password tab)
+ *
+ * @version 1.0
+ * @author Aleksander 'A.L.E.C' Machniak
+ */
+class password extends rcube_plugin
+{
+ public $task = 'settings';
+
+ function init()
+ {
+ $rcmail = rcmail::get_instance();
+ // add Tab label
+ $rcmail->output->add_label('password');
+ $this->register_action('plugin.password', array($this, 'password_init'));
+ $this->register_action('plugin.password-save', array($this, 'password_save'));
+ $this->register_handler('plugin.body', array($this, 'password_form'));
+ $this->include_script('password.js');
+ }
+
+ function password_init()
+ {
+ $this->add_texts('localization/');
+ rcmail::get_instance()->output->send('plugin');
+ }
+
+ function password_save()
+ {
+ $rcmail = rcmail::get_instance();
+
+ $this->add_texts('localization/');
+
+ if (!isset($_POST['_curpasswd']) || !isset($_POST['_newpasswd']))
+ $rcmail->output->command('display_message', $this->gettext('nopassword'), 'error');
+ else {
+ $curpwd = get_input_value('_curpasswd', RCUBE_INPUT_POST);
+ $newpwd = get_input_value('_newpasswd', RCUBE_INPUT_POST);
+
+ if ($_SESSION['password'] != $rcmail->encrypt_passwd($curpwd))
+ $rcmail->output->command('display_message', $this->gettext('passwordincorrect'), 'error');
+ else if ($res = $this->_save($newpwd)) {
+ $rcmail->output->command('display_message', $this->gettext('successfullysaved'), 'confirmation');
+ $_SESSION['password'] = $rcmail->encrypt_passwd($newpwd);
+ } else
+ $rcmail->output->command('display_message', $this->gettext('errorsaving'), 'error');
+ }
+
+ rcmail_overwrite_action('plugin.password');
+ rcmail::get_instance()->output->send('plugin');
+ }
+
+ function password_form()
+ {
+ $rcmail = rcmail::get_instance();
+
+ // add some labels to client
+ $rcmail->output->add_label(
+ 'password.nopassword',
+ 'password.nocurpassword',
+ 'password.passwordinconsistency',
+ 'password.changepasswd'
+ );
+// $rcmail->output->set_pagetitle($this->gettext('changepasswd'));
+ $rcmail->output->set_env('product_name', $rcmail->config->get('product_name'));
+
+ // allow the following attributes to be added to the <table> tag
+ $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
+
+ // return the complete edit form as table
+ $out = '<table' . $attrib_str . ">\n\n";
+
+ $a_show_cols = array('curpasswd' => array('type' => 'text'),
+ 'newpasswd' => array('type' => 'text'),
+ 'confpasswd' => array('type' => 'text'));
+
+ // show current password selection
+ $field_id = 'curpasswd';
+ $input_newpasswd = new html_passwordfield(array('name' => '_curpasswd', 'id' => $field_id, 'size' => 20));
+
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $field_id,
+ rep_specialchars_output($this->gettext('curpasswd')),
+ $input_newpasswd->show($rcmail->config->get('curpasswd')));
+
+ // show new password selection
+ $field_id = 'newpasswd';
+ $input_newpasswd = new html_passwordfield(array('name' => '_newpasswd', 'id' => $field_id, 'size' => 20));
+
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $field_id,
+ rep_specialchars_output($this->gettext('newpasswd')),
+ $input_newpasswd->show($rcmail->config->get('newpasswd')));
+
+ // show confirm password selection
+ $field_id = 'confpasswd';
+ $input_confpasswd = new html_passwordfield(array('name' => '_confpasswd', 'id' => $field_id, 'size' => 20));
+
+ $out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
+ $field_id,
+ rep_specialchars_output($this->gettext('confpasswd')),
+ $input_confpasswd->show($rcmail->config->get('confpasswd')));
+
+ $out .= "\n</table>";
+
+ $out .= '<br />';
+
+ $out .= $rcmail->output->button(array(
+ 'command' => 'plugin.password-save',
+ 'type' => 'input',
+ 'class' => 'button mainaction',
+ 'label' => 'save'
+ ));
+
+ $rcmail->output->add_gui_object('passform', 'password-form');
+
+ return $rcmail->output->form_tag(array(
+ 'id' => 'password-form',
+ 'name' => 'password-form',
+ 'method' => 'post',
+ 'action' => './?_task=settings&_action=plugin.password-save',
+ ), $out);
+ }
+
+
+ private function _save($passwd)
+ {
+ $cfg = rcmail::get_instance()->config;
+
+ if (!($sql = $cfg->get('password_query')))
+ $sql = "SELECT update_passwd('%p', '%u')";
+
+ $sql = str_replace('%u', $_SESSION['username'], $sql);
+ $sql = str_replace('%p', crypt($passwd), $sql);
+
+ if ($dsn = $cfg->get('db_passwd_dsn')) {
+ $db = new rcube_mdb2($dsn, '', FALSE);
+ $db->set_debug((bool)$cfg->get('sql_debug'));
+ $db->db_connect('w');
+ } else {
+ $db = rcmail::get_instance()->get_dbh();
+ }
+
+ if (!$db->db_connected)
+ return false;
+
+ $res = $db->query($sql);
+ $res = $db->fetch_array($res);
+
+ return $res;
+ }
+
+}
+
+?>
diff --git a/plugins/show_additional_headers/show_additional_headers.php b/plugins/show_additional_headers/show_additional_headers.php
new file mode 100644
index 000000000..c31c9df6b
--- /dev/null
+++ b/plugins/show_additional_headers/show_additional_headers.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Show additional message headers
+ *
+ * Proof-of-concept plugin which will fetch additional headers
+ * and display them in the message view.
+ *
+ * Enable the plugin in config/main.inc.php and add your desired headers:
+ * $rcmail_config['show_additional_headers'] = array('User-Agent');
+ *
+ * @version 1.0
+ * @author Thomas Bruederli
+ * @website http://roundcube.net
+ */
+class show_additional_headers extends rcube_plugin
+{
+ public $task = 'mail';
+
+ function init()
+ {
+ $rcmail = rcmail::get_instance();
+ if ($rcmail->action == 'show' || $rcmail->action == 'preview') {
+ $this->add_hook('imap_init', array($this, 'imap_init'));
+ $this->add_hook('message_headers_output', array($this, 'message_headers'));
+ }
+ }
+
+ function imap_init($p)
+ {
+ $rcmail = rcmail::get_instance();
+ if ($add_headers = $rcmail->config->get('show_additional_headers', array()))
+ $p['fetch_headers'] = trim($p['fetch_headers'].' ' . strtoupper(join(' ', $add_headers)));
+
+ return $p;
+ }
+
+ function message_headers($p)
+ {
+ $rcmail = rcmail::get_instance();
+ foreach ($rcmail->config->get('show_additional_headers', array()) as $header) {
+ $key = strtolower($header);
+ if ($value = $p['headers']->others[$key])
+ $p['output'][$key] = array('title' => $header, 'value' => $value);
+ }
+
+ return $p;
+ }
+}
diff --git a/plugins/subscriptions_option/localization/en_US.inc b/plugins/subscriptions_option/localization/en_US.inc
new file mode 100644
index 000000000..5a348e0ee
--- /dev/null
+++ b/plugins/subscriptions_option/localization/en_US.inc
@@ -0,0 +1,6 @@
+<?php
+
+$labels = array();
+$labels['useimapsubscriptions'] = 'Use IMAP Subscriptions';
+
+?>
diff --git a/plugins/subscriptions_option/subscriptions_option.php b/plugins/subscriptions_option/subscriptions_option.php
new file mode 100644
index 000000000..ba7236c67
--- /dev/null
+++ b/plugins/subscriptions_option/subscriptions_option.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Subscription Options
+ *
+ * A plugin which can enable or disable the use of imap subscriptions.
+ * It includes a toggle on the settings page under "Server Settings".
+ * The preference can also be locked
+ *
+ * Add it to the plugins list in config/main.inc.php to enable the user option
+ * The user option can be hidden and set globally by adding 'use_subscriptions'
+ * to the the 'dont_override' configure line:
+ * $rcmail_config['dont_override'] = array('use_subscriptions');
+ * and then set the global preference"
+ * $rcmail_config['use_subscriptions'] = true; // or false
+ *
+ * Roundcube caches folder lists. When a user changes this option or visits
+ * their folder list, this cache is refreshed. If the option is on the
+ * 'dont_override' list and the global option has changed, don't expect
+ * to see the change until the folder list cache is refreshed.
+ *
+ * @version 1.0
+ * @author Ziba Scott
+ */
+class subscriptions_option extends rcube_plugin
+{
+
+ function init()
+ {
+ $this->add_texts('localization/', false);
+ $dont_override = rcmail::get_instance()->config->get('dont_override', array());
+ if (!in_array('use_subscriptions', $dont_override)){
+ $this->add_hook('user_preferences', array($this, 'settings_table'));
+ $this->add_hook('save_preferences', array($this, 'save_prefs'));
+ }
+ $this->add_hook('list_mailboxes', array($this, 'list_mailboxes'));
+ $this->add_hook('manage_folders', array($this, 'manage_folders'));
+ }
+
+ function settings_table($args)
+ {
+ if ($args['section'] == 'server') {
+ $use_subscriptions = rcmail::get_instance()->config->get('use_subscriptions');
+ $field_id = 'rcmfd_use_subscriptions';
+ $use_subscriptions = new html_checkbox(array('name' => '_use_subscriptions', 'id' => $field_id, 'value' => 1));
+
+ $args['table']->add('title', html::label($field_id, Q($this->gettext('useimapsubscriptions'))));
+ $args['table']->add(null, $use_subscriptions->show($use_subscriptions?1:0));
+ }
+
+ return $args;
+ }
+
+ function save_prefs($args){
+ $rcmail = rcmail::get_instance();
+ $use_subscriptions = $rcmail->config->get('use_subscriptions');
+
+ $args['prefs']['use_subscriptions'] = isset($_POST['_use_subscriptions']) ? true : false;
+ // if the use_subscriptions preference changes, flush the folder cache
+ if (($use_subscriptions && !isset($_POST['_use_subscriptions'])) ||
+ (!$use_subscriptions && isset($_POST['_use_subscriptions']))) {
+ $rcmail->imap_init(true);
+ $rcmail->imap->clear_cache('mailboxes');
+ }
+
+ return $args;
+ }
+
+ function list_mailboxes($args){
+ $rcmail = rcmail::get_instance();
+ if (!$rcmail->config->get('use_subscriptions', true)) {
+ $args['folders'] = iil_C_ListMailboxes($rcmail->imap->conn, $rcmail->imap->_mod_mailbox($args['root']), $args['filter']);
+ }
+ return $args;
+ }
+
+ function manage_folders($args){
+ $rcmail = rcmail::get_instance();
+ if (!$rcmail->config->get('use_subscriptions', true)) {
+ $args['table']->remove_column('subscribed');
+ }
+ return $args;
+ }
+}
diff --git a/plugins/userinfo/localization/de_CH.inc b/plugins/userinfo/localization/de_CH.inc
new file mode 100644
index 000000000..5f236b66c
--- /dev/null
+++ b/plugins/userinfo/localization/de_CH.inc
@@ -0,0 +1,9 @@
+<?php
+
+$labels = array();
+$labels['userinfo'] = 'Benutzerinfo';
+$labels['created'] = 'Erstellt';
+$labels['lastlogin'] = 'Letztes Login';
+$labels['defaultidentity'] = 'Standard-Absender';
+
+?> \ No newline at end of file
diff --git a/plugins/userinfo/localization/en_US.inc b/plugins/userinfo/localization/en_US.inc
new file mode 100644
index 000000000..1a2fd9016
--- /dev/null
+++ b/plugins/userinfo/localization/en_US.inc
@@ -0,0 +1,9 @@
+<?php
+
+$labels = array();
+$labels['userinfo'] = 'User info';
+$labels['created'] = 'Created';
+$labels['lastlogin'] = 'Last Login';
+$labels['defaultidentity'] = 'Default Identity';
+
+?> \ No newline at end of file
diff --git a/plugins/userinfo/userinfo.js b/plugins/userinfo/userinfo.js
new file mode 100644
index 000000000..70a5085b3
--- /dev/null
+++ b/plugins/userinfo/userinfo.js
@@ -0,0 +1,16 @@
+/* Show user-info plugin script */
+
+if (window.rcmail) {
+ rcmail.addEventListener('init', function(evt) {
+ // <span id="settingstabdefault" class="tablink"><roundcube:button command="preferences" type="link" label="preferences" title="editpreferences" /></span>
+ var tab = $('<span>').attr('id', 'settingstabpluginuserinfo').addClass('tablink');
+
+ var button = $('<a>').attr('href', rcmail.env.comm_path+'&_action=plugin.userinfo').html(rcmail.gettext('userinfo', 'userinfo')).appendTo(tab);
+ button.bind('click', function(e){ return rcmail.command('plugin.userinfo', this) });
+
+ // add button and register command
+ rcmail.add_element(tab, 'tabs');
+ rcmail.register_command('plugin.userinfo', function(){ rcmail.goto_url('plugin.userinfo') }, true);
+ })
+}
+
diff --git a/plugins/userinfo/userinfo.php b/plugins/userinfo/userinfo.php
new file mode 100644
index 000000000..0f1b18cd9
--- /dev/null
+++ b/plugins/userinfo/userinfo.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * Sample plugin that adds a new tab to the settings section
+ * to display some information about the current user
+ */
+class userinfo extends rcube_plugin
+{
+ public $task = 'settings';
+
+ function init()
+ {
+ $this->add_texts('localization/', array('userinfo'));
+ $this->register_action('plugin.userinfo', array($this, 'infostep'));
+ $this->include_script('userinfo.js');
+ }
+
+ function infostep()
+ {
+ $this->register_handler('plugin.body', array($this, 'infohtml'));
+ rcmail::get_instance()->output->send('plugin');
+ }
+
+ function infohtml()
+ {
+ $rcmail = rcmail::get_instance();
+ $user = $rcmail->user;
+
+ $table = new html_table(array('cols' => 2, 'cellpadding' => 3));
+
+ $table->add('title', 'ID');
+ $table->add('', Q($user->ID));
+
+ $table->add('title', Q($this->gettext('username')));
+ $table->add('', Q($user->data['username']));
+
+ $table->add('title', Q($this->gettext('server')));
+ $table->add('', Q($user->data['mail_host']));
+
+ $table->add('title', Q($this->gettext('created')));
+ $table->add('', Q($user->data['created']));
+
+ $table->add('title', Q($this->gettext('lastlogin')));
+ $table->add('', Q($user->data['last_login']));
+
+ $identity = $user->get_identity();
+ $table->add('title', Q($this->gettext('defaultidentity')));
+ $table->add('', Q($identity['name'] . ' <' . $identity['email'] . '>'));
+
+ return html::tag('h4', null, Q('Infos for ' . $user->get_username())) . $table->show();
+ }
+
+} \ No newline at end of file
diff --git a/plugins/vcard_attachments/vcard_attachments.php b/plugins/vcard_attachments/vcard_attachments.php
new file mode 100644
index 000000000..da8ea1c15
--- /dev/null
+++ b/plugins/vcard_attachments/vcard_attachments.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * Detect VCard attachments and show a button to add them to address book
+ *
+ * @version 1.0
+ * @author Thomas Bruederli
+ */
+class vcard_attachments extends rcube_plugin
+{
+ public $task = 'mail';
+
+ private $message;
+ private $vcard_part;
+
+ function init()
+ {
+ $rcmail = rcmail::get_instance();
+ if ($rcmail->action == 'show' || $rcmail->action == 'preview') {
+ $this->add_hook('message_load', array($this, 'message_load'));
+ $this->add_hook('template_object_messagebody', array($this, 'html_output'));
+ }
+
+ $this->register_action('plugin.savevcard', array($this, 'save_vcard'));
+ }
+
+ /**
+ * Check message attachments for vcards
+ */
+ function message_load($p)
+ {
+ $this->message = $p['object'];
+
+ foreach ((array)$this->message->attachments as $attachment) {
+ if (in_array($attachment->mimetype, array('text/vcard', 'text/x-vcard')))
+ $this->vcard_part = $attachment->mime_id;
+ }
+ }
+
+ /**
+ * This callback function adds a box below the message content
+ * if there is a vcard attachment available
+ */
+ function html_output($p)
+ {
+ if ($this->vcard_part) {
+ $vcard = new rcube_vcard($this->message->get_part_content($this->vcard_part));
+
+ // successfully parsed vcard
+ if ($vcard->displayname) {
+ $display = $vcard->displayname;
+ if ($vcard->email[0])
+ $display .= ' <'.$vcard->email[0].'>';
+
+ // add box below messsage body
+ $p['content'] .= html::p(array('style' => "margin:1em; padding:0.5em; border:1px solid #999; width: auto;"),
+ html::a(array(
+ 'href' => "#",
+ 'onclick' => "return plugin_vcard_save_contact('".JQ($this->vcard_part)."')",
+ 'title' => "Save contact in local address book"), // TODO: localize this title
+ html::img(array('src' => '/images/buttons/add_contact_act.png', 'align' => "middle")))
+ . ' ' . html::span(null, Q($display)));
+
+ $this->include_script('vcardattach.js');
+ }
+ }
+
+ return $p;
+ }
+
+ /**
+ * Handler for request action
+ */
+ function save_vcard()
+ {
+ $uid = get_input_value('_uid', RCUBE_INPUT_POST);
+ $mbox = get_input_value('_mbox', RCUBE_INPUT_POST);
+ $mime_id = get_input_value('_part', RCUBE_INPUT_POST);
+
+ $rcmail = rcmail::get_instance();
+ $part = $uid && $mime_id ? $rcmail->imap->get_message_part($uid, $mime_id) : null;
+
+ $error_msg = 'Failed to saved vcard'; // TODO: localize this text
+
+ if ($part && ($vcard = new rcube_vcard($part)) && $vcard->displayname && $vcard->email) {
+ $contacts = $rcmail->get_address_book(null, true);
+
+ // check for existing contacts
+ $existing = $contacts->search('email', $vcard->email[0], true, false);
+ if ($done = $existing->count) {
+ $rcmail->output->command('display_message', $this->gettext('contactexists'), 'warning');
+ }
+ else {
+ // add contact
+ $success = $contacts->insert(array(
+ 'name' => $vcard->displayname,
+ 'firstname' => $vcard->firstname,
+ 'surname' => $vcard->surname,
+ 'email' => $vcard->email[0],
+ 'vcard' => $vcard->export(),
+ ));
+
+ if ($success)
+ $rcmail->output->command('display_message', $this->gettext('addedsuccessfully'), 'confirmation');
+ else
+ $rcmail->output->command('display_message', $error_msg, 'error');
+ }
+ }
+ else
+ $rcmail->output->command('display_message', $error_msg, 'error');
+
+ $rcmail->output->send();
+ }
+
+} \ No newline at end of file
diff --git a/plugins/vcard_attachments/vcardattach.js b/plugins/vcard_attachments/vcardattach.js
new file mode 100644
index 000000000..e03e5084d
--- /dev/null
+++ b/plugins/vcard_attachments/vcardattach.js
@@ -0,0 +1,10 @@
+
+function plugin_vcard_save_contact(mime_id)
+{
+ rcmail.set_busy(true, 'loading');
+ rcmail.http_post('plugin.savevcard', '_uid='+rcmail.env.uid+'&_mbox='+urlencode(rcmail.env.mailbox)+'&_part='+urlencode(mime_id), true);
+
+ return false;
+}
+
+
diff --git a/program/include/html.php b/program/include/html.php
index 01ad41544..78e696cb6 100644
--- a/program/include/html.php
+++ b/program/include/html.php
@@ -33,7 +33,7 @@ class html
protected $content;
public static $common_attrib = array('id','class','style','title','align');
- public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','tr','th','td','style');
+ public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','tr','th','td','style','script');
public static $lc_tags = true;
/**
@@ -599,6 +599,34 @@ class html_table extends html
$this->header[] = $cell;
}
+ /**
+ * Remove a column from a table
+ * Useful for plugins making alterations
+ *
+ * @param string $class
+ */
+ public function remove_column($class)
+ {
+ // Remove the header
+ foreach($this->header as $index=>$header){
+ if($header->attrib['class'] == $class){
+ unset($this->header[$index]);
+ break;
+ }
+ }
+
+ // Remove cells from rows
+ foreach($this->rows as $i=>$row){
+ foreach($row->cells as $j=>$cell){
+ if($cell->attrib['class'] == $class){
+ unset($this->rows[$i]->cells[$j]);
+ break;
+ }
+ }
+ }
+ }
+
+
/**
* Jump to next row
*
diff --git a/program/include/iniset.php b/program/include/iniset.php
index 234f9ebcb..46f3750ea 100755
--- a/program/include/iniset.php
+++ b/program/include/iniset.php
@@ -22,7 +22,7 @@
// application constants
-define('RCMAIL_VERSION', '0.2-trunk');
+define('RCMAIL_VERSION', '0.3-trunk');
define('RCMAIL_CHARSET', 'UTF-8');
define('JS_OBJECT_NAME', 'rcmail');
diff --git a/program/include/main.inc b/program/include/main.inc
index b22be1aca..b3d0dab2a 100644
--- a/program/include/main.inc
+++ b/program/include/main.inc
@@ -88,9 +88,9 @@ function get_sequence_name($sequence)
* @return string Localized text
* @see rcmail::gettext()
*/
-function rcube_label($p)
+function rcube_label($p, $domain=null)
{
- return rcmail::get_instance()->gettext($p);
+ return rcmail::get_instance()->gettext($p, $domain);
}
@@ -302,12 +302,11 @@ function rcube_charset_convert($str, $from, $to=NULL)
*/
function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
{
- global $OUTPUT;
static $html_encode_arr = false;
static $js_rep_table = false;
static $xml_rep_table = false;
- $charset = $OUTPUT->get_charset();
+ $charset = rcmail::get_instance()->config->get('charset', RCMAIL_CHARSET);
$is_iso_8859_1 = false;
if ($charset == 'ISO-8859-1') {
$is_iso_8859_1 = true;
@@ -692,11 +691,11 @@ function parse_attrib_string($str)
preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER);
// convert attributes to an associative array (name => value)
- if ($regs)
- foreach ($regs as $attr)
- {
- $attrib[strtolower($attr[1])] = $attr[3] . $attr[4];
- }
+ if ($regs) {
+ foreach ($regs as $attr) {
+ $attrib[strtolower($attr[1])] = html_entity_decode($attr[3] . $attr[4]);
+ }
+ }
return $attrib;
}
@@ -829,9 +828,13 @@ function format_email_recipient($email, $name='')
*/
function console()
{
+ $args = func_get_args();
+ if (class_exists('rcmail', false))
+ rcmail::get_instance()->plugins->exec_hook('console', $args);
+
$msg = array();
- foreach (func_get_args() as $arg)
- $msg[] = !is_string($arg) ? var_export($arg, true) : $arg;
+ foreach ($args as $arg)
+ $msg[] = !is_string($arg) ? var_export($arg, true) : $arg;
if (!($GLOBALS['CONFIG']['debug_level'] & 4))
write_log('console', join(";\n", $msg));
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 9aad25b27..56fc2f5db 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -37,6 +37,7 @@ class rcmail
public $db;
public $imap;
public $output;
+ public $plugins;
public $task = 'mail';
public $action = '';
public $comm_path = './';
@@ -88,7 +89,7 @@ class rcmail
$syslog_facility = $this->config->get('syslog_facility', LOG_USER);
openlog($syslog_id, LOG_ODELAY, $syslog_facility);
}
-
+
// set task and action properties
$this->set_task(strip_quotes(get_input_value('_task', RCUBE_INPUT_GPC)));
$this->action = asciiwords(get_input_value('_action', RCUBE_INPUT_GPC));
@@ -131,6 +132,9 @@ class rcmail
// create IMAP object
if ($this->task == 'mail')
$this->imap_init();
+
+ // create plugin API and load plugins
+ $this->plugins = rcube_plugin_api::get_instance();
}
@@ -255,10 +259,19 @@ class rcmail
$contacts = null;
$ldap_config = (array)$this->config->get('ldap_public');
$abook_type = strtolower($this->config->get('address_book_type'));
+
+ $plugin = $this->plugins->exec_hook('get_address_book', array('id' => $id, 'writeable' => $writeable));
- if ($id && $ldap_config[$id]) {
+ // plugin returned instance of a rcube_addressbook
+ if ($plugin['instance'] instanceof rcube_addressbook) {
+ $contacts = $plugin['instance'];
+ }
+ else if ($id && $ldap_config[$id]) {
$contacts = new rcube_ldap($ldap_config[$id]);
}
+ else if ($id === '0') {
+ $contacts = new rcube_contacts($this->db, $this->user->ID);
+ }
else if ($abook_type == 'ldap') {
// Use the first writable LDAP address book.
foreach ($ldap_config as $id => $prop) {
@@ -598,7 +611,7 @@ class rcmail
* @param mixed Named parameters array or label name
* @return string Localized text
*/
- public function gettext($attrib)
+ public function gettext($attrib, $domain=null)
{
// load localization files if not done yet
if (empty($this->texts))
@@ -613,9 +626,12 @@ class rcmail
$command_name = !empty($attrib['command']) ? $attrib['command'] : NULL;
$alias = $attrib['name'] ? $attrib['name'] : ($command_name && $command_label_map[$command_name] ? $command_label_map[$command_name] : '');
-
+
+ // check for text with domain
+ if ($domain && ($text_item = $this->texts[$domain.'.'.$alias]))
+ ;
// text does not exist
- if (!($text_item = $this->texts[$alias])) {
+ else if (!($text_item = $this->texts[$alias])) {
/*
raise_error(array(
'code' => 500,
@@ -677,7 +693,7 @@ class rcmail
*
* @param string Language ID
*/
- public function load_language($lang = null)
+ public function load_language($lang = null, $add = array())
{
$lang = $this->language_prop(($lang ? $lang : $_SESSION['language']));
@@ -707,6 +723,10 @@ class rcmail
$_SESSION['language'] = $lang;
}
+
+ // append additional texts (from plugin)
+ if (is_array($add) && !empty($add))
+ $this->texts += $add;
}
@@ -920,18 +940,20 @@ class rcmail
{
if (!is_array($p))
$p = array('_action' => @func_get_arg(0));
+
+ $task = $p['_task'] ? $p['_task'] : $p['task'];
+ if (!$task || !in_array($task, rcmail::$main_tasks))
+ $task = $this->task;
- if (!$p['task'] || !in_array($p['task'], rcmail::$main_tasks))
- $p['task'] = $this->task;
-
- $p['_task'] = $p['task'];
+ $p['_task'] = $task;
unset($p['task']);
$url = './';
$delm = '?';
- foreach (array_reverse($p) as $par => $val)
+ foreach (array_reverse($p) as $key => $val)
{
if (!empty($val)) {
+ $par = $key[0] == '_' ? $key : '_'.$key;
$url .= $delm.urlencode($par).'='.urlencode($val);
$delm = '&';
}
diff --git a/program/include/rcube_addressbook.php b/program/include/rcube_addressbook.php
new file mode 100644
index 000000000..9e970f213
--- /dev/null
+++ b/program/include/rcube_addressbook.php
@@ -0,0 +1,169 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_addressbook.php |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2006-2009, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Interface to the local address book database |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: $
+
+*/
+
+
+/**
+ * Abstract skeleton of an address book/repository
+ *
+ * @package Addressbook
+ */
+abstract class rcube_addressbook
+{
+ /** public properties */
+ var $primary_key;
+ var $readonly = true;
+ var $ready = false;
+ var $list_page = 1;
+ var $page_size = 10;
+
+ /**
+ * Save a search string for future listings
+ *
+ * @param mixed Search params to use in listing method, obtained by get_search_set()
+ */
+ abstract function set_search_set($filter);
+
+ /**
+ * Getter for saved search properties
+ *
+ * @return mixed Search properties used by this class
+ */
+ abstract function get_search_set();
+
+ /**
+ * Reset saved results and search parameters
+ */
+ abstract function reset();
+
+ /**
+ * List the current set of contact records
+ *
+ * @param array List of cols to show
+ * @param int Only return this number of records, use negative values for tail
+ * @return array Indexed list of contact records, each a hash array
+ */
+ abstract function list_records($cols=null, $subset=0);
+
+ /**
+ * Search records
+ *
+ * @param array List of fields to search in
+ * @param string Search value
+ * @param boolean True if results are requested, False if count only
+ * @return Indexed list of contact records and 'count' value
+ */
+ abstract function search($fields, $value, $strict=false, $select=true);
+
+ /**
+ * Count number of available contacts in database
+ *
+ * @return object rcube_result_set Result set with values for 'count' and 'first'
+ */
+ abstract function count();
+
+ /**
+ * Return the last result set
+ *
+ * @return object rcube_result_set Current result set or NULL if nothing selected yet
+ */
+ abstract function get_result();
+
+ /**
+ * Get a specific contact record
+ *
+ * @param mixed record identifier(s)
+ * @param boolean True to return record as associative array, otherwise a result set is returned
+ * @return mixed Result object with all record fields or False if not found
+ */
+ abstract function get_record($id, $assoc=false);
+
+ /**
+ * Close connection to source
+ * Called on script shutdown
+ */
+ function close() { }
+
+ /**
+ * Set internal list page
+ *
+ * @param number Page number to list
+ * @access public
+ */
+ function set_page($page)
+ {
+ $this->list_page = (int)$page;
+ }
+
+ /**
+ * Set internal page size
+ *
+ * @param number Number of messages to display on one page
+ * @access public
+ */
+ function set_pagesize($size)
+ {
+ $this->page_size = (int)$size;
+ }
+
+ /**
+ * Create a new contact record
+ *
+ * @param array Assoziative array with save data
+ * @param boolean True to check for duplicates first
+ * @return The created record ID on success, False on error
+ */
+ function insert($save_data, $check=false)
+ {
+ /* empty for read-only address books */
+ }
+
+ /**
+ * Update a specific contact record
+ *
+ * @param mixed Record identifier
+ * @param array Assoziative array with save data
+ * @return True on success, False on error
+ */
+ function update($id, $save_cols)
+ {
+ /* empty for read-only address books */
+ }
+
+ /**
+ * Mark one or more contact records as deleted
+ *
+ * @param array Record identifiers
+ */
+ function delete($ids)
+ {
+ /* empty for read-only address books */
+ }
+
+ /**
+ * Remove all records from the database
+ */
+ function delete_all()
+ {
+ /* empty for read-only address books */
+ }
+
+}
+ \ No newline at end of file
diff --git a/program/include/rcube_config.php b/program/include/rcube_config.php
index 7be2b0d2c..1312a73de 100644
--- a/program/include/rcube_config.php
+++ b/program/include/rcube_config.php
@@ -74,6 +74,7 @@ class rcube_config
// fix paths
$this->prop['log_dir'] = $this->prop['log_dir'] ? unslashify($this->prop['log_dir']) : INSTALL_PATH . 'logs';
$this->prop['temp_dir'] = $this->prop['temp_dir'] ? unslashify($this->prop['temp_dir']) : INSTALL_PATH . 'temp';
+ $this->prop['plugins_dir'] = $this->prop['plugins_dir'] ? unslashify($this->prop['plugins_dir']) : INSTALL_PATH . 'plugins';
// fix default imap folders encoding
foreach (array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox') as $folder)
diff --git a/program/include/rcube_contacts.php b/program/include/rcube_contacts.php
index 65d89ca2b..f440e5f8a 100644
--- a/program/include/rcube_contacts.php
+++ b/program/include/rcube_contacts.php
@@ -25,7 +25,7 @@
*
* @package Addressbook
*/
-class rcube_contacts
+class rcube_contacts extends rcube_addressbook
{
var $db = null;
var $db_name = '';
@@ -60,30 +60,6 @@ class rcube_contacts
/**
- * Set internal list page
- *
- * @param number Page number to list
- * @access public
- */
- function set_page($page)
- {
- $this->list_page = (int)$page;
- }
-
-
- /**
- * Set internal page size
- *
- * @param number Number of messages to display on one page
- * @access public
- */
- function set_pagesize($size)
- {
- $this->page_size = (int)$size;
- }
-
-
- /**
* Save a search string for future listings
*
* @param string SQL params to use in listing method
@@ -118,13 +94,6 @@ class rcube_contacts
/**
- * Close connection to source
- * Called on script shutdown
- */
- function close(){}
-
-
- /**
* List the current set of contact records
*
* @param array List of cols to show
@@ -233,7 +202,7 @@ class rcube_contacts
*
* @return Result array or NULL if nothing selected yet
*/
- function get_result($as_res=true)
+ function get_result()
{
return $this->result;
}
diff --git a/program/include/rcube_html_page.php b/program/include/rcube_html_page.php
index 78f6176bf..c83c6aeea 100644
--- a/program/include/rcube_html_page.php
+++ b/program/include/rcube_html_page.php
@@ -31,8 +31,8 @@ class rcube_html_page
protected $scripts = array();
protected $charset = 'UTF-8';
- protected $script_tag_file = "<script type=\"text/javascript\" src=\"%s%s\"></script>\n";
- protected $script_tag = "<script type=\"text/javascript\">\n<!--\n%s\n\n//-->\n</script>\n";
+ protected $script_tag_file = "<script type=\"text/javascript\" src=\"%s\"></script>\n";
+ protected $script_tag = "<script type=\"text/javascript\">\n/* <![CDATA[ */\n%s\n/* ]]> */\n</script>";
protected $default_template = "<html>\n<head><title></title></head>\n<body></body>\n</html>";
protected $title = '';
@@ -53,6 +53,9 @@ class rcube_html_page
public function include_script($file, $position='head')
{
static $sa_files = array();
+
+ if (!ereg('^https?://', $file) && $file[0] != '/')
+ $file = $this->scripts_path . $file;
if (in_array($file, $sa_files)) {
return;
@@ -165,7 +168,7 @@ class rcube_html_page
// definition of the code to be placed in the document header and footer
if (is_array($this->script_files['head'])) {
foreach ($this->script_files['head'] as $file) {
- $__page_header .= sprintf($this->script_tag_file, $this->scripts_path, $file);
+ $__page_header .= sprintf($this->script_tag_file, $file);
}
}
@@ -180,7 +183,7 @@ class rcube_html_page
if (is_array($this->script_files['foot'])) {
foreach ($this->script_files['foot'] as $file) {
- $__page_footer .= sprintf($this->script_tag_file, $this->scripts_path, $file);
+ $__page_footer .= sprintf($this->script_tag_file, $file);
}
}
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index da7c5bf88..e2b6c0d9a 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -55,6 +55,7 @@ class rcube_imap
var $default_charset = 'ISO-8859-1';
var $default_folders = array('INBOX');
var $default_folders_lc = array('inbox');
+ var $fetch_add_headers = '';
var $cache = array();
var $cache_keys = array();
var $cache_changes = array();
@@ -428,8 +429,16 @@ class rcube_imap
if (is_array($a_mboxes))
return $a_mboxes;
- // retrieve list of folders from IMAP server
- $a_folders = iil_C_ListSubscribed($this->conn, $this->_mod_mailbox($root), $filter);
+ // Give plugins a chance to provide a list of mailboxes
+ $data = rcmail::get_instance()->plugins->exec_hook('list_mailboxes',array('root'=>$root,'filter'=>$filter));
+ if (isset($data['folders'])) {
+ $a_folders = $data['folders'];
+ }
+ else{
+ // retrieve list of folders from IMAP server
+ $a_folders = iil_C_ListSubscribed($this->conn, $this->_mod_mailbox($root), $filter);
+ }
+
if (!is_array($a_folders) || !sizeof($a_folders))
$a_folders = array();
@@ -775,7 +784,7 @@ class rcube_imap
$cache_index = $this->get_message_cache_index($cache_key);
// fetch reuested headers from server
- $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, $msgs);
+ $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, $msgs, false, $this->fetch_add_headers);
$deleted_count = 0;
if (!empty($a_header_index))
@@ -829,14 +838,14 @@ class rcube_imap
if ($this->sort_field && $this->search_sort_field != $this->sort_field)
$this->search('', $this->search_string, $this->search_charset, $this->sort_field);
- if ($this->sort_order == 'DESC')
+ if ($this->sort_order == 'DESC')
$this->cache[$key] = array_reverse($this->search_set);
- else
- $this->cache[$key] = $this->search_set;
+ else
+ $this->cache[$key] = $this->search_set;
}
else
{
- $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, join(',', $this->search_set), $this->sort_field);
+ $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, join(',', $this->search_set), $this->sort_field, false);
if ($this->sort_order=="ASC")
asort($a_index);
@@ -923,7 +932,7 @@ class rcube_imap
// fetch complete headers and add to cache
- $headers = iil_C_FetchHeader($this->conn, $mailbox, $id);
+ $headers = iil_C_FetchHeader($this->conn, $mailbox, $id, false, $this->fetch_add_headers);
$this->add_message_cache($cache_key, $headers->id, $headers);
}
@@ -1062,7 +1071,7 @@ class rcube_imap
if ($uid && ($headers = &$this->get_cached_message($mailbox.'.msg', $uid)))
return $headers;
- $headers = iil_C_FetchHeader($this->conn, $mailbox, $id, $is_uid, $bodystr);
+ $headers = iil_C_FetchHeader($this->conn, $mailbox, $id, $is_uid, $bodystr, $this->fetch_add_headers);
// write headers cache
if ($headers)
@@ -2227,7 +2236,7 @@ class rcube_imap
if ($cache_count==$msg_count)
{
// get highest index
- $header = iil_C_FetchHeader($this->conn, $mailbox, "$msg_count");
+ $header = iil_C_FetchHeader($this->conn, $mailbox, "$msg_count", false, $this->fetch_add_headers);
$cache_uid = array_pop($cache_index);
// uids of highest message matches -> cache seems OK
@@ -2277,7 +2286,7 @@ class rcube_imap
// featch headers if unserialize failed
if (empty($this->cache[$cache_key][$uid]))
- $this->cache[$cache_key][$uid] = iil_C_FetchHeader($this->conn, preg_replace('/.msg$/', '', $key), $uid, true);
+ $this->cache[$cache_key][$uid] = iil_C_FetchHeader($this->conn, preg_replace('/.msg$/', '', $key), $uid, true, $this->fetch_add_headers);
}
}
diff --git a/program/include/rcube_json_output.php b/program/include/rcube_json_output.php
index 0bd3a2bad..a14f4ae14 100644
--- a/program/include/rcube_json_output.php
+++ b/program/include/rcube_json_output.php
@@ -196,26 +196,33 @@ class rcube_json_output
* @return void
* @deprecated
*/
- public function remote_response($add='', $flush=false)
+ public function remote_response($add='')
{
static $s_header_sent = false;
if (!$s_header_sent) {
$s_header_sent = true;
send_nocacheing_headers();
- header('Content-Type: application/x-javascript; charset=' . $this->get_charset());
+ header('Content-Type: text/plain; charset=' . $this->get_charset());
print '/** ajax response ['.date('d/M/Y h:i:s O')."] **/\n";
}
// unset default env vars
unset($this->env['task'], $this->env['action'], $this->env['comm_path']);
+ $rcmail = rcmail::get_instance();
+ $response = array('action' => $rcmail->action, 'unlock' => (bool)$_REQUEST['_unlock']);
+
+ if (!empty($this->env))
+ $response['env'] = $this->env;
+
+ if (!empty($this->texts))
+ $response['texts'] = $this->texts;
+
// send response code
- echo $this->get_js_commands() . $add;
+ $response['exec'] = $this->get_js_commands() . $add;
- // flush the output buffer
- if ($flush)
- flush();
+ echo json_serialize($response);
}
@@ -227,14 +234,7 @@ class rcube_json_output
private function get_js_commands()
{
$out = '';
-
- if (sizeof($this->env))
- $out .= 'this.set_env('.json_serialize($this->env).");\n";
- foreach($this->texts as $name => $text) {
- $out .= sprintf("this.add_label('%s', '%s');\n", $name, JQ($text));
- }
-
foreach ($this->commands as $i => $args) {
$method = array_shift($args);
foreach ($args as $i => $arg) {
diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php
index 016fe94d5..544c7f744 100644
--- a/program/include/rcube_ldap.php
+++ b/program/include/rcube_ldap.php
@@ -24,7 +24,7 @@
*
* @package Addressbook
*/
-class rcube_ldap
+class rcube_ldap extends rcube_addressbook
{
var $conn;
var $prop = array();
diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php
index ec3be4b00..5c03b2147 100644
--- a/program/include/rcube_message.php
+++ b/program/include/rcube_message.php
@@ -84,6 +84,9 @@ class rcube_message
else {
$this->body = $this->imap->get_body($uid);
}
+
+ // notify plugins and let them analyze this structured message object
+ $this->app->plugins->exec_hook('message_load', array('object' => $this));
}
diff --git a/program/include/rcube_plugin.php b/program/include/rcube_plugin.php
new file mode 100644
index 000000000..62f65a9e4
--- /dev/null
+++ b/program/include/rcube_plugin.php
@@ -0,0 +1,196 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_plugin.php |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2008-2009, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Abstract plugins interface/class |
+ | All plugins need to extend this class |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: $
+
+*/
+
+/**
+ * Plugin interface class
+ *
+ * @package Core
+ */
+abstract class rcube_plugin
+{
+ public $ID;
+ public $api;
+ public $task;
+ protected $home;
+ protected $urlbase;
+
+ /**
+ * Default constructor.
+ */
+ public function __construct($api)
+ {
+ $this->ID = get_class($this);
+ $this->api = $api;
+ $this->home = $api->dir . DIRECTORY_SEPARATOR . $this->ID;
+ $this->urlbase = $api->url . $this->ID . '/';
+ }
+
+ /**
+ * Initialization method, needs to be implemented by the plugin itself
+ */
+ abstract function init();
+
+ /**
+ * Register a callback function for a specific (server-side) hook
+ *
+ * @param string Hook name
+ * @param mixed Callback function as string or array with object reference and method name
+ */
+ public function add_hook($hook, $callback)
+ {
+ $this->api->register_hook($hook, $callback);
+ }
+
+ /**
+ * Load localized texts from the plugins dir
+ *
+ * @param string Directory to search in
+ * @param mixed Make texts also available on the client (array with list or true for all)
+ */
+ public function add_texts($dir, $add2client = false)
+ {
+ $domain = $this->ID;
+
+ $lang = $_SESSION['language'];
+ $locdir = slashify(realpath(slashify($this->home) . $dir));
+ $texts = array();
+
+ foreach (array('en_US', $lang) as $lng) {
+ @include($locdir . $lng . '.inc');
+ $texts = (array)$labels + (array)$messages + (array)$texts;
+ }
+
+ // prepend domain to text keys and add to the application texts repository
+ if (!empty($texts)) {
+ $add = array();
+ foreach ($texts as $key => $value)
+ $add[$domain.'.'.$key] = $value;
+
+ $rcmail = rcmail::get_instance();
+ $rcmail->load_language($lang, $add);
+
+ // add labels to client
+ if ($add2client) {
+ $js_labels = is_array($add2client) ? array_map(array($this, 'label_map_callback'), $add2client) : array_keys($add);
+ $rcmail->output->add_label($js_labels);
+ }
+ }
+ }
+
+ /**
+ * Wrapper for rcmail::gettext() adding the plugin ID as domain
+ *
+ * @return string Localized text
+ * @see rcmail::gettext()
+ */
+ function gettext($p)
+ {
+ return rcmail::get_instance()->gettext($p, $this->ID);
+ }
+
+ /**
+ * Register a handler for a specific client-request action
+ *
+ * The callback will be executed upon a request like /?_task=mail&_action=plugin.myaction
+ *
+ * @param string Action name (should be unique)
+ * @param mixed Callback function as string or array with object reference and method name
+ */
+ public function register_action($action, $callback)
+ {
+ $this->api->register_action($action, $this->ID, $callback);
+ }
+
+ /**
+ * Register a handler function for a template object
+ *
+ * When parsing a template for display, tags like <roundcube:object name="plugin.myobject" />
+ * will be replaced by the return value if the registered callback function.
+ *
+ * @param string Object name (should be unique and start with 'plugin.')
+ * @param mixed Callback function as string or array with object reference and method name
+ */
+ public function register_handler($name, $callback)
+ {
+ $this->api->register_handler($name, $this->ID, $callback);
+ }
+
+ /**
+ * Make this javascipt file available on the client
+ *
+ * @param string File path; absolute or relative to the plugin directory
+ */
+ public function include_script($fn)
+ {
+ $this->api->include_script($this->ressource_url($fn));
+ }
+
+ /**
+ * Make this stylesheet available on the client
+ *
+ * @param string File path; absolute or relative to the plugin directory
+ */
+ public function include_stylesheet($fn)
+ {
+ $this->api->include_stylesheet($this->ressource_url($fn));
+ }
+
+ /**
+ * Append a button to a certain container
+ *
+ * @param array Hash array with named parameters (as used in skin templates)
+ * @param string Container name where the buttons should be added to
+ * @see rcube_remplate::button()
+ */
+ public function add_button($p, $container)
+ {
+ if ($this->api->output->type == 'html') {
+ // fix relative paths
+ foreach (array('imagepas', 'imageact', 'imagesel') as $key)
+ if ($p[$key])
+ $p[$key] = $this->api->url . $this->ressource_url($p[$key]);
+
+ $this->api->add_content($this->api->output->button($p), $container);
+ }
+ }
+
+ /**
+ * Make the given file name link into the plugin directory
+ */
+ private function ressource_url($fn)
+ {
+ if ($fn[0] != '/' && !eregi('^https?://', $fn))
+ return $this->ID . '/' . $fn;
+ else
+ return $fn;
+ }
+
+ /**
+ * Callback function for array_map
+ */
+ private function label_map_callback($key)
+ {
+ return $this->ID.'.'.$key;
+ }
+
+
+}
+
diff --git a/program/include/rcube_plugin_api.php b/program/include/rcube_plugin_api.php
new file mode 100644
index 000000000..4780f2e7e
--- /dev/null
+++ b/program/include/rcube_plugin_api.php
@@ -0,0 +1,312 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_plugin_api.php |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2008-2009, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Plugins repository |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id: $
+
+*/
+
+/**
+ * The plugin loader and global API
+ *
+ * @package Core
+ */
+class rcube_plugin_api
+{
+ static private $instance;
+
+ public $dir;
+ public $url = 'plugins/';
+ public $output;
+
+ public $handlers = array();
+ private $plugins = array();
+ private $actions = array();
+ private $actionmap = array();
+ private $objectsmap = array();
+ private $template_contents = array();
+
+ private $required_plugins = array('filesystem_attachments');
+
+ /**
+ * This implements the 'singleton' design pattern
+ *
+ * @return object rcube_plugin_api The one and only instance if this class
+ */
+ static function get_instance()
+ {
+ if (!self::$instance) {
+ self::$instance = new rcube_plugin_api();
+ }
+
+ return self::$instance;
+ }
+
+
+ /**
+ * Private constructor
+ */
+ private function __construct()
+ {
+ $rcmail = rcmail::get_instance();
+ $this->dir = realpath($rcmail->config->get('plugins_dir'));
+ }
+
+
+ /**
+ * Load and init all enabled plugins
+ *
+ * This has to be done after rcmail::load_gui() or rcmail::init_json()
+ * was called because plugins need to have access to rcmail->output
+ */
+ public function init()
+ {
+ $rcmail = rcmail::get_instance();
+ $this->output = $rcmail->output;
+
+ $plugins_dir = dir($this->dir);
+ $plugins_enabled = (array)$rcmail->config->get('plugins', array());
+
+ foreach ($plugins_enabled as $plugin_name) {
+ $fn = $plugins_dir->path . DIRECTORY_SEPARATOR . $plugin_name . DIRECTORY_SEPARATOR . $plugin_name . '.php';
+
+ if (file_exists($fn)) {
+ include($fn);
+
+ // instantiate class if exists
+ if (class_exists($plugin_name, false)) {
+ $plugin = new $plugin_name($this);
+ // check inheritance and task specification
+ if (is_subclass_of($plugin, 'rcube_plugin') && (!$plugin->task || $plugin->task == $rcmail->task)) {
+ $plugin->init();
+ $this->plugins[] = $plugin;
+ }
+ }
+ else {
+ raise_error(array('code' => 520, 'type' => 'php', 'message' => "No plugin class $plugin_name found in $fn"), true, false);
+ }
+ }
+ else {
+ raise_error(array('code' => 520, 'type' => 'php', 'message' => "Failed to load plugin file $fn"), true, false);
+ }
+ }
+
+ // check existance of all required core plugins
+ foreach ($this->required_plugins as $plugin_name) {
+ $loaded = false;
+ foreach ($this->plugins as $plugin) {
+ if ($plugin instanceof $plugin_name) {
+ $loaded = true;
+ break;
+ }
+ }
+
+ // load required core plugin if no derivate was found
+ if (!$loaded) {
+ $fn = $plugins_dir->path . DIRECTORY_SEPARATOR . $plugin_name . DIRECTORY_SEPARATOR . $plugin_name . '.php';
+ if (file_exists($fn)) {
+ include($fn);
+
+ if (class_exists($plugin_name, false)) {
+ $plugin = new $plugin_name($this);
+ // check inheritance
+ if (is_subclass_of($plugin, 'rcube_plugin')) {
+ $plugin->init();
+ $this->plugins[] = $plugin;
+ $loaded = true;
+ }
+ }
+ }
+ }
+
+ // trigger fatal error if still not loaded
+ if (!$loaded) {
+ raise_error(array('code' => 520, 'type' => 'php', 'message' => "Requried plugin $plugin_name was not loaded"), true, true);
+ }
+ }
+
+ // register an internal hook
+ $this->register_hook('template_container', array($this, 'template_container_hook'));
+
+ // maybe also register a shudown function which triggers shutdown functions of all plugin objects
+
+
+ // call imap_init right now
+ // (should actually be done in rcmail::imap_init() but plugins are not initialized then)
+ if ($rcmail->imap) {
+ $hook = $this->exec_hook('imap_init', array('fetch_headers' => $rcmail->imap->fetch_add_headers));
+ if ($hook['fetch_headers'])
+ $rcmail->imap->fetch_add_headers = $hook['fetch_headers'];
+ }
+ }
+
+
+ /**
+ * Allows a plugin object to register a callback for a certain hook
+ *
+ * @param string Hook name
+ * @param mixed String with global function name or array($obj, 'methodname')
+ */
+ public function register_hook($hook, $callback)
+ {
+ if (is_callable($callback))
+ $this->handlers[$hook][] = $callback;
+ else
+ raise_error(array('code' => 521, 'type' => 'php', 'message' => "Invalid callback function for $hook"), true, false);
+ }
+
+
+ /**
+ * Triggers a plugin hook.
+ * This is called from the application and executes all registered handlers
+ *
+ * @param string Hook name
+ * @param array Named arguments (key->value pairs)
+ * @return array The (probably) altered hook arguments
+ */
+ public function exec_hook($hook, $args = array())
+ {
+ $args += array('abort' => false);
+
+ foreach ((array)$this->handlers[$hook] as $callback) {
+ $ret = call_user_func($callback, $args);
+ if ($ret && is_array($ret))
+ $args = $ret + $args;
+
+ if ($args['abort'])
+ break;
+ }
+
+ return $args;
+ }
+
+
+ /**
+ * Let a plugin register a handler for a specific request
+ *
+ * @param string Action name (_task=mail&_action=plugin.foo)
+ * @param string Plugin name that registers this action
+ * @param mixed Callback: string with global function name or array($obj, 'methodname')
+ */
+ public function register_action($action, $owner, $callback)
+ {
+ // check action name
+ if (strpos($action, 'plugin.') !== 0)
+ $action = 'plugin.'.$action;
+
+ // can register action only if it's not taken or registered by myself
+ if (!isset($this->actionmap[$action]) || $this->actionmap[$action] == $owner) {
+ $this->actions[$action] = $callback;
+ $this->actionmap[$action] = $owner;
+ }
+ else {
+ raise_error(array('code' => 523, 'type' => 'php', 'message' => "Cannot register action $action; already taken by another plugin"), true, false);
+ }
+ }
+
+
+ /**
+ * This method handles requests like _task=mail&_action=plugin.foo
+ * It executes the callback function that was registered with the given action.
+ *
+ * @param string Action name
+ */
+ public function exec_action($action)
+ {
+ if (isset($this->actions[$action])) {
+ call_user_func($this->actions[$action]);
+ }
+ else {
+ raise_error(array('code' => 524, 'type' => 'php', 'message' => "No handler found for action $action"), true, true);
+ }
+ }
+
+
+ /**
+ * Register a handler function for template objects
+ *
+ * @param string Object name
+ * @param string Plugin name that registers this action
+ * @param mixed Callback: string with global function name or array($obj, 'methodname')
+ */
+ public function register_handler($name, $owner, $callback)
+ {
+ // check name
+ if (strpos($name, 'plugin.') !== 0)
+ $name = 'plugin.'.$name;
+
+ // can register handler only if it's not taken or registered by myself
+ if (!isset($this->objectsmap[$name]) || $this->objectsmap[$name] == $owner) {
+ $this->output->add_handler($name, $callback);
+ $this->objectsmap[$name] = $owner;
+ }
+ else {
+ raise_error(array('code' => 525, 'type' => 'php', 'message' => "Cannot register template handler $name; already taken by another plugin"), true, false);
+ }
+ }
+
+ /**
+ * Include a plugin script file in the current HTML page
+ */
+ public function include_script($fn)
+ {
+ if ($this->output->type == 'html') {
+ $src = $this->ressource_url($fn);
+ $this->output->add_header(html::tag('script', array('type' => "text/javascript", 'src' => $src)));
+ }
+ }
+
+ /**
+ * Include a plugin stylesheet in the current HTML page
+ */
+ public function include_stylesheet($fn)
+ {
+ if ($this->output->type == 'html') {
+ $src = $this->ressource_url($fn);
+ $this->output->add_header(html::tag('link', array('rel' => "stylesheet", 'type' => "text/css", 'href' => $src)));
+ }
+ }
+
+ /**
+ * Save the given HTML content to be added to a template container
+ */
+ public function add_content($html, $container)
+ {
+ $this->template_contents[$container] .= $html . "\n";
+ }
+
+ /**
+ * Callback for template_container hooks
+ */
+ private function template_container_hook($attrib)
+ {
+ $container = $attrib['name'];
+ return array('content' => $this->template_contents[$container]);
+ }
+
+ /**
+ * Make the given file name link into the plugins directory
+ */
+ private function ressource_url($fn)
+ {
+ if ($fn[0] != '/' && !eregi('^https?://', $fn))
+ return $this->url . $fn;
+ else
+ return $fn;
+ }
+
+}
+
diff --git a/program/include/rcube_template.php b/program/include/rcube_template.php
index 1e732ca11..557509a14 100755
--- a/program/include/rcube_template.php
+++ b/program/include/rcube_template.php
@@ -66,11 +66,12 @@ class rcube_template extends rcube_html_page
$javascript = 'var '.JS_OBJECT_NAME.' = new rcube_webmail();';
// don't wait for page onload. Call init at the bottom of the page (delayed)
- $javascript_foot = "if (window.call_init)\n call_init('".JS_OBJECT_NAME."');";
+ $javascript_foot = '$(document).ready(function(){ '.JS_OBJECT_NAME.'.init(); });';
$this->add_script($javascript, 'head_top');
$this->add_script($javascript_foot, 'foot');
$this->scripts_path = 'program/js/';
+ $this->include_script('http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js');
$this->include_script('common.js');
$this->include_script('app.js');
@@ -208,8 +209,11 @@ class rcube_template extends rcube_html_page
*/
public function add_label()
{
- $arg_list = func_get_args();
- foreach ($arg_list as $i => $name) {
+ $args = func_get_args();
+ if (count($args) == 1 && is_array($args[0]))
+ $args = $args[0];
+
+ foreach ($args as $name) {
$this->command('add_label', $name, rcube_label($name));
}
}
@@ -375,9 +379,9 @@ class rcube_template extends rcube_html_page
$parent = $this->framed || preg_match('/^parent\./', $method);
$out .= sprintf(
"%s.%s(%s);\n",
- ($parent ? 'parent.' : '') . JS_OBJECT_NAME,
- preg_replace('/^parent\./', '', $method),
- implode(',', $args)
+ ($parent ? 'if(window.parent && parent.'.JS_OBJECT_NAME.') parent.' : '') . JS_OBJECT_NAME,
+ preg_replace('/^parent\./', '', $method),
+ implode(',', $args)
);
}
@@ -511,37 +515,21 @@ class rcube_template extends rcube_html_page
*/
private function parse_xml($input)
{
- return preg_replace_callback('/<roundcube:([-_a-z]+)\s+([^>]+)>/Ui', array($this, 'xml_command_callback'), $input);
- }
-
-
- /**
- * This is a callback function for preg_replace_callback (see #1485286)
- * It's only purpose is to reconfigure parameters for xml_command, so that the signature isn't disturbed
- */
- private function xml_command_callback($matches)
- {
- $str_attrib = isset($matches[2]) ? $matches[2] : '';
- $add_attrib = isset($matches[3]) ? $matches[3] : array();
-
- $command = $matches[1];
- //matches[0] is the entire matched portion of the string
-
- return $this->xml_command($command, $str_attrib, $add_attrib);
+ return preg_replace_callback('/<roundcube:([-_a-z]+)\s+([^>]+)>/Ui', array($this, 'xml_command'), $input);
}
/**
- * Convert a xml command tag into real content
+ * Callback function for parsing an xml command tag
+ * and turn it into real html content
*
- * @param string Tag command: object,button,label, etc.
- * @param string Attribute string
+ * @param array Matches array of preg_replace_callback
* @return string Tag/Object content
*/
- private function xml_command($command, $str_attrib, $add_attrib = array())
+ private function xml_command($matches)
{
- $command = strtolower($command);
- $attrib = parse_attrib_string($str_attrib) + $add_attrib;
+ $command = strtolower($matches[1]);
+ $attrib = parse_attrib_string($matches[2]);
// empty output if required condition is not met
if (!empty($attrib['condition']) && !$this->check_condition($attrib['condition'])) {
@@ -572,67 +560,70 @@ class rcube_template extends rcube_html_page
$incl = $this->include_php($path);
}
else {
- $incl = file_get_contents($path);
- }
+ $incl = file_get_contents($path);
+ }
return $this->parse_xml($incl);
}
break;
case 'plugin.include':
- //rcube::tfk_debug(var_export($this->config['skin_path'], true));
- $path = realpath($this->config['skin_path'].$attrib['file']);
- if (!$path) {
- //rcube::tfk_debug("Does not exist:");
- //rcube::tfk_debug($this->config['skin_path']);
- //rcube::tfk_debug($attrib['file']);
- //rcube::tfk_debug($path);
- }
- $incl = file_get_contents($path);
- if ($incl) {
- return $this->parse_xml($incl);
+ $hook = $this->app->plugins->exec_hook("template_plugin_include", $attrib);
+ return $hook['content'];
+ break;
+
+ // define a container block
+ case 'container':
+ if ($attrib['name'] && $attrib['id']) {
+ $this->command('gui_container', $attrib['name'], $attrib['id']);
+ // let plugins insert some content here
+ $hook = $this->app->plugins->exec_hook("template_container", $attrib);
+ return $hook['content'];
}
break;
// return code for a specific application object
case 'object':
$object = strtolower($attrib['name']);
+ $content = '';
// we are calling a class/method
if (($handler = $this->object_handlers[$object]) && is_array($handler)) {
if ((is_object($handler[0]) && method_exists($handler[0], $handler[1])) ||
(is_string($handler[0]) && class_exists($handler[0])))
- return call_user_func($handler, $attrib);
+ $content = call_user_func($handler, $attrib);
}
+ // execute object handler function
else if (function_exists($handler)) {
- // execute object handler function
- return call_user_func($handler, $attrib);
+ $content = call_user_func($handler, $attrib);
}
-
- if ($object=='productname') {
+ else if ($object == 'productname') {
$name = !empty($this->config['product_name']) ? $this->config['product_name'] : 'RoundCube Webmail';
- return Q($name);
+ $content = Q($name);
}
- if ($object=='version') {
+ else if ($object == 'version') {
$ver = (string)RCMAIL_VERSION;
if (is_file(INSTALL_PATH . '.svn/entries')) {
if (preg_match('/Revision:\s(\d+)/', @shell_exec('svn info'), $regs))
$ver .= ' [SVN r'.$regs[1].']';
}
- return $ver;
+ $content = Q($ver);
}
- if ($object=='steptitle') {
- return Q($this->get_pagetitle());
+ else if ($object == 'steptitle') {
+ $content = Q($this->get_pagetitle());
}
- if ($object=='pagetitle') {
+ else if ($object == 'pagetitle') {
$title = !empty($this->config['product_name']) ? $this->config['product_name'].' :: ' : '';
$title .= $this->get_pagetitle();
- return Q($title);
+ $content = Q($title);
}
- break;
+
+ // exec plugin hooks for this template object
+ $hook = $this->app->plugins->exec_hook("template_object_$object", $attrib + array('content' => $content));
+ return $hook['content'];
// return code for a specified eval expression
case 'exp':
- $value = $this->parse_expression($attrib['expression']);
+ $value = $this->parse_expression($attrib['expression']);
return eval("return Q($value);");
// return variable
@@ -702,7 +693,7 @@ class rcube_template extends rcube_html_page
static $s_button_count = 100;
// these commands can be called directly via url
- $a_static_commands = array('compose', 'list');
+ $a_static_commands = array('compose', 'list', 'preferences', 'folders', 'identities');
if (!($attrib['command'] || $attrib['name'])) {
return '';
@@ -941,11 +932,17 @@ class rcube_template extends rcube_html_page
$default_host = $this->config['default_host'];
$_SESSION['temp'] = true;
+
+ // save original url
+ $url = get_input_value('_url', RCUBE_INPUT_POST);
+ if (empty($url) && !preg_match('/_action=logout/', $_SERVER['QUERY_STRING']))
+ $url = $_SERVER['QUERY_STRING'];
$input_user = new html_inputfield(array('name' => '_user', 'id' => 'rcmloginuser', 'size' => 30) + $attrib);
$input_pass = new html_passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd', 'size' => 30) + $attrib);
$input_action = new html_hiddenfield(array('name' => '_action', 'value' => 'login'));
$input_tzone = new html_hiddenfield(array('name' => '_timezone', 'id' => 'rcmlogintz', 'value' => '_default_'));
+ $input_url = new html_hiddenfield(array('name' => '_url', 'id' => 'rcmloginurl', 'value' => $url));
$input_host = null;
if (is_array($default_host)) {
@@ -985,6 +982,7 @@ class rcube_template extends rcube_html_page
$out = $input_action->show();
$out .= $input_tzone->show();
+ $out .= $input_url->show();
$out .= $table->show();
// surround html output with a form tag
diff --git a/program/include/rcube_user.php b/program/include/rcube_user.php
index 17debeb7a..b68c56cfa 100644
--- a/program/include/rcube_user.php
+++ b/program/include/rcube_user.php
@@ -346,16 +346,22 @@ class rcube_user
*/
static function create($user, $host)
{
+ $user_name = '';
$user_email = '';
$rcmail = rcmail::get_instance();
+
+ $data = $rcmail->plugins->exec_hook('create_user', array('user'=>$user, 'user_name'=>$user_name, 'user_email'=>$user_email));
+ $user_name = $data['user_name'];
+ $user_email = $data['user_email'];
+
$dbh = $rcmail->get_dbh();
// try to resolve user in virtuser table and file
- if (!strpos($user, '@')) {
+ if ($user_email != '' && !strpos($user, '@')) {
if ($email_list = self::user2email($user, false))
$user_email = $email_list[0];
}
-
+
$dbh->query(
"INSERT INTO ".get_table_name('users')."
(created, last_login, username, mail_host, alias, language)
@@ -372,7 +378,9 @@ class rcube_user
if ($user_email=='')
$user_email = strpos($user, '@') ? $user : sprintf('%s@%s', $user, $mail_domain);
- $user_name = $user != $user_email ? $user : '';
+ if ($user_name == '') {
+ $user_name = $user != $user_email ? $user : '';
+ }
if (empty($email_list))
$email_list[] = strip_newlines($user_email);
@@ -385,10 +393,10 @@ class rcube_user
(user_id, del, standard, name, email)
VALUES (?, 0, ?, ?, ?)",
$user_id,
- $standard,
+ $standard,
strip_newlines($user_name),
preg_replace('/^@/', $user . '@', $email));
- $standard = 0;
+ $standard = 0;
}
}
else
@@ -446,9 +454,9 @@ class rcube_user
while ($sql_arr = $dbh->fetch_array($sql_result))
if (strpos($sql_arr[0], '@')) {
$result[] = $sql_arr[0];
- if ($first)
- return $result[0];
- }
+ if ($first)
+ return $result[0];
+ }
}
// File lookup
$r = self::findinvirtual('[[:space:]]' . quotemeta($user) . '[[:space:]]*$');
@@ -460,7 +468,7 @@ class rcube_user
{
$result[] = trim(str_replace('\\@', '@', $arr[0]));
- if ($first)
+ if ($first)
return $result[0];
}
}
diff --git a/program/include/rcube_vcard.php b/program/include/rcube_vcard.php
index ce5087a0f..444900cde 100644
--- a/program/include/rcube_vcard.php
+++ b/program/include/rcube_vcard.php
@@ -263,9 +263,9 @@ class rcube_vcard
$line[1] .= ';' . (strpos($prop, '=') ? $prop : 'TYPE='.$prop);
}
- if (!preg_match('/^(BEGIN|END)$/', $line[1]) && preg_match_all('/([^\\;]+);?/', $line[1], $regs2)) {
+ if (!preg_match('/^(BEGIN|END)$/i', $line[1]) && preg_match_all('/([^\\;]+);?/', $line[1], $regs2)) {
$entry = array('');
- $field = $regs2[1][0];
+ $field = strtoupper($regs2[1][0]);
foreach($regs2[1] as $attrid => $attr) {
if ((list($key, $value) = explode('=', $attr)) && $value) {
diff --git a/program/js/app.js b/program/js/app.js
index 205bb2d6d..0e0c8bf25 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -10,27 +10,26 @@
| Authors: Thomas Bruederli <roundcube@gmail.com> |
| Charles McNulty <charles@charlesmcnulty.com> |
+-----------------------------------------------------------------------+
- | Requires: common.js, list.js |
+ | Requires: jquery.js, common.js, list.js |
+-----------------------------------------------------------------------+
$Id$
*/
-var rcube_webmail_client;
-
function rcube_webmail()
- {
+{
this.env = new Object();
this.labels = new Object();
this.buttons = new Object();
this.gui_objects = new Object();
+ this.gui_containers = new Object();
this.commands = new Object();
+ this.command_handlers = new Object();
this.onloads = new Array();
// create protected reference to myself
- rcube_webmail_client = this;
- this.ref = 'rcube_webmail_client';
+ this.ref = 'rcmail';
var ref = this;
// webmail client settings
@@ -53,6 +52,12 @@ function rcube_webmail()
this.env.bin_path = './bin/';
this.env.blankpage = 'program/blank.gif';
+ // set jQuery ajax options
+ jQuery.ajaxSetup({ cache:false,
+ error:function(request, status, err){ ref.http_error(request, status, err); },
+ beforeSend:function(xmlhttp){ xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid')); }
+ });
+
// set environment variable(s)
this.set_env = function(p, value)
{
@@ -89,11 +94,33 @@ function rcube_webmail()
this.gui_objects[name] = id;
};
+ // register a container object
+ this.gui_container = function(name, id)
+ {
+ this.gui_containers[name] = id;
+ };
+
+ // add a GUI element (html node) to a specified container
+ this.add_element = function(elm, container)
+ {
+ if (this.gui_containers[container] && this.gui_containers[container].jquery)
+ this.gui_containers[container].append(elm);
+ };
+
+ // register an external handler for a certain command
+ this.register_command = function(command, callback, enable)
+ {
+ this.command_handlers[command] = callback;
+
+ if (enable)
+ this.enable_command(command, true);
+ };
+
// execute the given script on load
this.add_onload = function(f)
- {
- this.onloads[this.onloads.length] = f;
- };
+ {
+ this.onloads[this.onloads.length] = f;
+ };
// initialize webmail client
this.init = function()
@@ -108,6 +135,10 @@ function rcube_webmail()
return;
}
+ // find all registered gui containers
+ for (var n in this.gui_containers)
+ this.gui_containers[n] = $('#'+this.gui_containers[n]);
+
// find all registered gui objects
for (var n in this.gui_objects)
this.gui_objects[n] = rcube_find_object(this.gui_objects[n]);
@@ -135,15 +166,13 @@ function rcube_webmail()
this.message_list.addEventListener('dragstart', function(o){ p.drag_start(o); });
this.message_list.addEventListener('dragmove', function(o, e){ p.drag_move(e); });
this.message_list.addEventListener('dragend', function(o){ p.drag_active = false; });
+ document.onmouseup = function(e){ return p.doc_mouse_up(e); };
this.message_list.init();
this.enable_command('toggle_status', 'toggle_flag', true);
if (this.gui_objects.mailcontframe)
- {
this.gui_objects.mailcontframe.onmousedown = function(e){ return p.click_on_list(e); };
- document.onmouseup = function(e){ return p.doc_mouse_up(e); };
- }
else
this.message_list.focus();
}
@@ -196,7 +225,7 @@ function rcube_webmail()
{
this.env.spellcheck.spelling_state_observer = function(s){ ref.set_spellcheck_state(s); };
this.set_spellcheck_state('ready');
- if (rcube_find_object('_is_html').value == '1')
+ if ($("input[name='_is_html']").val() == '1')
this.display_spellcheck_controls(false);
}
if (this.env.drafts_mailbox)
@@ -263,6 +292,8 @@ function rcube_webmail()
}
else
this.contact_list.focus();
+
+ this.gui_objects.folderlist = this.gui_objects.contactslist;
}
this.set_page_buttons();
@@ -316,20 +347,16 @@ function rcube_webmail()
break;
case 'login':
- var input_user = rcube_find_object('rcmloginuser');
- var input_pass = rcube_find_object('rcmloginpwd');
- var input_tz = rcube_find_object('rcmlogintz');
-
- if (input_user)
- input_user.onkeyup = function(e){ return rcmail.login_user_keyup(e); };
- if (input_user && input_user.value=='')
+ var input_user = $('#rcmloginuser');
+ input_user.bind('keypress', function(e){ return rcmail.login_user_keyup(e); });
+
+ if (input_user.val() == '')
input_user.focus();
- else if (input_pass)
- input_pass.focus();
+ else
+ $('#rcmloginpwd').focus();
// detect client timezone
- if (input_tz)
- input_tz.value = new Date().getTimezoneOffset() / -60;
+ $('#rcmlogintz').val(new Date().getTimezoneOffset() / -60);
this.enable_command('login', true);
break;
@@ -347,11 +374,16 @@ function rcube_webmail()
// show message
if (this.pending_message)
this.display_message(this.pending_message[0], this.pending_message[1]);
+
+ // map implicit containers
+ if (this.gui_objects.folderlist)
+ this.gui_containers.foldertray = $(this.gui_objects.folderlist);
- // start keep-alive interval
- this.start_keepalive();
+ // trigger init event hook
+ this.triggerEvent('init', { task:this.task, action:this.env.action });
// execute all foreign onload scripts
+ // @deprecated
for (var i=0; i<this.onloads.length; i++)
{
if (typeof(this.onloads[i]) == 'string')
@@ -359,7 +391,10 @@ function rcube_webmail()
else if (typeof(this.onloads[i]) == 'function')
this.onloads[i]();
}
- };
+
+ // start keep-alive interval
+ this.start_keepalive();
+ };
// start interval for keep-alive/recent_check signal
this.start_keepalive = function()
@@ -417,34 +452,25 @@ function rcube_webmail()
return false;
//this.messageform = this.gui_objects.messageform;
- var input_from = rcube_find_object('_from');
- var input_to = rcube_find_object('_to');
- var input_cc = rcube_find_object('_cc');
- var input_bcc = rcube_find_object('_bcc');
- var input_replyto = rcube_find_object('_replyto');
- var input_subject = rcube_find_object('_subject');
- var input_message = rcube_find_object('_message');
- var draftid = rcube_find_object('_draft_saveid');
+ var input_from = $("[name='_from']");
+ var input_to = $("[name='_to']");
+ var input_subject = $("input[name='_subject']");
+ var input_message = $("[name='_message']").get(0);
// init live search events
- if (input_to)
- this.init_address_input_events(input_to);
- if (input_cc)
- this.init_address_input_events(input_cc);
- if (input_bcc)
- this.init_address_input_events(input_bcc);
+ this.init_address_input_events(input_to);
+ this.init_address_input_events($("[name='_cc']"));
+ this.init_address_input_events($("[name='_bcc']"));
// add signature according to selected identity
- if (input_from && input_from.type=='select-one' && (!draftid || draftid.value=='')
- // if we have HTML editor, signature is added in callback
- && rcube_find_object('_is_html').value != '1')
- {
- this.change_identity(input_from);
- }
+ if (input_from.attr('type') == 'select-one' && $("input[name='_draft_saveid']").val() == ''
+ && $("input[name='_is_html']").val() != '1') { // if we have HTML editor, signature is added in callback
+ this.change_identity(input_from[0]);
+ }
- if (input_to && input_to.value=='')
+ if (input_to.val() == '')
input_to.focus();
- else if (input_subject && input_subject.value=='')
+ else if (input_subject.val() == '')
input_subject.focus();
else if (input_message)
this.set_caret2start(input_message);
@@ -459,13 +485,8 @@ function rcube_webmail()
this.init_address_input_events = function(obj)
{
var handler = function(e){ return ref.ksearch_keypress(e,this); };
-
- if (obj.addEventListener)
- obj.addEventListener(bw.safari ? 'keydown' : 'keypress', handler, false);
- else
- obj.onkeydown = handler;
-
- obj.setAttribute('autocomplete', 'off');
+ obj.bind((bw.safari ? 'keydown' : 'keypress'), handler);
+ obj.attr('autocomplete', 'off');
};
@@ -499,7 +520,29 @@ function rcube_webmail()
return false;
}
- // process command
+ // process external commands
+ if (typeof this.command_handlers[command] == 'function')
+ {
+ var ret = this.command_handlers[command](props, obj);
+ return ret !== null ? ret : (obj ? false : true);
+ }
+ else if (typeof this.command_handlers[command] == 'string')
+ {
+ var ret = window[this.command_handlers[command]](props, obj);
+ return ret !== null ? ret : (obj ? false : true);
+ }
+
+ // trigger plugin hook
+ var event_ret = this.triggerEvent('before'+command, props);
+ if (typeof event_ret != 'undefined') {
+ // abort if one the handlers returned false
+ if (event_ret === false)
+ return false;
+ else
+ props = event_ret;
+ }
+
+ // process internal command
switch (command)
{
case 'login':
@@ -509,7 +552,7 @@ function rcube_webmail()
case 'logout':
this.goto_url('logout', '', true);
- break;
+ break;
// commands to switch task
case 'mail':
@@ -558,7 +601,6 @@ function rcube_webmail()
var a_sort = props.split('_');
var sort_col = a_sort[0];
var sort_order = a_sort[1] ? a_sort[1].toUpperCase() : null;
- var header;
// no sort order specified: toggle
if (sort_order==null)
@@ -573,10 +615,8 @@ function rcube_webmail()
break;
// set table header class
- if (header = document.getElementById('rcm'+this.env.sort_col))
- this.set_classname(header, 'sorted'+(this.env.sort_order.toUpperCase()), false);
- if (header = document.getElementById('rcm'+sort_col))
- this.set_classname(header, 'sorted'+sort_order, true);
+ $('#rcm'+this.env.sort_col).removeClass('sorted'+(this.env.sort_order.toUpperCase()));
+ $('#rcm'+sort_col).addClass('sorted'+sort_order);
// save new sort properties
this.env.sort_col = sort_col;
@@ -657,12 +697,12 @@ function rcube_webmail()
case 'save':
if (this.gui_objects.editform)
{
- var input_pagesize = rcube_find_object('_pagesize');
- var input_name = rcube_find_object('_name');
- var input_email = rcube_find_object('_email');
+ var input_pagesize = $("input[name='_pagesize']");
+ var input_name = $("input[name='_name']");
+ var input_email = $("input[name='_email']");
// user prefs
- if (input_pagesize && isNaN(parseInt(input_pagesize.value)))
+ if (input_pagesize.length && isNaN(parseInt(input_pagesize.val())))
{
alert(this.get_label('nopagesizewarning'));
input_pagesize.focus();
@@ -671,13 +711,13 @@ function rcube_webmail()
// contacts/identities
else
{
- if (input_name && input_name.value == '')
+ if (input_name.length && input_name.val() == '')
{
alert(this.get_label('nonamewarning'));
input_name.focus();
break;
}
- else if (input_email && !rcube_check_email(input_email.value))
+ else if (input_email.length && !rcube_check_email(input_email.val()))
{
alert(this.get_label('noemailwarning'));
input_email.focus();
@@ -1062,6 +1102,8 @@ function rcube_webmail()
break;
}
+
+ this.triggerEvent('after'+command, props);
return obj ? false : true;
};
@@ -1114,13 +1156,18 @@ function rcube_webmail()
};
// return a localized string
- this.get_label = function(name)
+ this.get_label = function(name, domain)
{
- if (this.labels[name])
+ if (domain && this.labels[domain+'.'+name])
+ return this.labels[domain+'.'+name];
+ else if (this.labels[name])
return this.labels[name];
else
return name;
};
+
+ // alias for convenience reasons
+ this.gettext = this.get_label;
// switch to another application task
this.switch_task = function(task)
@@ -1157,16 +1204,18 @@ function rcube_webmail()
this.doc_mouse_up = function(e)
{
- var model, li;
+ var model, list, li;
if (this.message_list) {
if (!rcube_mouse_is_over(e, this.message_list.list))
this.message_list.blur();
+ list = this.message_list;
model = this.env.mailboxes;
}
else if (this.contact_list) {
if (!rcube_mouse_is_over(e, this.contact_list.list))
this.contact_list.blur();
+ list = this.contact_list;
model = this.env.address_sources;
}
else if (this.ksearch_value) {
@@ -1175,9 +1224,10 @@ function rcube_webmail()
// handle mouse release when dragging
if (this.drag_active && model && this.env.last_folder_target) {
- this.set_classname(this.get_folder_li(this.env.last_folder_target), 'droptarget', false);
+ $(this.get_folder_li(this.env.last_folder_target)).removeClass('droptarget');
this.command('moveto', model[this.env.last_folder_target].id);
this.env.last_folder_target = null;
+ list.draglayer.hide();
}
};
@@ -1196,27 +1246,25 @@ function rcube_webmail()
if (this.gui_objects.folderlist && model)
{
var li, pos, list, height;
- list = rcube_find_object(this.task == 'mail' ? 'mailboxlist' : 'directorylist');
- pos = rcube_get_object_pos(list);
- this.env.folderlist_coords = {x1:pos.x, y1:pos.y, x2:pos.x + list.offsetWidth, y2:pos.y + list.offsetHeight};
+ list = $(this.gui_objects.folderlist);
+ pos = list.offset();
+ this.env.folderlist_coords = { x1:pos.left, y1:pos.top, x2:pos.left + list.width(), y2:pos.top + list.height() };
this.env.folder_coords = new Array();
for (var k in model) {
- if (li = this.get_folder_li(k))
- {
- pos = rcube_get_object_pos(li.firstChild);
- // only visible folders
- if (height = li.firstChild.offsetHeight)
- this.env.folder_coords[k] = {x1:pos.x, y1:pos.y, x2:pos.x + li.firstChild.offsetWidth, y2:pos.y + height};
- }
+ if (li = this.get_folder_li(k)) {
+ pos = $(li.firstChild).offset();
+ // only visible folders
+ if (height = li.firstChild.offsetHeight)
+ this.env.folder_coords[k] = { x1:pos.left, y1:pos.top, x2:pos.left + li.firstChild.offsetWidth, y2:pos.top + height };
}
}
+ }
};
this.drag_move = function(e)
- {
- if (this.gui_objects.folderlist && this.env.folder_coords)
- {
+ {
+ if (this.gui_objects.folderlist && this.env.folder_coords) {
// offsets to compensate for scrolling while dragging a message
var boffset = bw.ie ? -document.documentElement.scrollTop : this.initialBodyScrollTop;
var moffset = this.initialMailBoxScrollTop-document.getElementById('mailboxlist-container').scrollTop;
@@ -1229,53 +1277,50 @@ function rcube_webmail()
mouse.y += toffset;
// if mouse pointer is outside of folderlist
- if (mouse.x < pos.x1 || mouse.x >= pos.x2
- || mouse.y < pos.y1 || mouse.y >= pos.y2)
- {
- if (this.env.last_folder_target) {
- this.set_classname(this.get_folder_li(this.env.last_folder_target), 'droptarget', false);
+ if (mouse.x < pos.x1 || mouse.x >= pos.x2 || mouse.y < pos.y1 || mouse.y >= pos.y2) {
+ if (this.env.last_folder_target) {
+ $(this.get_folder_li(this.env.last_folder_target)).removeClass('droptarget');
this.env.last_folder_target = null;
- }
- return;
}
+ return;
+ }
// over the folders
- for (var k in this.env.folder_coords)
- {
- pos = this.env.folder_coords[k];
- if (this.check_droptarget(k) && ((mouse.x >= pos.x1) && (mouse.x < pos.x2)
- && (mouse.y >= pos.y1) && (mouse.y < pos.y2)))
- {
- this.set_classname(this.get_folder_li(k), 'droptarget', true);
- this.env.last_folder_target = k;
- }
- else
- this.set_classname(this.get_folder_li(k), 'droptarget', false);
+ for (var k in this.env.folder_coords) {
+ pos = this.env.folder_coords[k];
+ if (this.check_droptarget(k) && ((mouse.x >= pos.x1) && (mouse.x < pos.x2)
+ && (mouse.y >= pos.y1) && (mouse.y < pos.y2))) {
+ $(this.get_folder_li(k)).addClass('droptarget');
+ this.env.last_folder_target = k;
+ }
+ else {
+ $(this.get_folder_li(k)).removeClass('droptarget');
+ if (k == this.env.last_folder_target)
+ this.env.last_folder_target = null;
}
}
- };
+ }
+ };
this.collapse_folder = function(id)
{
var div;
if ((li = this.get_folder_li(id)) &&
- (div = li.getElementsByTagName("div")[0]) &&
- (div.className.match(/collapsed/) || div.className.match(/expanded/)))
+ (div = $(li.getElementsByTagName("div")[0])) &&
+ (div.hasClass('collapsed') || div.hasClass('expanded')))
{
- var ul = li.getElementsByTagName("ul")[0];
- if (div.className.match(/collapsed/))
+ var ul = $(li.getElementsByTagName("ul")[0]);
+ if (div.hasClass('collapsed'))
{
- ul.style.display = '';
- this.set_classname(div, 'collapsed', false);
- this.set_classname(div, 'expanded', true);
+ ul.show();
+ div.removeClass('collapsed').addClass('expanded');
var reg = new RegExp('&'+urlencode(id)+'&');
this.set_env('collapsed_folders', this.env.collapsed_folders.replace(reg, ''));
}
else
{
- ul.style.display = 'none';
- this.set_classname(div, 'expanded', false);
- this.set_classname(div, 'collapsed', true);
+ ul.hide();
+ div.removeClass('expanded').addClass('collapsed');
this.set_env('collapsed_folders', this.env.collapsed_folders+'&'+urlencode(id)+'&');
// select parent folder if one of its childs is currently selected
@@ -1309,10 +1354,6 @@ function rcube_webmail()
else if (this.contact_list)
this.contact_list.focus();
- var mbox_li;
- if (mbox_li = this.get_folder_li())
- this.set_classname(mbox_li, 'unfocused', true);
-
return rcube_event.get_button(e) == 2 ? true : rcube_event.cancel(e);
};
@@ -1412,6 +1453,7 @@ function rcube_webmail()
// also send search request to get the right messages
if (this.env.search_request)
add_url += '&_search='+this.env.search_request;
+
var url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox)+add_url;
if (action == 'preview' && String(target.location.href).indexOf(url) >= 0)
this.show_contentframe(true);
@@ -1424,19 +1466,19 @@ function rcube_webmail()
if (action == 'preview' && this.message_list && this.message_list.rows[id] && this.message_list.rows[id].unread)
{
this.set_message(id, 'unread', false);
- if (this.env.unread_counts[this.env.mailbox])
- {
- this.env.unread_counts[this.env.mailbox] -= 1;
- this.set_unread_count(this.env.mailbox, this.env.unread_counts[this.env.mailbox], this.env.mailbox == 'INBOX');
- }
- }
+ if (this.env.unread_counts[this.env.mailbox])
+ {
+ this.env.unread_counts[this.env.mailbox] -= 1;
+ this.set_unread_count(this.env.mailbox, this.env.unread_counts[this.env.mailbox], this.env.mailbox == 'INBOX');
+ }
+ }
}
};
this.show_contentframe = function(show)
{
var frm;
- if (this.env.contentframe && (frm = rcube_find_object(this.env.contentframe)))
+ if (this.env.contentframe && (frm = $('#'+this.env.contentframe)) && frm.length)
{
if (!show && window.frames[this.env.contentframe])
{
@@ -1444,7 +1486,7 @@ function rcube_webmail()
window.frames[this.env.contentframe].location.href = this.env.blankpage;
}
else if (!bw.safari && !bw.konq)
- frm.style.display = show ? 'block' : 'none';
+ frm[show ? 'show' : 'hide']();
}
if (!show && this.busy)
@@ -1677,37 +1719,38 @@ function rcube_webmail()
if (flag)
this.set_message_status(uid, flag, status);
+ var rowobj = $(rows[uid].obj);
if (rows[uid].unread && rows[uid].classname.indexOf('unread')<0)
{
rows[uid].classname += ' unread';
- this.set_classname(rows[uid].obj, 'unread', true);
+ rowobj.addClass('unread');
}
else if (!rows[uid].unread && rows[uid].classname.indexOf('unread')>=0)
{
rows[uid].classname = rows[uid].classname.replace(/\s*unread/, '');
- this.set_classname(rows[uid].obj, 'unread', false);
+ rowobj.removeClass('unread');
}
if (rows[uid].deleted && rows[uid].classname.indexOf('deleted')<0)
{
rows[uid].classname += ' deleted';
- this.set_classname(rows[uid].obj, 'deleted', true);
+ rowobj.addClass('deleted');
}
else if (!rows[uid].deleted && rows[uid].classname.indexOf('deleted')>=0)
{
rows[uid].classname = rows[uid].classname.replace(/\s*deleted/, '');
- this.set_classname(rows[uid].obj, 'deleted', false);
+ rowobj.removeClass('deleted');
}
if (rows[uid].flagged && rows[uid].classname.indexOf('flagged')<0)
{
rows[uid].classname += ' flagged';
- this.set_classname(rows[uid].obj, 'flagged', true);
+ rowobj.addClass('flagged');
}
else if (!rows[uid].flagged && rows[uid].classname.indexOf('flagged')>=0)
{
rows[uid].classname = rows[uid].classname.replace(/\s*flagged/, '');
- this.set_classname(rows[uid].obj, 'flagged', false);
+ rowobj.removeClass('flagged');
}
this.set_message_icon(uid);
@@ -1811,8 +1854,8 @@ function rcube_webmail()
{
this.set_message_status(id, 'deleted', true);
if (this.env.read_when_deleted)
- this.set_message_status(id, 'unread', false);
- this.set_message(id);
+ this.set_message_status(id, 'unread', false);
+ this.set_message(id);
}
}
}
@@ -1999,14 +2042,15 @@ function rcube_webmail()
this.login_user_keyup = function(e)
{
var key = rcube_event.get_keycode(e);
- var elm;
+ var passwd = $('#rcmloginpwd');
// enter
- if ((key==13) && (elm = rcube_find_object('_pass')))
- {
- elm.focus();
- return false;
+ if (key == 13 && passwd.length && !passwd.val()) {
+ passwd.focus();
+ return rcube_event.cancel(e);
}
+
+ return true;
};
@@ -2018,15 +2062,15 @@ function rcube_webmail()
this.check_compose_input = function()
{
// check input fields
- var input_to = rcube_find_object('_to');
- var input_cc = rcube_find_object('_cc');
- var input_bcc = rcube_find_object('_bcc');
- var input_from = rcube_find_object('_from');
- var input_subject = rcube_find_object('_subject');
- var input_message = rcube_find_object('_message');
+ var input_to = $("[name='_to']");
+ var input_cc = $("[name='_cc']");
+ var input_bcc = $("[name='_bcc']");
+ var input_from = $("[name='_from']");
+ var input_subject = $("[name='_subject']");
+ var input_message = $("[name='_message']");
// check sender (if have no identities)
- if (input_from.type == 'text' && !rcube_check_email(input_from.value, true))
+ if (input_from.attr('type') == 'text' && !rcube_check_email(input_from.val(), true))
{
alert(this.get_label('nosenderwarning'));
input_from.focus();
@@ -2034,7 +2078,7 @@ function rcube_webmail()
}
// check for empty recipient
- var recipients = input_to.value ? input_to.value : (input_cc.value ? input_cc.value : input_bcc.value);
+ var recipients = input_to.val() ? input_to.val() : (input_cc.val() ? input_cc.val() : input_bcc.val());
if (!rcube_check_email(recipients.replace(/^\s+/, '').replace(/[\s,;]+$/, ''), true))
{
alert(this.get_label('norecipientwarning'));
@@ -2043,7 +2087,7 @@ function rcube_webmail()
}
// display localized warning for missing subject
- if (input_subject && input_subject.value == '')
+ if (input_subject.val() == '')
{
var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject'));
@@ -2055,12 +2099,12 @@ function rcube_webmail()
}
else
{
- input_subject.value = subject ? subject : this.get_label('nosubject');
+ input_subject.val((subject ? subject : this.get_label('nosubject')));
}
}
// check for empty body
- if ((!window.tinyMCE || !tinyMCE.get('compose-body')) && input_message.value == '' && !confirm(this.get_label('nobodywarning')))
+ if ((!window.tinyMCE || !tinyMCE.get('compose-body')) && input_message.val() == '' && !confirm(this.get_label('nobodywarning')))
{
input_message.focus();
return false;
@@ -2105,9 +2149,7 @@ function rcube_webmail()
this.set_draft_id = function(id)
{
- var f;
- if (f = rcube_find_object('_draft_saveid'))
- f.value = id;
+ $("input[name='_draft_saveid']").val(id);
};
this.auto_save_start = function()
@@ -2122,29 +2164,26 @@ function rcube_webmail()
this.compose_field_hash = function(save)
{
// check input fields
- var input_to = rcube_find_object('_to');
- var input_cc = rcube_find_object('_cc');
- var input_bcc = rcube_find_object('_bcc');
- var input_subject = rcube_find_object('_subject');
- var editor, input_message;
+ var value_to = $("[name='_to']").val();
+ var value_cc = $("[name='_cc']").val();
+ var value_bcc = $("[name='_bcc']").val();
+ var value_subject = $("[name='_subject']").val();
var str = '';
- if (input_to && input_to.value)
- str += input_to.value+':';
- if (input_cc && input_cc.value)
- str += input_cc.value+':';
- if (input_bcc && input_bcc.value)
- str += input_bcc.value+':';
- if (input_subject && input_subject.value)
- str += input_subject.value+':';
-
- if (editor = tinyMCE.get('compose-body'))
+ if (value_to)
+ str += value_to+':';
+ if (value_cc)
+ str += value_cc+':';
+ if (value_bcc)
+ str += value_bcc+':';
+ if (value_subject)
+ str += value_subject+':';
+
+ var editor = tinyMCE.get('compose-body');
+ if (editor)
str += editor.getContent();
else
- {
- input_message = rcube_find_object('_message');
- str += input_message.value;
- }
+ str += $("[name='_message']").val();
if (save)
this.cmp_hash = str;
@@ -2158,9 +2197,9 @@ function rcube_webmail()
return false;
var id = obj.options[obj.selectedIndex].value;
- var input_message = rcube_find_object('_message');
- var message = input_message ? input_message.value : '';
- var is_html = (rcube_find_object('_is_html').value == '1');
+ var input_message = $("[name='_message']");
+ var message = input_message.val();
+ var is_html = ($("input[name='_is_html']").val() == '1');
var sig, p;
if (!this.env.identity)
@@ -2174,9 +2213,9 @@ function rcube_webmail()
if (this.env.signatures[this.env.identity]['is_html'])
sig = this.env.signatures[this.env.identity]['plain_text'];
else
- sig = this.env.signatures[this.env.identity]['text'];
+ sig = this.env.signatures[this.env.identity]['text'];
- if (sig.indexOf('-- ')!=0)
+ if (sig.indexOf('-- ')!=0)
sig = '-- \n'+sig;
p = message.lastIndexOf(sig);
@@ -2207,32 +2246,32 @@ function rcube_webmail()
{
// Append the signature as a div within the body
var sigElem = editor.dom.get('_rc_sig');
- var newsig = '';
- var htmlsig = true;
+ var newsig = '';
+ var htmlsig = true;
if (!sigElem)
{
- // add empty line before signature on IE
- if (bw.ie)
+ // add empty line before signature on IE
+ if (bw.ie)
editor.getBody().appendChild(editor.getDoc().createElement('br'));
- sigElem = editor.getDoc().createElement('div');
+ sigElem = editor.getDoc().createElement('div');
sigElem.setAttribute('id', '_rc_sig');
editor.getBody().appendChild(sigElem);
}
- if (this.env.signatures[id])
- {
- newsig = this.env.signatures[id]['text'];
- htmlsig = this.env.signatures[id]['is_html'];
-
- if (newsig) {
- if (htmlsig && this.env.signatures[id]['plain_text'].indexOf('-- ')!=0)
+ if (this.env.signatures[id])
+ {
+ newsig = this.env.signatures[id]['text'];
+ htmlsig = this.env.signatures[id]['is_html'];
+
+ if (newsig) {
+ if (htmlsig && this.env.signatures[id]['plain_text'].indexOf('-- ')!=0)
newsig = '<p>-- </p>' + newsig;
- else if (!htmlsig && newsig.indexOf('-- ')!=0)
+ else if (!htmlsig && newsig.indexOf('-- ')!=0)
newsig = '-- \n' + newsig;
- }
- }
+ }
+ }
if (htmlsig)
sigElem.innerHTML = newsig;
@@ -2241,8 +2280,7 @@ function rcube_webmail()
}
}
- if (input_message)
- input_message.value = message;
+ input_message.val(message);
this.env.identity = id;
return true;
@@ -2256,27 +2294,24 @@ function rcube_webmail()
var elm, list;
if (elm = this.gui_objects.uploadbox)
{
- if (a && (list = this.gui_objects.attachmentlist))
+ if (a && (list = this.gui_objects.attachmentlist))
{
- var pos = rcube_get_object_pos(list);
- var left = pos.x;
- var top = pos.y + list.offsetHeight + 10;
-
- elm.style.top = top+'px';
- elm.style.left = left+'px';
+ var pos = $(list).offset();
+ elm.style.top = (pos.top + list.offsetHeight + 10) + 'px';
+ elm.style.left = pos.left + 'px';
}
elm.style.visibility = a ? 'visible' : 'hidden';
}
// clear upload form
- try {
+ try {
if (!a && this.gui_objects.attachmentform != this.gui_objects.messageform)
- this.gui_objects.attachmentform.reset();
- }
- catch(e){} // ignore errors
+ this.gui_objects.attachmentform.reset();
+ }
+ catch(e){} // ignore errors
- return true;
+ return true;
};
// upload attachment file
@@ -2336,10 +2371,7 @@ function rcube_webmail()
if (!this.gui_objects.attachmentlist)
return false;
- var li = document.createElement('LI');
- li.id = name;
- li.innerHTML = content;
- this.gui_objects.attachmentlist.appendChild(li);
+ $('<li>').attr('id', name).html(content).appendTo(this.gui_objects.attachmentlist);
return true;
};
@@ -2439,7 +2471,7 @@ function rcube_webmail()
highlight = document.getElementById('rcmksearchSelected');
if (!highlight)
- highlight = this.ksearch_pane.ul.firstChild;
+ highlight = this.ksearch_pane.__ul.firstChild;
if (highlight)
this.ksearch_select(dir ? highlight.previousSibling : highlight.nextSibling);
@@ -2479,15 +2511,13 @@ function rcube_webmail()
this.ksearch_select = function(node)
{
- var current = document.getElementById('rcmksearchSelected');
- if (current && node) {
- current.removeAttribute('id');
- this.set_classname(current, 'selected', false);
+ var current = $('#rcmksearchSelected');
+ if (current[0] && node) {
+ current.removeAttr('id').removeClass('selected');
}
if (node) {
- node.setAttribute('id', 'rcmksearchSelected');
- this.set_classname(node, 'selected', true);
+ $(node).attr('id', 'rcmksearchSelected').addClass('selected');
this.ksearch_selected = node._rcm_id;
}
};
@@ -2521,8 +2551,8 @@ function rcube_webmail()
if (inp_value === null)
return;
- if (this.ksearch_pane && this.ksearch_pane.visible)
- this.ksearch_pane.show(0);
+ if (this.ksearch_pane && this.ksearch_pane.is(":visible"))
+ this.ksearch_pane.hide();
// get string from current cursor pos to last comma
var cpos = this.get_caret_pos(this.ksearch_input);
@@ -2561,15 +2591,13 @@ function rcube_webmail()
// create results pane if not present
if (!this.ksearch_pane) {
- ul = document.createElement('UL');
- this.ksearch_pane = new rcube_layer('rcmKSearchpane', {vis:0, zindex:30000});
- this.ksearch_pane.elm.appendChild(ul);
- this.ksearch_pane.ul = ul;
+ ul = $('<ul>');
+ this.ksearch_pane = $('<div>').attr('id', 'rcmKSearchpane').css({ position:'absolute', 'z-index':30000 }).append(ul).appendTo(document.body);
+ this.ksearch_pane.__ul = ul[0];
}
- else
- ul = this.ksearch_pane.ul;
// remove all search results
+ ul = this.ksearch_pane.__ul;
ul.innerHTML = '';
// add each result line to list
@@ -2583,14 +2611,12 @@ function rcube_webmail()
}
// select the first
- ul.firstChild.setAttribute('id', 'rcmksearchSelected');
- this.set_classname(ul.firstChild, 'selected', true);
+ $(ul.firstChild).attr('id', 'rcmksearchSelected').addClass('selected');
this.ksearch_selected = 0;
// move the results pane right under the input box and make it visible
- var pos = rcube_get_object_pos(this.ksearch_input);
- this.ksearch_pane.move(pos.x, pos.y+this.ksearch_input.offsetHeight);
- this.ksearch_pane.show(1);
+ var pos = $(this.ksearch_input).offset();
+ this.ksearch_pane.css({ left:pos.left+'px', top:(pos.top + this.ksearch_input.offsetHeight)+'px' }).show();
}
// hide results pane
else
@@ -2623,7 +2649,7 @@ function rcube_webmail()
this.ksearch_selected = null;
if (this.ksearch_pane)
- this.ksearch_pane.show(0);
+ this.ksearch_pane.hide();
};
@@ -2788,19 +2814,18 @@ function rcube_webmail()
// update a contact record in the list
this.update_contact_row = function(cid, cols_arr)
- {
+ {
var row;
- if (this.contact_list.rows[cid] && (row = this.contact_list.rows[cid].obj))
- {
+ if (this.contact_list.rows[cid] && (row = this.contact_list.rows[cid].obj)) {
for (var c=0; c<cols_arr.length; c++)
if (row.cells[c])
- row.cells[c].innerHTML = cols_arr[c];
+ $(row.cells[c]).html(cols_arr[c]);
return true;
- }
+ }
return false;
- };
+ };
/*********************************************************/
@@ -2882,30 +2907,29 @@ function rcube_webmail()
(folder = this.env.subscriptionrows[id][0]))
{
if (this.check_droptarget(folder) &&
- !this.env.subscriptionrows[this.get_folder_row_id(this.env.folder)][2] &&
- (folder != this.env.folder.replace(reg, '')) &&
+ !this.env.subscriptionrows[this.get_folder_row_id(this.env.folder)][2] &&
+ (folder != this.env.folder.replace(reg, '')) &&
(!folder.match(new RegExp('^'+RegExp.escape(this.env.folder+this.env.delimiter)))))
{
this.set_env('dstfolder', folder);
- this.set_classname(row, 'droptarget', true);
+ $(row).addClass('droptarget');
}
}
else if (this.env.folder.match(new RegExp(RegExp.escape(this.env.delimiter))))
{
this.set_env('dstfolder', this.env.delimiter);
- this.set_classname(this.subscription_list.frame, 'droptarget', true);
+ $(this.subscription_list.frame).addClass('droptarget');
}
}
this.unfocus_subscription = function(id)
{
- var row;
+ var row = $('#'+id);
this.set_env('dstfolder', null);
- if (this.env.subscriptionrows[id] &&
- (row = document.getElementById(id)))
- this.set_classname(row, 'droptarget', false);
+ if (this.env.subscriptionrows[id] && row[0])
+ row.removeClass('droptarget');
else
- this.set_classname(this.subscription_list.frame, 'droptarget', false);
+ $(this.subscription_list.frame).removeClass('droptarget');
}
this.subscription_select = function(list)
@@ -2919,7 +2943,7 @@ function rcube_webmail()
this.set_env('folder', null);
if (this.gui_objects.createfolderhint)
- this.gui_objects.createfolderhint.innerHTML = this.env.folder ? this.get_label('addsubfolderhint') : '';
+ $(this.gui_objects.createfolderhint).html(this.env.folder ? this.get_label('addsubfolderhint') : '');
};
this.subscription_move_folder = function(list)
@@ -3006,7 +3030,7 @@ function rcube_webmail()
var cell = this.name_input ? this.name_input.parentNode : null;
if (cell && this.edit_folder && this.env.subscriptionrows[this.edit_folder])
- cell.innerHTML = this.env.subscriptionrows[this.edit_folder][1];
+ $(cell).html(this.env.subscriptionrows[this.edit_folder][1]);
this.edit_folder = null;
};
@@ -3054,8 +3078,7 @@ function rcube_webmail()
this.http_post('delete-folder', '_mboxes='+urlencode(folder), true);
this.set_env('folder', null);
- if (this.gui_objects.createfolderhint)
- this.gui_objects.createfolderhint.innerHTML = '';
+ $(this.gui_objects.createfolderhint).html('');
}
};
@@ -3378,16 +3401,6 @@ function rcube_webmail()
}
};
- // set/unset a specific class name
- this.set_classname = function(obj, classname, set)
- {
- var reg = new RegExp('\s*'+classname, 'i');
- if (!set && obj.className.match(reg))
- obj.className = obj.className.replace(reg, '');
- else if (set && !obj.className.match(reg))
- obj.className += ' '+classname;
- };
-
// write to the document/window title
this.set_pagetitle = function(title)
{
@@ -3418,25 +3431,20 @@ function rcube_webmail()
if (type)
cont = '<div class="'+type+'">'+cont+'</div>';
- var _rcube = this;
- this.gui_objects.message.innerHTML = cont;
- this.gui_objects.message.style.display = 'block';
+ var obj = $(this.gui_objects.message).html(cont).show();
if (type!='loading')
- this.gui_objects.message.onmousedown = function(){ _rcube.hide_message(); return true; };
+ obj.bind('mousedown', function(){ ref.hide_message(); return true; });
if (!hold)
- this.message_timer = window.setTimeout(function(){ ref.hide_message(); }, this.message_time);
+ this.message_timer = window.setTimeout(function(){ ref.hide_message(true); }, this.message_time);
};
// make a message row disapear
- this.hide_message = function()
+ this.hide_message = function(fade)
{
if (this.gui_objects.message)
- {
- this.gui_objects.message.style.display = 'none';
- this.gui_objects.message.onmousedown = null;
- }
+ $(this.gui_objects.message).unbind()[(fade?'fadeOut':'hide')]();
};
// mark a mailbox as selected and set environment variable
@@ -3446,16 +3454,12 @@ function rcube_webmail()
{
var current_li, target_li;
- if ((current_li = this.get_folder_li(old)))
- {
- this.set_classname(current_li, 'selected', false);
- this.set_classname(current_li, 'unfocused', false);
+ if ((current_li = this.get_folder_li(old))) {
+ $(current_li).removeClass('selected').removeClass('unfocused');
}
- if ((target_li = this.get_folder_li(name)))
- {
- this.set_classname(target_li, 'unfocused', false);
- this.set_classname(target_li, 'selected', true);
+ if ((target_li = this.get_folder_li(name))) {
+ $(target_li).removeClass('unfocused').addClass('selected');
}
}
};
@@ -3512,22 +3516,24 @@ function rcube_webmail()
var rowcount = tbody.rows.length;
var even = rowcount%2;
- this.env.messages[uid] = {deleted:flags.deleted?1:0,
- replied:flags.replied?1:0,
- unread:flags.unread?1:0,
- forwarded:flags.forwarded?1:0,
- flagged:flags.flagged?1:0};
-
- var row = document.createElement('TR');
- row.id = 'rcmrow'+uid;
- row.className = 'message'
- + (even ? ' even' : ' odd')
+ this.env.messages[uid] = {
+ deleted: flags.deleted?1:0,
+ replied: flags.replied?1:0,
+ unread: flags.unread?1:0,
+ forwarded: flags.forwarded?1:0,
+ flagged:flags.flagged?1:0
+ };
+
+ var css_class = 'message'
+ + (even ? ' even' : ' odd')
+ (flags.unread ? ' unread' : '')
- + (flags.deleted ? ' deleted' : '')
- + (flags.flagged ? ' flagged' : '');
+ + (flags.deleted ? ' deleted' : '')
+ + (flags.flagged ? ' flagged' : '');
+
+ var row = $('<tr>').attr('id', 'rcmrow'+uid).attr('class', css_class);
if (this.message_list.in_selection(uid))
- row.className += ' selected';
+ row.addClass('selected');
var icon = this.env.messageicon;
if (flags.deleted && this.env.deletedicon)
@@ -3544,49 +3550,42 @@ function rcube_webmail()
else if(flags.unread && this.env.unreadicon)
icon = this.env.unreadicon;
- var col = document.createElement('TD');
- col.className = 'icon';
- col.innerHTML = icon ? '<img src="'+icon+'" alt="" />' : '';
- row.appendChild(col);
+ // add icon col
+ $('<td>').addClass('icon').html(icon ? '<img src="'+icon+'" alt="" />' : '').appendTo(row);
// add each submitted col
- for (var n = 0; n < this.coltypes.length; n++)
- {
+ for (var n = 0; n < this.coltypes.length; n++) {
var c = this.coltypes[n];
- col = document.createElement('TD');
- col.className = String(c).toLowerCase();
+ col = $('<td>').addClass(String(c).toLowerCase());
- if (c=='flag')
- {
+ if (c=='flag') {
if (flags.flagged && this.env.flaggedicon)
- col.innerHTML = '<img src="'+this.env.flaggedicon+'" alt="" />';
+ col.html('<img src="'+this.env.flaggedicon+'" alt="" />');
else if(!flags.flagged && this.env.unflaggedicon)
- col.innerHTML = '<img src="'+this.env.unflaggedicon+'" alt="" />';
+ col.html('<img src="'+this.env.unflaggedicon+'" alt="" />');
}
else if (c=='attachment')
- col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : '&nbsp;';
+ col.html(attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : '&nbsp;');
else
- col.innerHTML = cols[c];
+ col.html(cols[c]);
- row.appendChild(col);
+ col.appendTo(row);
}
this.message_list.insert_row(row, attop);
// remove 'old' row
- if (attop && this.env.pagesize && this.message_list.rowcount > this.env.pagesize)
- {
- var uid = this.message_list.get_last_row();
- this.message_list.remove_row(uid);
- this.message_list.clear_selection(uid);
- }
- };
+ if (attop && this.env.pagesize && this.message_list.rowcount > this.env.pagesize) {
+ var uid = this.message_list.get_last_row();
+ this.message_list.remove_row(uid);
+ this.message_list.clear_selection(uid);
+ }
+ };
// replace content of row count display
this.set_rowcount = function(text)
{
- if (this.gui_objects.countdisplay)
- this.gui_objects.countdisplay.innerHTML = text;
+ $(this.gui_objects.countdisplay).html(text);
// update page navigation buttons
this.set_page_buttons();
@@ -3602,8 +3601,8 @@ function rcube_webmail()
// replace content of quota display
this.set_quota = function(content)
{
- if (this.gui_objects.quotadisplay && content)
- this.gui_objects.quotadisplay.innerHTML = content;
+ if (content && this.gui_objects.quotadisplay)
+ $(this.gui_objects.quotadisplay).html(content);
};
// update the mailboxlist
@@ -3632,9 +3631,8 @@ function rcube_webmail()
{
// add children's counters
for (var k in this.env.unread_counts)
- if (k.indexOf(mbox + this.env.delimiter) == 0) {
+ if (k.indexOf(mbox + this.env.delimiter) == 0)
childcount += this.env.unread_counts[k];
- }
}
if (mycount && text_obj.innerHTML.match(reg))
@@ -3650,7 +3648,10 @@ function rcube_webmail()
this.set_unread_count_display(mbox.replace(reg, ''), false);
// set the right classes
- this.set_classname(item, 'unread', (mycount+childcount)>0 ? true : false);
+ if ((mycount+childcount)>0)
+ $(item).addClass('unread');
+ else
+ $(item).removeClass('unread');
}
// set unread count to window title
@@ -3691,21 +3692,15 @@ function rcube_webmail()
var rowcount = tbody.rows.length;
var even = rowcount%2;
- var row = document.createElement('TR');
- row.id = 'rcmrow'+cid;
- row.className = 'contact '+(even ? 'even' : 'odd');
+ var row = $('<tr>').attr('id', 'rcmrow'+cid).addClass('class').addClass(even ? 'even' : 'odd');
if (this.contact_list.in_selection(cid))
- row.className += ' selected';
+ row.addClass('selected');
// add each submitted col
- for (var c in cols)
- {
- col = document.createElement('TD');
- col.className = String(c).toLowerCase();
- col.innerHTML = cols[c];
- row.appendChild(col);
- }
+ for (var c in cols) {
+ col = $('<td>').addClass(String(c).toLowerCase()).html(cols[c]).appendTo(row);
+ }
this.contact_list.insert_row(row);
this.enable_command('export', (this.contact_list.rowcount > 0));
@@ -3720,19 +3715,16 @@ function rcube_webmail()
// display fetched raw headers
this.set_headers = function(content)
- {
- if (this.gui_objects.all_headers_row && this.gui_objects.all_headers_box && content)
- {
- var box = this.gui_objects.all_headers_box;
- box.innerHTML = content;
- box.style.display = 'block';
+ {
+ if (this.gui_objects.all_headers_row && this.gui_objects.all_headers_box && content) {
+ $(this.gui_objects.all_headers_box).html(content).show();
if (this.env.framed && parent.rcmail)
- parent.rcmail.set_busy(false);
+ parent.rcmail.set_busy(false);
else
this.set_busy(false);
- }
- };
+ }
+ };
// display all-headers row and fetch raw message headers
this.load_headers = function(elem)
@@ -3740,15 +3732,14 @@ function rcube_webmail()
if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box || !this.env.uid)
return;
- this.set_classname(elem, 'show-headers', false);
- this.set_classname(elem, 'hide-headers', true);
- this.gui_objects.all_headers_row.style.display = bw.ie ? 'block' : 'table-row';
+ $(elem).removeClass('show-headers').addClass('hide-headers');
+ $(this.gui_objects.all_headers_row).show();
elem.onclick = function() { rcmail.hide_headers(elem); }
// fetch headers only once
if (!this.gui_objects.all_headers_box.innerHTML)
{
- this.display_message(this.get_label('loading'), 'loading', true);
+ this.display_message(this.get_label('loading'), 'loading', true);
this.http_post('headers', '_uid='+this.env.uid);
}
}
@@ -3759,9 +3750,8 @@ function rcube_webmail()
if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box)
return;
- this.set_classname(elem, 'hide-headers', false);
- this.set_classname(elem, 'show-headers', true);
- this.gui_objects.all_headers_row.style.display = 'none';
+ $(elem).removeClass('hide-headers').addClass('show-headers');
+ $(this.gui_objects.all_headers_row).hide();
elem.onclick = function() { rcmail.load_headers(elem); }
}
@@ -3772,23 +3762,16 @@ function rcube_webmail()
this.html2plain = function(htmlText, id)
{
- var http_request = new rcube_http_request();
var url = this.env.bin_path+'html2text.php';
var rcmail = this;
this.set_busy(true, 'converting');
console.log('HTTP POST: '+url);
- http_request.onerror = function(o) { rcmail.http_error(o); };
- http_request.oncomplete = function(o) { rcmail.set_text_value(o, id); };
- http_request.POST(url, htmlText, 'application/octet-stream');
- }
-
- this.set_text_value = function(httpRequest, id)
- {
- this.set_busy(false);
- document.getElementById(id).value = httpRequest.get_text();
- console.log(httpRequest.get_text());
+ $.ajax({ type: 'POST', url: url, data: htmlText, contentType: 'application/octet-stream',
+ error: function(o) { rcmail.http_error(o); },
+ success: function(data) { rcmail.set_busy(false); $(document.getElementById(id)).val(data); console.log(data); }
+ });
}
@@ -3813,99 +3796,61 @@ function rcube_webmail()
this.redirect(this.env.comm_path+'&_action='+action+querystring, lock);
};
- this.http_sockets = new Array();
-
- // find a non-busy socket or create a new one
- this.get_request_obj = function()
- {
- for (var n=0; n<this.http_sockets.length; n++)
- {
- if (!this.http_sockets[n].busy)
- return this.http_sockets[n];
- }
-
- // create a new XMLHTTP object
- var i = this.http_sockets.length;
- this.http_sockets[i] = new rcube_http_request();
-
- return this.http_sockets[i];
- };
-
// send a http request to the server
this.http_request = function(action, querystring, lock)
- {
- var request_obj = this.get_request_obj();
+ {
querystring += (querystring ? '&' : '') + '_remote=1';
+ var url = this.env.comm_path + '&_action=' + action + '&' + querystring
- // add timestamp to request url to avoid cacheing problems in Safari
- if (bw.safari)
- querystring += '&_ts='+(new Date().getTime());
-
// send request
- if (request_obj)
- {
- console.log('HTTP request: '+this.env.comm_path+'&_action='+action+'&'+querystring);
-
- if (lock)
- this.set_busy(true);
-
- var rcm = this;
- request_obj.__lock = lock ? true : false;
- request_obj.__action = action;
- request_obj.onerror = function(o){ ref.http_error(o); };
- request_obj.oncomplete = function(o){ ref.http_response(o); };
- request_obj.GET(this.env.comm_path+'&_action='+action+'&'+querystring);
- }
- };
-
- // send a http POST request to the server
- this.http_post = function(action, postdata, lock)
- {
- var request_obj;
- if (postdata && typeof(postdata) == 'object')
- postdata._remote = 1;
- else
- postdata += (postdata ? '&' : '') + '_remote=1';
-
- // send request
- if (request_obj = this.get_request_obj())
- {
- console.log('HTTP POST: '+this.env.comm_path+'&_action='+action);
+ console.log('HTTP POST: ' + url);
+ jQuery.get(url, { _unlock:(lock?1:0) }, function(data){ ref.http_response(data); }, 'json');
+ };
- if (lock)
- this.set_busy(true);
+ // send a http POST request to the server
+ this.http_post = function(action, postdata, lock)
+ {
+ var url = this.env.comm_path+'&_action=' + action;
+
+ if (postdata && typeof(postdata) == 'object') {
+ postdata._remote = 1;
+ postdata._unlock = (lock ? 1 : 0);
+ }
+ else
+ postdata += (postdata ? '&' : '') + '_remote=1' + (lock ? '&_unlock=1' : '');
- var rcm = this;
- request_obj.__lock = lock ? true : false;
- request_obj.__action = action;
- request_obj.onerror = function(o){ rcm.http_error(o); };
- request_obj.oncomplete = function(o){ rcm.http_response(o); };
- request_obj.POST(this.env.comm_path+'&_action='+action, postdata);
- }
- };
+ // send request
+ console.log('HTTP POST: ' + url);
+ jQuery.post(url, postdata, function(data){ ref.http_response(data); }, 'json');
+ };
// handle HTTP response
- this.http_response = function(request_obj)
- {
- var ctype = request_obj.get_header('Content-Type');
- if (ctype)
- {
- ctype = String(ctype).toLowerCase();
- var ctype_array=ctype.split(";");
- ctype = ctype_array[0];
- }
-
- if (request_obj.__lock)
+ this.http_response = function(response)
+ {
+ var console_msg = '';
+
+ if (response.unlock)
this.set_busy(false);
- console.log(request_obj.get_text());
+ // set env vars
+ if (response.env)
+ this.set_env(response.env);
- // if we get javascript code from server -> execute it
- if (request_obj.get_text() && (ctype=='text/javascript' || ctype=='application/x-javascript'))
- eval(request_obj.get_text());
+ // we have labels to add
+ if (typeof response.texts == 'object') {
+ for (var name in response.texts)
+ if (typeof response.texts[name] == 'string')
+ this.add_label(name, response.texts[name]);
+ }
+ // if we get javascript code from server -> execute it
+ if (response.exec) {
+ console.log(response.exec);
+ eval(response.exec);
+ }
+
// process the response data according to the sent action
- switch (request_obj.__action) {
+ switch (response.action) {
case 'delete':
if (this.task == 'addressbook') {
var uid = this.contact_list.get_selection();
@@ -3922,7 +3867,7 @@ function rcube_webmail()
break;
case 'purge':
- case 'expunge':
+ case 'expunge':
if (!this.env.messagecount && this.task == 'mail') {
// clear preview pane content
if (this.env.contentframe)
@@ -3937,23 +3882,22 @@ function rcube_webmail()
case 'getunread':
case 'list':
if (this.task == 'mail') {
- if (this.message_list && request_obj.__action == 'list')
+ if (this.message_list && response.action == 'list')
this.msglist_select(this.message_list);
this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0));
this.enable_command('purge', this.purge_mailbox_test());
}
else if (this.task == 'addressbook')
this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
-
break;
- }
-
- request_obj.reset();
- };
+ }
+ };
// handle HTTP request errors
- this.http_error = function(request_obj)
+ this.http_error = function(request, status, err)
{
+ alert(status+":"+err);
+/*
//alert('Error sending request: '+request_obj.url+' => HTTP '+request_obj.xmlhttp.status);
if (request_obj.__lock)
this.set_busy(false);
@@ -3961,6 +3905,7 @@ function rcube_webmail()
request_obj.reset();
request_obj.__lock = false;
this.display_message('Unknown Server Error!', 'error');
+*/
};
// use an image to send a keep-alive siganl to the server
@@ -4066,161 +4011,11 @@ function rcube_webmail()
}
};
- } // end object rcube_webmail
+} // end object rcube_webmail
-/**
- * Class for sending HTTP requests
- * @constructor
- */
-function rcube_http_request()
- {
- this.url = '';
- this.busy = false;
- this.xmlhttp = null;
-
- // reset object properties
- this.reset = function()
- {
- // set unassigned event handlers
- this.onloading = function(){ };
- this.onloaded = function(){ };
- this.oninteractive = function(){ };
- this.oncomplete = function(){ };
- this.onabort = function(){ };
- this.onerror = function(){ };
-
- this.url = '';
- this.busy = false;
- this.xmlhttp = null;
- }
-
- // create HTMLHTTP object
- this.build = function()
- {
- if (window.XMLHttpRequest)
- this.xmlhttp = new XMLHttpRequest();
- else if (window.ActiveXObject)
- {
- try { this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
- catch(e) { this.xmlhttp = null; }
- }
- else
- {
-
- }
- }
-
- // send GET request
- this.GET = function(url)
- {
- this.build();
-
- if (!this.xmlhttp)
- {
- this.onerror(this);
- return false;
- }
-
- var _ref = this;
- this.url = url;
- this.busy = true;
-
- this.xmlhttp.onreadystatechange = function(){ _ref.xmlhttp_onreadystatechange(); };
- this.xmlhttp.open('GET', url, true);
- this.xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid'));
- this.xmlhttp.send(null);
- };
-
- this.POST = function(url, body, contentType)
- {
- // default value for contentType if not provided
- if (typeof(contentType) == 'undefined')
- contentType = 'application/x-www-form-urlencoded';
-
- this.build();
-
- if (!this.xmlhttp)
- {
- this.onerror(this);
- return false;
- }
-
- var req_body = body;
- if (typeof(body) == 'object')
- {
- req_body = '';
- for (var p in body)
- req_body += (req_body ? '&' : '') + p+'='+urlencode(body[p]);
- }
-
- var ref = this;
- this.url = url;
- this.busy = true;
-
- this.xmlhttp.onreadystatechange = function() { ref.xmlhttp_onreadystatechange(); };
- this.xmlhttp.open('POST', url, true);
- this.xmlhttp.setRequestHeader('Content-Type', contentType);
- this.xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid'));
- this.xmlhttp.send(req_body);
- };
-
- // handle onreadystatechange event
- this.xmlhttp_onreadystatechange = function()
- {
- if(this.xmlhttp.readyState == 1)
- this.onloading(this);
-
- else if(this.xmlhttp.readyState == 2)
- this.onloaded(this);
-
- else if(this.xmlhttp.readyState == 3)
- this.oninteractive(this);
-
- else if(this.xmlhttp.readyState == 4)
- {
- try {
- if (this.xmlhttp.status == 0)
- this.onabort(this);
- else if(this.xmlhttp.status == 200)
- this.oncomplete(this);
- else
- this.onerror(this);
-
- this.busy = false;
- }
- catch(err)
- {
- this.onerror(this);
- this.busy = false;
- }
- }
- }
-
- // getter method for HTTP headers
- this.get_header = function(name)
- {
- return this.xmlhttp.getResponseHeader(name);
- };
-
- this.get_text = function()
- {
- return this.xmlhttp.responseText;
- };
-
- this.get_xml = function()
- {
- return this.xmlhttp.responseXML;
- };
-
- this.reset();
-
- } // end class rcube_http_request
-
-// helper function to call the init method with a delay
-function call_init(o)
- {
- window.setTimeout('if (window[\''+o+'\'] && window[\''+o+'\'].init) { '+o+'.init(); }',
- bw.win ? 500 : 200);
- }
+// copy event engine prototype
+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;
diff --git a/program/js/common.js b/program/js/common.js
index bd699d924..407da4170 100644
--- a/program/js/common.js
+++ b/program/js/common.js
@@ -93,7 +93,7 @@ function roundcube_browser()
}
-// static functions for event handling
+// static functions for DOM event handling
var rcube_event = {
/**
@@ -159,8 +159,8 @@ get_mouse_pos: function(e)
}
if (e._offset) {
- mX += e._offset.x;
- mY += e._offset.y;
+ mX += e._offset.left;
+ mY += e._offset.top;
}
return { x:mX, y:mY };
@@ -234,7 +234,86 @@ cancel: function(evt)
};
-var rcube_layer_objects = new Array();
+/**
+ * rcmail objects event interface
+ */
+function rcube_event_engine()
+{
+ this._events = {};
+}
+
+rcube_event_engine.prototype = {
+
+/**
+ * Setter for object event handlers
+ *
+ * @param {String} Event name
+ * @param {Function} Handler function
+ * @return Listener ID (used to remove this handler later on)
+ */
+addEventListener: function(evt, func, obj)
+{
+ if (!this._events)
+ this._events = {};
+ if (!this._events[evt])
+ this._events[evt] = [];
+
+ var e = {func:func, obj:obj ? obj : window};
+ this._events[evt][this._events[evt].length] = e;
+},
+
+/**
+ * Removes a specific event listener
+ *
+ * @param {String} Event name
+ * @param {Int} Listener ID to remove
+ */
+removeEventListener: function(evt, func, obj)
+{
+ if (typeof obj == 'undefined')
+ obj = window;
+
+ for (var h,i=0; this._events && this._events[evt] && i < this._events[evt].length; i++)
+ if ((h = this._events[evt][i]) && h.func == func && h.obj == obj)
+ this._events[evt][i] = null;
+},
+
+/**
+ * This will execute all registered event handlers
+ *
+ * @param {String} Event to trigger
+ * @param {Object} Event object/arguments
+ */
+triggerEvent: function(evt, e)
+{
+ var ret, h;
+ if (typeof e == 'undefined')
+ e = {};
+ if (typeof e == 'object')
+ e.event = evt;
+
+ if (this._events && this._events[evt] && !this._event_exec) {
+ this._event_exec = true;
+ for (var i=0; i < this._events[evt].length; i++) {
+ if ((h = this._events[evt][i])) {
+ if (typeof h.func == 'function')
+ ret = h.func.call ? h.func.call(h.obj, this, e) : h.func(this, e);
+ else if (typeof h.obj[h.func] == 'function')
+ ret = h.obj[h.func](this, e);
+
+ // cancel event execution
+ if (typeof ret != 'undefined' && !ret)
+ break;
+ }
+ }
+ }
+
+ this._event_exec = false;
+ return ret;
+}
+
+} // end rcube_event_engine.prototype
+
/**
@@ -243,7 +322,7 @@ var rcube_layer_objects = new Array();
* @constructor
*/
function rcube_layer(id, attributes)
- {
+{
this.name = id;
// create a new layer in the current document
@@ -310,10 +389,6 @@ function rcube_layer(id, attributes)
this.y = parseInt(this.elm.offsetTop);
this.visible = (this.css.visibility=='visible' || this.css.visibility=='show' || this.css.visibility=='inherit') ? true : false;
- this.id = rcube_layer_objects.length;
- this.obj = 'rcube_layer_objects['+this.id+']';
- rcube_layer_objects[this.id] = this;
-
// ********* layer object methods *********
@@ -327,16 +402,6 @@ function rcube_layer(id, attributes)
this.css.top = Math.round(this.y)+'px';
}
-
- // move the layer for a specific step
- this.shift = function(x,y)
- {
- x = Math.round(x*100)/100;
- y = Math.round(y*100)/100;
- this.move(this.x+x, this.y+y);
- }
-
-
// change the layers width and height
this.resize = function(w,h)
{
@@ -347,15 +412,6 @@ function rcube_layer(id, attributes)
}
- // cut the layer (top,width,height,left)
- this.clip = function(t,w,h,l)
- {
- this.css.clip='rect('+t+' '+w+' '+h+' '+l+')';
- this.clip_height = h;
- this.clip_width = w;
- }
-
-
// show or hide the layer
this.show = function(a)
{
@@ -383,36 +439,7 @@ function rcube_layer(id, attributes)
this.elm.innerHTML = cont;
}
-
- // set the given color to the layer background
- this.set_bgcolor = function(c)
- {
- if(!c || c=='#')
- c = 'transparent';
-
- this.css.backgroundColor = c;
- }
-
-
- // set the opacity of a layer to the given ammount (in %)
- this.set_opacity = function(v)
- {
- if(!bw.opacity)
- return;
-
- var op = v<=1 ? Math.round(v*100) : parseInt(v);
-
- if(bw.ie)
- this.css.filter = 'alpha(opacity:'+op+')';
- else if(bw.safari)
- {
- this.css.opacity = op/100;
- this.css.KhtmlOpacity = op/100;
- }
- else if(bw.mz)
- this.css.MozOpacity = op/100;
- }
- }
+}
// check if input is a valid email address
@@ -472,7 +499,7 @@ function urlencode(str)
// get any type of html objects by id/name
function rcube_find_object(id, d)
- {
+{
var n, f, obj, e;
if(!d) d = document;
@@ -486,88 +513,34 @@ function rcube_find_object(id, d)
if(!obj && d.images.length)
obj = d.images[id];
- if(!obj && d.forms.length)
- for(f=0; f<d.forms.length; f++)
- {
+ if (!obj && d.forms.length) {
+ for (f=0; f<d.forms.length; f++) {
if(d.forms[f].name == id)
obj = d.forms[f];
else if(d.forms[f].elements[id])
obj = d.forms[f].elements[id];
- }
-
- if(!obj && d.layers)
- {
- if(d.layers[id]) obj = d.layers[id];
- for(n=0; !obj && n<d.layers.length; n++)
- obj = rcube_find_object(id, d.layers[n].document);
}
-
- return obj;
}
-
-// return the absolute position of an object within the document
-function rcube_get_object_pos(obj, relative)
- {
- if(typeof(obj)=='string')
- obj = rcube_find_object(obj);
-
- if(!obj) return {x:0, y:0};
-
- var iX = (bw.layers) ? obj.x : obj.offsetLeft;
- var iY = (bw.layers) ? obj.y : obj.offsetTop;
-
- if(!relative && (bw.ie || bw.mz))
- {
- var elm = obj.offsetParent;
- while(elm && elm!=null)
- {
- iX += elm.offsetLeft - (elm.parentNode && elm.parentNode.scrollLeft ? elm.parentNode.scrollLeft : 0);
- iY += elm.offsetTop - (elm.parentNode && elm.parentNode.scrollTop ? elm.parentNode.scrollTop : 0);
- elm = elm.offsetParent;
- }
- }
-
- return {x:iX, y:iY};
+ if (!obj && d.layers) {
+ if (d.layers[id]) obj = d.layers[id];
+ for (n=0; !obj && n<d.layers.length; n++)
+ obj = rcube_find_object(id, d.layers[n].document);
}
+ return obj;
+}
+
// determine whether the mouse is over the given object or not
function rcube_mouse_is_over(ev, obj)
{
var mouse = rcube_event.get_mouse_pos(ev);
- var pos = rcube_get_object_pos(obj);
-
- return ((mouse.x >= pos.x) && (mouse.x < (pos.x + obj.offsetWidth)) &&
- (mouse.y >= pos.y) && (mouse.y < (pos.y + obj.offsetHeight)));
-}
+ var pos = $(obj).offset();
+ return ((mouse.x >= pos.left) && (mouse.x < (pos.left + obj.offsetWidth)) &&
+ (mouse.y >= pos.top) && (mouse.y < (pos.top + obj.offsetHeight)));
+}
-/**
- * Return the currently applied value of a css property
- *
- * @param {Object} html_element Node reference
- * @param {String} css_property Property name to read in Javascript notation (eg. 'textAlign')
- * @param {String} mozilla_equivalent Equivalent property name in CSS notation (eg. 'text-align')
- * @return CSS property value
- * @type String
- */
-function get_elements_computed_style(html_element, css_property, mozilla_equivalent)
- {
- if (arguments.length==2)
- mozilla_equivalent = css_property;
-
- var el = html_element;
- if (typeof(html_element)=='string')
- el = rcube_find_object(html_element);
-
- if (el && el.currentStyle)
- return el.currentStyle[css_property];
- else if (el && document.defaultView && document.defaultView.getComputedStyle)
- return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozilla_equivalent);
- else
- return false;
- }
-
// cookie functions by GoogieSpell
function setCookie(name, value, expires, path, domain, secure)
@@ -611,7 +584,7 @@ function rcube_console()
if (box) {
if (msg.charAt(msg.length-1)=='\n')
- msg += '--------------------------------------\n';
+ msg += '--------------------------------------\n';
else
msg += '\n--------------------------------------\n';
@@ -633,7 +606,8 @@ function rcube_console()
}
var bw = new roundcube_browser();
-var console = new rcube_console();
+if (!window.console)
+ console = new rcube_console();
// Add escape() method to RegExp object
diff --git a/program/js/editor.js b/program/js/editor.js
index 6b847ba00..7f937b2b8 100644
--- a/program/js/editor.js
+++ b/program/js/editor.js
@@ -52,8 +52,8 @@ function rcmail_editor_init(skin_path, editor_lang, spellcheck, mode)
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'),
gecko_spellcheck : true,
relative_urls : false,
- remove_script_host : false ,
- rc_client: rcube_webmail_client,
+ remove_script_host : false,
+ rc_client: rcmail,
oninit : 'rcmail_editor_callback'
});
}
diff --git a/program/js/list.js b/program/js/list.js
index 522af59ab..dabcecb92 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -51,7 +51,6 @@ function rcube_list_widget(list, p)
this.drag_mouse_start = null;
this.dblclick_time = 600;
this.row_init = function(){};
- this.events = { click:[], dblclick:[], select:[], keypress:[], dragstart:[], dragmove:[], dragend:[] };
// overwrite default paramaters
if (p && typeof(p)=='object')
@@ -160,13 +159,15 @@ remove_row: function(uid, sel_next)
insert_row: function(row, attop)
{
var tbody = this.list.tBodies[0];
+ if (!row.jquery)
+ row = $(row);
if (attop && tbody.rows.length)
- tbody.insertBefore(row, tbody.firstChild);
+ row.prependTo(tbody)
else
- tbody.appendChild(row);
+ row.appendTo(tbody);
- this.init_row(row);
+ this.init_row(row[0]);
this.rowcount++;
},
@@ -181,10 +182,8 @@ focus: function(e)
for (var n=0; n<this.selection.length; n++)
{
id = this.selection[n];
- if (this.rows[id] && this.rows[id].obj)
- {
- this.set_classname(this.rows[id].obj, 'selected', true);
- this.set_classname(this.rows[id].obj, 'unfocused', false);
+ if (this.rows[id] && this.rows[id].obj) {
+ $(this.rows[id].obj).addClass('selected').removeClass('unfocused');
}
}
@@ -203,10 +202,8 @@ blur: function()
for (var n=0; n<this.selection.length; n++)
{
id = this.selection[n];
- if (this.rows[id] && this.rows[id].obj)
- {
- this.set_classname(this.rows[id].obj, 'selected', false);
- this.set_classname(this.rows[id].obj, 'unfocused', true);
+ if (this.rows[id] && this.rows[id].obj) {
+ $(this.rows[id].obj).removeClass('selected').addClass('unfocused');
}
}
},
@@ -251,26 +248,26 @@ drag_row: function(e, id)
if (iframes[n].contentDocument)
iframedoc = iframes[n].contentDocument;
else if (iframes[n].contentWindow)
- iframedoc = iframes[n].contentWindow.document;
+ iframedoc = iframes[n].contentWindow.document;
else if (iframes[n].document)
iframedoc = iframes[n].document;
if (iframedoc)
{
- var list = this;
- var pos = rcube_get_object_pos(document.getElementById(iframes[n].id));
- this.iframe_events[n] = function(e) { e._offset = pos; return list.drag_mouse_move(e); }
-
- if (iframedoc.addEventListener)
- iframedoc.addEventListener('mousemove', this.iframe_events[n], false);
- else if (iframes[n].attachEvent)
- iframedoc.attachEvent('onmousemove', this.iframe_events[n]);
- else
- iframedoc['onmousemove'] = this.iframe_events[n];
+ var list = this;
+ var pos = $('#'+iframes[n].id).offset();
+ this.iframe_events[n] = function(e) { e._offset = pos; return list.drag_mouse_move(e); }
+
+ if (iframedoc.addEventListener)
+ iframedoc.addEventListener('mousemove', this.iframe_events[n], false);
+ else if (iframes[n].attachEvent)
+ iframedoc.attachEvent('onmousemove', this.iframe_events[n]);
+ else
+ iframedoc['onmousemove'] = this.iframe_events[n];
rcube_event.add_listener({element:iframedoc, event:'mouseup', object:this, method:'drag_mouse_up'});
}
- }
+ }
}
return false;
@@ -307,9 +304,9 @@ click_row: function(e, id)
// row was double clicked
if (this.rows && dblclicked && this.in_selection(id))
- this.trigger_event('dblclick');
+ this.triggerEvent('dblclick');
else
- this.trigger_event('click');
+ this.triggerEvent('click');
if (!this.drag_active)
rcube_event.cancel(e);
@@ -407,10 +404,10 @@ select_row: function(id, mod_key, with_mouse)
// trigger event if selection changed
if (this.selection.join(',') != select_before)
- this.trigger_event('select');
+ this.triggerEvent('select');
if (this.last_selected != 0 && this.rows[this.last_selected])
- this.set_classname(this.rows[this.last_selected].obj, 'focused', false);
+ $(this.rows[this.last_selected].obj).removeClass('focused');
// unselect if toggleselect is active and the same row was clicked again
if (this.toggleselect && this.last_selected == id)
@@ -419,7 +416,7 @@ select_row: function(id, mod_key, with_mouse)
id = null;
}
else
- this.set_classname(this.rows[id].obj, 'focused', true);
+ $(this.rows[id].obj).addClass('focused');
if (!this.selection.length)
this.shift_start = null;
@@ -524,7 +521,7 @@ select_all: function(filter)
// trigger event if selection changed
if (this.selection.join(',') != select_before)
- this.trigger_event('select');
+ this.triggerEvent('select');
this.focus();
@@ -543,27 +540,24 @@ clear_selection: function(id)
if (id)
{
for (var n=0; n<this.selection.length; n++)
- if (this.selection[n] == id)
- {
- this.selection.splice(n,1);
- break;
- }
+ if (this.selection[n] == id) {
+ this.selection.splice(n,1);
+ break;
+ }
}
// all rows
else
{
for (var n=0; n<this.selection.length; n++)
- if (this.rows[this.selection[n]])
- {
- this.set_classname(this.rows[this.selection[n]].obj, 'selected', false);
- this.set_classname(this.rows[this.selection[n]].obj, 'unfocused', false);
+ if (this.rows[this.selection[n]]) {
+ $(this.rows[this.selection[n]].obj).removeClass('selected').removeClass('unfocused');
}
this.selection = new Array();
}
if (num_select && !this.selection.length)
- this.trigger_event('select');
+ this.triggerEvent('select');
},
@@ -599,7 +593,7 @@ highlight_row: function(id, multiple)
{
this.clear_selection();
this.selection[0] = id;
- this.set_classname(this.rows[id].obj, 'selected', true);
+ $(this.rows[id].obj).addClass('selected');
}
}
else if (this.rows[id])
@@ -607,7 +601,7 @@ highlight_row: function(id, multiple)
if (!this.in_selection(id)) // select row
{
this.selection[this.selection.length] = id;
- this.set_classname(this.rows[id].obj, 'selected', true);
+ $(this.rows[id].obj).addClass('selected');
}
else // unselect row
{
@@ -615,8 +609,7 @@ highlight_row: function(id, multiple)
var a_pre = this.selection.slice(0, p);
var a_post = this.selection.slice(p+1, this.selection.length);
this.selection = a_pre.concat(a_post);
- this.set_classname(this.rows[id].obj, 'selected', false);
- this.set_classname(this.rows[id].obj, 'unfocused', false);
+ $(this.rows[id].obj).removeClass('selected').removeClass('unfocused');
}
}
},
@@ -644,7 +637,7 @@ key_press: function(e)
default:
this.shiftkey = e.shiftKey;
this.key_pressed = keyCode;
- this.trigger_event('keypress');
+ this.triggerEvent('keypress');
if (this.key_pressed == this.BACKSPACE_KEY)
return rcube_event.cancel(e);
@@ -729,7 +722,7 @@ drag_mouse_move: function(e)
return false;
if (!this.draglayer)
- this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, vis:0, zindex:2000});
+ this.draglayer = $('<div>').attr('id', 'rcmdraglayer').css({ position:'absolute', display:'none', 'z-index':2000 }).appendTo(document.body);
// get subjects of selectedd messages
var names = '';
@@ -754,6 +747,9 @@ drag_mouse_move: function(e)
if (((node = obj.childNodes[i].firstChild) && (node.nodeType==3 || node.nodeName=='A')) &&
(this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == c)))
{
+ if (n == 0)
+ this.drag_start_pos = $(node).offset();
+
subject = node.nodeType==3 ? node.data : node.innerHTML;
// remove leading spaces
subject = subject.replace(/^\s+/i, '');
@@ -767,18 +763,18 @@ drag_mouse_move: function(e)
}
}
- this.draglayer.write(names);
- this.draglayer.show(1);
+ this.draglayer.html(names);
+ this.draglayer.show();
this.drag_active = true;
- this.trigger_event('dragstart');
+ this.triggerEvent('dragstart');
}
if (this.drag_active && this.draglayer)
{
var pos = rcube_event.get_mouse_pos(e);
- this.draglayer.move(pos.x+20, bw.ie ? pos.y-5+document.documentElement.scrollTop : pos.y-5);
- this.trigger_event('dragmove', e);
+ this.draglayer.css({ left:(pos.x+20)+'px', top:(pos.y-5 + (bw.ie ? document.documentElement.scrollTop : 0))+'px' });
+ this.triggerEvent('dragmove', e);
}
this.drag_start = false;
@@ -794,11 +790,15 @@ drag_mouse_up: function(e)
{
document.onmousemove = null;
- if (this.draglayer && this.draglayer.visible)
- this.draglayer.show(0);
+ if (this.draglayer && this.draglayer.is(':visible')) {
+ if (this.drag_start_pos)
+ this.draglayer.animate(this.drag_start_pos, 300, 'swing').hide(20);
+ else
+ this.draglayer.hide();
+ }
this.drag_active = false;
- this.trigger_event('dragend');
+ this.triggerEvent('dragend');
rcube_event.remove_listener({element:document, event:'mousemove', object:this, method:'drag_mouse_move'});
rcube_event.remove_listener({element:document, event:'mouseup', object:this, method:'drag_mouse_up'});
@@ -828,68 +828,10 @@ drag_mouse_up: function(e)
}
return rcube_event.cancel(e);
-},
-
-
-
-/**
- * set/unset a specific class name
- */
-set_classname: function(obj, classname, set)
-{
- var reg = new RegExp('\s*'+classname, 'i');
- if (!set && obj.className.match(reg))
- obj.className = obj.className.replace(reg, '');
- else if (set && !obj.className.match(reg))
- obj.className += ' '+classname;
-},
-
-
-/**
- * Setter for object event handlers
- *
- * @param {String} Event name
- * @param {Function} Handler function
- * @return Listener ID (used to remove this handler later on)
- */
-addEventListener: function(evt, handler)
-{
- if (this.events[evt]) {
- var handle = this.events[evt].length;
- this.events[evt][handle] = handler;
- return handle;
- }
- else
- return false;
-},
-
-
-/**
- * Removes a specific event listener
- *
- * @param {String} Event name
- * @param {Int} Listener ID to remove
- */
-removeEventListener: function(evt, handle)
-{
- if (this.events[evt] && this.events[evt][handle])
- this.events[evt][handle] = null;
-},
-
-
-/**
- * This will execute all registered event handlers
- * @private
- */
-trigger_event: function(evt, p)
-{
- if (this.events[evt] && this.events[evt].length) {
- for (var i=0; i<this.events[evt].length; i++)
- if (typeof(this.events[evt][i]) == 'function')
- this.events[evt][i](this, p);
- }
}
-
};
+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/lib/imap.inc b/program/lib/imap.inc
index 995d82fb6..967b3f160 100644
--- a/program/lib/imap.inc
+++ b/program/lib/imap.inc
@@ -182,6 +182,7 @@ class iilBasicHeader
var $forwarded = false;
var $junk = false;
var $flagged = false;
+ var $others = array();
}
/**
@@ -1661,7 +1662,7 @@ function iil_IndexThreads(&$tree) {
return $t_index;
}
-function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bodystr=false)
+function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bodystr=false, $add='')
{
global $IMAP_USE_INTERNAL_DATE;
@@ -1701,6 +1702,9 @@ function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bo
}
}
}
+
+ if ($add)
+ $add = ' '.strtoupper(trim($add));
/* FETCH uid, size, flags and headers */
$key = 'FH12';
@@ -1711,7 +1715,7 @@ function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bo
$request .= "BODY.PEEK[HEADER.FIELDS ";
$request .= "(DATE FROM TO SUBJECT REPLY-TO IN-REPLY-TO CC BCC ";
$request .= "CONTENT-TRANSFER-ENCODING CONTENT-TYPE MESSAGE-ID ";
- $request .= "REFERENCES DISPOSITION-NOTIFICATION-TO X-PRIORITY)])";
+ $request .= "REFERENCES DISPOSITION-NOTIFICATION-TO X-PRIORITY".$add.")])";
if (!iil_PutLine($fp, $request)) {
return false;
@@ -1859,7 +1863,7 @@ function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bo
list($field, $string) = iil_SplitHeaderLine($str);
$field = strtolower($field);
- $string = ereg_replace("\n[[:space:]]*"," ",$string);
+ $string = ereg_replace("\n[[:space:]]*"," ",$string);
switch ($field) {
case 'date';
@@ -1917,6 +1921,10 @@ function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bo
if (preg_match('/^(\d+)/', $string, $matches))
$result[$id]->priority = intval($matches[1]);
break;
+ default:
+ if (strlen($field) > 2)
+ $result[$id]->others[$field] = $string;
+ break;
} // end switch ()
} // end while ()
@@ -1964,9 +1972,9 @@ function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bo
return $result;
}
-function iil_C_FetchHeader(&$conn, $mailbox, $id, $uidfetch=false, $bodystr=false) {
+function iil_C_FetchHeader(&$conn, $mailbox, $id, $uidfetch=false, $bodystr=false, $add='') {
- $a = iil_C_FetchHeaders($conn, $mailbox, $id, $uidfetch, $bodystr);
+ $a = iil_C_FetchHeaders($conn, $mailbox, $id, $uidfetch, $bodystr, $add);
if (is_array($a)) {
return array_shift($a);
}
diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc
index 33dfad646..e3d7606cb 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -42,18 +42,20 @@ $OUTPUT->set_env('readonly', $CONTACTS->readonly, false);
$js_list = array();
if (strtolower($CONFIG['address_book_type']) != 'ldap') {
// We are using the DB address book, add it.
- $js_list = array("0" => array('id' => 0, 'readonly' => false));
+ $js_list['0'] = array('id' => 0, 'name' => rcube_label('personaladrbook'), 'readonly' => false);
}
if (is_array($CONFIG['ldap_public'])) {
foreach ($CONFIG['ldap_public'] as $id => $prop)
- $js_list[$id] = array('id' => $id, 'readonly' => !$prop['writable']);
+ $js_list[$id] = array('id' => $id, 'name' => $prop['name'], 'readonly' => !$prop['writable']);
}
-$OUTPUT->set_env('address_sources', $js_list);
+
+$plugin = $RCMAIL->plugins->exec_hook('address_sources', array('sources' => $js_list));
+$OUTPUT->set_env('address_sources', $plugin['sources']);
function rcmail_directory_list($attrib)
{
- global $CONFIG, $OUTPUT;
+ global $RCMAIL, $OUTPUT;
if (!$attrib['id'])
$attrib['id'] = 'rcmdirectorylist';
@@ -63,26 +65,24 @@ function rcmail_directory_list($attrib)
$current = get_input_value('_source', RCUBE_INPUT_GPC);
$line_templ = html::tag('li', array('id' => 'rcmli%s', 'class' => '%s'),
html::a(array('href' => '%s', 'onclick' => "return ".JS_OBJECT_NAME.".command('list','%s',this)"), '%s'));
-
- if (strtolower($CONFIG['address_book_type']) != 'ldap') {
- $out .= sprintf($line_templ, $local_id, (!$current ? 'selected' : ''),
- Q(rcmail_url(null, array('_source' => $local_id))), $local_id, rcube_label('personaladrbook'));
- } // end if
- else {
+
+ if (!$current && strtolower($RCMAIL->config->get('address_book_type', 'sql')) != 'ldap') {
+ $current = '0';
+ }
+ else if (!$current) {
// DB address book not used, see if a source is set, if not use the
// first LDAP directory.
- if (!$current) {
- $current = key((array)$CONFIG['ldap_public']);
- } // end if
- } // end else
-
- foreach ((array)$CONFIG['ldap_public'] as $id => $prop) {
+ $current = key((array)$RCMAIL->config->get('ldap_public', array()));
+ }
+
+ foreach ((array)$OUTPUT->env['address_sources'] as $j => $source) {
+ $id = $source['id'] ? $source['id'] : $j;
$js_id = JQ($id);
$dom_id = preg_replace('/[^a-z0-9\-_]/i', '', $id);
$out .= sprintf($line_templ, $dom_id, ($current == $id ? 'selected' : ''),
- Q(rcmail_url(null, array('_source' => $id))), $js_id, (!empty($prop['name']) ? Q($prop['name']) : Q($id)));
+ Q(rcmail_url(null, array('_source' => $id))), $js_id, (!empty($source['name']) ? Q($source['name']) : Q($id)));
}
-
+
$OUTPUT->add_gui_object('folderlist', $attrib['id']);
return html::tag('ul', $attrib, $out, html::$common_attrib);
diff --git a/program/steps/mail/attachments.inc b/program/steps/mail/attachments.inc
index f6e29f9d7..6d58edc8e 100644
--- a/program/steps/mail/attachments.inc
+++ b/program/steps/mail/attachments.inc
@@ -28,46 +28,45 @@ if (!$_SESSION['compose']) {
// remove an attachment
if ($RCMAIL->action=='remove-attachment')
{
- if (preg_match('/^rcmfile([0-9]+)$/', $_POST['_file'], $regs))
- {
+ $id = 'undefined';
+ if (preg_match('/^rcmfile(\w+)$/', $_POST['_file'], $regs))
$id = $regs[1];
- if (is_array($_SESSION['compose']['attachments'][$id]))
- {
- @unlink($_SESSION['compose']['attachments'][$id]['path']);
+ if ($attachment = $_SESSION['compose']['attachments'][$id])
+ $attachment = $RCMAIL->plugins->exec_hook('remove_attachment', $attachment);
+ if ($attachment['status']) {
+ if (is_array($_SESSION['compose']['attachments'][$id])) {
unset($_SESSION['compose']['attachments'][$id]);
$OUTPUT->command('remove_from_attachment_list', "rcmfile$id");
- $OUTPUT->send();
}
}
+
+ $OUTPUT->send();
exit;
}
if ($RCMAIL->action=='display-attachment')
{
- if (preg_match('/^rcmfile([0-9]+)$/', $_GET['_file'], $regs))
- {
+ $id = 'undefined';
+ if (preg_match('/^rcmfile(\w+)$/', $_GET['_file'], $regs))
$id = $regs[1];
- if (is_array($_SESSION['compose']['attachments'][$id]))
- {
- $apath = $_SESSION['compose']['attachments'][$id]['path'];
- header('Content-Type: ' . $_SESSION['compose']['attachments'][$id]['mimetype']);
- header('Content-Length: ' . filesize($apath));
- readfile($apath);
- }
+ if ($attachment = $_SESSION['compose']['attachments'][$id])
+ $attachment = $RCMAIL->plugins->exec_hook('display_attachment', $attachment);
+
+ if ($attachment['status']) {
+ $size = $attachment['data'] ? strlen($attachment['data']) : @filesize($attachment['path']);
+ header('Content-Type: ' . $attachment['mimetype']);
+ header('Content-Length: ' . $size);
+
+ if ($attachment['data'])
+ echo $attachment['data'];
+ else if ($attachment['path'])
+ readfile($attachment['path']);
}
exit;
}
// attachment upload action
-// use common temp dir for file uploads
-$temp_dir = unslashify($CONFIG['temp_dir']);
-
-// #1484529: we need absolute path on Windows for move_uploaded_file()
-if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
- $temp_dir = realpath($temp_dir);
-}
-
if (!is_array($_SESSION['compose']['attachments'])) {
$_SESSION['compose']['attachments'] = array();
}
@@ -77,15 +76,20 @@ $OUTPUT->reset();
if (is_array($_FILES['_attachments']['tmp_name'])) {
foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
- $tmpfname = tempnam($temp_dir, 'rcmAttmnt');
- if (move_uploaded_file($filepath, $tmpfname) && file_exists($tmpfname)) {
- $id = count($_SESSION['compose']['attachments']);
- $_SESSION['compose']['attachments'][] = array(
- 'name' => $_FILES['_attachments']['name'][$i],
- 'mimetype' => rc_mime_content_type($tmpfname, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i]),
- 'path' => $tmpfname,
- );
-
+ $attachment = array(
+ 'path' => $filepath,
+ 'name' => $_FILES['_attachments']['name'][$i],
+ 'mimetype' => rc_mime_content_type($tmpfname, $_FILES['_attachments']['type'][$i])
+ );
+
+ $attachment = $RCMAIL->plugins->exec_hook('upload_attachment', $attachment);
+ if ($attachment['status']) {
+ $id = $attachment['id'];
+
+ // store new attachment in session
+ unset($attachment['status']);
+ $_SESSION['compose']['attachments'][$id] = $attachment;
+
if (is_file($icon = $CONFIG['skin_path'] . '/images/icons/remove-attachment.png')) {
$button = html::img(array(
'src' => $icon,
@@ -99,11 +103,11 @@ if (is_array($_FILES['_attachments']['tmp_name'])) {
$content = html::a(array(
'href' => "#delete",
- 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%d', this)", JS_OBJECT_NAME, $id),
+ 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", JS_OBJECT_NAME, $id),
'title' => rcube_label('delete'),
), $button);
-
- $content .= Q($_FILES['_attachments']['name'][$i]);
+
+ $content .= Q($attachment['name']);
$OUTPUT->command('add2attachment_list', "rcmfile$id", $content);
}
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 49c4c3011..c93fa9be2 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -594,8 +594,6 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
global $OUTPUT;
$cid_map = array();
- $id = 0;
-
foreach ((array)$message->mime_parts as $pid => $part)
{
if (($part->ctype_primary != 'message' || !$bodyIsHtml) &&
@@ -603,16 +601,14 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
|| (empty($part->disposition) && $part->filename)))
{
if ($attachment = rcmail_save_attachment($message, $pid)) {
- $_SESSION['compose']['attachments'][$id] = $attachment;
- if ($bodyIsHtml && $part->filename && $part->content_id) {
- $cid_map['cid:'.$part->content_id] =
- $OUTPUT->app->comm_path.'&_action=display-attachment&_file=rcmfile'.$id;
+ $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
+ if ($bodyIsHtml && $part->filename && $part->content_id) {
+ $cid_map['cid:'.$part->content_id] = $OUTPUT->app->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'];
}
- $id++;
}
}
}
-
+
$_SESSION['compose']['forward_attachments'] = true;
return $cid_map;
@@ -624,15 +620,11 @@ function rcmail_write_inline_attachments(&$message)
global $OUTPUT;
$cid_map = array();
- $id = 0;
-
foreach ((array)$message->mime_parts as $pid => $part) {
if ($part->content_id && $part->filename) {
if ($attachment = rcmail_save_attachment($message, $pid)) {
- $_SESSION['compose']['attachments'][$id] = $attachment;
- $cid_map['cid:'.$part->content_id] =
- $OUTPUT->app->comm_path.'&_action=display-attachment&_file=rcmfile'.$id;
- $id++;
+ $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
+ $cid_map['cid:'.$part->content_id] = $OUTPUT->app->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'];
}
}
}
@@ -642,24 +634,22 @@ function rcmail_write_inline_attachments(&$message)
function rcmail_save_attachment(&$message, $pid)
{
- global $RCMAIL;
-
- $temp_dir = unslashify($RCMAIL->config->get('temp_dir'));
- $tmp_path = tempnam($temp_dir, 'rcmAttmnt');
$part = $message->mime_parts[$pid];
- if ($fp = fopen($tmp_path, 'w'))
- {
- $message->get_part_content($pid, $fp);
- fclose($fp);
-
- return array(
- 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
- 'name' => $part->filename,
- 'path' => $tmp_path,
- 'content_id' => $part->content_id
- );
+ $attachment = array(
+ 'name' => $part->filename,
+ 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
+ 'content_id' => $part->content_id,
+ 'data' => $message->get_part_content($pid),
+ );
+
+ $attachment = rcmail::get_instance()->plugins->exec_hook('save_attachment', $attachment);
+ if ($attachment['status']) {
+ unset($attachment['data'], $attachment['status']);
+ return $attachment;
}
+
+ return false;
}
@@ -739,7 +729,7 @@ function rcmail_compose_attachment_list($attrib)
html::a(array(
'href' => "#delete",
'title' => rcube_label('delete'),
- 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%d', this)", JS_OBJECT_NAME, $id)),
+ 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", JS_OBJECT_NAME, $id)),
$button) . Q($a_prop['name']));
}
}
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 8931cfa4e..28ae70ca3 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -730,71 +730,86 @@ function rcmail_wash_html($html, $p = array(), $cid_replaces)
*/
function rcmail_print_body($part, $p = array())
{
- $p += array('safe' => false, 'plain' => false, 'inline_html' => true);
+ global $RCMAIL;
+
+ // trigger plugin hook
+ $data = $RCMAIL->plugins->exec_hook('message_part_before',
+ array('type' => $part->ctype_secondary, 'body' => $part->body) + $p + array('safe' => false, 'plain' => false, 'inline_html' => true));
// convert html to text/plain
- if ($part->ctype_secondary == 'html' && $p['plain']) {
- $txt = new html2text($part->body, false, true);
+ if ($data['type'] == 'html' && $data['plain']) {
+ $txt = new html2text($data['body'], false, true);
$body = $txt->get_text();
$part->ctype_secondary = 'plain';
}
// text/html
- else if ($part->ctype_secondary == 'html') {
- return rcmail_wash_html($part->body, $p, $part->replaces);
+ else if ($data['type'] == 'html') {
+ $body = rcmail_wash_html($data['body'], $data, $part->replaces);
+ $part->ctype_secondary = $data['type'];
}
// text/enriched
- else if ($part->ctype_secondary=='enriched') {
+ else if ($data['type'] == 'enriched') {
$part->ctype_secondary = 'html';
require_once('lib/enriched.inc');
- return Q(enriched_to_html($part->body), 'show');
+ $body = Q(enriched_to_html($data['body']), 'show');
}
- else
+ else {
+ // assert plaintext
$body = $part->body;
+ $part->ctype_secondary = $data['type'] = 'plain';
+ }
+
+ // free some memory (hopefully)
+ unset($data['body']);
- /**** assert plaintext ****/
-
- // make links and email-addresses clickable
- $replacements = new rcube_string_replacer;
-
- $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
- $url_chars_within = '\?\.~,!';
-
- // search for patterns like links and e-mail addresses
- $body = preg_replace_callback("/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body);
- $body = preg_replace_callback("/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body);
- $body = preg_replace_callback('/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i', array($replacements, 'mailto_callback'), $body);
-
- // split body into single lines
- $a_lines = preg_split('/\r?\n/', $body);
- $quote_level = 0;
-
- // colorize quoted parts
- for ($n=0; $n < sizeof($a_lines); $n++) {
- $line = $a_lines[$n];
- $quotation = '';
- $q = 0;
+ // plaintext postprocessing
+ if ($part->ctype_secondary == 'plain') {
+ // make links and email-addresses clickable
+ $replacements = new rcube_string_replacer;
+
+ $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
+ $url_chars_within = '\?\.~,!';
+
+ // search for patterns like links and e-mail addresses
+ $body = preg_replace_callback("/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body);
+ $body = preg_replace_callback("/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body);
+ $body = preg_replace_callback('/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i', array($replacements, 'mailto_callback'), $body);
+
+ // split body into single lines
+ $a_lines = preg_split('/\r?\n/', $body);
+ $quote_level = 0;
+
+ // colorize quoted parts
+ for ($n=0; $n < count($a_lines); $n++) {
+ $line = $a_lines[$n];
+ $quotation = '';
+ $q = 0;
- if (preg_match('/^(>+\s*)+/', $line, $regs)) {
- $q = strlen(preg_replace('/\s/', '', $regs[0]));
- $line = substr($line, strlen($regs[0]));
-
- if ($q > $quote_level)
- $quotation = str_repeat('<blockquote>', $q - $quote_level);
- else if ($q < $quote_level)
- $quotation = str_repeat("</blockquote>", $quote_level - $q);
+ if (preg_match('/^(>+\s*)+/', $line, $regs)) {
+ $q = strlen(preg_replace('/\s/', '', $regs[0]));
+ $line = substr($line, strlen($regs[0]));
+
+ if ($q > $quote_level)
+ $quotation = str_repeat('<blockquote>', $q - $quote_level);
+ else if ($q < $quote_level)
+ $quotation = str_repeat("</blockquote>", $quote_level - $q);
+ }
+ else if ($quote_level > 0)
+ $quotation = str_repeat("</blockquote>", $quote_level);
+
+ $quote_level = $q;
+ $a_lines[$n] = $quotation . Q($line, 'replace', false); // htmlquote plaintext
}
- else if ($quote_level > 0)
- $quotation = str_repeat("</blockquote>", $quote_level);
- $quote_level = $q;
- $a_lines[$n] = $quotation . Q($line, 'replace', false); // htmlquote plaintext
+ // insert the links for urls and mailtos
+ $body = $replacements->resolve(join("\n", $a_lines));
}
+
+ // allow post-processing of the message body
+ $data = $RCMAIL->plugins->exec_hook('message_part_after', array('type' => $part->ctype_secondary, 'body' => $body) + $data);
- // insert the links for urls and mailtos
- $body = $replacements->resolve(join("\n", $a_lines));
-
- return html::tag('pre', array(), $body);
+ return $data['type'] == 'html' ? $data['body'] : html::tag('pre', array(), $data['body']);
}
@@ -842,7 +857,7 @@ function rcmail_washtml_callback($tagname, $attrib, $content)
*/
function rcmail_message_headers($attrib, $headers=NULL)
{
- global $IMAP, $OUTPUT, $MESSAGE, $PRINT_MODE, $CONFIG;
+ global $IMAP, $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL;
static $sa_attrib;
// keep header table attrib
@@ -851,7 +866,6 @@ function rcmail_message_headers($attrib, $headers=NULL)
else if (!is_array($attrib) && is_array($sa_attrib))
$attrib = $sa_attrib;
-
if (!isset($MESSAGE))
return FALSE;
@@ -859,58 +873,55 @@ function rcmail_message_headers($attrib, $headers=NULL)
if (!$headers)
$headers = is_object($MESSAGE->headers) ? get_object_vars($MESSAGE->headers) : $MESSAGE->headers;
- $header_count = 0;
-
- // allow the following attributes to be added to the <table> tag
- $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
- $out = '<table' . $attrib_str . ">\n";
-
// show these headers
$standard_headers = array('subject', 'from', 'to', 'cc', 'bcc', 'replyto', 'date');
+ $output_headers = array();
- foreach ($standard_headers as $hkey)
- {
+ foreach ($standard_headers as $hkey) {
if (!$headers[$hkey])
continue;
- if ($hkey == 'date')
- {
+ if ($hkey == 'date') {
if ($PRINT_MODE)
- $header_value = format_date($headers[$hkey], $CONFIG['date_long'] ? $CONFIG['date_long'] : 'x');
+ $header_value = format_date($headers[$hkey], $RCMAIL->config->get('date_long', 'x'));
else
$header_value = format_date($headers[$hkey]);
- }
- else if ($hkey == 'replyto')
- {
+ }
+ else if ($hkey == 'replyto') {
if ($headers['replyto'] != $headers['from'])
- $header_value = Q(rcmail_address_string($headers['replyto'], null, true, $attrib['addicon']), 'show');
+ $header_value = rcmail_address_string($headers['replyto'], null, true, $attrib['addicon']);
else
continue;
- }
+ }
else if (in_array($hkey, array('from', 'to', 'cc', 'bcc')))
- $header_value = Q(rcmail_address_string($headers[$hkey], null, true, $attrib['addicon']), 'show');
+ $header_value = rcmail_address_string($headers[$hkey], null, true, $attrib['addicon']);
else if ($hkey == 'subject' && empty($headers[$hkey]))
- $header_value = Q(rcube_label('nosubject'));
+ $header_value = rcube_label('nosubject');
else
- $header_value = Q(trim($IMAP->decode_header($headers[$hkey])));
-
- $out .= "\n<tr>\n";
- $out .= '<td class="header-title">'.Q(rcube_label($hkey)).":&nbsp;</td>\n";
- $out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
- $header_count++;
- }
+ $header_value = trim($IMAP->decode_header($headers[$hkey]));
+
+ $output_headers[$hkey] = array('title' => rcube_label($hkey), 'value' => $header_value, 'raw' => $headers[$hkey]);
+ }
+
+ $plugin = $RCMAIL->plugins->exec_hook('message_headers_output', array('output' => $output_headers, 'headers' => $MESSAGE->headers));
+
+ // compose html table
+ $table = new html_table(array('cols' => 2));
+
+ foreach ($plugin['output'] as $hkey => $row) {
+ $table->add(array('class' => 'header-title'), Q($row['title']));
+ $table->add(array('class' => $hkey, 'width' => "90%"), Q($row['value'], ($hkey == 'subject' ? 'strict' : 'show')));
+ }
// all headers division
- $out .= "\n".'<tr><td colspan="2" class="more-headers show-headers"
- onclick="return '.JS_OBJECT_NAME.'.command(\'load-headers\', \'\', this)"></td></tr>';
- $out .= "\n".'<tr id="all-headers"><td colspan="2" class="all"><div id="headers-source"></div></td></tr>';
-
+ $table->add(array('colspan' => 2, 'class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('load-headers','',this)"), '');
+ $table->add_row(array('id' => "all-headers"));
+ $table->add(array('colspan' => 2, 'class' => "all"), html::div(array('id' => 'headers-source'), ''));
+
$OUTPUT->add_gui_object('all_headers_row', 'all-headers');
$OUTPUT->add_gui_object('all_headers_box', 'headers-source');
- $out .= "\n</table>\n\n";
-
- return $header_count ? $out : '';
+ return $table->show($attrib);
}
@@ -1251,10 +1262,7 @@ function rcmail_compose_cleanup()
if (!isset($_SESSION['compose']))
return;
- // remove attachment files from temp dir
- if (is_array($_SESSION['compose']['attachments']))
- foreach ($_SESSION['compose']['attachments'] as $attachment)
- @unlink($attachment['path']);
+ rcmail::get_instance()->plugins->exec_hook('cleanup_attachments',array());
unset($_SESSION['compose']);
}
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index 9607619e9..34e2c0904 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -297,86 +297,92 @@ $MAIL_MIME = new rcube_mail_mime($RCMAIL->config->header_delimiter());
// For HTML-formatted messages, construct the MIME message with both
// the HTML part and the plain-text part
-if ($isHtml)
- {
- $MAIL_MIME->setHTMLBody($message_body . ($footer ? "\r\n<pre>".$footer.'</pre>' : ''));
+if ($isHtml) {
+ $plugin = $RCMAIL->plugins->exec_hook('outgoing_message_body', array('body' => $message_body, 'type' => 'html', 'message' => $MAIL_MIME));
+ $MAIL_MIME->setHTMLBody($plugin['body'] . ($footer ? "\r\n<pre>".$footer.'</pre>' : ''));
// add a plain text version of the e-mail as an alternative part.
- $h2t = new html2text($message_body, false, true, 0);
- $plainTextPart = rc_wordwrap($h2t->get_text(), 75, "\r\n"). ($footer ? "\r\n".$footer : '');
+ $h2t = new html2text($plugin['body'], false, true, 0);
+ $plainTextPart = rc_wordwrap($h2t->get_text(), 75, "\r\n") . ($footer ? "\r\n".$footer : '');
$plainTextPart = wordwrap($plainTextPart, 998, "\r\n", true);
- if (!strlen($plainTextPart))
- {
+ if (!strlen($plainTextPart)) {
// empty message body breaks attachment handling in drafts
$plainTextPart = "\r\n";
- }
- $MAIL_MIME->setTXTBody($plainTextPart);
+ }
+ $plugin = $RCMAIL->plugins->exec_hook('outgoing_message_body', array('body' => $plainTextPart, 'type' => 'alternative', 'message' => $MAIL_MIME));
+ $MAIL_MIME->setTXTBody($plugin['body']);
// look for "emoticon" images from TinyMCE and copy into message as attachments
$message_body = rcmail_attach_emoticons($MAIL_MIME);
- }
+}
else
{
$message_body = rc_wordwrap($message_body, 75, "\r\n");
if ($footer)
$message_body .= "\r\n" . $footer;
$message_body = wordwrap($message_body, 998, "\r\n", true);
- if (!strlen($message_body))
- {
+ if (!strlen($message_body)) {
// empty message body breaks attachment handling in drafts
$message_body = "\r\n";
- }
- $MAIL_MIME->setTXTBody($message_body, FALSE, TRUE);
}
+ $plugin = $RCMAIL->plugins->exec_hook('outgoing_message_body', array('body' => $message_body, 'type' => 'plain', 'message' => $MAIL_MIME));
+ $MAIL_MIME->setTXTBody($plugin['body'], false, true);
+}
// chose transfer encoding
$charset_7bit = array('ASCII', 'ISO-2022-JP', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-15');
$transfer_encoding = in_array(strtoupper($message_charset), $charset_7bit) ? '7bit' : '8bit';
// add stored attachments, if any
-if (is_array($_SESSION['compose']['attachments']))
- foreach ($_SESSION['compose']['attachments'] as $id => $attachment)
- {
- $dispurl = '/\ssrc\s*=\s*[\'"]*\S+display-attachment\S+file=rcmfile' . $id . '[\s\'"]\s*/';
- $match = preg_match($dispurl, $message_body, $matches);
- if ($isHtml && ($match > 0))
- {
+if (is_array($_SESSION['compose']['attachments'])) {
+ foreach ($_SESSION['compose']['attachments'] as $id => $attachment) {
+ // This hook retrieves the attachment contents from the file storage backend
+ $attachment = $RCMAIL->plugins->exec_hook('get_attachment', $attachment);
+
+ $dispurl = '/\ssrc\s*=\s*[\'"]*\S+display-attachment\S+file=rcmfile' . preg_quote($attachment['id']) . '[\s\'"]\s*/';
+ $message_body = $MAIL_MIME->getHTMLBody();
+ if ($isHtml && (preg_match($dispurl, $message_body) > 0)) {
$message_body = preg_replace($dispurl, ' src="'.$attachment['name'].'" ', $message_body);
$MAIL_MIME->setHTMLBody($message_body);
- $MAIL_MIME->addHTMLImage($attachment['path'], $attachment['mimetype'], $attachment['name']);
+
+ if ($attachment['data'])
+ $MAIL_MIME->addHTMLImage($attachment['data'], $attachment['mimetype'], $attachment['name'], false);
+ else
+ $MAIL_MIME->addHTMLImage($attachment['path'], $attachment['mimetype'], $attachment['name'], true);
}
- else
- {
+ else {
$ctype = str_replace('image/pjpeg', 'image/jpeg', $attachment['mimetype']); // #1484914
+ $file = $attachment['data'] ? $attachment['data'] : $attachment['path'];
// .eml attachments send inline
- $MAIL_MIME->addAttachment($attachment['path'],
+ $MAIL_MIME->addAttachment($file,
$ctype,
- $attachment['name'], true,
+ $attachment['name'],
+ ($attachment['data'] ? false : true),
($ctype == 'message/rfc822' ? $transfer_encoding : 'base64'),
($ctype == 'message/rfc822' ? 'inline' : 'attachment'),
$message_charset, '', '',
- $CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL,
- $CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL
- );
+ $CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL,
+ $CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL
+ );
}
}
+}
// add submitted attachments
-if (is_array($_FILES['_attachments']['tmp_name']))
- foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath)
- {
+if (is_array($_FILES['_attachments']['tmp_name'])) {
+ foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
$ctype = $files['type'][$i];
$ctype = str_replace('image/pjpeg', 'image/jpeg', $ctype); // #1484914
$MAIL_MIME->addAttachment($filepath, $ctype, $files['name'][$i], true,
- $ctype == 'message/rfc822' ? $transfer_encoding : 'base64',
- 'attachment', $message_charset, '', '',
- $CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL,
- $CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL
- );
- }
-
+ $ctype == 'message/rfc822' ? $transfer_encoding : 'base64',
+ 'attachment', $message_charset, '', '',
+ $CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL,
+ $CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL
+ );
+ }
+}
// encoding settings for mail composing
$MAIL_MIME->setParam(array(
@@ -388,6 +394,9 @@ $MAIL_MIME->setParam(array(
'text_charset' => $message_charset,
));
+$data = $RCMAIL->plugins->exec_hook('outgoing_message_headers', array('headers' => $headers));
+$headers = $data['headers'];
+
// encoding subject header with mb_encode provides better results with asian characters
if (function_exists("mb_encode_mimeheader"))
{
diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc
index fd31fa91c..9beb42521 100644
--- a/program/steps/mail/show.inc
+++ b/program/steps/mail/show.inc
@@ -135,9 +135,11 @@ if ($_GET['_uid']) {
}
// mark message as read
- if (!$MESSAGE->headers->seen)
+ if (!$MESSAGE->headers->seen) {
$IMAP->set_flag($MESSAGE->uid, 'SEEN');
+ $RCMAIL->plugins->exec_hook('message_read', array('uid' => $MESSAGE->uid, 'mailbox' => $IMAP->mailbox, 'message' => $MESSAGE));
}
+}
diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc
index 3f0357717..6eda4dba3 100644
--- a/program/steps/settings/func.inc
+++ b/program/steps/settings/func.inc
@@ -159,6 +159,8 @@ function rcmail_user_prefs_block($part, $no_override, $attrib)
}
}
+ $RCMAIL->plugins->exec_hook('user_preferences', array('section' => $part, 'table' => $table));
+
if ($table->size())
$out = html::tag('fieldset', null, html::tag('legend', null, Q(rcube_label('uisettings'))) . $table->show($attrib));
break;
@@ -216,6 +218,8 @@ function rcmail_user_prefs_block($part, $no_override, $attrib)
$table->add(null, $input_check_all->show($config['check_all_folders']?1:0));
}
+ $RCMAIL->plugins->exec_hook('user_preferences', array('section' => $part, 'table' => $table));
+
if ($table->size())
$out = html::tag('fieldset', null, html::tag('legend', null, Q(rcube_label('mailboxview'))) . $table->show($attrib));
break;
@@ -254,6 +258,8 @@ function rcmail_user_prefs_block($part, $no_override, $attrib)
$table->add(null, $input_inline_images->show($config['inline_images']?1:0));
}
+ $RCMAIL->plugins->exec_hook('user_preferences', array('section' => $part, 'table' => $table));
+
if ($table->size())
$out = html::tag('fieldset', null, html::tag('legend', null, Q(rcube_label('messagesdisplaying'))) . $table->show($attrib));
break;
@@ -295,6 +301,8 @@ function rcmail_user_prefs_block($part, $no_override, $attrib)
$table->add(null, $select_param_folding->show($config['mime_param_folding']));
}
+ $RCMAIL->plugins->exec_hook('user_preferences', array('section' => $part, 'table' => $table));
+
if ($table->size())
$out = html::tag('fieldset', null, html::tag('legend', null, Q(rcube_label('messagescomposition'))) . $table->show($attrib));
break;
@@ -329,6 +337,8 @@ function rcmail_user_prefs_block($part, $no_override, $attrib)
$table->add(null, $select->show($config['trash_mbox'], array('name' => "_trash_mbox")));
}
+ $RCMAIL->plugins->exec_hook('user_preferences', array('section' => $part, 'table' => $table));
+
$out = html::tag('fieldset', null, html::tag('legend', null, Q(rcube_label('specialfolders'))) . $table->show($attrib));
}
break;
@@ -381,6 +391,8 @@ function rcmail_user_prefs_block($part, $no_override, $attrib)
$table->add(null, $input_expunge->show($config['logout_expunge']?1:0));
}
+ $RCMAIL->plugins->exec_hook('user_preferences', array('section' => $part, 'table' => $table));
+
if ($table->size())
$out = html::tag('fieldset', null, html::tag('legend', null, Q(rcube_label('serversettings'))) . $table->show($attrib));
break;
diff --git a/program/steps/settings/manage_folders.inc b/program/steps/settings/manage_folders.inc
index 9affded98..79d313c37 100644
--- a/program/steps/settings/manage_folders.inc
+++ b/program/steps/settings/manage_folders.inc
@@ -256,6 +256,7 @@ function rcube_subscription_form($attrib)
$a_js_folders['rcmrow'.$idx] = array($folder_utf8, $display_folder, $protected || $folder['virtual']);
}
+ rcmail::get_instance()->plugins->exec_hook('manage_folders', array('table'=>$table));
$OUTPUT->add_gui_object('subscriptionlist', $attrib['id']);
$OUTPUT->set_env('subscriptionrows', $a_js_folders);
diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc
index 09cf63d6f..c5afd5b0c 100644
--- a/program/steps/settings/save_prefs.inc
+++ b/program/steps/settings/save_prefs.inc
@@ -48,6 +48,9 @@ $a_user_prefs = array(
'trash_mbox' => get_input_value('_trash_mbox', RCUBE_INPUT_POST),
);
+$data = rcmail::get_instance()->plugins->exec_hook('save_preferences', array('prefs' => $a_user_prefs));
+$a_user_prefs = $data['prefs'];
+
// don't override these parameters
foreach ((array)$CONFIG['dont_override'] as $p)
$a_user_prefs[$p] = $CONFIG[$p];
diff --git a/skins/default/common.css b/skins/default/common.css
index 34ea1d2b8..631321c72 100644
--- a/skins/default/common.css
+++ b/skins/default/common.css
@@ -246,6 +246,13 @@ a.button-logout
border: 1px solid #CCCCCC;
}
+#pagecontent
+{
+ position: absolute;
+ top: 95px;
+ left: 20px;
+}
+
.splitter
{
user-select: none;
diff --git a/skins/default/functions.js b/skins/default/functions.js
index 9e71f6f9a..fd6e612ee 100644
--- a/skins/default/functions.js
+++ b/skins/default/functions.js
@@ -8,24 +8,16 @@
function rcube_init_settings_tabs()
{
+ var tab = '#settingstabdefault';
if (window.rcmail && rcmail.env.action)
- {
- var action = rcmail.env.action=='preferences' ? 'default' : (rcmail.env.action.indexOf('identity')>0 ? 'identities' : rcmail.env.action);
- var tab = document.getElementById('settingstab'+action);
- }
- else
- var tab = document.getElementById('settingstabdefault');
-
- if (tab)
- tab.className = 'tablink-selected';
+ tab = '#settingstab' + (rcmail.env.action=='preferences' ? 'default' : (rcmail.env.action.indexOf('identity')>0 ? 'identities' : rcmail.env.action.replace(/\./g, '')));
+
+ $(tab).addClass('tablink-selected');
}
function rcube_show_advanced(visible)
{
- var rows = document.getElementsByTagName('TR');
- for(var i=0; i<rows.length; i++)
- if(rows[i].className && rows[i].className.match(/advanced/))
- rows[i].style.display = visible ? (bw.ie ? 'block' : 'table-row') : 'none';
+ $('tr.advanced').css('display', (visible ? (bw.ie ? 'block' : 'table-row') : 'none'));
}
/**
@@ -128,7 +120,7 @@ function rcmail_init_compose_form()
function rcube_mail_ui()
{
- this.markmenu = new rcube_layer('markmessagemenu');
+ this.markmenu = $('#markmessagemenu');
}
rcube_mail_ui.prototype = {
@@ -136,24 +128,24 @@ rcube_mail_ui.prototype = {
show_markmenu: function(show)
{
if (typeof show == 'undefined')
- show = this.markmenu.visible ? false : true;
+ show = this.markmenu.is(':visible') ? false : true;
var ref = rcube_find_object('markreadbutton');
if (show && ref)
- this.markmenu.move(ref.offsetLeft, ref.offsetTop + ref.offsetHeight);
+ this.markmenu.css({ left:ref.offsetLeft, top:(ref.offsetTop + ref.offsetHeight) });
- this.markmenu.show(show);
+ this.markmenu[show?'show':'hide']();
},
body_mouseup: function(evt, p)
{
- if (this.markmenu && this.markmenu.visible && rcube_event.get_target(evt) != rcube_find_object('markreadbutton'))
+ if (this.markmenu && this.markmenu.is(':visible') && rcube_event.get_target(evt) != rcube_find_object('markreadbutton'))
this.show_markmenu(false);
},
body_keypress: function(evt, p)
{
- if (rcube_event.get_keycode(evt) == 27 && this.markmenu && this.markmenu.visible)
+ if (rcube_event.get_keycode(evt) == 27 && this.markmenu && this.markmenu.is(':visible'))
this.show_markmenu(false);
}
diff --git a/skins/default/includes/settingstabs.html b/skins/default/includes/settingstabs.html
index 5121ba19b..ce6d23407 100644
--- a/skins/default/includes/settingstabs.html
+++ b/skins/default/includes/settingstabs.html
@@ -2,4 +2,6 @@
<span id="settingstabdefault" class="tablink"><roundcube:button command="preferences" type="link" label="preferences" title="editpreferences" /></span>
<span id="settingstabfolders" class="tablink"><roundcube:button command="folders" type="link" label="folders" title="managefolders" class="tablink" /></span>
<span id="settingstabidentities" class="tablink"><roundcube:button command="identities" type="link" label="identities" title="manageidentities" class="tablink" /></span>
+<roundcube:container name="tabs" id="tabsbar" />
+<script type="text/javascript"> if (window.rcmail) rcmail.add_onload(rcube_init_settings_tabs); </script>
</div>
diff --git a/skins/default/includes/taskbar.html b/skins/default/includes/taskbar.html
index ef1aa8268..c2841a606 100644
--- a/skins/default/includes/taskbar.html
+++ b/skins/default/includes/taskbar.html
@@ -1,4 +1,5 @@
<div id="taskbar">
+<roundcube:container name="taskbar" id="taskbar" />
<roundcube:button command="mail" label="mail" class="button-mail" />
<roundcube:button command="addressbook" label="addressbook" class="button-addressbook" />
<roundcube:button command="settings" label="settings" class="button-settings" />
diff --git a/skins/default/mail.css b/skins/default/mail.css
index 5a4e57bfe..ab4579eb9 100644
--- a/skins/default/mail.css
+++ b/skins/default/mail.css
@@ -49,7 +49,7 @@
top: 32px;
left: 90px;
width: auto;
- visibility: hidden;
+ display: none;
background-color: #F9F9F9;
border: 1px solid #CCC;
padding: 1px;
diff --git a/skins/default/splitter.js b/skins/default/splitter.js
index 3ed0eb62a..fae3ca5cb 100644
--- a/skins/default/splitter.js
+++ b/skins/default/splitter.js
@@ -22,18 +22,18 @@ function rcube_splitter(attrib)
this.p2 = document.getElementById(this.p2id);
// create and position the handle for this splitter
- this.p1pos = rcube_get_object_pos(this.p1, this.relative);
- this.p2pos = rcube_get_object_pos(this.p2, this.relative);
+ this.p1pos = this.relative ? $(this.p1).position() : $(this.p1).offset();
+ this.p2pos = this.relative ? $(this.p2).position() : $(this.p2).offset();
if (this.horizontal)
{
- var top = this.p1pos.y + this.p1.offsetHeight;
+ var top = this.p1pos.top + this.p1.offsetHeight;
this.layer = new rcube_layer(this.id, {x: 0, y: top, height: 10,
width: '100%', vis: 1, parent: this.p1.parentNode});
}
else
{
- var left = this.p1pos.x + this.p1.offsetWidth;
+ var left = this.p1pos.left + this.p1.offsetWidth;
this.layer = new rcube_layer(this.id, {x: left, y: 0, width: 10,
height: '100%', vis: 1, parent: this.p1.parentNode});
}
@@ -70,18 +70,18 @@ function rcube_splitter(attrib)
if (this.horizontal)
{
var lh = this.layer.height - this.offset * 2;
- this.p1.style.height = Math.floor(this.pos - this.p1pos.y - lh / 2) + 'px';
+ this.p1.style.height = Math.floor(this.pos - this.p1pos.top - lh / 2) + 'px';
this.p2.style.top = Math.ceil(this.pos + lh / 2) + 'px';
- this.layer.move(this.layer.x, Math.round(this.pos - lh / 2 + 1));
+ this.layer.move(this.layer.x, Math.round(this.pos - lh / 2 + 1));
if (bw.ie)
- {
+ {
var new_height = (parseInt(this.p2.parentNode.offsetHeight) - parseInt(this.p2.style.top));
this.p2.style.height = (new_height > 0 ? new_height : 0) +'px';
}
}
else
{
- this.p1.style.width = Math.floor(this.pos - this.p1pos.x - this.layer.width / 2) + 'px';
+ this.p1.style.width = Math.floor(this.pos - this.p1pos.left - this.layer.width / 2) + 'px';
this.p2.style.left = Math.ceil(this.pos + this.layer.width / 2) + 'px';
this.layer.move(Math.round(this.pos - this.layer.width / 2 + 1), this.layer.y);
if (bw.ie)
@@ -94,8 +94,8 @@ function rcube_splitter(attrib)
*/
this.onDragStart = function(e)
{
- this.p1pos = rcube_get_object_pos(this.p1, this.relative);
- this.p2pos = rcube_get_object_pos(this.p2, this.relative);
+ this.p1pos = this.relative ? $(this.p1).position() : $(this.p1).offset();
+ this.p2pos = this.relative ? $(this.p2).position() : $(this.p2).offset();
this.drag_active = true;
// start listening to mousemove events
@@ -119,8 +119,8 @@ function rcube_splitter(attrib)
// I don't use the add_listener function for this one because I need to create closures to fetch
// the position of each iframe when the event is received
var s = this;
- var id = iframes[n].id;
- this.iframe_events[n] = function(e){ e._offset = rcube_get_object_pos(document.getElementById(id)); return s.onDrag(e); }
+ var id = '#'+iframes[n].id;
+ this.iframe_events[n] = function(e){ e._offset = $(id).offset(); return s.onDrag(e); }
if (iframedoc.addEventListener)
iframedoc.addEventListener('mousemove', this.iframe_events[n], false);
@@ -145,14 +145,14 @@ function rcube_splitter(attrib)
if (this.relative)
{
- var parent = rcube_get_object_pos(this.p1.parentNode);
- pos.x -= parent.x;
- pos.y -= parent.y;
+ var parent = $(this.p1.parentNode).offset();
+ pos.x -= parent.left;
+ pos.y -= parent.top;
}
if (this.horizontal)
{
- if (((pos.y - this.layer.height * 1.5) > this.p1pos.y) && ((pos.y + this.layer.height * 1.5) < (this.p2pos.y + this.p2.offsetHeight)))
+ if (((pos.y - this.layer.height * 1.5) > this.p1pos.top) && ((pos.y + this.layer.height * 1.5) < (this.p2pos.top + this.p2.offsetHeight)))
{
this.pos = pos.y;
this.resize();
@@ -160,15 +160,15 @@ function rcube_splitter(attrib)
}
else
{
- if (((pos.x - this.layer.width * 1.5) > this.p1pos.x) && ((pos.x + this.layer.width * 1.5) < (this.p2pos.x + this.p2.offsetWidth)))
+ if (((pos.x - this.layer.width * 1.5) > this.p1pos.left) && ((pos.x + this.layer.width * 1.5) < (this.p2pos.left + this.p2.offsetWidth)))
{
this.pos = pos.x;
this.resize();
}
}
- this.p1pos = rcube_get_object_pos(this.p1, this.relative);
- this.p2pos = rcube_get_object_pos(this.p2, this.relative);
+ this.p1pos = this.relative ? $(this.p1).position() : $(this.p1).offset();
+ this.p2pos = this.relative ? $(this.p2).position() : $(this.p2).offset();
return false;
};
@@ -198,7 +198,7 @@ function rcube_splitter(attrib)
if (this.iframe_events[n]) {
if (iframedoc.removeEventListener)
iframedoc.removeEventListener('mousemove', this.iframe_events[n], false);
- else if (iframedoc.detachEvent)
+ else if (iframedoc.detachEvent)
iframedoc.detachEvent('onmousemove', this.iframe_events[n]);
else
iframedoc['onmousemove'] = null;
diff --git a/skins/default/templates/addressbook.html b/skins/default/templates/addressbook.html
index ce295567b..431c0589e 100644
--- a/skins/default/templates/addressbook.html
+++ b/skins/default/templates/addressbook.html
@@ -7,7 +7,7 @@
<script type="text/javascript" src="/splitter.js"></script>
<style type="text/css">
-<roundcube:if condition="config:ldap_public == false" />
+<roundcube:if condition="count(env:address_sources) &lt;= 1" />
#abookcountbar { left: 20px;}
#mainscreen { left:20px; /* IE hack */ width:expression((parseInt(document.documentElement.clientWidth)-40)+'px') }
#addresslist { width: <roundcube:exp expression="!empty(cookie:addressviewsplitter) ? cookie:addressviewsplitter-5 : 245" />px; }
@@ -44,7 +44,7 @@
<roundcube:object name="searchform" id="quicksearchbox" /><roundcube:button command="reset-search" id="searchreset" image="/images/icons/reset.gif" title="resetsearch" />
</div>
-<roundcube:if condition="config:ldap_public" />
+<roundcube:if condition="count(env:address_sources) &gt; 1" />
<div id="directorylist">
<div id="groups-title"><roundcube:label name="groups" /></div>
<roundcube:object name="directorylist" id="directories-list" />
diff --git a/skins/default/templates/identities.html b/skins/default/templates/identities.html
index 9799bc576..30d33a38b 100644
--- a/skins/default/templates/identities.html
+++ b/skins/default/templates/identities.html
@@ -6,7 +6,7 @@
<link rel="stylesheet" type="text/css" href="/settings.css" />
<script type="text/javascript" src="/functions.js"></script>
</head>
-<body onload="rcube_init_settings_tabs()">
+<body>
<roundcube:include file="/includes/taskbar.html" />
<roundcube:include file="/includes/header.html" />
diff --git a/skins/default/templates/mail.html b/skins/default/templates/mail.html
index 96be0f65e..4e1d7ce3a 100644
--- a/skins/default/templates/mail.html
+++ b/skins/default/templates/mail.html
@@ -119,6 +119,7 @@
<roundcube:button command="forward" imageSel="/images/buttons/forward_sel.png" imageAct="/images/buttons/forward_act.png" imagePas="/images/buttons/forward_pas.png" width="32" height="32" title="forwardmessage" />
<roundcube:button command="delete" imageSel="/images/buttons/delete_sel.png" imageAct="/images/buttons/delete_act.png" imagePas="/images/buttons/delete_pas.png" width="32" height="32" title="deletemessage" />
<roundcube:button command="print" imageSel="/images/buttons/print_sel.png" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="printmessage" />
+<roundcube:container name="toolbar" id="messagetoolbar" />
<div id="markmessagemenu">
<ul class="toolbarmenu">
diff --git a/skins/default/templates/managefolders.html b/skins/default/templates/managefolders.html
index 5da5c22f1..925bc2c81 100644
--- a/skins/default/templates/managefolders.html
+++ b/skins/default/templates/managefolders.html
@@ -6,7 +6,7 @@
<link rel="stylesheet" type="text/css" href="/settings.css" />
<script type="text/javascript" src="/functions.js"></script>
</head>
-<body onload="rcube_init_settings_tabs()">
+<body>
<roundcube:include file="/includes/taskbar.html" />
<roundcube:include file="/includes/header.html" />
diff --git a/skins/default/templates/message.html b/skins/default/templates/message.html
index b8d66c1be..7d42ef73f 100644
--- a/skins/default/templates/message.html
+++ b/skins/default/templates/message.html
@@ -36,6 +36,7 @@
<roundcube:button command="print" imageSel="/images/buttons/print_sel.png" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="printmessage" />
<roundcube:button command="viewsource" imageSel="/images/buttons/source_sel.png" imageAct="/images/buttons/source_act.png" imagePas="/images/buttons/source_pas.png" width="32" height="32" title="viewsource" />
<roundcube:object name="mailboxlist" type="select" noSelection="moveto" maxlength="25" onchange="rcmail.command('moveto', this.options[this.selectedIndex].value)" class="mboxlist" />
+<roundcube:container name="toolbar" id="messagetoolbar" />
</div>
<div id="mainscreen">
diff --git a/skins/default/templates/plugin.html b/skins/default/templates/plugin.html
new file mode 100644
index 000000000..9725fe4d8
--- /dev/null
+++ b/skins/default/templates/plugin.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/<roundcube:var name='env:task'/>.css" />
+<script type="text/javascript" src="/functions.js"></script>
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+<roundcube:if condition="env:task == 'settings'" />
+ <roundcube:include file="/includes/settingstabs.html" />
+<roundcube:endif />
+
+<div id="pagecontent">
+<roundcube:object name="plugin.body" />
+</div>
+
+<roundcube:object name="plugin.footer" />
+
+</body>
+</html>
diff --git a/skins/default/templates/settings.html b/skins/default/templates/settings.html
index a3f5298cd..0abe7fa41 100644
--- a/skins/default/templates/settings.html
+++ b/skins/default/templates/settings.html
@@ -6,7 +6,7 @@
<link rel="stylesheet" type="text/css" href="/settings.css" />
<script type="text/javascript" src="/functions.js"></script>
</head>
-<body onload="rcube_init_settings_tabs()">
+<body>
<roundcube:include file="/includes/taskbar.html" />
<roundcube:include file="/includes/header.html" />
@@ -17,7 +17,7 @@
<div id="userprefs-box">
<div id="userprefs-title"><roundcube:label name="userpreferences" /></div>
-<div style="padding:15px 0 15px 15px">
+<div id="userprefscontainer" style="padding:15px 0 15px 15px">
<div class="userprefs-block">
<roundcube:object name="userprefs" form="form" parts="general,mailbox,mailview" />
</div>
@@ -25,6 +25,8 @@
<roundcube:object name="userprefs" form="form" parts="compose,folders,server" />
</div>
<div style="clear:left"></div>
+
+<roundcube:container name="userprefs" id="userprefscontainer" />
</div>
</div>