diff options
author | thomascube <thomas@roundcube.net> | 2007-04-28 18:07:12 +0000 |
---|---|---|
committer | thomascube <thomas@roundcube.net> | 2007-04-28 18:07:12 +0000 |
commit | f1154163b0a9efb21d722bc658352739040ffd61 (patch) | |
tree | 28ccaa50bc27fa2c3d10eb8650a9862710668494 /program/include | |
parent | 9e5d051e97441794d765b094ed46d8cc732c3944 (diff) |
Merged branch devel-addressbook from r443 back to trunk
Diffstat (limited to 'program/include')
-rw-r--r-- | program/include/main.inc | 666 | ||||
-rw-r--r-- | program/include/rcmail_template.inc | 631 | ||||
-rw-r--r-- | program/include/rcube_contacts.inc | 429 | ||||
-rwxr-xr-x | program/include/rcube_db.inc | 4 | ||||
-rw-r--r-- | program/include/rcube_imap.inc | 18 | ||||
-rw-r--r-- | program/include/rcube_ldap.inc | 561 | ||||
-rw-r--r-- | program/include/rcube_shared.inc | 182 |
7 files changed, 1668 insertions, 823 deletions
diff --git a/program/include/main.inc b/program/include/main.inc index 6d77d5a07..71534f6b1 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -5,7 +5,7 @@ | program/include/main.inc | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev, - Switzerland | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -22,6 +22,7 @@ require_once('lib/des.inc'); require_once('lib/utf7.inc'); require_once('lib/utf8.class.php'); +require_once('include/rcmail_template.inc'); // define constannts for input reading @@ -34,7 +35,7 @@ define('RCUBE_INPUT_GPC', 0x0103); function rcmail_startup($task='mail') { global $sess_id, $sess_user_lang; - global $CONFIG, $INSTALL_PATH, $BROWSER, $OUTPUT, $_SESSION, $IMAP, $DB, $JS_OBJECT_NAME; + global $CONFIG, $INSTALL_PATH, $BROWSER, $OUTPUT, $_SESSION, $IMAP, $DB; // check client $BROWSER = rcube_browser(); @@ -103,11 +104,11 @@ function rcmail_startup($task='mail') // load roundcube configuration into global var function rcmail_load_config() { - global $INSTALL_PATH; + global $INSTALL_PATH; // load config file - include_once('config/main.inc.php'); - $conf = is_array($rcmail_config) ? $rcmail_config : array(); + include_once('config/main.inc.php'); + $conf = is_array($rcmail_config) ? $rcmail_config : array(); // load host-specific configuration rcmail_load_host_config($conf); @@ -208,7 +209,7 @@ function rcmail_authenticate_session() // create IMAP object and connect to server function rcmail_imap_init($connect=FALSE) { - global $CONFIG, $DB, $IMAP; + global $CONFIG, $DB, $IMAP, $OUTPUT; $IMAP = new rcube_imap($DB); $IMAP->debug_level = $CONFIG['debug_level']; @@ -219,7 +220,7 @@ function rcmail_imap_init($connect=FALSE) if ($connect) { if (!($conn = $IMAP->connect($_SESSION['imap_host'], $_SESSION['username'], decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']))) - show_message('imaperror', 'error'); + $OUTPUT->show_message('imaperror', 'error'); rcmail_set_imap_prop(); } @@ -360,34 +361,22 @@ function rcube_language_prop($lang, $prop='lang') // init output object for GUI and add common scripts -function load_gui() +function rcmail_load_gui() { - global $CONFIG, $OUTPUT, $COMM_PATH, $JS_OBJECT_NAME, $sess_user_lang; + global $CONFIG, $OUTPUT, $sess_user_lang; // init output page - $OUTPUT = new rcube_html_page(); - - // add common javascripts - $javascript = "var $JS_OBJECT_NAME = new rcube_webmail();\n"; - $javascript .= sprintf("%s.set_env('comm_path', '%s');\n", $JS_OBJECT_NAME, str_replace('&', '&', $COMM_PATH)); + $OUTPUT = new rcmail_template($CONFIG, $GLOBALS['_task']); + $OUTPUT->set_env('comm_path', $GLOBALS['COMM_PATH']); - if (isset($CONFIG['javascript_config'] )){ - foreach ($CONFIG['javascript_config'] as $js_config_var){ - $javascript .= "$JS_OBJECT_NAME.set_env('$js_config_var', '" . $CONFIG[$js_config_var] . "');\n"; - } + if (is_array($CONFIG['javascript_config'])) + { + foreach ($CONFIG['javascript_config'] as $js_config_var) + $OUTPUT->set_env($js_config_var, $CONFIG[$js_config_var]); } - // 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');"; - if (!empty($GLOBALS['_framed'])) - $javascript .= "$JS_OBJECT_NAME.set_env('framed', true);\n"; - - $OUTPUT->add_script($javascript, 'head'); - $OUTPUT->add_script($javascript_foot, 'foot'); - $OUTPUT->include_script('common.js'); - $OUTPUT->include_script('app.js'); - $OUTPUT->scripts_path = 'program/js/'; + $OUTPUT->set_env('framed', true); // set locale setting rcmail_set_locale($sess_user_lang); @@ -395,16 +384,25 @@ function load_gui() // set user-selected charset if (!empty($CONFIG['charset'])) $OUTPUT->set_charset($CONFIG['charset']); + + // register common UI objects + $OUTPUT->add_handlers(array( + 'loginform' => 'rcmail_login_form', + 'username' => 'rcmail_current_username', + 'message' => 'rcmail_message_container', + 'charsetselector' => 'rcmail_charset_selector', + )); // add some basic label to client - rcube_add_label('loading','checkingmail'); + if (!$OUTPUT->ajax_call) + rcube_add_label('loading'); } // set localization charset based on the given language function rcmail_set_locale($lang) { - global $OUTPUT, $CHARSET, $MBSTRING; + global $OUTPUT, $MBSTRING; static $s_mbstring_loaded = NULL; // settings for mbstring module (by Tadashi Jokagi) @@ -414,7 +412,7 @@ function rcmail_set_locale($lang) $MBSTRING = $s_mbstring_loaded = FALSE; if ($MBSTRING) - mb_internal_encoding($CHARSET); + mb_internal_encoding(RCMAIL_CHARSET); $OUTPUT->set_charset(rcube_language_prop($lang, 'charset')); } @@ -749,30 +747,35 @@ function rcmail_save_user_prefs($a_user_prefs) // overwrite action variable function rcmail_overwrite_action($action) { - global $OUTPUT, $JS_OBJECT_NAME; + global $OUTPUT; $GLOBALS['_action'] = $action; - - $OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $action)); + $OUTPUT->set_env('action', $action); } -function show_message($message, $type='notice', $vars=NULL) - { - global $OUTPUT, $JS_OBJECT_NAME, $REMOTE_REQUEST; +// compose a URL to the given action +function rcmail_self_url($action, $p=array(), $task=null) +{ + global $MAIN_TASKS, $COMM_PATH; + $qstring = ''; + $base = $COMM_PATH; - $framed = $GLOBALS['_framed']; - $command = sprintf("display_message('%s', '%s');", - JQ(rcube_label(array('name' => $message, 'vars' => $vars))), - $type); - - if ($REMOTE_REQUEST) - return 'this.'.$command; + if ($task && in_array($task, $MAIN_TASKS)) + $base = ereg_replace('_task=[a-z]+', '_task='.$task, $COMM_PATH); - else - $OUTPUT->add_script(sprintf("%s%s.%s\n", - $framed ? sprintf('if(parent.%s)parent.', $JS_OBJECT_NAME) : '', - $JS_OBJECT_NAME, - $command)); + if (is_array($p)) + foreach ($p as $key => $val) + $qstring .= '&'.urlencode($key).'='.urlencode($val); + + return $base . ($action ? '&_action='.$action : '') . $qstring; +} + + +// @deprecated +function show_message($message, $type='notice', $vars=NULL) + { + global $OUTPUT; + $OUTPUT->show_message($message, $type, $vars); } @@ -808,43 +811,6 @@ function get_des_key() } -// send correct response on a remote request -function rcube_remote_response($js_code, $flush=FALSE) - { - global $OUTPUT, $CHARSET; - static $s_header_sent = FALSE; - - if (!$s_header_sent) - { - $s_header_sent = TRUE; - send_nocacheing_headers(); - header('Content-Type: application/x-javascript; charset='.$CHARSET); - print '/** remote response ['.date('d/M/Y h:i:s O')."] **/\n"; - } - - // send response code - print rcube_charset_convert($js_code, $CHARSET, $OUTPUT->get_charset()); - - if ($flush) // flush the output buffer - flush(); - else // terminate script - exit; - } - - -// send correctly formatted response for a request posted to an iframe -function rcube_iframe_response($js_code='') - { - global $OUTPUT, $JS_OBJECT_NAME; - - if (!empty($js_code)) - $OUTPUT->add_script("if(parent.$JS_OBJECT_NAME){\n" . $js_code . "\n}"); - - $OUTPUT->write(); - exit; - } - - // read directory program/localization/ and return a list of available languages function rcube_list_languages() { @@ -875,14 +841,11 @@ function rcube_list_languages() // add a localized label to the client environment function rcube_add_label() { - global $OUTPUT, $JS_OBJECT_NAME; + global $OUTPUT; $arg_list = func_get_args(); foreach ($arg_list as $i => $name) - $OUTPUT->add_script(sprintf("%s.add_label('%s', '%s');", - $JS_OBJECT_NAME, - $name, - JQ(rcube_label($name)))); + $OUTPUT->command('add_label', $name, rcube_label($name)); } @@ -931,7 +894,7 @@ function rcmail_message_cache_gc() * * @param string Input string * @param string Suspected charset of the input string - * @param string Target charset to convert to; defaults to $GLOBALS['CHARSET'] + * @param string Target charset to convert to; defaults to RCMAIL_CHARSET * @return Converted string */ function rcube_charset_convert($str, $from, $to=NULL) @@ -939,7 +902,7 @@ function rcube_charset_convert($str, $from, $to=NULL) global $MBSTRING; $from = strtoupper($from); - $to = $to==NULL ? strtoupper($GLOBALS['CHARSET']) : strtoupper($to); + $to = $to==NULL ? strtoupper(RCMAIL_CHARSET) : strtoupper($to); if ($from==$to || $str=='' || empty($from)) return $str; @@ -1071,15 +1034,15 @@ function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) if ($enctype=='js') { if ($OUTPUT->get_charset()!='UTF-8') - $str = rcube_charset_convert($str, $GLOBALS['CHARSET'], $OUTPUT->get_charset()); + $str = rcube_charset_convert($str, RCMAIL_CHARSET, $OUTPUT->get_charset()); - return addslashes(preg_replace(array("/\r\n/", "/\r/"), array('\n', '\n'), strtr($str, $js_rep_table))); + return preg_replace(array("/\r?\n/", "/\r/"), array('\n', '\n'), addslashes(strtr($str, $js_rep_table))); } // no encoding given -> return original string return $str; } - + /** * Quote a given string. Alias function for rep_specialchars_output * @see rep_specialchars_output @@ -1160,426 +1123,41 @@ function strip_newlines($str) } -// ************** template parsing and gui functions ************** - - -// return boolean if a specific template exists -function template_exists($name) - { - global $CONFIG, $OUTPUT; - $skin_path = $CONFIG['skin_path']; - - // check template file - return is_file("$skin_path/templates/$name.html"); - } - - -// get page template an replace variable -// similar function as used in nexImage -function parse_template($name='main', $exit=TRUE) - { - global $CONFIG, $OUTPUT; - $skin_path = $CONFIG['skin_path']; - - // read template file - $templ = ''; - $path = "$skin_path/templates/$name.html"; - - if($fp = @fopen($path, 'r')) - { - $templ = fread($fp, filesize($path)); - fclose($fp); - } - else - { - raise_error(array('code' => 500, - 'type' => 'php', - 'line' => __LINE__, - 'file' => __FILE__, - 'message' => "Error loading template for '$name'"), TRUE, TRUE); - return FALSE; - } - - - // parse for specialtags - $output = parse_rcube_xml(parse_rcube_conditions($templ)); - - // add debug console - if ($CONFIG['debug_level'] & 8) - $OUTPUT->footer = '<div style="position:absolute;top:5px;left:5px;width:400px;opacity:0.8;z-index:9000;"><form name="debugform"><textarea name="console" rows="15" cols="40" style="width:400px;border:none;font-size:x-small"></textarea></form>'; - - $OUTPUT->write(trim(parse_with_globals($output)), $skin_path); - - if ($exit) - exit; - } - - - -// replace all strings ($varname) with the content of the according global variable -function parse_with_globals($input) - { - $GLOBALS['__comm_path'] = $GLOBALS['COMM_PATH']; - $output = preg_replace('/\$(__[a-z0-9_\-]+)/e', '$GLOBALS["\\1"]', $input); - return $output; - } - - -// parse conditional code -function parse_rcube_conditions($input) - { - if (($matches = preg_split('/<roundcube:(if|elseif|else|endif)\s+([^>]+)>/is', $input, 2, PREG_SPLIT_DELIM_CAPTURE)) && count($matches)==4) - { - if (preg_match('/^(else|endif)$/i', $matches[1])) - return $matches[0] . parse_rcube_conditions($matches[3]); - else - { - $attrib = parse_attrib_string($matches[2]); - if (isset($attrib['condition'])) - { - $condmet = rcube_xml_condition($attrib['condition']); - $submatches = preg_split('/<roundcube:(elseif|else|endif)\s+([^>]+)>/is', $matches[3], 2, PREG_SPLIT_DELIM_CAPTURE); - - if ($condmet) - $result = $submatches[0] . preg_replace('/.*<roundcube:endif\s+[^>]+>/is', '', $submatches[3]); - else - $result = "<roundcube:$submatches[1] $submatches[2]>" . $submatches[3]; - - return $matches[0] . parse_rcube_conditions($result); - } - else - { - raise_error(array('code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__, - 'message' => "Unable to parse conditional tag " . $matches[2]), TRUE, FALSE); - } - } - } - - return $input; - } - - /** - * Determines if a given condition is met + * Compose an URL for a specific action * - * @return True if condition is valid, False is not - */ -function rcube_xml_condition($condition) - { - $condition = preg_replace( - array('/session:([a-z0-9_]+)/i', '/config:([a-z0-9_]+)/i', '/request:([a-z0-9_]+)/ie'), - array("\$_SESSION['\\1']", "\$GLOBALS['CONFIG']['\\1']", "get_input_value('\\1', RCUBE_INPUT_GPC)"), - $condition); - - return @eval("return (".$condition.");"); - } - - -function parse_rcube_xml($input) - { - $output = preg_replace('/<roundcube:([-_a-z]+)\s+([^>]+)>/Uie', "rcube_xml_command('\\1', '\\2')", $input); - return $output; - } - - -/** - * Convert a xml command tag into real content + * @param string Request action + * @param array More URL parameters + * @return The application URL */ -function rcube_xml_command($command, $str_attrib, $add_attrib=array()) - { - global $IMAP, $CONFIG, $OUTPUT; - - $command = strtolower($command); - $attrib = parse_attrib_string($str_attrib) + $add_attrib; - - // empty output if required condition is not met - if (!empty($attrib['condition']) && !rcube_xml_condition($attrib['condition'])) - return ''; - - // execute command - switch ($command) - { - // return a button - case 'button': - if ($attrib['command']) - return rcube_button($attrib); - break; - - // show a label - case 'label': - if ($attrib['name'] || $attrib['command']) - return Q(rcube_label($attrib + array('vars' => array('product' => $CONFIG['product_name'])))); - break; - - // create a menu item - case 'menu': - if ($attrib['command'] && $attrib['group']) - rcube_menu($attrib); - break; - - // include a file - case 'include': - $path = realpath($CONFIG['skin_path'].$attrib['file']); - - if($fp = @fopen($path, 'r')) - { - $incl = fread($fp, filesize($path)); - fclose($fp); - return parse_rcube_xml($incl); - } - break; - - // return code for a specific application object - case 'object': - $object = strtolower($attrib['name']); - - $object_handlers = array( - // GENERAL - 'loginform' => 'rcmail_login_form', - 'username' => 'rcmail_current_username', - - // MAIL - 'mailboxlist' => 'rcmail_mailbox_list', - 'message' => 'rcmail_message_container', - 'messages' => 'rcmail_message_list', - 'messagecountdisplay' => 'rcmail_messagecount_display', - 'quotadisplay' => 'rcmail_quota_display', - 'messageheaders' => 'rcmail_message_headers', - 'messagebody' => 'rcmail_message_body', - 'messageattachments' => 'rcmail_message_attachments', - 'blockedobjects' => 'rcmail_remote_objects_msg', - 'messagecontentframe' => 'rcmail_messagecontent_frame', - 'messagepartframe' => 'rcmail_message_part_frame', - 'messagepartcontrols' => 'rcmail_message_part_controls', - 'composeheaders' => 'rcmail_compose_headers', - 'composesubject' => 'rcmail_compose_subject', - 'composebody' => 'rcmail_compose_body', - 'composeattachmentlist' => 'rcmail_compose_attachment_list', - 'composeattachmentform' => 'rcmail_compose_attachment_form', - 'composeattachment' => 'rcmail_compose_attachment_field', - 'priorityselector' => 'rcmail_priority_selector', - 'charsetselector' => 'rcmail_charset_selector', - 'editorselector' => 'rcmail_editor_selector', - 'searchform' => 'rcmail_search_form', - 'receiptcheckbox' => 'rcmail_receipt_checkbox', - - // ADDRESS BOOK - 'addresslist' => 'rcmail_contacts_list', - 'addressframe' => 'rcmail_contact_frame', - 'recordscountdisplay' => 'rcmail_rowcount_display', - 'contactdetails' => 'rcmail_contact_details', - 'contacteditform' => 'rcmail_contact_editform', - 'ldappublicsearch' => 'rcmail_ldap_public_search_form', - 'ldappublicaddresslist' => 'rcmail_ldap_public_list', - - // USER SETTINGS - 'userprefs' => 'rcmail_user_prefs_form', - 'itentitieslist' => 'rcmail_identities_list', - 'identityframe' => 'rcmail_identity_frame', - 'identityform' => 'rcube_identity_form', - 'foldersubscription' => 'rcube_subscription_form', - 'createfolder' => 'rcube_create_folder_form', - 'renamefolder' => 'rcube_rename_folder_form', - 'composebody' => 'rcmail_compose_body' - ); - - - // execute object handler function - if ($object_handlers[$object] && function_exists($object_handlers[$object])) - return call_user_func($object_handlers[$object], $attrib); - - else if ($object=='productname') - { - $name = !empty($CONFIG['product_name']) ? $CONFIG['product_name'] : 'RoundCube Webmail'; - return Q($name); - } - else if ($object=='version') - { - return (string)RCMAIL_VERSION; - } - else if ($object=='pagetitle') - { - $task = $GLOBALS['_task']; - $title = !empty($CONFIG['product_name']) ? $CONFIG['product_name'].' :: ' : ''; - - if ($task=='login') - $title = rcube_label(array('name' => 'welcome', 'vars' => array('product' => $CONFIG['product_name']))); - else if ($task=='mail' && isset($GLOBALS['MESSAGE']['subject'])) - $title .= $GLOBALS['MESSAGE']['subject']; - else if (isset($GLOBALS['PAGE_TITLE'])) - $title .= $GLOBALS['PAGE_TITLE']; - else if ($task=='mail' && ($mbox_name = $IMAP->get_mailbox_name())) - $title .= rcube_charset_convert($mbox_name, 'UTF-7', 'UTF-8'); - else - $title .= ucfirst($task); - - return Q($title); - } +function rcmail_url($action, $param=NULL) +{ + $url = $GLOBALS['COMM_PATH'] . '&'.$action; - break; - } + if (is_array($param)) + foreach ($param as $p => $val) + $url .= sprintf('&%s=%s', urlencode($p), urlencode($val)); - return ''; - } + return $url; +} -// create and register a button -function rcube_button($attrib) +// return boolean if a specific template exists +function template_exists($name) { - global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $BROWSER, $COMM_PATH, $MAIN_TASKS; - static $sa_buttons = array(); - static $s_button_count = 100; - - // these commands can be called directly via url - $a_static_commands = array('compose', 'list'); - + global $CONFIG; $skin_path = $CONFIG['skin_path']; - - if (!($attrib['command'] || $attrib['name'])) - return ''; - - // try to find out the button type - if ($attrib['type']) - $attrib['type'] = strtolower($attrib['type']); - else - $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'link'; - - $command = $attrib['command']; - - // take the button from the stack - if($attrib['name'] && $sa_buttons[$attrib['name']]) - $attrib = $sa_buttons[$attrib['name']]; - - // add button to button stack - else if($attrib['image'] || $attrib['imageact'] || $attrib['imagepas'] || $attrib['class']) - { - if(!$attrib['name']) - $attrib['name'] = $command; - - if (!$attrib['image']) - $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact']; - - $sa_buttons[$attrib['name']] = $attrib; - } - - // get saved button for this command/name - else if ($command && $sa_buttons[$command]) - $attrib = $sa_buttons[$command]; - - //else - // return ''; - - - // set border to 0 because of the link arround the button - if ($attrib['type']=='image' && !isset($attrib['border'])) - $attrib['border'] = 0; - - if (!$attrib['id']) - $attrib['id'] = sprintf('rcmbtn%d', $s_button_count++); - - // get localized text for labels and titles - if ($attrib['title']) - $attrib['title'] = Q(rcube_label($attrib['title'])); - if ($attrib['label']) - $attrib['label'] = Q(rcube_label($attrib['label'])); - - if ($attrib['alt']) - $attrib['alt'] = Q(rcube_label($attrib['alt'])); - - // set title to alt attribute for IE browsers - if ($BROWSER['ie'] && $attrib['title'] && !$attrib['alt']) - { - $attrib['alt'] = $attrib['title']; - unset($attrib['title']); - } - - // add empty alt attribute for XHTML compatibility - if (!isset($attrib['alt'])) - $attrib['alt'] = ''; - - - // register button in the system - if ($attrib['command']) - { - $OUTPUT->add_script(sprintf("%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');", - $JS_OBJECT_NAME, - $command, - $attrib['id'], - $attrib['type'], - $attrib['imageact'] ? $skin_path.$attrib['imageact'] : $attrib['classact'], - $attrib['imagesel'] ? $skin_path.$attrib['imagesel'] : $attrib['classsel'], - $attrib['imageover'] ? $skin_path.$attrib['imageover'] : '')); - - // make valid href to specific buttons - if (in_array($attrib['command'], $MAIN_TASKS)) - $attrib['href'] = htmlentities(ereg_replace('_task=[a-z]+', '_task='.$attrib['command'], $COMM_PATH)); - else if (in_array($attrib['command'], $a_static_commands)) - $attrib['href'] = htmlentities($COMM_PATH.'&_action='.$attrib['command']); - } - - // overwrite attributes - if (!$attrib['href']) - $attrib['href'] = '#'; - - if ($command) - $attrib['onclick'] = sprintf("return %s.command('%s','%s',this)", $JS_OBJECT_NAME, $command, $attrib['prop']); - - if ($command && $attrib['imageover']) - { - $attrib['onmouseover'] = sprintf("return %s.button_over('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); - $attrib['onmouseout'] = sprintf("return %s.button_out('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); - } - - if ($command && $attrib['imagesel']) - { - $attrib['onmousedown'] = sprintf("return %s.button_sel('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); - $attrib['onmouseup'] = sprintf("return %s.button_out('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); - } - - $out = ''; - - // generate image tag - if ($attrib['type']=='image') - { - $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'width', 'height', 'border', 'hspace', 'vspace', 'align', 'alt')); - $img_tag = sprintf('<img src="%%s"%s />', $attrib_str); - $btn_content = sprintf($img_tag, $skin_path.$attrib['image']); - if ($attrib['label']) - $btn_content .= ' '.$attrib['label']; - - $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'title'); - } - else if ($attrib['type']=='link') - { - $btn_content = $attrib['label'] ? $attrib['label'] : $attrib['command']; - $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style'); - } - else if ($attrib['type']=='input') - { - $attrib['type'] = 'button'; - - if ($attrib['label']) - $attrib['value'] = $attrib['label']; - - $attrib_str = create_attrib_string($attrib, array('type', 'value', 'onclick', 'id', 'class', 'style')); - $out = sprintf('<input%s disabled />', $attrib_str); - } - - // generate html code for button - if ($btn_content) - { - $attrib_str = create_attrib_string($attrib, $link_attrib); - $out = sprintf('<a%s>%s</a>', $attrib_str, $btn_content); - } - return $out; + // check template file + return is_file("$skin_path/templates/$name.html"); } -function rcube_menu($attrib) +// Wrapper for rcmail_template::parse() +// @deprecated +function parse_template($name='main', $exit=true) { - - return ''; + $GLOBALS['OUTPUT']->parse($name, $exit); } @@ -1685,6 +1263,24 @@ function rcmail_get_edit_field($col, $value, $attrib, $type='text') } +// return the mail domain configured for the given host +function rcmail_mail_domain($host) + { + global $CONFIG; + + $domain = $host; + if (is_array($CONFIG['mail_domain'])) + { + if (isset($CONFIG['mail_domain'][$host])) + $domain = $CONFIG['mail_domain'][$host]; + } + else if (!empty($CONFIG['mail_domain'])) + $domain = $CONFIG['mail_domain']; + + return $domain; + } + + // compose a valid attribute string for HTML tags function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style')) { @@ -1784,13 +1380,23 @@ function format_date($date, $format=NULL) } +function format_email_recipient($email, $name='') + { + if ($name && $name != $email) + return sprintf('%s <%s>', strpos($name, ",") ? '"'.$name.'"' : $name, $email); + else + return $email; + } + + + // ************** functions delivering gui objects ************** function rcmail_message_container($attrib) { - global $OUTPUT, $JS_OBJECT_NAME; + global $OUTPUT; if (!$attrib['id']) $attrib['id'] = 'rcmMessageContainer'; @@ -1799,7 +1405,7 @@ function rcmail_message_container($attrib) $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id')); $out = '<div' . $attrib_str . "></div>"; - $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('message', '$attrib[id]');"); + $OUTPUT->add_gui_object('message', $attrib['id']); return $out; } @@ -1837,28 +1443,10 @@ function rcmail_current_username($attrib) } -// return the mail domain configured for the given host -function rcmail_mail_domain($host) - { - global $CONFIG; - - $domain = $host; - if (is_array($CONFIG['mail_domain'])) - { - if (isset($CONFIG['mail_domain'][$host])) - $domain = $CONFIG['mail_domain'][$host]; - } - else if (!empty($CONFIG['mail_domain'])) - $domain = $CONFIG['mail_domain']; - - return $domain; - } - - // return code for the webmail login form function rcmail_login_form($attrib) { - global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $SESS_HIDDEN_FIELD; + global $CONFIG, $OUTPUT, $SESS_HIDDEN_FIELD; $labels = array(); $labels['user'] = rcube_label('username'); @@ -1911,7 +1499,7 @@ function rcmail_login_form($attrib) EOF; - $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('loginform', '$form_name');"); + $OUTPUT->add_gui_object('loginform', $form_name); $out = <<<EOF $form_start @@ -1972,6 +1560,36 @@ function rcmail_charset_selector($attrib) } +// return code for search function +function rcmail_search_form($attrib) + { + global $OUTPUT; + + // add some labels to client + rcube_add_label('searching'); + + $attrib['name'] = '_q'; + + if (empty($attrib['id'])) + $attrib['id'] = 'rcmqsearchbox'; + + $input_q = new textfield($attrib); + $out = $input_q->show(); + + $OUTPUT->add_gui_object('qsearchbox', $attrib['id']); + + // add form tag around text field + if (empty($attrib['form'])) + $out = sprintf( + '<form name="rcmqsearchform" action="./" '. + 'onsubmit="%s.command(\'search\');return false" style="display:inline;">%s</form>', + JS_OBJECT_NAME, + $out); + + return $out; + } + + /****** debugging functions ********/ diff --git a/program/include/rcmail_template.inc b/program/include/rcmail_template.inc new file mode 100644 index 000000000..e2fa682e4 --- /dev/null +++ b/program/include/rcmail_template.inc @@ -0,0 +1,631 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/include/rcmail_template.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2007, RoundCube Dev. - Switzerland | + | Licensed under the GNU GPL | + | | + | PURPOSE: | + | Class to handle HTML page output using a skin template. | + | Extends rcube_html_page class from rcube_shared.inc | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id: $ + +*/ + +require_once('include/rcube_shared.inc'); + + +class rcmail_template extends rcube_html_page +{ + var $config; + var $task = ''; + var $framed = false; + var $ajax_call = false; + var $pagetitle = ''; + var $env = array(); + var $js_env = array(); + var $js_commands = array(); + var $object_handlers = array(); + + + // PHP 5 constructor + function __construct(&$config, $task) + { + parent::__construct(); + + $this->task = $task; + $this->config = $config; + $this->ajax_call = !empty($_GET['_remote']) || !empty($_POST['_remote']); + + // add common javascripts + if (!$this->ajax_call) + { + $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."');"; + + $this->add_script($javascript, 'head_top'); + $this->add_script($javascript_foot, 'foot'); + $this->scripts_path = 'program/js/'; + $this->include_script('common.js'); + $this->include_script('app.js'); + } + } + + // PHP 4 compatibility + function rcmail_template(&$config, $task) + { + $this->__construct($config, $task); + } + + + /** + * Set environment variable + */ + function set_env($name, $value, $addtojs=true) + { + $this->env[$name] = $value; + if ($addtojs || isset($this->js_env[$name])) + $this->js_env[$name] = $value; + } + + + /** + * Set page title variable + */ + function set_pagetitle($title) + { + $this->pagetitle = $title; + } + + + /** + * Register a template object handler + * + * @param string Object name + * @param string Function name to call + */ + function add_handler($obj, $func) + { + $this->object_handlers[$obj] = $func; + } + + /** + * Register a list of template object handlers + * + * @param array Hash array with object=>handler pairs + */ + function add_handlers($arr) + { + $this->object_handlers = array_merge($this->object_handlers, $arr); + } + + /** + * Register a GUI object to the client script + * + * @param string Object name + * @param string Object ID + */ + function add_gui_object($obj, $id) + { + $this->add_script(JS_OBJECT_NAME.".gui_object('$obj', '$id');"); + } + + + /** + * Call a client method + * + * @param string Method to call + * @param ... Additional arguments + */ + function command() + { + $this->js_commands[] = func_get_args(); + } + + + /** + * Invoke display_message command + */ + function show_message($message, $type='notice', $vars=NULL) + { + $this->command( + 'display_message', + rcube_label(array('name' => $message, 'vars' => $vars)), + $type); + } + + + /** + * Delete all stored env variables and commands + */ + function reset() + { + $this->env = array(); + $this->js_env = array(); + $this->js_commands = array(); + $this->object_handlers = array(); + parent::reset(); + } + + /** + * Send the request output to the client. + * This will either parse a skin tempalte or send an AJAX response + * + * @param string Template name + * @param boolean True if script should terminate (default) + */ + function send($templ=null, $exit=true) + { + if ($this->ajax_call) + $this->remote_response('', !$exit); + else if ($templ != 'iframe') + $this->parse($templ, false); + else + { + $this->framed = $templ == 'iframe' ? true : $this->framed; + $this->write(); + } + + if ($exit) + exit; + } + + + /** + * Send an AJAX response with executable JS code + * + * @param string Additional JS code + * @param boolean True if output buffer should be flushed + */ + function remote_response($add='', $flush=false) + { + static $s_header_sent = FALSE; + + if (!$s_header_sent) + { + $s_header_sent = TRUE; + send_nocacheing_headers(); + header('Content-Type: application/x-javascript; charset='.RCMAIL_CHARSET); + print '/** ajax response ['.date('d/M/Y h:i:s O')."] **/\n"; + } + + // unset default env vars + unset($this->js_env['task'], $this->js_env['action'], $this->js_env['comm_path']); + + // send response code + print rcube_charset_convert($this->get_js_commands() . $add, RCMAIL_CHARSET, $this->get_charset()); + + if ($flush) // flush the output buffer + flush(); + } + + + /** + * @override + */ + function write($template='') + { + // write all env variables to client + $js = $this->framed ? "if(window.parent) {\n" : ''; + $js .= $this->get_js_commands() . ($this->framed ? ' }' : ''); + $this->add_script($js, 'head_top'); + + // call super method + parent::write($template, $this->config['skin_path']); + } + + + /** + * Parse a specific skin template and deliver to stdout + * + * @param string Template name + * @param boolean Exit script + */ + function parse($name='main', $exit=true) + { + $skin_path = $this->config['skin_path']; + + // read template file + $templ = ''; + $path = "$skin_path/templates/$name.html"; + + if($fp = @fopen($path, 'r')) + { + $templ = fread($fp, filesize($path)); + fclose($fp); + } + else + { + raise_error(array( + 'code' => 501, + 'type' => 'php', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Error loading template for '$name'"), TRUE, TRUE); + return FALSE; + } + + // parse for specialtags + $output = $this->parse_xml($this->parse_conditions($templ)); + + // add debug console + if ($this->config['debug_level'] & 8) + $this->add_footer('<div style="position:absolute;top:5px;left:5px;width:400px;padding:0.2em;background:white;opacity:0.8;z-index:9000"> + <a href="#toggle" onclick="con=document.getElementById(\'dbgconsole\');con.style.display=(con.style.display==\'none\'?\'block\':\'none\');return false">console</a> + <form action="/" name="debugform"><textarea name="console" id="dbgconsole" rows="20" cols="40" wrap="off" style="display:none;width:400px;border:none;font-size:x-small"></textarea></form></div>'); + + $this->write(trim($this->parse_with_globals($output)), $skin_path); + + if ($exit) + exit; + } + + + /** + * Return executable javascript code for all registered commands + * @private + */ + function get_js_commands() + { + $out = ''; + if (!$this->framed) + $out .= ($this->ajax_call ? 'this' : JS_OBJECT_NAME) . '.set_env('.json_serialize($this->js_env).");\n"; + + foreach ($this->js_commands as $i => $args) + { + $method = array_shift($args); + foreach ($args as $i => $arg) + $args[$i] = json_serialize($arg); + + $parent = $this->framed || preg_match('/^parent\./', $method); + $out .= sprintf( + "%s.%s(%s);\n", + $this->ajax_call ? 'this' : ($parent ? 'parent.' : '') . JS_OBJECT_NAME, + preg_replace('/^parent\./', '', $method), + join(',', $args)); + } + + return $out; + } + + /** + * Make URLs starting with a slash point to skin directory + */ + function abs_url($str) + { + return preg_replace('/^\//', $this->config['skin_path'].'/', $str); + } + + + + /***** Template parsing methods *****/ + + /** + * Replace all strings ($varname) with the content + * of the according global variable. + */ + function parse_with_globals($input) + { + $GLOBALS['__comm_path'] = $GLOBALS['COMM_PATH']; + return preg_replace('/\$(__[a-z0-9_\-]+)/e', '$GLOBALS["\\1"]', $input); + } + + + /** + * Parse for conditional tags + */ + function parse_conditions($input) + { + if (($matches = preg_split('/<roundcube:(if|elseif|else|endif)\s+([^>]+)>/is', $input, 2, PREG_SPLIT_DELIM_CAPTURE)) && count($matches)==4) + { + if (preg_match('/^(else|endif)$/i', $matches[1])) + return $matches[0] . $this->parse_conditions($matches[3]); + else + { + $attrib = parse_attrib_string($matches[2]); + if (isset($attrib['condition'])) + { + $condmet = $this->check_condition($attrib['condition']); + $submatches = preg_split('/<roundcube:(elseif|else|endif)\s+([^>]+)>/is', $matches[3], 2, PREG_SPLIT_DELIM_CAPTURE); + + if ($condmet) + $result = $submatches[0] . preg_replace('/.*<roundcube:endif\s+[^>]+>/is', '', $submatches[3]); + else + $result = "<roundcube:$submatches[1] $submatches[2]>" . $submatches[3]; + + return $matches[0] . $this->parse_conditions($result); + } + else + { + raise_error(array('code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__, + 'message' => "Unable to parse conditional tag " . $matches[2]), TRUE, FALSE); + } + } + } + + return $input; + } + + + /** + * Determines if a given condition is met + * + * @return True if condition is valid, False is not + */ + function check_condition($condition) + { + $condition = preg_replace( + array('/session:([a-z0-9_]+)/i', '/config:([a-z0-9_]+)/i', '/env:([a-z0-9_]+)/i', '/request:([a-z0-9_]+)/ie'), + array("\$_SESSION['\\1']", "\$this->config['\\1']", "\$this->env['\\1']", "get_input_value('\\1', RCUBE_INPUT_GPC)"), + $condition); + + return @eval("return (".$condition.");"); + } + + + /** + * Search for special tags in input and replace them + * with the appropriate content + * + * @param string Input string to parse + * @return Altered input string + */ + function parse_xml($input) + { + return preg_replace('/<roundcube:([-_a-z]+)\s+([^>]+)>/Uie', "\$this->xml_command('\\1', '\\2')", $input); + } + + + /** + * Convert a xml command tag into real content + * + * @param string Tag command: object,button,label, etc. + * @param string Attribute string + * @return Tag/Object content string + */ + function xml_command($command, $str_attrib, $add_attrib=array()) + { + $command = strtolower($command); + $attrib = parse_attrib_string($str_attrib) + $add_attrib; + + // empty output if required condition is not met + if (!empty($attrib['condition']) && !$this->check_condition($attrib['condition'])) + return ''; + + // execute command + switch ($command) + { + // return a button + case 'button': + if ($attrib['command']) + return $this->button($attrib); + break; + + // show a label + case 'label': + if ($attrib['name'] || $attrib['command']) + return Q(rcube_label($attrib + array('vars' => array('product' => $this->config['product_name'])))); + break; + + // include a file + case 'include': + $path = realpath($this->config['skin_path'].$attrib['file']); + if ($fp = @fopen($path, 'r')) + { + $incl = fread($fp, filesize($path)); + fclose($fp); + return $this->parse_xml($incl); + } + break; + + // return code for a specific application object + case 'object': + $object = strtolower($attrib['name']); + + // execute object handler function + if ($this->object_handlers[$object] && function_exists($this->object_handlers[$object])) + return call_user_func($this->object_handlers[$object], $attrib); + + else if ($object=='productname') + { + $name = !empty($this->config['product_name']) ? $this->config['product_name'] : 'RoundCube Webmail'; + return Q($name); + } + else if ($object=='version') + { + return (string)RCMAIL_VERSION; + } + else if ($object=='pagetitle') + { + $task = $this->task; + $title = !empty($this->config['product_name']) ? $this->config['product_name'].' :: ' : ''; + + if (!empty($this->pagetitle)) + $title .= $this->pagetitle; + else if ($task == 'login') + $title = rcube_label(array('name' => 'welcome', 'vars' => array('product' => $this->config['product_name']))); + else + $title .= ucfirst($task); + + return Q($title); + } + + break; + } + + return ''; + } + + + /** + * Create and register a button + * + * @param array Button attributes + * @return HTML button + */ + function button($attrib) + { + global $CONFIG, $OUTPUT, $BROWSER, $MAIN_TASKS; + static $sa_buttons = array(); + static $s_button_count = 100; + + // these commands can be called directly via url + $a_static_commands = array('compose', 'list'); + + $skin_path = $this->config['skin_path']; + + if (!($attrib['command'] || $attrib['name'])) + return ''; + + // try to find out the button type + if ($attrib['type']) + $attrib['type'] = strtolower($attrib['type']); + else + $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'link'; + + $command = $attrib['command']; + + // take the button from the stack + if($attrib['name'] && $sa_buttons[$attrib['name']]) + $attrib = $sa_buttons[$attrib['name']]; + + // add button to button stack + else if($attrib['image'] || $attrib['imageact'] || $attrib['imagepas'] || $attrib['class']) + { + if (!$attrib['name']) + $attrib['name'] = $command; + + if (!$attrib['image']) + $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact']; + + $sa_buttons[$attrib['name']] = $attrib; + } + + // get saved button for this command/name + else if ($command && $sa_buttons[$command]) + $attrib = $sa_buttons[$command]; + + //else + // return ''; + + + // set border to 0 because of the link arround the button + if ($attrib['type']=='image' && !isset($attrib['border'])) + $attrib['border'] = 0; + + if (!$attrib['id']) + $attrib['id'] = sprintf('rcmbtn%d', $s_button_count++); + + // get localized text for labels and titles + if ($attrib['title']) + $attrib['title'] = Q(rcube_label($attrib['title'])); + if ($attrib['label']) + $attrib['label'] = Q(rcube_label($attrib['label'])); + + if ($attrib['alt']) + $attrib['alt'] = Q(rcube_label($attrib['alt'])); + + // set title to alt attribute for IE browsers + if ($BROWSER['ie'] && $attrib['title'] && !$attrib['alt']) + { + $attrib['alt'] = $attrib['title']; + unset($attrib['title']); + } + + // add empty alt attribute for XHTML compatibility + if (!isset($attrib['alt'])) + $attrib['alt'] = ''; + + + // register button in the system + if ($attrib['command']) + { + $this->add_script(sprintf( + "%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');", + JS_OBJECT_NAME, + $command, + $attrib['id'], + $attrib['type'], + $attrib['imageact'] ? $skin_path.$attrib['imageact'] : $attrib['classact'], + $attrib['imagesel'] ? $skin_path.$attrib['imagesel'] : $attrib['classsel'], + $attrib['imageover'] ? $skin_path.$attrib['imageover'] : '') + ); + + // make valid href to specific buttons + if (in_array($attrib['command'], $MAIN_TASKS)) + $attrib['href'] = Q(rcmail_self_url(null, null, $attrib['command'])); + else if (in_array($attrib['command'], $a_static_commands)) + $attrib['href'] = Q(rcmail_self_url($attrib['command'])); + } + + // overwrite attributes + if (!$attrib['href']) + $attrib['href'] = '#'; + + if ($command) + $attrib['onclick'] = sprintf("return %s.command('%s','%s',this)", JS_OBJECT_NAME, $command, $attrib['prop']); + + if ($command && $attrib['imageover']) + { + $attrib['onmouseover'] = sprintf("return %s.button_over('%s','%s')", JS_OBJECT_NAME, $command, $attrib['id']); + $attrib['onmouseout'] = sprintf("return %s.button_out('%s','%s')", JS_OBJECT_NAME, $command, $attrib['id']); + } + + if ($command && $attrib['imagesel']) + { + $attrib['onmousedown'] = sprintf("return %s.button_sel('%s','%s')", JS_OBJECT_NAME, $command, $attrib['id']); + $attrib['onmouseup'] = sprintf("return %s.button_out('%s','%s')", JS_OBJECT_NAME, $command, $attrib['id']); + } + + $out = ''; + + // generate image tag + if ($attrib['type']=='image') + { + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'width', 'height', 'border', 'hspace', 'vspace', 'align', 'alt')); + $img_tag = sprintf('<img src="%%s"%s />', $attrib_str); + $btn_content = sprintf($img_tag, $skin_path.$attrib['image']); + if ($attrib['label']) + $btn_content .= ' '.$attrib['label']; + + $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'title'); + } + else if ($attrib['type']=='link') + { + $btn_content = $attrib['label'] ? $attrib['label'] : $attrib['command']; + $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style'); + } + else if ($attrib['type']=='input') + { + $attrib['type'] = 'button'; + + if ($attrib['label']) + $attrib['value'] = $attrib['label']; + + $attrib_str = create_attrib_string($attrib, array('type', 'value', 'onclick', 'id', 'class', 'style')); + $out = sprintf('<input%s disabled />', $attrib_str); + } + + // generate html code for button + if ($btn_content) + { + $attrib_str = create_attrib_string($attrib, $link_attrib); + $out = sprintf('<a%s>%s</a>', $attrib_str, $btn_content); + } + + return $out; + } + +} + +?>
\ No newline at end of file diff --git a/program/include/rcube_contacts.inc b/program/include/rcube_contacts.inc new file mode 100644 index 000000000..cc801c6db --- /dev/null +++ b/program/include/rcube_contacts.inc @@ -0,0 +1,429 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/include/rcube_contacts.inc | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2006-2007, RoundCube Dev. - Switzerland | + | Licensed under the GNU GPL | + | | + | PURPOSE: | + | Interface to the local address book database | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ + + $Id: rcube_contacts.inc 328 2006-08-30 17:41:21Z thomasb $ + +*/ + +class rcube_contacts +{ + var $db = null; + var $db_name = ''; + var $user_id = 0; + var $filter = '1'; + var $result = null; + var $search_fields; + var $search_string; + var $table_cols = array('name', 'email', 'firstname', 'surname'); + + /** public properties */ + var $primary_key = 'contact_id'; + var $readonly = false; + var $list_page = 1; + var $page_size = 10; + var $ready = false; + + + /** + * Object constructor + * + * @param object Instance of the rcube_db class + * @param integer User-ID + */ + function __construct($dbconn, $user) + { + $this->db = $dbconn; + $this->db_name = get_table_name('contacts'); + $this->user_id = $user; + $this->ready = $this->db && !$this->db->is_error(); + } + + /** + * PHP 4 object constructor + * + * @see rcube_contacts::__construct + */ + function rcube_contacts($dbconn, $user) + { + $this->__construct($dbconn, $user); + } + + + /** + * Set internal list page + * + * @param number Page number to list + * @access public + */ + function set_page($page) + { + $this->list_page = (int)$page; + } + + + /** + * Set internal page size + * + * @param number Number of messages to display on one page + * @access public + */ + function set_pagesize($size) + { + $this->page_size = (int)$size; + } + + + /** + * Save a search string for future listings + * + * @param string SQL params to use in listing method + */ + function set_search_set($filter) + { + $this->filter = $filter; + } + + + /** + * Getter for saved search properties + * + * @return mixed Search properties used by this class + */ + function get_search_set() + { + return $this->filter; + } + + + /** + * Reset all saved results and search parameters + */ + function reset() + { + $this->result = null; + $this->filter = '1'; + $this->search_fields = null; + $this->search_string = null; + } + + + /** + * List the current set of contact records + * + * @param array List of cols to show + * @return array Indexed list of contact records, each a hash array + */ + function list_records($cols=null, $subset=0) + { + // count contacts for this user + $this->result = $this->count(); + $sql_result = NULL; + + // get contacts from DB + if ($this->result->count) + { + $start_row = $subset < 0 ? $this->result->first + $this->page_size + $subset : $this->result->first; + $length = $subset != 0 ? abs($subset) : $this->page_size; + + $sql_result = $this->db->limitquery( + "SELECT * FROM ".$this->db_name." + WHERE del<>1 + AND user_id=? + AND (".$this->filter.") + ORDER BY name", + $start_row, + $length, + $this->user_id); + } + + while ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) + { + $sql_arr['ID'] = $sql_arr[$this->primary_key]; + // make sure we have a name to display + if (empty($sql_arr['name'])) + $sql_arr['name'] = $sql_arr['email']; + $this->result->add($sql_arr); + } + + return $this->result; + } + + + /** + * Search contacts + * + * @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 + */ + function search($fields, $value, $select=true) + { + if (!is_array($fields)) + $fields = array($fields); + + $add_where = array(); + foreach ($fields as $col) + { + if ($col == 'ID' || $col == $this->primary_key) + { + $ids = !is_array($value) ? split(',', $value) : $value; + $add_where[] = $this->primary_key." IN (".join(',', $ids).")"; + } + else + $add_where[] = $this->db->quoteIdentifier($col)." LIKE ".$this->db->quote(strlen($value)>2 ? "%$value%" : "$value%"); + } + + if (!empty($add_where)) + { + $this->set_search_set(join(' OR ', $add_where)); + if ($select) + $this->list_records(); + else + $this->result = $this->count(); + } + + return $this->result; + } + + + /** + * Count number of available contacts in database + * + * @return Result array with values for 'count' and 'first' + */ + function count() + { + // count contacts for this user + $sql_result = $this->db->query( + "SELECT COUNT(contact_id) AS rows + FROM ".$this->db_name." + WHERE del<>1 + AND user_id=? + AND (".$this->filter.")", + $this->user_id); + + $sql_arr = $this->db->fetch_assoc($sql_result); + return new rcube_result_set($sql_arr['rows'], ($this->list_page-1) * $this->page_size);; + } + + + /** + * Return the last result set + * + * @return Result array or NULL if nothing selected yet + */ + function get_result($as_res=true) + { + return $this->result; + } + + + /** + * Get a specific contact record + * + * @param mixed record identifier(s) + * @return Result object with all record fields or False if not found + */ + function get_record($id, $assoc=false) + { + // return cached result + if ($this->result && ($first = $this->result->first()) && $first[$this->primary_key] == $id) + return $assoc ? $first : $this->result; + + $this->db->query( + "SELECT * FROM ".$this->db_name." + WHERE contact_id=? + AND user_id=? + AND del<>1", + $id, + $this->user_id); + + if ($sql_arr = $this->db->fetch_assoc()) + { + $sql_arr['ID'] = $sql_arr[$this->primary_key]; + $this->result = new rcube_result_set(1); + $this->result->add($sql_arr); + } + + return $assoc && $sql_arr ? $sql_arr : $this->result; + } + + + /** + * Create a new contact record + * + * @param array Assoziative array with save data + * @return The created record ID on success, False on error + */ + function insert($save_data, $check=false) + { + if (is_object($save_data) && is_a($save_data, rcube_result_set)) + return $this->insert_recset($save_data, $check); + + $insert_id = $existing = false; + + if ($check) + $existing = $this->search('email', $save_data['email'], false); + + $a_insert_cols = $a_insert_values = array(); + foreach ($this->table_cols as $col) + if (isset($save_data[$col])) + { + $a_insert_cols[] = $this->db->quoteIdentifier($col); + $a_insert_values[] = $this->db->quote($save_data[$col]); + } + + if (!$existing->count && !empty($a_insert_cols)) + { + $this->db->query( + "INSERT INTO ".$this->db_name." + (user_id, changed, del, ".join(', ', $a_insert_cols).") + VALUES (?, ".$this->db->now().", 0, ".join(', ', $a_insert_values).")", + $this->user_id); + + $insert_id = $this->db->insert_id(get_sequence_name('contacts')); + } + + return $insert_id; + } + + + /** + * Insert new contacts for each row in set + */ + function insert_recset($result, $check=false) + { + $ids = array(); + while ($row = $result->next()) + { + if ($insert = $this->insert($row, $check)) + $ids[] = $insert; + } + return $ids; + } + + + /** + * 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) + { + $updated = false; + $write_sql = array(); + foreach ($this->table_cols as $col) + if (isset($save_cols[$col])) + $write_sql[] = sprintf("%s=%s", $this->db->quoteIdentifier($col), $this->db->quote($save_cols[$col])); + + if (!empty($write_sql)) + { + $this->db->query( + "UPDATE ".$this->db_name." + SET changed=".$this->db->now().", ".join(', ', $write_sql)." + WHERE contact_id=? + AND user_id=? + AND del<>1", + $id, + $this->user_id); + + $updated = $this->db->affected_rows(); + } + + return $updated; + } + + + /** + * Mark one or more contact records as deleted + * + * @param array Record identifiers + */ + function delete($ids) + { + if (is_array($ids)) + $ids = join(',', $ids); + + $this->db->query( + "UPDATE ".$this->db_name." + SET del=1 + WHERE user_id=? + AND contact_id IN (".$ids.")", + $this->user_id); + + return $this->db->affected_rows(); + } + +} + + +/** + * RoundCube result set class. + * Representing an address directory result set. + */ +class rcube_result_set +{ + var $count = 0; + var $first = 0; + var $current = 0; + var $records = array(); + + function __construct($c=0, $f=0) + { + $this->count = (int)$c; + $this->first = (int)$f; + } + + function rcube_result_set($c=0, $f=0) + { + $this->__construct($c, $f); + } + + function add($rec) + { + $this->records[] = $rec; + } + + function iterate() + { + return $this->records[$this->current++]; + } + + function first() + { + $this->current = 0; + return $this->records[$this->current++]; + } + + // alias + function next() + { + return $this->iterate(); + } + + function seek($i) + { + $this->current = $i; + } + +} + + +?>
\ No newline at end of file diff --git a/program/include/rcube_db.inc b/program/include/rcube_db.inc index 6f362a31d..626cb64df 100755 --- a/program/include/rcube_db.inc +++ b/program/include/rcube_db.inc @@ -5,7 +5,7 @@ | program/include/rcube_db.inc | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -102,7 +102,7 @@ class rcube_db $this->db_error = TRUE; $this->db_error_msg = $dbh->getMessage(); - raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, + raise_error(array('code' => 603, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $this->db_error_msg), TRUE, FALSE); return FALSE; diff --git a/program/include/rcube_imap.inc b/program/include/rcube_imap.inc index 3f4f2ebbd..c0016d332 100644 --- a/program/include/rcube_imap.inc +++ b/program/include/rcube_imap.inc @@ -2107,9 +2107,9 @@ class rcube_imap * --------------------------------*/ - function decode_address_list($input, $max=NULL) + function decode_address_list($input, $max=null, $decode=true) { - $a = $this->_parse_address_list($input); + $a = $this->_parse_address_list($input, $decode); $out = array(); if (!is_array($a)) @@ -2146,9 +2146,7 @@ class rcube_imap { $str = $this->decode_mime_string((string)$input); if ($str{0}=='"' && $remove_quotes) - { $str = str_replace('"', '', $str); - } return $str; } @@ -2159,7 +2157,7 @@ class rcube_imap * * @access static */ - function decode_mime_string($input, $recursive=false) + function decode_mime_string($input, $fallback=null) { $out = ''; @@ -2176,13 +2174,13 @@ class rcube_imap $rest = substr($input, $end_pos+2); $out .= rcube_imap::_decode_mime_string_part($encstr); - $out .= rcube_imap::decode_mime_string($rest); + $out .= rcube_imap::decode_mime_string($rest, $fallback); return $out; } - // no encoding information, defaults to what is specified in the class header - return rcube_charset_convert($input, 'ISO-8859-1'); + // no encoding information, use fallback + return rcube_charset_convert($input, !empty($fallback) ? $fallback : 'ISO-8859-1'); } @@ -2473,7 +2471,7 @@ class rcube_imap } - function _parse_address_list($str) + function _parse_address_list($str, $decode=true) { // remove any newlines and carriage returns before $a = $this->_explode_quoted_string('[,;]', preg_replace( "/[\r\n]/", " ", $str)); @@ -2482,7 +2480,7 @@ class rcube_imap foreach ($a as $key => $val) { $val = preg_replace("/([\"\w])</", "$1 <", $val); - $sub_a = $this->_explode_quoted_string(' ', $this->decode_header($val)); + $sub_a = $this->_explode_quoted_string(' ', $decode ? $this->decode_header($val) : $val); $result[$key]['name'] = ''; foreach ($sub_a as $k => $v) diff --git a/program/include/rcube_ldap.inc b/program/include/rcube_ldap.inc index 7cb9dee53..06a99ad0b 100644 --- a/program/include/rcube_ldap.inc +++ b/program/include/rcube_ldap.inc @@ -5,255 +5,432 @@ | program/include/rcube_ldap.inc | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Copyright (C) 2006-2007, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | PURPOSE: | - | Manage an LDAP connection | + | Interface to an LDAP address directory | | | +-----------------------------------------------------------------------+ - | Author: Jeremy Jongsma <jeremy@jongsma.org> | + | Author: Thomas Bruederli <roundcube@gmail.com> | +-----------------------------------------------------------------------+ $Id$ */ -require_once("bugs.inc"); - class rcube_ldap - { +{ var $conn; - var $host; - var $port; - var $protocol; - var $base_dn; - var $bind_dn; - var $bind_pass; - - // PHP 5 constructor - function __construct() - { - } + var $prop = array(); + var $fieldmap = array(); + + var $filter = ''; + var $result = null; + var $ldap_result = null; + var $sort_col = ''; + + /** public properties */ + var $primary_key = 'ID'; + var $readonly = true; + var $list_page = 1; + var $page_size = 10; + var $ready = false; + + + /** + * Object constructor + * + * @param array LDAP connection properties + * @param integer User-ID + */ + function __construct($p) + { + $this->prop = $p; + + foreach ($p as $prop => $value) + if (preg_match('/^(.+)_field$/', $prop, $matches)) + $this->fieldmap[$matches[1]] = $value; + + // $this->filter = "(dn=*)"; + $this->connect(); + } - // PHP 4 constructor - function rcube_ldap() - { - $this->__construct(); - } + /** + * PHP 4 object constructor + * + * @see rcube_ldap::__construct + */ + function rcube_ldap($p) + { + $this->__construct($p); + } + - function connect($hosts, $port=389, $protocol=3) - { + /** + * Establish a connection to the LDAP server + */ + function connect() + { if (!function_exists('ldap_connect')) - raise_error(array("type" => "ldap", - "message" => "No ldap support in this installation of php."), - TRUE); + raise_error(array('type' => 'ldap', 'message' => "No ldap support in this installation of PHP"), true); if (is_resource($this->conn)) - return TRUE; + return true; - if (!is_array($hosts)) - $hosts = array($hosts); + if (!is_array($this->prop['hosts'])) + $this->prop['hosts'] = array($this->prop['hosts']); - foreach ($hosts as $host) + foreach ($this->prop['hosts'] as $host) + { + if ($lc = @ldap_connect($host, $this->prop['port'])) { - if ($lc = @ldap_connect($host, $port)) - { - @ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $protocol); - $this->host = $host; - $this->port = $port; - $this->protocol = $protocol; + ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $this->prop['port']); + $this->prop['host'] = $host; $this->conn = $lc; - return TRUE; - } + break; } - - if (!is_resource($this->conn)) - raise_error(array("type" => "ldap", - "message" => "Could not connect to any LDAP server, tried $host:$port last"), - TRUE); } + + if (is_resource($this->conn)) + $this->ready = true; + else + raise_error(array('type' => 'ldap', 'message' => "Could not connect to any LDAP server, tried $host:{$this->prop[port]} last"), true); + } - function close() - { - if ($this->conn) - { - if (@ldap_unbind($this->conn)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Could not close connection to LDAP server: ".ldap_error($this->conn)), - TRUE); - } - return FALSE; - } - // Merge with connect()? + /** + * Merge with connect()? + */ function bind($dn=null, $pass=null) - { + { if ($this->conn) - { + { if ($dn) + { if (@ldap_bind($this->conn, $dn, $pass)) - return TRUE; + return true; else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Bind failed for dn=$dn: ".ldap_error($this->conn)), - TRUE); + raise_error(array('code' => ldap_errno($this->conn), + 'type' => 'ldap', + 'message' => "Bind failed for dn=$dn: ".ldap_error($this->conn)), + true); + } else + { if (@ldap_bind($this->conn)) - return TRUE; + return true; else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Anonymous bind failed: ".ldap_error($this->conn)), - TRUE); - } + raise_error(array('code' => ldap_errno($this->conn), + 'type' => 'ldap', + 'message' => "Anonymous bind failed: ".ldap_error($this->conn)), + true); + } + } else - raise_error(array("type" => "ldap", - "message" => "Attempted bind on nonexistent connection"), TRUE); - return FALSE; + raise_error(array('type' => 'ldap', 'message' => "Attempted bind on nonexistent connection"), true); + + return false; } - function count($base, $filter=null, $attributes=null, $scope="sub") - { + + /** + * Close connection to LDAP server + */ + function close() + { if ($this->conn) - { - if ($scope === 'sub') - $sr = @ldap_search($this->conn, $base, $filter, $attributes, 0, $limit); - else if ($scope === 'one') - $sr = @ldap_list($this->conn, $base, $filter, $attributes, 0, $limit); - else if ($scope === 'base') - $sr = @ldap_read($this->conn, $base, $filter, $attributes, 0, $limit); - if ($sr) - return @ldap_count_entries($this->conn, $sr); - } - else - raise_error(array("type" => "ldap", - "message" => "Attempted count search on nonexistent connection"), TRUE); - return FALSE; + @ldap_unbind($this->conn); + } + + + /** + * Set internal list page + * + * @param number Page number to list + * @access public + */ + function set_page($page) + { + $this->list_page = (int)$page; + } + + + /** + * Set internal page size + * + * @param number Number of messages to display on one page + * @access public + */ + function set_pagesize($size) + { + $this->page_size = (int)$size; + } + + + /** + * Save a search string for future listings + * + * @param string ?? + */ + function set_search_set($filter) + { + $this->filter = $filter; + } + + + /** + * Getter for saved search properties + * + * @return mixed Search properties used by this class + */ + function get_search_set() + { + return $this->filter; + } + + + /** + * Reset all saved results and search parameters + */ + function reset() + { + $this->result = null; + $this->ldap_result = null; + $this->filter = ''; + } + + + /** + * List the current set of contact records + * + * @param array List of cols to show + * @return array Indexed list of contact records, each a hash array + */ + function list_records($cols=null, $subset=0) + { + // exec LDAP search if no result resource is stored + if ($this->conn && !$this->ldap_result) + $this->_exec_search(); + + // count contacts for this user + $this->result = $this->count(); + + // we have a search result resource + if ($this->ldap_result && $this->result->count > 0) + { + if ($this->sort_col && $this->prop['scope'] !== "base") + @ldap_sort($this->conn, $this->ldap_result, $this->sort_col); + + $entries = ldap_get_entries($this->conn, $this->ldap_result); + for ($i = $this->result->first; $i < min($entries['count'], $this->result->first + $this->page_size); $i++) + $this->result->add($this->_ldap2result($entries[$i])); } - function search($base, $filter=null, $attributes=null, $scope='sub', $sort=null, $limit=0) + return $this->result; + } + + + /** + * Search contacts + * + * @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 + */ + function search($fields, $value, $select=true) + { + // special treatment for ID-based search + if ($fields == 'ID' || $fields == $this->primary_key) { - if ($this->conn) - { - if ($scope === 'sub') - $sr = @ldap_search($this->conn, $base, $filter, $attributes, 0, $limit); - else if ($scope === 'one') - $sr = @ldap_list($this->conn, $base, $filter, $attributes, 0, $limit); - else if ($scope === 'base') - $sr = @ldap_read($this->conn, $base, $filter, $attributes, 0, $limit); - if ($sr) + $ids = explode(',', $value); + $result = new rcube_result_set(); + foreach ($ids as $id) + if ($rec = $this->get_record($id, true)) { - if ($sort && $scope !== "base") - { - if (is_array($sort)) - { - // Start from the end so first sort field has highest priority - $sortfields = array_reverse($sort); - foreach ($sortfields as $sortfield) - @ldap_sort($this->conn, $sr, $sortfield); - } - else - @ldap_sort($this->conn, $sr, $sort); - } - return @ldap_get_entries($this->conn, $sr); + $result->add($rec); + $result->count++; } - } - else - raise_error(array("type" => "ldap", - "message" => "Attempted search on nonexistent connection"), TRUE); - return FALSE; + + return $result; } - - function add($dn, $object) + + $filter = '(|'; + $wc = $this->prop['fuzzy_search'] ? '*' : ''; + if (is_array($this->prop['search_fields'])) { - if ($this->conn) - { - if (@ldap_add($this->conn, $dn, $object)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Add object failed: ".ldap_error($this->conn)), - TRUE); - } + foreach ($this->prop['search_fields'] as $k => $field) + $filter .= "($field=$wc" . rcube_ldap::quote_string($value) . "$wc)"; + } else - raise_error(array("type" => "ldap", - "message" => "Add object faile: no connection"), - TRUE); - return FALSE; + { + foreach ((array)$fields as $field) + if ($f = $this->_map_field($field)) + $filter .= "($f=$wc" . rcube_ldap::quote_string($value) . "$wc)"; } + $filter .= ')'; - function modify($dn, $object) - { - if ($this->conn) - { - if (@ldap_modify($this->conn, $dn, $object)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Modify object failed: ".ldap_error($this->conn)), - TRUE); - } + // set filter string and execute search + $this->set_search_set($filter); + $this->_exec_search(); + + if ($select) + $this->list_records(); else - raise_error(array("type" => "ldap", - "message" => "Modify object failed: no connection"), - TRUE); - return FALSE; - } + $this->result = $this->count(); + + return $this->result; + } - function rename($dn, $newrdn, $parentdn) - { - if ($this->protocol < 3) - { - raise_error(array("type" => "ldap", - "message" => "rename() support requires LDAPv3 or above "), - TRUE); - return FALSE; - } - if ($this->conn) + /** + * Count number of available contacts in database + * + * @return Result array with values for 'count' and 'first' + */ + function count() + { + $count = 0; + if ($this->conn && $this->ldap_result) + $count = ldap_count_entries($this->conn, $this->ldap_result); + + return new rcube_result_set($count, ($this->list_page-1) * $this->page_size); + } + + + /** + * Return the last result set + * + * @return Result array or NULL if nothing selected yet + */ + function get_result() + { + return $this->result; + } + + + /** + * Get a specific contact record + * + * @param mixed record identifier + * @return Hash array with all record fields or False if not found + */ + function get_record($dn, $assoc=false) + { + $res = null; + if ($this->conn && $dn) + { + $this->ldap_result = @ldap_read($this->conn, base64_decode($dn), "(objectclass=*)", array_values($this->fieldmap)); + $entry = @ldap_first_entry($this->conn, $this->ldap_result); + + if ($entry && ($rec = ldap_get_attributes($this->conn, $entry))) { - if (@ldap_rename($this->conn, $dn, $newrdn, $parentdn, TRUE)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Rename object failed: ".ldap_error($this->conn)), - TRUE); + $res = $this->_ldap2result($rec); + $this->result = new rcube_result_set(1); + $this->result->add($res); } - else - raise_error(array("type" => "ldap", - "message" => "Rename object failed: no connection"), - TRUE); - return FALSE; } - function delete($dn) + return $assoc ? $res : $this->result; + } + + + /** + * Create a new contact record + * + * @param array Assoziative array with save data + * @return The create record ID on success, False on error + */ + function insert($save_cols) + { + // TODO + return false; + } + + + /** + * 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) + { + // TODO + return false; + } + + + /** + * Mark one or more contact records as deleted + * + * @param array Record identifiers + */ + function delete($ids) + { + // TODO + return false; + } + + + /** + * Execute the LDAP search based on the stored credentials + * + * @private + */ + function _exec_search() + { + if ($this->conn && $this->filter) { - if ($this->conn) - { - if (@ldap_delete($this->conn, $dn)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Delete object failed: ".ldap_error($this->conn)), - TRUE); - } + $function = $this->prop['scope'] == 'sub' ? 'ldap_search' : ($this->prop['scope'] == 'base' ? 'ldap_read' : 'ldap_list'); + $this->ldap_result = @$function($this->conn, $this->prop['base_dn'], $this->filter, array_values($this->fieldmap), 0, 0); + return true; + } else - raise_error(array("type" => "ldap", - "message" => "Delete object failed: no connection"), - TRUE); - return FALSE; + return false; + } + + + /** + * @private + */ + function _ldap2result($rec) + { + $out = array(); + + if ($rec['dn']) + $out[$this->primary_key] = base64_encode($rec['dn']); + + foreach ($this->fieldmap as $rf => $lf) + { + if ($rec[$lf]['count']) + $out[$rf] = $rec[$lf][0]; } - + + return $out; + } + + + /** + * @private + */ + function _map_field($field) + { + return $this->fieldmap[$field]; } + + + /** + * @static + */ + function quote_string($str) + { + return strtr($str, array('*'=>'\2a', '('=>'\28', ')'=>'\29', '\\'=>'\5c')); + } + + +} -// vi: et ts=2 sw=2 -?> +?>
\ No newline at end of file diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc index 0d502f81c..0f8be06c2 100644 --- a/program/include/rcube_shared.inc +++ b/program/include/rcube_shared.inc @@ -5,7 +5,7 @@ | rcube_shared.inc | | | | This file is part of the RoundCube PHP suite | - | Copyright (C) 2005-2006, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | CONTENTS: | @@ -84,11 +84,20 @@ class rcube_html_page function add_script($script, $position='head') { if (!isset($this->scripts[$position])) - $this->scripts[$position] = "\n$script"; + $this->scripts[$position] = "\n".rtrim($script); else - $this->scripts[$position] .= "\n$script"; + $this->scripts[$position] .= "\n".rtrim($script); } + function add_header($str) + { + $this->header .= "\n".$str; + } + + function add_footer($str) + { + $this->footer .= "\n".$str; + } function set_title($t) { @@ -121,6 +130,8 @@ class rcube_html_page $this->script_files = array(); $this->scripts = array(); $this->title = ''; + $this->header = ''; + $this->footer = ''; } @@ -150,39 +161,37 @@ class rcube_html_page foreach ($this->script_files['head'] as $file) $__page_header .= sprintf($this->script_tag_file, $this->scripts_path, $file); - if (is_array($this->external_scripts['head'])) - { + if (is_array($this->external_scripts['head'])) foreach ($this->external_scripts['head'] as $xscript) - { - $__page_header .= sprintf($this->tag_format_external_script, $xscript); - } - } + $__page_header .= sprintf($this->tag_format_external_script, $xscript); + + $head_script = $this->scripts['head_top'] . $this->scripts['head']; + if (!empty($head_script)) + $__page_header .= sprintf($this->script_tag, $head_script); + + if (!empty($this->header)) + $__page_header .= $this->header; - if (!empty($this->scripts['head'])) - $__page_header .= sprintf($this->script_tag, $this->scripts['head']); - 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); - } if (!empty($this->scripts['foot'])) $__page_footer .= sprintf($this->script_tag, $this->scripts['foot']); - if ($this->footer) - $__page_footer .= "\n" . $this->footer; + if (!empty($this->footer)) + $__page_footer .= $this->footer; $__page_header .= $this->css->show(); // find page header - if($hpos = rc_strpos(rc_strtolower($output), '</head>')) + if($hpos = strpos(strtolower($output), '</head>')) $__page_header .= "\n"; else { if (!is_numeric($hpos)) - $hpos = rc_strpos(rc_strtolower($output), '<body'); - if (!is_numeric($hpos) && ($hpos = rc_strpos(rc_strtolower($output), '<html'))) + $hpos = strpos(strtolower($output), '<body'); + if (!is_numeric($hpos) && ($hpos = strpos(strtolower($output), '<html'))) { while($output[$hpos]!='>') $hpos++; @@ -194,27 +203,27 @@ class rcube_html_page // add page hader if($hpos) - $output = rc_substr($output,0,$hpos) . $__page_header . rc_substr($output,$hpos,rc_strlen($output)); + $output = substr($output,0,$hpos) . $__page_header . substr($output,$hpos,strlen($output)); else $output = $__page_header . $output; // find page body - if($bpos = rc_strpos(rc_strtolower($output), '<body')) + if($bpos = strpos(strtolower($output), '<body')) { while($output[$bpos]!='>') $bpos++; $bpos++; } else - $bpos = rc_strpos(rc_strtolower($output), '</head>')+7; + $bpos = strpos(strtolower($output), '</head>')+7; // add page body if($bpos && $__page_body) - $output = rc_substr($output,0,$bpos) . "\n$__page_body\n" . rc_substr($output,$bpos,rc_strlen($output)); + $output = substr($output,0,$bpos) . "\n$__page_body\n" . substr($output,$bpos,strlen($output)); // find and add page footer - $output_lc = rc_strtolower($output); + $output_lc = strtolower($output); if(($fpos = strrstr($output_lc, '</body>')) || ($fpos = strrstr($output_lc, '</html>'))) $output = substr($output, 0, $fpos) . "$__page_footer\n" . substr($output, $fpos); @@ -1016,10 +1025,10 @@ class select extends base_form_element in_array($option['value'], $select, TRUE)) || (in_array($option['text'], $select, TRUE))) ? $this->_conv_case(' selected', 'attrib') : ''; - + $options_str .= sprintf("<%s%s%s>%s</%s>\n", $this->_conv_case('option', 'tag'), - isset($option['value']) ? sprintf($value_str, $option['value']) : '', + !empty($option['value']) ? sprintf($value_str, Q($option['value'])) : '', $selected, Q($option['text'], 'strict', FALSE), $this->_conv_case('option', 'tag')); @@ -1258,89 +1267,71 @@ function send_modified_header($mdate, $etag=null) } -// function to convert an array to a javascript array -function array2js($arr, $type='') +/** + * Convert a variable into a javascript notation string + */ +function json_serialize($var) { - if (!$type) - $type = 'mixed'; + if (is_object($var)) + $var = get_object_vars($var); - if (is_array($arr)) + if (is_array($var)) { - // no items in array - if (!sizeof($arr)) - return 'new Array()'; - else + // empty array + if (!sizeof($var)) + return '[]'; + else { - $a_pairs = array(); - $keys_arr = array_keys($arr); - $is_assoc = $have_numeric = 0; + $keys_arr = array_keys($var); + $is_assoc = $have_numeric = 0; - for ($i=0; $i<sizeof($keys_arr); ++$i) + for ($i=0; $i<sizeof($keys_arr); ++$i) { - if(is_numeric($keys_arr[$i])) - $have_numeric = 1; - if (!is_numeric($keys_arr[$i]) || $keys_arr[$i]!=$i) - $is_assoc = 1; - if($is_assoc && $have_numeric) - break; + if (is_numeric($keys_arr[$i])) + $have_numeric = 1; + if (!is_numeric($keys_arr[$i]) || $keys_arr[$i] != $i) + $is_assoc = 1; + if ($is_assoc && $have_numeric) + break; } + + $brackets = $is_assoc ? '{}' : '[]'; + $pairs = array(); - $previous_was_array = false; - while (list($key, $value) = each($arr)) + foreach ($var as $key => $value) { - // enclose key with quotes if it is not variable-name conform - if (!ereg("^[_a-zA-Z]{1}[_a-zA-Z0-9]*$", $key) /* || is_js_reserved_word($key) */) - $key = "'$key'"; + // enclose key with quotes if it is not variable-name conform + if (!ereg("^[_a-zA-Z]{1}[_a-zA-Z0-9]*$", $key) /* || is_js_reserved_word($key) */) + $key = "'$key'"; - if (!is_array($value) && is_string($value)) - { - $value = str_replace("\r\n", '\n', $value); - $value = str_replace("\n", '\n', $value); - } - - $is_string = false; - if (!is_array($value)) - { - if ($type=='string') - $is_string = true; - else if (($type == 'mixed' && is_bool($value)) || $type == 'bool') - { - $is_string = false; - $value = $value ? "true" : "false"; - } - else if ((($type=='mixed' && is_numeric($value)) || $type=='int') && rc_strlen($value)<16) // js interprets numbers with digits >15 as ...e+... - $is_string = FALSE; - else - $is_string = TRUE; - } - - if ($is_string) - $value = "'".preg_replace("/(?<!\\\)'/", "\'", $value)."'"; - - $a_pairs[] = sprintf("%s%s", - $is_assoc ? "$key:" : '', - is_array($value) ? array2js($value, $type) : $value); - } - - if ($a_pairs) - { - if ($is_assoc) - $return = '{'.implode(',', $a_pairs).'}'; - else - $return = '['.implode(',', $a_pairs).']'; + $pairs[] = sprintf("%s%s", $is_assoc ? "$key:" : '', json_serialize($value)); } - return $return; + return $brackets{0} . implode(',', $pairs) . $brackets{1}; } } - else - { - return $arr; - } + else if (is_numeric($var) && strval(intval($var)) === strval($var)) + return $var; + else if (is_bool($var)) + return $var ? '1' : '0'; + else + return "'".JQ($var)."'"; + + } + +/** + * function to convert an array to a javascript array + * @deprecated + */ +function array2js($arr, $type='') + { + return json_serialize($arr); } -// similar function as in_array() ut case-insensitive +/** + * Similar function as in_array() but case-insensitive + */ function in_array_nocase($needle, $haystack) { foreach ($haystack as $value) @@ -1353,8 +1344,9 @@ function in_array_nocase($needle, $haystack) } - -// find out if the string content means TRUE or FALSE +/** + * Find out if the string content means TRUE or FALSE + */ function get_boolean($str) { $str = strtolower($str); @@ -1469,7 +1461,7 @@ function rc_strtolower($str) } // wrapper function for substr -function rc_substr($str, $start, $len) +function rc_substr($str, $start, $len=null) { if (function_exists('mb_substr')) return mb_substr($str, $start, $len); |