Merge branch 'master' into fix/clean-ldap-access-factory-usage

Signed-off-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com>
This commit is contained in:
Côme Chilliet 2022-11-21 16:05:17 +01:00 committed by GitHub
commit 341dda1de6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1463 changed files with 14116 additions and 12423 deletions

View file

@ -374,7 +374,7 @@ services:
- name: cache
image: ghcr.io/nextcloud/continuous-integration-redis:latest
- name: mariadb
image: ghcr.io/nextcloud/continuous-integration-mariadb-10.6continuous-integration-mariadb-10.6:10.6
image: ghcr.io/nextcloud/continuous-integration-mariadb-10.6:latest
environment:
MYSQL_ROOT_PASSWORD: owncloud
MYSQL_USER: oc_autotest
@ -455,7 +455,7 @@ services:
image: ghcr.io/nextcloud/continuous-integration-postgres-10:postgres-10
environment:
POSTGRES_USER: oc_autotest
POSTGRES_DB: oc_autotest_dummy
POSTGRES_DB: oc_autotest
POSTGRES_PASSWORD: owncloud
tmpfs:
- /var/lib/postgresql/data
@ -491,7 +491,7 @@ services:
image: ghcr.io/nextcloud/continuous-integration-postgres-11:postgres-11
environment:
POSTGRES_USER: oc_autotest
POSTGRES_DB: oc_autotest_dummy
POSTGRES_DB: oc_autotest
POSTGRES_PASSWORD: owncloud
tmpfs:
- /var/lib/postgresql/data
@ -527,7 +527,43 @@ services:
image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13
environment:
POSTGRES_USER: oc_autotest
POSTGRES_DB: oc_autotest_dummy
POSTGRES_DB: oc_autotest
POSTGRES_PASSWORD: owncloud
tmpfs:
- /var/lib/postgresql/data
trigger:
branch:
- master
- stable*
event:
- pull_request
- push
---
kind: pipeline
name: postgres15-php8.0
steps:
- name: submodules
image: ghcr.io/nextcloud/continuous-integration-alpine-git:latest
commands:
- git submodule update --init
- name: postgres-php8.0
image: ghcr.io/nextcloud/continuous-integration-php8.0:latest
commands:
- bash tests/drone-run-php-tests.sh || exit 0
- sleep 10 # gives the database enough time to initialize
- POSTGRES=15 NOCOVERAGE=true TEST_SELECTION=DB ./autotest.sh pgsql
services:
- name: cache
image: ghcr.io/nextcloud/continuous-integration-redis:latest
- name: postgres-15
image: ghcr.io/nextcloud/continuous-integration-postgres-15:latest
environment:
POSTGRES_USER: oc_autotest
POSTGRES_DB: oc_autotest
POSTGRES_PASSWORD: owncloud
tmpfs:
- /var/lib/postgresql/data

2
.gitattributes vendored
View file

@ -1,2 +1,2 @@
/dist/* binary
/package-lock.json binary
/package-lock.json merge=binary

2
.github/CODEOWNERS vendored
View file

@ -16,4 +16,4 @@
/lib/public/Profiler @CarlSchwan
# Security team
resources/config/ca-bundle.crt @ChristophWurst @eneiluj @miaulalala @nickvergessen
resources/config/ca-bundle.crt @ChristophWurst @julien-nc @miaulalala @nickvergessen

View file

@ -51,8 +51,9 @@ jobs:
- name: Apply PR
run: |
git fetch origin ${{ github.event.pull_request.head.ref }}
git checkout ${{ github.event.pull_request.head.ref }}
git remote add pr ${{ github.event.pull_request.head.repo.clone_url }}
git fetch pr ${{ github.event.pull_request.head.ref }}
git checkout -b pr/${{ github.event.pull_request.head.ref }}
git submodule update
./occ upgrade

1
.gitignore vendored
View file

@ -73,6 +73,7 @@ CVS/*
RCS/*
*.backup*
.php_cs.cache
.php-cs-fixer.cache
# kdevelop
.kdev

View file

@ -13,5 +13,5 @@ repos:
- id: php-lint
- id: php-cs-fixer
files: \.(php)$
exclude: ^(config|data|composer)
exclude: ^(config|data|composer|lib\/composer)
args: []

@ -1 +1 @@
Subproject commit 3095d4062823f3f913d594f9ff313010ed55cd74
Subproject commit 02ba5bba999b61d40c79a6ed56683b69edf66619

View file

@ -1,7 +1,7 @@
OC.L10N.register(
"admin_audit",
{
"Auditing / Logging" : "Audit / journalisation",
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fournit des capacités de journalisation pour Nextcloud telles que l'enregistrement des accès aux fichiers ou d'autres actions sensibles."
"Auditing / Logging" : "Vérification / Connexion",
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fournit des capacités de traçage pour NextCloud tels que l'accès au fichier de connexions ou d'autres actions sensibles."
},
"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

View file

@ -1,5 +1,5 @@
{ "translations": {
"Auditing / Logging" : "Audit / journalisation",
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fournit des capacités de journalisation pour Nextcloud telles que l'enregistrement des accès aux fichiers ou d'autres actions sensibles."
"Auditing / Logging" : "Vérification / Connexion",
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fournit des capacités de traçage pour NextCloud tels que l'accès au fichier de connexions ou d'autres actions sensibles."
},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

View file

@ -17,7 +17,7 @@ OC.L10N.register(
"Delete comment" : "Вилучити коментар",
"Cancel edit" : "Скасувати редагування",
"Post comment" : "Опублікувати коментар",
"No comments yet, start the conversation!" : "Коментарі відсутні, почніть бесіду!",
"No comments yet, start the conversation!" : "Тут можна додати коментарі",
"No more messages" : "Більше жодних повідомлень",
"Retry" : "Ще раз",
"Unable to load the comments list" : "Не вдалося завантажити список коментарів",

View file

@ -15,7 +15,7 @@
"Delete comment" : "Вилучити коментар",
"Cancel edit" : "Скасувати редагування",
"Post comment" : "Опублікувати коментар",
"No comments yet, start the conversation!" : "Коментарі відсутні, почніть бесіду!",
"No comments yet, start the conversation!" : "Тут можна додати коментарі",
"No more messages" : "Більше жодних повідомлень",
"Retry" : "Ще раз",
"Unable to load the comments list" : "Не вдалося завантажити список коментарів",

View file

@ -69,6 +69,7 @@
:auto-complete="autoComplete"
:contenteditable="!loading"
:value="localMessage"
:user-data="userData"
@update:value="updateLocalMessage"
@submit="onSubmit" />
<NcButton class="comment__submit"

View file

@ -22,6 +22,7 @@
import { parseXML, prepareFileFromProps } from 'webdav/dist/node/tools/dav'
import { processResponsePayload } from 'webdav/dist/node/response'
import { decodeHtmlEntities } from '../utils/decodeHtmlEntities'
import client from './DavClient'
export const DEFAULT_LIMIT = 20
@ -89,16 +90,3 @@ function processMultistatus(result, isDetailed = false) {
return prepareFileFromProps(decodedProps, decodedProps.id.toString(), isDetailed)
})
}
/**
* @param {any} value -
* @param {any} passes -
*/
function decodeHtmlEntities(value, passes = 1) {
const parser = new DOMParser()
let decoded = value
for (let i = 0; i < passes; i++) {
decoded = parser.parseFromString(decoded, 'text/html').documentElement.textContent
}
return decoded
}

View file

@ -22,6 +22,7 @@
import { getCurrentUser } from '@nextcloud/auth'
import { getRootPath } from '../utils/davUtils'
import { decodeHtmlEntities } from '../utils/decodeHtmlEntities'
import axios from '@nextcloud/axios'
import client from './DavClient'
@ -55,5 +56,12 @@ export default async function(commentsType, ressourceId, message) {
details: true,
})
const props = comment.data.props
// Decode twice to handle potentially double-encoded entities
// FIXME Remove this once https://github.com/nextcloud/server/issues/29306
// is resolved
props.actorDisplayName = decodeHtmlEntities(props.actorDisplayName, 2)
props.message = decodeHtmlEntities(props.message, 2)
return comment.data
}

View file

@ -0,0 +1,34 @@
/**
* @copyright Copyright (c) 2021 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* @param {any} value -
* @param {any} passes -
*/
export function decodeHtmlEntities(value, passes = 1) {
const parser = new DOMParser()
let decoded = value
for (let i = 0; i < passes; i++) {
decoded = parser.parseFromString(decoded, 'text/html').documentElement.textContent
}
return decoded
}

View file

@ -2,6 +2,7 @@
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
-
- @author John Molakvoæ <skjnldsv@protonmail.com>
- @author Richard Steinmetz <richard@steinmetz.cloud>
-
- @license GNU AGPL version 3 or any later version
-
@ -25,14 +26,19 @@
<!-- Editor -->
<Comment v-bind="editorData"
:auto-complete="autoComplete"
:user-data="userData"
:editor="true"
:ressource-id="ressourceId"
class="comments__writer"
@new="onNewComment" />
<template v-if="!isFirstLoading">
<NcEmptyContent v-if="!hasComments && done" icon="icon-comment">
{{ t('comments', 'No comments yet, start the conversation!') }}
<NcEmptyContent v-if="!hasComments && done"
class="comments__empty"
:title="t('comments', 'No comments yet, start the conversation!')">
<template #icon>
<MessageReplyTextIcon />
</template>
</NcEmptyContent>
<!-- Comments -->
@ -55,14 +61,19 @@
</div>
<!-- Error message -->
<NcEmptyContent v-else-if="error" class="comments__error" icon="icon-error">
{{ error }}
<template #desc>
<button icon="icon-history" @click="getComments">
{{ t('comments', 'Retry') }}
</button>
</template>
</NcEmptyContent>
<template v-else-if="error">
<NcEmptyContent class="comments__error" :title="error">
<template #icon>
<AlertCircleOutlineIcon />
</template>
</NcEmptyContent>
<NcButton class="comments__retry" @click="getComments">
<template #icon>
<RefreshIcon />
</template>
{{ t('comments', 'Retry') }}
</NcButton>
</template>
</template>
</div>
</template>
@ -76,10 +87,14 @@ import VTooltip from 'v-tooltip'
import Vue from 'vue'
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent'
import NcButton from '@nextcloud/vue/dist/Components/NcButton'
import RefreshIcon from 'vue-material-design-icons/Refresh'
import MessageReplyTextIcon from 'vue-material-design-icons/MessageReplyText'
import AlertCircleOutlineIcon from 'vue-material-design-icons/AlertCircleOutline'
import Comment from '../components/Comment'
import getComments, { DEFAULT_LIMIT } from '../services/GetComments'
import cancelableRequest from '../utils/cancelableRequest'
import Comment from '../components/Comment.vue'
import getComments, { DEFAULT_LIMIT } from '../services/GetComments.js'
import cancelableRequest from '../utils/cancelableRequest.js'
Vue.use(VTooltip)
@ -90,6 +105,10 @@ export default {
// Avatar,
Comment,
NcEmptyContent,
NcButton,
RefreshIcon,
MessageReplyTextIcon,
AlertCircleOutlineIcon,
},
data() {
@ -111,6 +130,7 @@ export default {
},
Comment,
userData: {},
}
},
@ -155,21 +175,22 @@ export default {
* Make sure we have all mentions as Array of objects
*
* @param {Array} mentions the mentions list
* @return {object[]}
* @return {Object<string, object>}
*/
genMentionsData(mentions) {
const list = Object.values(mentions).flat()
return list.reduce((mentions, mention) => {
mentions[mention.mentionId] = {
// TODO: support groups
icon: 'icon-user',
id: mention.mentionId,
label: mention.mentionDisplayName,
source: 'users',
primary: getCurrentUser().uid === mention.mentionId,
}
return mentions
}, {})
Object.values(mentions)
.flat()
.forEach(mention => {
this.userData[mention.mentionId] = {
// TODO: support groups
icon: 'icon-user',
id: mention.mentionId,
label: mention.mentionDisplayName,
source: 'users',
primary: getCurrentUser().uid === mention.mentionId,
}
})
return this.userData
},
/**
@ -233,7 +254,9 @@ export default {
limit: loadState('comments', 'maxAutoCompleteResults'),
},
})
return callback(results.data.ocs.data)
// Save user data so it can be used by the editor to replace mentions
results.data.ocs.data.forEach(user => { this.userData[user.id] = user })
return callback(Object.values(this.userData))
},
/**
@ -276,8 +299,13 @@ export default {
<style lang="scss" scoped>
.comments {
// Do not add emptycontent top margin
&__error{
margin-top: 0;
&__empty,
&__error {
margin-top: 0 !important;
}
&__retry {
margin: 0 auto;
}
&__info {

View file

@ -0,0 +1,9 @@
OC.L10N.register(
"contactsinteraction",
{
"Recently contacted" : "Nylig kontaktet",
"Contacts Interaction" : "Interaksjon med kontakter",
"Manages interaction between users and contacts" : "Administrerer interaksjon mellom brukere og kontakter",
"Collect data about user and contacts interactions and provide an address book for the data" : "Samle inn data om bruker- og kontaktinteraksjoner og oppgi en adressebok for dataene"
},
"nplurals=2; plural=(n != 1);");

View file

@ -0,0 +1,7 @@
{ "translations": {
"Recently contacted" : "Nylig kontaktet",
"Contacts Interaction" : "Interaksjon med kontakter",
"Manages interaction between users and contacts" : "Administrerer interaksjon mellom brukere og kontakter",
"Collect data about user and contacts interactions and provide an address book for the data" : "Samle inn data om bruker- og kontaktinteraksjoner og oppgi en adressebok for dataene"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View file

@ -3,6 +3,7 @@ OC.L10N.register(
{
"Dashboard" : "Tableau de bord",
"Dashboard app" : "App Tableau de bord",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un\naperçu de vos rendez-vous à venir, des e-mails urgents, des messages de tchat,\ndes tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets\nqu'ils souhaitent et modifier l'arrière-plan à leur guise.",
"Customize" : "Personnaliser",
"Edit widgets" : "Modifier les widgets",
"Get more widgets from the App Store" : "Obtenez plus de widgets depuis la Boutique d'applications",

View file

@ -1,6 +1,7 @@
{ "translations": {
"Dashboard" : "Tableau de bord",
"Dashboard app" : "App Tableau de bord",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un\naperçu de vos rendez-vous à venir, des e-mails urgents, des messages de tchat,\ndes tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets\nqu'ils souhaitent et modifier l'arrière-plan à leur guise.",
"Customize" : "Personnaliser",
"Edit widgets" : "Modifier les widgets",
"Get more widgets from the App Store" : "Obtenez plus de widgets depuis la Boutique d'applications",

View file

@ -638,6 +638,5 @@ html, body {
#content {
overflow: auto;
position: static !important;;
}
</style>

View file

@ -24,6 +24,7 @@
<job>OCA\DAV\BackgroundJob\CleanupInvitationTokenJob</job>
<job>OCA\DAV\BackgroundJob\EventReminderJob</job>
<job>OCA\DAV\BackgroundJob\CalendarRetentionJob</job>
<job>OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob</job>
</background-jobs>
<repair-steps>

View file

@ -18,6 +18,7 @@ return array(
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => $baseDir . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => $baseDir . '/../lib/BackgroundJob/EventReminderJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => $baseDir . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\PruneOutdatedSyncTokensJob' => $baseDir . '/../lib/BackgroundJob/PruneOutdatedSyncTokensJob.php',
'OCA\\DAV\\BackgroundJob\\RefreshWebcalJob' => $baseDir . '/../lib/BackgroundJob/RefreshWebcalJob.php',
'OCA\\DAV\\BackgroundJob\\RegisterRegenerateBirthdayCalendars' => $baseDir . '/../lib/BackgroundJob/RegisterRegenerateBirthdayCalendars.php',
'OCA\\DAV\\BackgroundJob\\UpdateCalendarResourcesRoomsBackgroundJob' => $baseDir . '/../lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php',

View file

@ -33,6 +33,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/EventReminderJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\PruneOutdatedSyncTokensJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/PruneOutdatedSyncTokensJob.php',
'OCA\\DAV\\BackgroundJob\\RefreshWebcalJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/RefreshWebcalJob.php',
'OCA\\DAV\\BackgroundJob\\RegisterRegenerateBirthdayCalendars' => __DIR__ . '/..' . '/../lib/BackgroundJob/RegisterRegenerateBirthdayCalendars.php',
'OCA\\DAV\\BackgroundJob\\UpdateCalendarResourcesRoomsBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php',

View file

@ -75,6 +75,7 @@ OC.L10N.register(
"Contacts and groups" : "Kontakter og grupper",
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV endpoint",
"Availability" : "tilgængelighed",
"to" : "til",
"Delete slot" : "Slet slot",
"Monday" : "Mandag",

View file

@ -73,6 +73,7 @@
"Contacts and groups" : "Kontakter og grupper",
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV endpoint",
"Availability" : "tilgængelighed",
"to" : "til",
"Delete slot" : "Slet slot",
"Monday" : "Mandag",

View file

@ -31,6 +31,7 @@ OC.L10N.register(
"{actor} updated event {event} in calendar {calendar}" : "Ο {actor} ενημέρωσε το γεγονός {event} στο ημερολόγιο {calendar}",
"You updated event {event} in calendar {calendar}" : "Ενημερώσατε το συμβάν {event} στο ημερολόγιο {calendar}",
"Busy" : "Απασχολημένος",
"Calendar, contacts and tasks" : "Ημερολόγιο, επαφές και εργασίες",
"A <strong>calendar</strong> was modified" : "Τροποποιήθηκε ένα <strong>ημερολόγιο</strong> ",
"A calendar <strong>event</strong> was modified" : "Τροποποιήθηκε ένα <strong>γεγονός</strong> του ημερολογίου",
"Contact birthdays" : "Γενέθλια επαφών",

View file

@ -29,6 +29,7 @@
"{actor} updated event {event} in calendar {calendar}" : "Ο {actor} ενημέρωσε το γεγονός {event} στο ημερολόγιο {calendar}",
"You updated event {event} in calendar {calendar}" : "Ενημερώσατε το συμβάν {event} στο ημερολόγιο {calendar}",
"Busy" : "Απασχολημένος",
"Calendar, contacts and tasks" : "Ημερολόγιο, επαφές και εργασίες",
"A <strong>calendar</strong> was modified" : "Τροποποιήθηκε ένα <strong>ημερολόγιο</strong> ",
"A calendar <strong>event</strong> was modified" : "Τροποποιήθηκε ένα <strong>γεγονός</strong> του ημερολογίου",
"Contact birthdays" : "Γενέθλια επαφών",

View file

@ -31,6 +31,7 @@ OC.L10N.register(
"You deleted event {event} from calendar {calendar}" : "Poistit tapahtuman {event} kalenterista {calendar}",
"{actor} updated event {event} in calendar {calendar}" : "{actor} päivitti tapahtuman {event} kalenteriin {calendar}",
"You updated event {event} in calendar {calendar}" : "Päivitit tapahtuman {event} kalenteriin {calendar}",
"You moved event {event} from calendar {sourceCalendar} to calendar {targetCalendar}" : "Siirsit tapahtuman {event} kalenterista {sourceCalendar} kalenteriin {targetCalendar}",
"Busy" : "Varattu",
"Calendar, contacts and tasks" : "Kalenteri, yhteystiedot ja tehtävät",
"A <strong>calendar</strong> was modified" : "<strong>Kalenteria</strong> on muokattu",
@ -77,8 +78,13 @@ OC.L10N.register(
"{actor} shared address book {addressbook} with you" : "{actor} jakoi osoitekirjan {addressbook} kanssasi",
"You shared address book {addressbook} with {user}" : "Jaoit osoitekirjan {addressbook} käyttäjän {user} kanssa",
"{actor} shared address book {addressbook} with {user}" : "{actor} jakoi osoitekirjan {addressbook} käyttäjän {user} kanssa",
"{actor} unshared address book {addressbook} from you" : "{actor} lopetti osoitekirjan {addressbook} jakamisen kanssasi",
"You unshared address book {addressbook} from {user}" : "Lopetit osoitekirjan {addressbook} jakamisen käyttäjän {user} kanssa",
"{actor} unshared address book {addressbook} from {user}" : "{actor} lopetti osoitekirjan {addressbook} jakamisen käyttäjän {user} kanssa",
"You shared address book {addressbook} with group {group}" : "Jaoit osoitekirjan {addressbook} ryhmän {group} kanssa",
"{actor} shared address book {addressbook} with group {group}" : "{actor} jakoi osoitekirjan {addressbook} ryhmän {group} kanssa",
"You unshared address book {addressbook} from group {group}" : "Lopetit osoitekirjan {addressbook} jakamisen ryhmän {group} kanssa",
"{actor} unshared address book {addressbook} from group {group}" : "{actor} lopetti osoitekirjan {addressbook} jakamisen ryhmän {group} kanssa",
"{actor} created contact {card} in address book {addressbook}" : "{actor} loi yhteystiedon {card} osoitekirjaan {addressbook}",
"You created contact {card} in address book {addressbook}" : "Loit yhteystiedon {card} osoitekirjaan {addressbook}",
"{actor} deleted contact {card} from address book {addressbook}" : "{actor} poisti yhteystiedon {card} osoitekirjasta {addressbook}",
@ -88,6 +94,7 @@ OC.L10N.register(
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "<strong>Yhteystietoa</strong> tai <strong>osoitekirjaa</strong> muokattiin",
"File is not updatable: %1$s" : "Tiedosto ei ole päivitettävissä: %1$s",
"_%n byte_::_%n bytes_" : ["%n tavu","%n tavua"],
"Failed to check file size: %1$s" : "Tiedoston koon tarkistaminen epäonnistui: %1$s",
"Could not open file" : "Tiedoston avaaminen ei onnistunut",
"Encryption not ready: %1$s" : "Salaus ei ole valmis: %1$s",
"Failed to open file: %1$s" : "Tiedoston avaaminen epäonnistui: %1$s",

View file

@ -29,6 +29,7 @@
"You deleted event {event} from calendar {calendar}" : "Poistit tapahtuman {event} kalenterista {calendar}",
"{actor} updated event {event} in calendar {calendar}" : "{actor} päivitti tapahtuman {event} kalenteriin {calendar}",
"You updated event {event} in calendar {calendar}" : "Päivitit tapahtuman {event} kalenteriin {calendar}",
"You moved event {event} from calendar {sourceCalendar} to calendar {targetCalendar}" : "Siirsit tapahtuman {event} kalenterista {sourceCalendar} kalenteriin {targetCalendar}",
"Busy" : "Varattu",
"Calendar, contacts and tasks" : "Kalenteri, yhteystiedot ja tehtävät",
"A <strong>calendar</strong> was modified" : "<strong>Kalenteria</strong> on muokattu",
@ -75,8 +76,13 @@
"{actor} shared address book {addressbook} with you" : "{actor} jakoi osoitekirjan {addressbook} kanssasi",
"You shared address book {addressbook} with {user}" : "Jaoit osoitekirjan {addressbook} käyttäjän {user} kanssa",
"{actor} shared address book {addressbook} with {user}" : "{actor} jakoi osoitekirjan {addressbook} käyttäjän {user} kanssa",
"{actor} unshared address book {addressbook} from you" : "{actor} lopetti osoitekirjan {addressbook} jakamisen kanssasi",
"You unshared address book {addressbook} from {user}" : "Lopetit osoitekirjan {addressbook} jakamisen käyttäjän {user} kanssa",
"{actor} unshared address book {addressbook} from {user}" : "{actor} lopetti osoitekirjan {addressbook} jakamisen käyttäjän {user} kanssa",
"You shared address book {addressbook} with group {group}" : "Jaoit osoitekirjan {addressbook} ryhmän {group} kanssa",
"{actor} shared address book {addressbook} with group {group}" : "{actor} jakoi osoitekirjan {addressbook} ryhmän {group} kanssa",
"You unshared address book {addressbook} from group {group}" : "Lopetit osoitekirjan {addressbook} jakamisen ryhmän {group} kanssa",
"{actor} unshared address book {addressbook} from group {group}" : "{actor} lopetti osoitekirjan {addressbook} jakamisen ryhmän {group} kanssa",
"{actor} created contact {card} in address book {addressbook}" : "{actor} loi yhteystiedon {card} osoitekirjaan {addressbook}",
"You created contact {card} in address book {addressbook}" : "Loit yhteystiedon {card} osoitekirjaan {addressbook}",
"{actor} deleted contact {card} from address book {addressbook}" : "{actor} poisti yhteystiedon {card} osoitekirjasta {addressbook}",
@ -86,6 +92,7 @@
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "<strong>Yhteystietoa</strong> tai <strong>osoitekirjaa</strong> muokattiin",
"File is not updatable: %1$s" : "Tiedosto ei ole päivitettävissä: %1$s",
"_%n byte_::_%n bytes_" : ["%n tavu","%n tavua"],
"Failed to check file size: %1$s" : "Tiedoston koon tarkistaminen epäonnistui: %1$s",
"Could not open file" : "Tiedoston avaaminen ei onnistunut",
"Encryption not ready: %1$s" : "Salaus ei ole valmis: %1$s",
"Failed to open file: %1$s" : "Tiedoston avaaminen epäonnistui: %1$s",

View file

@ -2,6 +2,7 @@ OC.L10N.register(
"dav",
{
"Calendar" : "カレンダー",
"To-dos" : "To-dos",
"Personal" : "個人",
"{actor} created calendar {calendar}" : "{actor}はカレンダー {calendar} を作成しました",
"You created calendar {calendar}" : "カレンダー {calendar} を作成しました",

View file

@ -1,5 +1,6 @@
{ "translations": {
"Calendar" : "カレンダー",
"To-dos" : "To-dos",
"Personal" : "個人",
"{actor} created calendar {calendar}" : "{actor}はカレンダー {calendar} を作成しました",
"You created calendar {calendar}" : "カレンダー {calendar} を作成しました",

View file

@ -114,6 +114,7 @@ OC.L10N.register(
"If you configure your working hours, other users will see when you are out of office when they book a meeting." : "Om du konfigurerar dina arbetstimmar kan andra användare se när du inte är på jobbet när de bokar ett möte.",
"Time zone:" : "Tidszon:",
"to" : "till",
"Delete slot" : "Radera lucka",
"No working hours set" : "Inga arbetstimmar satta",
"Monday" : "Måndag",
"Tuesday" : "Tisdag",

View file

@ -112,6 +112,7 @@
"If you configure your working hours, other users will see when you are out of office when they book a meeting." : "Om du konfigurerar dina arbetstimmar kan andra användare se när du inte är på jobbet när de bokar ett möte.",
"Time zone:" : "Tidszon:",
"to" : "till",
"Delete slot" : "Radera lucka",
"No working hours set" : "Inga arbetstimmar satta",
"Monday" : "Måndag",
"Tuesday" : "Tisdag",

View file

@ -37,11 +37,22 @@ OC.L10N.register(
"{actor} restored event {event} of calendar {calendar}" : "{actor} 还原了事件 {event},它位于日历 {calendar}",
"You restored event {event} of calendar {calendar}" : "你还原了事件 {event},它位于日历 {calendar}",
"Busy" : "忙碌",
"{actor} created to-do {todo} in list {calendar}" : "{actor} 在清单 {calendar} 总创建了代办事项 {todo}",
"You created to-do {todo} in list {calendar}" : "您已在清单 {calendar} 总创建了代办事项 {todo}",
"{actor} created to-do {todo} in list {calendar}" : "{actor} 在清单 {calendar} 中创建了代办事项 {todo}",
"You created to-do {todo} in list {calendar}" : "您已在清单 {calendar} 中创建了代办事项 {todo}",
"{actor} deleted to-do {todo} from list {calendar}" : "{actor} 从列表 {calendar} 中删除了待办事项 {todo}",
"You deleted to-do {todo} from list {calendar}" : "你从列表 {calendar} 中删除了待办事项 {todo}",
"{actor} updated to-do {todo} in list {calendar}" : "{actor} 更新了列表 {calendar} 中的待办事项 {todo}",
"You updated to-do {todo} in list {calendar}" : "你更新了列表 {calendar} 中的待办事项 {todo}",
"{actor} solved to-do {todo} in list {calendar}" : "{actor} 解决了列表 {calendar} 中的待办事项 {todo}",
"You solved to-do {todo} in list {calendar}" : "你解决了列表 {calendar} 中的待办事项 {todo}",
"{actor} reopened to-do {todo} in list {calendar}" : "{actor} 重新开启了列表 {calendar} 中的待办事项 {todo}",
"You reopened to-do {todo} in list {calendar}" : "你重新开启了列表 {calendar} 中的待办事项 {todo}",
"{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} 将待办事项 {todo} 从列表 {sourceCalendar} 移动到列表 {targetCalendar}",
"You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "你将待办事项 {todo} 从列表 {sourceCalendar} 移动到列表 {targetCalendar}",
"Calendar, contacts and tasks" : "日历、联系人和任务",
"A <strong>calendar</strong> was modified" : "<strong>日历</strong>已经修改",
"A calendar <strong>event</strong> was modified" : "日历中<strong>事件</strong>已经修改",
"A calendar <strong>to-do</strong> was modified" : "日历中的<strong>待办事项</strong>已修改",
"Contact birthdays" : "联系人生日",
"Death of %s" : "%s 的忌日",
"Calendar:" : "日历:",
@ -101,6 +112,23 @@ OC.L10N.register(
"{actor} updated contact {card} in address book {addressbook}" : "{actor} 更新了通讯录 {addressbook} 中的联系人 {card} ",
"You updated contact {card} in address book {addressbook}" : "你更新了通讯录 {addressbook} 中的联系人 {card} ",
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "一名 <strong>联系人</strong>或一个<strong>通讯录</strong>被更改了",
"File is not updatable: %1$s" : "无法更新文件:%1$s",
"Could not write to final file, canceled by hook" : "无法写入最终文件,操作被插件取消",
"Could not write file contents" : "无法写入文件内容",
"_%n byte_::_%n bytes_" : ["%n字节"],
"Error while copying file to target location (copied: %1$s, expected filesize: %2$s)" : "将文件复制到目标位置时发生错误(已复制:%1$s预期大小%2$s",
"Expected filesize of %1$s but read (from Nextcloud client) and wrote (to Nextcloud storage) %2$s. Could either be a network problem on the sending side or a problem writing to the storage on the server side." : "预期文件大小为 %1$s实际从 Nextcloud 客户端读入并写入 Nextcloud 存储空间的大小为 %2$s。可能是发送端发生了网络问题或者是服务器写入存储设备时发生错误。",
"Could not rename part file to final file, canceled by hook" : "无法将部分文件重命名为最终文件,操作被插件取消",
"Could not rename part file to final file" : "无法将部分文件重命名为最终文件",
"Failed to check file size: %1$s" : "检查文件大小失败:%1$s",
"Could not open file" : "无法打开文件",
"Encryption not ready: %1$s" : "加密不可用:%1$s",
"Failed to open file: %1$s" : "打开文件失败:%1$s",
"Failed to unlink: %1$s" : "解除链接失败:%1$s",
"Invalid chunk name" : "分片名称无效",
"Could not rename part file assembled from chunks" : "无法重命名从分片组合的部分文件",
"Failed to write file contents: %1$s" : "写入文件内容失败:%1$s",
"File not found: %1$s" : "找不到文件:%1$s",
"System is in maintenance mode." : "系统处于维护模式 ",
"Upgrade needed" : "需要升级",
"Your %s needs to be configured to use HTTPS in order to use CalDAV and CardDAV with iOS/macOS." : "您的%s 需要配置使用HTTPS以在iOS/macOS中使用CalDAV和CardDAV。",
@ -112,6 +140,8 @@ OC.L10N.register(
"Completed on %s" : "已完成 %s",
"Due on %s by %s" : "到期于%s ,在%s之前",
"Due on %s" : "到期于%s",
"Migrated calendar (%1$s)" : "迁移的日历(%1$s",
"Calendars including events, details and attendees" : "日历包含活动、参与人和事件详情",
"Contacts and groups" : "联系人和群组",
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV端点",
@ -129,7 +159,11 @@ OC.L10N.register(
"Friday" : "周五",
"Saturday" : "周六",
"Sunday" : "周日",
"Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "不在可用时间内时,自动将用户状态设置为“请勿打扰”并静音所有通知。",
"Save" : "保存",
"Failed to load availability" : "可用时间段加载失败",
"Saved availability" : "可用时间已保存",
"Failed to save availability" : "可用时间保存失败",
"Calendar server" : "日历服务器",
"Send invitations to attendees" : "向与会者发送邀请",
"Automatically generate a birthday calendar" : "自动生成生日日历",

View file

@ -35,11 +35,22 @@
"{actor} restored event {event} of calendar {calendar}" : "{actor} 还原了事件 {event},它位于日历 {calendar}",
"You restored event {event} of calendar {calendar}" : "你还原了事件 {event},它位于日历 {calendar}",
"Busy" : "忙碌",
"{actor} created to-do {todo} in list {calendar}" : "{actor} 在清单 {calendar} 总创建了代办事项 {todo}",
"You created to-do {todo} in list {calendar}" : "您已在清单 {calendar} 总创建了代办事项 {todo}",
"{actor} created to-do {todo} in list {calendar}" : "{actor} 在清单 {calendar} 中创建了代办事项 {todo}",
"You created to-do {todo} in list {calendar}" : "您已在清单 {calendar} 中创建了代办事项 {todo}",
"{actor} deleted to-do {todo} from list {calendar}" : "{actor} 从列表 {calendar} 中删除了待办事项 {todo}",
"You deleted to-do {todo} from list {calendar}" : "你从列表 {calendar} 中删除了待办事项 {todo}",
"{actor} updated to-do {todo} in list {calendar}" : "{actor} 更新了列表 {calendar} 中的待办事项 {todo}",
"You updated to-do {todo} in list {calendar}" : "你更新了列表 {calendar} 中的待办事项 {todo}",
"{actor} solved to-do {todo} in list {calendar}" : "{actor} 解决了列表 {calendar} 中的待办事项 {todo}",
"You solved to-do {todo} in list {calendar}" : "你解决了列表 {calendar} 中的待办事项 {todo}",
"{actor} reopened to-do {todo} in list {calendar}" : "{actor} 重新开启了列表 {calendar} 中的待办事项 {todo}",
"You reopened to-do {todo} in list {calendar}" : "你重新开启了列表 {calendar} 中的待办事项 {todo}",
"{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} 将待办事项 {todo} 从列表 {sourceCalendar} 移动到列表 {targetCalendar}",
"You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "你将待办事项 {todo} 从列表 {sourceCalendar} 移动到列表 {targetCalendar}",
"Calendar, contacts and tasks" : "日历、联系人和任务",
"A <strong>calendar</strong> was modified" : "<strong>日历</strong>已经修改",
"A calendar <strong>event</strong> was modified" : "日历中<strong>事件</strong>已经修改",
"A calendar <strong>to-do</strong> was modified" : "日历中的<strong>待办事项</strong>已修改",
"Contact birthdays" : "联系人生日",
"Death of %s" : "%s 的忌日",
"Calendar:" : "日历:",
@ -99,6 +110,23 @@
"{actor} updated contact {card} in address book {addressbook}" : "{actor} 更新了通讯录 {addressbook} 中的联系人 {card} ",
"You updated contact {card} in address book {addressbook}" : "你更新了通讯录 {addressbook} 中的联系人 {card} ",
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "一名 <strong>联系人</strong>或一个<strong>通讯录</strong>被更改了",
"File is not updatable: %1$s" : "无法更新文件:%1$s",
"Could not write to final file, canceled by hook" : "无法写入最终文件,操作被插件取消",
"Could not write file contents" : "无法写入文件内容",
"_%n byte_::_%n bytes_" : ["%n字节"],
"Error while copying file to target location (copied: %1$s, expected filesize: %2$s)" : "将文件复制到目标位置时发生错误(已复制:%1$s预期大小%2$s",
"Expected filesize of %1$s but read (from Nextcloud client) and wrote (to Nextcloud storage) %2$s. Could either be a network problem on the sending side or a problem writing to the storage on the server side." : "预期文件大小为 %1$s实际从 Nextcloud 客户端读入并写入 Nextcloud 存储空间的大小为 %2$s。可能是发送端发生了网络问题或者是服务器写入存储设备时发生错误。",
"Could not rename part file to final file, canceled by hook" : "无法将部分文件重命名为最终文件,操作被插件取消",
"Could not rename part file to final file" : "无法将部分文件重命名为最终文件",
"Failed to check file size: %1$s" : "检查文件大小失败:%1$s",
"Could not open file" : "无法打开文件",
"Encryption not ready: %1$s" : "加密不可用:%1$s",
"Failed to open file: %1$s" : "打开文件失败:%1$s",
"Failed to unlink: %1$s" : "解除链接失败:%1$s",
"Invalid chunk name" : "分片名称无效",
"Could not rename part file assembled from chunks" : "无法重命名从分片组合的部分文件",
"Failed to write file contents: %1$s" : "写入文件内容失败:%1$s",
"File not found: %1$s" : "找不到文件:%1$s",
"System is in maintenance mode." : "系统处于维护模式 ",
"Upgrade needed" : "需要升级",
"Your %s needs to be configured to use HTTPS in order to use CalDAV and CardDAV with iOS/macOS." : "您的%s 需要配置使用HTTPS以在iOS/macOS中使用CalDAV和CardDAV。",
@ -110,6 +138,8 @@
"Completed on %s" : "已完成 %s",
"Due on %s by %s" : "到期于%s ,在%s之前",
"Due on %s" : "到期于%s",
"Migrated calendar (%1$s)" : "迁移的日历(%1$s",
"Calendars including events, details and attendees" : "日历包含活动、参与人和事件详情",
"Contacts and groups" : "联系人和群组",
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV端点",
@ -127,7 +157,11 @@
"Friday" : "周五",
"Saturday" : "周六",
"Sunday" : "周日",
"Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "不在可用时间内时,自动将用户状态设置为“请勿打扰”并静音所有通知。",
"Save" : "保存",
"Failed to load availability" : "可用时间段加载失败",
"Saved availability" : "可用时间已保存",
"Failed to save availability" : "可用时间保存失败",
"Calendar server" : "日历服务器",
"Send invitations to attendees" : "向与会者发送邀请",
"Automatically generate a birthday calendar" : "自动生成生日日历",

View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/**
* @copyright 2022 Thomas Citharel <nextcloud@tcit.fr>
*
* @author Thomas Citharel <nextcloud@tcit.fr>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\DAV\BackgroundJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use OCA\DAV\AppInfo\Application;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CardDAV\CardDavBackend;
use OCP\IConfig;
use Psr\Log\LoggerInterface;
class PruneOutdatedSyncTokensJob extends TimedJob {
private IConfig $config;
private LoggerInterface $logger;
private CardDavBackend $cardDavBackend;
private CalDavBackend $calDavBackend;
public function __construct(ITimeFactory $timeFactory, CalDavBackend $calDavBackend, CardDavBackend $cardDavBackend, IConfig $config, LoggerInterface $logger) {
parent::__construct($timeFactory);
$this->calDavBackend = $calDavBackend;
$this->cardDavBackend = $cardDavBackend;
$this->config = $config;
$this->logger = $logger;
$this->setInterval(60 * 60 * 24); // One day
$this->setTimeSensitivity(self::TIME_INSENSITIVE);
}
public function run($argument) {
$limit = max(1, (int) $this->config->getAppValue(Application::APP_ID, 'totalNumberOfSyncTokensToKeep', '10000'));
$prunedCalendarSyncTokens = $this->calDavBackend->pruneOutdatedSyncTokens($limit);
$prunedAddressBookSyncTokens = $this->cardDavBackend->pruneOutdatedSyncTokens($limit);
$this->logger->info('Pruned {calendarSyncTokensNumber} calendar sync tokens and {addressBooksSyncTokensNumber} address book sync tokens', [
'calendarSyncTokensNumber' => $prunedCalendarSyncTokens,
'addressBooksSyncTokensNumber' => $prunedAddressBookSyncTokens
]);
}
}

View file

@ -38,9 +38,6 @@ abstract class Base implements IProvider {
/** @var IUserManager */
protected $userManager;
/** @var string[] */
protected $userDisplayNames = [];
/** @var IGroupManager */
protected $groupManager;

View file

@ -417,7 +417,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
[, $name] = Uri\split($row['principaluri']);
$uri = $row['uri'] . '_shared_by_' . $name;
$row['displayname'] = $row['displayname'] . ' (' . $this->getUserDisplayName($name) . ')';
$row['displayname'] = $row['displayname'] . ' (' . ($this->userManager->getDisplayName($name) ?? ($name ?? '')) . ')';
$components = [];
if ($row['components']) {
$components = explode(',',$row['components']);
@ -493,25 +493,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
return array_values($calendars);
}
/**
* @param $uid
* @return string
*/
private function getUserDisplayName($uid) {
if (!isset($this->userDisplayNames[$uid])) {
$user = $this->userManager->get($uid);
if ($user instanceof IUser) {
$this->userDisplayNames[$uid] = $user->getDisplayName();
} else {
$this->userDisplayNames[$uid] = $uid;
}
}
return $this->userDisplayNames[$uid];
}
/**
* @return array
*/
@ -3104,6 +3085,20 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
return (int)$objectIds['id'];
}
/**
* @throws \InvalidArgumentException
*/
public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
if ($keep < 0) {
throw new \InvalidArgumentException();
}
$query = $this->db->getQueryBuilder();
$query->delete('calendarchanges')
->orderBy('id', 'DESC')
->setFirstResult($keep);
return $query->executeStatement();
}
/**
* return legacy endpoint principal name to new principal name
*

View file

@ -8,6 +8,7 @@ declare(strict_types=1);
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Anna Larch <anna.larch@gmx.net>
*
* @license GNU AGPL version 3 or any later version
*
@ -29,22 +30,18 @@ namespace OCA\DAV\CalDAV;
use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin;
use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Calendar\Exceptions\CalendarException;
use OCP\Calendar\ICreateFromString;
use OCP\Calendar\IHandleImipMessage;
use OCP\Constants;
use OCP\Security\ISecureRandom;
use Psr\Log\LoggerInterface;
use Sabre\DAV\Exception\Conflict;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Component\VEvent;
use Sabre\VObject\Document;
use Sabre\VObject\ITip\Message;
use Sabre\VObject\Property\VCard\DateTime;
use Sabre\VObject\Reader;
use function Sabre\Uri\split as uriSplit;
class CalendarImpl implements ICreateFromString {
class CalendarImpl implements ICreateFromString, IHandleImipMessage {
private CalDavBackend $backend;
private Calendar $calendar;
@ -214,20 +211,20 @@ class CalendarImpl implements ICreateFromString {
if(!isset($vEvent->{'ORGANIZER'}) || !isset($vEvent->{'ATTENDEE'})) {
throw new CalendarException('Could not process scheduling data, neccessary data missing from ICAL');
}
$orgaizer = $vEvent->{'ORGANIZER'}->getValue();
$organizer = $vEvent->{'ORGANIZER'}->getValue();
$attendee = $vEvent->{'ATTENDEE'}->getValue();
$iTipMessage->method = $vObject->{'METHOD'}->getValue();
if($iTipMessage->method === 'REPLY') {
if ($server->isExternalAttendee($vEvent->{'ATTENDEE'}->getValue())) {
$iTipMessage->recipient = $orgaizer;
$iTipMessage->recipient = $organizer;
} else {
$iTipMessage->recipient = $attendee;
}
$iTipMessage->sender = $attendee;
} else if($iTipMessage->method === 'CANCEL') {
$iTipMessage->recipient = $attendee;
$iTipMessage->sender = $orgaizer;
$iTipMessage->sender = $organizer;
}
$iTipMessage->uid = isset($vEvent->{'UID'}) ? $vEvent->{'UID'}->getValue() : '';
$iTipMessage->component = 'VEVENT';

View file

@ -8,6 +8,7 @@ declare(strict_types=1);
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license GNU AGPL version 3 or any later version
*
@ -42,10 +43,12 @@ interface INotificationProvider {
*
* @param VEvent $vevent
* @param string $calendarDisplayName
* @param string[] $principalEmailAddresses All email addresses associated to the principal owning the calendar object
* @param IUser[] $users
* @return void
*/
public function send(VEvent $vevent,
string $calendarDisplayName,
array $principalEmailAddresses,
array $users = []): void;
}

View file

@ -10,6 +10,7 @@ declare(strict_types=1);
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license GNU AGPL version 3 or any later version
*
@ -82,11 +83,13 @@ abstract class AbstractProvider implements INotificationProvider {
*
* @param VEvent $vevent
* @param string $calendarDisplayName
* @param string[] $principalEmailAddresses
* @param IUser[] $users
* @return void
*/
abstract public function send(VEvent $vevent,
string $calendarDisplayName,
array $principalEmailAddresses,
array $users = []): void;
/**

View file

@ -71,16 +71,28 @@ class EmailProvider extends AbstractProvider {
*
* @param VEvent $vevent
* @param string $calendarDisplayName
* @param string[] $principalEmailAddresses
* @param array $users
* @throws \Exception
*/
public function send(VEvent $vevent,
string $calendarDisplayName,
array $principalEmailAddresses,
array $users = []):void {
$fallbackLanguage = $this->getFallbackLanguage();
$organizerEmailAddress = null;
if (isset($vevent->ORGANIZER)) {
$organizerEmailAddress = $this->getEMailAddressOfAttendee($vevent->ORGANIZER);
}
$emailAddressesOfSharees = $this->getEMailAddressesOfAllUsersWithWriteAccessToCalendar($users);
$emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent);
$emailAddressesOfAttendees = [];
if (count($principalEmailAddresses) === 0
|| ($organizerEmailAddress && in_array($organizerEmailAddress, $principalEmailAddresses, true))
) {
$emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent);
}
// Quote from php.net:
// If the input arrays have the same string keys, then the later value for that key will overwrite the previous one.

View file

@ -10,6 +10,7 @@ declare(strict_types=1);
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Citharel <nextcloud@tcit.fr>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license GNU AGPL version 3 or any later version
*
@ -73,11 +74,13 @@ class PushProvider extends AbstractProvider {
*
* @param VEvent $vevent
* @param string $calendarDisplayName
* @param string[] $principalEmailAddresses
* @param IUser[] $users
* @throws \Exception
*/
public function send(VEvent $vevent,
string $calendarDisplayName = null,
string $calendarDisplayName,
array $principalEmailAddresses,
array $users = []):void {
if ($this->config->getAppValue('dav', 'sendEventRemindersPush', 'no') !== 'yes') {
return;

View file

@ -11,6 +11,7 @@ declare(strict_types=1);
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Citharel <nextcloud@tcit.fr>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license GNU AGPL version 3 or any later version
*
@ -32,6 +33,7 @@ namespace OCA\DAV\CalDAV\Reminder;
use DateTimeImmutable;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IGroup;
@ -76,6 +78,9 @@ class ReminderService {
/** @var LoggerInterface */
private $logger;
/** @var Principal */
private $principalConnector;
public const REMINDER_TYPE_EMAIL = 'EMAIL';
public const REMINDER_TYPE_DISPLAY = 'DISPLAY';
public const REMINDER_TYPE_AUDIO = 'AUDIO';
@ -98,7 +103,8 @@ class ReminderService {
CalDavBackend $caldavBackend,
ITimeFactory $timeFactory,
IConfig $config,
LoggerInterface $logger) {
LoggerInterface $logger,
Principal $principalConnector) {
$this->backend = $backend;
$this->notificationProviderManager = $notificationProviderManager;
$this->userManager = $userManager;
@ -107,6 +113,7 @@ class ReminderService {
$this->timeFactory = $timeFactory;
$this->config = $config;
$this->logger = $logger;
$this->principalConnector = $principalConnector;
}
/**
@ -175,12 +182,18 @@ class ReminderService {
$users[] = $user;
}
$userPrincipalEmailAddresses = [];
$userPrincipal = $this->principalConnector->getPrincipalByPath($reminder['principaluri']);
if ($userPrincipal) {
$userPrincipalEmailAddresses = $this->principalConnector->getEmailAddressesOfPrincipal($userPrincipal);
}
$this->logger->debug('Reminder {id} will be sent to {numUsers} users', [
'id' => $reminder['id'],
'numUsers' => count($users),
]);
$notificationProvider = $this->notificationProviderManager->getProvider($reminder['type']);
$notificationProvider->send($vevent, $reminder['displayname'], $users);
$notificationProvider->send($vevent, $reminder['displayname'], $userPrincipalEmailAddresses, $users);
$this->deleteOrProcessNext($reminder, $vevent);
}

View file

@ -178,12 +178,7 @@ class IMipPlugin extends SabreIMipPlugin {
$recipientName = $iTipMessage->recipientName ?: null;
if ($senderName === null || empty(trim($senderName))) {
$user = $this->userManager->get($this->userId);
if ($user) {
// getDisplayName automatically uses the uid
// if no display-name is set
$senderName = $user->getDisplayName();
}
$senderName = $this->userManager->getDisplayName($this->userId);
}
/** @var VEvent $vevent */
@ -225,7 +220,7 @@ class IMipPlugin extends SabreIMipPlugin {
];
$fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
$fromName = $l10n->t('%1$s via %2$s', [$senderName, $this->defaults->getName()]);
$fromName = $l10n->t('%1$s via %2$s', [$senderName ?? $this->userId, $this->defaults->getName()]);
$message = $this->mailer->createMessage()
->setFrom([$fromEMail => $fromName])

View file

@ -9,6 +9,7 @@
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Citharel <nextcloud@tcit.fr>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license GNU AGPL version 3 or any later version
*
@ -46,6 +47,7 @@ use Sabre\VObject\Component;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Component\VEvent;
use Sabre\VObject\DateTimeParser;
use Sabre\VObject\Document;
use Sabre\VObject\FreeBusyGenerator;
use Sabre\VObject\ITip;
use Sabre\VObject\Parameter;
@ -164,6 +166,14 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
* @inheritDoc
*/
public function scheduleLocalDelivery(ITip\Message $iTipMessage):void {
/** @var Component|null $vevent */
$vevent = $iTipMessage->message->VEVENT ?? null;
// Strip VALARMs from incoming VEVENT
if ($vevent && isset($vevent->VALARM)) {
$vevent->remove('VALARM');
}
parent::scheduleLocalDelivery($iTipMessage);
// We only care when the message was successfully delivered locally
@ -200,18 +210,10 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
return;
}
if (!isset($iTipMessage->message)) {
if (!$vevent) {
return;
}
$vcalendar = $iTipMessage->message;
if (!isset($vcalendar->VEVENT)) {
return;
}
/** @var Component $vevent */
$vevent = $vcalendar->VEVENT;
// We don't support autoresponses for recurrencing events for now
if (isset($vevent->RRULE) || isset($vevent->RDATE)) {
return;

View file

@ -207,7 +207,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
[, $name] = \Sabre\Uri\split($row['principaluri']);
$uri = $row['uri'] . '_shared_by_' . $name;
$displayName = $row['displayname'] . ' (' . $this->getUserDisplayName($name) . ')';
$displayName = $row['displayname'] . ' (' . ($this->userManager->getDisplayName($name) ?? $name ?? '') . ')';
$addressBooks[$row['id']] = [
'id' => $row['id'],
@ -256,20 +256,6 @@ class CardDavBackend implements BackendInterface, SyncSupport {
return array_values($addressBooks);
}
private function getUserDisplayName($uid) {
if (!isset($this->userDisplayNames[$uid])) {
$user = $this->userManager->get($uid);
if ($user instanceof IUser) {
$this->userDisplayNames[$uid] = $user->getDisplayName();
} else {
$this->userDisplayNames[$uid] = $uid;
}
}
return $this->userDisplayNames[$uid];
}
/**
* @param int $addressBookId
*/
@ -1327,6 +1313,20 @@ class CardDavBackend implements BackendInterface, SyncSupport {
return $this->sharingBackend->applyShareAcl($addressBookId, $acl);
}
/**
* @throws \InvalidArgumentException
*/
public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
if ($keep < 0) {
throw new \InvalidArgumentException();
}
$query = $this->db->getQueryBuilder();
$query->delete('addressbookchanges')
->orderBy('id', 'DESC')
->setFirstResult($keep);
return $query->executeStatement();
}
private function convertPrincipal(string $principalUri, bool $toV2): string {
if ($this->principalBackend->getPrincipalPrefix() === 'principals') {
[, $name] = \Sabre\Uri\split($principalUri);

View file

@ -607,4 +607,44 @@ class Principal implements BackendInterface {
return [];
}
/**
* Get all email addresses associated to a principal.
*
* @param array $principal Data from getPrincipal*()
* @return string[] All email addresses without the mailto: prefix
*/
public function getEmailAddressesOfPrincipal(array $principal): array {
$emailAddresses = [];
if (($primaryAddress = $principal['{http://sabredav.org/ns}email-address'])) {
$emailAddresses[] = $primaryAddress;
}
if (isset($principal['{DAV:}alternate-URI-set'])) {
foreach ($principal['{DAV:}alternate-URI-set'] as $address) {
if (str_starts_with($address, 'mailto:')) {
$emailAddresses[] = substr($address, 7);
}
}
}
if (isset($principal['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set'])) {
foreach ($principal['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set'] as $address) {
if (str_starts_with($address, 'mailto:')) {
$emailAddresses[] = substr($address, 7);
}
}
}
if (isset($principal['{http://calendarserver.org/ns/}email-address-set'])) {
foreach ($principal['{http://calendarserver.org/ns/}email-address-set'] as $address) {
if (str_starts_with($address, 'mailto:')) {
$emailAddresses[] = substr($address, 7);
}
}
}
return array_values(array_unique($emailAddresses));
}
}

View file

@ -44,7 +44,6 @@ use Sabre\DAV\INode;
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
/** @var \OC\Files\View */
private $view;
@ -79,6 +78,7 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
$server->on('beforeWriteContent', [$this, 'beforeWriteContent'], 10);
$server->on('beforeCreateFile', [$this, 'beforeCreateFile'], 10);
$server->on('beforeMove', [$this, 'beforeMove'], 10);
$server->on('beforeCopy', [$this, 'beforeCopy'], 10);
}
/**
@ -131,7 +131,42 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
$destinationNode = $this->server->tree->getNodeForPath($destination);
$path = $destinationNode->getPath();
} else {
$parentNode = $this->server->tree->getNodeForPath(dirname($destination));
$parent = dirname($destination);
if ($parent === '.') {
$parent = '';
}
$parentNode = $this->server->tree->getNodeForPath($parent);
$path = $parentNode->getPath();
}
return $this->checkQuota($path, $sourceNode->getSize());
}
/**
* Check quota on the target destination before a copy.
*/
public function beforeCopy(string $sourcePath, string $destinationPath): bool {
$sourceNode = $this->server->tree->getNodeForPath($sourcePath);
if (!$sourceNode instanceof Node) {
return false;
}
// get target node for proper path conversion
if ($this->server->tree->nodeExists($destinationPath)) {
$destinationNode = $this->server->tree->getNodeForPath($destinationPath);
if (!$destinationNode instanceof Node) {
return false;
}
$path = $destinationNode->getPath();
} else {
$parent = dirname($destinationPath);
if ($parent === '.') {
$parent = '';
}
$parentNode = $this->server->tree->getNodeForPath($parent);
if (!$parentNode instanceof Node) {
return false;
}
$path = $parentNode->getPath();
}

View file

@ -55,7 +55,7 @@ use SearchDAV\Query\Order;
use SearchDAV\Query\Query;
class FileSearchBackend implements ISearchBackend {
const OPERATOR_LIMIT = 100;
public const OPERATOR_LIMIT = 100;
/** @var CachingTree */
private $tree;
@ -432,7 +432,7 @@ class FileSearchBackend implements ISearchBackend {
if (is_numeric($value)) {
return max(0, 0 + $value);
}
$date = \DateTime::createFromFormat(\DateTimeInterface::ATOM, $value);
$date = \DateTime::createFromFormat(\DateTimeInterface::ATOM, (string)$value);
return ($date instanceof \DateTime && $date->getTimestamp() !== false) ? $date->getTimestamp() : 0;
default:
return $value;

View file

@ -118,7 +118,6 @@ class RootCollection extends SimpleCollection {
$roomCalendarRoot->disableListing = $disableListing;
$publicCalendarRoot = new PublicCalendarRoot($caldavBackend, $l10n, $config, $logger);
$publicCalendarRoot->disableListing = $disableListing;
$systemTagCollection = new SystemTag\SystemTagsByIdCollection(
\OC::$server->getSystemTagManager(),

View file

@ -72,18 +72,18 @@ exports[`CalDavSettings interactions 1`] = `
>
<span
class="checkbox-radio-switch checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-2b31639e=""
data-v-751c6bad=""
data-v-d423551e=""
style="--icon-size: 36px;"
>
<label
class="checkbox-radio-switch__label"
data-v-2b31639e=""
data-v-d423551e=""
for="caldavSendInvitations"
>
<input
class="checkbox-radio-switch__input"
data-v-2b31639e=""
data-v-d423551e=""
id="caldavSendInvitations"
type="checkbox"
value=""
@ -92,7 +92,7 @@ exports[`CalDavSettings interactions 1`] = `
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon checkbox-radio-switch__icon"
data-v-2b31639e=""
data-v-d423551e=""
role="img"
>
<svg
@ -133,18 +133,18 @@ exports[`CalDavSettings interactions 1`] = `
>
<span
class="checkbox-radio-switch checkbox checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-2b31639e=""
data-v-751c6bad=""
data-v-d423551e=""
style="--icon-size: 36px;"
>
<label
class="checkbox-radio-switch__label"
data-v-2b31639e=""
data-v-d423551e=""
for="caldavGenerateBirthdayCalendar"
>
<input
class="checkbox-radio-switch__input"
data-v-2b31639e=""
data-v-d423551e=""
id="caldavGenerateBirthdayCalendar"
type="checkbox"
value=""
@ -153,7 +153,7 @@ exports[`CalDavSettings interactions 1`] = `
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon checkbox-radio-switch__icon"
data-v-2b31639e=""
data-v-d423551e=""
role="img"
>
<svg
@ -202,18 +202,18 @@ exports[`CalDavSettings interactions 1`] = `
>
<span
class="checkbox-radio-switch checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-2b31639e=""
data-v-751c6bad=""
data-v-d423551e=""
style="--icon-size: 36px;"
>
<label
class="checkbox-radio-switch__label"
data-v-2b31639e=""
data-v-d423551e=""
for="caldavSendEventReminders"
>
<input
class="checkbox-radio-switch__input"
data-v-2b31639e=""
data-v-d423551e=""
id="caldavSendEventReminders"
type="checkbox"
value=""
@ -222,7 +222,7 @@ exports[`CalDavSettings interactions 1`] = `
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon checkbox-radio-switch__icon"
data-v-2b31639e=""
data-v-d423551e=""
role="img"
>
<svg
@ -276,18 +276,18 @@ exports[`CalDavSettings interactions 1`] = `
>
<span
class="checkbox-radio-switch checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-2b31639e=""
data-v-751c6bad=""
data-v-d423551e=""
style="--icon-size: 36px;"
>
<label
class="checkbox-radio-switch__label"
data-v-2b31639e=""
data-v-d423551e=""
for="caldavSendEventRemindersToSharedGroupMembers"
>
<input
class="checkbox-radio-switch__input"
data-v-2b31639e=""
data-v-d423551e=""
id="caldavSendEventRemindersToSharedGroupMembers"
type="checkbox"
value=""
@ -296,7 +296,7 @@ exports[`CalDavSettings interactions 1`] = `
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon checkbox-radio-switch__icon"
data-v-2b31639e=""
data-v-d423551e=""
role="img"
>
<svg
@ -334,18 +334,18 @@ exports[`CalDavSettings interactions 1`] = `
>
<span
class="checkbox-radio-switch checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-2b31639e=""
data-v-751c6bad=""
data-v-d423551e=""
style="--icon-size: 36px;"
>
<label
class="checkbox-radio-switch__label"
data-v-2b31639e=""
data-v-d423551e=""
for="caldavSendEventRemindersPush"
>
<input
class="checkbox-radio-switch__input"
data-v-2b31639e=""
data-v-d423551e=""
id="caldavSendEventRemindersPush"
type="checkbox"
value=""
@ -354,7 +354,7 @@ exports[`CalDavSettings interactions 1`] = `
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon checkbox-radio-switch__icon"
data-v-2b31639e=""
data-v-d423551e=""
role="img"
>
<svg

View file

@ -32,19 +32,17 @@ namespace OCA\DAV\Tests\unit\BackgroundJob;
use OCA\DAV\BackgroundJob\CleanupInvitationTokenJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\QueryBuilder\IQueryFunction;
use OCP\IDBConnection;
use Test\TestCase;
class CleanupInvitationTokenJobTest extends TestCase {
/** @var IDBConnection | \PHPUnit\Framework\MockObject\MockObject */
private $dbConnection;
/** @var ITimeFactory | \PHPUnit\Framework\MockObject\MockObject */
private $timeFactory;
/** @var \OCA\DAV\BackgroundJob\GenerateBirthdayCalendarBackgroundJob */
/** @var \OCA\DAV\BackgroundJob\CleanupInvitationTokenJob */
private $backgroundJob;
protected function setUp(): void {
@ -78,7 +76,7 @@ class CleanupInvitationTokenJobTest extends TestCase {
[1337, \PDO::PARAM_STR, null, 'namedParameter1337']
]);
$function = $this->createMock(IQueryFunction::class);
$function = 'function1337';
$expr->expects($this->once())
->method('lt')
->with('expiration', 'namedParameter1337')

View file

@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
/**
* @copyright 2018, Georg Ehrke <oc.list@georgehrke.com>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\DAV\Tests\unit\BackgroundJob;
use OCA\DAV\AppInfo\Application;
use OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CardDAV\CardDavBackend;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\TestCase;
class PruneOutdatedSyncTokensJobTest extends TestCase {
/** @var ITimeFactory | MockObject */
private $timeFactory;
/** @var CalDavBackend | MockObject */
private $calDavBackend;
/** @var CardDavBackend | MockObject */
private $cardDavBackend;
/** @var IConfig|MockObject */
private $config;
/** @var LoggerInterface|MockObject*/
private $logger;
/** @var PruneOutdatedSyncTokensJob */
private PruneOutdatedSyncTokensJob $backgroundJob;
protected function setUp(): void {
parent::setUp();
$this->timeFactory = $this->createMock(ITimeFactory::class);
$this->calDavBackend = $this->createMock(CalDavBackend::class);
$this->cardDavBackend = $this->createMock(CardDavBackend::class);
$this->config = $this->createMock(IConfig::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->backgroundJob = new PruneOutdatedSyncTokensJob($this->timeFactory, $this->calDavBackend, $this->cardDavBackend, $this->config, $this->logger);
}
/**
* @dataProvider dataForTestRun
*/
public function testRun(string $configValue, int $actualLimit, int $deletedCalendarSyncTokens, int $deletedAddressBookSyncTokens) {
$this->config->expects($this->once())
->method('getAppValue')
->with(Application::APP_ID, 'totalNumberOfSyncTokensToKeep', '10000')
->willReturn($configValue);
$this->calDavBackend->expects($this->once())
->method('pruneOutdatedSyncTokens')
->with($actualLimit)
->willReturn($deletedCalendarSyncTokens);
$this->cardDavBackend->expects($this->once())
->method('pruneOutdatedSyncTokens')
->with($actualLimit)
->willReturn($deletedAddressBookSyncTokens);
$this->logger->expects($this->once())
->method('info')
->with('Pruned {calendarSyncTokensNumber} calendar sync tokens and {addressBooksSyncTokensNumber} address book sync tokens', [
'calendarSyncTokensNumber' => $deletedCalendarSyncTokens,
'addressBooksSyncTokensNumber' => $deletedAddressBookSyncTokens
]);
$this->backgroundJob->run(null);
}
public function dataForTestRun(): array {
return [
['100', 100, 2, 3],
['0', 1, 0, 0]
];
}
}

View file

@ -1273,4 +1273,58 @@ EOD;
$this->assertEquals($sharerPrivate, $sharerSearchResults[1]['calendardata']);
$this->assertEquals($sharerConfidential, $sharerSearchResults[2]['calendardata']);
}
/**
* @throws \OCP\DB\Exception
* @throws \Sabre\DAV\Exception\BadRequest
*/
public function testPruneOutdatedSyncTokens(): void {
$calendarId = $this->createTestCalendar();
$uri = static::getUniqueID('calobj');
$calData = <<<EOD
BEGIN:VCALENDAR
VERSION:2.0
PRODID:Nextcloud Calendar
BEGIN:VEVENT
CREATED;VALUE=DATE-TIME:20130910T125139Z
UID:47d15e3ec8
LAST-MODIFIED;VALUE=DATE-TIME:20130910T125139Z
DTSTAMP;VALUE=DATE-TIME:20130910T125139Z
SUMMARY:Test Event
DTSTART;VALUE=DATE-TIME:20130912T130000Z
DTEND;VALUE=DATE-TIME:20130912T140000Z
CLASS:PUBLIC
END:VEVENT
END:VCALENDAR
EOD;
$this->backend->createCalendarObject($calendarId, $uri, $calData);
// update the card
$calData = <<<'EOD'
BEGIN:VCALENDAR
VERSION:2.0
PRODID:Nextcloud Calendar
BEGIN:VEVENT
CREATED;VALUE=DATE-TIME:20130910T125139Z
UID:47d15e3ec8
LAST-MODIFIED;VALUE=DATE-TIME:20130910T125139Z
DTSTAMP;VALUE=DATE-TIME:20130910T125139Z
SUMMARY:123 Event 🙈
DTSTART;VALUE=DATE-TIME:20130912T130000Z
DTEND;VALUE=DATE-TIME:20130912T140000Z
ATTENDEE;CN=test:mailto:foo@bar.com
END:VEVENT
END:VCALENDAR
EOD;
$this->backend->updateCalendarObject($calendarId, $uri, $calData);
$deleted = $this->backend->pruneOutdatedSyncTokens(0);
// At least one from the object creation and one from the object update
$this->assertGreaterThanOrEqual(2, $deleted);
$changes = $this->backend->getChangesForCalendar($calendarId, '5', 1);
$this->assertEmpty($changes['added']);
$this->assertEmpty($changes['modified']);
$this->assertEmpty($changes['deleted']);
}
}

View file

@ -65,6 +65,7 @@ class EmailProviderTest extends AbstractNotificationProviderTest {
public function testSendWithoutAttendees():void {
[$user1, $user2, $user3, , $user5] = $users = $this->getUsers();
$principalEmailAddresses = [$user1->getEmailAddress()];
$enL10N = $this->createMock(IL10N::class);
$enL10N->method('t')
@ -170,11 +171,12 @@ class EmailProviderTest extends AbstractNotificationProviderTest {
$this->setupURLGeneratorMock(2);
$vcalendar = $this->getNoAttendeeVCalendar();
$this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $users);
$this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $principalEmailAddresses, $users);
}
public function testSendWithAttendees(): void {
public function testSendWithAttendeesWhenOwnerIsOrganizer(): void {
[$user1, $user2, $user3, , $user5] = $users = $this->getUsers();
$principalEmailAddresses = [$user1->getEmailAddress()];
$enL10N = $this->createMock(IL10N::class);
$enL10N->method('t')
@ -266,7 +268,81 @@ class EmailProviderTest extends AbstractNotificationProviderTest {
$this->setupURLGeneratorMock(2);
$vcalendar = $this->getAttendeeVCalendar();
$this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $users);
$this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $principalEmailAddresses, $users);
}
public function testSendWithAttendeesWhenOwnerIsAttendee(): void {
[$user1, $user2, $user3] = $this->getUsers();
$users = [$user2, $user3];
$principalEmailAddresses = [$user2->getEmailAddress()];
$deL10N = $this->createMock(IL10N::class);
$deL10N->method('t')
->willReturnArgument(0);
$deL10N->method('l')
->willReturnArgument(0);
$this->l10nFactory
->method('getUserLanguage')
->willReturnMap([
[$user2, 'de'],
[$user3, 'de'],
]);
$this->l10nFactory
->method('findGenericLanguage')
->willReturn('en');
$this->l10nFactory
->method('languageExists')
->willReturnMap([
['dav', 'de', true],
]);
$this->l10nFactory
->method('get')
->willReturnMap([
['dav', 'de', null, $deL10N],
]);
$template1 = $this->getTemplateMock();
$message12 = $this->getMessageMock('uid2@example.com', $template1);
$message13 = $this->getMessageMock('uid3@example.com', $template1);
$this->mailer->expects(self::once())
->method('createEMailTemplate')
->with('dav.calendarReminder')
->willReturnOnConsecutiveCalls(
$template1,
);
$this->mailer->expects($this->atLeastOnce())
->method('validateMailAddress')
->willReturnMap([
['foo1@example.org', true],
['foo3@example.org', true],
['foo4@example.org', true],
['uid1@example.com', true],
['uid2@example.com', true],
['uid3@example.com', true],
['invalid', false],
]);
$this->mailer->expects($this->exactly(2))
->method('createMessage')
->with()
->willReturnOnConsecutiveCalls(
$message12,
$message13,
);
$this->mailer->expects($this->exactly(2))
->method('send')
->withConsecutive(
[$message12],
[$message13],
)->willReturn([]);
$this->setupURLGeneratorMock(1);
$vcalendar = $this->getAttendeeVCalendar();
$this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $principalEmailAddresses, $users);
}
/**
@ -376,6 +452,14 @@ class EmailProviderTest extends AbstractNotificationProviderTest {
'DESCRIPTION' => 'DESCRIPTION 456',
]);
$vcalendar->VEVENT->add(
'ORGANIZER',
'mailto:uid1@example.com',
[
'LANG' => 'en'
]
);
$vcalendar->VEVENT->add(
'ATTENDEE',
'mailto:foo1@example.org',

View file

@ -10,6 +10,7 @@ declare(strict_types=1);
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Citharel <nextcloud@tcit.fr>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license GNU AGPL version 3 or any later version
*
@ -90,7 +91,7 @@ class PushProviderTest extends AbstractNotificationProviderTest {
$users = [$user1, $user2, $user3];
$this->provider->send($this->vcalendar->VEVENT, $this->calendarDisplayName, $users);
$this->provider->send($this->vcalendar->VEVENT, $this->calendarDisplayName, [], $users);
}
public function testSend(): void {
@ -143,7 +144,7 @@ class PushProviderTest extends AbstractNotificationProviderTest {
->method('notify')
->with($notification3);
$this->provider->send($this->vcalendar->VEVENT, $this->calendarDisplayName, $users);
$this->provider->send($this->vcalendar->VEVENT, $this->calendarDisplayName, [], $users);
}
/**

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Citharel <nextcloud@tcit.fr>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license GNU AGPL version 3 or any later version
*
@ -33,18 +34,17 @@ use OCA\DAV\CalDAV\Reminder\Backend;
use OCA\DAV\CalDAV\Reminder\INotificationProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProviderManager;
use OCA\DAV\CalDAV\Reminder\ReminderService;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\TestCase;
class ReminderServiceTest extends TestCase {
/** @var Backend|MockObject */
private $backend;
@ -72,6 +72,9 @@ class ReminderServiceTest extends TestCase {
/** @var MockObject|LoggerInterface */
private $logger;
/** @var MockObject|Principal */
private $principalConnector;
public const CALENDAR_DATA = <<<EOD
BEGIN:VCALENDAR
PRODID:-//Nextcloud calendar v1.6.4
@ -202,6 +205,7 @@ EOD;
$this->timeFactory = $this->createMock(ITimeFactory::class);
$this->config = $this->createMock(IConfig::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->principalConnector = $this->createMock(Principal::class);
$this->caldavBackend->method('getShares')->willReturn([]);
@ -214,6 +218,7 @@ EOD;
$this->timeFactory,
$this->config,
$this->logger,
$this->principalConnector,
);
}

View file

@ -183,13 +183,10 @@ class IMipPluginTest extends TestCase {
$message = $this->_testMessage();
$message->senderName = null;
$user = $this->createMock(IUser::class);
$user->method('getDisplayName')->willReturn('Mr. Wizard');
$this->userManager->expects($this->once())
->method('get')
->method('getDisplayName')
->with('user123')
->willReturn($user);
->willReturn('Mr. Wizard');
$this->_expectSend();
$this->plugin->schedule($message);

View file

@ -40,7 +40,6 @@ use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class BackendTest extends TestCase {
/** @var IManager|MockObject */
protected $activityManager;
@ -53,6 +52,9 @@ class BackendTest extends TestCase {
/** @var IAppManager|MockObject */
protected $appManager;
/** @var IUserManager|MockObject */
protected $userManager;
protected function setUp(): void {
parent::setUp();
$this->activityManager = $this->createMock(IManager::class);

View file

@ -845,4 +845,22 @@ class CardDavBackendTest extends TestCase {
$result = $this->backend->collectCardProperties(666, 'FN');
$this->assertEquals(['John Doe'], $result);
}
/**
* @throws \OCP\DB\Exception
* @throws \Sabre\DAV\Exception\BadRequest
*/
public function testPruneOutdatedSyncTokens(): void {
$addressBookId = $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
$uri = $this->getUniqueID('card');
$this->backend->createCard($addressBookId, $uri, $this->vcardTest0);
$this->backend->updateCard($addressBookId, $uri, $this->vcardTest1);
$deleted = $this->backend->pruneOutdatedSyncTokens(0);
// At least one from the object creation and one from the object update
$this->assertGreaterThanOrEqual(2, $deleted);
$changes = $this->backend->getChangesForAddressBook($addressBookId, '5', 1);
$this->assertEmpty($changes['added']);
$this->assertEmpty($changes['modified']);
$this->assertEmpty($changes['deleted']);
}
}

View file

@ -11,6 +11,7 @@
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license AGPL-3.0
*
@ -53,7 +54,6 @@ use Sabre\DAV\PropPatch;
use Test\TestCase;
class PrincipalTest extends TestCase {
/** @var IUserManager | MockObject */
private $userManager;
@ -657,12 +657,12 @@ class PrincipalTest extends TestCase {
$user2->method('getSystemEMailAddress')->willReturn('user2@foo.bar');
$user3 = $this->createMock(IUser::class);
$user3->method('getUID')->willReturn('user3');
$user2->method('getDisplayName')->willReturn('User 22');
$user2->method('getSystemEMailAddress')->willReturn('user2@foo.bar123');
$user3->method('getDisplayName')->willReturn('User 22');
$user3->method('getSystemEMailAddress')->willReturn('user2@foo.bar123');
$user4 = $this->createMock(IUser::class);
$user4->method('getUID')->willReturn('user4');
$user2->method('getDisplayName')->willReturn('User 222');
$user2->method('getSystemEMailAddress')->willReturn('user2@foo.bar456');
$user4->method('getDisplayName')->willReturn('User 222');
$user4->method('getSystemEMailAddress')->willReturn('user2@foo.bar456');
$this->userManager->expects($this->at(0))
->method('searchDisplayName')
@ -974,4 +974,34 @@ class PrincipalTest extends TestCase {
['mailto:user3@foo.bar', 'user3@foo.bar', 'principals/users/user3'],
];
}
public function testGetEmailAddressesOfPrincipal(): void {
$principal = [
'{http://sabredav.org/ns}email-address' => 'bar@company.org',
'{DAV:}alternate-URI-set' => [
'/some/url',
'mailto:foo@bar.com',
'mailto:duplicate@example.com',
],
'{urn:ietf:params:xml:ns:caldav}calendar-user-address-set' => [
'mailto:bernard@example.com',
'mailto:bernard.desruisseaux@example.com',
],
'{http://calendarserver.org/ns/}email-address-set' => [
'mailto:duplicate@example.com',
'mailto:user@some.org',
],
];
$expected = [
'bar@company.org',
'foo@bar.com',
'duplicate@example.com',
'bernard@example.com',
'bernard.desruisseaux@example.com',
'user@some.org',
];
$actual = $this->connector->getEmailAddressesOfPrincipal($principal);
$this->assertEquals($expected, $actual);
}
}

View file

@ -37,14 +37,12 @@ use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\IResult;
use OCP\DB\QueryBuilder\IExpressionBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\QueryBuilder\IQueryFunction;
use OCP\IDBConnection;
use OCP\IRequest;
use Sabre\VObject\ITip\Message;
use Test\TestCase;
class InvitationResponseControllerTest extends TestCase {
/** @var InvitationResponseController */
private $controller;
@ -478,7 +476,7 @@ EOF;
->with(\PDO::FETCH_ASSOC)
->willReturn($return);
$function = $this->createMock(IQueryFunction::class);
$function = 'functionToken';
$expr->expects($this->once())
->method('eq')
->with('token', 'namedParameterToken')

View file

@ -1,8 +1,38 @@
OC.L10N.register(
"encryption",
{
"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." : "Šifrēšanas lietotnei nepareiza privātā atslēga. Lūdzu atjaunojiet savu privāto atslēgu personīgo uzstādījumu sadaļā, lai atjaunot pieeju šifrētajiem failiem.",
"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" : "Šifrēšanas lietotnes ir pieslēgta, bet šifrēšanas atslēgas nav uzstādītas. Lūdzu izejiet no sistēmas un ieejiet sistēmā atpakaļ.",
"Enabled" : "Pievienots"
"Missing recovery key password" : "Pazudusi atkopšanas atslēgas parole",
"Please repeat the recovery key password" : "Lūdzu atkārtot atgūšanas atslēgas paroli",
"Repeated recovery key password does not match the provided recovery key password" : "Atkārtota atkopšanas atslēgas parole nesakrīt ar izsniegto atkopšanas atslēgu paroli",
"Recovery key successfully enabled" : "Atkopšanas atslēga ir veiksmīgi iespējota",
"Could not enable recovery key. Please check your recovery key password!" : "Atkopšanas atslēgu nevarēja iespējot. Lūdzu, pārbaudiet atkopšanas atslēgas paroli!",
"Recovery key successfully disabled" : "Atkopšanas atslēga ir veiksmīgi deaktivizēta",
"Could not disable recovery key. Please check your recovery key password!" : "Atkopšanas atslēgu nevarēja atspējot. Lūdzu, pārbaudiet atkopšanas atslēgas paroli!",
"Missing parameters" : "Trūkstošos parametrs",
"Please provide the old recovery password" : "Lūdzu, norādiet iepriekšējo atkopšanas paroli",
"Please provide a new recovery password" : "Lūdzu, ievadiet jaunu paroli",
"Please repeat the new recovery password" : "Lūdzu, atkārtojiet jauno atkopšanas paroli",
"Password successfully changed." : "Parole veiksmīgi nomainīta.",
"Could not change the password. Maybe the old password was not correct." : "Nevarēja mainīt paroli. Varbūt vecā parole nav pareiza.",
"Recovery Key disabled" : "Atkopšanas atslēga deaktivizēta",
"Recovery Key enabled" : "Atkopšanas atslēga aktivizēta",
"Could not enable the recovery key, please try again or contact your administrator" : "Nevarēja iespējot atkopšanas atslēga, lūdzu, mēģiniet vēlreiz vai sazinieties ar administratoru",
"Could not update the private key password." : "Nevarēja atjaunināt privātās atslēgas paroli.",
"The old password was not correct, please try again." : "Vecā parole nav pareiza, lūdzu, mēģiniet vēlreiz.",
"The current log-in password was not correct, please try again." : "Pašreizējā pieteikšanās parole nebija pareiza, lūdzu, mēģiniet vēlreiz.",
"Private key password successfully updated." : "Privātās atslēgas parole ir veiksmīgi atjaunināta.",
"Encryption app is enabled and ready" : "Šifrēšanas lietotne ir iespējota un gatava",
"Bad Signature" : "Nederīgs paraksts",
"Missing Signature" : "Trūkst paraksta",
"The share will expire on %s." : "Koplietošana beigsies %s.",
"Cheers!" : "Priekā!",
"Change Password" : "Mainīt paroli",
"Your private key password no longer matches your log-in password." : "Jūsu privātās atslēgas parole vairs nesakrīt ar jūsu pieteikšanās paroli.",
"Set your old private key password to your current log-in password:" : "Iestatiet savu veco privātās atslēgas paroli uz pašreizējo pieteikšanās paroli:",
"Old log-in password" : "Vecā pieteikšanās parole",
"Current log-in password" : "Pašreizējā pieteikšanās parole",
"Enable password recovery:" : "Iespējot paroles atjaunošanu:",
"Enabled" : "Pievienots",
"Disabled" : "Atspējots"
},
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);");

View file

@ -1,6 +1,36 @@
{ "translations": {
"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." : "Šifrēšanas lietotnei nepareiza privātā atslēga. Lūdzu atjaunojiet savu privāto atslēgu personīgo uzstādījumu sadaļā, lai atjaunot pieeju šifrētajiem failiem.",
"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" : "Šifrēšanas lietotnes ir pieslēgta, bet šifrēšanas atslēgas nav uzstādītas. Lūdzu izejiet no sistēmas un ieejiet sistēmā atpakaļ.",
"Enabled" : "Pievienots"
"Missing recovery key password" : "Pazudusi atkopšanas atslēgas parole",
"Please repeat the recovery key password" : "Lūdzu atkārtot atgūšanas atslēgas paroli",
"Repeated recovery key password does not match the provided recovery key password" : "Atkārtota atkopšanas atslēgas parole nesakrīt ar izsniegto atkopšanas atslēgu paroli",
"Recovery key successfully enabled" : "Atkopšanas atslēga ir veiksmīgi iespējota",
"Could not enable recovery key. Please check your recovery key password!" : "Atkopšanas atslēgu nevarēja iespējot. Lūdzu, pārbaudiet atkopšanas atslēgas paroli!",
"Recovery key successfully disabled" : "Atkopšanas atslēga ir veiksmīgi deaktivizēta",
"Could not disable recovery key. Please check your recovery key password!" : "Atkopšanas atslēgu nevarēja atspējot. Lūdzu, pārbaudiet atkopšanas atslēgas paroli!",
"Missing parameters" : "Trūkstošos parametrs",
"Please provide the old recovery password" : "Lūdzu, norādiet iepriekšējo atkopšanas paroli",
"Please provide a new recovery password" : "Lūdzu, ievadiet jaunu paroli",
"Please repeat the new recovery password" : "Lūdzu, atkārtojiet jauno atkopšanas paroli",
"Password successfully changed." : "Parole veiksmīgi nomainīta.",
"Could not change the password. Maybe the old password was not correct." : "Nevarēja mainīt paroli. Varbūt vecā parole nav pareiza.",
"Recovery Key disabled" : "Atkopšanas atslēga deaktivizēta",
"Recovery Key enabled" : "Atkopšanas atslēga aktivizēta",
"Could not enable the recovery key, please try again or contact your administrator" : "Nevarēja iespējot atkopšanas atslēga, lūdzu, mēģiniet vēlreiz vai sazinieties ar administratoru",
"Could not update the private key password." : "Nevarēja atjaunināt privātās atslēgas paroli.",
"The old password was not correct, please try again." : "Vecā parole nav pareiza, lūdzu, mēģiniet vēlreiz.",
"The current log-in password was not correct, please try again." : "Pašreizējā pieteikšanās parole nebija pareiza, lūdzu, mēģiniet vēlreiz.",
"Private key password successfully updated." : "Privātās atslēgas parole ir veiksmīgi atjaunināta.",
"Encryption app is enabled and ready" : "Šifrēšanas lietotne ir iespējota un gatava",
"Bad Signature" : "Nederīgs paraksts",
"Missing Signature" : "Trūkst paraksta",
"The share will expire on %s." : "Koplietošana beigsies %s.",
"Cheers!" : "Priekā!",
"Change Password" : "Mainīt paroli",
"Your private key password no longer matches your log-in password." : "Jūsu privātās atslēgas parole vairs nesakrīt ar jūsu pieteikšanās paroli.",
"Set your old private key password to your current log-in password:" : "Iestatiet savu veco privātās atslēgas paroli uz pašreizējo pieteikšanās paroli:",
"Old log-in password" : "Vecā pieteikšanās parole",
"Current log-in password" : "Pašreizējā pieteikšanās parole",
"Enable password recovery:" : "Iespējot paroles atjaunošanu:",
"Enabled" : "Pievienots",
"Disabled" : "Atspējots"
},"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"
}

View file

@ -5,7 +5,7 @@ OC.L10N.register(
"Please repeat the recovery key password" : "Введіть ще раз пароль для ключа відновлення",
"Repeated recovery key password does not match the provided recovery key password" : "Введені паролі ключа відновлення не співпадають",
"Recovery key successfully enabled" : "Ключ відновлення підключено",
"Could not enable recovery key. Please check your recovery key password!" : "Не вдалося підключити ключ відновлення. Будь ласка, перевірте пароль свого ключа відновлення!",
"Could not enable recovery key. Please check your recovery key password!" : "Не вдалося застосувати ключ відновлення. Будь ласка, перевірте пароль ключа відновлення!",
"Recovery key successfully disabled" : "Ключ відновлення відключено",
"Could not disable recovery key. Please check your recovery key password!" : "Не вдалося відключити ключ відновлення. Будь ласка, перевірте пароль ключа відновлення!",
"Missing parameters" : "Відсутні параметри",
@ -16,7 +16,7 @@ OC.L10N.register(
"Could not change the password. Maybe the old password was not correct." : "Не вдалося змінити пароль. Можливо ви неправильно ввели старий пароль.",
"Recovery Key disabled" : "Ключ відновлення відключений",
"Recovery Key enabled" : "Відновлення ключа увімкнено",
"Could not enable the recovery key, please try again or contact your administrator" : "Не вдалося підключити ключ відновлення, будь ласка, перевірте пароль ключа відновлення!",
"Could not enable the recovery key, please try again or contact your administrator" : "Не вдалося застосувати ключ відновлення, будь ласка, перевірте пароль ключа відновлення або сконтактуйте з адміністратором!",
"Could not update the private key password." : "Не вдалося оновити пароль секретного ключа.",
"The old password was not correct, please try again." : "Старий пароль введено не вірно, спробуйте ще раз.",
"The current log-in password was not correct, please try again." : "Невірний пароль входу, будь ласка, спробуйте ще раз.",

View file

@ -3,7 +3,7 @@
"Please repeat the recovery key password" : "Введіть ще раз пароль для ключа відновлення",
"Repeated recovery key password does not match the provided recovery key password" : "Введені паролі ключа відновлення не співпадають",
"Recovery key successfully enabled" : "Ключ відновлення підключено",
"Could not enable recovery key. Please check your recovery key password!" : "Не вдалося підключити ключ відновлення. Будь ласка, перевірте пароль свого ключа відновлення!",
"Could not enable recovery key. Please check your recovery key password!" : "Не вдалося застосувати ключ відновлення. Будь ласка, перевірте пароль ключа відновлення!",
"Recovery key successfully disabled" : "Ключ відновлення відключено",
"Could not disable recovery key. Please check your recovery key password!" : "Не вдалося відключити ключ відновлення. Будь ласка, перевірте пароль ключа відновлення!",
"Missing parameters" : "Відсутні параметри",
@ -14,7 +14,7 @@
"Could not change the password. Maybe the old password was not correct." : "Не вдалося змінити пароль. Можливо ви неправильно ввели старий пароль.",
"Recovery Key disabled" : "Ключ відновлення відключений",
"Recovery Key enabled" : "Відновлення ключа увімкнено",
"Could not enable the recovery key, please try again or contact your administrator" : "Не вдалося підключити ключ відновлення, будь ласка, перевірте пароль ключа відновлення!",
"Could not enable the recovery key, please try again or contact your administrator" : "Не вдалося застосувати ключ відновлення, будь ласка, перевірте пароль ключа відновлення або сконтактуйте з адміністратором!",
"Could not update the private key password." : "Не вдалося оновити пароль секретного ключа.",
"The old password was not correct, please try again." : "Старий пароль введено не вірно, спробуйте ще раз.",
"The current log-in password was not correct, please try again." : "Невірний пароль входу, будь ласка, спробуйте ще раз.",

View file

@ -59,7 +59,6 @@ OC.L10N.register(
"Not supported!" : "Не се поддържа!",
"Press ⌘-C to copy." : "За копиране натиснете ⌘-C",
"Press Ctrl-C to copy." : "За копиране натиснете Ctrl-C",
"Open documentation" : "Отвори документацията",
"Adjust how people can share between servers." : "Настройка на начина по който хората могат да споделят между сървърите. "
"Open documentation" : "Отвори документацията"
},
"nplurals=2; plural=(n != 1);");

View file

@ -57,7 +57,6 @@
"Not supported!" : "Не се поддържа!",
"Press ⌘-C to copy." : "За копиране натиснете ⌘-C",
"Press Ctrl-C to copy." : "За копиране натиснете Ctrl-C",
"Open documentation" : "Отвори документацията",
"Adjust how people can share between servers." : "Настройка на начина по който хората могат да споделят между сървърите. "
"Open documentation" : "Отвори документацията"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View file

@ -53,7 +53,6 @@ OC.L10N.register(
"Not supported!" : "No soportat!",
"Press ⌘-C to copy." : "Premeu ⌘-C per copiar.",
"Press Ctrl-C to copy." : "Premeu CTRL+C per copiar.",
"Open documentation" : "Obre la documentació",
"Adjust how people can share between servers." : "Ajusteu com la gent pot compartir entre servidors."
"Open documentation" : "Obre la documentació"
},
"nplurals=2; plural=(n != 1);");

View file

@ -51,7 +51,6 @@
"Not supported!" : "No soportat!",
"Press ⌘-C to copy." : "Premeu ⌘-C per copiar.",
"Press Ctrl-C to copy." : "Premeu CTRL+C per copiar.",
"Open documentation" : "Obre la documentació",
"Adjust how people can share between servers." : "Ajusteu com la gent pot compartir entre servidors."
"Open documentation" : "Obre la documentació"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View file

@ -59,7 +59,6 @@ OC.L10N.register(
"Not supported!" : "Nepodporováno!",
"Press ⌘-C to copy." : "Zkopírujete stisknutím ⌘C.",
"Press Ctrl-C to copy." : "Zkopírujete stisknutím Ctrl+C.",
"Open documentation" : "Otevřít dokumentaci",
"Adjust how people can share between servers." : "Upravte, jak mohou lidé mezi servery sdílet."
"Open documentation" : "Otevřít dokumentaci"
},
"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;");

View file

@ -57,7 +57,6 @@
"Not supported!" : "Nepodporováno!",
"Press ⌘-C to copy." : "Zkopírujete stisknutím ⌘C.",
"Press Ctrl-C to copy." : "Zkopírujete stisknutím Ctrl+C.",
"Open documentation" : "Otevřít dokumentaci",
"Adjust how people can share between servers." : "Upravte, jak mohou lidé mezi servery sdílet."
"Open documentation" : "Otevřít dokumentaci"
},"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;"
}

View file

@ -53,7 +53,6 @@ OC.L10N.register(
"Not supported!" : "Ikke understøttet!",
"Press ⌘-C to copy." : "Tryk ⌘-C for kopiering.",
"Press Ctrl-C to copy." : "Tryk Ctrl-C for at kopiere.",
"Open documentation" : "Åbn dokumentation",
"Adjust how people can share between servers." : "Juster hvordan folk kan dele mellem servere."
"Open documentation" : "Åbn dokumentation"
},
"nplurals=2; plural=(n != 1);");

View file

@ -51,7 +51,6 @@
"Not supported!" : "Ikke understøttet!",
"Press ⌘-C to copy." : "Tryk ⌘-C for kopiering.",
"Press Ctrl-C to copy." : "Tryk Ctrl-C for at kopiere.",
"Open documentation" : "Åbn dokumentation",
"Adjust how people can share between servers." : "Juster hvordan folk kan dele mellem servere."
"Open documentation" : "Åbn dokumentation"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View file

@ -55,7 +55,6 @@ OC.L10N.register(
"Not supported!" : "Nicht unterstützt!",
"Press ⌘-C to copy." : "⌘-C zum Kopieren drücken.",
"Press Ctrl-C to copy." : "Zum Kopieren Strg-C drücken.",
"Open documentation" : "Dokumentation öffnen",
"Adjust how people can share between servers." : "Definiere wie die Benutzer Inhalte mit anderen Servern teilen können."
"Open documentation" : "Dokumentation öffnen"
},
"nplurals=2; plural=(n != 1);");

View file

@ -53,7 +53,6 @@
"Not supported!" : "Nicht unterstützt!",
"Press ⌘-C to copy." : "⌘-C zum Kopieren drücken.",
"Press Ctrl-C to copy." : "Zum Kopieren Strg-C drücken.",
"Open documentation" : "Dokumentation öffnen",
"Adjust how people can share between servers." : "Definiere wie die Benutzer Inhalte mit anderen Servern teilen können."
"Open documentation" : "Dokumentation öffnen"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View file

@ -59,7 +59,6 @@ OC.L10N.register(
"Not supported!" : "Nicht unterstützt!",
"Press ⌘-C to copy." : "⌘-C zum Kopieren drücken.",
"Press Ctrl-C to copy." : "Zum Kopieren Strg-C drücken.",
"Open documentation" : "Dokumentation öffnen",
"Adjust how people can share between servers." : "Definiere wie die Benutzer Inhalte mit anderen Servern teilen können."
"Open documentation" : "Dokumentation öffnen"
},
"nplurals=2; plural=(n != 1);");

View file

@ -57,7 +57,6 @@
"Not supported!" : "Nicht unterstützt!",
"Press ⌘-C to copy." : "⌘-C zum Kopieren drücken.",
"Press Ctrl-C to copy." : "Zum Kopieren Strg-C drücken.",
"Open documentation" : "Dokumentation öffnen",
"Adjust how people can share between servers." : "Definiere wie die Benutzer Inhalte mit anderen Servern teilen können."
"Open documentation" : "Dokumentation öffnen"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View file

@ -51,7 +51,6 @@ OC.L10N.register(
"Not supported!" : "Δεν υποστηρίζεται!",
"Press ⌘-C to copy." : "Για αντιγραφή πατήστε ⌘-C.",
"Press Ctrl-C to copy." : "Για αντιγραφή πατήστε Ctrl-C.",
"Open documentation" : "Άνοιγμα τεκμηρίωσης",
"Adjust how people can share between servers." : "Προσαρμόστε το πως τα άτομα θα διαμοιράζονται μεταξύ των διακομιστών"
"Open documentation" : "Άνοιγμα τεκμηρίωσης"
},
"nplurals=2; plural=(n != 1);");

View file

@ -49,7 +49,6 @@
"Not supported!" : "Δεν υποστηρίζεται!",
"Press ⌘-C to copy." : "Για αντιγραφή πατήστε ⌘-C.",
"Press Ctrl-C to copy." : "Για αντιγραφή πατήστε Ctrl-C.",
"Open documentation" : "Άνοιγμα τεκμηρίωσης",
"Adjust how people can share between servers." : "Προσαρμόστε το πως τα άτομα θα διαμοιράζονται μεταξύ των διακομιστών"
"Open documentation" : "Άνοιγμα τεκμηρίωσης"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View file

@ -42,7 +42,6 @@ OC.L10N.register(
"Not supported!" : "Not supported!",
"Press ⌘-C to copy." : "Press ⌘-C to copy.",
"Press Ctrl-C to copy." : "Press Ctrl-C to copy.",
"Open documentation" : "Open documentation",
"Adjust how people can share between servers." : "Adjust how people can share between servers."
"Open documentation" : "Open documentation"
},
"nplurals=2; plural=(n != 1);");

View file

@ -40,7 +40,6 @@
"Not supported!" : "Not supported!",
"Press ⌘-C to copy." : "Press ⌘-C to copy.",
"Press Ctrl-C to copy." : "Press Ctrl-C to copy.",
"Open documentation" : "Open documentation",
"Adjust how people can share between servers." : "Adjust how people can share between servers."
"Open documentation" : "Open documentation"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View file

@ -50,7 +50,6 @@ OC.L10N.register(
"Not supported!" : "Ne subtenite!",
"Press ⌘-C to copy." : "Premu ⌘-C por kopii.",
"Press Ctrl-C to copy." : "Premu Ctrl-C por kopii.",
"Open documentation" : "Malfermi la dokumentaron",
"Adjust how people can share between servers." : "Agordi, kiel oni povas kunhavigi inter serviloj."
"Open documentation" : "Malfermi la dokumentaron"
},
"nplurals=2; plural=(n != 1);");

View file

@ -48,7 +48,6 @@
"Not supported!" : "Ne subtenite!",
"Press ⌘-C to copy." : "Premu ⌘-C por kopii.",
"Press Ctrl-C to copy." : "Premu Ctrl-C por kopii.",
"Open documentation" : "Malfermi la dokumentaron",
"Adjust how people can share between servers." : "Agordi, kiel oni povas kunhavigi inter serviloj."
"Open documentation" : "Malfermi la dokumentaron"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View file

@ -59,7 +59,6 @@ OC.L10N.register(
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajusta cómo la gente puede compartir entre servidores."
"Open documentation" : "Abrir documentación"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

View file

@ -57,7 +57,6 @@
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajusta cómo la gente puede compartir entre servidores."
"Open documentation" : "Abrir documentación"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

View file

@ -39,7 +39,6 @@ OC.L10N.register(
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

View file

@ -37,7 +37,6 @@
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

View file

@ -39,7 +39,6 @@ OC.L10N.register(
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presione ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presione Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

View file

@ -37,7 +37,6 @@
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presione ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presione Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

View file

@ -41,7 +41,6 @@ OC.L10N.register(
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

View file

@ -39,7 +39,6 @@
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

View file

@ -41,7 +41,6 @@ OC.L10N.register(
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

View file

@ -39,7 +39,6 @@
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

View file

@ -41,7 +41,6 @@ OC.L10N.register(
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

View file

@ -39,7 +39,6 @@
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

View file

@ -41,7 +41,6 @@ OC.L10N.register(
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

View file

@ -39,7 +39,6 @@
"Not supported!" : "¡No soportado!",
"Press ⌘-C to copy." : "Presiona ⌘-C para copiar.",
"Press Ctrl-C to copy." : "Presiona Ctrl-C para copiar.",
"Open documentation" : "Abrir documentación",
"Adjust how people can share between servers." : "Ajustar cómo las personas pueden compartir entre servidores. "
"Open documentation" : "Abrir documentación"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

Some files were not shown because too many files have changed in this diff Show more