Compare commits

...

3 Commits

  1. 27
      lib/AppInfo/Application.php
  2. 21
      lib/Migration/DataJob.php
  3. 20
      lib/Migration/GroupJob.php
  4. 73
      lib/Migration/MigrationJob.php
  5. 20
      lib/Migration/QuotaJob.php
  6. 53
      lib/Migration/ShareJob.php
  7. 32
      lib/Migration/Utils.php
  8. 19
      scripts/shares_export.py

@ -23,8 +23,6 @@
namespace OCA\FirstRunMigrate\AppInfo;
use OCA\FirstRunMigrate\Migration\DataJob;
use OCA\FirstRunMigrate\Migration\GroupJob;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IBootContext;
@ -68,30 +66,7 @@ class Application extends App implements IBootstrap {
return;
}
$logger->debug("Checking $uid migrations");
if (DataJob::isMigration()){
$logger->debug("Scheduling $uid data migrations");
$jobList->add(DataJob::class, ['uid' => $uid]);
Utils::setMigrationStatus('data', 'scheduled', $user);
} else {
$logger->info("$uid don't have data migrations");
}
if (GroupJob::isMigration()) {
$logger->debug("Scheduling $uid groups migrations");
$jobList->add(GroupJob::class, ['uid' => $uid]);
Utils::setMigrationStatus('group', 'scheduled', $user);
} else {
$logger->info("$uid don't have groups migrations");
}
if (QuotaJob::isMigration()) {
$logger->debug("Scheduling $uid quota migrations");
$jobList->add(QuotaJob::class, ['uid' => $uid]);
Utils::setMigrationStatus('quota', 'scheduled', $user);
} else {
$logger->info("$uid don't have quota migrations");
}
QuotaJob::schredule($user, $logger, $jobList);
});
}
}

@ -21,7 +21,7 @@
namespace OCA\FirstRunMigrate\Migration;
use OCP\BackgroundJob\QueuedJob;
use OCA\FirstRunMigrate\Migration\MigrationJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OC_Util;
use OCA\FirstRunMigrate\Migration\Utils;
@ -32,7 +32,7 @@ use OCP\BackgroundJob\IJobList;
use OCP\Files\IRootFolder;
use Psr\Log\LoggerInterface;
class DataJob extends QueuedJob {
class DataJob extends MigrationJob {
protected LoggerInterface $logger;
protected IUserManager $userManager;
@ -41,6 +41,10 @@ class DataJob extends QueuedJob {
protected IRootFolder $rootFolder;
public static string $type = 'data';
public static $next = GroupJob::class;
/**
* BackgroundJob constructor.
*
@ -63,7 +67,7 @@ class DataJob extends QueuedJob {
$uid = $argument['uid'];
$user = $this->userManager->get($uid);
Utils::setMigrationStatus('data', 'started', $user);
self::setMigrationStatus('started', $user);
if ($migrate_dir = $this->getUserMigration($user)) {
// Trigger creation of user home and /files folder
@ -80,16 +84,9 @@ class DataJob extends QueuedJob {
$this->logger->info("{$this->getId()}: No dir to migrate");
}
// Schedule share migration only after migration user's datas
if (ShareJob::isMigration()) {
$this->logger->debug("Scheduling $uid share migrations");
$this->jobList->add(ShareJob::class, ['uid' => $uid]);
Utils::setMigrationStatus('share', 'scheduled', $user);
} else {
$this->logger->info("$uid don't have share migrations");
}
self::setMigrationStatus('finished', $user);
Utils::setMigrationStatus('data', 'finished', $user);
self::schredule_next($user, $this->logger, $this->jobList);
}
private static function getMigrationDir() : ?string {

@ -21,33 +21,41 @@
namespace OCA\FirstRunMigrate\Migration;
use OCP\BackgroundJob\QueuedJob;
use OCA\FirstRunMigrate\Migration\MigrationJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCA\FirstRunMigrate\Migration\Utils;
use OCP\BackgroundJob\IJobList;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IUserManager;
use OCP\IUser;
use Psr\Log\LoggerInterface;
class GroupJob extends QueuedJob {
class GroupJob extends MigrationJob {
protected LoggerInterface $logger;
protected IUserManager $userManager;
protected IGroupManager $groupManager;
protected IJobList $jobList;
public static string $type = 'group';
public static $next = ShareJob::class;
/**
* BackgroundJob constructor.
*
* @param INotificationManager $notificationManager
*/
public function __construct(ITimeFactory $timeFactory, IUserManager $userManager,
IGroupManager $groupManager, LoggerInterface $logger) {
IGroupManager $groupManager, LoggerInterface $logger, IJobList $jobList) {
parent::__construct($timeFactory);
$this->logger = $logger;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->jobList = $jobList;
}
/**
@ -57,7 +65,7 @@ class GroupJob extends QueuedJob {
$this->logger->debug("Starting groups migration job {$this->getId()} with args " . json_encode($argument));
$user = $this->userManager->get($argument['uid']);
Utils::setMigrationStatus('group', 'started', $user);
self::setMigrationStatus('started', $user);
if ($groups = $this->getUserMigration($user)) {
foreach ($groups as $group) {
@ -73,7 +81,9 @@ class GroupJob extends QueuedJob {
$this->logger->info("{$this->getId()}: No groups to migrate");
}
Utils::setMigrationStatus('group', 'finished', $user);
self::setMigrationStatus('finished', $user);
self::schredule_next($user, $this->logger, $this->jobList);
}
private static function getMigrationFile() : ?string {

@ -0,0 +1,73 @@
<?php
namespace OCA\FirstRunMigrate\Migration;
use OC\BackgroundJob\JobList;
use OCA\FirstRunMigrate\AppInfo\Application;
use OCA\FirstRunMigrate\Migration\Utils;
use OCP\BackgroundJob\QueuedJob;
use OCP\IConfig;
use OCP\IUser;
use Psr\Log\LoggerInterface;
use OCP\Notification\IManager as INotificationManager;
abstract class MigrationJob extends QueuedJob {
public static string $type;
public static $next = null;
public static abstract function isMigration() : bool;
public static function schredule(IUser $user, LoggerInterface $logger, JobList $jobList) {
$uid = $user->getUID();
if (static::isMigration()) {
$logger->debug("Scheduling $uid " . static::class);
$jobList->add(static::class, ['uid' => $uid]);
static::setMigrationStatus('scheduled', $user);
return true;
} else {
$logger->info("$uid don't have " . static::class);
return static::schredule_next($user, $logger, $jobList);
}
}
public static function schredule_next(IUser $user, LoggerInterface $logger, JobList $jobList) : bool {
if (static::$next) {
return static::$next::schredule($user, $logger, $jobList);
} else {
return false;
}
}
public static function setMigrationStatus(string $status, IUser $user, array $parameters = array()) {
/** @var INotificationManager */
$notificationManager = \OC::$server->get(INotificationManager::class);
/** @var IConfig */
$config = \OC::$server->get(IConfig::class);
$uid = $user->getUID();
$config->setUserValue($uid, Application::APP_ID, static::$type . '_status', $status);
$config->setUserValue($uid, Application::APP_ID, static::$type . '_' . $status . '_date',
(new \DateTime())->getTimestamp());
$notification = $notificationManager->createNotification();
$notification->setApp(Application::APP_ID)
->setUser($uid)
->setDateTime(new \DateTime())
->setObject(static::$type, $status)
->setSubject(static::$type . '_' .$status, $parameters);
$notificationManager->notify($notification);
}
public static function isUserMigrated(IUser $user) : bool {
// If user never logged into Nextcloud
if ($user->getLastLogin() == 0) {
return false;
}
$config = \OC::$server->get(IConfig::class);
// If user finish the share migration
return $config->getUserValue($user->getUID(), Application::APP_ID, static::$type . '_status', null) == 'finished';
}
}

@ -21,28 +21,36 @@
namespace OCA\FirstRunMigrate\Migration;
use OCP\BackgroundJob\QueuedJob;
use OCA\FirstRunMigrate\Migration\MigrationJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCA\FirstRunMigrate\Migration\Utils;
use OCP\BackgroundJob\IJobList;
use OCP\IConfig;
use OCP\IUserManager;
use OCP\IUser;
use Psr\Log\LoggerInterface;
class QuotaJob extends QueuedJob {
class QuotaJob extends MigrationJob {
protected LoggerInterface $logger;
protected IUserManager $userManager;
protected IJobList $jobList;
public static string $type = 'quota';
public static $next = DataJob::class;
/**
* BackgroundJob constructor.
*
* @param INotificationManager $notificationManager
*/
public function __construct(ITimeFactory $timeFactory, IUserManager $userManager, LoggerInterface $logger) {
public function __construct(ITimeFactory $timeFactory, IUserManager $userManager, LoggerInterface $logger, IJobList $jobList) {
parent::__construct($timeFactory);
$this->logger = $logger;
$this->userManager = $userManager;
$this->jobList = $jobList;
}
/**
@ -53,7 +61,7 @@ class QuotaJob extends QueuedJob {
$user = $this->userManager->get($argument['uid']);
$this->logger->debug("{$this->getId()}: Migrating quota");
Utils::setMigrationStatus('quota', 'started', $user);
self::setMigrationStatus('started', $user);
if ($quota = $this->getUserMigration($user)) {
$user->setQuota($quota);
@ -61,7 +69,9 @@ class QuotaJob extends QueuedJob {
$this->logger->info("{$this->getId()}: No quota to migrate");
}
Utils::setMigrationStatus('quota', 'finished', $user);
self::setMigrationStatus('finished', $user);
self::schredule_next($user, $this->logger, $this->jobList);
}
private static function getMigrationFile() : ?string {

@ -21,8 +21,8 @@
namespace OCA\FirstRunMigrate\Migration;
use DateTime;
use Exception;
use OCA\FirstRunMigrate\Migration\MigrationJob;
use Throwable;
use OC\Share20\DefaultShareProvider;
use OC\Share20\ShareAttributes;
@ -30,7 +30,6 @@ use OC\Share20\Share;
use OCP\IUser;
use OCP\IConfig;
use OCP\Share\IShare;
use OCP\BackgroundJob\QueuedJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCA\FirstRunMigrate\Migration\Utils;
use OCP\IUserManager;
@ -39,11 +38,12 @@ use OCP\Files\Node;
use OCP\Files\NotFoundException;
use Psr\Log\LoggerInterface;
use OCA\FirstRunMigrate\Exceptions\MissingShareUser;
use OCP\BackgroundJob\IJobList;
use OCP\IDBConnection;
use OCP\Share\Exceptions\AlreadySharedException;
use OCP\Share\Exceptions\ShareNotFound;
class ShareJob extends QueuedJob {
class ShareJob extends MigrationJob {
protected LoggerInterface $logger;
protected IUserManager $userManager;
@ -54,11 +54,15 @@ class ShareJob extends QueuedJob {
protected IDBConnection $dbConn;
public static string $type = 'share';
protected IJobList $jobList;
/**
* BackgroundJob constructor.
*/
public function __construct(ITimeFactory $timeFactory, IUserManager $userManager,
LoggerInterface $logger, IRootFolder $rootFolder, IDBConnection $connection) {
LoggerInterface $logger, IRootFolder $rootFolder, IDBConnection $connection, IJobList $jobList) {
parent::__construct($timeFactory);
$this->setAllowParallelRuns(false);
$this->logger = $logger;
@ -66,6 +70,7 @@ class ShareJob extends QueuedJob {
$this->shareProvider = \OC::$server->get(DefaultShareProvider::class);
$this->rootFolder = $rootFolder;
$this->dbConn = $connection;
$this->jobList = $jobList;
}
/**
@ -75,7 +80,7 @@ class ShareJob extends QueuedJob {
$this->logger->debug("Starting shares migration job {$this->getId()} with args " . json_encode($argument));
$user = $this->userManager->get($argument['uid']);
Utils::setMigrationStatus('share', 'started', $user);
self::setMigrationStatus('started', $user);
$shares = $this->getUserShares($user);
@ -122,7 +127,9 @@ class ShareJob extends QueuedJob {
}
$this->logger->debug("{$this->getId()}: $migrated shares migrated, $missingUser missing users, $missingFile missing files, $errors errors");
Utils::setMigrationStatus('share', 'finished', $user, [$migrated, $missingUser, $missingFile, $errors]);
self::setMigrationStatus('finished', $user, [$migrated, $missingUser, $missingFile, $errors]);
self::schredule_next($user, $this->logger, $this->jobList);
}
private function createShareFromData(array $share_data, IUser $user) : Share {
@ -271,15 +278,43 @@ class ShareJob extends QueuedJob {
private function getUserForShare(IUser $user, string $userType, array $share_data) : string {
$userShare = Utils::getUserByID($share_data[$userType]);
if (is_null($userShare) || ($userShare->getUID() !== $user->getUID() &&
!Utils::isUserMigrated($userShare, 'share'))) {
!self::isUserMigrated($userShare))) {
throw new MissingShareUser($userType, $share_data[$userType]);
}
return $userShare->getUID();
}
/**
* @throws NotFoundException
*/
private function getFileForShare(array $share_data) : Node {
$owner = Utils::getUserByID($share_data['owner']);
return $this->rootFolder->getUserFolder($owner->getUID())->get($share_data['path']);
if (str_starts_with($share_data['path'], 'files/')) {
$path = substr($share_data['path'], 6);
$owner = Utils::getUserByID($share_data['owner']);
return $this->rootFolder->getUserFolder($owner->getUID())->get($path);
} if (str_starts_with($share_data['path'], '__groupfolders/')) {
$path = explode('/', $share_data['path']);
$gf_id = $this->getGroupFolderByMountPoint($path[1]);
$path = implode('/', array_slice($path, 2));
return $this->rootFolder->get('__groupfolders/' . $gf_id . '/' . $path);
} else {
return $this->rootFolder->get($share_data['path']);
}
}
private function getGroupFolderByMountPoint(string $mountPoint) : int {
$query = $this->dbConn->getQueryBuilder();
$query->select('folder_id')
->from('group_folders', 'f')
->where($query->expr()->eq('mount_point', $query->createNamedParameter($mountPoint)));
$result = $query->executeQuery();
$row = $result->fetch();
$result->closeCursor();
return (int)$row['folder_id'];
}
private static function getMigrationFile() : ?string {

@ -11,26 +11,6 @@ use OCP\IUserManager;
use OCP\Notification\IManager as INotificationManager;
class Utils {
public static function setMigrationStatus(string $type, string $status, IUser $user, array $parameters = array()) {
/** @var INotificationManager */
$notificationManager = \OC::$server->get(INotificationManager::class);
/** @var IConfig */
$config = \OC::$server->get(IConfig::class);
$uid = $user->getUID();
$config->setUserValue($uid, Application::APP_ID, $type . '_status', $status);
$config->setUserValue($uid, Application::APP_ID, $type . '_' . $status . '_date',
(new \DateTime())->getTimestamp());
$notification = $notificationManager->createNotification();
$notification->setApp(Application::APP_ID)
->setUser($uid)
->setDateTime(new \DateTime())
->setObject($type, $status)
->setSubject($type . '_' .$status, $parameters);
$notificationManager->notify($notification);
}
public static function getUserId(IUser $user) : ?string {
/** @var IConfig */
$config = \OC::$server->get(IConfig::class);
@ -38,18 +18,6 @@ class Utils {
return $config->getUserValue($user->getUID(), 'settings', 'email', null);
}
public static function isUserMigrated(IUser $user, string $migration) : bool {
// If user never logged into Nextcloud
if ($user->getLastLogin() == 0) {
return false;
}
$config = \OC::$server->get(IConfig::class);
// If user finish the share migration
return $config->getUserValue($user->getUID(), Application::APP_ID, $migration . '_status', null) == 'finished';
}
/**
* @throws Exception
*/

@ -37,7 +37,15 @@ SELECT
'permissions', s.permissions,
'by', p_initiator.configvalue,
'owner', p_owner.configvalue,
'path', REGEXP_REPLACE(fc.path, '^files/', ''),
'path', CASE
WHEN fc.path LIKE '__groupfolders/%'
THEN (
SELECT CONCAT('__groupfolders/', CONCAT(ggf.mount_point, REGEXP_REPLACE(fc.path, '^__groupfolders/[0-9]+(/.*)', '\\\\1')))
FROM {db_prefix}group_folders ggf
WHERE ggf.folder_id = REGEXP_REPLACE(fc.path, '^__groupfolders/([0-9]+)/.*', '\\\\1')
)
ELSE fc.path
END,
'target', s.file_target,
'with', CASE
WHEN s.share_type = 0
@ -74,7 +82,12 @@ INNER JOIN {db_prefix}preferences p_initiator ON p_initiator.userid = s.uid_init
INNER JOIN {db_prefix}preferences p_owner ON p_owner.userid = s.uid_owner AND p_owner.appid = 'settings' AND p_owner.configkey = 'email'
INNER JOIN {db_prefix}filecache fc ON fc.fileid = file_source
LEFT JOIN {db_prefix}preferences p_with ON p_with.userid = s.share_with AND p_with.appid = 'settings' AND p_with.configkey = 'email'
WHERE s.share_type IN (0, 1);
WHERE s.share_type IN (0, 1)
AND (
fc.path LIKE 'files/%'
OR
fc.path LIKE '__groupfolders/%'
);
"""
args = ["mysql", "-Ns", "-h", db_host]
if db_port:
@ -93,7 +106,7 @@ WHERE s.share_type IN (0, 1);
'permissions', s.permissions,
'by', p_initiator.configvalue,
'owner', p_owner.configvalue,
'path', regexp_replace(fc.path, '^files/', ''),
'path', TO UPDATE,
'target', s.file_target,
'with', CASE
WHEN s.share_type = 0

Loading…
Cancel
Save