/** * A draft can be loaded from the database if an id was supplied * or filled with dummy data if no id was supplied. If no id was supplied, * the user wants to create a new email. In this case, the id defaults to * -1. If the user requests to save the draft later on, the id will be updated * to the value of the auto_increment field of the table. * Along with an id the application will need a folder_id so it can tell whether * an existing view has to be updated if this draft was edited and the folder * is currently visible. * Note, that getDraft will also be executed when the user wants to reply to * an email or forward an email. in this case, the id defaults to the email to * which the user wants to forward/ reply to. * * The method awaits 4 POST parameters: * id - the original message to reply to OR the id of the draft that is being * edited * type - the context the draft is in: can be either "new", "forward", * "reply", "reply_all" or "edit" * name: the name of an recipient to send this email to * address: the address of an recipient to send this email to. If that value is not * empty. id will be set to -1 and type will be set to new. If address equals * to name or if name is left empty, only the address will be used to send the * email to. Address is given presedence in any case */ public function getDraftAction() { if ($this->_helper->conjoonContext()->getCurrentContext() != self::CONTEXT_JSON) { /** * see Conjoon_Controller_Action_InvalidContextException */ require_once 'Conjoon/Controller/Action/InvalidContextException.php'; throw new Conjoon_Controller_Action_InvalidContextException("Invalid context for action, expected \"" . self::CONTEXT_JSON . "\", got \"" . $this->_helper->conjoonContext()->getCurrentContext() . "\""); } $path = $_POST['path']; // check if folder is remote folder /** * @see Conjoon_Text_Parser_Mail_MailboxFolderPathJsonParser */ require_once 'Conjoon/Text/Parser/Mail/MailboxFolderPathJsonParser.php'; $parser = new Conjoon_Text_Parser_Mail_MailboxFolderPathJsonParser(); $pathInfo = $parser->parse($path); /** * @see Conjoon_Modules_Groupware_Email_Folder_Facade */ require_once 'Conjoon/Modules/Groupware/Email/Folder/Facade.php'; $facade = Conjoon_Modules_Groupware_Email_Folder_Facade::getInstance(); if (!empty($pathInfo) && $facade->isRemoteFolder($pathInfo['rootId'])) { return $this->getDraftFromRemoteServer($_POST['id'], $path, $_POST['type']); } /** * @see Conjoon_Keys */ require_once 'Conjoon/Keys.php'; /** * @see Conjoon_BeanContext_Inspector */ require_once 'Conjoon/BeanContext/Inspector.php'; /** * @see Conjoon_Modules_Groupware_Email_Draft_Filter_DraftResponse */ require_once 'Conjoon/Modules/Groupware/Email/Draft/Filter/DraftResponse.php'; /** * @see Conjoon_Modules_Groupware_Email_Account_Model_Account */ require_once 'Conjoon/Modules/Groupware/Email/Account/Model/Account.php'; /** * @see Conjoon_Util_Array */ require_once 'Conjoon/Util/Array.php'; $auth = Zend_Registry::get(Conjoon_Keys::REGISTRY_AUTH_OBJECT); $userId = $auth->getIdentity()->getId(); $id = (int) $_POST['id']; $type = (string) $_POST['type']; $accountModel = new Conjoon_Modules_Groupware_Email_Account_Model_Account(); // create a new draft so that the user is able to write an email from scratch! if ($id <= 0) { /** * @see Conjoon_Modules_Groupware_Email_Draft */ require_once 'Conjoon/Modules/Groupware/Email/Draft.php'; $standardId = $accountModel->getStandardAccountIdForUser($userId); if ($standardId == 0) { $this->view->error = $this->getErrorDto('Error while opening draft', 'Please configure an email account first.', Conjoon_Error::LEVEL_ERROR); $this->view->draft = null; $this->view->success = false; return; } $post = $_POST; Conjoon_Util_Array::apply($post, array('groupwareEmailAccountsId' => $standardId, 'groupwareEmailFoldersId' => -1)); $draftFilter = new Conjoon_Modules_Groupware_Email_Draft_Filter_DraftResponse($post, Conjoon_Modules_Groupware_Email_Draft_Filter_DraftResponse::CONTEXT_NEW); $data = $draftFilter->getProcessedData(); $draft = Conjoon_BeanContext_Inspector::create('Conjoon_Modules_Groupware_Email_Draft', $data); $this->view->success = true; $this->view->error = null; $this->view->draft = $draft->getDto(); $this->view->type = $type; return; } // load an email to edit, to reply or to forward it /** * @see Conjoon_Modules_Groupware_Email_Draft_Model_Draft */ require_once 'Conjoon/Modules/Groupware/Email/Draft/Model/Draft.php'; $draftModel = new Conjoon_Modules_Groupware_Email_Draft_Model_Draft(); $draftData = $draftModel->getDraft($id, $userId, $type); if (empty($draftData)) { $this->view->error = $this->getErrorDto('Error while opening draft', 'Could not find the referenced draft.', Conjoon_Error::LEVEL_ERROR); $this->view->draft = null; $this->view->success = false; return; } switch ($type) { case 'reply': $context = Conjoon_Modules_Groupware_Email_Draft_Filter_DraftResponse::CONTEXT_REPLY; break; case 'reply_all': $context = Conjoon_Modules_Groupware_Email_Draft_Filter_DraftResponse::CONTEXT_REPLY_ALL; break; case 'forward': $context = Conjoon_Modules_Groupware_Email_Draft_Filter_DraftResponse::CONTEXT_FORWARD; break; case 'edit': $context = Conjoon_Modules_Groupware_Email_Draft_Filter_DraftResponse::CONTEXT_EDIT; break; default: throw new Exception("Type {$type} not supported."); break; } Conjoon_Util_Array::camelizeKeys($draftData); $addresses = $accountModel->getEmailAddressesForUser($userId); $draftData['userEmailAddresses'] = $addresses; /** * @ticket CN-708 * if context is not edit and equals to reply* or forward, read out the * "to" address and find the matching account we'll be using for setting as * account from which the mail gets edited */ $matchingAccountId = -1; if ($context !== Conjoon_Modules_Groupware_Email_Draft_Filter_DraftResponse::CONTEXT_EDIT) { $orgTo = $draftData['to']; $matchingAccountId = $this->guessMailAccountForAddress($orgTo); if ($matchingAccountId > -1) { $draftData['groupwareEmailAccountsId'] = $matchingAccountId; } } $draftFilter = new Conjoon_Modules_Groupware_Email_Draft_Filter_DraftResponse($draftData, $context); $data = $draftFilter->getProcessedData(); $templateData = $data; // needed for draft forward because of Bean_Inspector unset($data['userEmailAddresses']); unset($data['from']); unset($data['replyTo']); if ($type == 'forward') { $data['to'] = array(); $data['cc'] = array(); } // convert email addresses /** * @see Conjoon_Modules_Groupware_Email_Address */ require_once 'Conjoon/Modules/Groupware/Email/Address.php'; $to = array(); $cc = array(); $bcc = array(); foreach ($data['to'] as $add) { $to[] = new Conjoon_Modules_Groupware_Email_Address($add); } foreach ($data['cc'] as $add) { $cc[] = new Conjoon_Modules_Groupware_Email_Address($add); } foreach ($data['bcc'] as $add) { $bcc[] = new Conjoon_Modules_Groupware_Email_Address($add); } $data['to'] = $to; $data['cc'] = $cc; $data['bcc'] = $bcc; $draft = Conjoon_BeanContext_Inspector::create('Conjoon_Modules_Groupware_Email_Draft', $data)->getDto(); if ($type == 'forward') { $applicationPath = $this->_helper->registryAccess()->getApplicationPath(); /** * @see Conjoon_Text_PhpTemplate */ require_once 'Conjoon/Text/PhpTemplate.php'; /** * @see Conjoon_Filter_StringWrap */ require_once 'Conjoon/Filter/StringWrap.php'; /** * @see Zend_Filter_HtmlEntities */ require_once 'Zend/Filter/HtmlEntities.php'; $cfsw = new Conjoon_Filter_StringWrap('[Fwd: ', ']'); $zfhe = new Zend_Filter_HtmlEntities(array('quotestyle' => ENT_COMPAT, 'charset' => 'UTF-8')); $draft->subject = $cfsw->filter($templateData['subject']); $templateData['subject'] = $zfhe->filter($templateData['subject']); $phpTemplate = new Conjoon_Text_PhpTemplate(array(Conjoon_Text_PhpTemplate::PATH => $applicationPath . '/templates/groupware/email/message.forward.phtml', Conjoon_Text_PhpTemplate::VARS => $templateData)); $draft->contentTextPlain = $phpTemplate->getParsedTemplate(); } $this->view->success = true; $this->view->error = null; $this->view->draft = $draft; $this->view->type = $type; }
/** * Deletes all items with the specified ids for the specified user. * The items will only be deleted if all rows in groupware_email_items_flags * for the corresponding item have been set to is_deleted=1. Otherwise, * only the is_deleted field for the specified user will be set to 1. * If that is the case and the item gets entirely deleted, the method will * use the accounts-model to check whether there are any accounts flagged * as "is_deleted = 1" and also remove this accounts if no more items * are exiting in the data storage. * * @param array $itemIds A numeric array with all id's of the items that are * about to be deleted * @param integer $userId The id of the user for which the items get deleted. * * @return integer The number of total rows deleted */ public function deleteItemsForUser(array $itemIds, $userId) { $userId = (int) $userId; if ($userId <= 0) { return 0; } $clearedItemIds = array(); $cc = 0; for ($i = 0, $len = count($itemIds); $i < $len; $i++) { $id = (int) $itemIds[$i]; if ($id > 0) { $clearedItemIds[] = $id; $cc++; } } if ($cc == 0) { return 0; } $referenceModel = new Conjoon_Modules_Groupware_Email_Item_Model_References(); // delete all references for the items for the specified user $referenceModel->delete('user_id = ' . $userId . ' AND reference_items_id IN (' . implode(',', $clearedItemIds) . ')'); $flagModel = new Conjoon_Modules_Groupware_Email_Item_Model_Flag(); $flagModel->flagItemsAsDeleted($clearedItemIds, $userId); $deleteValues = $flagModel->areItemsFlaggedAsDeleted($clearedItemIds); // if the second argument to array_filter is ommited, array_filter gets // all keys which values does not equal to false $itemsToDelete = array_filter($deleteValues); $idString = implode(',', array_keys($itemsToDelete)); $deleted = $this->delete('id IN (' . $idString . ')'); if ($deleted > 0) { $referencesModel = new Conjoon_Modules_Groupware_Email_Item_Model_References(); $inboxModel = new Conjoon_Modules_Groupware_Email_Item_Model_Inbox(); $outboxModel = new Conjoon_Modules_Groupware_Email_Item_Model_Outbox(); $attachmentModel = new Conjoon_Modules_Groupware_Email_Attachment_Model_Attachment(); /** * @see Conjoon_Modules_Groupware_Email_Account_Model_Account */ require_once 'Conjoon/Modules/Groupware/Email/Account/Model/Account.php'; $accountModel = new Conjoon_Modules_Groupware_Email_Account_Model_Account(); $referencesModel->delete('is_pending=1 AND user_id = ' . $userId . ' AND groupware_email_items_id IN (' . $idString . ')'); $flagModel->delete('user_id = ' . $userId . ' AND groupware_email_items_id IN (' . $idString . ')'); $attachmentModel->delete('groupware_email_items_id IN (' . $idString . ')'); $inboxModel->delete('groupware_email_items_id IN (' . $idString . ')'); $outboxModel->delete('groupware_email_items_id IN (' . $idString . ')'); $accountModel->removeAsDeletedFlaggedAccounts($userId); } return $deleted; }
/** * This action will update email accounts according to the POST params * that come along with the request. * Depending of the current context the action was called, the format of the * POST params will differ: For json, it will be an array holding json-encoded parameters. * Despite all different formats the POST params may come in, each request sends * values for 'updated' and 'deleted' accounts: 'deleted' as an array, holding all * ids that should become deleted, and 'updated' an array holding information about * the records that get edited, represented by specific associative array, representing * the fields of the record. * The view will await values assigned to 'updated_failed' and 'deleted_failed', whereas * 'deleted_failed' is an array containing all ids that couldn't be deleted, and * 'updated_failed' an array containing the records (in associative array notation) * that could not be updated. * * Note: If any error in the user-input was detected, no update-action will happen, but * deltes may already have been submitted to the underlying datastore. * The first error found in the passed data will be returned as an error of the type * Conjoon_Error_Form, containing the fields that where errorneous. * */ public function updateEmailAccountsAction() { require_once 'Conjoon/Modules/Groupware/Email/Account/Filter/Account.php'; require_once 'Conjoon/Util/Array.php'; require_once 'Conjoon/Modules/Groupware/Email/Account/Model/Account.php'; $toDelete = array(); $toUpdate = array(); $deletedFailed = array(); $updatedFailed = array(); $model = new Conjoon_Modules_Groupware_Email_Account_Model_Account(); $data = array(); $error = null; if ($this->_helper->conjoonContext()->getCurrentContext() == self::CONTEXT_JSON) { require_once 'Zend/Json.php'; $toDelete = Zend_Json::decode($_POST['deleted'], Zend_Json::TYPE_ARRAY); $toUpdate = Zend_Json::decode($_POST['updated'], Zend_Json::TYPE_ARRAY); } $numToUpdate = count($toUpdate); $numToDelete = count($toDelete); if ($numToUpdate != 0 || $numToDelete != 0) { /** * @see Conjoon_Builder_Factory */ require_once 'Conjoon/Builder/Factory.php'; Conjoon_Builder_Factory::getBuilder(Conjoon_Keys::CACHE_EMAIL_ACCOUNTS, Zend_Registry::get(Conjoon_Keys::REGISTRY_CONFIG_OBJECT)->toArray())->cleanCacheForTags(array('userId' => Zend_Registry::get(Conjoon_Keys::REGISTRY_AUTH_OBJECT)->getIdentity()->getId())); } $userId = $this->_helper->registryAccess()->getUserId(); for ($i = 0; $i < $numToDelete; $i++) { $affected = $model->deleteAccount($toDelete[$i], $userId); if ($affected == 0) { $deletedFailed[] = $toDelete[$i]; } } $folderMappingData = array(); for ($i = 0; $i < $numToUpdate; $i++) { $_ = $toUpdate[$i]; $folderMappingData[$toUpdate[$i]['id']] = $toUpdate[$i]['folderMappings']; $filter = new Conjoon_Modules_Groupware_Email_Account_Filter_Account($_, Conjoon_Filter_Input::CONTEXT_UPDATE); try { $data[$i] = $filter->getProcessedData(); Conjoon_Util_Array::underscoreKeys($data[$i]); } catch (Zend_Filter_Exception $e) { require_once 'Conjoon/Error.php'; $error = Conjoon_Error::fromFilter($filter, $e); $this->view->success = false; $this->view->updatedFailed = array($_['id']); $this->view->deletedFailed = $deletedFailed; $this->view->error = $error->getDto(); return; } } $createdLocalRootMailFolders = array(); $removedLocalRootMailFolders = array(); /** * @see Conjoon_BeanContext_Decorator */ require_once 'Conjoon/BeanContext/Decorator.php'; $decoratedFolderModel = new Conjoon_BeanContext_Decorator('Conjoon_Modules_Groupware_Email_Folder_Model_Folder', null, false); $decoratedAccountModel = new Conjoon_BeanContext_Decorator('Conjoon_Modules_Groupware_Email_Account_Model_Account'); /** * @see Conjoon_Modules_Groupware_Email_Folder_Model_Folder */ require_once 'Conjoon/Modules/Groupware/Email/Folder/Model/Folder.php'; $folderModel = new Conjoon_Modules_Groupware_Email_Folder_Model_Folder(); /** * @see Conjoon_Modules_Groupware_Email_Folder_Model_FoldersAccounts */ require_once 'Conjoon/Modules/Groupware/Email/Folder/Model/FoldersAccounts.php'; $foldersAccounts = new Conjoon_Modules_Groupware_Email_Folder_Model_FoldersAccounts(); for ($i = 0, $len = count($data); $i < $len; $i++) { $id = $data[$i]['id']; unset($data[$i]['id']); // check here for duplicates $duplicates = $model->getAccountWithNameForUser($data[$i]['name'], $userId); $affected = 0; if (!empty($duplicates)) { for ($a = 0, $lena = count($duplicates); $a < $lena; $a++) { if ($duplicates[$a]['id'] != $id) { $affected = -1; break; } } } if ($affected != -1) { $affected = $model->updateAccount($id, $data[$i]); $entityManager = Zend_Registry::get(Conjoon_Keys::DOCTRINE_ENTITY_MANAGER); // update folderMappings if (isset($folderMappingData[$id])) { $fmRep = $entityManager->getRepository('\\Conjoon\\Data\\Entity\\Mail\\DefaultFolderMappingEntity'); for ($u = 0, $lenu = count($folderMappingData[$id]); $u < $lenu; $u++) { if (isset($folderMappingData[$id][$u]['id']) && empty($folderMappingData[$id][$u]['path'])) { $updEnt = $fmRep->findById($folderMappingData[$id][$u]['id']); $updEnt->setGlobalName(""); $fmRep->register($updEnt); } else { if (isset($folderMappingData[$id][$u]['id']) && !empty($folderMappingData[$id][$u]['path'])) { /** * @see \Conjoon\Data\Entity\Mail\DefaultFolderMappingEntity */ require_once 'Conjoon/Data/Entity/Mail/DefaultFolderMappingEntity.php'; /** * @see Conjoon_Modules_Groupware_Email_ImapHelper */ require_once 'Conjoon/Modules/Groupware/Email/ImapHelper.php'; $accDto = $decoratedAccountModel->getAccountAsDto($id, $userId); $delim = Conjoon_Modules_Groupware_Email_ImapHelper::getFolderDelimiterForImapAccount($accDto); $p = $folderMappingData[$id][$u]['path']; array_shift($p); array_shift($p); $globalName = implode($delim, $p); $updEnt = $fmRep->findById($folderMappingData[$id][$u]['id']); $updEnt->setGlobalName($globalName); $fmRep->register($updEnt); continue; } } } $fmRep->flush(); } // take care of folder heirarchies if ($affected != -1) { /** * @todo Facade */ $hasSeparateFolderHierarchy = array_key_exists('has_separate_folder_hierarchy', $data[$i]) ? (bool) (int) $data[$i]['has_separate_folder_hierarchy'] : null; // get org protocol of account so it cannot be changed from // the outside $orgAccount = $model->getAccount($id, $userId); $orgProtocol = $orgAccount['protocol']; if ($hasSeparateFolderHierarchy !== null && strtolower($orgProtocol) !== 'imap') { // the original folder ids, before remapping occures $oldAccountFolderIds = $foldersAccounts->getFolderIdsForAccountId($id); // read out folder base data of folder for associated account $rootFolderBaseData = $decoratedFolderModel->getAnyRootMailFolderBaseDataAsDto($id, $userId); if (!$rootFolderBaseData) { throw new RuntimeException("No root folder base data available."); } if (!$hasSeparateFolderHierarchy) { // do nothing if type is already accounts_root and // separateFolderHierarchy = false is submitted; // but if the type is root and the folderHierarchy // (i.e. "root) should be removed, // we need to switch from root to accounts_root if ($rootFolderBaseData->type == 'root') { // check first if accounts_root exist! $accountsRootFolderId = $folderModel->getAccountsRootMailFolderBaseData($userId); // accounts root not yet existing if (!$accountsRootFolderId) { $folderModel->createFolderBaseHierarchyAndMapAccountIdForUserId($id, $userId); } else { $newFolderIds = $folderModel->getFoldersForAccountsRoot($userId); // accounts root already existing. // remove the root and remap to accounts_root foreach ($oldAccountFolderIds as $oldFolderId) { $folderModel->deleteFolder($oldFolderId, $userId, false); } $removedLocalRootMailFolders[$id] = $rootFolderBaseData; $foldersAccounts->mapFolderIdsToAccountId($newFolderIds, $id); } $createdLocalRootMailFolders[$id] = $decoratedFolderModel->getAnyRootMailFolderBaseDataAsDto($id, $userId); } } else { // do nothing if the type is already root which means // there already exists a separate folder hierarchy // if the type is accounts_root, we need to switch // to a root hierarchy if ($rootFolderBaseData->type == 'accounts_root') { // remove old mappings $foldersAccounts->deleteForAccountId($id); $folderModel->createFolderHierarchyAndMapAccountIdForUserId($id, $userId, $data[$i]['name']); $createdLocalRootMailFolders[$id] = $decoratedFolderModel->getAnyRootMailFolderBaseDataAsDto($id, $userId); } } } } } if ($affected == -1) { $updatedFailed[] = $id; } } $this->view->success = empty($updatedFailed) ? true : false; $this->view->updatedFailed = $updatedFailed; $this->view->deletedFailed = $deletedFailed; $this->view->error = null; $this->view->createdLocalRootMailFolders = $createdLocalRootMailFolders; $this->view->removedLocalRootMailFolders = $removedLocalRootMailFolders; }
/** * Returns an assoc array with the data of an draft. The returned array * has all properties as according to Conjoon_Modules_Groupware_Email_Draft. * * @param integer $itemId * @param integer $userId * @param string $context The context used to fetch the draft. Important * when dealign with contexts "reply", "reply_all" and "forward". * - context "forward": fields "references" and "in_reply_to" will be set * to an empty string * - context "reply", "reply_all": "in_reply_to" will be set to the message-id * of the email, references will be concatenated with the message-id * * @return array */ public function getDraft($itemId, $userId, $context = '') { $itemId = (int) $itemId; if ($itemId <= 0) { return array(); } $itemModel = new Conjoon_Modules_Groupware_Email_Item_Model_Item(); $row = $itemModel->fetchRow($itemModel->select()->from($itemModel)->where('id = ?', $itemId)); if (!$row) { return array(); } $draft = array('id' => $row->id, 'date' => $row->date, 'subject' => $row->subject, 'from' => $row->from, 'reply_to' => $row->reply_to, 'to' => $row->to, 'cc' => $row->cc, 'bcc' => $row->bcc, 'in_reply_to' => $row->in_reply_to, 'references' => $row->references, 'content_text_plain' => $row->content_text_plain, 'content_text_html' => $row->content_text_html, 'groupware_email_folders_id' => $row->groupware_email_folders_id, 'attachments' => array()); // clear memory unset($row); // set in_reply_to, references according to the context switch ($context) { case Conjoon_Modules_Groupware_Email_Keys::REFERENCE_TYPE_REPLY: case Conjoon_Modules_Groupware_Email_Keys::REFERENCE_TYPE_REPLY_ALL: $inboxModel = new Conjoon_Modules_Groupware_Email_Item_Model_Inbox(); $messageId = $inboxModel->getMessageIdForItem($draft['id']); if ($messageId != "") { $draft['in_reply_to'] = $messageId; $draft['references'] = $draft['references'] != '' ? $draft['references'] . ' ' . $messageId : $messageId; } else { $draft['in_reply_to'] = ''; $draft['references'] = ''; } break; case Conjoon_Modules_Groupware_Email_Keys::REFERENCE_TYPE_FORWARD: $draft['in_reply_to'] = ''; $draft['references'] = ''; case '': case Conjoon_Modules_Groupware_Email_Keys::REFERENCE_TYPE_EDIT: /** * @see Conjoon_Modules_Groupware_Email_Attachment_Model_Attachment */ require_once 'Conjoon/Modules/Groupware/Email/Attachment/Model/Attachment.php'; $attachmentModel = new Conjoon_Modules_Groupware_Email_Attachment_Model_Attachment(); $draft['attachments'] = $attachmentModel->getAttachmentsForItem($draft['id'])->toArray(); break; } // check if the item is available in outbox and get the id of it under which it was // created. Otherwise, get the standard account out of the accounts-table $outboxModel = new Conjoon_Modules_Groupware_Email_Item_Model_Outbox(); $accIdRow = $outboxModel->fetchRow($outboxModel->select()->from($outboxModel, array('groupware_email_accounts_id'))->where('groupware_email_items_id = ? ', $draft['id'])); $accountModel = new Conjoon_Modules_Groupware_Email_Account_Model_Account(); if (!$accIdRow) { $accId = $accountModel->getStandardAccountIdForUser($userId); } else { $accId = $accIdRow->groupware_email_accounts_id; // check if the account still exists $account = $accountModel->getAccount($accId, $userId); if (empty($account)) { $accId = $accountModel->getStandardAccountIdForUser($userId); if ($accId == 0) { return array(); } } } $draft['groupware_email_accounts_id'] = $accId; return $draft; }