Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -400,13 +400,88 @@ jobs:
run_command: empty-upload
force: true

user-migration-smoke-test:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: [ '8.5' ]
nextcloud-versions: [ 'stable35' ]
db: [ 'mysql' ]
env:
TEST_USER: user1
TEST_EMAIL: user1@localhost
EXPORT_DIRECTORY: /tmp
name: Nextcloud ${{ matrix.nextcloud-versions }} php${{ matrix.php-versions }} user migration test
steps:
- name: Set up Nextcloud env
uses: nextcloud/setup-server-action@34b73d5b0e3633f83a52227d00cc2a6c41d01d9a # v1.0.0
with:
nextcloud-version: ${{ matrix.nextcloud-versions }}
php-version: ${{ matrix.php-versions }}
patch-php-version-check: ${{ matrix.php-versions == '8.6' }}
node-version: 'false'
install: true
- name: Checkout mail
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: nextcloud/apps/mail
fetch-depth: 2
- name: Install dependencies
working-directory: nextcloud/apps/mail
run: composer install --no-dev
- name: Install mail
run: php -f nextcloud/occ app:enable mail
- name: Checkout user_migration
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: nextcloud/user_migration
ref: 3b4007d9ed2c80c53ca8bce6692e14aab5e482b0 # v10.5.0
path: nextcloud/apps/user_migration
- name: Install dependencies
working-directory: nextcloud/apps/user_migration
run: composer install --no-dev
- name: Patch version check for nightly Nextcloud
if: ${{ matrix.nextcloud-versions == 'master' }}
run: sed -i -E 's/max-version="([0-9]{2})"/max-version="99"/' nextcloud/apps/user_migration/appinfo/info.xml
- name: Install user_migration
run: php -f nextcloud/occ app:enable user_migration
- name: Create test user
run: php -f nextcloud/occ user:add --generate-password $TEST_USER
- name: Create mail account to migrate
run: |
php -f nextcloud/occ mail:account:create-imap \
$TEST_USER 'Test Account' $TEST_EMAIL \
imap.localhost 993 ssl $TEST_EMAIL imap-password \
smtp.localhost 465 ssl $TEST_EMAIL smtp-password
- name: Test basic mail export
run: php -f nextcloud/occ user:export -t mail_account $TEST_USER $EXPORT_DIRECTORY
- name: Assert the account was written to the export
run: |
EXPORTED_ZIP=$(find $EXPORT_DIRECTORY -name "$TEST_USER*.zip" | tail -1)
unzip -l "$EXPORTED_ZIP" | grep -q 'mail/accounts/.*\.json' \
|| { echo "::error::no account file in the export"; unzip -l "$EXPORTED_ZIP"; exit 1; }
- name: Delete exported user to avoid overwrite confirmation during import
run: php -f nextcloud/occ user:delete $TEST_USER
- name: Test basic mail import
run: |
EXPORTED_ZIP=$(find $EXPORT_DIRECTORY -name "$TEST_USER*.zip" | tail -1)
php -f nextcloud/occ user:import $EXPORTED_ZIP
- name: Assert the account was restored
run: |
php -f nextcloud/occ mail:account:export $TEST_USER | tee /tmp/imported-accounts.txt
grep -q "E-Mail: $TEST_EMAIL" /tmp/imported-accounts.txt \
|| { echo "::error::imported user has no mail account"; exit 1; }
grep -q "IMAP user: $TEST_EMAIL" /tmp/imported-accounts.txt \
|| { echo "::error::imported account lost its IMAP username"; exit 1; }

summary:
runs-on: ubuntu-latest-low
needs:
- unit-tests
- integration-tests
- frontend-unit-test
- frontend-e2e-tests
- user-migration-smoke-test

if: always()

Expand All @@ -421,3 +496,5 @@ jobs:
run: if ${{ needs.frontend-unit-test.result != 'success' && needs.frontend-unit-test.result != 'skipped' }}; then exit 1; fi
- name: Frontend E2E test status
run: if ${{ needs.frontend-e2e-tests.result != 'success' && needs.frontend-e2e-tests.result != 'skipped' }}; then exit 1; fi
- name: User migration smoke test status
run: if ${{ needs.user-migration-smoke-test.result != 'success' && needs.user-migration-smoke-test.result != 'skipped' }}; then exit 1; fi
3 changes: 3 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use OCA\Mail\Contracts\IAvatarService;
use OCA\Mail\Contracts\IDkimService;
use OCA\Mail\Contracts\IDkimValidator;
use OCA\Mail\Contracts\IInternalAddressService;
use OCA\Mail\Contracts\IMailManager;
use OCA\Mail\Contracts\IMailSearch;
use OCA\Mail\Contracts\IMailTransmission;
Expand Down Expand Up @@ -63,6 +64,7 @@
use OCA\Mail\Service\AvatarService;
use OCA\Mail\Service\DkimService;
use OCA\Mail\Service\DkimValidator;
use OCA\Mail\Service\InternalAddressService;
use OCA\Mail\Service\MailManager;
use OCA\Mail\Service\MailTransmission;
use OCA\Mail\Service\Search\MailSearch;
Expand Down Expand Up @@ -127,6 +129,7 @@ public function register(IRegistrationContext $context): void {
$context->registerServiceAlias(IMailManager::class, MailManager::class);
$context->registerServiceAlias(IMailSearch::class, MailSearch::class);
$context->registerServiceAlias(IMailTransmission::class, MailTransmission::class);
$context->registerServiceAlias(IInternalAddressService::class, InternalAddressService::class);
$context->registerServiceAlias(ITrustedSenderService::class, TrustedSenderService::class);
$context->registerServiceAlias(IUserPreferences::class, UserPreferenceService::class);
$context->registerServiceAlias(IDkimService::class, DkimService::class);
Expand Down
7 changes: 7 additions & 0 deletions lib/Db/Actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ public function __construct() {
$this->addType('accountId', 'integer');
}

/**
* @return list<ActionStep>
*/
public function getActionSteps(): array {
return $this->actionSteps;
}

public function setActionSteps(array $actionSteps): void {
$this->actionSteps = $actionSteps;
}
Expand Down
23 changes: 23 additions & 0 deletions lib/Db/AliasMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use function array_map;
Expand Down Expand Up @@ -88,6 +89,28 @@ public function findAll(int $accountId, string $currentUserId): array {
return $this->findEntities($qb);
}

/**
* @throws Exception
*/
public function countByAccountId(int $accountId, string $currentUserId): int {
$qb = $this->db->getQueryBuilder();
$qb->select($qb->func()->count('*', 'count'))
->from($this->getTableName(), 'aliases')
->join('aliases', 'mail_accounts', 'accounts', $qb->expr()->eq('aliases.account_id', 'accounts.id'))
->where(
$qb->expr()->andX(
$qb->expr()->eq('accounts.user_id', $qb->createNamedParameter($currentUserId)),
$qb->expr()->eq('aliases.account_id', $qb->createNamedParameter($accountId))
)
);

$result = $qb->executeQuery();
$count = (int)$result->fetchOne();
$result->closeCursor();

return $count;
}

/**
* @param int $accountId the account whose aliases will be deleted
*
Expand Down
17 changes: 17 additions & 0 deletions lib/Db/MailboxMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,23 @@ public function findAll(Account $account): array {
return $this->findEntities($select);
}

/**
* @throws Exception
*/
public function countByAccount(Account $account): int {
$qb = $this->db->getQueryBuilder();

$select = $qb->select($qb->func()->count('*', 'count'))
->from($this->getTableName())
->where($qb->expr()->eq('account_id', $qb->createNamedParameter($account->getId())));

$result = $select->executeQuery();
$count = (int)$result->fetchOne();
$result->closeCursor();

return $count;
}

/**
* @return \Generator<int>
*/
Expand Down
10 changes: 8 additions & 2 deletions lib/Service/AccountService.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,19 @@ public function deleteByAccountId(int $accountId): void {

/**
* @param MailAccount $newAccount
* @param bool $scheduleBackgroundJobs Optional parameter to save the mail account
* without scheduling the corresponding background jobs. This can be useful if
* further database modifications must be done before running any background
* jobs. Defaults to `true`.
* @return MailAccount
*/
public function save(MailAccount $newAccount): MailAccount {
public function save(MailAccount $newAccount, bool $scheduleBackgroundJobs = true): MailAccount {
$newAccount = $this->mapper->save($newAccount);

// Insert background jobs for this account
$this->scheduleBackgroundJobs($newAccount->getId());
if ($scheduleBackgroundJobs) {
$this->scheduleBackgroundJobs($newAccount->getId());
}

// Invalidate cache to ensure created account is being included
// in subsequent `findByUserId` and `findByUserIdAndAddress` calls
Expand Down
23 changes: 16 additions & 7 deletions lib/Service/AliasesService.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use OCA\Mail\Db\MailAccountMapper;
use OCA\Mail\Exception\ClientException;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\DB\Exception;

class AliasesService {
public function __construct(
Expand All @@ -32,6 +33,13 @@ public function findAll(int $accountId, string $currentUserId): array {
return $this->aliasMapper->findAll($accountId, $currentUserId);
}

/**
* @throws Exception
*/
public function countByAccountId(int $accountId, string $currentUserId): int {
return $this->aliasMapper->countByAccountId($accountId, $currentUserId);
}

/**
* @param int $aliasId
* @param string $currentUserId
Expand All @@ -53,21 +61,22 @@ public function findByAliasAndUserId(string $aliasEmail, string $userId): Alias
}

/**
* @param string $userId
* @param int $accountId
* @param string $alias
* @param string $aliasName
*
* @return Alias
* @throws DoesNotExistException
*/
public function create(string $userId, int $accountId, string $alias, string $aliasName): Alias {
public function create(string $userId,
int $accountId,
string $alias,
?string $aliasName,
?string $signature = null,
?int $smimeCertificateId = null): Alias {
$this->mailAccountMapper->find($userId, $accountId);

$aliasEntity = new Alias();
$aliasEntity->setAccountId($accountId);
$aliasEntity->setAlias($alias);
$aliasEntity->setName($aliasName);
$aliasEntity->setSignature($signature);
$aliasEntity->setSmimeCertificateId($smimeCertificateId);

return $this->aliasMapper->insert($aliasEntity);
}
Expand Down
Loading
Loading