Skip to content

Commit 58f9c5c

Browse files
committed
Add support for is_granted_for_user access_decision access_decision_for_user Twig function and UserAuthorizationCheckerInterface with completion and navigation
1 parent 7c98ab0 commit 58f9c5c

File tree

4 files changed

+389
-4
lines changed

4 files changed

+389
-4
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/security/VoterGotoCompletionRegistrar.java

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,25 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
2727
// {% is_granted('foobar') %}
2828
// {% is_granted({'foobar'}) %}
2929
// {% is_granted(['foobar']) %}
30+
// {% set voter_decision = access_decision('post_edit', post) %} (Symfony 7.4)
3031
registrar.register(
3132
PlatformPatterns.or(
32-
TwigPattern.getPrintBlockOrTagFunctionPattern("is_granted"),
33-
TwigPattern.getFunctionWithFirstParameterAsArrayPattern("is_granted"),
34-
TwigPattern.getFunctionWithFirstParameterAsLiteralPattern("is_granted")
33+
TwigPattern.getPrintBlockOrTagFunctionPattern("is_granted", "access_decision"),
34+
TwigPattern.getFunctionWithFirstParameterAsArrayPattern("is_granted", "access_decision"),
35+
TwigPattern.getFunctionWithFirstParameterAsLiteralPattern("is_granted", "access_decision")
36+
),
37+
MyVisitorGotoCompletionProvider::new
38+
);
39+
40+
// {% is_granted_for_user(user, 'foobar') %}
41+
// {% is_granted_for_user(user, {'foobar'}) %}
42+
// {% is_granted_for_user(user, ['foobar']) %}
43+
// {% set voter_decision = access_decision_for_user(user, 'post_edit', post) %} (Symfony 7.4)
44+
registrar.register(
45+
PlatformPatterns.or(
46+
TwigPattern.getPrintBlockOrTagFunctionSecondParameterPattern("is_granted_for_user", "access_decision_for_user"),
47+
TwigPattern.getFunctionWithSecondParameterAsArrayPattern("is_granted_for_user", "access_decision_for_user"),
48+
TwigPattern.getFunctionWithSecondParameterAsLiteralPattern("is_granted_for_user", "access_decision_for_user")
3549
),
3650
MyVisitorGotoCompletionProvider::new
3751
);
@@ -96,6 +110,15 @@ public static boolean isIsGrantedMethodMatch(@NotNull StringLiteralExpression st
96110
return true;
97111
}
98112

113+
// Check for isGrantedForUser second parameter (index 1)
114+
MethodMatcher.MethodMatchParameter secondParameterMatch = new MethodMatcher.StringParameterRecursiveMatcher(stringLiteralExpression, 1)
115+
.withSignature("Symfony\\Component\\Security\\Core\\Authorization\\UserAuthorizationCheckerInterface", "isGrantedForUser")
116+
.match();
117+
118+
if (secondParameterMatch != null) {
119+
return true;
120+
}
121+
99122
MethodMatcher.MethodMatchParameter arrayMatchParameter = new MethodMatcher.ArrayParameterMatcher(stringLiteralExpression, 0)
100123
.withSignature("Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller", "isGranted")
101124
.withSignature("Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller", "denyAccessUnlessGranted")
@@ -107,6 +130,15 @@ public static boolean isIsGrantedMethodMatch(@NotNull StringLiteralExpression st
107130
.withSignature("Symfony\\Component\\Security\\Core\\Authorization\\AuthorizationCheckerInterface", "isGranted")
108131
.match();
109132

110-
return arrayMatchParameter != null;
133+
if (arrayMatchParameter != null) {
134+
return true;
135+
}
136+
137+
// Check for isGrantedForUser second parameter as an array (index 1)
138+
MethodMatcher.MethodMatchParameter secondArrayParameterMatch = new MethodMatcher.ArrayParameterMatcher(stringLiteralExpression, 1)
139+
.withSignature("Symfony\\Component\\Security\\Core\\Authorization\\UserAuthorizationCheckerInterface", "isGrantedForUser")
140+
.match();
141+
142+
return secondArrayParameterMatch != null;
111143
}
112144
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigPattern.java

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,38 @@ public static ElementPattern<PsiElement> getPrintBlockOrTagFunctionPattern() {
225225
.withLanguage(TwigLanguage.INSTANCE);
226226
}
227227

228+
/**
229+
* Check for {{ foo(bar, '|') }}, {% foo(bar, '|') %}
230+
*
231+
* @param functionName twig function name
232+
*/
233+
public static ElementPattern<PsiElement> getPrintBlockOrTagFunctionSecondParameterPattern(String... functionName) {
234+
return PlatformPatterns
235+
.psiElement(TwigTokenTypes.STRING_TEXT)
236+
.withParent(
237+
getFunctionCallScopePattern()
238+
)
239+
.afterLeafSkipping(
240+
PlatformPatterns.or(
241+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
242+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE),
243+
PlatformPatterns.psiElement(TwigTokenTypes.SINGLE_QUOTE),
244+
PlatformPatterns.psiElement(TwigTokenTypes.DOUBLE_QUOTE)
245+
),
246+
PlatformPatterns.psiElement(TwigTokenTypes.COMMA).afterLeafSkipping(
247+
PlatformPatterns.or(PARAMETER_WHITE_LIST),
248+
PlatformPatterns.psiElement(TwigTokenTypes.LBRACE).afterLeafSkipping(
249+
PlatformPatterns.or(
250+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
251+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE)
252+
),
253+
PlatformPatterns.psiElement(TwigTokenTypes.IDENTIFIER).withText(PlatformPatterns.string().oneOf(functionName))
254+
)
255+
)
256+
)
257+
.withLanguage(TwigLanguage.INSTANCE);
258+
}
259+
228260
/**
229261
* {{ foo('<caret>') }}
230262
* {{ 'test'|foo('<caret>') }}
@@ -279,6 +311,47 @@ public static ElementPattern<PsiElement> getFunctionWithFirstParameterAsLiteralP
279311
.withLanguage(TwigLanguage.INSTANCE);
280312
}
281313

314+
/**
315+
* Literal as second parameter
316+
*
317+
* {{ foo(bar, {'foobar', 'foo<caret>bar'}) }}
318+
* {{ foo(bar, {'fo<caret>obar'}) }}
319+
*/
320+
public static ElementPattern<PsiElement> getFunctionWithSecondParameterAsLiteralPattern(@NotNull String... functionName) {
321+
return PlatformPatterns
322+
.psiElement(TwigTokenTypes.STRING_TEXT).afterLeafSkipping(
323+
PlatformPatterns.or(
324+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
325+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE),
326+
PlatformPatterns.psiElement(TwigTokenTypes.SINGLE_QUOTE),
327+
PlatformPatterns.psiElement(TwigTokenTypes.DOUBLE_QUOTE)
328+
),
329+
PlatformPatterns.or(
330+
PlatformPatterns.psiElement(TwigTokenTypes.LBRACE_CURL),
331+
PlatformPatterns.psiElement(TwigTokenTypes.COMMA)
332+
)
333+
)
334+
.withParent(
335+
PlatformPatterns.psiElement(TwigElementTypes.LITERAL).afterLeafSkipping(
336+
PlatformPatterns.or(
337+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
338+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE)
339+
),
340+
PlatformPatterns.psiElement(TwigTokenTypes.COMMA).afterLeafSkipping(
341+
PlatformPatterns.or(PARAMETER_WHITE_LIST),
342+
PlatformPatterns.psiElement(TwigTokenTypes.LBRACE).afterLeafSkipping(
343+
PlatformPatterns.or(
344+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
345+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE)
346+
),
347+
PlatformPatterns.psiElement(TwigTokenTypes.IDENTIFIER).withText(PlatformPatterns.string().oneOf(functionName))
348+
)
349+
)
350+
)
351+
)
352+
.withLanguage(TwigLanguage.INSTANCE);
353+
}
354+
282355
/**
283356
* {{ foo({'foo<caret>bar': 'foo'}}) }}
284357
* {{ foo({'foobar': 'foo', 'foo<caret>bar': 'foo'}}) }}
@@ -431,6 +504,74 @@ public static ElementPattern<PsiElement> getFunctionWithFirstParameterAsArrayPat
431504
);
432505
}
433506

507+
/**
508+
* Array values as second parameter
509+
*
510+
* {{ foo(bar, ['foobar', 'foo<caret>bar']) }}
511+
* {{ foo(bar, ['fo<caret>obar']) }}
512+
*/
513+
public static ElementPattern<PsiElement> getFunctionWithSecondParameterAsArrayPattern(@NotNull String... functionName) {
514+
515+
// "foo(param, [<caret>"
516+
PsiElementPattern.Capture<PsiElement> functionPattern = PlatformPatterns
517+
.psiElement(TwigTokenTypes.LBRACE_SQ)
518+
.afterLeafSkipping(
519+
PlatformPatterns.or(
520+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
521+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE)
522+
),
523+
PlatformPatterns.psiElement(TwigTokenTypes.COMMA).afterLeafSkipping(
524+
PlatformPatterns.or(PARAMETER_WHITE_LIST),
525+
PlatformPatterns.psiElement(TwigTokenTypes.LBRACE).afterLeafSkipping(
526+
PlatformPatterns.or(
527+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
528+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE)
529+
),
530+
PlatformPatterns.psiElement(TwigTokenTypes.IDENTIFIER).withText(PlatformPatterns.string().oneOf(functionName))
531+
)
532+
)
533+
);
534+
535+
return
536+
PlatformPatterns.or(
537+
// {{ foo(bar, ['fo<caret>obar']) }}
538+
PlatformPatterns
539+
.psiElement(TwigTokenTypes.STRING_TEXT).afterLeafSkipping(
540+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
541+
PlatformPatterns.psiElement().withElementType(PlatformPatterns.elementType().or(
542+
TwigTokenTypes.SINGLE_QUOTE,
543+
TwigTokenTypes.DOUBLE_QUOTE
544+
)).afterLeafSkipping(
545+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE),
546+
functionPattern
547+
)
548+
).withLanguage(TwigLanguage.INSTANCE),
549+
550+
// {{ foo(bar, ['foobar', 'foo<caret>bar']) }}
551+
PlatformPatterns
552+
.psiElement(TwigTokenTypes.STRING_TEXT).afterLeafSkipping(
553+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
554+
PlatformPatterns.psiElement().withElementType(PlatformPatterns.elementType().or(
555+
TwigTokenTypes.SINGLE_QUOTE,
556+
TwigTokenTypes.DOUBLE_QUOTE
557+
)).afterLeafSkipping(
558+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
559+
PlatformPatterns.psiElement(TwigTokenTypes.COMMA).afterLeafSkipping(
560+
PlatformPatterns.or(
561+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE),
562+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
563+
PlatformPatterns.psiElement(TwigTokenTypes.STRING_TEXT),
564+
PlatformPatterns.psiElement(TwigTokenTypes.SINGLE_QUOTE),
565+
PlatformPatterns.psiElement(TwigTokenTypes.DOUBLE_QUOTE),
566+
PlatformPatterns.psiElement(TwigTokenTypes.COMMA)
567+
),
568+
functionPattern
569+
)
570+
)
571+
).withLanguage(TwigLanguage.INSTANCE)
572+
);
573+
}
574+
434575
/**
435576
* {% render "foo"
436577
*

0 commit comments

Comments
 (0)