mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-25 18:19:22 +03:00
Merge branch 'upstream/master' into dev/sync-nextcloud-addressbook
This commit is contained in:
commit
957654c746
1528 changed files with 85567 additions and 32765 deletions
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
class NextcloudContactsSuggestions implements \RainLoop\Providers\Suggestions\ISuggestions
|
||||
{
|
||||
/**
|
||||
* @var \MailSo\Log\Logger
|
||||
*/
|
||||
private $oLogger = null;
|
||||
use \MailSo\Log\Inherit;
|
||||
|
||||
private bool $ignoreSystemAddressbook;
|
||||
|
||||
|
|
@ -75,16 +72,9 @@ class NextcloudContactsSuggestions implements \RainLoop\Providers\Suggestions\IS
|
|||
}
|
||||
catch (\Exception $oException)
|
||||
{
|
||||
if ($this->oLogger) {
|
||||
$this->oLogger->WriteException($oException);
|
||||
}
|
||||
$this->logException($oException);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function SetLogger(\MailSo\Log\Logger $oLogger)
|
||||
{
|
||||
$this->oLogger = $oLogger;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,19 +4,21 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
{
|
||||
const
|
||||
NAME = 'Nextcloud',
|
||||
VERSION = '3.0',
|
||||
RELEASE = '2023-04-11',
|
||||
VERSION = '2.37.0',
|
||||
RELEASE = '2024-08-11',
|
||||
CATEGORY = 'Integrations',
|
||||
DESCRIPTION = 'Integrate with Nextcloud v20+',
|
||||
REQUIRED = '2.27.0';
|
||||
REQUIRED = '2.36.2';
|
||||
|
||||
public function Init() : void
|
||||
{
|
||||
if (static::IsIntegrated()) {
|
||||
\SnappyMail\Log::debug('Nextcloud', 'integrated');
|
||||
$this->UseLangs(true);
|
||||
|
||||
$this->addHook('main.fabrica', 'MainFabrica');
|
||||
$this->addHook('filter.app-data', 'FilterAppData');
|
||||
$this->addHook('filter.language', 'FilterLanguage');
|
||||
|
||||
$this->addCss('style.css');
|
||||
|
||||
|
|
@ -34,10 +36,13 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$this->addTemplate('templates/PopupsNextcloudFiles.html');
|
||||
$this->addTemplate('templates/PopupsNextcloudCalendars.html');
|
||||
|
||||
$this->addHook('imap.before-login', 'oidcLogin');
|
||||
$this->addHook('smtp.before-login', 'oidcLogin');
|
||||
$this->addHook('sieve.before-login', 'oidcLogin');
|
||||
// $this->addHook('login.credentials.step-2', 'loginCredentials2');
|
||||
// $this->addHook('login.credentials', 'loginCredentials');
|
||||
$this->addHook('imap.before-login', 'beforeLogin');
|
||||
$this->addHook('smtp.before-login', 'beforeLogin');
|
||||
$this->addHook('sieve.before-login', 'beforeLogin');
|
||||
} else {
|
||||
\SnappyMail\Log::debug('Nextcloud', 'NOT integrated');
|
||||
// \OC::$server->getConfig()->getAppValue('snappymail', 'snappymail-no-embed');
|
||||
$this->addHook('main.content-security-policy', 'ContentSecurityPolicy');
|
||||
}
|
||||
|
|
@ -65,15 +70,49 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
return static::IsIntegrated() && \OC::$server->getUserSession()->isLoggedIn();
|
||||
}
|
||||
|
||||
public function oidcLogin(\RainLoop\Model\Account $oAccount, \MailSo\Net\NetClient $oClient, \MailSo\Net\ConnectSettings $oSettings) : void
|
||||
public function loginCredentials(string &$sEmail, string &$sLogin, ?string &$sPassword = null) : void
|
||||
{
|
||||
if ($this->Config()->Get('plugin', 'oidc', false)
|
||||
/**
|
||||
* This has an issue.
|
||||
* When user changes email address, all settings are gone as the new
|
||||
* _data_/_default_/storage/{domain}/{local-part} is used
|
||||
*/
|
||||
// $ocUser = \OC::$server->getUserSession()->getUser();
|
||||
// $sEmail = $ocUser->getEMailAddress() ?: $ocUser->getPrimaryEMailAddress() ?: $sEmail;
|
||||
}
|
||||
|
||||
public function loginCredentials2(string &$sEmail, ?string &$sPassword = null) : void
|
||||
{
|
||||
$ocUser = \OC::$server->getUserSession()->getUser();
|
||||
$sEmail = $ocUser->getEMailAddress() ?: $ocUser->getPrimaryEMailAddress() ?: $sEmail;
|
||||
}
|
||||
|
||||
public function beforeLogin(\RainLoop\Model\Account $oAccount, \MailSo\Net\NetClient $oClient, \MailSo\Net\ConnectSettings $oSettings) : void
|
||||
{
|
||||
// https://apps.nextcloud.com/apps/oidc_login
|
||||
$config = \OC::$server->getConfig();
|
||||
$oUser = \OC::$server->getUserSession()->getUser();
|
||||
$sUID = $oUser->getUID();
|
||||
|
||||
$sEmail = $config->getUserValue($sUID, 'snappymail', 'snappymail-email');
|
||||
$sPassword = $config->getUserValue($sUID, 'snappymail', 'passphrase')
|
||||
?: $config->getUserValue($sUID, 'snappymail', 'snappymail-password');
|
||||
$bAccountDefinedExplicitly = ($sEmail && $sPassword) && $sEmail === $oSettings->username;
|
||||
|
||||
$sNcEmail = $oUser->getEMailAddress() ?: $oUser->getPrimaryEMailAddress();
|
||||
|
||||
// Only login with OIDC access token if
|
||||
// it is enabled in config, the user is currently logged in with OIDC,
|
||||
// the current snappymail account is the OIDC account and no account defined explicitly
|
||||
if (\OC::$server->getConfig()->getAppValue('snappymail', 'snappymail-autologin-oidc', false)
|
||||
&& \OC::$server->getSession()->get('is_oidc')
|
||||
&& $sNcEmail === $oSettings->username
|
||||
&& !$bAccountDefinedExplicitly
|
||||
// && $oClient->supportsAuthType('OAUTHBEARER') // v2.28
|
||||
) {
|
||||
$sAccessToken = \OC::$server->getSession()->get('oidc_access_token');
|
||||
if ($sAccessToken) {
|
||||
$oSettings->Password = $sAccessToken;
|
||||
$oSettings->passphrase = $sAccessToken;
|
||||
\array_unshift($oSettings->SASLMechanisms, 'OAUTHBEARER');
|
||||
}
|
||||
}
|
||||
|
|
@ -112,7 +151,8 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
{
|
||||
$sSaveFolder = \ltrim($this->jsonParam('folder', ''), '/');
|
||||
// $aValues = \RainLoop\Api::Actions()->decodeRawKey($this->jsonParam('msgHash', ''));
|
||||
$aValues = \json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($this->jsonParam('msgHash', '')), true);
|
||||
$msgHash = $this->jsonParam('msgHash', '');
|
||||
$aValues = \json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($msgHash), true);
|
||||
$aResult = [
|
||||
'folder' => '',
|
||||
'filename' => '',
|
||||
|
|
@ -132,7 +172,10 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$oFiles->is_dir($sSaveFolder) || $oFiles->mkdir($sSaveFolder);
|
||||
}
|
||||
$aResult['folder'] = $sSaveFolder;
|
||||
$aResult['filename'] = ($this->jsonParam('filename', '') ?: \date('YmdHis')) . '.eml';
|
||||
$aResult['filename'] = \MailSo\Base\Utils::SecureFileName(
|
||||
\mb_substr($this->jsonParam('filename', '') ?: \date('YmdHis'), 0, 100)
|
||||
) . '.' . \md5($msgHash) . '.eml';
|
||||
|
||||
|
||||
$oMailClient->MessageMimeStream(
|
||||
function ($rResource) use ($oFiles, $aResult) {
|
||||
|
|
@ -159,10 +202,14 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$oFiles->is_dir($sSaveFolder) || $oFiles->mkdir($sSaveFolder);
|
||||
$data->result = true;
|
||||
foreach ($data->items as $aItem) {
|
||||
$sSavedFileName = isset($aItem['fileName']) ? $aItem['fileName'] : 'file.dat';
|
||||
$sSavedFileHash = !empty($aItem['fileHash']) ? $aItem['fileHash'] : '';
|
||||
if (!empty($sSavedFileHash)) {
|
||||
$fFile = $data->filesProvider->GetFile($data->account, $sSavedFileHash, 'rb');
|
||||
$sSavedFileName = empty($aItem['fileName']) ? 'file.dat' : $aItem['fileName'];
|
||||
if (!empty($aItem['data'])) {
|
||||
$sSavedFileNameFull = static::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, $oFiles);
|
||||
if (!$oFiles->file_put_contents($sSavedFileNameFull, $aItem['data'])) {
|
||||
$data->result = false;
|
||||
}
|
||||
} else if (!empty($aItem['fileHash'])) {
|
||||
$fFile = $data->filesProvider->GetFile($data->account, $aItem['fileHash'], 'rb');
|
||||
if (\is_resource($fFile)) {
|
||||
$sSavedFileNameFull = static::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, $oFiles);
|
||||
if (!$oFiles->file_put_contents($sSavedFileNameFull, $fFile)) {
|
||||
|
|
@ -175,20 +222,14 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data->items as $aItem) {
|
||||
$sFileHash = (string) (isset($aItem['fileHash']) ? $aItem['fileHash'] : '');
|
||||
if (!empty($sFileHash)) {
|
||||
$data->filesProvider->Clear($data->account, $sFileHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function FilterAppData($bAdmin, &$aResult) : void
|
||||
{
|
||||
if (!$bAdmin && \is_array($aResult)) {
|
||||
$sUID = \OC::$server->getUserSession()->getUser()->getUID();
|
||||
$ocUser = \OC::$server->getUserSession()->getUser();
|
||||
$sUID = $ocUser->getUID();
|
||||
$oUrlGen = \OC::$server->getURLGenerator();
|
||||
$sWebDAV = $oUrlGen->getAbsoluteURL($oUrlGen->linkTo('', 'remote.php') . '/dav');
|
||||
// $sWebDAV = \OCP\Util::linkToRemote('dav');
|
||||
|
|
@ -207,6 +248,8 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$sEmail = $sUID;
|
||||
} else if ($config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) {
|
||||
$sEmail = $config->getUserValue($sUID, 'settings', 'email', '');
|
||||
} else {
|
||||
\SnappyMail\Log::debug('Nextcloud', 'snappymail-autologin is off');
|
||||
}
|
||||
// If the user has set credentials for SnappyMail in their personal
|
||||
// settings, override everything before and use those instead.
|
||||
|
|
@ -214,6 +257,22 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
if ($sCustomEmail) {
|
||||
$sEmail = $sCustomEmail;
|
||||
}
|
||||
if (!$sEmail) {
|
||||
$sEmail = $ocUser->getEMailAddress();
|
||||
// ?: $ocUser->getPrimaryEMailAddress();
|
||||
}
|
||||
/*
|
||||
if ($config->getAppValue('snappymail', 'snappymail-autologin-oidc', false)) {
|
||||
if (\OC::$server->getSession()->get('is_oidc')) {
|
||||
$sEmail = "{$sUID}@nextcloud";
|
||||
$aResult['DevPassword'] = \OC::$server->getSession()->get('oidc_access_token');
|
||||
} else {
|
||||
\SnappyMail\Log::debug('Nextcloud', 'Not an OIDC login');
|
||||
}
|
||||
} else {
|
||||
\SnappyMail\Log::debug('Nextcloud', 'OIDC is off');
|
||||
}
|
||||
*/
|
||||
$aResult['DevEmail'] = $sEmail ?: '';
|
||||
} else if (!empty($aResult['ContactsSync'])) {
|
||||
$bSave = false;
|
||||
|
|
@ -225,8 +284,13 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$aResult['ContactsSync']['User'] = $sUID;
|
||||
$bSave = true;
|
||||
}
|
||||
if (empty($aResult['ContactsSync']['Password'])) {
|
||||
$aResult['ContactsSync']['Password'] = '';
|
||||
$pass = \OC::$server->getSession()['snappymail-passphrase'];
|
||||
if ($pass/* && empty($aResult['ContactsSync']['Password'])*/) {
|
||||
$pass = \SnappyMail\Crypt::DecryptUrlSafe($pass, $sUID);
|
||||
if ($pass) {
|
||||
$aResult['ContactsSync']['Password'] = $pass;
|
||||
$bSave = true;
|
||||
}
|
||||
}
|
||||
if ($bSave) {
|
||||
$oActions = \RainLoop\Api::Actions();
|
||||
|
|
@ -244,6 +308,54 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
}
|
||||
}
|
||||
|
||||
public function FilterLanguage(&$sLanguage, $bAdmin) : void
|
||||
{
|
||||
if (!\RainLoop\Api::Config()->Get('webmail', 'allow_languages_on_settings', true)) {
|
||||
$aResultLang = \SnappyMail\L10n::getLanguages($bAdmin);
|
||||
$userId = \OC::$server->getUserSession()->getUser()->getUID();
|
||||
$userLang = \OC::$server->getConfig()->getUserValue($userId, 'core', 'lang', 'en');
|
||||
$userLang = \strtr($userLang, '_', '-');
|
||||
$sLanguage = $this->determineLocale($userLang, $aResultLang);
|
||||
// Check if $sLanguage is null
|
||||
if (!$sLanguage) {
|
||||
$sLanguage = 'en'; // Assign 'en' if $sLanguage is null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine locale from user language.
|
||||
*
|
||||
* @param string $langCode The name of the input.
|
||||
* @param array $languagesArray The value of the array.
|
||||
*
|
||||
* @return string return locale
|
||||
*/
|
||||
private function determineLocale(string $langCode, array $languagesArray) : ?string
|
||||
{
|
||||
// Direct check for the language code
|
||||
if (\in_array($langCode, $languagesArray)) {
|
||||
return $langCode;
|
||||
}
|
||||
|
||||
// Check without country code
|
||||
if (\str_contains($langCode, '-')) {
|
||||
$langCode = \explode('-', $langCode)[0];
|
||||
if (\in_array($langCode, $languagesArray)) {
|
||||
return $langCode;
|
||||
}
|
||||
}
|
||||
|
||||
// Check with uppercase country code
|
||||
$langCodeWithUpperCase = $langCode . '-' . \strtoupper($langCode);
|
||||
if (\in_array($langCodeWithUpperCase, $languagesArray)) {
|
||||
return $langCodeWithUpperCase;
|
||||
}
|
||||
|
||||
// If no match is found
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $mResult
|
||||
*/
|
||||
|
|
@ -266,6 +378,12 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
include_once __DIR__ . '/NextcloudAddressBook.php';
|
||||
$mResult = new NextcloudAddressBook();
|
||||
}
|
||||
/*
|
||||
if ($this->Config()->Get('plugin', 'storage', false) && ('storage' === $sName || 'storage-local' === $sName)) {
|
||||
require_once __DIR__ . '/storage.php';
|
||||
$oDriver = new \NextcloudStorage(APP_PRIVATE_DATA.'storage', $sName === 'storage-local');
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,10 +396,12 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
\RainLoop\Plugins\Property::NewInstance('ignoreSystemAddressbook')->SetLabel('Ignore system addressbook')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
->SetDefaultValue(true),
|
||||
\RainLoop\Plugins\Property::NewInstance('calendar')->SetLabel('Enable "Put ICS in calendar"')
|
||||
/*
|
||||
\RainLoop\Plugins\Property::NewInstance('storage')->SetLabel('Use Nextcloud user ID in config storage path')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
->SetDefaultValue(false),
|
||||
\RainLoop\Plugins\Property::NewInstance('oidc')->SetLabel('Login with OIDC')
|
||||
->SetDefaultValue(false)
|
||||
*/
|
||||
\RainLoop\Plugins\Property::NewInstance('calendar')->SetLabel('Enable "Put ICS in calendar"')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
->SetDefaultValue(false)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,13 +29,14 @@ const
|
|||
</propfind>`,
|
||||
|
||||
propfindCal = `<?xml version="1.0"?>
|
||||
<propfind xmlns="DAV:">
|
||||
<prop>
|
||||
<resourcetype/>
|
||||
<current-user-privilege-set/>
|
||||
<displayname/>
|
||||
</prop>
|
||||
</propfind>`,
|
||||
<d:propfind xmlns:d="DAV:" xmlns:x1="http://apple.com/ns/ical/">
|
||||
<d:prop>
|
||||
<d:resourcetype/>
|
||||
<d:current-user-privilege-set/>
|
||||
<d:displayname/>
|
||||
<x1:calendar-color/>
|
||||
</d:prop>
|
||||
</d:propfind>`,
|
||||
|
||||
xmlParser = new DOMParser(),
|
||||
pathRegex = /.*\/remote.php\/dav\/[^/]+\/[^/]+/g,
|
||||
|
|
@ -112,6 +113,8 @@ const
|
|||
}
|
||||
elemList.push(elem);
|
||||
}
|
||||
// https://github.com/the-djmaze/snappymail/issues/1177
|
||||
// elemList.sort((a, b) => a.isFile != b.isFile ? (a.isFile ? 1 : -1) : a.name.localeCompare(b.name));
|
||||
return Promise.resolve(elemList);
|
||||
});
|
||||
},
|
||||
|
|
@ -326,7 +329,51 @@ class NextcloudCalendarsPopupView extends rl.pluginPopupView {
|
|||
}
|
||||
});
|
||||
}
|
||||
createCalendarListItem(calendarData, treeElement) {
|
||||
const {
|
||||
displayName,
|
||||
href,
|
||||
calendarColor
|
||||
} = calendarData;
|
||||
|
||||
const li = document.createElement('li');
|
||||
li.style.display = 'flex';
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.setAttribute('role', 'img');
|
||||
span.className = 'material-design-icon checkbox-blank-circle-icon';
|
||||
span.style.fill = calendarColor;
|
||||
span.style.width = '20px';
|
||||
span.style.height = '20px';
|
||||
|
||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.setAttribute('width', '20');
|
||||
svg.setAttribute('height', '20');
|
||||
svg.setAttribute('viewBox', '0 0 24 24');
|
||||
|
||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
path.setAttribute('d', 'M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z');
|
||||
svg.appendChild(path);
|
||||
|
||||
span.appendChild(svg);
|
||||
|
||||
const button = document.createElement('button');
|
||||
button.className = 'button-vue';
|
||||
button.style.backgroundColor = 'transparent';
|
||||
button.style.border = '0';
|
||||
button.style.fontSize = 'large';
|
||||
button.style.padding = '0';
|
||||
button.style.cursor = 'pointer';
|
||||
button.style.marginLeft = '5px';
|
||||
button.href = href.replace(pathRegex, '').replace(/\/$/, '');
|
||||
button.textContent = displayName;
|
||||
button.style.color = '#1968DF';
|
||||
|
||||
li.appendChild(span);
|
||||
li.appendChild(button);
|
||||
|
||||
treeElement.appendChild(li);
|
||||
}
|
||||
// Happens after showModal()
|
||||
beforeShow(fResolve) {
|
||||
this.select = '';
|
||||
|
|
@ -341,26 +388,30 @@ class NextcloudCalendarsPopupView extends rl.pluginPopupView {
|
|||
})
|
||||
.then(response => (response.status < 400) ? response.text() : Promise.reject(new Error({ response })))
|
||||
.then(text => {
|
||||
const
|
||||
responseList = getDavElementsByTagName(
|
||||
xmlParser.parseFromString(text, 'application/xml').documentElement,
|
||||
'response'
|
||||
);
|
||||
// Parse the XML text
|
||||
const xmlDoc = xmlParser.parseFromString(text, 'application/xml').documentElement;
|
||||
const responseList = getElementsInNamespaces(xmlDoc, 'response');
|
||||
for (let i = 0; i < responseList.length; ++i) {
|
||||
const e = responseList.item(i);
|
||||
const e = responseList[i];
|
||||
if (getDavElementByTagName(e, 'resourcetype').getElementsByTagNameNS(nsCalDAV, 'calendar').length) {
|
||||
// && getDavElementsByTagName(getDavElementByTagName(e, 'current-user-privilege-set'), 'write').length) {
|
||||
const li = document.createElement('li'),
|
||||
btn = document.createElement('button');
|
||||
li.dataset.icon = '📅';
|
||||
li.textContent = getDavElementByTagName(e, 'displayname').textContent;
|
||||
btn.href = getDavElementByTagName(e, 'href').textContent
|
||||
.replace(pathRegex, '').replace(/\/$/, '');
|
||||
btn.textContent = 'select';
|
||||
btn.className = 'button-vue';
|
||||
btn.style.marginLeft = '1em';
|
||||
li.append(btn);
|
||||
this.tree.append(li);
|
||||
const displayNameElement = getElementsInNamespaces(e, 'displayname')[0];
|
||||
const displayName = displayNameElement ? displayNameElement.textContent.trim() : '';
|
||||
|
||||
const hrefElement = getElementsInNamespaces(e, 'href')[0];
|
||||
const href = hrefElement ? hrefElement.textContent.trim() : '';
|
||||
|
||||
const calendarColorElement = getElementsInNamespaces(e, 'calendar-color')[0];
|
||||
const calendarColor = calendarColorElement ? calendarColorElement.textContent.trim() : '#000000';
|
||||
|
||||
// Create an object to hold calendar data
|
||||
const calendarData = {
|
||||
displayName,
|
||||
href,
|
||||
calendarColor
|
||||
};
|
||||
|
||||
// Call the function to create and append the list item
|
||||
this.createCalendarListItem(calendarData, this.tree);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -433,3 +484,19 @@ rl.nextcloud = {
|
|||
};
|
||||
|
||||
})(window.rl);
|
||||
|
||||
function getElementsInNamespaces(xmlDocument, tagName) {
|
||||
const namespaces = {
|
||||
d: 'DAV:',
|
||||
x1: 'http://apple.com/ns/ical/',
|
||||
};
|
||||
const results = [];
|
||||
for (const prefix in namespaces) {
|
||||
const namespaceURI = namespaces[prefix];
|
||||
const elements = xmlDocument.getElementsByTagNameNS(namespaceURI, tagName);
|
||||
for (const element of elements) {
|
||||
results.push(element);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"NEXTCLOUD": {
|
||||
"SAVE_ATTACHMENTS": "Save in Nextcloud",
|
||||
"SAVE_EML": "Save as .eml in Nextcloud",
|
||||
"SAVE_ICS": "Put in Calendar",
|
||||
"SAVE_ICS": "Add to calendar",
|
||||
"SELECT_FOLDER": "Select folder",
|
||||
"SELECT_FILES": "Select file(s)",
|
||||
"ATTACH_FILES": "Attach Nextcloud files",
|
||||
|
|
|
|||
14
plugins/nextcloud/langs/zh-TW.json
Normal file
14
plugins/nextcloud/langs/zh-TW.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"NEXTCLOUD": {
|
||||
"SAVE_ATTACHMENTS": "儲存到 Nextcloud",
|
||||
"SAVE_EML": "以 .eml 檔案儲存到 Nextcloud",
|
||||
"SAVE_ICS": "新增到日曆",
|
||||
"SELECT_FOLDER": "選擇檔案夾",
|
||||
"SELECT_FILES": "選擇檔案",
|
||||
"ATTACH_FILES": "新增 Nextcloud 檔案",
|
||||
"SELECT_CALENDAR": "選擇日曆",
|
||||
"FILE_ATTACH": "附加",
|
||||
"FILE_INTERNAL": "內部",
|
||||
"FILE_PUBLIC": "公開"
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,8 @@
|
|||
"SELECT_FILES": "选择文件",
|
||||
"ATTACH_FILES": "添加 Nextcloud 文件",
|
||||
"SELECT_CALENDAR": "选择日历",
|
||||
"FILE_ATTACH": "attach",
|
||||
"FILE_INTERNAL": "internal",
|
||||
"FILE_PUBLIC": "public"
|
||||
"FILE_ATTACH": "附加",
|
||||
"FILE_INTERNAL": "内部",
|
||||
"FILE_PUBLIC": "公开"
|
||||
}
|
||||
}
|
||||
22
plugins/nextcloud/storage.php
Normal file
22
plugins/nextcloud/storage.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
use RainLoop\Providers\Storage\Enumerations\StorageType;
|
||||
|
||||
class NextcloudStorage extends \RainLoop\Providers\Storage\FileStorage
|
||||
{
|
||||
/**
|
||||
* @param \RainLoop\Model\Account|string|null $mAccount
|
||||
*/
|
||||
public function GenerateFilePath($mAccount, int $iStorageType, bool $bMkDir = false) : string
|
||||
{
|
||||
$sDataPath = parent::GenerateFilePath($mAccount, $iStorageType, $bMkDir);
|
||||
if (StorageType::CONFIG === $iStorageType) {
|
||||
$sUID = \OC::$server->getUserSession()->getUser()->getUID();
|
||||
$sDataPath .= ".config/{$sUID}/";
|
||||
if ($bMkDir && !\is_dir($sDataPath) && !\mkdir($sDataPath, 0700, true)) {
|
||||
throw new \RainLoop\Exceptions\Exception('Can\'t make storage directory "'.$sDataPath.'"');
|
||||
}
|
||||
}
|
||||
return $sDataPath;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue