diff --git a/lib/Alchemy/Phrasea/Command/Developer/RegenerateSqliteDb.php b/lib/Alchemy/Phrasea/Command/Developer/RegenerateSqliteDb.php index fe39bd6a51..ef581d48d1 100644 --- a/lib/Alchemy/Phrasea/Command/Developer/RegenerateSqliteDb.php +++ b/lib/Alchemy/Phrasea/Command/Developer/RegenerateSqliteDb.php @@ -23,6 +23,7 @@ use Alchemy\Phrasea\Model\Entities\FeedItem; use Alchemy\Phrasea\Model\Entities\FeedPublisher; use Alchemy\Phrasea\Model\Entities\FeedToken; use Alchemy\Phrasea\Model\Entities\LazaretSession; +use Alchemy\Phrasea\Model\Entities\Registration; use Alchemy\Phrasea\Model\Entities\Session; use Alchemy\Phrasea\Model\Entities\Task; use Alchemy\Phrasea\Model\Entities\User; @@ -36,6 +37,7 @@ use Alchemy\Phrasea\Model\Entities\StoryWZ; use Alchemy\Phrasea\Core\Provider\ORMServiceProvider; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Tools\SchemaTool; +use Gedmo\Timestampable\TimestampableListener; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Filesystem\Filesystem; @@ -94,6 +96,9 @@ class RegenerateSqliteDb extends Command $this->insertOneAggregateToken($this->container['EM'], $DI); $this->insertLazaretFiles($this->container['EM'], $DI); $this->insertAuthFailures($this->container['EM'], $DI); + $this->insertOneRegistration($DI, $this->container['EM'], $DI['user_alt1'], $DI['coll'], 'now', 'registration_1'); + $this->insertOneRegistration($DI, $this->container['EM'], $DI['user_alt2'], $DI['coll'], '-3 months', 'registration_2'); + $this->insertOneRegistration($DI, $this->container['EM'], $DI['user_notAdmin'], $DI['coll'], 'now', 'registration_3'); $fixtures['user']['test_phpunit'] = $DI['user']->getId(); $fixtures['user']['test_phpunit_not_admin'] = $DI['user_notAdmin']->getId(); @@ -121,6 +126,10 @@ class RegenerateSqliteDb extends Command $fixtures['record']['record_6'] = $DI['record_6']->get_record_id(); $fixtures['record']['record_7'] = $DI['record_7']->get_record_id(); + $fixtures['registrations']['registration_1'] = $DI['registration_1']->getId(); + $fixtures['registrations']['registration_2'] = $DI['registration_2']->getId(); + $fixtures['registrations']['registration_3'] = $DI['registration_3']->getId(); + $fixtures['lazaret']['lazaret_1'] = $DI['lazaret_1']->getId(); $fixtures['user']['user_1'] = $DI['user_1']->getId(); @@ -637,4 +646,19 @@ class RegenerateSqliteDb extends Command $em->persist($entry); } + + private function insertOneRegistration(\Pimple $DI, EntityManager $em, User $user, \collection $collection, $when, $name) + { + $em->getEventManager()->removeEventSubscriber(new TimestampableListener()); + $registration = new Registration(); + $registration->setCollection($collection); + $registration->setUser($user); + $registration->setUpdated(new \DateTime($when)); + $registration->setCreated(new \DateTime($when)); + $em->persist($registration); + $em->flush(); + $em->getEventManager()->addEventSubscriber(new TimestampableListener()); + + $DI[$name] = $registration; + } } diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Users.php b/lib/Alchemy/Phrasea/Controller/Admin/Users.php index 51f6359efa..73be535de7 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/Users.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/Users.php @@ -353,58 +353,39 @@ class Users implements ControllerProviderInterface return $response; })->bind('admin_users_export_csv'); - $controllers->get('/demands/', function (Application $app) { - $lastMonth = time() - (3 * 4 * 7 * 24 * 60 * 60); - $sql = "DELETE FROM demand WHERE date_modif < :date"; - $stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql); - $stmt->execute([':date' => date('Y-m-d', $lastMonth)]); - $stmt->closeCursor(); + $controllers->get('/registrations/', function (Application $app) { + $app['manipulator.registration']->deleteOldRegistrations(); - $basList = array_keys($app['acl']->get($app['authentication']->getUser())->get_granted_base(['canadmin'])); $models = $app['manipulator.user']->getRepository()->findModelOf($app['authentication']->getUser()); - $currentUsr = null; - $table = ['users' => [], 'coll' => []]; - - foreach ($app['EM.native-query']->getUsersRegistrationDemand($basList) as $row) { - $user = $row[0]; - - if ($user->getId() !== $currentUsr) { - $currentUsr = $user->getId(); - $table['users'][$currentUsr] = [ - 'user' => $user, - 'date_demand' => $row['date_demand'], - ]; - } - - if (!isset($table['coll'][$user->getId()])) { - $table['coll'][$user->getId()] = []; - } - - if (!in_array($row['base_demand'], $table['coll'][$user->getId()])) { - $table['coll'][$user->getId()][] = $row['base_demand']; - } + $userRegistrations = []; + foreach ($app['manipulator.registration']->getRepository()->getUserRegistrations( + $app['authentication']->getUser(), + $app['acl']->get($app['authentication']->getUser())->get_granted_base(['canadmin']) + ) as $registration) { + $user = $registration->getUser(); + $userRegistrations[$user->getId()]['user'] = $user; + $userRegistrations[$user->getId()]['registrations'][$registration->getBaseid()] = $registration; } - $stmt->closeCursor(); - - return $app['twig']->render('admin/user/demand.html.twig', [ - 'table' => $table, + return $app['twig']->render('admin/user/registrations.html.twig', [ + 'user_registrations' => $userRegistrations, 'models' => $models, ]); - })->bind('users_display_demands'); - - $controllers->post('/demands/', function (Application $app, Request $request) { + })->bind('users_display_registrations'); + $controllers->post('/registrations/', function (Application $app, Request $request) { $templates = $deny = $accept = $options = []; foreach ($request->request->get('template', []) as $tmp) { - if (trim($tmp) != '') { - $tmp = explode('_', $tmp); + if ('' === trim($tmp)) { + continue; + } - if (count($tmp) == 2) { - $templates[$tmp[0]] = $tmp[1]; - } + $tmp = explode('_', $tmp); + + if (count($tmp) == 2) { + $templates[$tmp[0]] = $tmp[1]; } } @@ -438,127 +419,102 @@ class Users implements ControllerProviderInterface } if (count($templates) > 0 || count($deny) > 0 || count($accept) > 0) { - $done = []; - $cache_to_update = []; + $cacheToUpdate = $done = []; foreach ($templates as $usr => $template_id) { - $user = $app['manipulator.user']->getRepository()->find($usr); - $cache_to_update[$usr] = true; + if (null === $user = $app['manipulator.user']->getRepository()->find($usr)) { + $app->abort(400, srpintf("User with id % in provided in 'template' request variable could not be found", $usr)); + } + $cacheToUpdate[$usr] = $user; $user_template = $app['manipulator.user']->getRepository()->find($template_id); - $base_ids = array_keys($app['acl']->get($user_template)->get_granted_base()); + $collections = $app['acl']->get($user_template)->get_granted_base(); + $baseIds = array_keys($collections); - $app['acl']->get($user)->apply_model($user_template, $base_ids); + $app['acl']->get($user)->apply_model($user_template, $baseIds); - if (!isset($done[$usr])) { - $done[$usr] = []; + foreach ($collections as $collection) { + $done[$usr][$collection->get_base_id()] = true; } - foreach ($base_ids as $base_id) { - $done[$usr][$base_id] = true; - } - - $sql = " - DELETE FROM demand - WHERE usr_id = :usr_id - AND (base_id = " . implode(' OR base_id = ', $base_ids) . ")"; - - $stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql); - $stmt->execute([':usr_id' => $usr]); - $stmt->closeCursor(); + $app['manipulator.registration']->deleteUserRegistrations($user, $collections); } - $sql = " - UPDATE demand SET en_cours=0, refuser=1, date_modif=now() - WHERE usr_id = :usr_id - AND base_id = :base_id"; - - $stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql); - foreach ($deny as $usr => $bases) { - $cache_to_update[$usr] = true; - foreach ($bases as $bas) { - $stmt->execute([':usr_id' => $usr, ':base_id' => $bas]); - - if (!isset($done[$usr])) { - $done[$usr] = []; - } - - $done[$usr][$bas] = false; + if (null === $user = $app['manipulator.user']->getRepository()->find($usr)) { + $app->abort(400, srpintf("User with id % in provided in 'deny' request variable could not be found", $usr)); + } + $cacheToUpdate[$usr] = $user; + foreach ($app['manipulator.registration']->getRepository()->getUserRegistrations( + $user, + array_map(function ($baseId) use ($app) { + return \collection::get_from_base_id($app, $baseId); + }, $bases) + ) as $registration) { + $app['manipulator.registration']->rejectRegistration($registration); + $done[$usr][$registration->getBaseId()] = false; } } - $stmt->closeCursor(); - foreach ($accept as $usr => $bases) { - $user = $app['manipulator.user']->getRepository()->find($usr); - $cache_to_update[$usr] = true; - - foreach ($bases as $bas) { - $app['acl']->get($user)->give_access_to_sbas([\phrasea::sbasFromBas($app, $bas)]); - - $rights = [ - 'canputinalbum' => '1', - 'candwnldhd' => ($options[$usr][$bas]['HD'] ? '1' : '0'), - 'nowatermark' => ($options[$usr][$bas]['WM'] ? '0' : '1'), - 'candwnldpreview' => '1', - 'actif' => '1', - ]; - - $app['acl']->get($user)->give_access_to_base([$bas]); - $app['acl']->get($user)->update_rights_to_base($bas, $rights); - - if (!isset($done[$usr])) { - $done[$usr] = []; - } - - $done[$usr][$bas] = true; - - $sql = "DELETE FROM demand WHERE usr_id = :usr_id AND base_id = :base_id"; - $stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql); - $stmt->execute([':usr_id' => $usr, ':base_id' => $bas]); - $stmt->closeCursor(); + if (null === $user = $app['manipulator.user']->getRepository()->find($usr)) { + $app->abort(400, srpintf("User with id % in provided in 'accept' request variable could not be found", $usr)); + } + $cacheToUpdate[$usr] = $user; + foreach ($app['manipulator.registration']->getRepository()->getUserRegistrations( + $user, + array_map(function ($baseId) use ($app) { + return \collection::get_from_base_id($app, $baseId); + }, $bases) + ) as $registration) { + $done[$usr][$registration->getBaseId()] = true; + $app['manipulator.registration']->acceptRegistration( + $registration, + $options[$usr][$registration->getBaseId()]['HD'], + $options[$usr][$registration->getBaseId()]['WM'] + ); } } - foreach (array_keys($cache_to_update) as $usr_id) { - $user = $app['manipulator.user']->getRepository()->find($usr_id); + array_walk($cacheToUpdate, function (User $user) use ($app) { $app['acl']->get($user)->delete_data_from_cache(); - unset($user); - } + }); + unset ($cacheToUpdate); foreach ($done as $usr => $bases) { + $user = $app['manipulator.user']->getRepository()->find($usr); $acceptColl = $denyColl = []; - if (null !== $user = $app['manipulator.user']->getRepository()->find($usr)) { - if (\Swift_Validate::email($user->getEmail())) { - foreach ($bases as $bas => $isok) { - if ($isok) { - $acceptColl[] = \phrasea::bas_labels($bas, $app); - } else { - $denyColl[] = \phrasea::bas_labels($bas, $app); - } - } - if (0 !== count($acceptColl) || 0 !== count($denyColl)) { - $message = ''; - if (0 !== count($acceptColl)) { - $message .= "\n" . $app->trans('login::register:email: Vous avez ete accepte sur les collections suivantes : ') . implode(', ', $acceptColl). "\n"; - } - if (0 !== count($denyColl)) { - $message .= "\n" . $app->trans('login::register:email: Vous avez ete refuse sur les collections suivantes : ') . implode(', ', $denyColl) . "\n"; - } - $receiver = new Receiver(null, $user->getEmail()); - $mail = MailSuccessEmailUpdate::create($app, $receiver, null, $message); + foreach ($bases as $bas => $isok) { + $collection = \collection::get_from_base_id($app, $bas); - $app['notification.deliverer']->deliver($mail); - } + if ($isok) { + $acceptColl[] = $collection->get_label($app['locale']); + continue; } + + $denyColl[] = $collection->get_label($app['locale']); + } + + if (0 !== count($acceptColl) || 0 !== count($denyColl)) { + $message = ''; + if (0 !== count($acceptColl)) { + $message .= "\n" . $app->trans('login::register:email: Vous avez ete accepte sur les collections suivantes : ') . implode(', ', $acceptColl). "\n"; + } + if (0 !== count($denyColl)) { + $message .= "\n" . $app->trans('login::register:email: Vous avez ete refuse sur les collections suivantes : ') . implode(', ', $denyColl) . "\n"; + } + + $receiver = new Receiver(null, $user->getEmail()); + $mail = MailSuccessEmailUpdate::create($app, $receiver, null, $message); + + $app['notification.deliverer']->deliver($mail); } } } - return $app->redirectPath('users_display_demands', ['success' => 1]); - })->bind('users_submit_demands'); + return $app->redirectPath('users_display_registrations', ['success' => 1]); + })->bind('users_submit_registrations'); $controllers->get('/import/file/', function (Application $app, Request $request) { return $app['twig']->render('admin/user/import/file.html.twig'); diff --git a/lib/Alchemy/Phrasea/Controller/Root/Account.php b/lib/Alchemy/Phrasea/Controller/Root/Account.php index 6be7b357c8..9135575be3 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Account.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Account.php @@ -231,10 +231,8 @@ class Account implements ControllerProviderInterface */ public function accountAccess(Application $app, Request $request) { - require_once $app['root.path'] . '/lib/classes/deprecated/inscript.api.php'; - return $app['twig']->render('account/access.html.twig', [ - 'inscriptions' => giveMeBases($app, $app['authentication']->getUser()->getId()) + 'inscriptions' => $app['registration.manager']->getRegistrationSummary($app['authentication']->getUser()) ]); } @@ -328,17 +326,15 @@ class Account implements ControllerProviderInterface */ public function updateAccount(PhraseaApplication $app, Request $request) { - $demands = (array) $request->request->get('demand', []); - - if (0 !== count($demands)) { - foreach ($demands as $baseId) { - try { - $app['phraseanet.appbox-register']->add_request($app['authentication']->getUser(), \collection::get_from_base_id($app, $baseId)); - $app->addFlash('success', $app->trans('login::notification: Vos demandes ont ete prises en compte')); - } catch (\Exception $e) { - - } + $registrations = $request->request->get('registrations'); + if (false === is_array($registrations)) { + $app->abort(400, '"registrations" parameter must be an array of base ids.'); + } + if (0 !== count($registrations)) { + foreach ($registrations as $baseId) { + $app['manipulator.registration']->createRegistration($app['authentication']->getUser(), \collection::get_from_base_id($app, $baseId)); } + $app->addFlash('success', $app->trans('Your registration requests have been taken into account.')); } $accountFields = [ diff --git a/lib/Alchemy/Phrasea/Controller/Root/Login.php b/lib/Alchemy/Phrasea/Controller/Root/Login.php index d5c3feb814..9969c2ffc8 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Login.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Login.php @@ -38,6 +38,7 @@ use Alchemy\Phrasea\Form\Login\PhraseaForgotPasswordForm; use Alchemy\Phrasea\Form\Login\PhraseaRecoverPasswordForm; use Alchemy\Phrasea\Form\Login\PhraseaRegisterForm; use Doctrine\ORM\EntityManager; +use igorw; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Cookie; @@ -80,7 +81,7 @@ class Login implements ControllerProviderInterface 'recaptcha_display' => $app->isCaptchaRequired(), 'unlock_usr_id' => $app->getUnlockAccountData(), 'guest_allowed' => $app->isGuestAllowed(), - 'register_enable' => $app['registration.enabled'], + 'register_enable' => $app['registration.manager']->isRegistrationEnabled(), 'display_layout' => $app['conf']->get(['registry', 'general', 'home-presentation-mode']), 'authentication_providers' => $app['authentication.providers'], 'registration_fields' => $app['registration.fields'], @@ -266,7 +267,7 @@ class Login implements ControllerProviderInterface public function doRegistration(PhraseaApplication $app, Request $request) { - if (!$app['registration.enabled']) { + if (!$app['registration.manager']->isRegistrationEnabled()) { $app->abort(404, 'Registration is disabled'); } @@ -329,14 +330,12 @@ class Login implements ControllerProviderInterface throw new FormProcessingException($app->trans('Invalid captcha answer.')); } - require_once $app['root.path'] . '/lib/classes/deprecated/inscript.api.php'; - if ($app['conf']->get(['registry', 'registration', 'auto-select-collections'])) { $selected = null; } else { $selected = isset($data['collections']) ? $data['collections'] : null; } - $inscriptions = giveMeBases($app); + $inscriptions = $app['registration.manager']->getRegistrationSummary(); $inscOK = []; foreach ($app['phraseanet.appbox']->get_databoxes() as $databox) { @@ -345,15 +344,8 @@ class Login implements ControllerProviderInterface continue; } - $sbas_id = $databox->get_sbas_id(); - - if (isset($inscriptions[$sbas_id]) - && $inscriptions[$sbas_id]['inscript'] === true - && (isset($inscriptions[$sbas_id]['Colls'][$collection->get_coll_id()]) - || isset($inscriptions[$sbas_id]['CollsCGU'][$collection->get_coll_id()]))) { - $inscOK[$collection->get_base_id()] = true; - } else { - $inscOK[$collection->get_base_id()] = false; + if ($canRegister = igorw\get_in($inscriptions, [$databox->get_sbas_id(), 'config', 'collections', $collection->get_base_id(), 'can-register'])) { + $inscOK[$collection->get_base_id()] = $canRegister; } } } @@ -393,34 +385,25 @@ class Login implements ControllerProviderInterface $app['EM']->flush(); } - $demandOK = []; - + $registrationsOK = []; if ($app['conf']->get(['registry', 'registration', 'auto-register-enabled'])) { $template_user = $app['manipulator.user']->getRepository()->findByLogin(User::USER_AUTOREGISTER); - - $base_ids = []; - - foreach (array_keys($inscOK) as $base_id) { - $base_ids[] = $base_id; - } - - $app['acl']->get($user)->apply_model($template_user, $base_ids); + $app['acl']->get($user)->apply_model($template_user, array_keys($inscOK)); } $autoReg = $app['acl']->get($user)->get_granted_base(); - foreach ($inscOK as $base_id => $autorisation) { - if (false === $autorisation || $app['acl']->get($user)->has_access_to_base($base_id)) { + foreach ($inscOK as $baseId => $authorization) { + if (false === $authorization || $app['acl']->get($user)->has_access_to_base($baseId)) { continue; } - $collection = \collection::get_from_base_id($app, $base_id); - $app['phraseanet.appbox-register']->add_request($user, $collection); - $demandOK[$base_id] = true; + $app['manipulator.registration']->createRegistration($user, \collection::get_from_base_id($app, $baseId)); + $registrationsOK[$baseId] = true; } $params = [ - 'demand' => $demandOK, + 'registrations'=> $registrationsOK, 'autoregister' => $autoReg, 'usr_id' => $user->getId() ]; @@ -705,7 +688,7 @@ class Login implements ControllerProviderInterface */ public function displayRegisterForm(PhraseaApplication $app, Request $request) { - if (!$app['registration.enabled']) { + if (!$app['registration.manager']->isRegistrationEnabled()) { $app->abort(404, 'Registration is disabled'); } @@ -750,8 +733,6 @@ class Login implements ControllerProviderInterface */ public function login(PhraseaApplication $app, Request $request) { - require_once $app['root.path'] . '/lib/classes/deprecated/inscript.api.php'; - try { $app['phraseanet.appbox']->get_connection(); } catch (\Exception $e) { @@ -973,7 +954,7 @@ class Login implements ControllerProviderInterface } return $app->redirect($redirection); - } elseif ($app['registration.enabled']) { + } elseif ($app['registration.manager']->isRegistrationEnabled()) { return $app->redirectPath('login_register_classic', ['providerId' => $providerId]); } diff --git a/lib/Alchemy/Phrasea/Core/Configuration/RegistrationManager.php b/lib/Alchemy/Phrasea/Core/Configuration/RegistrationManager.php new file mode 100644 index 0000000000..992b090644 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Configuration/RegistrationManager.php @@ -0,0 +1,204 @@ +appbox = $appbox; + $this->repository = $repository; + $this->locale = $locale; + } + + /** + * Tells whether registration is enabled or not. + * + * @return boolean + */ + public function isRegistrationEnabled() + { + foreach ($this->appbox->get_databoxes() as $databox) { + foreach ($databox->get_collections() as $collection) { + if ($collection->isRegistrationEnabled()) { + return true; + } + } + } + + return false; + } + + /** + * Gets information about registration configuration and registration status if a user id is provided. + * + * @param null|user $user + * + * @return array + */ + public function getRegistrationSummary(User $user = null) + { + $data = $userData = []; + + // Gets user data + if (null !== $user) { + $userData = $this->repository->getRegistrationsSummaryForUser($user); + } + + foreach ($this->appbox->get_databoxes() as $databox) { + $data[$databox->get_sbas_id()] = [ + // Registrations on databox by type + 'registrations' => [ + 'by-type' => [ + 'inactive' => [], + 'accepted' => [], + 'in-time' => [], + 'out-dated' => [], + 'pending' => [], + 'rejected' => [], + ] + ], + // Registration configuration on databox and collections that belong to the databox + 'config' => [ + 'db-name' => $databox->get_dbname(), + 'cgu' => $databox->get_cgus(), + 'can-register' => $databox->isRegistrationEnabled(), + // Configuration on collection + 'collections' => [], + ] + ]; + + foreach ($databox->get_collections() as $collection) { + // Sets collection info + $data[$databox->get_sbas_id()]['config']['collections'][$collection->get_base_id()] = $this->getCollectionSummary($collection, $userData); + // Sets registration by type + if (null !== $registration = $this->getUserCollectionRegistration($collection, $userData)) { + $data[$databox->get_sbas_id()]['registrations']['by-type'][$registration['type']][] = $registration; + } + } + } + + return $data; + } + + /** + * Tells whether user has ever requested a registration on collection or not. + * + * @param \collection $collection + * @param $userData + * + * @return boolean + */ + private function userHasRequestedARegistrationOnCollection(\collection $collection, $userData) + { + if (null === $userRegistration = igorw\get_in($userData, [$collection->get_sbas_id(), $collection->get_base_id()])) { + return false; + } + + return !is_null($userRegistration['active']); + } + + /** + * Returns a user registration for given collection or null if no registration were requested. + * + * @param \collection $collection + * @param $userData + * + * @return null|array + */ + private function getUserCollectionRegistration(\collection $collection, $userData) + { + if (false === $this->userHasRequestedARegistrationOnCollection($collection, $userData)) { + return null; + } + + $userRegistration = igorw\get_in($userData, [$collection->get_sbas_id(), $collection->get_base_id()]); + + // sets collection name + $userRegistration['coll-name'] = $collection->get_label($this->locale); + // sets default type + $userRegistration['type'] = 'active'; + + // gets registration entity + $registration = $userRegistration['registration']; + + // set registration type & return user registration + $registrationStillExists = !is_null($registration); + $registrationNoMoreExists = !$registrationStillExists; + $isPending = $registrationStillExists && $registration->isPending() && !$registration->isRejected(); + $isRejected = $registrationStillExists && $registration->isRejected(); + $isDone = ($registrationNoMoreExists) || (!$isPending && !$isRejected); + $isActive = (Boolean) $userRegistration['active']; + $isTimeLimited = (Boolean) $userRegistration['time-limited']; + $isNotTimeLimited = !$isTimeLimited; + $isOnTime = (Boolean) $userRegistration['in-time']; + $isOutDated = !$isOnTime; + + if (!$isActive) { + $userRegistration['type'] = 'inactive'; + + return $userRegistration; + } + + if ($isDone) { + $userRegistration['type'] = 'accepted'; + + return $userRegistration; + } + + if ($isRejected) { + $userRegistration['type'] = 'rejected'; + + return $userRegistration; + } + + if ($isTimeLimited && $isOnTime && $isPending) { + $userRegistration['type'] = 'in-time'; + + return $userRegistration; + } + + if ($isTimeLimited && $isOutDated && $isPending) { + $userRegistration['type'] = 'out-time'; + + return $userRegistration; + } + + if ($isNotTimeLimited && $isPending) { + $userRegistration['type'] = 'pending'; + + return $userRegistration; + } + + return $userRegistration; + } + + private function getCollectionSummary(\collection $collection, $userData) + { + return [ + 'coll-name' => $collection->get_label($this->locale), + // gets collection registration or fallback to databox configuration + 'can-register' => $collection->isRegistrationEnabled(), + 'cgu' => $collection->getTermsOfUse(), + // boolean to tell whether user has already requested an access to the collection + 'registration' => $this->userHasRequestedARegistrationOnCollection($collection, $userData) + ]; + } +} diff --git a/lib/Alchemy/Phrasea/Core/Provider/AuthenticationManagerServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/AuthenticationManagerServiceProvider.php index 147451f2c3..ff01ca97e6 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/AuthenticationManagerServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/AuthenticationManagerServiceProvider.php @@ -68,7 +68,7 @@ class AuthenticationManagerServiceProvider implements ServiceProviderInterface } }, $authConf['auto-create']['templates'])); - $enabled = $app['conf']->get(['registry', 'registration', 'auto-register-enabled']) && $app['registration.enabled']; + $enabled = $app['conf']->get(['registry', 'registration', 'auto-register-enabled']) && $app['registration.manager']->isRegistrationEnabled(); return new AccountCreator($app['tokens'], $app['phraseanet.appbox'], $enabled, $templates); }); diff --git a/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php index 0bf66163d4..a2bb2c8996 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php @@ -12,6 +12,7 @@ namespace Alchemy\Phrasea\Core\Provider; use Alchemy\Phrasea\Model\Manipulator\ACLManipulator; +use Alchemy\Phrasea\Model\Manipulator\RegistrationManipulator; use Alchemy\Phrasea\Model\Manipulator\TaskManipulator; use Alchemy\Phrasea\Model\Manipulator\UserManipulator; use Alchemy\Phrasea\Model\Manager\UserManager; @@ -37,6 +38,10 @@ class ManipulatorServiceProvider implements ServiceProviderInterface $app['model.user-manager'] = $app->share(function ($app) { return new UserManager($app['EM'], $app['phraseanet.appbox']->get_connection()); }); + + $app['manipulator.registration'] = $app->share(function ($app) { + return new RegistrationManipulator($app, $app['EM'], $app['acl'], $app['phraseanet.appbox']); + }); } public function boot(SilexApplication $app) diff --git a/lib/Alchemy/Phrasea/Core/Provider/RegistrationServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/RegistrationServiceProvider.php index 62c1cf2fa2..f3c9b173a3 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/RegistrationServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/RegistrationServiceProvider.php @@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\Core\Provider; use Alchemy\Phrasea\Form\Constraint\NewLogin; use Alchemy\Phrasea\Model\Entities\User; +use Alchemy\Phrasea\Core\Configuration\RegistrationManager; use Silex\Application; use Silex\ServiceProviderInterface; use Symfony\Component\Validator\Constraints as Assert; @@ -25,20 +26,8 @@ class RegistrationServiceProvider implements ServiceProviderInterface return $app['conf']->get('registration-fields', []); }); - $app['registration.enabled'] = $app->share(function (Application $app) { - require_once __DIR__ . '/../../../../classes/deprecated/inscript.api.php'; - - $bases = giveMeBases($app); - - if ($bases) { - foreach ($bases as $base) { - if ($base['inscript']) { - return true; - } - } - } - - return false; + $app['registration.manager'] = $app->share(function (Application $app) { + return new RegistrationManager($app['phraseanet.appbox'], $app['manipulator.registration']->getRepository(), $app['locale']); }); $app['registration.optional-fields'] = $app->share(function (Application $app) { diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php index 0116a51337..790a226bc0 100644 --- a/lib/Alchemy/Phrasea/Core/Version.php +++ b/lib/Alchemy/Phrasea/Core/Version.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\Core; class Version { - protected static $number = '3.9.0-alpha.12'; + protected static $number = '3.9.0-alpha.13'; protected static $name = 'Epanterias'; public static function getNumber() diff --git a/lib/Alchemy/Phrasea/Form/Login/PhraseaRegisterForm.php b/lib/Alchemy/Phrasea/Form/Login/PhraseaRegisterForm.php index 0792dce296..837ad767a8 100644 --- a/lib/Alchemy/Phrasea/Form/Login/PhraseaRegisterForm.php +++ b/lib/Alchemy/Phrasea/Form/Login/PhraseaRegisterForm.php @@ -72,39 +72,16 @@ class PhraseaRegisterForm extends AbstractType $builder->add('provider-id', 'hidden'); - require_once $this->app['root.path'] . '/lib/classes/deprecated/inscript.api.php'; - $choices = []; - $baseIds = []; + $choices = $baseIds = []; - foreach (\giveMeBases($this->app) as $sbas_id => $baseInsc) { - if (($baseInsc['CollsCGU'] || $baseInsc['Colls']) && $baseInsc['inscript']) { - if ($baseInsc['Colls']) { - foreach ($baseInsc['Colls'] as $collId => $collName) { - $baseId = \phrasea::baseFromColl($sbas_id, $collId, $this->app); - $sbasName= \phrasea::sbas_names($sbas_id, $this->app); - - if (!isset($choices[$sbasName])) { - $choices[$sbasName] = []; - } - - $choices[$sbasName][$baseId] = \phrasea::bas_labels($baseId, $this->app); - $baseIds[] = $baseId; - } - } - - if ($baseInsc['CollsCGU']) { - foreach ($baseInsc['CollsCGU'] as $collId => $collName) { - $baseId = \phrasea::baseFromColl($sbas_id, $collId, $this->app); - $sbasName= \phrasea::sbas_names($sbas_id, $this->app); - - if (!isset($choices[$sbasName])) { - $choices[$sbasName] = []; - } - - $choices[$sbasName][$baseId] = \phrasea::bas_labels($baseId, $this->app); - $baseIds[] = $baseId; - } + foreach ($this->app['registration.manager']->getRegistrationSummary() as $baseInfo) { + $dbName = $baseInfo['config']['db-name']; + foreach ($baseInfo['config']['collections'] as $baseId => $collInfo) { + if (false === $collInfo['can-register']) { + continue; } + $choices[$dbName][$baseId] = $collInfo['coll-name']; + $baseIds[] = $baseId; } } diff --git a/lib/Alchemy/Phrasea/Model/Entities/Registration.php b/lib/Alchemy/Phrasea/Model/Entities/Registration.php new file mode 100644 index 0000000000..4223753642 --- /dev/null +++ b/lib/Alchemy/Phrasea/Model/Entities/Registration.php @@ -0,0 +1,209 @@ +id; + } + + /** + * @param mixed $pending + * + * @return Registration + */ + public function setPending($pending) + { + $this->pending = (Boolean) $pending; + + return $this; + } + + /** + * @return mixed + */ + public function isPending() + { + return $this->pending; + } + + /** + * @param mixed $rejected + * + * @return Registration + */ + public function setRejected($rejected) + { + $this->rejected = (Boolean) $rejected; + + return $this; + } + + /** + * @return mixed + */ + public function isRejected() + { + return $this->rejected; + } + + /** + * @return Registration + */ + public function setUser(User $user) + { + $this->user = $user; + + return $this; + } + + /** + * @return integer + */ + public function getUser() + { + return $this->user; + } + + /** + * @param Application $app + * + * @return \collection + */ + public function getCollection(Application $app) + { + return \collection::get_from_base_id($app, $this->baseId); + } + + /** + * @param \collection $collection + * + * @return $this + */ + public function setCollection(\collection $collection) + { + $this->baseId = $collection->get_base_id(); + + return $this; + } + + /** + * @param mixed $baseId + * + * @return Registration + */ + public function setBaseId($baseId) + { + $this->baseId = $baseId; + + return $this; + } + + /** + * @return mixed + */ + public function getBaseId() + { + return $this->baseId; + } + + /** + * @return \DateTime + */ + public function getCreated() + { + return $this->created; + } + + /** + * @return \DateTime + */ + public function getUpdated() + { + return $this->updated; + } + + /** + * @param \Datetime $created + */ + public function setCreated(\Datetime $created) + { + $this->created = $created; + } + + /** + * @param \Datetime $updated + */ + public function setUpdated(\Datetime $updated) + { + $this->updated = $updated; + } +} diff --git a/lib/Alchemy/Phrasea/Model/Manipulator/RegistrationManipulator.php b/lib/Alchemy/Phrasea/Model/Manipulator/RegistrationManipulator.php new file mode 100644 index 0000000000..58e6fa4f8c --- /dev/null +++ b/lib/Alchemy/Phrasea/Model/Manipulator/RegistrationManipulator.php @@ -0,0 +1,155 @@ +app = $app; + $this->em = $em; + $this->appbox = $appbox; + $this->aclProvider = $aclProvider; + $this->repository = $this->em->getRepository('Phraseanet:Registration'); + } + + /** + * Creates a new registration. + * + * @param User $user + * @param \collection $collection + * + * @return Registration + */ + public function createRegistration(User $user, \collection $collection) + { + $registration = new Registration(); + $registration->setUser($user); + $registration->setCollection($collection); + $this->em->persist($registration); + $this->em->flush(); + + return $registration; + } + + /** + * Rejects a registration. + * + * @param Registration $registration + */ + public function rejectRegistration(Registration $registration) + { + $registration->setPending(false); + $registration->setRejected(true); + $this->em->persist($registration); + $this->em->flush(); + } + + /** + * Accepts a registration. + * + * @param Registration $registration + * @param bool $grantHd + * @param bool $grantWatermark + */ + public function acceptRegistration(Registration $registration, $grantHd = false, $grantWatermark = false) + { + $user = $registration->getUser(); + $collection = $registration->getCollection($this->app); + + $this->aclProvider->get($user)->give_access_to_sbas([$collection->get_sbas_id()]); + $this->aclProvider->get($user)->give_access_to_base([$collection->get_base_id()]); + $this->aclProvider->get($user)->update_rights_to_base($collection->get_base_id(), [ + 'canputinalbum' => '1', + 'candwnldhd' => (string) (int) $grantHd, + 'nowatermark' => (string) (int) $grantWatermark, + 'candwnldpreview' => '1', + 'actif' => '1', + ]); + $this->em->remove($registration); + $this->em->flush(); + } + + /** + * Gets Registration Repository. + * + * @return RegistrationRepository + */ + public function getRepository() + { + return $this->repository; + } + + /** + * Deletes registration for given user. + * + * @param User $user + * @param \collection[] $collections + * + * @return mixed + */ + public function deleteUserRegistrations(User $user, array $collections) + { + $qb = $this->getRepository()->createQueryBuilder('d'); + $qb->delete('Phraseanet:Registration', 'd'); + $qb->where($qb->expr()->eq('d.user', ':user')); + $qb->setParameter(':user', $user->getId()); + + if (count($collections) > 0) { + $qb->andWhere('d.baseId IN (:bases)'); + $qb->setParameter(':bases', array_map(function ($collection) { + return $collection->get_base_id(); + }, $collections)); + } + + return $qb->getQuery()->execute(); + } + + /** + * Deletes old registrations. + */ + public function deleteOldRegistrations() + { + $qb = $this->getRepository()->createQueryBuilder('d'); + $qb->delete('Phraseanet:Registration', 'd'); + $qb->where($qb->expr()->lt('d.created', ':date')); + $qb->setParameter(':date', new \DateTime('-1 month')); + $qb->getQuery()->execute(); + } + + /** + * Deletes registrations on given collection. + * + * @param $baseId + */ + public function deleteRegistrationsOnCollection(\collection $collection) + { + $qb = $this->getRepository()->createQueryBuilder('d'); + $qb->delete('Phraseanet:Registration', 'd'); + $qb->where($qb->expr()->eq('d.baseId', ':base')); + $qb->setParameter(':base', $collection->get_base_id()); + $qb->getQuery()->execute(); + } +} diff --git a/lib/Alchemy/Phrasea/Model/NativeQueryProvider.php b/lib/Alchemy/Phrasea/Model/NativeQueryProvider.php index 8ea422c27d..83adb29da3 100644 --- a/lib/Alchemy/Phrasea/Model/NativeQueryProvider.php +++ b/lib/Alchemy/Phrasea/Model/NativeQueryProvider.php @@ -82,6 +82,6 @@ class NativeQueryProvider AND u.deleted="0"', $rsm ); - return $query->getResults(); + return $query->getResult(); } } diff --git a/lib/Alchemy/Phrasea/Model/Repositories/RegistrationRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/RegistrationRepository.php new file mode 100644 index 0000000000..4573c623ab --- /dev/null +++ b/lib/Alchemy/Phrasea/Model/Repositories/RegistrationRepository.php @@ -0,0 +1,101 @@ +createQueryBuilder('d'); + $qb->where($qb->expr()->eq('d.user', ':user')); + $qb->setParameter(':user', $user->getId()); + + if (count($collections) > 0) { + $qb->andWhere('d.baseId IN (:bases)'); + $qb->setParameter(':bases', array_map(function ($collection) { + return $collection->get_base_id(); + }, $collections)); + } + + $qb->orderBy('d.created', 'DESC'); + + return $qb->getQuery()->getResult(); + } + + /** + * Gets registration registrations for a user. + * + * @param User $user + * + * @return array + */ + public function getRegistrationsSummaryForUser(User $user) + { + $data = []; + $rsm = $this->createResultSetMappingBuilder('d'); + $rsm->addScalarResult('sbas_id','sbas_id'); + $rsm->addScalarResult('bas_id','bas_id'); + $rsm->addScalarResult('dbname','dbname'); + $rsm->addScalarResult('time_limited', 'time_limited'); + $rsm->addScalarResult('limited_from', 'limited_from'); + $rsm->addScalarResult('limited_to', 'limited_to'); + $rsm->addScalarResult('actif', 'actif'); + + $sql = " + SELECT dbname, sbas.sbas_id, time_limited, + UNIX_TIMESTAMP( limited_from ) AS limited_from, + UNIX_TIMESTAMP( limited_to ) AS limited_to, + bas.server_coll_id, usr.usr_id, basusr.actif, + bas.base_id AS bas_id , " . $rsm->generateSelectClause(['d' => 'd',]) . " + FROM (usr, bas, sbas) + LEFT JOIN basusr ON ( usr.usr_id = basusr.usr_id AND bas.base_id = basusr.base_id ) + LEFT JOIN Registration d ON ( d.user_id = usr.usr_id AND bas.base_id = d.base_id ) + WHERE bas.active = 1 AND bas.sbas_id = sbas.sbas_id + AND usr.usr_id = ? + AND model_of = 0"; + + $query = $this->_em->createNativeQuery($sql, $rsm); + $query->setParameter(1, $user->getId()); + + foreach ($query->getResult() as $row) { + $registrationEntity = $row[0]; + + $data[$row['sbas_id']][$row['bas_id']] = [ + 'base-id' => $row['bas_id'], + 'db-name' => $row['dbname'], + 'active' => (Boolean) $row['actif'], + 'time-limited' => (Boolean) $row['time_limited'], + 'in-time' => $row['time_limited'] && ! ($row['limited_from'] >= time() && $row['limited_to'] <= time()), + 'registration' => $registrationEntity + ]; + } + + return $data; + } +} diff --git a/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/RegistrationMigration.php b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/RegistrationMigration.php new file mode 100644 index 0000000000..b1564c84e6 --- /dev/null +++ b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/RegistrationMigration.php @@ -0,0 +1,27 @@ +addSql("CREATE TABLE Registration (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, base_id INT NOT NULL, pending TINYINT(1) NOT NULL, rejected TINYINT(1) NOT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, INDEX IDX_7A997C5FA76ED395 (user_id), UNIQUE INDEX unique_registration (user_id, base_id, pending), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"); + } + + public function doDownSql(Schema $schema) + { + $this->addSql("DROP TABLE Registration"); + } +} diff --git a/lib/classes/appbox/register.php b/lib/classes/appbox/register.php deleted file mode 100644 index 89442dc565..0000000000 --- a/lib/classes/appbox/register.php +++ /dev/null @@ -1,95 +0,0 @@ -appbox = $appbox; - - return $this; - } - - /** - * Add a registration request for a user on a collection - * - * @param User $user - * @param collection $collection - * @return appbox_register - */ - public function add_request(User $user, collection $collection) - { - $sql = "INSERT INTO demand (date_modif, usr_id, base_id, en_cours, refuser) - VALUES (now(), :usr_id , :base_id, 1, 0)"; - $stmt = $this->appbox->get_connection()->prepare($sql); - $stmt->execute([':usr_id' => $user->getId(), ':base_id' => $collection->get_base_id()]); - $stmt->closeCursor(); - - return $this; - } - - /** - * Return an array of collection objects where provided - * user is waiting for approbation - * - * @param Application $app - * @param User $user - * - * @return array - */ - public function get_collection_awaiting_for_user(Application $app, User $user) - { - $sql = 'SELECT base_id FROM demand WHERE usr_id = :usr_id AND en_cours="1" '; - $stmt = $this->appbox->get_connection()->prepare($sql); - $stmt->execute([':usr_id' => $user->getId()]); - $rs = $stmt->fetchAll(PDO::FETCH_ASSOC); - $stmt->closeCursor(); - $ret = []; - foreach ($rs as $row) { - $ret[] = collection::get_from_base_id($app, $row['base_id']); - } - - return $ret; - } - - /** - * Remove all registration older than a month - * - * @param appbox $appbox - * @return appbox_register - */ - public static function clean_old_requests(appbox $appbox) - { - $lastMonth = new DateTime('-1 month'); - $sql = "delete from demand where date_modif < :lastMonth"; - $stmt = $appbox->get_connection()->prepare($sql); - $stmt->execute([':lastMonth' => $lastMonth->format(DATE_ISO8601)]); - $stmt->closeCursor(); - - return; - } -} diff --git a/lib/classes/collection.php b/lib/classes/collection.php index 33b594ed41..14e389afbc 100644 --- a/lib/classes/collection.php +++ b/lib/classes/collection.php @@ -406,10 +406,7 @@ class collection implements cache_cacheableInterface $stmt->execute([':base_id' => $this->get_base_id()]); $stmt->closeCursor(); - $sql = "DELETE FROM demand WHERE base_id = :base_id"; - $stmt = $appbox->get_connection()->prepare($sql); - $stmt->execute([':base_id' => $this->get_base_id()]); - $stmt->closeCursor(); + $this->app['manipulator.registration']->deleteRegistrationsOnCollection($this); $this->get_databox()->delete_data_from_cache(databox::CACHE_COLLECTIONS); @@ -543,10 +540,7 @@ class collection implements cache_cacheableInterface $stmt->execute($params); $stmt->closeCursor(); - $sql = "DELETE FROM demand WHERE base_id = :base_id"; - $stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql); - $stmt->execute($params); - $stmt->closeCursor(); + $this->app['manipulator.registration']->deleteRegistrationsOnCollection($this); phrasea::reset_baseDatas($app['phraseanet.appbox']); @@ -752,4 +746,48 @@ class collection implements cache_cacheableInterface { self::$_collections = []; } + + /** + * Tells whether registration is activated for provided collection or not. + * + * @return boolean + */ + public function isRegistrationEnabled() + { + if (false === $xml = simplexml_load_string($this->get_prefs())) { + return false; + } + + $element = $xml->xpath('/baseprefs/caninscript'); + + if (count($element) === 0) { + return $this->databox->isRegistrationEnabled(); + } + + foreach ($element as $caninscript) { + if (false !== (Boolean) (string) $caninscript) { + return true; + } + } + + return false; + } + + /** + * Gets terms of use. + * + * @param \collection $collection + * + * @return null|string + */ + public function getTermsOfUse() + { + if (false === $xml = simplexml_load_string($this->get_prefs())) { + return; + } + + foreach ($xml->xpath('/baseprefs/cgu') as $sbpcgu) { + return $sbpcgu->saveXML(); + } + } } diff --git a/lib/classes/databox.php b/lib/classes/databox.php index 0ac8b268d9..9df97cbab8 100644 --- a/lib/classes/databox.php +++ b/lib/classes/databox.php @@ -1482,4 +1482,22 @@ class databox extends base { self::$_xpath_thesaurus = self::$_dom_thesaurus = self::$_thesaurus = self::$_sxml_thesaurus = []; } + + /** + * Tells whether the registration is enable or not. + * + * @return boolean + */ + public function isRegistrationEnabled() + { + if (false !== $xml = $this->get_sxml_structure()) { + foreach ($xml->xpath('/record/caninscript') as $canRegister) { + if (false !== (Boolean) (string) $canRegister) { + return true; + } + } + } + + return false; + } } diff --git a/lib/classes/databox/cgu.php b/lib/classes/databox/cgu.php index 4a54b836af..6bf4234c5d 100644 --- a/lib/classes/databox/cgu.php +++ b/lib/classes/databox/cgu.php @@ -29,7 +29,7 @@ class databox_cgu if (trim($term['terms']) == '') { continue; } - $out .= '