From 1d963646f5f0626a010a81e7eb7a362a8465959c Mon Sep 17 00:00:00 2001 From: Klaus Leithoff Date: Tue, 21 May 2013 08:46:54 +0000 Subject: [PATCH] attempt to better control preview area; implementation of addFolder (action on branch still missing); basic search functionality --- mail/inc/class.mail_bo.inc.php | 41 ++++++++++++ mail/inc/class.mail_ui.inc.php | 87 ++++++++++++++++++++++--- mail/js/app.js | 116 ++++++++++++++++++++++++++++++--- 3 files changed, 225 insertions(+), 19 deletions(-) diff --git a/mail/inc/class.mail_bo.inc.php b/mail/inc/class.mail_bo.inc.php index 1e7323f7f7..87c4f7e2c6 100644 --- a/mail/inc/class.mail_bo.inc.php +++ b/mail/inc/class.mail_bo.inc.php @@ -1596,6 +1596,47 @@ class mail_bo #return translation::convert($_folderName, self::$displayCharset, 'UTF7-IMAP'); } + /** + * create a new folder under given parent folder + * + * @param string _parent the parent foldername + * @param string _folderName the new foldername + * @param bool _subscribe subscribe to the new folder + * + * @return mixed name of the newly created folder or false on error + */ + function createFolder($_parent, $_folderName, $_subscribe=false) + { + if (self::$debug) error_log(__METHOD__.__LINE__."->"."$_parent, $_folderName, $_subscribe"); + $parent = $this->_encodeFolderName($_parent); + $folderName = $this->_encodeFolderName($_folderName); + + if(empty($parent)) { + $newFolderName = $folderName; + } else { + $HierarchyDelimiter = $this->getHierarchyDelimiter(); + $newFolderName = $parent . $HierarchyDelimiter . $folderName; + } + if (self::$debug) error_log(__METHOD__.__LINE__.'->'.$newFolderName); + if (self::folderExists($newFolderName,true)) + { + error_log(__METHOD__.__LINE__." Folder $newFolderName already exists."); + return $newFolderName; + } + $rv = $this->icServer->createMailbox($newFolderName); + if ( PEAR::isError($rv ) ) { + error_log(__METHOD__.__LINE__.' create Folder '.$newFolderName.'->'.$rv->message.' Namespace:'.array2string($this->icServer->getNameSpaces())); + return false; + } + $srv = $this->icServer->subscribeMailbox($newFolderName); + if ( PEAR::isError($srv ) ) { + error_log(__METHOD__.__LINE__.' subscribe to new folder '.$newFolderName.'->'.$srv->message); + return false; + } + + return $newFolderName; + } + /** * rename a folder * diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index 4f74990960..812b5a9095 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -211,13 +211,18 @@ class mail_ui 'type' => 'drop', ), // Tree does support this one + 'add' => array( + 'caption' => 'Add Folder', + 'type' => 'popup', + 'onExecute' => 'javaScript:app.mail.mail_AddFolder' + ), 'rename' => array( - 'caption' => 'Rename', + 'caption' => 'Rename Folder', 'type' => 'popup', 'onExecute' => 'javaScript:app.mail.mail_RenameFolder' ), 'delete' => array( - 'caption' => 'Delete', + 'caption' => 'Delete Folder', 'type' => 'popup', 'onExecute' => 'javaScript:app.mail.mail_DeleteFolder' ) @@ -374,13 +379,15 @@ class mail_ui * * @param string $_GET[id] if of node whos children are requested */ - public function ajax_foldertree() + public function ajax_foldertree($_nodeID = null) { $nodeID = $_GET['id']; + if (!is_null($_nodeID)) $nodeID = $_nodeID; //error_log(__METHOD__.__LINE__.'->'.array2string($_REQUEST)); //error_log(__METHOD__.__LINE__.'->'.array2string($_GET)); - $data = $this->getFolderTree(false, $nodeID); - + $data = $this->getFolderTree(!is_null($_nodeID), $nodeID); + //error_log(__METHOD__.__LINE__.':'.$nodeID.'->'.array2string($data)); + if (!is_null($_nodeID)) return $data; header('Content-Type: application/json; charset=utf-8'); echo json_encode($data); common::egw_exit(); @@ -513,7 +520,7 @@ class mail_ui if ($_nodeID) { $node = self::findNode($out,$_nodeID); - //error_log(__METHOD__.__LINE__.array2string($node)); + error_log(__METHOD__.__LINE__.':'.$_nodeID.'->'.array2string($node)); return $node; } return ($c?$out:array('id'=>0, 'item'=>array('text'=>'INBOX','tooltip'=>'INBOX'.' '.lang('(not connected)'),'im0'=>'kfm_home.png'))); @@ -522,7 +529,7 @@ class mail_ui /** * findNode - helper function to return only a branch of the tree * - * @param array &$out, out array (to be processed) + * @param array $out, out array (to be searched) * @param string $_nodeID, node to search for * @param boolean $childElements=true return node itself, or only its child items * @return array structured subtree @@ -531,10 +538,16 @@ class mail_ui { foreach($_out['item'] as $node) { - if ($node['id']==$_nodeID) + //error_log(__METHOD__.__LINE__.':'.$_nodeID.'->'.$node['id']); + if ($node['id']===$_nodeID) { return ($childElements?$node['item']:$node); } + elseif (is_array($node['item']) && strncmp($node['id'],$_nodeID,strlen($node['id']))===0 && strlen($_nodeID)>strlen($node['id'])) + { + //error_log(__METHOD__.__LINE__.' descend into '.$node['id']); + return self::findNode($node,$_nodeID,$childElements); + } } } @@ -978,7 +991,15 @@ unset($query['actions']); $offset = $query['start']+1; // we always start with 1 $maxMessages = $query['num_rows']; $sort = $query['order']; - $filter = array(); + if (!empty($query['search'])) + { + //([filterName] => Schnellsuche[type] => quick[string] => ebay[status] => any + $filter = array('filterName' => lang('quicksearch'),'type' => 'quick','string' => $query['search'],'status' => 'any'); + } + else + { + $filter = array(); + } $reverse = ($query['order']=='ASC'?false:true); //error_log(__METHOD__.__LINE__.' maxMessages:'.$maxMessages.' Offset:'.$offset.' Filter:'.array2string($this->sessionData['messageFilter'])); if ($maxMessages > 75) @@ -2190,6 +2211,51 @@ blockquote[type=cite] { } } + /** + * ajax_addFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax) + * @param string $_parentFolderName folder to add a folder to + * @param string $_newName new foldername + * @return nothing + */ + function ajax_addFolder($_parentFolderName, $_newName) + { + error_log(__METHOD__.__LINE__.' ParentFolderName:'.array2string($_parentFolderName).' NewName/Folder:'.array2string($_newName)); + if ($_parentFolderName) + { + $created = false; + $decodedFolderName = $this->mail_bo->decodeEntityFolderName($_parentFolderName); + $_newName = translation::convert($this->mail_bo->decodeEntityFolderName($_newName), $this->charset, 'UTF7-IMAP'); + $del = $this->mail_bo->getHierarchyDelimiter(false); + list($profileID,$parentFolderName) = explode(self::$delimiter,$decodedFolderName,2); + if (is_numeric($profileID)) + { + if ($profileID != $this->mail_bo->profileID) return; // only current connection + $pA = explode($del,$parentFolderName); + array_pop($pA); + $parentFolder = implode($del,$pA); + //if (strtoupper($parentFolderName)!= 'INBOX') + { + //error_log(__METHOD__.__LINE__."$folderName, $parentFolder, $_newName"); + $oldFolderInfo = $this->mail_bo->getFolderStatus($parentFolderName,false); + //error_log(__METHOD__.__LINE__.array2string($oldFolderInfo)); + + $this->mail_bo->reopen('INBOX'); + if($newFolderName = $this->mail_bo->createFolder($parentFolderName, $_newName, true)) { + $this->mail_bo->resetFolderObjectCache($profileID); + $created=true; + } + $this->mail_bo->reopen($parentFolderName); + } + } + //error_log(__METHOD__.__LINE__.array2string($oA)); + if ($created===true) + { + $response = egw_json_response::get(); + $response->call('app.mail.mail_reloadNode',array($_parentFolderName=>$oldFolderInfo['shortDisplayName']),'mail'); + } + } + } + /** * ajax_renameFolder - its called via json, so the function must start with ajax (or the class-name must contain ajax) * @param string $_folderName folder to rename and refresh @@ -2316,7 +2382,7 @@ blockquote[type=cite] { */ function ajax_deleteFolder($_folderName) { - error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName)); + //error_log(__METHOD__.__LINE__.' OldFolderName:'.array2string($_folderName)); $success = false; if ($_folderName) { @@ -2364,6 +2430,7 @@ blockquote[type=cite] { $response = egw_json_response::get(); if ($success) { + //error_log(__METHOD__.__LINE__.array2string($oA)); $response->call('app.mail.mail_removeLeaf',$oA,'mail'); } else diff --git a/mail/js/app.js b/mail/js/app.js index b1ad62e708..3d8589c575 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -17,6 +17,9 @@ app.mail = AppJS.extend( mail_refreshTimeOut: 1000*60*3, // initial call mail_queuedFolders: [], mail_queuedFoldersIndex: 0, + + mail_selectedMails: [], + mail_currentlyFocussed: '', /** * Initialize javascript for this application @@ -24,7 +27,6 @@ app.mail = AppJS.extend( * @memberOf app.mail */ init: function() { - this._super.apply(this,arguments); window.register_app_refresh("mail", this.app_refresh); @@ -34,6 +36,35 @@ app.mail = AppJS.extend( window.setTimeout(function() {self.mail_refreshFolderStatus.apply(self);},1000); }, + /** + * mail_fetchCurrentlyFocussed - implementation to decide wich mail of all the selected ones is the current + * + * @param _selected array of the selected mails + * @param _reset bool - tell the function to reset the global vars used + */ + mail_fetchCurrentlyFocussed: function(_selected, _reset) { + //console.log("mail_fetchCurrentlyFocussed",_selected, _reset); + // reinitialize the buffer-info on selected mails + if (_reset == true || typeof _selected == 'undefined') + { + if (this.mail_currentlyFocussed!='') egw.dataDeleteUID(this.mail_currentlyFocussed); + for(var k = 0; k < this.mail_selectedMails.length; k++) egw.dataDeleteUID(this.mail_selectedMails[k]); + this.mail_selectedMails = []; + this.mail_currentlyFocussed = ''; + return ''; + } + for(var k = 0; k < _selected.length; k++) + { + if (jQuery.inArray(_selected[k],this.mail_selectedMails)==-1) + { + this.mail_currentlyFocussed = _selected[k]; + break; + } + } + this.mail_selectedMails = _selected; + return this.mail_currentlyFocussed; + }, + /** * mail_open - implementation of the open action * @@ -43,6 +74,11 @@ app.mail = AppJS.extend( mail_open: function(_action, _senders) { console.log("mail_open",_action, _senders); var _id = _senders[0].id; + // reinitialize the buffer-info on selected mails + this.mail_selectedMails = []; + this.mail_selectedMails.push(_id); + this.mail_currentlyFocussed = _id; + var dataElem = egw.dataGetUIDdata(_id); var subject = dataElem.data.subject; var sw = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('previewSubject'); @@ -105,17 +141,22 @@ app.mail = AppJS.extend( * @param selected Array Selected row IDs. May be empty if user unselected all rows. */ mail_preview: function(nextmatch, selected) { - //console.log("mail_preview",nextmatch, selected); - + console.log("mail_preview",nextmatch, selected); + var splitter = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('mailSplitter'); + var previewarea = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('mailPreview'); // Empty values, just in case selected is empty (user cleared selection) var dataElem = {data:{subject:"",fromaddress:"",toaddress:"",date:"",subject:""}}; - if(selected.length > 0) + if(typeof selected != 'undefined' && selected.length > 0) { - var _id = selected[0]; + var _id = this.mail_fetchCurrentlyFocussed(selected); dataElem = egw.dataGetUIDdata(_id); + previewarea.visible = true; } if(typeof selected == 'undefined' || selected.length == 0 || typeof dataElem =='undefined') { + this.mail_fetchCurrentlyFocussed(); + splitter.dock(); + previewarea.visible = false; var subject =""; etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('previewFromAddress').set_value(""); etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('previewToAddress').set_value(""); @@ -126,6 +167,8 @@ app.mail = AppJS.extend( return; } //console.log("mail_preview",dataElem); + splitter.undock(); + this.mail_selectedMails.push(_id); var subject =dataElem.data.subject; etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('previewFromAddress').set_value(dataElem.data.fromaddress); etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('previewToAddress').set_value(dataElem.data.toaddress); @@ -133,8 +176,7 @@ app.mail = AppJS.extend( etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('previewSubject').set_value(subject); var IframeHandle = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('messageIFRAME'); IframeHandle.set_src(egw.link('/index.php',{menuaction:'mail.mail_ui.loadEmailBody',_messageID:_id})); - - + // var request = new egw_json_request('mail.mail_ui.ajax_loadEmailBody',[_id]); // request.sendRequest(false); }, @@ -257,8 +299,8 @@ app.mail = AppJS.extend( * key is the id of the leaf to delete * multiple sets can be passed to mail_deleteLeaf */ - mail_removelLeaf: function(_status) { - //console.log('mail_setLeaf',_status); + mail_removeLeaf: function(_status) { + console.log('mail_removeLeaf',_status); var ftree = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('nm[foldertree]'); var selectedNode = ftree.getSelectedNode(); for (var i in _status) @@ -277,6 +319,32 @@ app.mail = AppJS.extend( } }, + /** + * mail_reloadNode, function to reload the leaf represented by the given ID + * @param array _status status array with the required data (KEY id, VALUE desc) + * key is the id of the leaf to delete + * multiple sets can be passed to mail_deleteLeaf + */ + mail_reloadNode: function(_status) { + console.log('mail_reloadNode',_status); + var ftree = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('nm[foldertree]'); + var selectedNode = ftree.getSelectedNode(); + for (var i in _status) + { + // if olddesc is undefined or #skip# then skip the message, as we process subfolders + if (typeof _status[i] !== 'undefined' && _status[i] !== '#skip-user-interaction-message#') app.mail.app_refresh(egw.lang("Reloaded Folder %1 ",_status[i], 'mail')); + ftree.refreshItem(i); + var selectedNodeAfter = ftree.getSelectedNode(); + //alert(i +'->'+_status[i]['id']+'+'+_status[i]['desc']); + if (selectedNodeAfter.id!=selectedNode.id && selectedNode.id==i) + { + var nm = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('nm'); + nm.activeFilters["selectedFolder"] = selectedNodeAfter.id; + nm.applyFilters(); + } + } + }, + /** * mail_refreshMessageGrid, function to call to reread ofthe current folder */ @@ -363,6 +431,7 @@ app.mail = AppJS.extend( request.sendRequest(false); for (var i = 0; i < msg['msg'].length; i++) egw.dataDeleteUID(msg['msg'][i]); this.mail_refreshMessageGrid(); + this.mail_preview(); }, /** @@ -467,6 +536,7 @@ app.mail = AppJS.extend( //mail_refreshMessageGrid(); this.mail_refreshFolderStatus(folder,'forced'); this.mail_startTimerFolderStatusUpdate(this.mail_refreshTimeOut); + this.mail_preview(); }, /** @@ -702,6 +772,34 @@ app.mail = AppJS.extend( request.sendRequest(false); this.mail_refreshMessageGrid(); }, + + /** + * mail_AddFolder - implementation of the AddFolder action of right click options on the tree + * + * @param _action + * @param _senders - the representation of the tree leaf to be manipulated + */ + mail_AddFolder: function(action,_senders) { + //console.log(action,_senders); + //action.id == 'add' + //_senders.iface.id == target leaf / leaf to edit + var ftree = etemplate2.getByApplication('mail')[0].widgetContainer.getWidgetById('nm[foldertree]'); + OldFolderName = ftree.getLabel(_senders[0].iface.id); + if (jQuery(OldFolderName).text().length>0) OldFolderName = jQuery(OldFolderName).text(); + OldFolderName = OldFolderName.trim(); + OldFolderName = OldFolderName.replace(/\([0-9]*\)/g,'').trim(); + //console.log(OldFolderName); + NewFolderName = prompt(egw.lang("Add a new Folder to %1:",OldFolderName)); + if (jQuery(NewFolderName).text().length>0) NewFolderName = jQuery(NewFolderName).text(); + //alert(NewFolderName); + if (NewFolderName && NewFolderName.length>0) + { + app.mail.app_refresh(egw.lang("Adding Folder %1 to %2",NewFolderName, OldFolderName, 'mail')); + var request = new egw_json_request('mail.mail_ui.ajax_addFolder',[_senders[0].iface.id, NewFolderName]); + request.sendRequest(true); + } + }, + /** * mail_RenameFolder - implementation of the RenameFolder action of right click options on the tree *