1 | <?php |
||
2 | /** |
||
3 | * @package midcom.helper |
||
4 | * @author The Midgard Project, http://www.midgard-project.org |
||
5 | * @copyright The Midgard Project, http://www.midgard-project.org |
||
6 | * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License |
||
7 | */ |
||
8 | |||
9 | use midcom\templating\loader; |
||
10 | |||
11 | /** |
||
12 | * This class is responsible for all style management. It is |
||
13 | * accessible through the midcom::get()->style object. |
||
14 | * |
||
15 | * The method <code>show($style)</code> returns the style element $style for the current |
||
16 | * component: |
||
17 | * |
||
18 | * It checks whether a style path is defined for the current component. |
||
19 | * |
||
20 | * - If there is a user defined style path, the element named $style in |
||
21 | * this path is returned, |
||
22 | * - otherwise the element "$style" is taken from the default style of the |
||
23 | * current component (/path/to/component/style/$path). |
||
24 | * |
||
25 | * (The default fallback is always the default style, e.g. if $style |
||
26 | * is not in the user defined style path) |
||
27 | * |
||
28 | * To enable cross-style referencing and provide the opportunity to access |
||
29 | * any style element, "show" can be called with a full qualified style |
||
30 | * path (like "/mystyle/element1", while the current page's style may be set |
||
31 | * to "/yourstyle"). |
||
32 | * |
||
33 | * Note: To make sure sub-styles and elements included in styles are handled |
||
34 | * correctly, use: |
||
35 | * |
||
36 | * <code> |
||
37 | * <?php midcom_show_style ("elementname"); ?> |
||
38 | * </code> |
||
39 | * |
||
40 | * @package midcom.helper |
||
41 | */ |
||
42 | class midcom_helper_style |
||
43 | { |
||
44 | /** |
||
45 | * Current topic |
||
46 | */ |
||
47 | private ?midcom_db_topic $_topic = null; |
||
48 | |||
49 | /** |
||
50 | * Context stack |
||
51 | * |
||
52 | * @var midcom_core_context[] |
||
53 | */ |
||
54 | private array $_context = []; |
||
55 | |||
56 | /** |
||
57 | * List of styledirs to handle after componentstyle |
||
58 | */ |
||
59 | private array $_styledirs_append = []; |
||
60 | |||
61 | /** |
||
62 | * List of styledirs to handle before componentstyle |
||
63 | */ |
||
64 | private array $_styledirs_prepend = []; |
||
65 | |||
66 | private loader $loader; |
||
67 | |||
68 | /** |
||
69 | * Data to pass to the style |
||
70 | */ |
||
71 | public array $data; |
||
72 | |||
73 | 1 | public function __construct(loader $loader) |
|
74 | { |
||
75 | 1 | $this->loader = $loader; |
|
76 | } |
||
77 | |||
78 | /** |
||
79 | * Looks for a style element matching $path (either in a user defined style |
||
80 | * or the default style snippetdir) and displays/evaluates it. |
||
81 | */ |
||
82 | 207 | public function show(string $path) : bool |
|
83 | { |
||
84 | 207 | if ($this->_context === []) { |
|
85 | debug_add("Trying to show '{$path}' but there is no context set", MIDCOM_LOG_INFO); |
||
86 | return false; |
||
87 | } |
||
88 | |||
89 | 207 | $style = $this->loader->get_element($path, true); |
|
90 | |||
91 | 207 | if ($style === null) { |
|
92 | 24 | if ($path == 'ROOT') { |
|
93 | // Go to fallback ROOT instead of displaying a blank page |
||
94 | return $this->show_midcom($path); |
||
95 | } |
||
96 | |||
97 | 24 | debug_add("The element '{$path}' could not be found.", MIDCOM_LOG_INFO); |
|
98 | 24 | return false; |
|
99 | } |
||
100 | 206 | $this->render($style, $path); |
|
101 | |||
102 | 206 | return true; |
|
103 | } |
||
104 | |||
105 | /** |
||
106 | * Renders the style element with current request data |
||
107 | * |
||
108 | * @param string $style The style element content |
||
109 | * @param string $path the element's name |
||
110 | * @throws midcom_error |
||
111 | */ |
||
112 | 206 | private function render(string $style, string $path) |
|
113 | { |
||
114 | 206 | if (midcom::get()->config->get('wrap_style_show_with_name')) { |
|
115 | $style = "\n<!-- Start of style '{$path}' -->\n" . $style; |
||
116 | $style .= "\n<!-- End of style '{$path}' -->\n"; |
||
117 | } |
||
118 | |||
119 | // Resolve variables |
||
120 | 206 | $preparsed = midcom_helper_formatter::compile($style); |
|
121 | |||
122 | 206 | if (midcom_core_context::get()->has_custom_key('request_data')) { |
|
123 | 206 | $data =& midcom_core_context::get()->get_custom_key('request_data'); |
|
0 ignored issues
–
show
Unused Code
introduced
by
![]() |
|||
124 | } |
||
125 | |||
126 | try { |
||
127 | 206 | eval('?>' . $preparsed); |
|
0 ignored issues
–
show
|
|||
128 | } catch (ParseError $e) { |
||
0 ignored issues
–
show
catch (\ParseError $e) is not reachable.
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed. Unreachable code is most often the result of function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last ![]() |
|||
129 | throw new midcom_error("Failed to parse style element '{$path}': " . $e->getMessage() . ' in line ' . $e->getLine()); |
||
130 | } |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * Looks for a midcom core style element matching $path and displays/evaluates it. |
||
135 | * This offers a bit reduced functionality and will only look in the DB root style, |
||
136 | * the theme directory and midcom's style directory, because it has to work even when |
||
137 | * midcom is not yet fully initialized |
||
138 | */ |
||
139 | 1 | public function show_midcom(string $path) : bool |
|
140 | { |
||
141 | 1 | $loader = clone $this->loader; |
|
142 | 1 | $loader->set_directories(null, [MIDCOM_ROOT . '/midcom/style'], []); |
|
143 | |||
144 | 1 | $_style = $loader->get_element($path, false); |
|
145 | |||
146 | 1 | if ($_style !== null) { |
|
147 | 1 | $this->render($_style, $path); |
|
148 | 1 | return true; |
|
149 | } |
||
150 | debug_add("The element '{$path}' could not be found.", MIDCOM_LOG_INFO); |
||
151 | return false; |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Adds an extra style directory to check for style elements at |
||
156 | * the end of the styledir queue. |
||
157 | * |
||
158 | * @throws midcom_error exception if directory does not exist. |
||
159 | */ |
||
160 | 75 | public function append_styledir(string $dirname) |
|
161 | { |
||
162 | 75 | if (!file_exists($dirname)) { |
|
163 | throw new midcom_error("Style directory $dirname does not exist!"); |
||
164 | } |
||
165 | 75 | $this->_styledirs_append[midcom_core_context::get()->id][] = $dirname; |
|
166 | } |
||
167 | |||
168 | /** |
||
169 | * Function prepend styledir |
||
170 | */ |
||
171 | 83 | public function prepend_styledir(string $dirname) |
|
172 | { |
||
173 | 83 | if (!file_exists($dirname)) { |
|
174 | throw new midcom_error("Style directory {$dirname} does not exist."); |
||
175 | } |
||
176 | 83 | $this->_styledirs_prepend[midcom_core_context::get()->id][] = $dirname; |
|
177 | } |
||
178 | |||
179 | /** |
||
180 | * Append the styledir of a component to the queue of styledirs. |
||
181 | */ |
||
182 | public function append_component_styledir(string $component) |
||
183 | { |
||
184 | $path = midcom::get()->componentloader->path_to_snippetpath($component) . "/style"; |
||
185 | $this->append_styledir($path); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Prepend the styledir of a component |
||
190 | */ |
||
191 | 83 | public function prepend_component_styledir(string $component) |
|
192 | { |
||
193 | 83 | $path = midcom::get()->componentloader->path_to_snippetpath($component) . "/style"; |
|
194 | 83 | $this->prepend_styledir($path); |
|
195 | } |
||
196 | |||
197 | /** |
||
198 | * Appends a substyle after the currently selected component style. |
||
199 | * |
||
200 | * Enables a depth of more than one style during substyle selection. |
||
201 | */ |
||
202 | 31 | public function append_substyle(string $newsub) |
|
203 | { |
||
204 | // Make sure try to use only the first argument if we get space separated list, fixes #1788 |
||
205 | 31 | if (str_contains($newsub, ' ')) { |
|
206 | $newsub = preg_replace('/^(.+?) .+/', '$1', $newsub); |
||
207 | } |
||
208 | |||
209 | 31 | $context = midcom_core_context::get(); |
|
210 | 31 | $current_style = $context->get_key(MIDCOM_CONTEXT_SUBSTYLE); |
|
211 | |||
212 | 31 | if (!empty($current_style)) { |
|
213 | $newsub = $current_style . '/' . $newsub; |
||
214 | } |
||
215 | |||
216 | 31 | $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $newsub); |
|
217 | } |
||
218 | |||
219 | /** |
||
220 | * Prepends a substyle before the currently selected component style. |
||
221 | * |
||
222 | * Enables a depth of more than one style during substyle selection. |
||
223 | * |
||
224 | * @param string $newsub The substyle to prepend. |
||
225 | */ |
||
226 | function prepend_substyle($newsub) |
||
0 ignored issues
–
show
|
|||
227 | { |
||
228 | $context = midcom_core_context::get(); |
||
229 | $current_style = $context->get_key(MIDCOM_CONTEXT_SUBSTYLE); |
||
230 | |||
231 | if (!empty($current_style)) { |
||
232 | $newsub .= "/" . $current_style; |
||
233 | } |
||
234 | debug_add("Updating Component Context Substyle from $current_style to $newsub"); |
||
235 | |||
236 | $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $newsub); |
||
237 | } |
||
238 | |||
239 | /** |
||
240 | * Switches the context (see dynamic load). |
||
241 | * |
||
242 | * Private variables are adjusted, and the prepend and append styles are merged with the componentstyle. |
||
243 | * You cannot change the style stack after that (unless you call enter_context again of course). |
||
244 | */ |
||
245 | 275 | public function enter_context(midcom_core_context $context) |
|
246 | { |
||
247 | // set new context and topic |
||
248 | 275 | array_unshift($this->_context, $context); // push into context stack |
|
249 | |||
250 | 275 | if ($this->_topic = $context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC)) { |
|
251 | 275 | $style = $this->_topic->style ?: $context->get_inherited_style(); |
|
252 | 275 | if (!$style) { |
|
253 | 275 | $styleengine_default_styles = midcom::get()->config->get('styleengine_default_styles'); |
|
254 | 275 | if (isset($styleengine_default_styles[$this->_topic->component])) { |
|
255 | $style = $styleengine_default_styles[$this->_topic->component]; |
||
256 | } |
||
257 | } |
||
258 | 275 | $this->loader->initialize($context, $style); |
|
259 | } |
||
260 | |||
261 | 275 | $this->loader->set_directories( |
|
262 | 275 | $this->_topic, |
|
263 | 275 | $this->_styledirs_prepend[$context->id] ?? [], |
|
264 | 275 | $this->_styledirs_append[$context->id] ?? [] |
|
265 | 275 | ); |
|
266 | } |
||
267 | |||
268 | /** |
||
269 | * Switches the context (see dynamic load). Private variables $_context, $_topic |
||
270 | * and $loader are adjusted. |
||
271 | */ |
||
272 | 275 | public function leave_context() |
|
273 | { |
||
274 | 275 | array_shift($this->_context); |
|
275 | 275 | $previous_context = $this->_context[0] ?? midcom_core_context::get(); |
|
276 | |||
277 | 275 | $this->_topic = $previous_context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC); |
|
278 | 275 | $this->loader->set_directories( |
|
279 | 275 | $this->_topic, |
|
280 | 275 | $this->_styledirs_prepend[$previous_context->id] ?? [], |
|
281 | 275 | $this->_styledirs_append[$previous_context->id] ?? [] |
|
282 | 275 | ); |
|
283 | } |
||
284 | } |
||
285 |