1 | <?php |
||||||
2 | /** |
||||||
3 | * Twigfield for Craft CMS |
||||||
4 | * |
||||||
5 | * Provides a twig editor field with Twig & Craft API autocomplete |
||||||
6 | * |
||||||
7 | * @link https://nystudio107.com |
||||||
0 ignored issues
–
show
Coding Style
introduced
by
![]() |
|||||||
8 | * @copyright Copyright (c) 2022 nystudio107 |
||||||
0 ignored issues
–
show
|
|||||||
9 | */ |
||||||
0 ignored issues
–
show
|
|||||||
10 | |||||||
11 | namespace nystudio107\twigfield\autocompletes; |
||||||
12 | |||||||
13 | use Craft; |
||||||
14 | use craft\base\Element; |
||||||
15 | use nystudio107\twigfield\base\ObjectParserAutocomplete; |
||||||
16 | use nystudio107\twigfield\models\CompleteItem; |
||||||
17 | use nystudio107\twigfield\types\AutocompleteTypes; |
||||||
18 | use nystudio107\twigfield\types\CompleteItemKind; |
||||||
19 | |||||||
20 | /** |
||||||
0 ignored issues
–
show
|
|||||||
21 | * @author nystudio107 |
||||||
0 ignored issues
–
show
Content of the @author tag must be in the form "Display Name <[email protected]>"
![]() |
|||||||
22 | * @package twigfield |
||||||
0 ignored issues
–
show
|
|||||||
23 | * @since 1.0.12 |
||||||
0 ignored issues
–
show
|
|||||||
24 | */ |
||||||
0 ignored issues
–
show
|
|||||||
25 | class CraftApiAutocomplete extends ObjectParserAutocomplete |
||||||
26 | { |
||||||
27 | // Constants |
||||||
28 | // ========================================================================= |
||||||
29 | |||||||
30 | const ELEMENT_ROUTE_EXCLUDES = [ |
||||||
31 | 'matrixblock', |
||||||
32 | 'globalset' |
||||||
33 | ]; |
||||||
34 | |||||||
35 | // Public Properties |
||||||
36 | // ========================================================================= |
||||||
37 | |||||||
38 | /** |
||||||
0 ignored issues
–
show
|
|||||||
39 | * @var string The name of the autocomplete |
||||||
40 | */ |
||||||
41 | public $name = 'CraftApiAutocomplete'; |
||||||
42 | |||||||
43 | /** |
||||||
0 ignored issues
–
show
|
|||||||
44 | * @var string The type of the autocomplete |
||||||
45 | */ |
||||||
46 | public $type = AutocompleteTypes::TwigExpressionAutocomplete; |
||||||
47 | |||||||
48 | /** |
||||||
0 ignored issues
–
show
|
|||||||
49 | * @var string Whether the autocomplete should be parsed with . -delimited nested sub-properties |
||||||
50 | */ |
||||||
51 | public $hasSubProperties = true; |
||||||
52 | |||||||
53 | /** |
||||||
0 ignored issues
–
show
|
|||||||
54 | * @var array A key-value array of the Twig global variables to parse. If left empty, it will |
||||||
55 | * default to the current Twig context global variables |
||||||
56 | */ |
||||||
57 | public $twigGlobals = []; |
||||||
58 | |||||||
59 | /** |
||||||
0 ignored issues
–
show
|
|||||||
60 | * @var array A key-value array of the Element Route variables (the injected `entry`, etc. |
||||||
61 | * variable). If left empty, it will default to the current Element Route variables |
||||||
62 | */ |
||||||
63 | public $elementRouteGlobals = []; |
||||||
64 | |||||||
65 | /** |
||||||
0 ignored issues
–
show
|
|||||||
66 | * @var array A key-value array of additional global variables to parse for completions |
||||||
67 | */ |
||||||
68 | public $additionalGlobals = []; |
||||||
69 | |||||||
70 | // Public Methods |
||||||
71 | // ========================================================================= |
||||||
72 | |||||||
73 | /** |
||||||
0 ignored issues
–
show
|
|||||||
74 | * @inerhitDoc |
||||||
75 | */ |
||||||
0 ignored issues
–
show
|
|||||||
76 | public function init(): void |
||||||
77 | { |
||||||
78 | if (empty($this->twigGlobals)) { |
||||||
79 | $this->twigGlobals = Craft::$app->view->getTwig()->getGlobals(); |
||||||
80 | } |
||||||
81 | if (empty($this->elementRouteGlobals)) { |
||||||
82 | $this->elementRouteGlobals = $this->getElementRouteGlobals(); |
||||||
83 | } |
||||||
84 | } |
||||||
85 | |||||||
86 | /** |
||||||
0 ignored issues
–
show
|
|||||||
87 | * @inerhitDoc |
||||||
88 | */ |
||||||
0 ignored issues
–
show
|
|||||||
89 | public function generateCompleteItems(): void |
||||||
90 | { |
||||||
91 | // Gather up all of the globals to parse |
||||||
92 | $globals = array_merge( |
||||||
93 | $this->twigGlobals, |
||||||
94 | $this->elementRouteGlobals, |
||||||
95 | $this->additionalGlobals, |
||||||
96 | $this->overrideValues() |
||||||
97 | ); |
||||||
98 | foreach ($globals as $key => $value) { |
||||||
99 | if (!in_array($key, parent::EXCLUDED_PROPERTY_NAMES, true)) { |
||||||
100 | $type = gettype($value); |
||||||
101 | switch ($type) { |
||||||
102 | case 'object': |
||||||
0 ignored issues
–
show
|
|||||||
103 | $this->parseObject($key, $value, 0); |
||||||
104 | break; |
||||||
105 | |||||||
106 | case 'array': |
||||||
0 ignored issues
–
show
|
|||||||
107 | case 'boolean': |
||||||
0 ignored issues
–
show
|
|||||||
108 | case 'double': |
||||||
0 ignored issues
–
show
|
|||||||
109 | case 'integer': |
||||||
0 ignored issues
–
show
|
|||||||
110 | case 'string': |
||||||
0 ignored issues
–
show
|
|||||||
111 | $kind = CompleteItemKind::VariableKind; |
||||||
112 | $path = $key; |
||||||
113 | $normalizedKey = preg_replace("/[^A-Za-z]/", '', $key); |
||||||
114 | if (ctype_upper($normalizedKey)) { |
||||||
0 ignored issues
–
show
|
|||||||
115 | $kind = CompleteItemKind::ConstantKind; |
||||||
116 | } |
||||||
0 ignored issues
–
show
|
|||||||
117 | // If this is an array, JSON-encode the keys. In the future, we could recursively parse the array |
||||||
118 | // To allow for nested values |
||||||
119 | if (is_array($value)) { |
||||||
0 ignored issues
–
show
|
|||||||
120 | $value = json_encode(array_keys($value)); |
||||||
121 | } |
||||||
0 ignored issues
–
show
|
|||||||
122 | CompleteItem::create() |
||||||
123 | ->detail((string)$value) |
||||||
124 | ->kind($kind) |
||||||
125 | ->label((string)$key) |
||||||
126 | ->insertText((string)$key) |
||||||
127 | ->add($this, $path); |
||||||
128 | break; |
||||||
129 | } |
||||||
130 | } |
||||||
131 | } |
||||||
132 | } |
||||||
133 | |||||||
134 | // Protected Methods |
||||||
135 | // ========================================================================= |
||||||
136 | |||||||
137 | /** |
||||||
138 | * Add in the element types that could be injected as route variables |
||||||
139 | * |
||||||
140 | * @return array |
||||||
141 | */ |
||||||
142 | protected function getElementRouteGlobals(): array |
||||||
143 | { |
||||||
144 | $routeVariables = []; |
||||||
145 | $elementTypes = Craft::$app->elements->getAllElementTypes(); |
||||||
0 ignored issues
–
show
The method
getAllElementTypes() does not exist on null .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
146 | foreach ($elementTypes as $elementType) { |
||||||
147 | /* @var Element $elementType */ |
||||||
148 | $key = $elementType::refHandle(); |
||||||
149 | if (!empty($key) && !in_array($key, self::ELEMENT_ROUTE_EXCLUDES)) { |
||||||
150 | $routeVariables[$key] = new $elementType(); |
||||||
151 | } |
||||||
152 | } |
||||||
153 | |||||||
154 | return $routeVariables; |
||||||
155 | } |
||||||
156 | |||||||
157 | /** |
||||||
158 | * Override certain values that we always want hard-coded |
||||||
159 | * |
||||||
160 | * @return array |
||||||
161 | */ |
||||||
162 | protected function overrideValues(): array |
||||||
163 | { |
||||||
164 | return [ |
||||||
165 | // Set the nonce to a blank string, as it changes on every request |
||||||
166 | 'nonce' => '', |
||||||
167 | ]; |
||||||
168 | } |
||||||
169 | } |
||||||
170 |