mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-06-28 06:46:27 +00:00
Resolve #717
This commit is contained in:
@@ -11,6 +11,7 @@ import { leftPanelDisabled, toggleLeftPanel,
|
||||
addShortcut, registerShortcut, formFieldFocused
|
||||
} from 'Common/Globals';
|
||||
|
||||
import { arrayLength } from 'Common/Utils';
|
||||
import { computedPaginatorHelper, showMessageComposer, populateMessageBody, download, moveAction } from 'Common/UtilsUser';
|
||||
import { FileInfo } from 'Common/File';
|
||||
import { isFullscreen, toggleFullscreen } from 'Common/Fullscreen';
|
||||
@@ -102,6 +103,9 @@ export class MailMessageList extends AbstractViewRight {
|
||||
this.dragOver = ko.observable(false).extend({ throttle: 1 });
|
||||
this.dragOverEnter = ko.observable(false).extend({ throttle: 1 });
|
||||
|
||||
const attachmentsActions = Settings.app('attachmentsActions');
|
||||
this.attachmentsActions = ko.observableArray(arrayLength(attachmentsActions) ? attachmentsActions : []);
|
||||
|
||||
addComputablesTo(this, {
|
||||
|
||||
sortSupported: () =>
|
||||
@@ -148,7 +152,9 @@ export class MailMessageList extends AbstractViewRight {
|
||||
return '𝐒' + (desc ? '⬆' : '⬇');
|
||||
}
|
||||
return (mode.includes('SIZE') ? '✉' : '📅') + (desc ? '⬇' : '⬆');
|
||||
}
|
||||
},
|
||||
|
||||
downloadAsZipAllowed: () => this.attachmentsActions.includes('zip')
|
||||
});
|
||||
|
||||
this.selector = new Selector(
|
||||
@@ -259,7 +265,8 @@ export class MailMessageList extends AbstractViewRight {
|
||||
).throttle(50));
|
||||
|
||||
decorateKoCommands(this, {
|
||||
downloadCommand: canBeMovedHelper,
|
||||
downloadAttachCommand: canBeMovedHelper,
|
||||
downloadZipCommand: canBeMovedHelper,
|
||||
forwardCommand: canBeMovedHelper,
|
||||
deleteWithoutMoveCommand: canBeMovedHelper,
|
||||
deleteCommand: canBeMovedHelper,
|
||||
@@ -292,7 +299,30 @@ export class MailMessageList extends AbstractViewRight {
|
||||
]);
|
||||
}
|
||||
|
||||
downloadCommand() {
|
||||
downloadZipCommand() {
|
||||
let hashes = []/*, uids = []*/;
|
||||
// MessagelistUserStore.forEach(message => message.checked() && uids.push(message.uid));
|
||||
MessagelistUserStore.forEach(message => message.checked() && hashes.push(message.requestHash));
|
||||
if (hashes.length) {
|
||||
Remote.post('AttachmentsActions', null, {
|
||||
Do: 'Zip',
|
||||
Folder: MessagelistUserStore().Folder,
|
||||
// Uids: uids,
|
||||
Hashes: hashes
|
||||
})
|
||||
.then(result => {
|
||||
let hash = result?.Result?.FileHash;
|
||||
if (hash) {
|
||||
download(attachmentDownload(hash), hash+'.zip');
|
||||
} else {
|
||||
alert('Download failed');
|
||||
}
|
||||
})
|
||||
.catch(() => alert('Download failed'));
|
||||
}
|
||||
}
|
||||
|
||||
downloadAttachCommand() {
|
||||
let hashes = [];
|
||||
MessagelistUserStore.forEach(message => {
|
||||
if (message.checked()) {
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace RainLoop\Actions;
|
||||
|
||||
use RainLoop\Enumerations\Capa;
|
||||
use RainLoop\Utils;
|
||||
|
||||
trait Attachments
|
||||
{
|
||||
/**
|
||||
* @throws \MailSo\RuntimeException
|
||||
*/
|
||||
public function DoAttachmentsActions() : array
|
||||
{
|
||||
$sAction = $this->GetActionParam('Do', '');
|
||||
$sFolder = $this->GetActionParam('Folder', '');
|
||||
$aHashes = $this->GetActionParam('Hashes', null);
|
||||
$oFilesProvider = $this->FilesProvider();
|
||||
if (empty($sAction) || !$this->GetCapa(Capa::ATTACHMENTS_ACTIONS) || !$oFilesProvider || !$oFilesProvider->IsActive()) {
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
$bError = false;
|
||||
$aData = [];
|
||||
$mUIDs = [];
|
||||
|
||||
if (\is_array($aHashes) && \count($aHashes)) {
|
||||
foreach ($aHashes as $sZipHash) {
|
||||
$aResult = $this->getMimeFileByHash($oAccount, $sZipHash);
|
||||
if (empty($aResult['FileHash'])) {
|
||||
$bError = true;
|
||||
break;
|
||||
}
|
||||
$aData[] = $aResult;
|
||||
if (!empty($aResult['MimeIndex'])) {
|
||||
$mUIDs[$aResult['Uid']] = $aResult['Uid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$mUIDs = 1 < \count($mUIDs);
|
||||
|
||||
if ($bError || !\count($aData)) {
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$mResult = false;
|
||||
switch (\strtolower($sAction))
|
||||
{
|
||||
case 'zip':
|
||||
|
||||
$sZipHash = \MailSo\Base\Utils::Sha1Rand();
|
||||
$sZipFileName = $oFilesProvider->GenerateLocalFullFileName($oAccount, $sZipHash);
|
||||
|
||||
if (!empty($sZipFileName)) {
|
||||
if (\class_exists('ZipArchive')) {
|
||||
$oZip = new \ZipArchive();
|
||||
$oZip->open($sZipFileName, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE);
|
||||
$oZip->setArchiveComment('SnappyMail/'.APP_VERSION);
|
||||
foreach ($aData as $aItem) {
|
||||
$sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $aItem['FileHash']);
|
||||
$sFileName = ($mUIDs ? "{$aItem['Uid']}/" : ($sFolder ? "{$aItem['Uid']}-" : '')) . $aItem['FileName'];
|
||||
if (!$oZip->addFile($sFullFileNameHash, $sFileName)) {
|
||||
$bError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bError) {
|
||||
$oZip->close();
|
||||
} else {
|
||||
$bError = !$oZip->close();
|
||||
}
|
||||
/*
|
||||
} else {
|
||||
@\unlink($sZipFileName);
|
||||
$oZip = new \SnappyMail\Stream\ZIP($sZipFileName);
|
||||
// $oZip->setArchiveComment('SnappyMail/'.APP_VERSION);
|
||||
foreach ($aData as $aItem) {
|
||||
if ($aItem['FileHash']) {
|
||||
$sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $aItem['FileHash']);
|
||||
if (!$oZip->addFile($sFullFileNameHash, $aItem['FileName'])) {
|
||||
$bError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$oZip->close();
|
||||
*/
|
||||
} else {
|
||||
@\unlink($sZipFileName);
|
||||
$oZip = new \PharData($sZipFileName . '.zip', 0, null, \Phar::ZIP);
|
||||
$oZip->compressFiles(\Phar::GZ);
|
||||
foreach ($aData as $aItem) {
|
||||
$oZip->addFile(
|
||||
$oFilesProvider->GetFileName($oAccount, $aItem['FileHash']),
|
||||
($mUIDs ? "{$aItem['Uid']}/" : ($sFolder ? "{$aItem['Uid']}-" : '')) . $aItem['FileName']
|
||||
);
|
||||
}
|
||||
$oZip->compressFiles(\Phar::GZ);
|
||||
unset($oZip);
|
||||
\rename($sZipFileName . '.zip', $sZipFileName);
|
||||
}
|
||||
|
||||
foreach ($aData as $aItem) {
|
||||
$oFilesProvider->Clear($oAccount, $aItem['FileHash']);
|
||||
}
|
||||
|
||||
if (!$bError) {
|
||||
$mResult = array(
|
||||
'FileHash' => Utils::EncodeKeyValuesQ(array(
|
||||
'Account' => $oAccount ? $oAccount->Hash() : '',
|
||||
'FileName' => ($sFolder ? 'messages' : 'attachments') . \date('-YmdHis') . '.zip',
|
||||
'MimeType' => 'application/zip',
|
||||
'FileHash' => $sZipHash
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$data = new \SnappyMail\AttachmentsAction;
|
||||
$data->action = $sAction;
|
||||
$data->items = $aData;
|
||||
$data->filesProvider = $oFilesProvider;
|
||||
$data->account = $oAccount;
|
||||
$this->Plugins()->RunHook('json.attachments', array($data));
|
||||
$mResult = $data->result;
|
||||
break;
|
||||
}
|
||||
|
||||
// $this->requestSleep();
|
||||
return $this->DefaultResponse(__FUNCTION__, $bError ? false : $mResult);
|
||||
}
|
||||
|
||||
private function getMimeFileByHash(\RainLoop\Model\Account $oAccount, string $sHash) : array
|
||||
{
|
||||
$aValues = $this->getDecodedRawKeyValue($sHash);
|
||||
|
||||
$sFolder = isset($aValues['Folder']) ? (string) $aValues['Folder'] : '';
|
||||
$iUid = isset($aValues['Uid']) ? (int) $aValues['Uid'] : 0;
|
||||
$sMimeIndex = isset($aValues['MimeIndex']) ? (string) $aValues['MimeIndex'] : '';
|
||||
|
||||
$sContentTypeIn = isset($aValues['MimeType']) ? (string) $aValues['MimeType'] : '';
|
||||
$sFileNameIn = isset($aValues['FileName']) ? (string) $aValues['FileName'] : 'file.dat';
|
||||
|
||||
$oFileProvider = $this->FilesProvider();
|
||||
|
||||
$sResultHash = '';
|
||||
|
||||
$mResult = $this->MailClient()->MessageMimeStream(function ($rResource, $sContentType, $sFileName, $sMimeIndex = '')
|
||||
use ($oAccount, $oFileProvider, $sFileNameIn, $sContentTypeIn, &$sResultHash) {
|
||||
|
||||
unset($sContentType, $sFileName, $sMimeIndex);
|
||||
|
||||
if (\is_resource($rResource))
|
||||
{
|
||||
$sHash = \MailSo\Base\Utils::Sha1Rand($sFileNameIn.'~'.$sContentTypeIn);
|
||||
$rTempResource = $oFileProvider->GetFile($oAccount, $sHash, 'wb+');
|
||||
|
||||
if (\is_resource($rTempResource))
|
||||
{
|
||||
if (false !== \MailSo\Base\Utils::MultipleStreamWriter($rResource, array($rTempResource)))
|
||||
{
|
||||
$sResultHash = $sHash;
|
||||
}
|
||||
|
||||
\fclose($rTempResource);
|
||||
}
|
||||
}
|
||||
|
||||
}, $sFolder, $iUid, $sMimeIndex);
|
||||
|
||||
$aValues['FileName'] = $sFileNameIn;
|
||||
$aValues['FileHash'] = $mResult ? $sResultHash : '';
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ trait User
|
||||
use Filters;
|
||||
use Folders;
|
||||
use Messages;
|
||||
use Attachments;
|
||||
use Pgp;
|
||||
|
||||
/**
|
||||
@@ -68,131 +69,6 @@ trait User
|
||||
return $this->DefaultResponse(__FUNCTION__, $this->AppData(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \MailSo\RuntimeException
|
||||
*/
|
||||
public function DoAttachmentsActions() : array
|
||||
{
|
||||
$sAction = $this->GetActionParam('Do', '');
|
||||
$aHashes = $this->GetActionParam('Hashes', null);
|
||||
$oFilesProvider = $this->FilesProvider();
|
||||
if (empty($sAction) || !$this->GetCapa(Capa::ATTACHMENTS_ACTIONS) || !$oFilesProvider || !$oFilesProvider->IsActive()) {
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
$bError = false;
|
||||
$aData = [];
|
||||
$mUIDs = [];
|
||||
|
||||
if (\is_array($aHashes) && \count($aHashes)) {
|
||||
foreach ($aHashes as $sZipHash) {
|
||||
$aResult = $this->getMimeFileByHash($oAccount, $sZipHash);
|
||||
if (empty($aResult['FileHash'])) {
|
||||
$bError = true;
|
||||
break;
|
||||
}
|
||||
$aData[] = $aResult;
|
||||
$mUIDs[$aResult['Uid']] = $aResult['Uid'];
|
||||
}
|
||||
}
|
||||
$mUIDs = 1 < \count($mUIDs);
|
||||
|
||||
if ($bError || !\count($aData)) {
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$mResult = false;
|
||||
switch (\strtolower($sAction))
|
||||
{
|
||||
case 'zip':
|
||||
|
||||
$sZipHash = \MailSo\Base\Utils::Sha1Rand();
|
||||
$sZipFileName = $oFilesProvider->GenerateLocalFullFileName($oAccount, $sZipHash);
|
||||
|
||||
if (!empty($sZipFileName)) {
|
||||
if (\class_exists('ZipArchive')) {
|
||||
$oZip = new \ZipArchive();
|
||||
$oZip->open($sZipFileName, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE);
|
||||
$oZip->setArchiveComment('SnappyMail/'.APP_VERSION);
|
||||
foreach ($aData as $aItem) {
|
||||
$sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $aItem['FileHash']);
|
||||
$sFileName = ($mUIDs ? "{$aItem['Uid']}/" : '') . ($aItem['FileName'] ?: 'file.dat');
|
||||
if (!$oZip->addFile($sFullFileNameHash, $sFileName)) {
|
||||
$bError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bError) {
|
||||
$oZip->close();
|
||||
} else {
|
||||
$bError = !$oZip->close();
|
||||
}
|
||||
/*
|
||||
} else {
|
||||
@\unlink($sZipFileName);
|
||||
$oZip = new \SnappyMail\Stream\ZIP($sZipFileName);
|
||||
// $oZip->setArchiveComment('SnappyMail/'.APP_VERSION);
|
||||
foreach ($aData as $aItem) {
|
||||
$sFileName = (string) (isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat');
|
||||
$sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : '');
|
||||
if (!empty($sFileHash)) {
|
||||
$sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $sFileHash);
|
||||
if (!$oZip->addFile($sFullFileNameHash, $sFileName)) {
|
||||
$bError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$oZip->close();
|
||||
*/
|
||||
} else {
|
||||
@\unlink($sZipFileName);
|
||||
$oZip = new \PharData($sZipFileName . '.zip', 0, null, \Phar::ZIP);
|
||||
$oZip->compressFiles(\Phar::GZ);
|
||||
foreach ($aData as $aItem) {
|
||||
$oZip->addFile(
|
||||
$oFilesProvider->GetFileName($oAccount, $aItem['FileHash']),
|
||||
($mUIDs ? "{$aItem['Uid']}/" : '') . ($aItem['FileName'] ?: 'file.dat')
|
||||
);
|
||||
}
|
||||
$oZip->compressFiles(\Phar::GZ);
|
||||
unset($oZip);
|
||||
\rename($sZipFileName . '.zip', $sZipFileName);
|
||||
}
|
||||
|
||||
foreach ($aData as $aItem) {
|
||||
$oFilesProvider->Clear($oAccount, $aItem['FileHash']);
|
||||
}
|
||||
|
||||
if (!$bError) {
|
||||
$mResult = array(
|
||||
'FileHash' => Utils::EncodeKeyValuesQ(array(
|
||||
'Account' => $oAccount ? $oAccount->Hash() : '',
|
||||
'FileName' => 'attachments.zip',
|
||||
'MimeType' => 'application/zip',
|
||||
'FileHash' => $sZipHash
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$data = new \SnappyMail\AttachmentsAction;
|
||||
$data->action = $sAction;
|
||||
$data->items = $aData;
|
||||
$data->filesProvider = $oFilesProvider;
|
||||
$data->account = $oAccount;
|
||||
$this->Plugins()->RunHook('json.attachments', array($data));
|
||||
$mResult = $data->result;
|
||||
break;
|
||||
}
|
||||
|
||||
// $this->requestSleep();
|
||||
return $this->DefaultResponse(__FUNCTION__, $bError ? false : $mResult);
|
||||
}
|
||||
|
||||
public function DoLogout() : array
|
||||
{
|
||||
$bMain = true; // empty($_COOKIE[self::AUTH_ADDITIONAL_TOKEN_KEY]);
|
||||
@@ -468,53 +344,6 @@ trait User
|
||||
$this->SettingsProvider()->Save($oAccount, $oSettings) : false);
|
||||
}
|
||||
|
||||
private function getMimeFileByHash(\RainLoop\Model\Account $oAccount, string $sHash) : array
|
||||
{
|
||||
$aValues = $this->getDecodedRawKeyValue($sHash);
|
||||
|
||||
$sFolder = isset($aValues['Folder']) ? (string) $aValues['Folder'] : '';
|
||||
$iUid = isset($aValues['Uid']) ? (int) $aValues['Uid'] : 0;
|
||||
$sMimeIndex = isset($aValues['MimeIndex']) ? (string) $aValues['MimeIndex'] : '';
|
||||
|
||||
$sContentTypeIn = isset($aValues['MimeType']) ? (string) $aValues['MimeType'] : '';
|
||||
$sFileNameIn = isset($aValues['FileName']) ? (string) $aValues['FileName'] : '';
|
||||
|
||||
$oFileProvider = $this->FilesProvider();
|
||||
|
||||
$sResultHash = '';
|
||||
|
||||
$mResult = $this->MailClient()->MessageMimeStream(function ($rResource, $sContentType, $sFileName, $sMimeIndex = '')
|
||||
use ($oAccount, $oFileProvider, $sFileNameIn, $sContentTypeIn, &$sResultHash) {
|
||||
|
||||
unset($sContentType, $sFileName, $sMimeIndex);
|
||||
|
||||
if ($oAccount && \is_resource($rResource))
|
||||
{
|
||||
$sHash = \MailSo\Base\Utils::Sha1Rand($sFileNameIn.'~'.$sContentTypeIn);
|
||||
$rTempResource = $oFileProvider->GetFile($oAccount, $sHash, 'wb+');
|
||||
|
||||
if (\is_resource($rTempResource))
|
||||
{
|
||||
if (false !== \MailSo\Base\Utils::MultipleStreamWriter($rResource, array($rTempResource)))
|
||||
{
|
||||
$sResultHash = $sHash;
|
||||
}
|
||||
|
||||
\fclose($rTempResource);
|
||||
}
|
||||
}
|
||||
|
||||
}, $sFolder, $iUid, $sMimeIndex);
|
||||
|
||||
$aValues['FileHash'] = '';
|
||||
if ($mResult)
|
||||
{
|
||||
$aValues['FileHash'] = $sResultHash;
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
private function setSettingsFromParams(\RainLoop\Settings $oSettings, string $sConfigName, string $sType = 'string', ?callable $cCallback = null) : void
|
||||
{
|
||||
if ($this->HasActionParam($sConfigName))
|
||||
|
||||
@@ -43,9 +43,12 @@
|
||||
<li class="dividerbar" role="presentation" data-bind="command: forwardCommand">
|
||||
<a href="#" tabindex="-1" data-icon="↞" data-i18n="MESSAGE_LIST/BUTTON_MULTY_FORWARD"></a>
|
||||
</li>
|
||||
<li role="presentation" data-bind="command: downloadCommand">
|
||||
<li role="presentation" data-bind="command: downloadAttachCommand, visible: downloadAsZipAllowed">
|
||||
<a href="#" tabindex="-1" data-icon="" data-i18n="MESSAGE_LIST/DOWNLOAD_ALL_ATTACHMENTS"></a>
|
||||
</li>
|
||||
<li role="presentation" data-bind="command: downloadZipCommand, visible: downloadAsZipAllowed">
|
||||
<a href="#" tabindex="-1" data-icon="💾" data-i18n="MESSAGE/LINK_DOWNLOAD_AS_ZIP"></a>
|
||||
</li>
|
||||
<li class="dividerbar" role="presentation" data-bind="command: moveCommand">
|
||||
<a href="#" tabindex="-1" data-icon="📁" data-i18n="GLOBAL/MOVE_TO"></a>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user