diff options
author | lizyn <zhiylin@outlook.com> | 2020-02-24 10:04:13 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-24 10:04:13 +0800 |
commit | b19df31d78d881a43bcf6b3215e5fc3781e8e8aa (patch) | |
tree | d0d9861694a4b5e5fbfdbeb53c255ecd15fe9328 /src/Wallabag/CoreBundle/Controller/ConfigController.php | |
parent | 4d0c632c70ea50d459c3c55ddda2e0f394dd51cb (diff) | |
parent | 04d918cae0227c06a41d27fb6533dddbf30dfe71 (diff) | |
download | wallabag-b19df31d78d881a43bcf6b3215e5fc3781e8e8aa.tar.gz wallabag-b19df31d78d881a43bcf6b3215e5fc3781e8e8aa.tar.zst wallabag-b19df31d78d881a43bcf6b3215e5fc3781e8e8aa.zip |
Merge pull request #1 from wallabag/master
Keep up with the master again
Diffstat (limited to 'src/Wallabag/CoreBundle/Controller/ConfigController.php')
-rw-r--r-- | src/Wallabag/CoreBundle/Controller/ConfigController.php | 278 |
1 files changed, 242 insertions, 36 deletions
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index b999c539..6655ef93 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php | |||
@@ -2,17 +2,23 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Controller; | 3 | namespace Wallabag\CoreBundle\Controller; |
4 | 4 | ||
5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use JMS\Serializer\SerializationContext; |
6 | use JMS\Serializer\SerializerBuilder; | ||
7 | use PragmaRX\Recovery\Recovery as BackupCodes; | ||
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 8 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
7 | use Symfony\Component\HttpFoundation\JsonResponse; | 9 | use Symfony\Component\HttpFoundation\JsonResponse; |
8 | use Symfony\Component\HttpFoundation\RedirectResponse; | 10 | use Symfony\Component\HttpFoundation\RedirectResponse; |
9 | use Symfony\Component\HttpFoundation\Request; | 11 | use Symfony\Component\HttpFoundation\Request; |
12 | use Symfony\Component\HttpFoundation\Response; | ||
10 | use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | 13 | use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; |
14 | use Symfony\Component\Routing\Annotation\Route; | ||
15 | use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint; | ||
11 | use Wallabag\CoreBundle\Entity\Config; | 16 | use Wallabag\CoreBundle\Entity\Config; |
12 | use Wallabag\CoreBundle\Entity\TaggingRule; | 17 | use Wallabag\CoreBundle\Entity\TaggingRule; |
13 | use Wallabag\CoreBundle\Form\Type\ChangePasswordType; | 18 | use Wallabag\CoreBundle\Form\Type\ChangePasswordType; |
14 | use Wallabag\CoreBundle\Form\Type\ConfigType; | 19 | use Wallabag\CoreBundle\Form\Type\ConfigType; |
15 | use Wallabag\CoreBundle\Form\Type\RssType; | 20 | use Wallabag\CoreBundle\Form\Type\FeedType; |
21 | use Wallabag\CoreBundle\Form\Type\TaggingRuleImportType; | ||
16 | use Wallabag\CoreBundle\Form\Type\TaggingRuleType; | 22 | use Wallabag\CoreBundle\Form\Type\TaggingRuleType; |
17 | use Wallabag\CoreBundle\Form\Type\UserInformationType; | 23 | use Wallabag\CoreBundle\Form\Type\UserInformationType; |
18 | use Wallabag\CoreBundle\Tools\Utils; | 24 | use Wallabag\CoreBundle\Tools\Utils; |
@@ -20,8 +26,6 @@ use Wallabag\CoreBundle\Tools\Utils; | |||
20 | class ConfigController extends Controller | 26 | class ConfigController extends Controller |
21 | { | 27 | { |
22 | /** | 28 | /** |
23 | * @param Request $request | ||
24 | * | ||
25 | * @Route("/config", name="config") | 29 | * @Route("/config", name="config") |
26 | */ | 30 | */ |
27 | public function indexAction(Request $request) | 31 | public function indexAction(Request $request) |
@@ -45,7 +49,7 @@ class ConfigController extends Controller | |||
45 | $activeTheme = $this->get('liip_theme.active_theme'); | 49 | $activeTheme = $this->get('liip_theme.active_theme'); |
46 | $activeTheme->setName($config->getTheme()); | 50 | $activeTheme->setName($config->getTheme()); |
47 | 51 | ||
48 | $this->get('session')->getFlashBag()->add( | 52 | $this->addFlash( |
49 | 'notice', | 53 | 'notice', |
50 | 'flashes.config.notice.config_saved' | 54 | 'flashes.config.notice.config_saved' |
51 | ); | 55 | ); |
@@ -67,7 +71,7 @@ class ConfigController extends Controller | |||
67 | $userManager->updateUser($user, true); | 71 | $userManager->updateUser($user, true); |
68 | } | 72 | } |
69 | 73 | ||
70 | $this->get('session')->getFlashBag()->add('notice', $message); | 74 | $this->addFlash('notice', $message); |
71 | 75 | ||
72 | return $this->redirect($this->generateUrl('config') . '#set4'); | 76 | return $this->redirect($this->generateUrl('config') . '#set4'); |
73 | } | 77 | } |
@@ -82,7 +86,7 @@ class ConfigController extends Controller | |||
82 | if ($userForm->isSubmitted() && $userForm->isValid()) { | 86 | if ($userForm->isSubmitted() && $userForm->isValid()) { |
83 | $userManager->updateUser($user, true); | 87 | $userManager->updateUser($user, true); |
84 | 88 | ||
85 | $this->get('session')->getFlashBag()->add( | 89 | $this->addFlash( |
86 | 'notice', | 90 | 'notice', |
87 | 'flashes.config.notice.user_updated' | 91 | 'flashes.config.notice.user_updated' |
88 | ); | 92 | ); |
@@ -90,17 +94,17 @@ class ConfigController extends Controller | |||
90 | return $this->redirect($this->generateUrl('config') . '#set3'); | 94 | return $this->redirect($this->generateUrl('config') . '#set3'); |
91 | } | 95 | } |
92 | 96 | ||
93 | // handle rss information | 97 | // handle feed information |
94 | $rssForm = $this->createForm(RssType::class, $config, ['action' => $this->generateUrl('config') . '#set2']); | 98 | $feedForm = $this->createForm(FeedType::class, $config, ['action' => $this->generateUrl('config') . '#set2']); |
95 | $rssForm->handleRequest($request); | 99 | $feedForm->handleRequest($request); |
96 | 100 | ||
97 | if ($rssForm->isSubmitted() && $rssForm->isValid()) { | 101 | if ($feedForm->isSubmitted() && $feedForm->isValid()) { |
98 | $em->persist($config); | 102 | $em->persist($config); |
99 | $em->flush(); | 103 | $em->flush(); |
100 | 104 | ||
101 | $this->get('session')->getFlashBag()->add( | 105 | $this->addFlash( |
102 | 'notice', | 106 | 'notice', |
103 | 'flashes.config.notice.rss_updated' | 107 | 'flashes.config.notice.feed_updated' |
104 | ); | 108 | ); |
105 | 109 | ||
106 | return $this->redirect($this->generateUrl('config') . '#set2'); | 110 | return $this->redirect($this->generateUrl('config') . '#set2'); |
@@ -130,7 +134,7 @@ class ConfigController extends Controller | |||
130 | $em->persist($taggingRule); | 134 | $em->persist($taggingRule); |
131 | $em->flush(); | 135 | $em->flush(); |
132 | 136 | ||
133 | $this->get('session')->getFlashBag()->add( | 137 | $this->addFlash( |
134 | 'notice', | 138 | 'notice', |
135 | 'flashes.config.notice.tagging_rules_updated' | 139 | 'flashes.config.notice.tagging_rules_updated' |
136 | ); | 140 | ); |
@@ -138,28 +142,168 @@ class ConfigController extends Controller | |||
138 | return $this->redirect($this->generateUrl('config') . '#set5'); | 142 | return $this->redirect($this->generateUrl('config') . '#set5'); |
139 | } | 143 | } |
140 | 144 | ||
145 | // handle tagging rules import | ||
146 | $taggingRulesImportform = $this->createForm(TaggingRuleImportType::class); | ||
147 | $taggingRulesImportform->handleRequest($request); | ||
148 | |||
149 | if ($taggingRulesImportform->isSubmitted() && $taggingRulesImportform->isValid()) { | ||
150 | $message = 'flashes.config.notice.tagging_rules_not_imported'; | ||
151 | $file = $taggingRulesImportform->get('file')->getData(); | ||
152 | |||
153 | if (null !== $file && $file->isValid() && \in_array($file->getClientMimeType(), ['application/json', 'application/octet-stream'], true)) { | ||
154 | $content = json_decode(file_get_contents($file->getPathname()), true); | ||
155 | |||
156 | if (\is_array($content)) { | ||
157 | foreach ($content as $rule) { | ||
158 | $taggingRule = new TaggingRule(); | ||
159 | $taggingRule->setRule($rule['rule']); | ||
160 | $taggingRule->setTags($rule['tags']); | ||
161 | $taggingRule->setConfig($config); | ||
162 | $em->persist($taggingRule); | ||
163 | } | ||
164 | |||
165 | $em->flush(); | ||
166 | |||
167 | $message = 'flashes.config.notice.tagging_rules_imported'; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | $this->addFlash('notice', $message); | ||
172 | |||
173 | return $this->redirect($this->generateUrl('config') . '#set5'); | ||
174 | } | ||
175 | |||
141 | return $this->render('WallabagCoreBundle:Config:index.html.twig', [ | 176 | return $this->render('WallabagCoreBundle:Config:index.html.twig', [ |
142 | 'form' => [ | 177 | 'form' => [ |
143 | 'config' => $configForm->createView(), | 178 | 'config' => $configForm->createView(), |
144 | 'rss' => $rssForm->createView(), | 179 | 'feed' => $feedForm->createView(), |
145 | 'pwd' => $pwdForm->createView(), | 180 | 'pwd' => $pwdForm->createView(), |
146 | 'user' => $userForm->createView(), | 181 | 'user' => $userForm->createView(), |
147 | 'new_tagging_rule' => $newTaggingRule->createView(), | 182 | 'new_tagging_rule' => $newTaggingRule->createView(), |
183 | 'import_tagging_rule' => $taggingRulesImportform->createView(), | ||
148 | ], | 184 | ], |
149 | 'rss' => [ | 185 | 'feed' => [ |
150 | 'username' => $user->getUsername(), | 186 | 'username' => $user->getUsername(), |
151 | 'token' => $config->getRssToken(), | 187 | 'token' => $config->getFeedToken(), |
152 | ], | 188 | ], |
153 | 'twofactor_auth' => $this->getParameter('twofactor_auth'), | 189 | 'twofactor_auth' => $this->getParameter('twofactor_auth'), |
154 | 'wallabag_url' => $this->getParameter('domain_name'), | 190 | 'wallabag_url' => $this->getParameter('domain_name'), |
155 | 'enabled_users' => $this->get('wallabag_user.user_repository') | 191 | 'enabled_users' => $this->get('wallabag_user.user_repository')->getSumEnabledUsers(), |
156 | ->getSumEnabledUsers(), | 192 | ]); |
193 | } | ||
194 | |||
195 | /** | ||
196 | * Enable 2FA using email. | ||
197 | * | ||
198 | * @Route("/config/otp/email", name="config_otp_email") | ||
199 | */ | ||
200 | public function otpEmailAction() | ||
201 | { | ||
202 | if (!$this->getParameter('twofactor_auth')) { | ||
203 | return $this->createNotFoundException('two_factor not enabled'); | ||
204 | } | ||
205 | |||
206 | $user = $this->getUser(); | ||
207 | |||
208 | $user->setGoogleAuthenticatorSecret(null); | ||
209 | $user->setBackupCodes(null); | ||
210 | $user->setEmailTwoFactor(true); | ||
211 | |||
212 | $this->container->get('fos_user.user_manager')->updateUser($user, true); | ||
213 | |||
214 | $this->addFlash( | ||
215 | 'notice', | ||
216 | 'flashes.config.notice.otp_enabled' | ||
217 | ); | ||
218 | |||
219 | return $this->redirect($this->generateUrl('config') . '#set3'); | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * Enable 2FA using OTP app, user will need to confirm the generated code from the app. | ||
224 | * | ||
225 | * @Route("/config/otp/app", name="config_otp_app") | ||
226 | */ | ||
227 | public function otpAppAction() | ||
228 | { | ||
229 | if (!$this->getParameter('twofactor_auth')) { | ||
230 | return $this->createNotFoundException('two_factor not enabled'); | ||
231 | } | ||
232 | |||
233 | $user = $this->getUser(); | ||
234 | $secret = $this->get('scheb_two_factor.security.google_authenticator')->generateSecret(); | ||
235 | |||
236 | $user->setGoogleAuthenticatorSecret($secret); | ||
237 | $user->setEmailTwoFactor(false); | ||
238 | |||
239 | $backupCodes = (new BackupCodes())->toArray(); | ||
240 | $backupCodesHashed = array_map( | ||
241 | function ($backupCode) { | ||
242 | return password_hash($backupCode, PASSWORD_DEFAULT); | ||
243 | }, | ||
244 | $backupCodes | ||
245 | ); | ||
246 | |||
247 | $user->setBackupCodes($backupCodesHashed); | ||
248 | |||
249 | $this->container->get('fos_user.user_manager')->updateUser($user, true); | ||
250 | |||
251 | return $this->render('WallabagCoreBundle:Config:otp_app.html.twig', [ | ||
252 | 'backupCodes' => $backupCodes, | ||
253 | 'qr_code' => $this->get('scheb_two_factor.security.google_authenticator')->getQRContent($user), | ||
157 | ]); | 254 | ]); |
158 | } | 255 | } |
159 | 256 | ||
160 | /** | 257 | /** |
161 | * @param Request $request | 258 | * Cancelling 2FA using OTP app. |
162 | * | 259 | * |
260 | * @Route("/config/otp/app/cancel", name="config_otp_app_cancel") | ||
261 | */ | ||
262 | public function otpAppCancelAction() | ||
263 | { | ||
264 | if (!$this->getParameter('twofactor_auth')) { | ||
265 | return $this->createNotFoundException('two_factor not enabled'); | ||
266 | } | ||
267 | |||
268 | $user = $this->getUser(); | ||
269 | $user->setGoogleAuthenticatorSecret(null); | ||
270 | $user->setBackupCodes(null); | ||
271 | |||
272 | $this->container->get('fos_user.user_manager')->updateUser($user, true); | ||
273 | |||
274 | return $this->redirect($this->generateUrl('config') . '#set3'); | ||
275 | } | ||
276 | |||
277 | /** | ||
278 | * Validate OTP code. | ||
279 | * | ||
280 | * @Route("/config/otp/app/check", name="config_otp_app_check") | ||
281 | */ | ||
282 | public function otpAppCheckAction(Request $request) | ||
283 | { | ||
284 | $isValid = $this->get('scheb_two_factor.security.google_authenticator')->checkCode( | ||
285 | $this->getUser(), | ||
286 | $request->get('_auth_code') | ||
287 | ); | ||
288 | |||
289 | if (true === $isValid) { | ||
290 | $this->addFlash( | ||
291 | 'notice', | ||
292 | 'flashes.config.notice.otp_enabled' | ||
293 | ); | ||
294 | |||
295 | return $this->redirect($this->generateUrl('config') . '#set3'); | ||
296 | } | ||
297 | |||
298 | $this->addFlash( | ||
299 | 'two_factor', | ||
300 | 'scheb_two_factor.code_invalid' | ||
301 | ); | ||
302 | |||
303 | return $this->redirect($this->generateUrl('config_otp_app')); | ||
304 | } | ||
305 | |||
306 | /** | ||
163 | * @Route("/generate-token", name="generate_token") | 307 | * @Route("/generate-token", name="generate_token") |
164 | * | 308 | * |
165 | * @return RedirectResponse|JsonResponse | 309 | * @return RedirectResponse|JsonResponse |
@@ -167,28 +311,52 @@ class ConfigController extends Controller | |||
167 | public function generateTokenAction(Request $request) | 311 | public function generateTokenAction(Request $request) |
168 | { | 312 | { |
169 | $config = $this->getConfig(); | 313 | $config = $this->getConfig(); |
170 | $config->setRssToken(Utils::generateToken()); | 314 | $config->setFeedToken(Utils::generateToken()); |
171 | 315 | ||
172 | $em = $this->getDoctrine()->getManager(); | 316 | $em = $this->getDoctrine()->getManager(); |
173 | $em->persist($config); | 317 | $em->persist($config); |
174 | $em->flush(); | 318 | $em->flush(); |
175 | 319 | ||
176 | if ($request->isXmlHttpRequest()) { | 320 | if ($request->isXmlHttpRequest()) { |
177 | return new JsonResponse(['token' => $config->getRssToken()]); | 321 | return new JsonResponse(['token' => $config->getFeedToken()]); |
178 | } | 322 | } |
179 | 323 | ||
180 | $this->get('session')->getFlashBag()->add( | 324 | $this->addFlash( |
181 | 'notice', | 325 | 'notice', |
182 | 'flashes.config.notice.rss_token_updated' | 326 | 'flashes.config.notice.feed_token_updated' |
183 | ); | 327 | ); |
184 | 328 | ||
185 | return $this->redirect($this->generateUrl('config') . '#set2'); | 329 | return $this->redirect($this->generateUrl('config') . '#set2'); |
186 | } | 330 | } |
187 | 331 | ||
188 | /** | 332 | /** |
189 | * Deletes a tagging rule and redirect to the config homepage. | 333 | * @Route("/revoke-token", name="revoke_token") |
190 | * | 334 | * |
191 | * @param TaggingRule $rule | 335 | * @return RedirectResponse|JsonResponse |
336 | */ | ||
337 | public function revokeTokenAction(Request $request) | ||
338 | { | ||
339 | $config = $this->getConfig(); | ||
340 | $config->setFeedToken(null); | ||
341 | |||
342 | $em = $this->getDoctrine()->getManager(); | ||
343 | $em->persist($config); | ||
344 | $em->flush(); | ||
345 | |||
346 | if ($request->isXmlHttpRequest()) { | ||
347 | return new JsonResponse(); | ||
348 | } | ||
349 | |||
350 | $this->addFlash( | ||
351 | 'notice', | ||
352 | 'flashes.config.notice.feed_token_revoked' | ||
353 | ); | ||
354 | |||
355 | return $this->redirect($this->generateUrl('config') . '#set2'); | ||
356 | } | ||
357 | |||
358 | /** | ||
359 | * Deletes a tagging rule and redirect to the config homepage. | ||
192 | * | 360 | * |
193 | * @Route("/tagging-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_tagging_rule") | 361 | * @Route("/tagging-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_tagging_rule") |
194 | * | 362 | * |
@@ -202,7 +370,7 @@ class ConfigController extends Controller | |||
202 | $em->remove($rule); | 370 | $em->remove($rule); |
203 | $em->flush(); | 371 | $em->flush(); |
204 | 372 | ||
205 | $this->get('session')->getFlashBag()->add( | 373 | $this->addFlash( |
206 | 'notice', | 374 | 'notice', |
207 | 'flashes.config.notice.tagging_rules_deleted' | 375 | 'flashes.config.notice.tagging_rules_deleted' |
208 | ); | 376 | ); |
@@ -213,8 +381,6 @@ class ConfigController extends Controller | |||
213 | /** | 381 | /** |
214 | * Edit a tagging rule. | 382 | * Edit a tagging rule. |
215 | * | 383 | * |
216 | * @param TaggingRule $rule | ||
217 | * | ||
218 | * @Route("/tagging-rule/edit/{id}", requirements={"id" = "\d+"}, name="edit_tagging_rule") | 384 | * @Route("/tagging-rule/edit/{id}", requirements={"id" = "\d+"}, name="edit_tagging_rule") |
219 | * | 385 | * |
220 | * @return RedirectResponse | 386 | * @return RedirectResponse |
@@ -268,7 +434,7 @@ class ConfigController extends Controller | |||
268 | break; | 434 | break; |
269 | } | 435 | } |
270 | 436 | ||
271 | $this->get('session')->getFlashBag()->add( | 437 | $this->addFlash( |
272 | 'notice', | 438 | 'notice', |
273 | 'flashes.config.notice.' . $type . '_reset' | 439 | 'flashes.config.notice.' . $type . '_reset' |
274 | ); | 440 | ); |
@@ -281,8 +447,6 @@ class ConfigController extends Controller | |||
281 | * | 447 | * |
282 | * @Route("/account/delete", name="delete_account") | 448 | * @Route("/account/delete", name="delete_account") |
283 | * | 449 | * |
284 | * @param Request $request | ||
285 | * | ||
286 | * @throws AccessDeniedHttpException | 450 | * @throws AccessDeniedHttpException |
287 | * | 451 | * |
288 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | 452 | * @return \Symfony\Component\HttpFoundation\RedirectResponse |
@@ -313,8 +477,6 @@ class ConfigController extends Controller | |||
313 | * | 477 | * |
314 | * @Route("/config/view-mode", name="switch_view_mode") | 478 | * @Route("/config/view-mode", name="switch_view_mode") |
315 | * | 479 | * |
316 | * @param Request $request | ||
317 | * | ||
318 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | 480 | * @return \Symfony\Component\HttpFoundation\RedirectResponse |
319 | */ | 481 | */ |
320 | public function changeViewModeAction(Request $request) | 482 | public function changeViewModeAction(Request $request) |
@@ -330,6 +492,52 @@ class ConfigController extends Controller | |||
330 | } | 492 | } |
331 | 493 | ||
332 | /** | 494 | /** |
495 | * Change the locale for the current user. | ||
496 | * | ||
497 | * @param string $language | ||
498 | * | ||
499 | * @Route("/locale/{language}", name="changeLocale") | ||
500 | * | ||
501 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||
502 | */ | ||
503 | public function setLocaleAction(Request $request, $language = null) | ||
504 | { | ||
505 | $errors = $this->get('validator')->validate($language, (new LocaleConstraint())); | ||
506 | |||
507 | if (0 === \count($errors)) { | ||
508 | $request->getSession()->set('_locale', $language); | ||
509 | } | ||
510 | |||
511 | return $this->redirect($request->headers->get('referer', $this->generateUrl('homepage'))); | ||
512 | } | ||
513 | |||
514 | /** | ||
515 | * Export tagging rules for the logged in user. | ||
516 | * | ||
517 | * @Route("/tagging-rule/export", name="export_tagging_rule") | ||
518 | * | ||
519 | * @return Response | ||
520 | */ | ||
521 | public function exportTaggingRulesAction() | ||
522 | { | ||
523 | $data = SerializerBuilder::create()->build()->serialize( | ||
524 | $this->getUser()->getConfig()->getTaggingRules(), | ||
525 | 'json', | ||
526 | SerializationContext::create()->setGroups(['export_tagging_rule']) | ||
527 | ); | ||
528 | |||
529 | return Response::create( | ||
530 | $data, | ||
531 | 200, | ||
532 | [ | ||
533 | 'Content-type' => 'application/json', | ||
534 | 'Content-Disposition' => 'attachment; filename="tagging_rules_' . $this->getUser()->getUsername() . '.json"', | ||
535 | 'Content-Transfer-Encoding' => 'UTF-8', | ||
536 | ] | ||
537 | ); | ||
538 | } | ||
539 | |||
540 | /** | ||
333 | * Remove all tags for given tags and a given user and cleanup orphan tags. | 541 | * Remove all tags for given tags and a given user and cleanup orphan tags. |
334 | * | 542 | * |
335 | * @param array $tags | 543 | * @param array $tags |
@@ -395,8 +603,6 @@ class ConfigController extends Controller | |||
395 | 603 | ||
396 | /** | 604 | /** |
397 | * Validate that a rule can be edited/deleted by the current user. | 605 | * Validate that a rule can be edited/deleted by the current user. |
398 | * | ||
399 | * @param TaggingRule $rule | ||
400 | */ | 606 | */ |
401 | private function validateRuleAction(TaggingRule $rule) | 607 | private function validateRuleAction(TaggingRule $rule) |
402 | { | 608 | { |