Browse Source

merge branche

raphael 3 years ago
parent
commit
88b43215ed

+ 1
- 1
.env.test View File

@@ -7,4 +7,4 @@ KERNEL_CLASS='Logipro\Phoenix\Kernel\AppKernel'
7 7
 APP_SECRET='s$cretf0rt3st'
8 8
 SYMFONY_DEPRECATIONS_HELPER=999999
9 9
 
10
-DATABASE_TEST_URL="sqlite:///%kernel.project_dir%/var/app.db"
10
+DATABASE_TEST_URL="sqlite:///%kernel.project_dir%/var/app.db"

+ 0
- 1
config/services.yaml View File

@@ -106,7 +106,6 @@ services:
106 106
   # Service de gestion des uploads
107 107
   Logipro\Phoenix\Service\FileUploader:
108 108
     public: true
109
-    arguments: ['@service_container']
110 109
 
111 110
   Logipro\Phoenix\Component\VichUploader\UniqidNamer:
112 111
     public: true

+ 18
- 24
src/Phoenix/Component/PhxCrypt.php View File

@@ -1,4 +1,5 @@
1 1
 <?php
2
+
2 3
 namespace Logipro\Phoenix\Component;
3 4
 
4 5
 class PhxCrypt
@@ -6,63 +7,56 @@ class PhxCrypt
6 7
     private static $cryptPassword = "__Pas.s:Word_!/_";
7 8
     private static $cryptIV = '1234567812345678';
8 9
 
9
-    protected static function serialize($data) : string
10
+    protected static function serialize(array $data): string
10 11
     {
11 12
         return serialize($data);
12 13
     }
13 14
 
14
-    public static function crypt($data)
15
+    public static function crypt($data, $withDate = true) : string
15 16
     {
16
-        if (is_string($data))
17
-        {
17
+        if (is_string($data)) {
18 18
             $data = array('_s_' => $data);
19 19
         }
20
-        
20
+
21 21
         $data['_d_'] = time();
22 22
 
23 23
         $data = self::serialize($data);
24
-		$openSslCrypt = openssl_encrypt($data, 'aes128',self::$cryptPassword,0,self::$cryptIV);
24
+        $openSslCrypt = openssl_encrypt($data, 'aes128', self::$cryptPassword, 0, self::$cryptIV);
25 25
         return rtrim(strtr(($openSslCrypt), '+/', '-_'), '=');
26 26
     }
27 27
 
28
-    public static function decrypt($data,$duration = null)
28
+    public static function decrypt($data, $duration = null)
29 29
     {
30 30
         $padding = str_repeat('=', 4 - (strlen($data) % 4));
31
-        
31
+
32 32
         // décodage base64
33
-        $decoded = (strtr($data.$padding, '-_', '+/'));
34
-        if (!$decoded)
35
-        {
33
+        $decoded = (strtr($data . $padding, '-_', '+/'));
34
+        if (!$decoded) {
36 35
             return null;
37 36
         }
38 37
 
39 38
         // décodage ssl
40
-        $decoded = openssl_decrypt ($decoded, 'aes128',self::$cryptPassword,0,self::$cryptIV);
41
-        if (!$decoded)
42
-        {
39
+        $decoded = openssl_decrypt($decoded, 'aes128', self::$cryptPassword, 0, self::$cryptIV);
40
+        if (!$decoded) {
43 41
             return null;
44 42
         }
45 43
 
46 44
         // désérialization
47 45
         $decoded = unserialize($decoded);
48
-        if (!is_array($decoded))
49
-        {
46
+        if (!is_array($decoded)) {
50 47
             return null;
51 48
         }
52
-        
53
-        if ($duration)
54
-        {
55
-            if ($decoded['_d_'] + $duration <= time())
56
-            {
49
+
50
+        if ($duration) {
51
+            if ($decoded['_d_'] + $duration <= time()) {
57 52
                 return null;
58 53
             }
59 54
         }
60 55
 
61
-        if (isset($decoded['_s_']))
62
-        {
56
+        if (isset($decoded['_s_'])) {
63 57
             return $decoded['_s_'];
64 58
         }
65 59
         unset($decoded['_d_']);
66 60
         return $decoded;
67 61
     }
68
-}
62
+}

+ 75
- 100
src/Phoenix/Controller/Catalog/LoginController.php View File

@@ -1,4 +1,5 @@
1 1
 <?php
2
+
2 3
 namespace Logipro\Phoenix\Controller\Catalog;
3 4
 
4 5
 use Logipro\Phoenix\Events;
@@ -32,11 +33,11 @@ use Logipro\Phoenix\Service\LoginService;
32 33
 class LoginController extends CatalogController
33 34
 {
34 35
 	protected function phxRenderView(string $view, array $parameters = array()): string
35
-    {
36
-		$view = str_replace($this->viewPath,'',$view);
36
+	{
37
+		$view = str_replace($this->viewPath, '', $view);
37 38
 		$view = $this->viewPath . $view;
38 39
 
39
-		return parent::phxRenderView($view,$parameters);
40
+		return parent::phxRenderView($view, $parameters);
40 41
 	}
41 42
 
42 43
 	private function createLoginForm()
@@ -63,16 +64,16 @@ class LoginController extends CatalogController
63 64
 	private function createRecoverForm()
64 65
 	{
65 66
 		$formBuilder = $this->createFormBuilder();
66
-		
67
+
67 68
 		$formBuilder->add('username', TextType::class, array(
68
-			'required' => true, 
69
+			'required' => true,
69 70
 			'label_format' => 'catalog_recover_%id%'
70 71
 		));
71 72
 
72 73
 		return $formBuilder->getForm();
73 74
 	}
74 75
 
75
-	public function manageLogin(Request $request, UserPasswordEncoderInterface $passwordEncoder,SessionInterface $session)
76
+	public function manageLogin(Request $request, UserPasswordEncoderInterface $passwordEncoder, SessionInterface $session)
76 77
 	{
77 78
 		$continue = $request->request->get('continue');
78 79
 
@@ -80,7 +81,7 @@ class LoginController extends CatalogController
80 81
 
81 82
 		$formLogin = $this->createLoginForm();
82 83
 
83
-		$formSubscribe = $this->createForm(CatalogSubscribeType::class,$person); 
84
+		$formSubscribe = $this->createForm(CatalogSubscribeType::class, $person);
84 85
 
85 86
 		$formRecover = $this->createRecoverForm();
86 87
 
@@ -95,14 +96,13 @@ class LoginController extends CatalogController
95 96
 			"form_recover" => $formRecover->createView()
96 97
 		);
97 98
 
98
-		$html = $this->phxRenderView('layouts/partials/header/modal-login-content.html.twig',$renderVars);
99
+		$html = $this->phxRenderView('layouts/partials/header/modal-login-content.html.twig', $renderVars);
99 100
 
100 101
 		$response['html'] = $html;
101 102
 		$response['nav'] = $this->phxRenderView('layouts/partials/header/header.html.twig');
102
-		
103
+
103 104
 		$response['success'] = false;
104
-		if ($this->getUser())
105
-		{
105
+		if ($this->getUser()) {
106 106
 			$response['success'] = true;
107 107
 		}
108 108
 		$response['continue'] = $continue;
@@ -110,63 +110,56 @@ class LoginController extends CatalogController
110 110
 		return $this->json($response);
111 111
 	}
112 112
 
113
-	public function validateSubscribe(Request $request,LearnerAccountGenerator $accountGenerator,ValidatorInterface $validator, EventDispatcherInterface $eventDispatcher,LoginService $loginService)
113
+	public function validateSubscribe(Request $request, LearnerAccountGenerator $accountGenerator, ValidatorInterface $validator, EventDispatcherInterface $eventDispatcher, LoginService $loginService)
114 114
 	{
115 115
 		// si l'utilisateur est connecté => erreur
116
-		if ($this->getLearnerSpaceUser())
117
-		{
116
+		if ($this->getLearnerSpaceUser()) {
118 117
 			throw $this->createAccessDeniedException($this->translate('error_403_message'));
119 118
 		}
120
-		
119
+
121 120
 		$person = new Person();
122 121
 
123 122
 		$service = $this->get('phxSettings');
124 123
 		$person->setSociety($service->getDefaultSociety());
125 124
 
126
-		$formSubscribe = $this->createForm(CatalogSubscribeType::class,$person); 
125
+		$formSubscribe = $this->createForm(CatalogSubscribeType::class, $person);
127 126
 
128 127
 		$succes = $error = null;
129 128
 
130 129
 		$response = array();
131
-		try
132
-		{
133
-			if ($request->isMethod('POST'))
134
-			{
130
+		try {
131
+			if ($request->isMethod('POST')) {
135 132
 				$formSubscribe->handleRequest($request);
136 133
 
137 134
 				// valide l'objet distant et remonte l'erreur sur le formulaire d'origine
138 135
 				$result = $validator->validate($person->getLearnerAccount());
139 136
 				$learnerAccountField = $formSubscribe->get('learnerAccount');
140
-				foreach ($result as $error)
141
-				{
142
-					$this->addFormError($learnerAccountField->get($error->getPropertyPath()),$error->getMessage());
137
+				foreach ($result as $error) {
138
+					$this->addFormError($learnerAccountField->get($error->getPropertyPath()), $error->getMessage());
143 139
 				}
144 140
 
145 141
 				// test rgpd
146 142
 				$rgpdField = $formSubscribe->get('rgpd');
147
-				if (!$rgpdField->getData())
148
-				{
143
+				if (!$rgpdField->getData()) {
149 144
 					// ajoute une erreur sur le champ
150 145
 					$message = 'la case doit être cochée';
151
-					$this->addFormError($rgpdField,$message);
146
+					$this->addFormError($rgpdField, $message);
152 147
 				}
153 148
 
154 149
 				// test le mot de passe
155 150
 				$plainPasswordField = $learnerAccountField->get('plainPassword')->get('first');
156 151
 				$plainPassword = $plainPasswordField->getData();
157
-				$errorMsg = PasswordValidator::renderError($this->get('translator'),$this->get('phxSettings'),$plainPassword);
158
-				if ($errorMsg != '')
159
-				{
160
-					$this->addFormError($plainPasswordField,$errorMsg);
152
+				$errorMsg = PasswordValidator::renderError($this->get('translator'), $this->get('phxSettings'), $plainPassword);
153
+				if ($errorMsg != '') {
154
+					$this->addFormError($plainPasswordField, $errorMsg);
161 155
 				}
162 156
 
163
-				if ($formSubscribe->isSubmitted() && $formSubscribe->isValid())
164
-				{
157
+				if ($formSubscribe->isSubmitted() && $formSubscribe->isValid()) {
165 158
 					$learnerAccount = $accountGenerator->generateLearnerAccount($person);
166 159
 
167 160
 					//$learnerAccount = $person->getLearnerAccount();
168 161
 					$encoder = $this->get('security.password_encoder');
169
-					$learnerAccount->setPassword($encoder->encodePassword($learnerAccount,$plainPassword));
162
+					$learnerAccount->setPassword($encoder->encodePassword($learnerAccount, $plainPassword));
170 163
 					$learnerAccount->setUsername($learnerAccountField->get('username')->getData());
171 164
 					$learnerAccount->setStatus(EnumAccountStatusType::STATUS_ENABLED);
172 165
 					$person->setLearnerAccount($learnerAccount);
@@ -176,40 +169,34 @@ class LoginController extends CatalogController
176 169
 
177 170
 					$manager->persist($person);
178 171
 					$manager->persist($learnerAccount);
179
-					
172
+
180 173
 					$manager->flush();
181 174
 
182 175
 					// connecte l'utilisateur
183
-					$loginService->loginCatalogUser($learnerAccount,$plainPassword);
176
+					$loginService->loginCatalogUser($learnerAccount, $plainPassword);
184 177
 
185 178
 					// envoi l'email
186 179
 					$event = new GenericEvent($person);
187
-					$eventDispatcher->dispatch($event,Events::LEARNER_SUBSCRIBE);
180
+					$eventDispatcher->dispatch($event, Events::LEARNER_SUBSCRIBE);
188 181
 
189 182
 					$succes = true;
190
-				}
191
-				else
192
-				{
183
+				} else {
193 184
 					$errors = $this->getFormFieldError($formSubscribe);
194 185
 					throw $this->createNotFoundException($this->serializeFormFieldError($errors));
195 186
 				}
196 187
 			}
197
-		}
198
-		catch (\Exception $exp)
199
-		{
188
+		} catch (\Exception $exp) {
200 189
 			$succes = false;
201
-			
190
+
202 191
 			$errors = $this->getFormFieldError($formSubscribe);
203 192
 
204 193
 			$response['error'] = $exp->getMessage();
205 194
 			$response['formError'] = array();
206 195
 
207 196
 			// parcours les erreurs
208
-			foreach ($errors as $fieldId => $errorField)
209
-			{
197
+			foreach ($errors as $fieldId => $errorField) {
210 198
 				$message = $this->getFormFieldErrorMessage($errorField);
211
-				if ($message)
212
-				{
199
+				if ($message) {
213 200
 					$response['formError'][$fieldId] = $message;
214 201
 				}
215 202
 			}
@@ -224,36 +211,29 @@ class LoginController extends CatalogController
224 211
 
225 212
 	public function validateRecover(Request $request, EventDispatcherInterface $eventDispatcher)
226 213
 	{
227
-		if ($this->getLearnerSpaceUser())
228
-		{
214
+		if ($this->getLearnerSpaceUser()) {
229 215
 			throw $this->createErrorException($this->translate('error_recover_password_logged'));
230 216
 		}
231 217
 
232 218
 		$error = $succes = null;
233
-		try
234
-		{
235
-			if ($request->isMethod('POST'))
236
-			{
219
+		try {
220
+			if ($request->isMethod('POST')) {
237 221
 				$formRecover = $this->createRecoverForm();
238 222
 				$formRecover->handleRequest($request);
239
-				if ($formRecover->isSubmitted() && $formRecover->isValid())
240
-				{
223
+				if ($formRecover->isSubmitted() && $formRecover->isValid()) {
241 224
 					// cherche le compte
242 225
 					$learnerAccountRepository = $this->getRepository(LearnerAccount::class);
243 226
 					$learnerAccountSearch = $learnerAccountRepository->loadUserByUsername($formRecover->get('username')->getData());
244
-					if (!$learnerAccountSearch)
245
-					{
227
+					if (!$learnerAccountSearch) {
246 228
 						throw $this->createNotFoundException();
247 229
 					}
248 230
 
249 231
 					// déclanche l'événement du mail
250 232
 					$event = new GenericEvent($learnerAccountSearch);
251
-					$eventDispatcher->dispatch($event,Events::LEARNER_RECOVER_ON_CATALOG);
233
+					$eventDispatcher->dispatch($event, Events::LEARNER_RECOVER_ON_CATALOG);
252 234
 				}
253 235
 			}
254
-		}
255
-		catch (\Exception $exp)
256
-		{
236
+		} catch (\Exception $exp) {
257 237
 			$error = $exp->getMessage();
258 238
 		}
259 239
 
@@ -268,8 +248,7 @@ class LoginController extends CatalogController
268 248
 
269 249
 	public function validateLogout(Request $request)
270 250
 	{
271
-		if (!$this->getLearnerSpaceUser())
272
-		{
251
+		if (!$this->getLearnerSpaceUser()) {
273 252
 			throw $this->createAccessDeniedException($this->translate('error_403_message'));
274 253
 		}
275 254
 
@@ -281,89 +260,85 @@ class LoginController extends CatalogController
281 260
 		$session->set('_security_' . $name, null);
282 261
 		$session->save();
283 262
 
284
-//		$request->getSession()->invalidate();
263
+		//		$request->getSession()->invalidate();
285 264
 
286 265
 		$response['nav'] = $this->phxRenderView('layouts/partials/header/header.html.twig');
287 266
 		return $this->json($response);
288 267
 	}
289 268
 
290
-	public function manageDefinePassword(Request $request,LoginService $loginService)
269
+	public function manageDefinePassword(Request $request, LoginService $loginService)
291 270
 	{
292
-		if ($this->getLearnerSpaceUser())
293
-		{
271
+		if ($this->getLearnerSpaceUser()) {
294 272
 			throw $this->createErrorException($this->translate('error_recover_password_logged'));
295 273
 		}
296 274
 		$token = $request->get('token');
275
+		if (!$token) {
276
+			throw $this->createNotFoundException($this->translate('error_403_message'));
277
+		}
297 278
 
298 279
 		$tokenData = PasswordRecoverNotifyLearner::decrytUrlArg($token);
299 280
 		if (!$tokenData) {
300
-            // token expiré
301
-            $parameters = array(
302
-                'text' => $this->translate('catalog_password_expired'),
303
-                'path' => 'catalog_homepage',
304
-                'go' => $this->translate('catalog_password_return')
305
-            );
306
-            return $this->phxRender('bundles/TwigBundle/Exception/definePassword.html.twig', $parameters);
281
+			$response = new Response('', 404);
282
+
283
+			// token expiré
284
+			$parameters = array(
285
+				'text' => $this->translate('catalog_password_expired'),
286
+				'path' => 'catalog_homepage',
287
+				'go' => $this->translate('catalog_password_return')
288
+			);
289
+			return $this->phxRender('bundles/TwigBundle/Exception/definePassword.html.twig', $parameters,$response);
307 290
 		}
308
-		
291
+
309 292
 		$respository = $this->getRepository(LearnerAccount::class);
310 293
 		$learnerAccount = $respository->findOneBy(array('learnerAccountId' => $tokenData['learnerAccountId']));
311
-		if (!$learnerAccount)
312
-		{
294
+		if (!$learnerAccount) {
313 295
 			throw $this->createNotFoundException($this->translate('error_403_message'));
314 296
 		}
315
-		$form = $this->createForm(CatalogDefinePasswordType::class,$learnerAccount); 
297
+		$form = $this->createForm(CatalogDefinePasswordType::class, $learnerAccount);
316 298
 
317
-		try
318
-		{
319
-			if ($request->isMethod('POST'))
320
-			{
299
+		try {
300
+			if ($request->isMethod('POST')) {
321 301
 				$form->handleRequest($request);
322 302
 
323 303
 				// test le mot de passe
324 304
 				$plainPasswordField = $form->get('plainPassword')->get('first');
325 305
 				$plainPassword = $plainPasswordField->getData();
326
-				$errorMsg = PasswordValidator::renderError($this->get('translator'),$this->get('phxSettings'),$plainPassword);
327
-				if ($errorMsg != '')
328
-				{
329
-					$this->addFormError($plainPasswordField,$errorMsg);
306
+				$errorMsg = PasswordValidator::renderError($this->get('translator'), $this->get('phxSettings'), $plainPassword);
307
+				if ($errorMsg != '') {
308
+					$this->addFormError($plainPasswordField, $errorMsg);
330 309
 				}
331 310
 
332 311
 				// test le formulaire
333
-				if ($form->isSubmitted() && $form->isValid())
334
-				{
312
+				if ($form->isSubmitted() && $form->isValid()) {
335 313
 					$encoder = $this->get('security.password_encoder');
336
-					$learnerAccount->setPassword($encoder->encodePassword($learnerAccount,$plainPassword));
314
+					$learnerAccount->setPassword($encoder->encodePassword($learnerAccount, $plainPassword));
337 315
 
338 316
 					$manager = $this->getManager();
339 317
 					$manager->flush();
340 318
 
341 319
 					// logue l'utilisateur
342
-					$loginService->loginCatalogUser($learnerAccount,$plainPassword);
343
-					
320
+					$loginService->loginCatalogUser($learnerAccount, $plainPassword);
321
+
344 322
 					//$this->addFlash('success', "mot de passe redéfini");
345 323
 
346 324
 					// redirige sur le catalogue
347 325
 					$route = $tokenData['route'];
348
-					if ($route)
349
-					{
326
+					if ($route) {
350 327
 						$route = 'catalog_homepage';
351 328
 					}
352 329
 
353 330
 					$this->addFlash('success', $this->translate('new_password_defined'));
354
-					
331
+
355 332
 					return $this->redirectToRoute($route);
356 333
 				}
357 334
 			}
358
-		}
359
-		catch (\Exception $exp)
360
-		{
335
+		} catch (\Exception $exp) {
361 336
 			$error = $exp->getMessage();
362 337
 			$succes = false;
363 338
 		}
364 339
 
365 340
 		// affiche la vue pour définir le mot de passe
366
-		$parameters = array('token' => $token,'form' => $form->createView());
367
-		return $this->phxRender('catalog/pages/define_password.html.twig',$parameters);
341
+		$parameters = array('token' => $token, 'form' => $form->createView());
342
+		return $this->phxRender('catalog/pages/define_password.html.twig', $parameters);
368 343
 	}
369
-}
344
+}

+ 16
- 2
src/Phoenix/Controller/LearningSpace/LoginController.php View File

@@ -67,7 +67,21 @@ class LoginController extends LearningSpaceController
67 67
 				$manager = $this->getDoctrine()->getManager();
68 68
 				$repository = $manager->getRepository(LearnerAccount::class);
69 69
 
70
-				$learnerAccount = $repository->loadUserByUsername($email);
70
+				try {
71
+					$learnerAccount = $repository->loadUserByUsername($email);
72
+				} catch (\Exception $exp) {
73
+
74
+					$response = new Response('', 409);
75
+
76
+					// token expiré
77
+					$parameters = array(
78
+						'text' => $exp->getMessage(),
79
+						'path' => 'ls_password_recover',
80
+						'go' => $this->translate('catalog_form_catalog_password_title')
81
+					);
82
+					return $this->phxRender('bundles/TwigBundle/Exception/definePassword.html.twig', $parameters, $response);
83
+				}
84
+
71 85
 				if ($learnerAccount) {
72 86
 					//On déclenche l'event si l'adresse email est précisée
73 87
 					if ($learnerAccount->getEmail() != "") {
@@ -111,7 +125,7 @@ class LoginController extends LearningSpaceController
111 125
 		$tokenData = PasswordRecoverNotifyLearner::decrytUrlArg($token);
112 126
 		if (!$tokenData) {
113 127
 
114
-			$response = new Response('',404);
128
+			$response = new Response('', 404);
115 129
 
116 130
 			// token expiré
117 131
 			$parameters = array(

+ 6
- 1
src/Phoenix/Controller/NewBackOffice/Admin/LoginController.php View File

@@ -18,6 +18,7 @@ use Logipro\Phoenix\Entity\Account\LearnerAccount;
18 18
 use Symfony\Component\Security\Http\Firewall\ContextListener;
19 19
 use Symfony\Component\HttpKernel\KernelInterface;
20 20
 use Logipro\Phoenix\Service\LoginService;
21
+use Symfony\Component\HttpFoundation\Response;
21 22
 
22 23
 class LoginController extends BackOfficeAdminController
23 24
 {
@@ -120,16 +121,20 @@ class LoginController extends BackOfficeAdminController
120 121
             throw $this->createNotFoundException($this->translate('error_403_message'));
121 122
         }
122 123
         $token = $request->get('token');
124
+        if (!$token) {
125
+            throw $this->createNotFoundException($this->translate('error_403_message'));
126
+        }
123 127
 
124 128
         $tokenData = PasswordRecoverNotifyUser::decrytUrlArg($token);
125 129
         if (!$tokenData) {
130
+            $response = new Response('', 404);
126 131
             // token expiré
127 132
             $parameters = array(
128 133
                 'text' => $this->translate('bo_password_expired'),
129 134
                 'path' => 'bo_homepage',
130 135
                 'go' => $this->translate('back_homepage')
131 136
             );
132
-            return $this->phxRender('bundles/TwigBundle/Exception/definePassword.html.twig', $parameters);
137
+            return $this->phxRender('bundles/TwigBundle/Exception/definePassword.html.twig', $parameters, $response);
133 138
         }
134 139
 
135 140
         $respository = $this->getRepository(User::class);

+ 12
- 12
src/Phoenix/Controller/NewBackOffice/Admin/Session/Create/SessionController.php View File

@@ -38,6 +38,7 @@ use Logipro\Phoenix\Service\Session\SessionExportHandler;
38 38
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
39 39
 use Logipro\Phoenix\Form\Session\SessionIntraCreateAndUpdateSocietyType;
40 40
 use Logipro\Phoenix\Controller\NewBackOffice\Admin\BackOfficeAdminController;
41
+use Logipro\Phoenix\Service\EntityDuplicator;
41 42
 
42 43
 class SessionController extends BackOfficeAdminController
43 44
 {
@@ -524,7 +525,7 @@ class SessionController extends BackOfficeAdminController
524 525
 	 * @param int $sessionId
525 526
 	 * @return \Symfony\Component\HttpFoundation\Response
526 527
 	 */
527
-	public function manageImportPath(Request $request, $sessionId)
528
+	public function manageImportPath(Request $request, $sessionId,EntityDuplicator $entityDuplicator)
528 529
 	{
529 530
 		$this->denyAccessUnlessGranted('ROLE_MANAGE_SESSION');
530 531
 
@@ -549,7 +550,7 @@ class SessionController extends BackOfficeAdminController
549 550
 
550 551
 			try {
551 552
 				// Importer le parcours
552
-				$this->importSessionPath($session, $templatePath);
553
+				$this->importSessionPath($session, $templatePath, $entityDuplicator);
553 554
 				$this->addFlash('success', $this->get('translator')->trans('success_learning_path_import'));
554 555
 			} catch (PhoenixException $e) {
555 556
 				$this->addFlash('error', $this->get('translator')->trans('error_import_failed', array('%message%' => $e->getMessage())));
@@ -576,30 +577,29 @@ class SessionController extends BackOfficeAdminController
576 577
 	/**
577 578
 	 *
578 579
 	 * @param Session $session
579
-	 * @param LearningPath $templatePath
580
+	 * @param LearningPath $templateLearningPath
580 581
 	 */
581
-	protected function importSessionPath(Session $session, LearningPath $templatePath)
582
+	protected function importSessionPath(Session $session, LearningPath $templateLearningPath,EntityDuplicator $entityDuplicator): void
582 583
 	{
583
-		$manager = $this->getDoctrine()->getManager();
584
-
585 584
 		if ($session->getLearningPath()) {
586 585
 			//FIXME ???
587 586
 			throw new PhoenixException($this->get('translator')->trans('exception_session_already_learning_path'));
588 587
 		}
589 588
 
590 589
 		// Clonage du gabarit
591
-		$entityDuplicator = $this->get('Logipro\Phoenix\Service\EntityDuplicator');
592
-		$newLearningPath = $entityDuplicator->duplicateLearningPath($templatePath, $session, false);
590
+		$entityDuplicator->duplicateLearningPath($templateLearningPath, $session);
593 591
 
594 592
 		// Durée
593
+		// raph: cet algo n'est pas au niveau de l'event
594
+		// raph: ce ne serait pas une comparaison par rapport à '00:00:00' ??
595 595
 		if ($session->getDuration()->format('%h:%i:%s') == '0:0:0') {
596
-			$session->setDuration($templatePath->getTotalDuration());
596
+			$session->setDuration($templateLearningPath->getTotalDuration());
597
+
598
+			$manager = $this->getDoctrine()->getManager();
599
+			$manager->flush();
597 600
 		}
598
-		$manager->persist($newLearningPath);
599
-		$manager->flush();
600 601
 	}
601 602
 
602
-
603 603
 	/**
604 604
 	 *
605 605
 	 * @param Request $request

+ 70
- 0
src/Phoenix/DoctrineMigrations/Version20191203165250.php View File

@@ -0,0 +1,70 @@
1
+<?php
2
+
3
+declare(strict_types=1);
4
+
5
+namespace Logipro\Phoenix\DoctrineMigrations;
6
+
7
+use Doctrine\DBAL\Schema\Schema;
8
+use Doctrine\Migrations\AbstractMigration;
9
+use Symfony\Component\Filesystem\Filesystem;
10
+use Logipro\Phoenix\Service\FileUploader;
11
+use Logipro\Phoenix\Service\FileUploaderInterface;
12
+use Logipro\Phoenix\Entity\Sequence;
13
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
14
+use Symfony\Component\DependencyInjection\ContainerInterface;
15
+
16
+final class Version20191203165250 extends AbstractMigration
17
+implements ContainerAwareInterface
18
+{
19
+    private $fileUploader;
20
+
21
+    public function setContainer(ContainerInterface $container = null)
22
+    {
23
+        $this->fileUploader = $container->get(FileUploader::class);
24
+    }
25
+    public function up(Schema $schema): void
26
+    {
27
+        $fileUploader = $this->fileUploader;
28
+        $filesystem = new Filesystem();
29
+        $sequence = new Sequence();
30
+        $propertyName = 'image';
31
+
32
+        // extrait les images des sequences et regarde les sequences qui utilisent plusieurs fois la même image
33
+        $query = "SELECT COUNT(*) as `number`,`image` FROM `phx_sequence` WHERE `image` != '' GROUP BY `image` HAVING `number` > 1";
34
+        foreach ($this->connection->fetchAll($query) as $result) {
35
+            $imageName = $result['image'];
36
+
37
+            // recherche les identifiants des sequences de l'image
38
+            $query = 'SELECT `sequence_id` FROM `phx_sequence` WHERE `image`=\'' . $imageName . '\'';
39
+            $results = $this->connection->fetchAll($query);
40
+
41
+            // supprime le premier
42
+            array_shift($results);
43
+
44
+            // parcours les identifiants pour la copie
45
+            $queries = array();
46
+            foreach ($results as $result) {
47
+                $newFileName = $fileUploader->generateNewName($imageName);
48
+                try {
49
+                    $filesystem->copy(
50
+                        $fileUploader->buildUploadPublicPath($imageName, $sequence, $propertyName),
51
+                        $fileUploader->buildUploadPublicPath($newFileName, $sequence, $propertyName)
52
+                    );
53
+
54
+                    // après la copie, on met a jour l'objet
55
+                    $queries[] = 'UPDATE `phx_sequence` SET `image`=\'' . $newFileName . '\' WHERE `sequence_id`=' . $result['sequence_id'];
56
+                } catch (\Exception $exp) {
57
+                    dump($exp->getMessage());
58
+                }
59
+            }
60
+
61
+            foreach ($queries as $query)
62
+            {
63
+                $this->connection->executeQuery($query);
64
+            }
65
+        }
66
+    }
67
+
68
+    public function down(Schema $schema): void
69
+    { }
70
+}

+ 23
- 14
src/Phoenix/Entity/Assertion/Assertion.php View File

@@ -1,4 +1,5 @@
1 1
 <?php
2
+
2 3
 namespace Logipro\Phoenix\Entity\Assertion;
3 4
 
4 5
 use Logipro\Phoenix\Entity\Common\AbstractEntity;
@@ -44,19 +45,20 @@ class Assertion extends AbstractEntity
44 45
 	protected $testedSequenceItem;
45 46
 
46 47
 	/**
47
-	 *
48
+	 * @deprecated use LearningPathHandler:duplicateTemplate instead
48 49
 	 */
49 50
 	public function __clone()
50 51
 	{
51 52
 		$this->assertionId = null;
52
-		$this->name = $this->name.' clone';
53
-	}	
53
+		$this->name = $this->name . ' clone';
54
+	}
54 55
 
55 56
 	/**
56 57
 	 *
57 58
 	 * @return int
58 59
 	 */
59
-	public function getAssertionId() {
60
+	public function getAssertionId()
61
+	{
60 62
 		return $this->assertionId;
61 63
 	}
62 64
 
@@ -64,7 +66,8 @@ class Assertion extends AbstractEntity
64 66
 	 * Nom de l'affirmation
65 67
 	 * @return string
66 68
 	 */
67
-	public function getName() {
69
+	public function getName()
70
+	{
68 71
 		return $this->name;
69 72
 	}
70 73
 
@@ -72,7 +75,8 @@ class Assertion extends AbstractEntity
72 75
 	 * Ajoute un nom à l'affirmation
73 76
 	 * @param string $name
74 77
 	 */
75
-	public function setName($name) {
78
+	public function setName($name)
79
+	{
76 80
 		$this->name = $name;
77 81
 	}
78 82
 
@@ -81,7 +85,8 @@ class Assertion extends AbstractEntity
81 85
 	 *
82 86
 	 * @return boolean
83 87
 	 */
84
-	public function isChecked() {
88
+	public function isChecked()
89
+	{
85 90
 		return $this->isChecked;
86 91
 	}
87 92
 
@@ -89,7 +94,8 @@ class Assertion extends AbstractEntity
89 94
 	 *
90 95
 	 * @param $isChecked
91 96
 	 */
92
-	public function setIsChecked($isChecked) {
97
+	public function setIsChecked($isChecked)
98
+	{
93 99
 		$this->isChecked = $isChecked;
94 100
 	}
95 101
 
@@ -97,7 +103,8 @@ class Assertion extends AbstractEntity
97 103
 	 *
98 104
 	 * @return ConditionPursuit
99 105
 	 */
100
-	public function getConditionPursuit(): ConditionPursuit {
106
+	public function getConditionPursuit(): ConditionPursuit
107
+	{
101 108
 		return $this->conditionPursuit;
102 109
 	}
103 110
 
@@ -105,7 +112,8 @@ class Assertion extends AbstractEntity
105 112
 	 *
106 113
 	 * @param ConditionPursuit|NULL $conditionPursuit
107 114
 	 */
108
-	public function setConditionPursuit($conditionPursuit) {
115
+	public function setConditionPursuit($conditionPursuit)
116
+	{
109 117
 		$this->conditionPursuit = $conditionPursuit;
110 118
 	}
111 119
 
@@ -114,7 +122,8 @@ class Assertion extends AbstractEntity
114 122
 	 *
115 123
 	 * @return SequenceItem|NULL
116 124
 	 */
117
-	public function getTestedSequenceItem() {
125
+	public function getTestedSequenceItem()
126
+	{
118 127
 		return $this->testedSequenceItem;
119 128
 	}
120 129
 
@@ -122,7 +131,8 @@ class Assertion extends AbstractEntity
122 131
 	 *
123 132
 	 * @return SequenceItem|NULL $testedSequenceItem
124 133
 	 */
125
-	public function setTestedSequenceItem($testedSequenceItem) {
134
+	public function setTestedSequenceItem($testedSequenceItem)
135
+	{
126 136
 		$this->testedSequenceItem = $testedSequenceItem;
127 137
 	}
128 138
 
@@ -131,8 +141,7 @@ class Assertion extends AbstractEntity
131 141
 	 */
132 142
 	public function preRemove()
133 143
 	{
134
-		if (!$this->isDeletable())
135
-		{
144
+		if (!$this->isDeletable()) {
136 145
 			$message = $this->getLastExcuseMessage(EntityExcuse::TYPE_IS_DELETABLE);
137 146
 			throw new PhoenixException($message);
138 147
 		}

+ 1
- 1
src/Phoenix/Entity/Condition/Condition.php View File

@@ -48,7 +48,7 @@ abstract class Condition extends AbstractEntity
48 48
 	}
49 49
 
50 50
 	/**
51
-	 *
51
+	 * @deprecated use LearningPathHandler:duplicateTemplate instead
52 52
 	 */
53 53
 	public function __clone()
54 54
 	{

+ 2
- 0
src/Phoenix/Entity/File/File.php View File

@@ -127,6 +127,8 @@ class File extends AbstractUploadableEntity
127 127
 		return $this;
128 128
 	}
129 129
 
130
+	/**
131
+	 */
130 132
 	public function __clone()
131 133
 	{
132 134
 		$this->fileId = null;

+ 294
- 349
src/Phoenix/Entity/LearningPath.php View File

@@ -1,4 +1,5 @@
1 1
 <?php
2
+
2 3
 namespace Logipro\Phoenix\Entity;
3 4
 
4 5
 use Logipro\Phoenix\Entity\Common\AbstractEntity;
@@ -19,147 +20,92 @@ use Logipro\Phoenix\Entity\ProgramInterface;
19 20
  */
20 21
 class LearningPath extends AbstractEntity implements ProgramInterface
21 22
 {
22
-	/**
23
-	 * ID du parcours.
24
-	 * @var int
25
-	 */
26
-	protected $learningPathId;
27
-
28
-	/**
29
-	 *
30
-	 * @var \DateTime
31
-	 */
32
-	protected $creationDate;
33
-
34
-	/**
35
-	 *
36
-	 * @var \DateTime
37
-	 */
38
-	protected $updateDate;
39
-
40
-	/**
41
-	 * @var string
42
-	 */
43
-	protected $title;
44
-
45
-	/**
46
-	 * @var string
47
-	 */
48
-	protected $description;
49
-
50
-	/**
51
-	 * Si ce parcours est un gabarit.
52
-	 * @var bool
53
-	 */
54
-	protected $isTemplate = false;
55
-
56
-	/**
57
-	 * Si ce parcours est accessible à tout les auteurs.
58
-	 * @var bool
59
-	 */
60
-	protected $isAccessAtAllAuthor = true;
61
-
62
-	/**
63
-	 * Session parente du parcours.
64
-	 * @var SessionFixedFixed
65
-	 */
66
-	protected $session;
67
-
68
-	/**
69
-	 * Gabarit de parcours qui a servi à la création.
70
-	 * @var LearningPath
71
-	 */
72
-	protected $template;
73
-
74
-	/**
75
-	 * Auteur créateur du parcours.
76
-	 * @var User
77
-	 */
78
-	protected $creatingUser;
79
-
80
-	/**
81
-	 * Dernier colaborateur à avoir modifier le parcours.
82
-	 * @var User
83
-	 */
84
-	protected $updatingUser;
85
-
86
-	/**
87
-	 *
88
-	 * @var ArrayCollection
89
-	 */
90
-	protected $sequences;
91
-	
92
-
93
-    /**
94
-	 * Program items
95
-	 * @var ArrayCollection
96
-	 */
97
-	protected $programItems;
98
-	
99
-	protected $isClone = false;
100
-	
101
-	/**
102
-	 * Constructeur de parcours.
103
-	 */
104
-	public function __construct()
105
-	{
106
-		$this->sequences = new ArrayCollection();
107
-		$this->programItems = new ArrayCollection();
108
-	}
109
-
110
-    /**
111
-     * Clonage protégé.
112
-     * Utiliser cloneThisTemplate.
113
-     */
114
-    protected function __clone()
115
-    {
116
-        $this->isClone = true;
117
-        
118
-        $this->learningPathId = null;
23
+    /**
24
+     * ID du parcours.
25
+     * @var int
26
+     */
27
+    protected $learningPathId;
119 28
 
120
-        // Copie les séquences
121
-        $sequences = new ArrayCollection();
122
-        foreach ($this->sequences as $sequence) {
123
-            $newSequence = clone $sequence;
124
-            $newSequence->setLearningPath($this);
29
+    /**
30
+     *
31
+     * @var \DateTime
32
+     */
33
+    protected $creationDate;
125 34
 
126
-            $sequences->add($newSequence);
127
-        }
35
+    /**
36
+     *
37
+     * @var \DateTime
38
+     */
39
+    protected $updateDate;
128 40
 
129
-        $this->sequences = $sequences;
41
+    /**
42
+     * @var string
43
+     */
44
+    protected $title;
130 45
 
131
-        // Copie les éléments de parcours
132
-        $programItems = new ArrayCollection();
133
-        foreach ($this->programItems as $programItem) {
134
-            $newProgramItem = clone $programItem;
135
-            $newProgramItem->setLearningPath($this);
46
+    /**
47
+     * @var string
48
+     */
49
+    protected $description;
136 50
 
137
-            $programItems->add($newProgramItem);
138
-        }
51
+    /**
52
+     * Si ce parcours est un gabarit.
53
+     * @var bool
54
+     */
55
+    protected $isTemplate = false;
139 56
 
140
-        $this->programItems = $programItems;
141
-    }
57
+    /**
58
+     * Si ce parcours est accessible à tout les auteurs.
59
+     * @var bool
60
+     */
61
+    protected $isAccessAtAllAuthor = true;
62
+
63
+    /**
64
+     * Session parente du parcours.
65
+     * @var SessionFixedFixed
66
+     */
67
+    protected $session;
68
+
69
+    /**
70
+     * Gabarit de parcours qui a servi à la création.
71
+     * @var LearningPath
72
+     */
73
+    protected $template;
74
+
75
+    /**
76
+     * Auteur créateur du parcours.
77
+     * @var User
78
+     */
79
+    protected $creatingUser;
80
+
81
+    /**
82
+     * Dernier colaborateur à avoir modifier le parcours.
83
+     * @var User
84
+     */
85
+    protected $updatingUser;
142 86
 
143 87
     /**
144
-     * Retourne une copie du parcours.
145 88
      *
146
-     * @param boolean Le parcours est il un gabarit ?
147
-     * @throws PhoenixException
148
-     * @return \Logipro\Phoenix\Entity\LearningPath
89
+     * @var ArrayCollection
149 90
      */
150
-    public function cloneThisTemplate($isTemplate = false)
151
-    {
152
-        //FIXME Peut-on copier un parcours qui n'est pas un gabarit ?
153
-        if (!$this->isTemplate()) {
154
-            throw new PhoenixException('Ce parcours n\'est pas un gabarit');
155
-        }
91
+    protected $sequences;
92
+
93
+
94
+    /**
95
+     * Program items
96
+     * @var ArrayCollection
97
+     */
98
+    protected $programItems;
156 99
 
157
-        // Clonage du parcours (et de ses éléments enfants en cascade)
158
-        $pathCopy = clone $this;
159
-        $pathCopy->setTemplate($this);
160
-        $pathCopy->setIsTemplate($isTemplate);
100
+    protected $isClone = false;
161 101
 
162
-        return $pathCopy;
102
+    /**
103
+     * Constructeur de parcours.
104
+     */
105
+    public function __construct()
106
+    {
107
+        $this->sequences = new ArrayCollection();
108
+        $this->programItems = new ArrayCollection();
163 109
     }
164 110
 
165 111
     /**
@@ -181,6 +127,12 @@ class LearningPath extends AbstractEntity implements ProgramInterface
181 127
         $this->updateDate = new \DateTime('now');
182 128
     }
183 129
 
130
+    public function __clone()
131
+    {
132
+        $this->learningPathId = null;
133
+        $this->isClone = true;
134
+    }
135
+
184 136
     /**
185 137
      *
186 138
      * @return int
@@ -271,9 +223,10 @@ class LearningPath extends AbstractEntity implements ProgramInterface
271 223
         return $this->session;
272 224
     }
273 225
 
274
-	public function setSession($session) {
275
-		$this->session = $session;
276
-	}
226
+    public function setSession($session)
227
+    {
228
+        $this->session = $session;
229
+    }
277 230
 
278 231
     /**
279 232
      *
@@ -311,21 +264,23 @@ class LearningPath extends AbstractEntity implements ProgramInterface
311 264
         $this->updatingUser = $updatingUser;
312 265
     }
313 266
 
314
-	/**
315
-	 *
316
-	 * @return User|NULL
317
-	 */
318
-	public function getCreatingUser() {
319
-		return $this->creatingUser;
320
-	}
267
+    /**
268
+     *
269
+     * @return User|NULL
270
+     */
271
+    public function getCreatingUser()
272
+    {
273
+        return $this->creatingUser;
274
+    }
321 275
 
322
-	/**
323
-	 *
324
-	 * @param User|NULL $creatingUser
325
-	 */
326
-	public function setCreatingUser($creatingUser) {
327
-		$this->creatingUser = $creatingUser;
328
-	}
276
+    /**
277
+     *
278
+     * @param User|NULL $creatingUser
279
+     */
280
+    public function setCreatingUser($creatingUser)
281
+    {
282
+        $this->creatingUser = $creatingUser;
283
+    }
329 284
 
330 285
     /**
331 286
      *
@@ -371,6 +326,12 @@ class LearningPath extends AbstractEntity implements ProgramInterface
371 326
         $this->sequences->removeElement($sequence);
372 327
     }
373 328
 
329
+    public function setSequences($sequences)
330
+    {
331
+        $this->sequences = $sequences;
332
+    }
333
+
334
+
374 335
     /**
375 336
      * Retourne le dossier d'un parcours
376 337
      * @return string
@@ -478,7 +439,7 @@ class LearningPath extends AbstractEntity implements ProgramInterface
478 439
         }
479 440
         return $events;
480 441
     }
481
-    
442
+
482 443
     public function getEventClassrooms()
483 444
     {
484 445
         $events = array();
@@ -506,7 +467,7 @@ class LearningPath extends AbstractEntity implements ProgramInterface
506 467
 
507 468
         return $this;
508 469
     }
509
-    
470
+
510 471
     /**
511 472
      * Get program items
512 473
      *
@@ -531,205 +492,189 @@ class LearningPath extends AbstractEntity implements ProgramInterface
531 492
     public function removeProgramItem(ProgramItem $programItem)
532 493
     {
533 494
         $this->programItems->removeElement($programItem);
534
-	}
535
-
536
-	/**
537
-	 * Retourne si le programme est vide ou non
538
-	 *
539
-	 * @return boolean
540
-	 */
541
-	public function programIsEmpty()
542
-	{
543
-		// test si le programme de l'offre a des elements
544
-		if (!empty($this->programItems))
545
-		{
546
-			// test si le programme est complété
547
-			foreach ($this->programItems as $programItem)
548
-			{
549
-				if (!$programItem->isEmpty()) {
550
-					return false;
551
-				}
552
-			}
553
-		}
554
-
555
-		return true;
556
-	}
557
-
558
-	/**
559
-	 * Retourne si le programme, 
560
-	 * à travers les types d'éléments renseignés en param à tester, 
561
-	 * est vide ou non
562
-	 *
563
-	 * @param array $programTypes
564
-	 *
565
-	 * @return boolean
566
-	 */
567
-	public function programIsEmptyFilterByTypes($programTypes)
568
-	{
569
-		// test si le programme de l'offre a des elements
570
-		if (!empty($this->programItems))
571
-		{
572
-			// S'il y a une liste de type d'élément de program a tester
573
-			if (is_array($programTypes))
574
-			{
575
-				// test si le programme est complété
576
-				foreach ($this->programItems as $programItem)
577
-				{
578
-					$programTypeItem = $programItem->getProgramTypeItem();
579
-					// Si le type d'élément est présent dans la liste des types d'élément à tester
580
-					if ($programTypeItem && in_array($programTypeItem->getName(), $programTypes)) {
581
-						if (!$programItem->isEmpty()) {
582
-							return false;
583
-						}
584
-					}
585
-				}
586
-			}
587
-			else
588
-			{
589
-				$this->programIsEmpty();
590
-			}
591
-		}
592
-
593
-		return true;
594
-	}
595
-	
596
-	/**
597
-	 * Retourne le cumul des durées des sequences du parcours
598
-	 *
599
-	 * @return DateInterval
600
-	 */
601
-	public function getTotalDuration()
602
-	{
603
-		$duration = 0;
604
-
605
-		$totalSeconds = 0;
606
-		
607
-		foreach ($this->sequences as $sequence)
608
-		{
609
-			foreach ($sequence->getSequenceItems() as $sequenceItem)
610
-			{
611
-				if ($sequenceItem->getSequenceItemId())
612
-				{
613
-					$sequenceItemDuration = $sequenceItem->getDuration();
614
-					if ($sequenceItemDuration)
615
-					{
616
-						$totalSeconds += $sequenceItemDuration->h * 3600;
617
-						$totalSeconds += $sequenceItemDuration->i * 60;
618
-						$totalSeconds += $sequenceItemDuration->s;
619
-					}
620
-				}
621
-			}
622
-		}
623
-
624
-		$dateIntervalReturn = new \DateInterval('PT'.$totalSeconds.'S');
625
-		
626
-		return $dateIntervalReturn;
627
-	}
628
-
629
-	
630
-	
631
-	/**
632
-	 * Definit si une sequence est verouillée (en fonction du sequence item conditionable qui la précède)
633
-	 */
634
-	public function getLastSequenceItemStartedByLearner($learner)
635
-	{
636
-		// Recuperation du sequenceItem portant la condition sur celle là
637
-		$repository = $this->entityManager->getRepository(SequenceItem::class);
638
-		$sequenceItem = $repository->findLastSequenceItemStartedOfLearningPathByLearner($learner, $this);
639
-		
640
-		return $sequenceItem;
641
-	}
642
-
643
-	/**
644
-	 * Retourne les sequences items du parcours
645
-	 *
646
-	 * @return SequenceItem[]
647
-	 */
648
-	public function getSequenceItems()
649
-	{
650
-		$items = array();
651
-		foreach ($this->sequences as $sequence)
652
-		{
653
-			$sequenceItems = $sequence->getSequenceItems();
654
-			foreach ($sequenceItems as $sequenceItem)
655
-			{
656
-				$items[] = $sequenceItem;
657
-			}
658
-		}
659
-		return $items;
660
-	}
661
-
662
-	/**
663
-	 * retourne le mode du parcours
664
-	 * 
665
-	 * @return string
666
-	 */
667
-	public function getMode()
668
-	{
669
-		$items = $this->getSequenceItems();
670
-
671
-		$type = null;
672
-		foreach ($items as $item)
673
-		{
674
-			switch (get_class($item))
675
-			{
676
-				// présentiel
677
-				case EventClassroom::class:
678
-				case EventVirtualClassroom::class:
679
-					if (is_null($type))
680
-					{
681
-						$type = EnumSessionModeType::SESSION_MODE_FACE_TO_FACE;
682
-					}
683
-					elseif ($type == EnumSessionModeType::SESSION_MODE_E_LEARNING)
684
-					{
685
-						$type = EnumSessionModeType::SESSION_MODE_BLENDED;
686
-					}
687
-					break;
688
-
689
-				// virtuel
690
-				case ActivityPage::class:
691
-				case ActivityPackage::class:
692
-				//case EventVirtualClassroom::class:
693
-					if (is_null($type))
694
-					{
695
-						$type = EnumSessionModeType::SESSION_MODE_E_LEARNING;
696
-					}
697
-					elseif ($type == EnumSessionModeType::SESSION_MODE_FACE_TO_FACE)
698
-					{
699
-						$type = EnumSessionModeType::SESSION_MODE_BLENDED;
700
-					}
701
-					break;
702
-			}
703
-		}
704
-		return $type;
705
-	}
706
-
707
-	public function getIsClone()
708
-	{
709
-		return $this->isClone;
710
-	}
711
-	
712
-	/**
713
-	 * Get si ce parcours est accessible à tout les auteurs.
714
-	 *
715
-	 * @return  bool
716
-	 */ 
717
-	public function getIsAccessAtAllAuthor()
718
-	{
719
-		return $this->isAccessAtAllAuthor;
720
-	}
721
-
722
-	/**
723
-	 * Set si ce parcours est accessible à tout les auteurs.
724
-	 *
725
-	 * @param  bool  $isAccessAtAllAuthor  Si ce parcours est accessible à tout les auteurs.
726
-	 *
727
-	 * @return  self
728
-	 */ 
729
-	public function setIsAccessAtAllAuthor(bool $isAccessAtAllAuthor)
730
-	{
731
-		$this->isAccessAtAllAuthor = $isAccessAtAllAuthor;
732
-
733
-		return $this;
734
-	}
495
+    }
496
+
497
+    public function setProgramItems($programItems)
498
+    {
499
+        $this->programItems = $programItems;
500
+    }
501
+
502
+    /**
503
+     * Retourne si le programme est vide ou non
504
+     *
505
+     * @return boolean
506
+     */
507
+    public function programIsEmpty()
508
+    {
509
+        // test si le programme de l'offre a des elements
510
+        if (!empty($this->programItems)) {
511
+            // test si le programme est complété
512
+            foreach ($this->programItems as $programItem) {
513
+                if (!$programItem->isEmpty()) {
514
+                    return false;
515
+                }
516
+            }
517
+        }
518
+
519
+        return true;
520
+    }
521
+
522
+    /**
523
+     * Retourne si le programme, 
524
+     * à travers les types d'éléments renseignés en param à tester, 
525
+     * est vide ou non
526
+     *
527
+     * @param array $programTypes
528
+     *
529
+     * @return boolean
530
+     */
531
+    public function programIsEmptyFilterByTypes($programTypes)
532
+    {
533
+        // test si le programme de l'offre a des elements
534
+        if (!empty($this->programItems)) {
535
+            // S'il y a une liste de type d'élément de program a tester
536
+            if (is_array($programTypes)) {
537
+                // test si le programme est complété
538
+                foreach ($this->programItems as $programItem) {
539
+                    $programTypeItem = $programItem->getProgramTypeItem();
540
+                    // Si le type d'élément est présent dans la liste des types d'élément à tester
541
+                    if ($programTypeItem && in_array($programTypeItem->getName(), $programTypes)) {
542
+                        if (!$programItem->isEmpty()) {
543
+                            return false;
544
+                        }
545
+                    }
546
+                }
547
+            } else {
548
+                $this->programIsEmpty();
549
+            }
550
+        }
551
+
552
+        return true;
553
+    }
554
+
555
+    /**
556
+     * Retourne le cumul des durées des sequences du parcours
557
+     *
558
+     * @return DateInterval
559
+     */
560
+    public function getTotalDuration()
561
+    {
562
+        $duration = 0;
563
+
564
+        $totalSeconds = 0;
565
+
566
+        foreach ($this->sequences as $sequence) {
567
+            foreach ($sequence->getSequenceItems() as $sequenceItem) {
568
+                if ($sequenceItem->getSequenceItemId()) {
569
+                    $sequenceItemDuration = $sequenceItem->getDuration();
570
+                    if ($sequenceItemDuration) {
571
+                        $totalSeconds += $sequenceItemDuration->h * 3600;
572
+                        $totalSeconds += $sequenceItemDuration->i * 60;
573
+                        $totalSeconds += $sequenceItemDuration->s;
574
+                    }
575
+                }
576
+            }
577
+        }
578
+
579
+        $dateIntervalReturn = new \DateInterval('PT' . $totalSeconds . 'S');
580
+
581
+        return $dateIntervalReturn;
582
+    }
583
+
584
+
585
+
586
+    /**
587
+     * Definit si une sequence est verouillée (en fonction du sequence item conditionable qui la précède)
588
+     */
589
+    public function getLastSequenceItemStartedByLearner($learner)
590
+    {
591
+        // Recuperation du sequenceItem portant la condition sur celle là
592
+        $repository = $this->entityManager->getRepository(SequenceItem::class);
593
+        $sequenceItem = $repository->findLastSequenceItemStartedOfLearningPathByLearner($learner, $this);
594
+
595
+        return $sequenceItem;
596
+    }
597
+
598
+    /**
599
+     * Retourne les sequences items du parcours
600
+     *
601
+     * @return SequenceItem[]
602
+     */
603
+    public function getSequenceItems()
604
+    {
605
+        $items = array();
606
+        foreach ($this->sequences as $sequence) {
607
+            $sequenceItems = $sequence->getSequenceItems();
608
+            foreach ($sequenceItems as $sequenceItem) {
609
+                $items[] = $sequenceItem;
610
+            }
611
+        }
612
+        return $items;
613
+    }
614
+
615
+    /**
616
+     * retourne le mode du parcours
617
+     * 
618
+     * @return string
619
+     */
620
+    public function getMode()
621
+    {
622
+        $items = $this->getSequenceItems();
623
+
624
+        $type = null;
625
+        foreach ($items as $item) {
626
+            switch (get_class($item)) {
627
+                    // présentiel
628
+                case EventClassroom::class:
629
+                case EventVirtualClassroom::class:
630
+                    if (is_null($type)) {
631
+                        $type = EnumSessionModeType::SESSION_MODE_FACE_TO_FACE;
632
+                    } elseif ($type == EnumSessionModeType::SESSION_MODE_E_LEARNING) {
633
+                        $type = EnumSessionModeType::SESSION_MODE_BLENDED;
634
+                    }
635
+                    break;
636
+
637
+                    // virtuel
638
+                case ActivityPage::class:
639
+                case ActivityPackage::class:
640
+                    //case EventVirtualClassroom::class:
641
+                    if (is_null($type)) {
642
+                        $type = EnumSessionModeType::SESSION_MODE_E_LEARNING;
643
+                    } elseif ($type == EnumSessionModeType::SESSION_MODE_FACE_TO_FACE) {
644
+                        $type = EnumSessionModeType::SESSION_MODE_BLENDED;
645
+                    }
646
+                    break;
647
+            }
648
+        }
649
+        return $type;
650
+    }
651
+
652
+    public function getIsClone()
653
+    {
654
+        return $this->isClone;
655
+    }
656
+
657
+    /**
658
+     * Get si ce parcours est accessible à tout les auteurs.
659
+     *
660
+     * @return  bool
661
+     */
662
+    public function getIsAccessAtAllAuthor()
663
+    {
664
+        return $this->isAccessAtAllAuthor;
665
+    }
666
+
667
+    /**
668
+     * Set si ce parcours est accessible à tout les auteurs.
669
+     *
670
+     * @param  bool  $isAccessAtAllAuthor  Si ce parcours est accessible à tout les auteurs.
671
+     *
672
+     * @return  self
673
+     */
674
+    public function setIsAccessAtAllAuthor(bool $isAccessAtAllAuthor)
675
+    {
676
+        $this->isAccessAtAllAuthor = $isAccessAtAllAuthor;
677
+
678
+        return $this;
679
+    }
735 680
 }

+ 1
- 1
src/Phoenix/Entity/ProgramItem.php View File

@@ -75,7 +75,7 @@ class ProgramItem extends AbstractEntity
75 75
 	
76 76
 
77 77
 	/**
78
-	 *
78
+	 * @deprecated use LearningPathHandler:duplicateTemplate instead
79 79
 	 */
80 80
 	public function __clone()
81 81
 	{

+ 61
- 99
src/Phoenix/Entity/Sequence.php View File

@@ -1,4 +1,5 @@
1 1
 <?php
2
+
2 3
 namespace Logipro\Phoenix\Entity;
3 4
 
4 5
 use Logipro\Phoenix\Entity\Common\AbstractUploadableEntity;
@@ -46,14 +47,14 @@ class Sequence extends AbstractUploadableEntity
46 47
 	protected $description;
47 48
 
48 49
 	/**
49
-     * @var string
50
-     */
50
+	 * @var string
51
+	 */
51 52
 	private $image;
52 53
 
53
-    /**
54
-     */
54
+	/**
55
+	 */
55 56
 	private $imageFile = null;
56
-	
57
+
57 58
 	/**
58 59
 	 *
59 60
 	 * @var LearningPath
@@ -75,38 +76,11 @@ class Sequence extends AbstractUploadableEntity
75 76
 	}
76 77
 
77 78
 	/**
78
-	 *
79
+	 * @deprecated use LearningPathHandler:duplicateTemplate instead
79 80
 	 */
80 81
 	public function __clone()
81 82
 	{
82 83
 		$this->sequenceId = null;
83
-
84
-		// Copie les éléments
85
-		$items = new ArrayCollection();
86
-		$counter = 0;
87
-		foreach ($this->sequenceItems as $item)
88
-		{
89
-	
90
-			if ($item->getItemType() == 'conditionpursuit')
91
-			{
92
-				$previousSequenceItem = ($items->get($counter-1));
93
-				$item->setAssociatedSequenceItem($previousSequenceItem);
94
-				$newItem = clone $item;
95
-				$newItem->setSequence($this);
96
-			}
97
-			else
98
-			{
99
-				$newItem = clone $item;
100
-				$newItem->setSequence($this);
101
-			}
102
-
103
-
104
-
105
-			$items->add($newItem);
106
-			$counter++;
107
-		}
108
-
109
-		$this->sequenceItems = $items;
110 84
 	}
111 85
 
112 86
 	/**
@@ -132,7 +106,8 @@ class Sequence extends AbstractUploadableEntity
132 106
 	 *
133 107
 	 * @return int
134 108
 	 */
135
-	public function getSequenceId() {
109
+	public function getSequenceId()
110
+	{
136 111
 		return $this->sequenceId;
137 112
 	}
138 113
 
@@ -140,7 +115,8 @@ class Sequence extends AbstractUploadableEntity
140 115
 	 *
141 116
 	 * @return string
142 117
 	 */
143
-	public function getTitle() {
118
+	public function getTitle()
119
+	{
144 120
 		return $this->title;
145 121
 	}
146 122
 
@@ -148,7 +124,8 @@ class Sequence extends AbstractUploadableEntity
148 124
 	 *
149 125
 	 * @param string $title
150 126
 	 */
151
-	public function setTitle($title) {
127
+	public function setTitle($title)
128
+	{
152 129
 		$this->title = $title;
153 130
 	}
154 131
 
@@ -156,7 +133,8 @@ class Sequence extends AbstractUploadableEntity
156 133
 	 *
157 134
 	 * @return string
158 135
 	 */
159
-	public function getDescription() {
136
+	public function getDescription()
137
+	{
160 138
 		return $this->description;
161 139
 	}
162 140
 
@@ -164,7 +142,8 @@ class Sequence extends AbstractUploadableEntity
164 142
 	 *
165 143
 	 * @param string $description
166 144
 	 */
167
-	public function setDescription($description) {
145
+	public function setDescription($description)
146
+	{
168 147
 		$this->description = $description;
169 148
 	}
170 149
 
@@ -192,7 +171,7 @@ class Sequence extends AbstractUploadableEntity
192 171
 	 */
193 172
 	public function hasSequenceItems()
194 173
 	{
195
-		if (count ($this->getSequenceItems()) > 0) {
174
+		if (count($this->getSequenceItems()) > 0) {
196 175
 			return true;
197 176
 		}
198 177
 		return false;
@@ -217,10 +196,10 @@ class Sequence extends AbstractUploadableEntity
217 196
 		$this->sequenceItems = $sequenceItems;
218 197
 		return $this;
219 198
 	}
220
-	
199
+
221 200
 	/**
222
-	* @param SequenceItem $sequenceItem
223
-	*/
201
+	 * @param SequenceItem $sequenceItem
202
+	 */
224 203
 	public function addSequenceItem($sequenceItem)
225 204
 	{
226 205
 		if ($this->sequenceItems->contains($sequenceItem)) {
@@ -240,10 +219,8 @@ class Sequence extends AbstractUploadableEntity
240 219
 
241 220
 		$sequenceItems = $this->getSequenceItems();
242 221
 
243
-		foreach ($sequenceItems as $sequenceItem)
244
-		{
245
-			if ($sequenceItem instanceof ActivityInterface)
246
-			{
222
+		foreach ($sequenceItems as $sequenceItem) {
223
+			if ($sequenceItem instanceof ActivityInterface) {
247 224
 				$number++;
248 225
 			}
249 226
 		}
@@ -261,10 +238,8 @@ class Sequence extends AbstractUploadableEntity
261 238
 
262 239
 		$sequenceItems = $this->getSequenceItems();
263 240
 
264
-		foreach ($sequenceItems as $sequenceItem)
265
-		{
266
-			if ($sequenceItem instanceof EventInterface || $sequenceItem instanceof ActivityInterface)
267
-			{
241
+		foreach ($sequenceItems as $sequenceItem) {
242
+			if ($sequenceItem instanceof EventInterface || $sequenceItem instanceof ActivityInterface) {
268 243
 				$number++;
269 244
 			}
270 245
 		}
@@ -282,10 +257,8 @@ class Sequence extends AbstractUploadableEntity
282 257
 
283 258
 		$sequenceItems = $this->getSequenceItems();
284 259
 
285
-		foreach ($sequenceItems as $sequenceItem)
286
-		{
287
-			if ($sequenceItem instanceof EventInterface)
288
-			{
260
+		foreach ($sequenceItems as $sequenceItem) {
261
+			if ($sequenceItem instanceof EventInterface) {
289 262
 				$number++;
290 263
 			}
291 264
 		}
@@ -304,10 +277,8 @@ class Sequence extends AbstractUploadableEntity
304 277
 
305 278
 		$sequenceItems = $this->getSequenceItems();
306 279
 
307
-		foreach ($sequenceItems as $sequenceItem)
308
-		{
309
-			if ($sequenceItem instanceof EventInterface && $sequenceItem->getStartTime() != null && $now > $sequenceItem->getStartTime())
310
-			{
280
+		foreach ($sequenceItems as $sequenceItem) {
281
+			if ($sequenceItem instanceof EventInterface && $sequenceItem->getStartTime() != null && $now > $sequenceItem->getStartTime()) {
311 282
 				$number++;
312 283
 			}
313 284
 		}
@@ -325,10 +296,8 @@ class Sequence extends AbstractUploadableEntity
325 296
 
326 297
 		$sequenceItems = $this->getSequenceItems();
327 298
 
328
-		foreach ($sequenceItems as $sequenceItem)
329
-		{
330
-			if ($sequenceItem instanceof EventInterface)
331
-			{
299
+		foreach ($sequenceItems as $sequenceItem) {
300
+			if ($sequenceItem instanceof EventInterface) {
332 301
 				if ($sequenceItem->isReadyToBePlayed() === true) {
333 302
 					$number++;
334 303
 				}
@@ -348,10 +317,8 @@ class Sequence extends AbstractUploadableEntity
348 317
 
349 318
 		$sequenceItems = $this->getSequenceItems();
350 319
 
351
-		foreach ($sequenceItems as $sequenceItem)
352
-		{
353
-			if ($sequenceItem instanceof ActivityInterface)
354
-			{
320
+		foreach ($sequenceItems as $sequenceItem) {
321
+			if ($sequenceItem instanceof ActivityInterface) {
355 322
 				if ($sequenceItem->isReadyToBePlayed() === true) {
356 323
 					$number++;
357 324
 				}
@@ -359,14 +326,14 @@ class Sequence extends AbstractUploadableEntity
359 326
 		}
360 327
 		return $number;
361 328
 	}
362
-	
329
+
363 330
 	/**
364 331
 	 * Set the value of updateDate
365 332
 	 *
366 333
 	 * @param  \DateTime  $updateDate
367 334
 	 *
368 335
 	 * @return  self
369
-	 */ 
336
+	 */
370 337
 	public function setUpdateDate(\DateTime $updateDate)
371 338
 	{
372 339
 		$this->updateDate = $updateDate;
@@ -386,34 +353,34 @@ class Sequence extends AbstractUploadableEntity
386 353
 	}
387 354
 
388 355
 	public function setImageFile(?File $image = null)
389
-    {
390
-        $this->imageFile = $image;
356
+	{
357
+		$this->imageFile = $image;
391 358
 
392
-        if ($image) {
359
+		if ($image) {
393 360
 
394 361
 			/**
395 362
 			 * Ce champ n'est pas mappé dans la DB donc il est nécessaire
396 363
 			 * de mettre à jour un champ mappé pour que Dotrine fasse
397 364
 			 * son job de mise à jour.
398 365
 			 */
399
-            $this->updateDate = new \DateTime('now');
400
-        }
401
-    }
366
+			$this->updateDate = new \DateTime('now');
367
+		}
368
+	}
402 369
 
403 370
 	/**
404 371
 	 * retourne le fichier de l'image
405 372
 	 * @return string
406 373
 	 */
407
-    public function getImageFile()
408
-    {
409
-        return $this->imageFile;
410
-    }
374
+	public function getImageFile()
375
+	{
376
+		return $this->imageFile;
377
+	}
411 378
 
412 379
 	/**
413 380
 	 * Get the value of image
414 381
 	 *
415 382
 	 * @return  string
416
-	 */ 
383
+	 */
417 384
 	public function getImage()
418 385
 	{
419 386
 		return $this->image;
@@ -425,7 +392,7 @@ class Sequence extends AbstractUploadableEntity
425 392
 	 * @param  string  $image
426 393
 	 *
427 394
 	 * @return  self
428
-	 */ 
395
+	 */
429 396
 	public function setImage($image)
430 397
 	{
431 398
 		$this->image = $image;
@@ -445,20 +412,19 @@ class Sequence extends AbstractUploadableEntity
445 412
 			'image/gif'
446 413
 		);
447 414
 	}
448
-	
449
-	public function getDownloadName($property = "name",$includeExtension = true)
415
+
416
+	public function getDownloadName($property = "name", $includeExtension = true)
450 417
 	{
451 418
 		$path = $this->image;
452 419
 		$infos = pathinfo($path);
453 420
 
454 421
 		$endName = $this->sanitizeFileName($this->getTitle());
455
-		if (!$includeExtension)
456
-		{
422
+		if (!$includeExtension) {
457 423
 			return $endName;
458 424
 		}
459
-		return $endName .  ".". $infos['extension'];
425
+		return $endName .  "." . $infos['extension'];
460 426
 	}
461
-	
427
+
462 428
 	/**
463 429
 	 * Definit si une sequence est verouillée (en fonction du sequence item conditionable qui la précède)
464 430
 	 */
@@ -467,9 +433,8 @@ class Sequence extends AbstractUploadableEntity
467 433
 		// Recuperation du sequenceItem portant la condition sur celle là
468 434
 		$repository = $this->entityManager->getRepository(SequenceItem::class);
469 435
 		$sequenceItemCondition = $repository->findReferringSequenceItemOfTheConditionBySequence($this);
470
-		
471
-		if ($sequenceItemCondition)
472
-		{
436
+
437
+		if ($sequenceItemCondition) {
473 438
 			$information = $sequenceItemCondition->getInfoLocked($learner);
474 439
 			return $information['locked'];
475 440
 		}
@@ -488,14 +453,11 @@ class Sequence extends AbstractUploadableEntity
488 453
 		$totalSeconds = 0;
489 454
 
490 455
 		$sequenceItems = $this->getSequenceItems();
491
-		
492
-		foreach ($sequenceItems as $sequenceItem)
493
-		{
494
-			if ($sequenceItem->getSequenceItemId())
495
-			{
456
+
457
+		foreach ($sequenceItems as $sequenceItem) {
458
+			if ($sequenceItem->getSequenceItemId()) {
496 459
 				$sequenceItemDuration = $sequenceItem->getDuration();
497
-				if ($sequenceItemDuration)
498
-				{
460
+				if ($sequenceItemDuration) {
499 461
 					$totalSeconds += $sequenceItemDuration->h * 3600;
500 462
 					$totalSeconds += $sequenceItemDuration->i * 60;
501 463
 					$totalSeconds += $sequenceItemDuration->s;
@@ -503,8 +465,8 @@ class Sequence extends AbstractUploadableEntity
503 465
 			}
504 466
 		}
505 467
 
506
-		$dateIntervalReturn = new \DateInterval('PT'.$totalSeconds.'S');
507
-		
468
+		$dateIntervalReturn = new \DateInterval('PT' . $totalSeconds . 'S');
469
+
508 470
 		return $dateIntervalReturn;
509
-	}	
471
+	}
510 472
 }

+ 1
- 1
src/Phoenix/Entity/SequenceItem/ConditionPursuit.php View File

@@ -30,7 +30,7 @@ implements ConditionInterface
30 30
 	}
31 31
 
32 32
 	/**
33
-	 *
33
+	 * @deprecated use LearningPathHandler:duplicateTemplate instead
34 34
 	 */
35 35
 	public function __clone()
36 36
 	{

+ 1
- 1
src/Phoenix/Entity/SequenceItem/SequenceItem.php View File

@@ -113,7 +113,7 @@ abstract class SequenceItem extends AbstractUploadableEntity
113 113
 	}
114 114
 
115 115
 	/**
116
-	 *
116
+	 * @deprecated use LearningPathHandler:duplicateTemplate instead
117 117
 	 */
118 118
 	public function __clone()
119 119
 	{

+ 15
- 18
src/Phoenix/Repository/LearningSpace/LearnerAccountRepository.php View File

@@ -1,4 +1,5 @@
1 1
 <?php
2
+
2 3
 namespace Logipro\Phoenix\Repository\LearningSpace;
3 4
 
4 5
 use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
@@ -25,43 +26,39 @@ class LearnerAccountRepository extends ServiceEntityRepository implements UserLo
25 26
 	}
26 27
 
27 28
 	public function loadUserByUsername($username)
28
-    {
29
+	{
29 30
 		// recherche l'utilisateur par identifiant
30 31
 		$query = $this->createQueryBuilder('u')
31
-		->where('u.username = :username')
32
-		->setParameter('username', $username)
33
-		->getQuery();
32
+			->where('u.username = :username')
33
+			->setParameter('username', $username)
34
+			->getQuery();
34 35
 
35 36
 		// si 1 seul utilisateur trouvé => on le retourne
36 37
 		// si aucun ou plusieur, on regarde l'email
37 38
 		$users = $query->execute();
38
-		if ($users)
39
-		{
39
+		if ($users) {
40 40
 			// multi utilisateur = erreur
41
-			if (count($users) == 1)
42
-			{
41
+			if (count($users) == 1) {
43 42
 				return reset($users);
44 43
 			}
45 44
 		}
46 45
 
47 46
 		// recherche l'utilisateur par email
48
- 		$query = $this->createQueryBuilder('u')
49
-		->where('u.email = :email')
50
-		->setParameter('email', $username)
51
-		->getQuery();
47
+		$query = $this->createQueryBuilder('u')
48
+			->where('u.email = :email')
49
+			->setParameter('email', $username)
50
+			->getQuery();
52 51
 
53 52
 		$users = $query->execute();
54
-		if (!$users)
55
-		{
53
+		if (!$users) {
56 54
 			return null;
57 55
 		}
58 56
 
59 57
 		// multi utilisateur = erreur
60
-		if (count($users) > 1)
61
-		{
62
-            throw new \Exception('Cette adresse email correspond à plusieurs utilisateurs, veuillez saisir votre identifiant.');
58
+		if (count($users) > 1) {
59
+			throw new \Exception('Cette adresse email correspond à plusieurs utilisateurs, veuillez saisir votre identifiant.');
63 60
 		}
64 61
 
65 62
 		return reset($users);
66
-    }
63
+	}
67 64
 }

+ 110
- 22
src/Phoenix/Service/EntityDuplicator.php View File

@@ -1,4 +1,5 @@
1 1
 <?php
2
+
2 3
 namespace Logipro\Phoenix\Service;
3 4
 
4 5
 use Doctrine\Common\Persistence\ManagerRegistry;
@@ -12,6 +13,9 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface;
12 13
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
13 14
 use Symfony\Component\DependencyInjection\ContainerInterface;
14 15
 use Logipro\Phoenix\Entity\Session\Session;
16
+use Symfony\Component\Security\Core\Security;
17
+use Logipro\Phoenix\Entity\ProgramItem;
18
+use Doctrine\Common\Collections\ArrayCollection;
15 19
 
16 20
 /**
17 21
  * Service de duplication des entités.
@@ -20,52 +24,136 @@ class EntityDuplicator implements ContainerAwareInterface
20 24
 {
21 25
     use ContainerAwareTrait;
22 26
 
27
+    private $security;
28
+    private $fileUploader;
29
+
23 30
     /**
24 31
      * Constructeur.
25 32
      * @param ContainerInterface $container
26 33
      */
27
-    public function __construct(ContainerInterface $container)
28
-    {
34
+    public function __construct(
35
+        ContainerInterface $container,
36
+        Security $security,
37
+        FileUploader $fileUploader
38
+    ) {
29 39
         $this->setContainer($container);
40
+        $this->security = $security;
41
+        $this->fileUploader = $fileUploader;
30 42
     }
31 43
 
32 44
     /**
33 45
      * Duplique un parcours
34 46
      *
35 47
      * @param LearningPath $learningPath
36
-     * @param bool $isTemplate
37 48
      * @return LearningPath
38 49
      */
39
-    public function duplicateLearningPath(LearningPath $learningPath,Session $session, bool $isTemplate = false)
50
+    public function duplicateLearningPath(LearningPath $learningPath, Session $session = null, bool $isTemplate = false): LearningPath
40 51
     {
41 52
         $em = $this->getDoctrine()->getManager();
42 53
 
43 54
         $newLearningPath = null;
44 55
 
45
-        // Si le parcours existe
46
-        if (!empty($learningPath)) {
47
-            // Si c'est une duplication de template
48
-            if ($isTemplate) {
49
-                $title = '[copie] '.$learningPath->getTitle();
50
-                $newLearningPath = $learningPath->cloneThisTemplate(true);
51
-                $newLearningPath->setTitle($title);
52
-            } else {
53
-                $newLearningPath = $learningPath->cloneThisTemplate();
54
-            }
56
+        // Si c'est une duplication de template
57
+        if ($isTemplate) {
58
+            $title = '[copie] ' . $learningPath->getTitle();
59
+            $newLearningPath = $this->cloneLearningPath($learningPath, true);
60
+            $newLearningPath->setTitle($title);
61
+        } else {
62
+            $newLearningPath = $this->cloneLearningPath($learningPath);
63
+        }
64
+
65
+        $autor = $this->security->getUser();
66
+        $newLearningPath->setCreatingUser($autor);
55 67
 
68
+        if ($session) {
56 69
             $newLearningPath->setSession($session);
57 70
             $session->setLearningPath($newLearningPath);
71
+        }
72
+
73
+        $em->persist($newLearningPath);
74
+        $em->flush();
58 75
 
59
-            $em->persist($newLearningPath);
60
-            $em->flush();
76
+        // Duplication des cours au niveau du moteur SCORM
77
+        $this->duplicateScormCoursesFromPaths($learningPath, $newLearningPath);
61 78
 
62
-            // Duplication des cours au niveau du moteur SCORM
63
-            $this->duplicateScormCoursesFromPaths($learningPath, $newLearningPath);
64
-            
65
-            return $newLearningPath;
79
+        return $newLearningPath;
80
+    }
81
+
82
+    private function cloneLearningPath(LearningPath $learningPath, bool $isTemplate = false): LearningPath
83
+    {
84
+        //FIXME Peut-on copier un parcours qui n'est pas un gabarit ?
85
+        if (!$learningPath->isTemplate()) {
86
+            throw new PhoenixException('Ce parcours n\'est pas un gabarit');
66 87
         }
67 88
 
68
-        throw new PhoenixException($this->container->get('translator')->trans('exception_duplicator_no_learning_path'));
89
+        // Clonage du parcours (et de ses éléments enfants en cascade)
90
+        $pathCopy = clone $learningPath;
91
+
92
+        // Copie les séquences
93
+        $sequences = new ArrayCollection();
94
+        foreach ($learningPath->getSequences() as $sequence) {
95
+            $newSequence = $this->cloneSequence($sequence);
96
+            $newSequence->setLearningPath($pathCopy);
97
+
98
+            $sequences->add($newSequence);
99
+        }
100
+        $pathCopy->setSequences($sequences);
101
+
102
+        // Copie les éléments de parcours
103
+        $programItems = new ArrayCollection();
104
+        foreach ($learningPath->getProgramItems() as $programItem) {
105
+            $newProgramItem = $this->cloneProgramItem($programItem);
106
+            $newProgramItem->setLearningPath($pathCopy);
107
+
108
+            $programItems->add($newProgramItem);
109
+        }
110
+        $pathCopy->setProgramItems($programItems);
111
+
112
+        $pathCopy->setTemplate($learningPath);
113
+        $pathCopy->setIsTemplate($isTemplate);
114
+
115
+        return $pathCopy;
116
+    }
117
+
118
+    private function cloneSequence(Sequence $sequence): Sequence
119
+    {
120
+        $sequenceCopy = clone $sequence;
121
+        // Copie les éléments
122
+        $items = new ArrayCollection();
123
+        $counter = 0;
124
+        foreach ($sequence->getSequenceItems() as $item) {
125
+
126
+            if ($item->getItemType() == 'conditionpursuit') {
127
+                $previousSequenceItem = ($items->get($counter - 1));
128
+                $item->setAssociatedSequenceItem($previousSequenceItem);
129
+                $newItem = $this->cloneSequenceItem($item);
130
+                $newItem->setSequence($sequenceCopy);
131
+            } else {
132
+                $newItem = clone $item;
133
+                $newItem->setSequence($sequenceCopy);
134
+            }
135
+            $items->add($newItem);
136
+            $counter++;
137
+        }
138
+        $sequenceCopy->setSequenceItems($items->toArray());
139
+
140
+        // clone l'image de la sequence
141
+        $name = $this->fileUploader->cloneFile($sequence, 'image');
142
+        $sequenceCopy->setImage($name);
143
+
144
+        return $sequenceCopy;
145
+    }
146
+
147
+    private function cloneProgramItem(ProgramItem $programItem): ProgramItem
148
+    {
149
+        // FIXME à corriger
150
+        return clone $programItem;
151
+    }
152
+
153
+    private function cloneSequenceItem(SequenceItem $sequenceItem): SequenceItem
154
+    {
155
+        // FIXME à corriger
156
+        return clone $sequenceItem;
69 157
     }
70 158
 
71 159
     /**
@@ -115,7 +203,7 @@ class EntityDuplicator implements ContainerAwareInterface
115 203
             // Pour chaque item
116 204
             for ($j = 0; $j < count($originalItems); ++$j) {
117 205
                 $original = $originalItems[$j];
118
-                
206
+
119 207
                 if ($original instanceof ActivityPackage && $original->getPackageType() == AbstractPackageFile::TYPE_SCORM2004) {
120 208
                     $new  = $newItems[$j];
121 209
                     $response = $scormEngine->duplicateCourse($original->getSequenceItemId(), $new->getSequenceItemId());

+ 71
- 13
src/Phoenix/Service/FileUploader.php View File

@@ -6,22 +6,26 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface;
6 6
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
7 7
 use Symfony\Component\DependencyInjection\ContainerInterface;
8 8
 use Logipro\Phoenix\Service\FileUploaderInterface;
9
+use Symfony\Component\Filesystem\Filesystem;
10
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
11
+use Vich\UploaderBundle\Mapping\PropertyMappingFactory;
12
+use Logipro\Phoenix\Entity\Common\AbstractEntity;
9 13
 
10 14
 /**
11 15
  * Service de gestion des fichiers uploadés.
12 16
  */
13 17
 class FileUploader
14
-implements ContainerAwareInterface, FileUploaderInterface
18
+implements FileUploaderInterface
15 19
 {
16
-	use ContainerAwareTrait;
20
+	private $params;
17 21
 
18 22
 	/**
19 23
 	 * Constructeur.
20
-	 * @param ContainerInterface $container
21 24
 	 */
22
-	public function __construct(ContainerInterface $container)
25
+	public function __construct(ParameterBagInterface $params, PropertyMappingFactory $factory)
23 26
 	{
24
-		$this->setContainer($container);
27
+		$this->params = $params;
28
+		$this->factory = $factory;
25 29
 	}
26 30
 
27 31
 	/**
@@ -66,7 +70,7 @@ implements ContainerAwareInterface, FileUploaderInterface
66 70
 	 */
67 71
 	protected function getUploadPublicRootPath()
68 72
 	{
69
-		return $this->container->getParameter('phoenix.upload_public_directory');
73
+		return $this->params->get('phoenix.upload_public_directory');
70 74
 	}
71 75
 
72 76
 	/**
@@ -75,7 +79,7 @@ implements ContainerAwareInterface, FileUploaderInterface
75 79
 	 */
76 80
 	protected function getUploadPublicRootUrl()
77 81
 	{
78
-		return $this->container->getParameter('phoenix.upload_public_url');
82
+		return $this->params->get('phoenix.upload_public_url');
79 83
 	}
80 84
 
81 85
 	/**
@@ -84,7 +88,7 @@ implements ContainerAwareInterface, FileUploaderInterface
84 88
 	 */
85 89
 	public function getUploadTmpDir()
86 90
 	{
87
-		return $this->container->getParameter('phoenix.upload_tmp_directory');
91
+		return $this->params->get('phoenix.upload_tmp_directory');
88 92
 	}
89 93
 
90 94
 	/**
@@ -93,14 +97,27 @@ implements ContainerAwareInterface, FileUploaderInterface
93 97
 	 * @param string $path Chemin relatif du fichier
94 98
 	 * @return string
95 99
 	 */
96
-	public function buildUploadPublicPath(string $path)
100
+	public function buildUploadPublicPath(string $path = null, AbstractEntity $entity = null, string $propertyName = null)
97 101
 	{
98
-		$pathPrefix = $this->getUploadPublicRootPath();
99
-
100 102
 		// Elimine le slash au début
101
-		$path = ltrim($path, '/');
103
+		$path = ltrim($path, DIRECTORY_SEPARATOR);
102 104
 
103
-		return sprintf('%s/%s', $pathPrefix, $path);
105
+		if ($entity && $propertyName) {
106
+			// ici, sert uniquement à tester si la méthode de l'entité existe
107
+			$this->getEntityValue($entity, $propertyName);
108
+
109
+			// plugin vich uploader
110
+			$mapping = $this->factory->fromObject($entity);
111
+			foreach ($mapping as $map) {
112
+
113
+				if ($map->getFileNamePropertyName() == $propertyName) {
114
+					$pathPrefix = $map->getUploadDestination();
115
+					return $pathPrefix . DIRECTORY_SEPARATOR . $path;
116
+				}
117
+			}
118
+		}
119
+		$pathPrefix = $this->getUploadPublicRootPath();
120
+		return $pathPrefix . DIRECTORY_SEPARATOR . $path;
104 121
 	}
105 122
 
106 123
 	/**
@@ -119,6 +136,14 @@ implements ContainerAwareInterface, FileUploaderInterface
119 136
 		return sprintf('%s/%s', $pathPrefix, $path);
120 137
 	}
121 138
 
139
+	public function getUploadPublicPath(AbstractEntity $entity, string $propertyName)
140
+	{
141
+		$oldFileName = $this->getEntityValue($entity, $propertyName);
142
+
143
+		// fonctionnement normal
144
+		return $this->buildUploadPublicPath($oldFileName, $entity, $propertyName);
145
+	}
146
+
122 147
 	/**
123 148
 	 * Retourne l'URL complète vers un fichier à partir de son chemin
124 149
 	 * relatif dans le répertoire public des uploads.
@@ -134,4 +159,37 @@ implements ContainerAwareInterface, FileUploaderInterface
134 159
 
135 160
 		return sprintf('%s/%s', $urlPrefix, $path);
136 161
 	}
162
+
163
+	private function getEntityValue(AbstractEntity $entity, string $propertyName)
164
+	{
165
+		// test le lien entre le propriété et l'objet
166
+		$getter = 'get' . ucfirst($propertyName);
167
+		if (!method_exists($entity, $getter)) {
168
+			throw new \Exception();
169
+		}
170
+		return $entity->{$getter}();
171
+	}
172
+
173
+	public function generateNewName(string $oldFileName): string
174
+	{
175
+		return uniqid() . '.' . pathinfo($oldFileName, PATHINFO_EXTENSION);
176
+	}
177
+
178
+	public function cloneFile(AbstractEntity $entity, string $propertyName): string
179
+	{
180
+		$filesystem = new Filesystem();
181
+		$oldFileName = $this->getEntityValue($entity, $propertyName);
182
+		$newFileName = $this->generateNewName($oldFileName);
183
+
184
+		try {
185
+			$filesystem->copy(
186
+				$this->buildUploadPublicPath($oldFileName, $entity, $propertyName),
187
+				$this->buildUploadPublicPath($newFileName, $entity, $propertyName)
188
+			);
189
+		} catch (\Exception $exp) {
190
+			dd($exp->getMessage());
191
+		}
192
+
193
+		return $newFileName;
194
+	}
137 195
 }

+ 6
- 10
src/Phoenix/Service/LearningPath/LearningPathHandler.php View File

@@ -9,6 +9,7 @@ use Symfony\Component\Security\Core\Security;
9 9
 use Logipro\Phoenix\Exception\PhoenixException;
10 10
 use Symfony\Component\Translation\TranslatorInterface;
11 11
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
12
+use Logipro\Phoenix\Service\EntityDuplicator;
12 13
 
13 14
 class LearningPathHandler
14 15
 {
@@ -19,12 +20,14 @@ class LearningPathHandler
19 20
 		EntityManagerInterface $manager,
20 21
 		TranslatorInterface $translator,
21 22
 		FlashPhxHandler $flashPhxHandler,
22
-		Security $security
23
+		Security $security,
24
+		EntityDuplicator $entityDuplicator
23 25
 	) {
24 26
 		$this->manager = $manager;
25 27
 		$this->translator = $translator;
26 28
 		$this->flashPhxHandler = $flashPhxHandler;
27 29
 		$this->security = $security;
30
+		$this->entityDuplicator = $entityDuplicator;
28 31
 	}
29 32
 
30 33
 
@@ -59,7 +62,7 @@ class LearningPathHandler
59 62
 	 * @param int $id
60 63
 	 * @return LearningPath|null
61 64
 	 */
62
-	public function duplicateTemplate($id): LearningPath
65
+	public function duplicateTemplate($id)
63 66
 	{
64 67
 		$learningPath = $this->requireLearningPath($id);
65 68
 
@@ -70,15 +73,8 @@ class LearningPathHandler
70 73
 			}
71 74
 
72 75
 			// Clonage du gabarit
73
-
74 76
 			$title = '[' . $this->translator->trans('prefix_learning_path_title_duplicated') . '] ' . $learningPath->getTitle();
75
-			$newLearningPath = $learningPath->cloneThisTemplate(true);
76
-			$newLearningPath->setTitle($title);
77
-			$autor = $this->security->getUser();
78
-			$newLearningPath->setCreatingUser($autor);
79
-
80
-			$this->manager->persist($newLearningPath);
81
-			$this->manager->flush();
77
+			$newLearningPath = $this->entityDuplicator->duplicateLearningPath($learningPath, null, $learningPath->isTemplate());
82 78
 
83 79
 			$this->flashPhxHandler->addFlash('success', $this->translator->trans('success_learning_path_duplicated'));
84 80
 

+ 2
- 3
templates/bundles/TwigBundle/Exception/error404.html.twig View File

@@ -15,10 +15,9 @@
15 15
 			
16 16
 			{% block content %}
17 17
 				{% set text = exception.message %}
18
-				{% if text == '' %}
19
-					{% set text = 'error_404_funny_text'|trans %}
18
+				{% if text != '' %}
19
+					<p class="h3">{{ text }}</p>
20 20
 				{% endif %}
21
-				<p class="h3">{{ text }}</p>
22 21
 
23 22
 				<p class="">
24 23
 					<a href="{{ path('bo_homepage') }}">{{ 'action_go_back_home'|trans }}</a>

+ 2
- 2
tests/Phoenix/Component/ExpiredPhxCrypt.php View File

@@ -9,7 +9,7 @@ class ExpiredPhxCrypt extends PhxCrypt
9 9
     protected static function serialize($data): string
10 10
     {
11 11
         $data['_d_'] = 0;
12
-        
12
+
13 13
         return parent::serialize($data);
14 14
     }
15
-}
15
+}

+ 4
- 3
tests/Phoenix/Controller/NewBackOffice/Session/Follow/SessionFixedControllerTest.php View File

@@ -1,9 +1,11 @@
1 1
 <?php
2
+
2 3
 namespace Logipro\Phoenix\tests\Phoenix\Controller\NewBackOffice\Session\Follow;
3 4
 
4 5
 use Logipro\Phoenix\tests\PhxWebTestCase;
6
+
5 7
 class SessionFiexedControllerTest extends PhxWebTestCase
6
-{  
8
+{
7 9
 
8 10
     /*
9 11
     // A TERMINER IL Y A UN PB AVEC L'AUTHENTIFCATION
@@ -18,5 +20,4 @@ class SessionFiexedControllerTest extends PhxWebTestCase
18 20
 
19 21
         $this->assertSame(200, $client->getResponse()->getStatusCode());
20 22
     }
21
-    */   
22
-}
23
+    */ }

+ 28
- 18
tests/Phoenix/Service/Comment/CommentHandlerTest.php View File

@@ -1,4 +1,5 @@
1 1
 <?php
2
+
2 3
 namespace Logipro\Phoenix\tests\Phoenix\Service\Comment;
3 4
 
4 5
 use PHPUnit\Framework\TestCase;
@@ -16,26 +17,28 @@ use Twig\Environment;
16 17
 use Doctrine\ORM\EntityManagerInterface;
17 18
 use Symfony\Component\Security\Core\Security;
18 19
 
20
+use Logipro\Phoenix\Service\Session\SessionHandler;
21
+
19 22
 class CommentHandlerTest extends TestCase
20
-{	
23
+{
21 24
 	private $comment;
22 25
 	private $session;
23 26
 	private $person;
24 27
 
25 28
 
26
-    private $entityManager;
29
+	private $entityManager;
27 30
 
28
-    public function setUp()
29
-    {
31
+	public function setUp()
32
+	{
30 33
 		$this->person = new Person();
31 34
 		$this->person->setGivenName('Laurent');
32 35
 
33
-        $this->session = new SessionOpen();
36
+		$this->session = new SessionOpen();
34 37
 		$this->session->setTitle('Test CommentHandlerTest');
35 38
 
36 39
 		$this->comment = new Comment($this->person);
37 40
 		$this->comment->setContent('Mon commentaire');
38
-		$this->comment->setSession($this->session);	
41
+		$this->comment->setSession($this->session);
39 42
 	}
40 43
 
41 44
 	/**
@@ -54,10 +57,11 @@ class CommentHandlerTest extends TestCase
54 57
 		$translator = $this->createMock(TranslatorInterface::class);
55 58
 		$templing = $this->createMock(Environment::class);
56 59
 		$security = $this->createMock(Security::class);
60
+		$sessionHandler = $this->createMock(SessionHandler::class);
57 61
 
58
-		$commentHandler = new CommentHandler($dispatcher,$entityManager,$translator,$templing,$security);
59
-		$commentHandler->sendNotificationNewComment($this->comment, $this->session, 'session');	
60
-	}		
62
+		$commentHandler = new CommentHandler($dispatcher, $entityManager, $translator, $templing, $security, $sessionHandler);
63
+		$commentHandler->sendNotificationNewComment($this->comment, $this->session, 'session');
64
+	}
61 65
 
62 66
 	/**
63 67
 	 * Test pour verifier que le mail nouveau commentaire ne soit pas envoyé si l'option notifyMailForum est à false
@@ -75,8 +79,10 @@ class CommentHandlerTest extends TestCase
75 79
 		$templing = $this->createMock(Environment::class);
76 80
 		$security = $this->createMock(Security::class);
77 81
 
78
-		$commentHandler = new CommentHandler($dispatcher,$entityManager,$translator,$templing,$security);
79
-		$commentHandler->sendNotificationNewComment($this->comment, $this->session, 'session');	
82
+		$sessionHandler = $this->createMock(SessionHandler::class);
83
+
84
+		$commentHandler = new CommentHandler($dispatcher, $entityManager, $translator, $templing, $security, $sessionHandler);
85
+		$commentHandler->sendNotificationNewComment($this->comment, $this->session, 'session');
80 86
 	}
81 87
 
82 88
 
@@ -96,9 +102,11 @@ class CommentHandlerTest extends TestCase
96 102
 		$templing = $this->createMock(Environment::class);
97 103
 		$security = $this->createMock(Security::class);
98 104
 
99
-		$commentHandler = new CommentHandler($dispatcher,$entityManager,$translator,$templing,$security);
100
-		$commentHandler->sendNotificationNewComment($this->comment, $this->session, 'session');	
101
-	}	
105
+		$sessionHandler = $this->createMock(SessionHandler::class);
106
+
107
+		$commentHandler = new CommentHandler($dispatcher, $entityManager, $translator, $templing, $security, $sessionHandler);
108
+		$commentHandler->sendNotificationNewComment($this->comment, $this->session, 'session');
109
+	}
102 110
 
103 111
 	/**
104 112
 	 * Test pour verifier que le mail nouveau commentaire ne soit pas envoyé si il n'y a pas de responsable pédagogique
@@ -116,7 +124,9 @@ class CommentHandlerTest extends TestCase
116 124
 		$templing = $this->createMock(Environment::class);
117 125
 		$security = $this->createMock(Security::class);
118 126
 
119
-		$commentHandler = n