├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── publish-to-redaxo-org.yml ├── .gitignore ├── package.yml ├── LICENSE ├── fragments └── uk3 │ ├── accordeon_tabs.php │ └── card.php ├── README.md └── lib └── FORHtml └── FORHtml.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: skerbis 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | vendor/autoload.php 3 | vendor/composer/* 4 | vendor/airmanbzh/php-html-generator/tests/* 5 | vendor/airmanbzh/php-html-generator/phpunit.xml 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /package.yml: -------------------------------------------------------------------------------- 1 | package: forhtml 2 | version: '1.1.0' 3 | author: 'Friends Of REDAXO' 4 | supportpage: https://github.com/FriendsOfREDAXO/forhtml 5 | 6 | requires: 7 | redaxo: '^5.14' 8 | php: 9 | version: '>=8.1' 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot config reference 2 | # https://help.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: composer 7 | directory: / 8 | versioning-strategy: increase 9 | schedule: 10 | interval: monthly 11 | open-pull-requests-limit: 15 12 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-redaxo-org.yml: -------------------------------------------------------------------------------- 1 | # Instructions: https://github.com/FriendsOfREDAXO/installer-action/ 2 | 3 | name: Publish to REDAXO.org 4 | on: 5 | release: 6 | types: 7 | - published 8 | 9 | jobs: 10 | redaxo_publish: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - if: hashFiles('composer.json') != '' 15 | uses: shivammathur/setup-php@v2 16 | with: 17 | php-version: "8.2" 18 | - if: hashFiles('composer.json') != '' 19 | uses: ramsey/composer-install@v2 20 | with: 21 | composer-options: "--no-dev" 22 | - uses: FriendsOfREDAXO/installer-action@v1 23 | with: 24 | myredaxo-username: ${{ secrets.MYREDAXO_USERNAME }} 25 | myredaxo-api-key: ${{ secrets.MYREDAXO_API_KEY }} 26 | description: ${{ github.event.release.body }} 27 | version: ${{ github.event.release.tag_name }} 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Friends Of REDAXO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /fragments/uk3/accordeon_tabs.php: -------------------------------------------------------------------------------- 1 | help) && $this->help === true) { 9 | $help = []; 10 | $help['info'] = 'Nimmt ein Array an und erstellt eine Tab oder Akkordeon Liste'; 11 | $help['type'] = 'Bei 1 > Akkordeon, bei 2 Tabs'; 12 | $help['items'] = 'Array mit den Keys title und body'; 13 | dump($help); 14 | } 15 | $values = []; 16 | 17 | if (isset($this->items) && is_array($this->items)) { 18 | $values = array_filter($this->items); 19 | } 20 | $type = 1; 21 | if (isset($this->type)) { 22 | $type = $this->type; 23 | } 24 | ?> 25 | 26 |
= $value['text'] ?>
37 | 38 | 39 | setVar('media', $value['media'], false); 42 | echo $fragment->parse('/uk3/gallery.php'); 43 | }?> 44 |Inhalt
23 | ``` 24 | 25 | ### Verschachtelte Elemente 26 | 27 | ```php 28 | $container = FORHtml::createElement('div') 29 | ->addClass('container') 30 | ->addElement('a') 31 | ->set('href', './seite.php') 32 | ->text('Mein Link') 33 | ->getParent() // Zurück zum Container 34 | ->addElement('p') 35 | ->text('Mein Text'); 36 | 37 | // Ausgabe: 38 | //Mein Text
41 | //
266 | ```
267 |
268 | ---
269 |
270 | ## Credits
271 |
272 | Basierend auf [PHP HTML Generator](https://github.com/Airmanbzh/php-html-generator) von Airmanbzh.
273 |
274 | ## Support
275 |
276 | Bei Fragen oder Problemen bitte ein [GitHub Issue](https://github.com/FriendsOfREDAXO/for-html/issues) erstellen.
277 |
--------------------------------------------------------------------------------
/lib/FORHtml/FORHtml.php:
--------------------------------------------------------------------------------
1 | tag = $tag;
31 | $this->top = $top;
32 | $this->autoclosed = in_array($this->tag, $this->autocloseTagsList);
33 | }
34 |
35 | public static function __callStatic(string $tag, array $content): FORHtml
36 | {
37 | return self::createElement($tag)
38 | ->attr(count($content) && is_array($content[0]) ? array_pop($content) : [])
39 | ->text(implode('', $content));
40 | }
41 |
42 | public function __call(string $tag, array $content): FORHtml
43 | {
44 | return $this
45 | ->addElement($tag)
46 | ->attr(count($content) && is_array($content[0]) ? array_pop($content) : [])
47 | ->text(implode('', $content));
48 | }
49 |
50 | public function __invoke(): FORHtml
51 | {
52 | return $this->getParent();
53 | }
54 |
55 | public static function createElement(string $tag = ''): FORHtml
56 | {
57 | self::$instance = new static($tag);
58 | return self::$instance;
59 | }
60 |
61 | public function addElement(string $tag = ''): FORHtml
62 | {
63 | $htmlTag = (is_object($tag) && $tag instanceof self) ? clone $tag : new static($tag);
64 | $htmlTag->top = $this->getTop();
65 | $htmlTag->parent = $this;
66 | $this->content[] = $htmlTag;
67 | return $htmlTag;
68 | }
69 |
70 | public function set(array|string $attribute, ?string $value = null): FORHtml
71 | {
72 | if (is_array($attribute)) {
73 | foreach ($attribute as $key => $value) {
74 | $this[$key] = $value;
75 | }
76 | } else {
77 | $this[$attribute] = $value;
78 | }
79 | return $this;
80 | }
81 |
82 | public function attr(array|string $attribute, ?string $value = null): FORHtml
83 | {
84 | return $this->set(...func_get_args());
85 | }
86 |
87 | public function offsetExists(mixed $attribute): bool
88 | {
89 | return isset($this->attributeList[$attribute]);
90 | }
91 |
92 | public function offsetGet(mixed $attribute): mixed
93 | {
94 | return $this->attributeList[$attribute] ?? null;
95 | }
96 |
97 | public function offsetSet(mixed $attribute, mixed $value): void
98 | {
99 | $this->attributeList[$attribute] = $value;
100 | }
101 |
102 | public function offsetUnset(mixed $attribute): void
103 | {
104 | unset($this->attributeList[$attribute]);
105 | }
106 |
107 | public function text(string $value): FORHtml
108 | {
109 | $this->addElement('')->text = static::$avoidXSS ? static::unXSS($value) : $value;
110 | return $this;
111 | }
112 |
113 | public function getTop(): FORHtml
114 | {
115 | return $this->top ?? $this;
116 | }
117 |
118 | public function getParent(): ?FORHtml
119 | {
120 | return $this->parent;
121 | }
122 |
123 | public function getFirst(): ?FORHtml
124 | {
125 | return $this->parent->content[0] ?? null;
126 | }
127 |
128 | public function getPrevious(): FORHtml
129 | {
130 | $prev = $this;
131 | if ($this->parent !== null) {
132 | foreach ($this->parent->content as $c) {
133 | if ($c === $this) {
134 | break;
135 | }
136 | $prev = $c;
137 | }
138 | }
139 | return $prev;
140 | }
141 |
142 | public function getNext(): ?FORHtml
143 | {
144 | $next = null;
145 | if ($this->parent !== null) {
146 | $found = false;
147 | foreach ($this->parent->content as $c) {
148 | if ($found) {
149 | $next = $c;
150 | break;
151 | }
152 | if ($c === $this) {
153 | $found = true;
154 | }
155 | }
156 | }
157 | return $next;
158 | }
159 |
160 | public function getLast(): ?FORHtml
161 | {
162 | return $this->parent->content[array_key_last($this->parent->content)] ?? null;
163 | }
164 |
165 | public function remove(): ?FORHtml
166 | {
167 | if ($this->parent !== null) {
168 | foreach ($this->parent->content as $key => $value) {
169 | if ($value === $this) {
170 | unset($this->parent->content[$key]);
171 | }
172 | }
173 | }
174 | return null;
175 | }
176 |
177 | public function __toString(): string
178 | {
179 | return $this->getTop()->toString();
180 | }
181 |
182 | public function toString(): string
183 | {
184 | $string = '';
185 | if (!empty($this->tag)) {
186 | $string .= "<{$this->tag}" . $this->attributesToString();
187 | $string .= $this->autoclosed ? '/>' : ">{$this->contentToString()}{$this->tag}>";
188 | } else {
189 | $string .= $this->text . $this->contentToString();
190 | }
191 | return $string;
192 | }
193 |
194 | protected function attributesToString(): string
195 | {
196 | $string = '';
197 | $XMLConvention = in_array(static::$outputLanguage, [ENT_XML1, ENT_XHTML]);
198 | foreach ($this->attributeList as $key => $value) {
199 | if ($value !== null && ($value !== false || $XMLConvention)) {
200 | if (is_array($value)) {
201 | $value = implode(' ', $value);
202 | }
203 | $escapedValue = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
204 | $string .= " {$key}=\"{$escapedValue}\"";
205 | }
206 | }
207 | return $string;
208 | }
209 |
210 | protected function contentToString(): string
211 | {
212 | return array_reduce($this->content, fn($carry, $c) => $carry . $c->toString(), '');
213 | }
214 |
215 | public static function unXSS(string $input): string
216 | {
217 | return htmlentities($input, ENT_QUOTES | ENT_DISALLOWED | static::$outputLanguage);
218 | }
219 |
220 | // Methods from HtmlTag
221 | public function id(string $value): FORHtml
222 | {
223 | return $this->set('id', $value);
224 | }
225 |
226 | public function addClass(string $value): FORHtml
227 | {
228 | if (!isset($this->attributeList['class']) || is_null($this->attributeList['class'])) {
229 | $this->attributeList['class'] = [];
230 | }
231 | $this->attributeList['class'][] = $value;
232 | return $this;
233 | }
234 |
235 | public function removeClass(string $value): FORHtml
236 | {
237 | if (!is_null($this->attributeList['class'])) {
238 | unset($this->attributeList['class'][array_search($value, $this->attributeList['class'])]);
239 | }
240 | return $this;
241 | }
242 |
243 | public function mmfile(string $type = 'default', string $file =''): string
244 | {
245 | return $this->set('src', rex_media_manager::getUrl($type, $file));
246 | }
247 |
248 | public function content(string $content =''): string
249 | {
250 | return $this->text($content);
251 | }
252 |
253 | // Neue Methode für Fragmente
254 | public function parseFragment(string $template, array $vars = []): FORHtml
255 | {
256 | $fragment = new rex_fragment();
257 | foreach ($vars as $key => $value) {
258 | $fragment->setVar($key, $value, false);
259 | }
260 | $this->text($fragment->parse($template));
261 | return $this;
262 | }
263 | }
264 |
--------------------------------------------------------------------------------