currentUser = $current_user; $this->appStrings = $app_strings; $this->modStrings = $mod_strings; $this->timeDate = $timedate; $this->beanList = $bean_list; $this->sugarConfig = $sugar_config; $this->currentLanguage = $current_language; if (empty($this->currentUser)) { global $current_user; $this->currentUser = $current_user; } if (empty($this->appStrings)) { global $app_strings; $this->appStrings = $app_strings; } if (empty($this->modStrings)) { global $mod_strings; $this->modStrings = $mod_strings; } if (empty($this->timeDate)) { global $timedate; $this->timeDate = $timedate; } if (empty($this->beanList)) { global $beanList; $this->beanList = $beanList; } if (empty($this->sugarConfig)) { global $sugar_config; $this->sugarConfig = $sugar_config; } if (empty($this->currentLanguage)) { global $current_language; $this->currentLanguage = $current_language; } $this->db = DBManagerFactory::getInstance(); $this->core = "SELECT f.id, f.name, f.has_child, f.is_group, f.is_dynamic, f.dynamic_query," . " f.folder_type, f.created_by, f.deleted FROM folders f "; $this->coreSubscribed = "SELECT f.id, f.name, f.has_child, f.is_group, f.is_dynamic,". " f.dynamic_query, f.folder_type, f.created_by, f.deleted FROM folders f LEFT JOIN folders_subscriptions". " fs ON f.id = fs.folder_id "; $this->coreWhere = "WHERE f.deleted != 1 "; $this->coreWhereSubscribed = "WHERE f.deleted != 1 AND fs.assigned_user_id = "; $this->coreOrderBy = " ORDER BY f.is_dynamic, f.is_group, f.name ASC "; $this->hrSortLocal = array( 'flagged' => 'type', 'status' => 'reply_to_status', 'from' => 'emails_text.from_addr', 'subject' => 'name', 'date' => 'date_sent_received', 'AssignedTo' => 'assigned_user_id', 'flagged' => 'flagged' ); } /** * Delete the email from the all folders * * @param int $id * @return boolean */ public function deleteEmailFromAllFolder($id) { $query = "DELETE FROM folders_rel WHERE polymorphic_module = 'Emails' AND polymorphic_id = " . $this->db->quoted($id); return $this->db->query($query); } /** * Delete Email From Folder * * @param string $id * @return boolean */ public function deleteEmailFromFolder($id) { $query = "DELETE FROM folders_rel " . "WHERE polymorphic_module = 'Emails' " . "AND polymorphic_id = " . $this->db->quoted($id) . " " . "AND folder_id = " . $this->db->quoted($this->id); return $this->db->query($query); } /** * Check an email exists for folder * * @param string $id * @return boolean */ public function checkEmailExistForFolder($id) { $query = "SELECT COUNT(*) c FROM folders_rel WHERE polymorphic_module = 'Emails' AND polymorphic_id = " . $this->db->quoted($id) . " AND folder_id = " . $this->db->quoted($this->id); $res = $this->db->query($query); $a = $this->db->fetchByAssoc($res); return $a['c'] > 0; } /** * Moves beans from one folder to another folder * * @param string fromFolder GUID of source folder * @param string toFolder GUID of destination folder * @param string beanId GUID of SugarBean being moved * @return boolean */ public function move($fromFolder, $toFolder, $beanId) { $query = "UPDATE folders_rel SET folder_id = " . $this->db->quoted($toFolder) . " " . "WHERE folder_id = " . $this->db->quoted($fromFolder) . " " . "AND polymorphic_id = " . $this->db->quoted($beanId) . " AND deleted = 0"; return $this->db->query($query); } /** * Copies one bean from one folder to another * * @param string $fromFolder * @param string $toFolder * @param string $beanId * @param string $module * @return boolean */ public function copyBean($fromFolder, $toFolder, $beanId, $module) { $guid = create_guid(); $query = "INSERT INTO folders_rel (id, folder_id, polymorphic_module, polymorphic_id, deleted) " . "VALUES(" . $this->db->quoted($guid) . ", " . $this->db->quoted($toFolder) . ", " . $this->db->quoted($module) . ", " . $this->db->quoted($beanId) . ", 0)"; return $this->db->query($query); } /** * Creates a new group Folder from the passed fields * * @param array fields * @return boolean */ public function setFolder($fields) { if (empty($fields['groupFoldersUser'])) { $fields['groupFoldersUser'] = $this->currentUser->id; } $this->name = $fields['name']; $this->parent_folder = $fields['parent_folder']; $this->has_child = 0; $this->is_group = 1; $this->assign_to_id = $fields['groupFoldersUser']; return $this->save(); } /** * Returns GUIDs of folders that the user in focus is subscribed to * * @param User User object in focus * @return array */ public function getSubscriptions($user) { if (empty($user)) { $user = $this->currentUser; } $query = "SELECT folder_id FROM folders_subscriptions WHERE assigned_user_id = " . $this->db->quoted($user->id); $r = $this->db->query($query); $ret = array(); while ($a = $this->db->fetchByAssoc($r)) { $ret[] = $a['folder_id']; } return $ret; } /** * Sets a user's preferences for subscribe folders (Sugar only) * * @param array Sub Array of IDs for subscribed folders * @param User Specify which user to set the subscriptions * @return void */ public function setSubscriptions($subs, $user = null) { if (null === $user) { $user = $this->currentUser; } if (empty($user->id)) { $GLOBALS['log']->fatal("*** FOLDERS: tried to update folder subscriptions for a user with no ID"); return false; } $cleanSubscriptions = array(); // Remove the duplications $subs = array_unique($subs); // Ensure parent folders are selected, regardless. foreach ($subs as $id) { $id = trim($id); if (!empty($id)) { $cleanSubscriptions[] = $id; $queryChk = "SELECT parent_folder FROM folders WHERE id = " . $this->db->quoted($id); $rChk = $this->db->query($queryChk); $aChk = $this->db->fetchByAssoc($rChk); if (!empty($aChk['parent_folder'])) { $cleanSubscriptions = $this->getParentIDRecursive($aChk['parent_folder'], $cleanSubscriptions); } } } $this->clearSubscriptions($user); foreach ($cleanSubscriptions as $id) { $this->insertFolderSubscription($id, $user->id); } } /** * Given a folder id and user id, create a folder subscription entry. * * @param string $folderId * @param string $userID * @return string The id of the newly created folder subscription. */ public function insertFolderSubscription($folderId, $userID) { $guid = create_guid(); $query = "INSERT INTO folders_subscriptions" . " (id, folder_id, assigned_user_id) VALUES (" . $this->db->quoted($guid) . ", " . $this->db->quoted($folderId) . ", " . $this->db->quoted($userID) . ')'; $r = $this->db->query($query); return $guid; } /** * Recursively finds parent node until it hits root * * @param string id Starting id to follow up * @param array ret collected ids * @return array of IDs */ public function getParentIDRecursive($id, $ret = array()) { $query = "SELECT * FROM folders WHERE id = " . $this->db->quoted($id) . " AND deleted = 0"; $r = $this->db->query($query); $a = $this->db->fetchByAssoc($r); if (!in_array($id, $ret)) { $ret[] = $id; } if ($a['parent_folder'] != '') { $queryChk = "SELECT parent_folder FROM folders WHERE id = " . $this->db->quoted($id); $rChk = $this->db->query($queryChk); $aChk = $this->db->fetchByAssoc($rChk); if (!empty($aChk['parent_folder'])) { $ret = $this->getParentIDRecursive($aChk['parent_folder'], $ret); } } return $ret; } /** * Deletes subscriptions to folders in preparation for reset * * @param User|null $user User * @return boolean */ public function clearSubscriptions($user = null) { if (!$user) { $user = $this->currentUser; } if (!empty($user->id)) { $query = "DELETE FROM folders_subscriptions WHERE assigned_user_id = " . $this->db->quoted($user->id); $r = $this->db->query($query); } } /** * Deletes all subscriptions for a particular folder id * @param string $folder_id * @return boolean */ public function clearSubscriptionsForFolder($folder_id) { $query = "DELETE FROM folders_subscriptions WHERE folder_id = " . $this->db->quoted($folder_id); return $this->db->query($query); } /** * Get the Generate Archive Folder Query * * @return string */ public function generateArchiveFolderQuery() { return "SELECT emails.id , emails.name, emails.date_sent_received, emails.status, emails.type, emails.flagged, " . "emails.reply_to_status, emails_text.from_addr, emails_text.to_addrs, 'Emails' polymorphic_module " . "FROM emails " . "JOIN emails_text on emails.id = emails_text.email_id " . "JOIN emails_email_addr_rel eear ON eear.email_id = emails.id " . "JOIN email_addr_bean_rel eabr ON eabr.email_address_id=eear.email_address_id " . "WHERE emails.deleted=0 AND emails.type NOT IN ('out', 'draft') AND emails.status NOT IN ('sent', 'draft') " . "AND eabr.bean_id = " . $this->db->quoted($this->currentUser->id) . " AND eabr.bean_module = 'Users' " . "AND eear.deleted=0 " . "GROUP BY id"; } public function generateSugarsDynamicFolderQuery() { $type = $this->folder_type; if ($type == 'archived') { return $this->generateArchiveFolderQuery(); } $status = $type; if ($type == "sent") { $type = "out"; } if ($type == 'inbound') { $ret = " AND emails.status NOT IN ('sent', 'archived', 'draft') AND emails.type NOT IN ('out', 'archived', 'draft')"; } else { $ret = " AND emails.status NOT IN ('archived') AND emails.type NOT IN ('archived')"; } $query = "SELECT emails.id, emails.name, emails.date_sent_received, emails.status, emails.type, emails.flagged,". " emails.reply_to_status, emails_text.from_addr, emails_text.to_addrs, ". "'Emails' polymorphic_module FROM emails" . " JOIN emails_text on emails.id = emails_text.email_id WHERE (type = " . $this->db->quoted($type) . " OR status = " . $this->db->quoted($status) . ")" . " AND assigned_user_id = " . $this->db->quoted($this->currentUser->id) . " AND emails.deleted = 0"; return $query . $ret; } /** * returns array of items for listView display in yui-ext Grid */ public function getListItemsForEmailXML($folderId, $page = 1, $pageSize = 10, $sort = '', $direction = '') { $this->retrieve($folderId); $start = ($page - 1) * $pageSize; $sort = (empty($sort)) ? $this->defaultSort : $sort; if (!in_array(strtolower($direction), array('asc', 'desc'))) { $direction = $this->defaultDirection; } if (!empty($this->hrSortLocal[$sort])) { $order = " ORDER BY " . $this->db->quoted($this->hrSortLocal[$sort]) . " {$direction}"; } else { $order = ""; } if ($this->is_dynamic) { $r = $this->db->limitQuery( from_html($this->generateSugarsDynamicFolderQuery() . $order), $start, $pageSize ); } else { // get items and iterate through them $query = "SELECT emails.id , emails.name, emails.date_sent_received, emails.status, emails.type, emails.flagged,". " emails.reply_to_status, emails_text.from_addr, emails_text.to_addrs,". " 'Emails' polymorphic_module FROM emails JOIN folders_rel ON emails.id = folders_rel.polymorphic_id" . " JOIN emails_text on emails.id = emails_text.email_id WHERE folders_rel.folder_id = " . $this->db->quoted($folderId) . " AND folders_rel.deleted = 0 AND emails.deleted = 0"; if ($this->is_group) { $query = $query . " AND (emails.assigned_user_id is null or emails.assigned_user_id = '')"; } $r = $this->db->limitQuery($query . $order, $start, $pageSize); } $return = array(); $email = BeanFactory::newBean('Emails'); //Needed for email specific functions. while ($a = $this->db->fetchByAssoc($r)) { $temp = array(); $temp['flagged'] = (is_null($a['flagged']) || $a['flagged'] == '0') ? '' : 1; $temp['status'] = (is_null($a['reply_to_status']) || $a['reply_to_status'] == '0') ? '' : 1; $temp['from'] = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['from_addr']); $temp['subject'] = $a['name']; $temp['date'] = $this->timeDate->to_display_date_time($this->db->fromConvert($a['date_sent_received'], 'datetime')); $temp['uid'] = $a['id']; $temp['mbox'] = 'sugar::' . $a['polymorphic_module']; $temp['ieId'] = $folderId; $temp['site_url'] = $this->sugarConfig['site_url']; $temp['seen'] = ($a['status'] == 'unread') ? 0 : 1; $temp['type'] = $a['type']; $temp['hasAttach'] = $email->doesImportedEmailHaveAttachment($a['id']); $temp['to_addrs'] = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['to_addrs']); $return[] = $temp; } $metadata = array(); $metadata['mbox'] = $this->appStrings['LBL_EMAIL_SUITE_FOLDER'] . ': ' . $this->name; $metadata['ieId'] = $folderId; $metadata['name'] = $this->name; $metadata['unreadChecked'] = ($this->currentUser->getPreference('showUnreadOnly', 'Emails') == 1) ? 'CHECKED' : ''; $metadata['out'] = $return; return $metadata; } /** * Get the count of items for dynamic folder * * @param bool $unread * @return int */ public function getDynamicFolderCount($unread = false) { $selectQuery = $this->generateSugarsDynamicFolderQuery(); $pattern = '/SELECT(.*?)(\s){1}FROM(\s){1}/is'; // ignores the case if ($this->folder_type === 'archived') { $replacement = 'SELECT count(DISTINCT emails.id) c FROM '; $modifiedSelectQuery = preg_replace($pattern, $replacement, $selectQuery, 1); // remove GROUP BY statement $pattern = '/GROUP BY id(\s)?/s'; $modifiedSelectQuery = preg_replace($pattern, '', $modifiedSelectQuery, 1); } else { $replacement = 'SELECT count(*) c FROM '; $modifiedSelectQuery = preg_replace($pattern, $replacement, $selectQuery, 1); } $query = from_html($modifiedSelectQuery); if ($unread) { $query .= " AND emails.status = 'unread'"; } $res = $this->db->query($query); $result = $this->db->fetchByAssoc($res); return $result['c']; } /** * Get the count of items * * @param string $folderId * @return int */ public function getCountItems($folderId) { $this->retrieve($folderId); if ($this->is_dynamic) { return $this->getDynamicFolderCount(); } // Get items and iterate through them $query = "SELECT count(*) c FROM folders_rel JOIN emails ON emails.id = folders_rel.polymorphic_id" . " WHERE folder_id = " . $this->db->quoted($folderId) . " AND folders_rel.deleted = 0 AND emails.deleted = 0"; if ($this->is_group) { $query .= " AND (emails.assigned_user_id is null or emails.assigned_user_id = '')"; } $res = $this->db->query($query); $result = $this->db->fetchByAssoc($res); return $result['c']; } /** * Get a count of the Unread Items * * @param string $folderId * @return integer */ public function getCountUnread($folderId) { $this->retrieve($folderId); if ($this->is_dynamic) { return $this->getDynamicFolderCount(true); } // Get items and iterate through them $query = "SELECT count(*) c FROM folders_rel fr JOIN emails on fr.folder_id = " . $this->db->quoted($folderId) . " AND fr.deleted = 0 " . "AND fr.polymorphic_id = emails.id AND emails.status = 'unread' AND emails.deleted = 0"; if ($this->is_group) { $query .= " AND (emails.assigned_user_id is null or emails.assigned_user_id = '')"; } $res = $this->db->query($query); $result = $this->db->fetchByAssoc($res); return $result['c']; } /** * Convenience method, pass a SugarBean * * @param SugarBean $bean * * @return boolean */ public function addBean(SugarBean $bean) { if (empty($bean->id) || empty($bean->module_dir)) { $GLOBALS['log']->fatal("*** FOLDERS: addBean() got empty bean - not saving"); return false; } if (empty($this->id)) { $GLOBALS['log']->fatal("*** FOLDERS: addBean() is trying to save to a non-saved or non-existent folder"); return false; } /* Fix issue #9192 - Duplicating rows for folders_rel First check if a row with the same data already exists If so, return false */ $q = "SELECT id FROM folders_rel WHERE". " folder_id = ".$this->db->quoted($this->id). " AND polymorphic_module = ".$this->db->quoted($bean->module_dir). " AND polymorphic_id = ".$this->db->quoted($bean->id). " AND deleted = 0"; $result = $this->db->fetchByAssoc($this->db->query($q)); if($result) { $GLOBALS['log']->debug("*** FOLDERS: addBean() is trying to create an already existing relationship"); return false; } $guid = create_guid(); $query = "INSERT INTO folders_rel " . "(id, folder_id, polymorphic_module, polymorphic_id, deleted) VALUES (" . $this->db->quoted($guid) . ", " . $this->db->quoted($this->id) . ", " . $this->db->quoted($bean->module_dir) . ", " . $this->db->quoted($bean->id) . ", 0)"; return $this->db->query($query); } /** * Builds up a metacollection of user/group folders to be passed to processor methods * * @param User $user Defaults to $current_user * @return array Array of abstract folder objects * @throws \SugarFolderEmptyException */ public function retrieveFoldersForProcessing($user, $subscribed = true) { $myEmailTypeString = 'inbound'; $myDraftsTypeString = 'draft'; $mySentEmailTypeString = 'sent'; $myArchiveTypeString = 'archived'; if (empty($user)) { $user = $this->currentUser; } $rootWhere = ''; $teamSecurityClause = ''; $rootWhere .= " AND (f.parent_folder IS NULL OR f.parent_folder = '')"; if ($subscribed) { $query = $this->coreSubscribed . $teamSecurityClause . $this->coreWhereSubscribed . $this->db->quoted($user->id) . $rootWhere . $this->coreOrderBy; } else { $query = $this->core . $teamSecurityClause . $this->coreWhere . $rootWhere . $this->coreOrderBy; } $res = $this->db->query($query); $return = array(); $found = array(); while ($a = $this->db->fetchByAssoc($res)) { if (!empty($a['folder_type']) && $a['folder_type'] !== $myArchiveTypeString ) { if (!isset($found[$a['id']])) { $found[$a['id']] = true; $children = $this->db->query("SELECT * FROM folders WHERE parent_folder = '" . $a['id'] . "'"); while ($b = $this->db->fetchByAssoc($children)) { $a['children'][] = $b; } $return[] = $a; } elseif ($found[$a['id']] === true) { LoggerManager::getLogger()->error('Duplicated folder detected: ' . $a['id']); } } } if (empty($found)) { LoggerManager::getLogger()->error( ' SugarFolder::retrieveFoldersForProcessing() Cannot Retrieve Folders - '. 'Please check the users inbound email settings.' ); } $secureReturn = []; $userAccessibleInboundIds = $this->getUserAccessibleInboundIds($user); foreach ($return as $item) { if (empty($item) || empty($item['id'])) { continue; } $isGroup = $item['isgroup'] ?? ''; if ($isGroup === 1) { $secureReturn[] = $item; } if (!empty($userAccessibleInboundIds[$item['id']])) { $secureReturn[] = $item; } } return $secureReturn; } /** * Preps object array for async call from user's Settings->Folders * * @param User|null $focusUser * @return array */ public function getGroupFoldersForSettings($focusUser = null) { $grp = array(); $folders = $this->retrieveFoldersForProcessing($focusUser, false); $subscriptions = $this->getSubscriptions($focusUser); foreach ($folders as $a) { $a['selected'] = (in_array($a['id'], $subscriptions)) ? true : false; $a['origName'] = $a['name']; if ($a['is_group'] == 1) { if ($a['deleted'] != 1) { $grp[] = $a; } } } return $grp; } /** * Preps object array for async call from user's Settings->Folders * * @param User|null $focusUser * @return array */ public function getFoldersForSettings($focusUser = null) { $user = array(); $grp = array(); $user[] = array( 'id' => '', 'name' => $this->appStrings['LBL_NONE'], 'has_child' => 0, 'is_group' => 0, 'selected' => false ); $grp[] = array( 'id' => '', 'name' => $this->appStrings['LBL_NONE'], 'has_child' => 0, 'is_group' => 1, 'selected' => false, 'origName' => "" ); try { $folders = $this->retrieveFoldersForProcessing($focusUser, false); $subscriptions = $this->getSubscriptions($focusUser); foreach ($folders as $a) { $a['selected'] = (in_array($a['id'], $subscriptions)) ? true : false; $a['origName'] = $a['name']; if (isTrue($a['deleted'] ?? false)) { continue; } if (isset($a['dynamic_query'])) { unset($a['dynamic_query']); } if ($a['is_group'] == 1) { $grp[] = $a; } else { $user[] = $a; } if ($a['has_child'] == 1) { $qGetChildren = $this->core . $this->coreWhere . "AND parent_folder = " . $this->db->quoted($a['id']); $rGetChildren = $this->db->query($qGetChildren); while ($aGetChildren = $this->db->fetchByAssoc($rGetChildren)) { if ($a['is_group']) { $this->_depth = 1; $grp = $this->getFoldersChildForSettings($aGetChildren, $grp, $subscriptions); } else { $this->_depth = 1; $user = $this->getFoldersChildForSettings($aGetChildren, $user, $subscriptions); } } } } } catch (SugarFolderEmptyException $e) { // And empty sugar folder exception is ok in this case. } $user = $this->removeDeletedFolders($user); $ret = array( 'userFolders' => $user, 'groupFolders' => $grp, ); return $ret; } /** * Remove folders of deleted inbounds * * @param array $folders - array of folders table rows * @return array */ private function removeDeletedFolders($folders) { $ret = array(); foreach ($folders as $folder) { $correct = false; if (!$folder['id']) { $correct = true; } $ie = BeanFactory::getBean('InboundEmail', $folder['id']); if ($ie) { $correct = true; } if ($correct) { $ret[] = $folder; } } return $ret; } /** * Gets all child folders for settings * * @param array $a * @param array $collection * @param array $subscriptions * @return array */ public function getFoldersChildForSettings($a, $collection, $subscriptions) { $a['selected'] = (in_array($a['id'], $subscriptions)) ? true : false; $a['origName'] = $a['name']; if (isset($a['dynamic_query'])) { unset($a['dynamic_query']); } for ($i = 0; $i < $this->_depth; $i++) { $a['name'] = "." . $a['name']; } $collection[] = $a; if ($a['has_child'] == 1) { $this->_depth++; $qGetChildren = $this->core . $this->coreWhere . " AND parent_folder = " . $this->db->quoted($a['id']); $rGetChildren = $this->db->query($qGetChildren); while ($aGetChildren = $this->db->fetchByAssoc($rGetChildren)) { $collection = $this->getFoldersChildForSettings($aGetChildren, $collection, $subscriptions); } } return $collection; } /** * Returns the number of "new" items (based on passed criteria) * * @param string id ID of folder * @param array criteria * expected: * array('field' => 'status', * 'value' => 'unread'); * @param array * @return int */ public function getCountNewItems($id, $criteria, $folder) { $sugarFolder = new SugarFolder(); return $sugarFolder->getCountUnread($id); } /** * Collects, sorts, and builds tree of user's folders * * @param object $rootNode Reference to tree root node * @param array $folderStates User pref folder open/closed states * @param User $user Optional User in focus, default current_user * * @return array */ public function getUserFolders(&$rootNode, $folderStates, $user = null, $forRefresh = false) { if (empty($user)) { $user = $this->currentUser; } $folders = $this->retrieveFoldersForProcessing($user, true); $subscriptions = $this->getSubscriptions($user); $refresh = ($forRefresh) ? array() : null; if (!is_array($folderStates)) { $folderStates = array(); } $settingsFolders = $this->getFoldersForSettings($user); $selectedFolders = []; foreach ($folders as $folder) { if ($this->isToDisplay($folder['id'] ?? '', $settingsFolders)){ $selectedFolders[] = $folder; } } foreach ($selectedFolders as $a) { if ($a['deleted'] == 1) { continue; } $label = ($a['name'] == 'My Email' ? $this->modStrings['LNK_MY_INBOX'] : $a['name']); $folderNode = new ExtNode($a['id'], $label); $folderNode->dynamicloadfunction = ''; $folderNode->expanded = false; if (array_key_exists('Home::' . $a['id'], $folderStates)) { if ($folderStates['Home::' . $a['id']] == 'open') { $folderNode->expanded = true; } } $nodePath = "Home::" . $folderNode->_properties['id']; $folderNode->dynamic_load = true; $folderNode->set_property('ieId', 'folder'); $folderNode->set_property('is_group', ($a['is_group'] == 1) ? 'true' : 'false'); $folderNode->set_property('is_dynamic', ($a['is_dynamic'] == 1) ? 'true' : 'false'); $folderNode->set_property('mbox', $folderNode->_properties['id']); $folderNode->set_property('id', $a['id']); $folderNode->set_property('folder_type', $a['folder_type']); $folderNode->set_property('children', array()); if (in_array($a['id'], $subscriptions) && $a['has_child'] == 1) { $qGetChildren = $this->core . $this->coreWhere . "AND parent_folder = " . $this->db->quoted($a['id']); $rGetChildren = $this->db->query($qGetChildren); while ($aGetChildren = $this->db->fetchByAssoc($rGetChildren)) { if (in_array($aGetChildren['id'], $subscriptions)) { $folderNode->add_node( $this->buildTreeNodeFolders( $aGetChildren, $nodePath, $folderStates, $subscriptions ) ); } } } if (is_null($rootNode)) { $guid = create_guid(); $label = 'Parent'; $rootNode = new ExtNode($guid, $label); } $rootNode->add_node($folderNode); } /* the code below is called only by Settings->Folders when selecting folders to subscribe to */ if ($forRefresh) { $metaNode = array(); if (!empty($rootNode->nodes)) { foreach ($rootNode->nodes as $node) { $metaNode[] = $this->buildTreeNodeRefresh($node, $subscriptions); } } return $metaNode; } } /** * Builds up a metanode for folder refresh (Sugar folders only) * * @param string $folderNode * @param array $subscriptions * @return array */ public function buildTreeNodeRefresh($folderNode, $subscriptions) { $metaNode = $folderNode->_properties; $metaNode['expanded'] = $folderNode->expanded; $metaNode['text'] = $folderNode->_label; if ($metaNode['is_group'] == 'true') { $metaNode['cls'] = 'groupFolder'; } else { $metaNode['cls'] = 'sugarFolder'; } $metaNode['id'] = $folderNode->_properties['id']; $metaNode['children'] = array(); $metaNode['type'] = 1; $metaNode['leaf'] = false; $metaNode['isTarget'] = true; $metaNode['allowChildren'] = true; if (!empty($folderNode->nodes)) { foreach ($folderNode->nodes as $node) { if (in_array($node->_properties['id'], $subscriptions)) { $metaNode['children'][] = $this->buildTreeNodeRefresh($node, $subscriptions); } } } return $metaNode; } /** * Builds children nodes for folders for TreeView * @return $folderNode TreeView node */ public function buildTreeNodeFolders($a, $nodePath, $folderStates, $subscriptions) { $label = $a['name']; if ($a['name'] == 'My Drafts') { $label = $this->modStrings['LBL_LIST_TITLE_MY_DRAFTS']; } if ($a['name'] == 'Sent Emails') { $label = $this->modStrings['LBL_LIST_TITLE_MY_SENT']; } $folderNode = new ExtNode($a['id'], $label); $folderNode->dynamicloadfunction = ''; $folderNode->expanded = false; $nodePath .= "::{$a['id']}"; if (array_key_exists($nodePath, $folderStates)) { if ($folderStates[$nodePath] == 'open') { $folderNode->expanded = true; } } $folderNode->dynamic_load = true; $folderNode->set_property( 'click', "SUGAR.email2.listView.populateListFrameSugarFolder(". "YAHOO.namespace('frameFolders').selectednode, '{$a['id']}', 'false');" ); $folderNode->set_property('ieId', 'folder'); $folderNode->set_property('mbox', $a['id']); $folderNode->set_property('is_group', ($a['is_group'] == 1) ? 'true' : 'false'); $folderNode->set_property('is_dynamic', ($a['is_dynamic'] == 1) ? 'true' : 'false'); $folderNode->set_property('folder_type', $a['folder_type']); if (in_array($a['id'], $subscriptions) && $a['has_child'] == 1) { $qGetChildren = $this->core . $this->coreWhere . "AND parent_folder = " . $this->db->quoted($a['id']) . $this->coreOrderBy; $rGetChildren = $this->db->query($qGetChildren); while ($aGetChildren = $this->db->fetchByAssoc($rGetChildren)) { $folderNode->add_node( $this->buildTreeNodeFolders( $aGetChildren, $nodePath, $folderStates, $subscriptions ) ); } } return $folderNode; } /** * Flags a folder as deleted * * @return bool True on success */ public function delete() { if (!empty($this->id)) { if ($this->has_child) { $this->deleteChildrenCascade($this->id); } $ownerCheck = ($this->currentUser->is_admin == 0) ? " AND created_by = " . $this->db->quoted($this->currentUser->id) : ""; $query = "UPDATE folders SET deleted = 1 WHERE id = " . $this->db->quoted($this->id) . $ownerCheck; $r = $this->db->query($query); return true; } return false; } /** * Deletes all children in a cascade * * @param string $id ID of parent * @return bool True on success */ public function deleteChildrenCascade($id) { $canContinue = true; $checkInboundQuery = "SELECT count(*) c FROM inbound_email WHERE groupfolder_id = " . $this->db->quoted($id) . " AND deleted = 0"; $resultSet = $this->db->query($checkInboundQuery); $a = $this->db->fetchByAssoc($resultSet); if ($a['c'] > 0) { return false; } $q = "SELECT COUNT(*) c FROM folders_rel WHERE polymorphic_module = 'Emails' ". "AND polymorphic_id = " . $this->db->quoted($id) . " AND folder_id = " . $this->db->quoted($this->id); $checkEmailQuery = "SELECT count(*) c FROM folders_rel WHERE polymorphic_module = 'Emails' ". "AND folder_id = " . $this->db->quoted($id) . " AND deleted = 0"; $resultSet = $this->db->query($checkEmailQuery); $a = $this->db->fetchByAssoc($resultSet); if ($a['c'] > 0) { return false; } $query = "SELECT * FROM folders WHERE id = " . $this->db->quoted($id); $r = $this->db->query($query); $a = $this->db->fetchByAssoc($r); if ($a['has_child'] == 1) { $query2 = "SELECT id FROM folders WHERE parent_folder = " . $this->db->quoted($id); $r2 = $this->db->query($query2); while ($a2 = $this->db->fetchByAssoc($r2)) { $canContinue = $this->deleteChildrenCascade($a2['id']); } } if ($canContinue) { // flag deleted $ownerCheck = ($this->currentUser->is_admin == 0) ? " AND created_by = " . $this->db->quoted($this->currentUser->id) . "" : ""; $query3 = "UPDATE folders SET deleted = 1 WHERE id = " . $this->db->quoted($id) . $ownerCheck; $r3 = $this->db->query($query3); // flag rels $qRel = "UPDATE folders_rel SET deleted = 1 WHERE folder_id = " . $this->db->quoted($id); $rRel = $this->db->query($qRel); // delete subscriptions $qSub = "DELETE FROM folders_subscriptions WHERE folder_id = " . $this->db->quoted($id); $rSub = $this->db->query($qSub); } return $canContinue; } /** * Saves folder * * @param boolean $addSubscriptions Add the Subscriptions * @return boolean */ public function save($addSubscriptions = true) { $this->dynamic_query = $this->db->quote($this->dynamic_query); if ((!empty($this->id) && $this->new_with_id == false) || (empty($this->id) && $this->new_with_id == true)) { if (empty($this->id) && $this->new_with_id == true) { $guid = create_guid(); $this->id = $guid; } $query = "INSERT INTO folders (id, name, folder_type, parent_folder, has_child, is_group, " . "is_dynamic, dynamic_query, assign_to_id, created_by, modified_by, deleted) VALUES (" . $this->db->quoted($this->id) . ", " . $this->db->quoted($this->name) . ", " . $this->db->quoted($this->folder_type) . ", " . $this->db->quoted($this->parent_folder) . ", " . $this->db->quoted($this->has_child) . ", " . $this->db->quoted($this->is_group) . ", " . $this->db->quoted($this->is_dynamic) . ", " . $this->db->quoted($this->dynamic_query) . ", " . $this->db->quoted($this->assign_to_id) . ", " . $this->db->quoted($this->currentUser->id) . ", " . $this->db->quoted($this->currentUser->id) . ", 0)"; if ($addSubscriptions) { // create default subscription $this->addSubscriptionsToGroupFolder(); } // if parent_id is set, update parent's has_child flag if (!empty($this->parent_folder)) { $query3 = "UPDATE folders SET has_child = 1 WHERE id = " . $this->db->quoted($this->parent_folder); $r3 = $this->db->query($query3); } } else { $query = "UPDATE folders SET " . "name = " . $this->db->quoted($this->name) . ", " . "parent_folder = " . $this->db->quoted($this->parent_folder) . ", " . "dynamic_query = " . $this->db->quoted($this->dynamic_query) . ", " . "assign_to_id = " . $this->db->quoted($this->assign_to_id) . ", " . "modified_by = " . $this->db->quoted($this->currentUser->id) . " " . "WHERE id = " . $this->db->quoted($this->id); } return $this->db->query($query, true); } /** * Add subscriptions to this group folder. * * @return void */ public function addSubscriptionsToGroupFolder() { $this->createSubscriptionForUser($this->currentUser->id); } /** * Add subscriptions to this group folder. * * @return boolean */ public function createSubscriptionForUser($user_id) { $guid2 = create_guid(); $query = "INSERT INTO folders_subscriptions VALUES(" . $this->db->quoted($guid2) . ", " . $this->db->quoted($this->id) . ", " . $this->db->quoted($user_id) . ")"; return $this->db->query($query); } /** * Update the folder * * @param array $fields * @return array */ public function updateFolder($fields) { $id = $fields['record']; $name = $fields['name']; $parent_folder = $fields['parent_folder']; // first do the retrieve $this->retrieve($id); if ($this->has_child) { $childrenArray = array(); $this->findAllChildren($id, $childrenArray); if (in_array($parent_folder, $childrenArray)) { return array('status' => "failed", 'message' => "Can not add this folder to its children"); } } // update has_child to 0 for this parent folder if this is the only child it has $query1 = "select count(*) count from folders where deleted = 0 AND parent_folder = " . $this->db->quoted($this->parent_folder); $r1 = $this->db->query($query1); $a1 = $this->db->fetchByAssoc($r1); if ($a1['count'] == 1) { $query1 = "UPDATE folders SET has_child = 0 WHERE id = " . $this->db->quoted($this->parent_folder); $r1 = $this->db->query($query1); } $this->name = $name; $this->parent_folder = $parent_folder; $query2 = "UPDATE folders SET name = " . $this->db->query($this->name) . ", parent_folder = " . $this->db->quoted($this->parent_folder) . "," . " dynamic_query = " . $this->db->query($this->dynamic_query) . ", " . "modified_by = " . $this->db->query($this->currentUser->id) . " WHERE id = " . $this->db->quoted($this->id); $r2 = $this->db->query($query2); if (!empty($this->parent_folder)) { $query3 = "UPDATE folders SET has_child = 1 WHERE id = " . $this->db->quoted($this->parent_folder); $r3 = $this->db->query($query3); } return array('status' => "done"); } /** * Find all the children * * @param string $folderId * @param array &$childrenArray * @return void */ public function findAllChildren($folderId, &$childrenArray) { $query = "SELECT * FROM folders WHERE id = " . $this->db->quoted($folderId); $r = $this->db->query($query); $a = $this->db->fetchByAssoc($r); if ($a['has_child'] == 1) { $query2 = "SELECT id FROM folders WHERE deleted = 0 AND parent_folder = " . $this->db->quoted($folderId); $r2 = $this->db->query($query2); while ($a2 = $this->db->fetchByAssoc($r2)) { $childrenArray[] = $a2['id']; $this->findAllChildren($a2['id'], $childrenArray); } } } /** * Retrieves and populates object * * @param string $id ID of folder * @return boolean True on success */ public function retrieve($id) { $query = "SELECT * FROM folders WHERE id = " . $this->db->quoted($id) . " AND deleted = 0"; $r = $this->db->query($query); $a = $this->db->fetchByAssoc($r); if (!empty($a)) { foreach ($a as $k => $v) { if ($k == 'dynamic_query') { $v = from_html($v); } $this->$k = $v; } $new_with_id = false; return true; } return false; } /** * Get first display folder * @return mixed|null */ public function getFirstDisplayFolders(): ?array { global $current_user; $settingsFolders = $this->getFoldersForSettings($current_user); foreach ($settingsFolders['userFolders'] as $folder) { $isSelected = $folder['selected'] ?? false; if (isFalse($isSelected)) { continue; } return $folder; } foreach ($settingsFolders['groupFolders'] as $folder) { $isSelected = $folder['selected'] ?? false; if (isFalse($isSelected)) { continue; } return $folder; } return null; } /** * Check if it subscribed * @param string|null $folderId * @param array|null $folders * @return bool */ public function isToDisplay(?string $folderId, array $folders = null): bool { global $current_user; if (empty($folderId)){ return false; } if ($folders === null){ $folders = $this->getFoldersForSettings($current_user); } if ($this->shouldFolderDisplay($folders['userFolders'] ?? [], $folderId)) { return true; } if ($this->shouldFolderDisplay($folders['groupFolders'] ?? [], $folderId)) { return true; } return false; } /** * @param array $folders * @param string $folderId * @return bool */ protected function shouldFolderDisplay(array $folders, string $folderId): bool { if (empty($folders)) { return false; } foreach ($folders as $folder) { $isSelected = $folder['selected'] ?? false; if (isFalse($isSelected)) { continue; } $id = $folder['id'] ?? ''; if ($id === $folderId) { return true; } } return false; } /** * @param User|null $user * @return array */ protected function getUserAccessibleInboundIds(?User $user): array { $userAccessibleInboundAccountIds = []; /** @var InboundEmail $inboundEmail */ $inboundEmail = BeanFactory::newBean('InboundEmail'); $accessibleInboundEmails = $inboundEmail->getUserInboundAccounts(); if (!empty($accessibleInboundEmails)) { foreach ($accessibleInboundEmails as $accessibleInboundEmail) { if (empty($accessibleInboundEmail)) { continue; } $id = $accessibleInboundEmail->id ?? ''; if (!empty($id)) { $userAccessibleInboundAccountIds[$id] = true; } } } return $userAccessibleInboundAccountIds; } } // end class def