Skip to content

Commit 9f8d483

Browse files
committed
feat: ✨ Allow saving of compose_data somewhere other than in the session
1 parent be1594c commit 9f8d483

File tree

4 files changed

+139
-11
lines changed

4 files changed

+139
-11
lines changed

config/config.inc.php.sample

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,8 @@ $config['plugins'] = [
6464

6565
// skin name: folder from skins/
6666
$config['skin'] = 'elastic';
67+
// Backend to use for compose_data storage.
68+
//Can either be 'session' (default), 'redis', 'memcache', 'apc', or 'db'
69+
$config['compose_data_storage'] = 'session';
70+
// Lifetime of compose data storage. Possible units: s, m, h, d, w
71+
$config['compose_data_storage_ttl'] = '24h';

config/defaults.inc.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,3 +1567,8 @@
15671567
// 0 - Reply-All always
15681568
// 1 - Reply-List if mailing list is detected
15691569
$config['reply_all_mode'] = 0;
1570+
// Backend to use for compose_data storage.
1571+
//Can either be 'session' (default), 'redis', 'memcache', 'apc', or 'db'
1572+
$config['compose_data_storage'] = 'session';
1573+
// Lifetime of compose data storage. Possible units: s, m, h, d, w
1574+
$config['compose_data_storage_ttl'] = '24h';

program/actions/mail/compose.php

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,17 @@ public function run($args = [])
4242
self::$COMPOSE_ID = rcube_utils::get_input_string('_id', rcube_utils::INPUT_GET);
4343
self::$COMPOSE = null;
4444

45-
if (self::$COMPOSE_ID && !empty($_SESSION['compose_data_' . self::$COMPOSE_ID])) {
46-
self::$COMPOSE = &$_SESSION['compose_data_' . self::$COMPOSE_ID];
45+
if (self::$COMPOSE_ID && !empty(self::get_compose_data(self::$COMPOSE_ID))) {
46+
self::$COMPOSE = self::get_compose_data(self::$COMPOSE_ID);
4747
}
4848

4949
// give replicated session storage some time to synchronize
5050
$retries = 0;
5151
while (self::$COMPOSE_ID && !is_array(self::$COMPOSE) && $rcmail->db->is_replicated() && $retries++ < 5) {
5252
usleep(500000);
5353
$rcmail->session->reload();
54-
if ($_SESSION['compose_data_' . self::$COMPOSE_ID]) {
55-
self::$COMPOSE = &$_SESSION['compose_data_' . self::$COMPOSE_ID];
54+
if (self::get_compose_data(self::$COMPOSE_ID)) {
55+
self::$COMPOSE = self::get_compose_data(self::$COMPOSE_ID);
5656
}
5757
}
5858

@@ -74,15 +74,14 @@ public function run($args = [])
7474
self::$COMPOSE_ID = uniqid(mt_rand());
7575
$params = rcube_utils::request2param(rcube_utils::INPUT_GET, 'task|action', true);
7676

77-
$_SESSION['compose_data_' . self::$COMPOSE_ID] = [
78-
'id' => self::$COMPOSE_ID,
79-
'param' => $params,
77+
self::$COMPOSE = [
78+
'id' => self::$COMPOSE_ID,
79+
'param' => $params,
8080
'mailbox' => isset($params['mbox']) && strlen($params['mbox'])
8181
? $params['mbox'] : $rcmail->storage->get_folder(),
8282
];
83-
84-
self::$COMPOSE = &$_SESSION['compose_data_' . self::$COMPOSE_ID];
8583
self::process_compose_params(self::$COMPOSE);
84+
self::set_compose_data(self::$COMPOSE_ID, self::$COMPOSE);
8685

8786
// check if folder for saving sent messages exists and is subscribed (#1486802)
8887
if (!empty(self::$COMPOSE['param']['sent_mbox'])) {
@@ -336,6 +335,8 @@ public function run($args = [])
336335

337336
self::spellchecker_init();
338337

338+
self::set_compose_data(self::$COMPOSE_ID, self::$COMPOSE);
339+
339340
$rcmail->output->send('compose');
340341
}
341342

@@ -1730,4 +1731,121 @@ public static function quote_text($text)
17301731

17311732
return rtrim($out, "\n");
17321733
}
1734+
1735+
/**
1736+
* Handles storage actions (get, set, remove) for compose data using the configured backend.
1737+
*
1738+
* @param string $id The compose data identifier
1739+
* @param int $type The action type: 0 = get, 1 = set, 2 = remove
1740+
* @param mixed $data Optional data to store (used for set)
1741+
* @return mixed|null The compose data for get, or null for set/remove
1742+
* @throws Exception If an invalid storage type or action is provided
1743+
*/
1744+
private static function _compose_storage_action($id, $type, $data = null) {
1745+
$compose_data = null;
1746+
$rcmail = rcmail::get_instance();
1747+
$storage_type = $rcmail->config->get('compose_data_storage', 'session');
1748+
1749+
switch ($storage_type) {
1750+
case 'session':
1751+
$key = "compose_data_$id";
1752+
switch ($type) {
1753+
case 0:
1754+
$compose_data = $_SESSION[$key];
1755+
break;
1756+
1757+
case 1:
1758+
$_SESSION[$key] = $data;
1759+
break;
1760+
1761+
case 2:
1762+
$rcmail->session->remove($key);
1763+
break;
1764+
1765+
default:
1766+
throw new Exception("Invalide storage type");
1767+
1768+
}
1769+
break;
1770+
1771+
case 'apc':
1772+
case 'db':
1773+
case 'redis':
1774+
case 'memcache':
1775+
$ttl = $rcmail->config->get('compose_data_ttl', '8h');
1776+
$compose_data = call_user_func([$rcmail->get_cache('compose_data', $storage_type, $ttl), self::_get_cache_function($type)], $id, $data); //$id instead of $key for avoid redundant name in cache
1777+
break;
1778+
1779+
default:
1780+
$plugin = $rcmail->plugins->exec_hook(self::_get_cache_function($type).'_compose_data', ['id' => $id, 'storage_type' => $storage_type, 'data' => $data, 'compose_data' => null]);
1781+
if (isset($plugin['compose_data'])) $compose_data = $plugin['compose_data'];
1782+
break;
1783+
}
1784+
1785+
return $compose_data;
1786+
}
1787+
1788+
/**
1789+
* Returns the cache function name corresponding to the action type.
1790+
*
1791+
* @param int $type The action type: 0 = get, 1 = set, 2 = remove
1792+
* @return string The cache function name ('get', 'set', or 'remove')
1793+
* @throws Exception If an invalid action type is provided
1794+
*/
1795+
private static function _get_cache_function($type) : string {
1796+
$cache_function = null;
1797+
switch ($type) {
1798+
case 0:
1799+
$cache_function = 'get';
1800+
break;
1801+
1802+
case 1:
1803+
$cache_function = 'set';
1804+
break;
1805+
1806+
case 2:
1807+
$cache_function = 'remove';
1808+
break;
1809+
1810+
default:
1811+
throw new Exception("Invalide storage type");
1812+
1813+
}
1814+
1815+
return $cache_function;
1816+
}
1817+
1818+
/**
1819+
* Retrieve compose data by ID from the configured storage backend.
1820+
*
1821+
* @param string $id The compose data identifier
1822+
* @return mixed The compose data if found, or null otherwise
1823+
*/
1824+
public static function get_compose_data($id) {
1825+
return self::_compose_storage_action($id, 0);
1826+
}
1827+
1828+
/**
1829+
* Store compose data by ID in the configured storage backend.
1830+
*
1831+
* @param string $id The compose data identifier
1832+
* @param mixed $data The compose data to store
1833+
* @return mixed The stored compose data
1834+
*/
1835+
public static function set_compose_data($id, $data) {
1836+
self::_compose_storage_action($id, 1, $data);
1837+
1838+
return $data;
1839+
}
1840+
1841+
/**
1842+
* Remove compose data by ID from the configured storage backend.
1843+
*
1844+
* @param string $id The compose data identifier
1845+
* @return void
1846+
*/
1847+
public static function remove_compose_data($id) : void {
1848+
self::_compose_storage_action($id, 2);
1849+
}
1850+
17331851
}

program/actions/mail/send.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function run($args = [])
3535
$rcmail->output->framed = true;
3636

3737
$COMPOSE_ID = rcube_utils::get_input_string('_id', rcube_utils::INPUT_GPC);
38-
$COMPOSE = &$_SESSION['compose_data_' . $COMPOSE_ID];
38+
$COMPOSE = rcmail_action_mail_compose::get_compose_data($COMPOSE_ID);
3939

4040
// Sanity checks
4141
if (!isset($COMPOSE['id'])) {
@@ -296,7 +296,7 @@ public function run($args = [])
296296
$save_error = true;
297297
} else {
298298
$rcmail->delete_uploaded_files($COMPOSE_ID);
299-
$rcmail->session->remove('compose_data_' . $COMPOSE_ID);
299+
rcmail_action_mail_compose::remove_compose_data($COMPOSE_ID);
300300
$_SESSION['last_compose_session'] = $COMPOSE_ID;
301301

302302
$rcmail->output->command('remove_compose_data', $COMPOSE_ID);

0 commit comments

Comments
 (0)