Merged branch devel-api (from r2208 to r2387) back into trunk (omitting some sample plugins)

release-0.6
thomascube 17 years ago
parent fb253ee9a8
commit cc97ea0559

@ -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';

@ -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++;
}

@ -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;
}
}

@ -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';
}
}

@ -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']);
}
}

@ -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();
}
}
?>

@ -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);
}
}
}
?>

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,7 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Mark as Junk';
$labels['reportedasjunk'] = 'Successfully reported as Junk';
?>

@ -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);
});
})
}

@ -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();
}
}

@ -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;
}
}
?>

@ -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.";
?>

@ -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.';
?>

@ -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);
}
}

@ -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;
}
}
?>

@ -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;
}
}

@ -0,0 +1,6 @@
<?php
$labels = array();
$labels['useimapsubscriptions'] = 'Use IMAP Subscriptions';
?>

@ -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;
}
}

@ -0,0 +1,9 @@
<?php
$labels = array();
$labels['userinfo'] = 'Benutzerinfo';
$labels['created'] = 'Erstellt';
$labels['lastlogin'] = 'Letztes Login';
$labels['defaultidentity'] = 'Standard-Absender';
?>

@ -0,0 +1,9 @@
<?php
$labels = array();
$labels['userinfo'] = 'User info';
$labels['created'] = 'Created';
$labels['lastlogin'] = 'Last Login';
$labels['defaultidentity'] = 'Default Identity';
?>

@ -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);
})
}

@ -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();
}
}

@ -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();
}
}

@ -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;
}

@ -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
*

@ -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');

@ -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));

@ -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 = '&';
}

@ -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 */
}
}

@ -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)

@ -25,7 +25,7 @@
*
* @package Addressbook
*/
class rcube_contacts
class rcube_contacts extends rcube_addressbook
{
var $db = null;
var $db_name = '';
@ -59,30 +59,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
*
@ -117,13 +93,6 @@ class rcube_contacts
}
/**
* Close connection to source
* Called on script shutdown
*/
function close(){}
/**
* List the current set of contact records
*
@ -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;
}

@ -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);
}
}

@ -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);
}
}

@ -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) {

@ -24,7 +24,7 @@
*
* @package Addressbook
*/
class rcube_ldap
class rcube_ldap extends rcube_addressbook
{
var $conn;
var $prop = array();

@ -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));
}

@ -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;
}
}

@ -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;
}
}

@ -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

@ -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];
}
}

@ -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) {

File diff suppressed because it is too large Load Diff

@ -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

@ -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'
});
}

@ -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;

@ -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);
}

@ -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);

@ -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);
}

@ -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']));
}
}

@ -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']);
}

@ -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"))
{

@ -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));
}
}

@ -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;

@ -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);

@ -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];

@ -246,6 +246,13 @@ a.button-logout
border: 1px solid #CCCCCC;
}
#pagecontent
{
position: absolute;
top: 95px;
left: 20px;
}
.splitter
{
user-select: none;

@ -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);
}

@ -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>

@ -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" />

@ -49,7 +49,7 @@
top: 32px;
left: 90px;
width: auto;
visibility: hidden;
display: none;
background-color: #F9F9F9;
border: 1px solid #CCC;
padding: 1px;

@ -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;

@ -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" />

@ -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" />

@ -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">

@ -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" />

@ -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">

@ -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>

@ -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>

Loading…
Cancel
Save