mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-06 16:07:03 +03:00
Resolve #64
This commit is contained in:
parent
72815f842e
commit
3648b3eb60
7 changed files with 83 additions and 119 deletions
|
|
@ -116,6 +116,8 @@ export class FolderCollectionModel extends AbstractCollectionModel
|
||||||
this.namespace;
|
this.namespace;
|
||||||
this.optimized
|
this.optimized
|
||||||
this.capabilities
|
this.capabilities
|
||||||
|
this.allow; // allow adding
|
||||||
|
// this.exist;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -303,6 +305,7 @@ export class FolderModel extends AbstractModel {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.fullName = '';
|
this.fullName = '';
|
||||||
|
this.parentName = '';
|
||||||
this.delimiter = '';
|
this.delimiter = '';
|
||||||
this.deep = 0;
|
this.deep = 0;
|
||||||
this.expires = 0;
|
this.expires = 0;
|
||||||
|
|
@ -323,12 +326,10 @@ export class FolderModel extends AbstractModel {
|
||||||
|
|
||||||
focused: false,
|
focused: false,
|
||||||
selected: false,
|
selected: false,
|
||||||
editing: false,
|
|
||||||
isSubscribed: true,
|
isSubscribed: true,
|
||||||
checkable: false, // Check for new messages
|
checkable: false, // Check for new messages
|
||||||
askDelete: false,
|
askDelete: false,
|
||||||
|
|
||||||
nameForEdit: '',
|
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
|
|
||||||
totalEmails: 0,
|
totalEmails: 0,
|
||||||
|
|
@ -348,7 +349,6 @@ export class FolderModel extends AbstractModel {
|
||||||
this.addSubscribables({
|
this.addSubscribables({
|
||||||
kolabType: sValue => this.metadata[FolderMetadataKeys.KolabFolderType] = sValue,
|
kolabType: sValue => this.metadata[FolderMetadataKeys.KolabFolderType] = sValue,
|
||||||
permanentFlags: aValue => this.tagsAllowed(aValue.includes('\\*')),
|
permanentFlags: aValue => this.tagsAllowed(aValue.includes('\\*')),
|
||||||
editing: value => value && this.nameForEdit(this.name()),
|
|
||||||
unreadEmails: unread => FolderType.Inbox === this.type() && fireEvent('mailbox.inbox-unread-count', unread)
|
unreadEmails: unread => FolderType.Inbox === this.type() && fireEvent('mailbox.inbox-unread-count', unread)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -485,18 +485,21 @@ export class FolderModel extends AbstractModel {
|
||||||
showScreenPopup(FolderPopupView, [this]);
|
showScreenPopup(FolderPopupView, [this]);
|
||||||
}
|
}
|
||||||
|
|
||||||
rename() {
|
rename(nameToEdit, parentName) {
|
||||||
|
nameToEdit = nameToEdit.trim();
|
||||||
const folder = this,
|
const folder = this,
|
||||||
nameToEdit = folder.nameForEdit().trim();
|
oldFullname = folder.fullName,
|
||||||
if (nameToEdit && folder.name() !== nameToEdit) {
|
newFullname = parentName + folder.delimiter + nameToEdit;
|
||||||
|
if (nameToEdit && newFullname != oldFullname) {
|
||||||
Remote.abort('Folders').post('FolderRename', FolderUserStore.foldersRenaming, {
|
Remote.abort('Folders').post('FolderRename', FolderUserStore.foldersRenaming, {
|
||||||
folder: folder.fullName,
|
oldName: oldFullname,
|
||||||
newFolderName: nameToEdit,
|
newName: newFullname,
|
||||||
subscribe: folder.isSubscribed() ? 1 : 0
|
subscribe: folder.isSubscribed() ? 1 : 0
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then(() => {
|
||||||
folder.name(nameToEdit/*data.name*/);
|
folder.fullName = newFullname;
|
||||||
if (folder.subFolders.length) {
|
folder.name(nameToEdit);
|
||||||
|
if (folder.subFolders.length || folder.parentName != parentName) {
|
||||||
Remote.setTrigger(FolderUserStore.foldersLoading, true);
|
Remote.setTrigger(FolderUserStore.foldersLoading, true);
|
||||||
// clearTimeout(Remote.foldersTimeout);
|
// clearTimeout(Remote.foldersTimeout);
|
||||||
// Remote.foldersTimeout = setTimeout(loadFolders, 500);
|
// Remote.foldersTimeout = setTimeout(loadFolders, 500);
|
||||||
|
|
@ -504,7 +507,6 @@ export class FolderModel extends AbstractModel {
|
||||||
// TODO: rename all subfolders with folder.delimiter to prevent reload?
|
// TODO: rename all subfolders with folder.delimiter to prevent reload?
|
||||||
} else {
|
} else {
|
||||||
removeFolderFromCacheList(folder.fullName);
|
removeFolderFromCacheList(folder.fullName);
|
||||||
folder.fullName = data.Result.fullName;
|
|
||||||
setFolder(folder);
|
setFolder(folder);
|
||||||
const parent = getFolderFromCacheList(folder.parentName);
|
const parent = getFolderFromCacheList(folder.parentName);
|
||||||
sortFolders(parent ? parent.subFolders : FolderUserStore.folderList);
|
sortFolders(parent ? parent.subFolders : FolderUserStore.folderList);
|
||||||
|
|
|
||||||
|
|
@ -64,16 +64,6 @@ class RemoteUserFetch extends AbstractFetchRemote {
|
||||||
[key]: value
|
[key]: value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
folderMove(sPrevFolderFullName, sNewFolderFullName, bSubscribe) {
|
|
||||||
return this.post('FolderMove', FolderUserStore.foldersRenaming, {
|
|
||||||
folder: sPrevFolderFullName,
|
|
||||||
newFolder: sNewFolderFullName,
|
|
||||||
subscribe: bSubscribe ? 1 : 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new RemoteUserFetch();
|
export default new RemoteUserFetch();
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,42 @@
|
||||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||||
import { addObservablesTo } from 'External/ko';
|
import { addObservablesTo, koComputable } from 'External/ko';
|
||||||
import Remote from 'Remote/User/Fetch';
|
import Remote from 'Remote/User/Fetch';
|
||||||
import { FolderUserStore } from 'Stores/User/Folder';
|
import { FolderUserStore } from 'Stores/User/Folder';
|
||||||
|
|
||||||
|
import { defaultOptionsAfterRender } from 'Common/Utils';
|
||||||
|
import { folderListOptionsBuilder } from 'Common/Folders';
|
||||||
|
|
||||||
export class FolderPopupView extends AbstractViewPopup {
|
export class FolderPopupView extends AbstractViewPopup {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('Folder');
|
super('Folder');
|
||||||
addObservablesTo(this, {
|
addObservablesTo(this, {
|
||||||
folder: null // FolderModel
|
folder: null, // FolderModel
|
||||||
|
parentFolder: '',
|
||||||
|
name: '',
|
||||||
|
editing: false
|
||||||
});
|
});
|
||||||
this.ACLAllowed = FolderUserStore.hasCapability('ACL');
|
this.ACLAllowed = FolderUserStore.hasCapability('ACL');
|
||||||
this.ACL = ko.observableArray();
|
this.ACL = ko.observableArray();
|
||||||
|
|
||||||
|
this.parentFolderSelectList = koComputable(() =>
|
||||||
|
folderListOptionsBuilder(
|
||||||
|
[],
|
||||||
|
[['', '']],
|
||||||
|
oItem => oItem ? oItem.detailedName() : '',
|
||||||
|
item => !item.subFolders.allow
|
||||||
|
|| (FolderUserStore.namespace && !item.fullName.startsWith(FolderUserStore.namespace))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||||
}
|
}
|
||||||
|
|
||||||
afterHide() {
|
afterHide() {
|
||||||
this.folder().editing(false);
|
this.editing(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
submitForm(form) {
|
submitForm(form) {
|
||||||
this.folder().rename();
|
this.folder().rename(this.name(), this.parentFolder());
|
||||||
console.dir({form});
|
console.dir({form});
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +50,9 @@ export class FolderPopupView extends AbstractViewPopup {
|
||||||
}, {
|
}, {
|
||||||
folder: folder.fullName
|
folder: folder.fullName
|
||||||
});
|
});
|
||||||
folder.editing(!folder.type() && folder.exists && folder.selectable());
|
this.editing(!folder.type() && folder.exists && folder.selectable());
|
||||||
|
this.name(folder.name()),
|
||||||
|
this.parentFolder(folder.parentName);
|
||||||
this.folder(folder);
|
this.folder(folder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class NamespaceResult implements \JsonSerializable
|
||||||
if (\is_array($entry) && 2 <= \count($entry)) {
|
if (\is_array($entry) && 2 <= \count($entry)) {
|
||||||
$this->aPersonal[] = [
|
$this->aPersonal[] = [
|
||||||
'prefix' => \array_shift($entry),
|
'prefix' => \array_shift($entry),
|
||||||
'separator' => \array_shift($entry),
|
'delimiter' => \array_shift($entry),
|
||||||
'extension' => $entry
|
'extension' => $entry
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ class NamespaceResult implements \JsonSerializable
|
||||||
if (\is_array($entry) && 2 <= \count($entry)) {
|
if (\is_array($entry) && 2 <= \count($entry)) {
|
||||||
$this->aOtherUsers[] = [
|
$this->aOtherUsers[] = [
|
||||||
'prefix' => \array_shift($entry),
|
'prefix' => \array_shift($entry),
|
||||||
'separator' => \array_shift($entry),
|
'delimiter' => \array_shift($entry),
|
||||||
'extension' => $entry
|
'extension' => $entry
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +51,7 @@ class NamespaceResult implements \JsonSerializable
|
||||||
if (\is_array($entry) && 2 <= \count($entry)) {
|
if (\is_array($entry) && 2 <= \count($entry)) {
|
||||||
$this->aShared[] = [
|
$this->aShared[] = [
|
||||||
'prefix' => \array_shift($entry),
|
'prefix' => \array_shift($entry),
|
||||||
'separator' => \array_shift($entry),
|
'delimiter' => \array_shift($entry),
|
||||||
'extension' => $entry
|
'extension' => $entry
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -64,9 +64,9 @@ class NamespaceResult implements \JsonSerializable
|
||||||
$sPrefix = '';
|
$sPrefix = '';
|
||||||
if (isset($this->aPersonal[0])) {
|
if (isset($this->aPersonal[0])) {
|
||||||
$sPrefix = $this->aPersonal[0]['prefix'];
|
$sPrefix = $this->aPersonal[0]['prefix'];
|
||||||
$sSeparator = $this->aPersonal[0]['separator'];
|
$sDelimiter = $this->aPersonal[0]['delimiter'];
|
||||||
if ('INBOX'.$sSeparator === \substr(\strtoupper($sPrefix), 0, 6)) {
|
if ('INBOX'.$sDelimiter === \substr(\strtoupper($sPrefix), 0, 6)) {
|
||||||
$sPrefix = 'INBOX'.$sSeparator.\substr($sPrefix, 6);
|
$sPrefix = 'INBOX'.$sDelimiter.\substr($sPrefix, 6);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return $sPrefix;
|
return $sPrefix;
|
||||||
|
|
|
||||||
|
|
@ -947,50 +947,22 @@ class MailClient
|
||||||
/**
|
/**
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
public function FolderMove(string $sPrevFolderFullName, string $sNextFolderFullNameInUtf, bool $bSubscribeOnMove = true) : self
|
public function FolderRename(string $sPrevFolderFullName, string $sNewFolderFullName, bool $bSubscribe = true) : self
|
||||||
{
|
|
||||||
if (!$this->oImapClient->FolderHierarchyDelimiter($sPrevFolderFullName)) {
|
|
||||||
// TODO: Translate
|
|
||||||
throw new \MailSo\RuntimeException('Cannot move non-existent folder.');
|
|
||||||
}
|
|
||||||
return $this->folderModify($sPrevFolderFullName, $sNextFolderFullNameInUtf, $bSubscribeOnMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function FolderRename(string $sPrevFolderFullName, string $sNewTopFolderNameInUtf, bool $bSubscribeOnRename = true) : string
|
|
||||||
{
|
|
||||||
$sDelimiter = $this->oImapClient->FolderHierarchyDelimiter($sPrevFolderFullName);
|
|
||||||
if (!$sDelimiter) {
|
|
||||||
// TODO: Translate
|
|
||||||
throw new \MailSo\RuntimeException('Cannot rename non-existent folder.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (\strlen($sDelimiter) && false !== \strpos($sNewTopFolderNameInUtf, $sDelimiter)) {
|
|
||||||
// TODO: Translate
|
|
||||||
throw new \MailSo\RuntimeException('New folder name contains delimiter.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$iLast = \strrpos($sPrevFolderFullName, $sDelimiter);
|
|
||||||
$sNewFolderFullName = (false === $iLast ? '' : \substr($sPrevFolderFullName, 0, $iLast + 1))
|
|
||||||
. $sNewTopFolderNameInUtf;
|
|
||||||
|
|
||||||
$this->folderModify($sPrevFolderFullName, $sNewFolderFullName, $bSubscribeOnRename);
|
|
||||||
|
|
||||||
return $sNewFolderFullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws \ValueError
|
|
||||||
* @throws \MailSo\RuntimeException
|
|
||||||
*/
|
|
||||||
protected function folderModify(string $sPrevFolderFullName, string $sNewFolderFullName, bool $bSubscribe) : self
|
|
||||||
{
|
{
|
||||||
if (!\strlen($sPrevFolderFullName) || !\strlen($sNewFolderFullName)) {
|
if (!\strlen($sPrevFolderFullName) || !\strlen($sNewFolderFullName)) {
|
||||||
throw new \ValueError;
|
throw new \ValueError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$this->oImapClient->FolderHierarchyDelimiter($sPrevFolderFullName)) {
|
||||||
|
// TODO: Translate
|
||||||
|
throw new \MailSo\RuntimeException('Cannot rename non-existent folder.');
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (\strlen($sDelimiter) && false !== \strpos($sNewFolderFullName, $sDelimiter)) {
|
||||||
|
// TODO: Translate
|
||||||
|
throw new \MailSo\RuntimeException('New folder name contains delimiter.');
|
||||||
|
}
|
||||||
|
*/
|
||||||
$oSubscribedFolders = array();
|
$oSubscribedFolders = array();
|
||||||
if ($bSubscribe) {
|
if ($bSubscribe) {
|
||||||
$oSubscribedFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullName, '*');
|
$oSubscribedFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullName, '*');
|
||||||
|
|
|
||||||
|
|
@ -190,15 +190,15 @@ trait Folders
|
||||||
/**
|
/**
|
||||||
* @throws \MailSo\RuntimeException
|
* @throws \MailSo\RuntimeException
|
||||||
*/
|
*/
|
||||||
public function DoFolderMove() : array
|
public function DoFolderRename() : array
|
||||||
{
|
{
|
||||||
$this->initMailClientConnection();
|
$this->initMailClientConnection();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$this->MailClient()->FolderMove(
|
$sFullName = $this->MailClient()->FolderRename(
|
||||||
$this->GetActionParam('folder', ''),
|
$this->GetActionParam('oldName', ''),
|
||||||
$this->GetActionParam('newFolder', ''),
|
$this->GetActionParam('newName', ''),
|
||||||
!empty($this->GetActionParam('subscribe', 1))
|
!empty($this->GetActionParam('subscribe', 1))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -210,34 +210,6 @@ trait Folders
|
||||||
return $this->TrueResponse();
|
return $this->TrueResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws \MailSo\RuntimeException
|
|
||||||
*/
|
|
||||||
public function DoFolderRename() : array
|
|
||||||
{
|
|
||||||
$this->initMailClientConnection();
|
|
||||||
|
|
||||||
$sName = $this->GetActionParam('newFolderName', '');
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$sFullName = $this->MailClient()->FolderRename(
|
|
||||||
$this->GetActionParam('folder', ''),
|
|
||||||
$sName,
|
|
||||||
!empty($this->GetActionParam('subscribe', 1))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (\Throwable $oException)
|
|
||||||
{
|
|
||||||
throw new ClientException(Notifications::CantRenameFolder, $oException);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FolderInformation(string $sFolderName, int $iPrevUidNext = 0, array $aUids = array())
|
|
||||||
return $this->DefaultResponse(array(
|
|
||||||
'name' => $sName,
|
|
||||||
'fullName' => $sFullName,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \MailSo\RuntimeException
|
* @throws \MailSo\RuntimeException
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,31 @@
|
||||||
<h3 data-bind="text: folder()?.fullName"></h3>
|
<h3 data-bind="text: folder()?.fullName"></h3>
|
||||||
</header>
|
</header>
|
||||||
<form id="folderform" class="modal-body form-horizontal" autocomplete="off" spellcheck="false" data-bind="submit: submitForm">
|
<form id="folderform" class="modal-body form-horizontal" autocomplete="off" spellcheck="false" data-bind="submit: submitForm">
|
||||||
|
<div class="control-group" data-bind="if: editing">
|
||||||
|
<label data-i18n="POPUPS_CREATE_FOLDER/LABEL_PARENT"></label>
|
||||||
|
<select name="parent" data-bind="options: parentFolderSelectList, value: parentFolder,
|
||||||
|
optionsText: 'name', optionsValue: 'id', optionsAfterRender: defaultOptionsAfterRender"></select>
|
||||||
|
</div>
|
||||||
|
<div class="control-group" data-bind="if: editing">
|
||||||
|
<label data-i18n="GLOBAL/NAME"></label>
|
||||||
|
<input name="folder" type="text"
|
||||||
|
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
|
required="" data-bind="textInput: name">
|
||||||
|
</div>
|
||||||
<div data-bind="with: folder">
|
<div data-bind="with: folder">
|
||||||
<div class="control-group" data-bind="if: editing">
|
<!-- ko if: canBeSubscribed -->
|
||||||
<label data-i18n="GLOBAL/NAME"></label>
|
<div class="control-group">
|
||||||
<input name="folder" type="text"
|
<label></label>
|
||||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
<div data-bind="component: {
|
||||||
required="" data-bind="textInput: nameForEdit">
|
name: 'Checkbox',
|
||||||
|
params: {
|
||||||
|
name: 'subscribe',
|
||||||
|
label: 'SETTINGS_FOLDERS/BUTTON_SUBSCRIBE',
|
||||||
|
value: isSubscribed
|
||||||
|
}
|
||||||
|
}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- /ko -->
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label data-i18n="POPUPS_FILTER/SELECT_FIELD_SIZE"></label>
|
<label data-i18n="POPUPS_FILTER/SELECT_FIELD_SIZE"></label>
|
||||||
<span data-bind="text: friendlySize"></span>
|
<span data-bind="text: friendlySize"></span>
|
||||||
|
|
@ -19,16 +37,6 @@
|
||||||
<span data-bind="text: unreadEmails"></span>
|
<span data-bind="text: unreadEmails"></span>
|
||||||
/ <span style="margin:0" data-bind="text: totalEmails"></span>
|
/ <span style="margin:0" data-bind="text: totalEmails"></span>
|
||||||
</div>
|
</div>
|
||||||
<!-- ko if: canBeSubscribed -->
|
|
||||||
<div class="control-group" data-bind="component: {
|
|
||||||
name: 'Checkbox',
|
|
||||||
params: {
|
|
||||||
name: 'subscribe',
|
|
||||||
label: 'SETTINGS_FOLDERS/BUTTON_SUBSCRIBE',
|
|
||||||
value: isSubscribed
|
|
||||||
}
|
|
||||||
}"></div>
|
|
||||||
<!-- /ko -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group" data-bind="if: ACLAllowed">
|
<div class="control-group" data-bind="if: ACLAllowed">
|
||||||
<label>ACL</label>
|
<label>ACL</label>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue