... | ... |
@@ -1,36 +1,40 @@ |
1 |
-<p align="center"><img src="https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/cme-logo.svg" width="200"></p> |
|
1 |
+<p align="center"><img src="/assets/markdown/logo.svg" width="200" alt="Contao Member Extension Bundle"></p> |
|
2 | 2 |
<h1 align="center">Contao Member Extension Bundle</h1> |
3 | 3 |
<p align="center"><i>Adds a listing of members with detail pages and extends them with an avatar that can be uploaded and deleted</i></p> |
4 | 4 |
<p align="center"> |
5 |
- <a href="https://www.oveleon.de"><img src="https://img.shields.io/badge/oveleon-maintained-83aa0e?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAUCAYAAABvVQZ0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/xpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjVEMjA4OTI0OTNCRkRCMTE5MTRBODU5MEQzMTUwOEM4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjM5MjZBNjQzMzZFQjExRUFBMTdBQkNFQTAxNjg2RDI4IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjM5MjZBNjQyMzZFQjExRUFBMTdBQkNFQTAxNjg2RDI4IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIElsbHVzdHJhdG9yIENTNiAoV2luZG93cykiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0idXVpZDplMDhkZDhmZC1mOTA4LTQ5YzItYWMwZC00OGE3YTI4ODc2YWEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OTA2RDhGOENERUQxRTgxMTgyMjVBMzBGQ0NBNjE4RUQiLz4gPGRjOnRpdGxlPiA8cmRmOkFsdD4gPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ij5Mb2dvX292ZWxlb25fWmVpY2hlbl9yejwvcmRmOmxpPiA8L3JkZjpBbHQ+IDwvZGM6dGl0bGU+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+P8iBTQAAAbFJREFUeNqU08srRFEcB/BromGGaJRHKSIaYfIm7JRSsvHIyuM/wMorNeEfUMqKnbKwU2TBwpSUSWYWFmSBDKWmWcgwcX2Pvrc5HXfuXL/6zD2P3z2Puedouq5rilG40JPxAuUmeX84tGTkwyXsghOWYQmKoFazExzVDXFIgEeazcPV9TCnzGplRiHMl7KgCWYghy9vQAasMEfkTqQarJ9JFWw0YlhJFv2rUv8tdKiDhTib0eiHQyhOsR2xyjlp0BF5MBELdr6WohE++X6raBOz6PwWCfiCOLzDN8sRGICYyfdrgBA8Q2kmfnzQJirghlw+XZANUQ5qFmHY52R9/9lWCXjBpbRPcaubDhtHsRoOuN1reAO/1H/PpzfdaqrggzOLK7YFD6zPMqeL9at0gwWZuKa0i3hkuZf1E6tteqEZnmCRbZVwxPIOn3V83litaogzbrPuhDOIwbyUd8q8SavBOpl0rnxROadFugkFVoOJAx1h4rRJvw9e2b9u3ACrY9ENAZaD/L/E0aiHMbbfQc3v7bFxWAchqptHAAqN3HQrM0JcsXFohzzexWPYk5N+BBgAix5VyvzRZbwAAAAASUVORK5CYII=" alt="Oveleon"></a> |
|
6 |
- <a href="https://github.com/oveleon/https://github.com/oveleon/contao-member-extension-bundle"><img src="https://img.shields.io/badge/license-MIT-83aa0e?style=flat-square"/></a> |
|
7 |
- <a href="https://packagist.org/packages/oveleon/contao-member-extension-bundle"><img src="https://img.shields.io/packagist/dt/oveleon/contao-member-extension-bundle?color=0A7BBC&style=flat-square"/></a> |
|
5 |
+ <a href="https://github.com/oveleon/contao-member-extension-bundle"><img src="https://img.shields.io/github/license/oveleon/contao-member-extension-bundle?color=ef9838"/></a> |
|
6 |
+ <a href="https://www.oveleon.de"><img src="https://img.shields.io/badge/oveleon-maintained-ef9838?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAUCAYAAABvVQZ0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/xpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjVEMjA4OTI0OTNCRkRCMTE5MTRBODU5MEQzMTUwOEM4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjM5MjZBNjQzMzZFQjExRUFBMTdBQkNFQTAxNjg2RDI4IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjM5MjZBNjQyMzZFQjExRUFBMTdBQkNFQTAxNjg2RDI4IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIElsbHVzdHJhdG9yIENTNiAoV2luZG93cykiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0idXVpZDplMDhkZDhmZC1mOTA4LTQ5YzItYWMwZC00OGE3YTI4ODc2YWEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OTA2RDhGOENERUQxRTgxMTgyMjVBMzBGQ0NBNjE4RUQiLz4gPGRjOnRpdGxlPiA8cmRmOkFsdD4gPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ij5Mb2dvX292ZWxlb25fWmVpY2hlbl9yejwvcmRmOmxpPiA8L3JkZjpBbHQ+IDwvZGM6dGl0bGU+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+P8iBTQAAAbFJREFUeNqU08srRFEcB/BromGGaJRHKSIaYfIm7JRSsvHIyuM/wMorNeEfUMqKnbKwU2TBwpSUSWYWFmSBDKWmWcgwcX2Pvrc5HXfuXL/6zD2P3z2Puedouq5rilG40JPxAuUmeX84tGTkwyXsghOWYQmKoFazExzVDXFIgEeazcPV9TCnzGplRiHMl7KgCWYghy9vQAasMEfkTqQarJ9JFWw0YlhJFv2rUv8tdKiDhTib0eiHQyhOsR2xyjlp0BF5MBELdr6WohE++X6raBOz6PwWCfiCOLzDN8sRGICYyfdrgBA8Q2kmfnzQJirghlw+XZANUQ5qFmHY52R9/9lWCXjBpbRPcaubDhtHsRoOuN1reAO/1H/PpzfdaqrggzOLK7YFD6zPMqeL9at0gwWZuKa0i3hkuZf1E6tteqEZnmCRbZVwxPIOn3V83litaogzbrPuhDOIwbyUd8q8SavBOpl0rnxROadFugkFVoOJAx1h4rRJvw9e2b9u3ACrY9ENAZaD/L/E0aiHMbbfQc3v7bFxWAchqptHAAqN3HQrM0JcsXFohzzexWPYk5N+BBgAix5VyvzRZbwAAAAASUVORK5CYII=" alt="Oveleon"></a> |
|
7 |
+ <a href="https://packagist.org/packages/oveleon/contao-member-extension-bundle"><img src="https://img.shields.io/packagist/dt/oveleon/contao-member-extension-bundle?color=59a192"/></a> |
|
8 |
+ <a href="https://packagist.org/packages/oveleon/contao-member-extension-bundle"><img src="https://img.shields.io/packagist/dependency-v/oveleon/contao-member-extension-bundle/php?color=59a192"></a> |
|
9 |
+<a href="https://github.com/sponsors/oveleon"><img src="https://img.shields.io/github/sponsors/oveleon?label=Sponsor&logo=GitHub&color=%23fe8e86"/></a> |
|
8 | 10 |
</p> |
9 | 11 |
<br/> |
10 | 12 |
|
11 | 13 |
--- |
12 | 14 |
|
13 |
-> Working with **Contao 4.13** (PHP ^8.0) |
|
15 |
+> Working with **Contao 4.13** and **Contao ^5.3** (PHP ^8.1) |
|
14 | 16 |
|
15 | 17 |
--- |
16 | 18 |
|
17 | 19 |
The Member extension bunde adds the possibility to display members and their details in lists using frontend modules. |
18 |
-The member options are extended with an avatar that can be changed and uploaded in the member edit module and registration module. |
|
19 |
-Additionally, you can display members with their details in a reader page. |
|
20 |
+The member options are extended with an avatar that can be changed and uploaded in the member edit module and |
|
21 |
+registration module. Additionally, you can display members with their details in a reader page. |
|
20 | 22 |
|
21 | 23 |
+ [Features](#features) |
22 | 24 |
+ [Installation](#installation) |
23 |
- + [Upgrading (v.1.1 to >=v.1.2)](#upgrading-to-version--12) |
|
24 |
- + [Composer](#via-composer) |
|
25 |
- + [Contao Manager](#via-contao-manager) |
|
25 |
+ + [Composer](#via-composer) |
|
26 |
+ + [Contao Manager](#via-contao-manager) |
|
26 | 27 |
+ [Initial Setup](#initial-setup) |
27 | 28 |
+ [Insert tags](#insert-tags) |
28 |
- + [Avatar insert tags](#avatar-insert-tags) |
|
29 |
+ + [Avatar insert tags](#avatar-insert-tags) |
|
29 | 30 |
+ [Front end modules](#front-end-modules) |
30 |
- + [Memberlist](#memberlist) |
|
31 |
- + [Memberreader](#memberreader) |
|
32 |
- + [Avatar / Profile picture](#avatar--profile-picture) |
|
33 |
- + [Delete avatar](#delete-avatar) |
|
31 |
+ + [Memberlist](#memberlist) |
|
32 |
+ + [Memberreader](#memberreader) |
|
33 |
+ + [Avatar / Profile picture](#avatar--profile-picture) |
|
34 |
+ + [Delete avatar](#delete-avatar) |
|
35 |
++ [Advanced](#advanced) |
|
36 |
+ + [Filter](#filter) |
|
37 |
+ + [Hooks](#hooks) |
|
34 | 38 |
+ [Support](#support) |
35 | 39 |
+ [Sponsoring](#sponsoring) |
36 | 40 |
|
... | ... |
@@ -41,20 +45,21 @@ Additionally, you can display members with their details in a reader page. |
41 | 45 |
- Paginated member lists |
42 | 46 |
- Member detail pages |
43 | 47 |
- Insert tags for member avatars |
48 |
+- Sortable data-tables (funded by @netzarbeiter) |
|
49 |
+ - requires jQuery to work (https://datatables.net/) |
|
44 | 50 |
|
45 | 51 |
--- |
46 | 52 |
|
47 | 53 |
## Installation |
48 | 54 |
|
49 |
-#### Upgrading to version >=1.2 |
|
50 |
-> After upgrading from version 1.1 to version >=1.2, make sure to edit your modules (memberlist, memberreader and avatar/profile picture) and set up the new templates. |
|
51 |
- |
|
52 | 55 |
#### Via composer |
56 |
+ |
|
53 | 57 |
``` |
54 | 58 |
composer require oveleon/contao-member-extension-bundle |
55 | 59 |
``` |
56 | 60 |
|
57 | 61 |
#### Via contao-manager |
62 |
+ |
|
58 | 63 |
``` |
59 | 64 |
Search for contao member extension bundle and add it to your extensions. |
60 | 65 |
``` |
... | ... |
@@ -64,50 +69,54 @@ After installing the contao-member-extension-bundle, you need to run a **contao |
64 | 69 |
--- |
65 | 70 |
|
66 | 71 |
## Initial setup |
72 |
+ |
|
67 | 73 |
This bundle extends contao with the possibiity to extend members with an avatar and displaying members in a list with |
68 | 74 |
detail pages. |
69 | 75 |
|
70 | 76 |
1. Go into members and set up a default avatar in the newly added settings |
71 | 77 |
|
72 |
-  |
|
73 |
-  |
|
78 |
+  |
|
79 |
+  |
|
74 | 80 |
|
75 | 81 |
2. To display your members, you need to set up a memberlist |
76 |
- 1. Create the front end module *memberlist* |
|
77 |
- 2. Choose the member groups and the member fields that should be displayed |
|
78 |
- 3. Optionally you can set up a redirect page to your memberreader |
|
79 |
- 4. Embed the module in a page |
|
82 |
+ 1. Create the front end module *memberlist* |
|
83 |
+ 2. Choose the member groups and the member fields that should be displayed |
|
84 |
+ 3. Optionally you can set up a redirect page to your memberreader |
|
85 |
+ 4. Embed the module in a page |
|
80 | 86 |
|
81 |
-  |
|
87 |
+  |
|
82 | 88 |
|
83 | 89 |
3. Displaying the avatar |
84 |
- 1. Create the front end module *Avatar / profile picture* |
|
85 |
- 2. Optionally you can set an image size |
|
86 |
- 3. Embed the module in a page |
|
90 |
+ 1. Create the front end module *Avatar / profile picture* |
|
91 |
+ 2. Optionally you can set an image size |
|
92 |
+ 3. Embed the module in a page |
|
87 | 93 |
|
88 |
-  |
|
94 |
+  |
|
89 | 95 |
|
90 | 96 |
4. Module to delete an avatar |
91 |
- 1. Create the front end module *Delete Avatar* |
|
92 |
- 2. Embed the module in a page |
|
93 |
- 3. The module only appears if a frontend user is logged in |
|
94 |
- 4. You can check the "profile picture option" within registration to enable members to upload a profile picture within |
|
95 |
- registration |
|
97 |
+ 1. Create the front end module *Delete Avatar* |
|
98 |
+ 2. Embed the module in a page |
|
99 |
+ 3. The module only appears if a frontend user is logged in |
|
100 |
+ 4. You can check the "profile picture option" within registration to enable members to upload a profile picture |
|
101 |
+ within |
|
102 |
+ registration |
|
96 | 103 |
|
97 |
-  |
|
104 |
+  |
|
98 | 105 |
|
99 |
-6. Member reader page |
|
100 |
- 1. Create the front end module *memberreader* |
|
101 |
- 2. Choose the member groups and the member fields that are allowed |
|
106 |
+5. Member reader page |
|
107 |
+ 1. Create the front end module *memberreader* |
|
108 |
+ 2. Choose the member groups and the member fields that are allowed |
|
102 | 109 |
|
103 | 110 |
--- |
104 | 111 |
|
105 | 112 |
## Insert tags |
113 |
+ |
|
106 | 114 |
Member avatars can be shown using following *insert-tags* |
107 | 115 |
|
108 | 116 |
> For more information on *Insert tags*, please visit the official <a href="https://docs.contao.org/manual/en/article-management/insert-tags/" title="Insert tags :: Contao Manual" target="_blank">Contao documentation</a>. |
109 | 117 |
|
110 | 118 |
**Example** |
119 |
+ |
|
111 | 120 |
``` |
112 | 121 |
{{avatar::member::current}} |
113 | 122 |
{{avatar::member::current::200x200xproportional}} |
... | ... |
@@ -123,9 +132,10 @@ The allowed image size parameters are: |
123 | 132 |
"<strong>width</strong> x <strong>height</strong> x <strong>mode</strong>" |
124 | 133 |
|
125 | 134 |
Size mode (See: [Size Array](https://docs.contao.org/dev/framework/image-processing/image-sizes/#size-array)) |
135 |
+ |
|
126 | 136 |
- crop |
127 |
-- proportional |
|
128 | 137 |
- box |
138 |
+- proportional _(Contao 4.13 only)_ |
|
129 | 139 |
|
130 | 140 |
The standard mode vor avatar insert tags is *crop* |
131 | 141 |
|
... | ... |
@@ -171,16 +181,16 @@ The standard mode vor avatar insert tags is *crop* |
171 | 181 |
|
172 | 182 |
### Memberlist |
173 | 183 |
|
174 |
-Displays activated members in a list |
|
184 |
+Displays activated members in a list. |
|
175 | 185 |
|
176 | 186 |
### Memberreader |
177 | 187 |
|
178 |
-Displays a detail page of a member |
|
188 |
+Displays a detail page of a member. |
|
179 | 189 |
|
180 | 190 |
### Avatar / Profile picture |
181 | 191 |
|
182 |
-Displays an avatar of a member. If no avatar has been uploaded, the default avatar (or the fallback avatar from the bundle) |
|
183 |
-will be shown |
|
192 |
+Displays an avatar of a member. If no avatar has been uploaded, the default avatar (or the fallback avatar from the |
|
193 |
+bundle) will be shown. |
|
184 | 194 |
|
185 | 195 |
### Delete Avatar |
186 | 196 |
|
... | ... |
@@ -188,12 +198,99 @@ A module that can be embedded into a page that adds the possibility to delete th |
188 | 198 |
|
189 | 199 |
--- |
190 | 200 |
|
201 |
+## Advanced |
|
202 |
+ |
|
203 |
+The member extension provides additional options that can be used with programmatic knowledge. |
|
204 |
+ |
|
205 |
+### Filter |
|
206 |
+ |
|
207 |
+Allows filtering the member list in the frontend if the following conditions are met: |
|
208 |
+ |
|
209 |
+- 'Activate filters' is set to true within the member list module |
|
210 |
+- there exists fields within `tl_member` of inputType `checkbox` and evaluation `feFilterable` set to true |
|
211 |
+ |
|
212 |
+### Hooks |
|
213 |
+ |
|
214 |
+#### getMembers |
|
215 |
+ |
|
216 |
+Allows modifying the columns and options for the database query. |
|
217 |
+ |
|
218 |
+```php |
|
219 |
+// src/EventListener/onGetMembersListener.php |
|
220 |
+namespace App\EventListener; |
|
221 |
+ |
|
222 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; |
|
223 |
+use Oveleon\ContaoMemberExtensionBundle\Controller\FrontendModule\MemberListController; |
|
224 |
+ |
|
225 |
+#[AsHook('getMembers')] |
|
226 |
+class onGetMembersListener |
|
227 |
+{ |
|
228 |
+ public function __invoke(array &$columns, array &$options, MemberListController &$context): void |
|
229 |
+ { |
|
230 |
+ // Do something... |
|
231 |
+ } |
|
232 |
+} |
|
233 |
+``` |
|
234 |
+ |
|
235 |
+#### parseMemberReader |
|
236 |
+ |
|
237 |
+Allows modifying the member detail page |
|
238 |
+ |
|
239 |
+```php |
|
240 |
+// src/EventListener/onParseMemberReaderListener.php |
|
241 |
+namespace App\EventListener; |
|
242 |
+ |
|
243 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; |
|
244 |
+use Contao\MemberModel; |
|
245 |
+use Contao\Model; |
|
246 |
+use Contao\ModuleModel; |
|
247 |
+use Contao\Template; |
|
248 |
+use Oveleon\ContaoMemberExtensionBundle\Controller\FrontendModule\MemberReaderController; |
|
249 |
+ |
|
250 |
+#[AsHook('parseMemberReader')] |
|
251 |
+class onParseMemberReaderListener |
|
252 |
+{ |
|
253 |
+ public function __invoke(MemberModel|Model &$member, Template &$template, ModuleModel &$model, MemberReaderController &$context): void |
|
254 |
+ { |
|
255 |
+ // Do something... |
|
256 |
+ } |
|
257 |
+} |
|
258 |
+``` |
|
259 |
+ |
|
260 |
+#### parseMemberTemplate |
|
261 |
+ |
|
262 |
+Allows modifying the member details |
|
263 |
+ |
|
264 |
+```php |
|
265 |
+// src/EventListener/onParseMemberTemplateListener.php |
|
266 |
+namespace App\EventListener; |
|
267 |
+ |
|
268 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; |
|
269 |
+use Contao\FrontendTemplate; |
|
270 |
+use Contao\MemberModel; |
|
271 |
+use Contao\Model; |
|
272 |
+use Contao\ModuleModel; |
|
273 |
+use Oveleon\ContaoMemberExtensionBundle\Controller\FrontendModule\MemberExtensionController; |
|
274 |
+ |
|
275 |
+#[AsHook('parseMemberTemplate')] |
|
276 |
+class onParseMemberTemplateListener |
|
277 |
+{ |
|
278 |
+ public function __invoke(MemberModel|Model &$member, array &$fields, FrontendTemplate &$template, ModuleModel &$model, MemberExtensionController &$context): void |
|
279 |
+ { |
|
280 |
+ // Do something... |
|
281 |
+ } |
|
282 |
+} |
|
283 |
+``` |
|
284 |
+ |
|
285 |
+--- |
|
286 |
+ |
|
191 | 287 |
## Support |
288 |
+ |
|
192 | 289 |
> We **only provide support** for **bugs, and feature requests**; please only post issues about these two topics. |
193 | 290 |
> |
194 |
-> If you need help implementing Contao Member Extension Bundle or you are just starting out |
|
291 |
+> If you need help implementing Contao Member Extension Bundle or you are just starting out |
|
195 | 292 |
> with Contao/CSS or HTML, please contact us on our [website](https://www.oveleon.de/kontakt.html#kontaktformular), |
196 |
-> visit the [Contao Community](https://community.contao.org/) |
|
293 |
+> visit the [Contao Community](https://community.contao.org/) |
|
197 | 294 |
> or the [Contao Slack](https://join.slack.com/t/contao/shared_invite/enQtNjUzMjY4MDU0ODM3LWVjYWMzODVkZjM5NjdlNDRiZjk2OTI3OWVkMmQ1YjA0MTQ3YTljMjFjODkwYTllN2NkMDcxMThiNzMzZjZlOGU), |
198 | 295 |
> you will be able to find more help there. |
199 | 296 |
> |
... | ... |
@@ -203,6 +300,11 @@ A module that can be embedded into a page that adds the possibility to delete th |
203 | 300 |
|
204 | 301 |
## Sponsoring |
205 | 302 |
|
206 |
-If you find this plugin useful, please consider [sponsoring us](https://github.com/sponsors/oveleon) |
|
207 |
-to help contribute to our time invested and to further development of this and other open source projects. |
|
208 |
-Thank you for your support! - [Oveleon](https://www.oveleon.de). |
|
303 |
+If you think this plugin is useful, please consider [sponsoring us](https://github.com/sponsors/oveleon) to help |
|
304 |
+contribute to our time invested and to further development of this and other open source projects. |
|
305 |
+ |
|
306 |
+Your contributions, whether through `coding`, `testing`, `providing feedback`, or even |
|
307 |
+a [donation](https://github.com/sponsors/oveleon), help ensure that we can continue offering free open source software. |
|
308 |
+Join us in making a difference, and thank you for your support! - [Oveleon](https://www.oveleon.de). |
|
309 |
+ |
|
310 |
+[](https://github.com/sponsors/oveleon) |
... | ... |
@@ -1,60 +1,62 @@ |
1 | 1 |
{ |
2 |
- "name": "vonrotenberg/contao-member-extension-bundle", |
|
3 |
- "type": "contao-bundle", |
|
4 |
- "description": "Member feature extension for Contao.", |
|
5 |
- "license": "MIT", |
|
6 |
- "keywords": ["contao","member-extension-bundle"], |
|
7 |
- "homepage": "https://oveleon.de/", |
|
8 |
- "authors": [ |
|
9 |
- { |
|
10 |
- "name": "Sebastian Zoglowek", |
|
11 |
- "homepage": "https://github.com/zoglo", |
|
12 |
- "role": "Developer" |
|
13 |
- }, |
|
14 |
- { |
|
15 |
- "name": "Daniele Sciannimanica", |
|
16 |
- "homepage": "https://github.com/doishub", |
|
17 |
- "role":"Developer" |
|
18 |
- }, |
|
19 |
- { |
|
20 |
- "name": "Fabian Ekert", |
|
21 |
- "homepage": "https://github.com/eki89", |
|
22 |
- "role": "Developer" |
|
23 |
- } |
|
24 |
- ], |
|
25 |
- "require": { |
|
26 |
- "php": "^8.0", |
|
27 |
- "contao/core-bundle": "^4.13 || ^5.2" |
|
28 |
- }, |
|
29 |
- "require-dev": { |
|
30 |
- "contao/manager-plugin": "^2.3.1" |
|
2 |
+ "name": "vonrotenberg/contao-member-extension-bundle", |
|
3 |
+ "type": "contao-bundle", |
|
4 |
+ "description": "Member feature extension for Contao.", |
|
5 |
+ "license": "MIT", |
|
6 |
+ "keywords": [ |
|
7 |
+ "contao", |
|
8 |
+ "member-extension-bundle" |
|
9 |
+ ], |
|
10 |
+ "homepage": "https://oveleon.de/", |
|
11 |
+ "authors": [ |
|
12 |
+ { |
|
13 |
+ "name": "Sebastian Zoglowek", |
|
14 |
+ "homepage": "https://github.com/zoglo", |
|
15 |
+ "role": "Developer" |
|
31 | 16 |
}, |
32 |
- "conflict": { |
|
33 |
- "contao/core": "*", |
|
34 |
- "contao/manager-plugin": "<2.0 || >=3.0" |
|
17 |
+ { |
|
18 |
+ "name": "Daniele Sciannimanica", |
|
19 |
+ "homepage": "https://github.com/doishub", |
|
20 |
+ "role": "Developer" |
|
35 | 21 |
}, |
36 |
- "extra": { |
|
37 |
- "branch-alias": { |
|
38 |
- "dev-master": "1.3.x-dev" |
|
39 |
- }, |
|
40 |
- "contao-manager-plugin": "Oveleon\\ContaoMemberExtensionBundle\\ContaoManager\\Plugin" |
|
22 |
+ { |
|
23 |
+ "name": "Fabian Ekert", |
|
24 |
+ "homepage": "https://github.com/eki89", |
|
25 |
+ "role": "Developer" |
|
26 |
+ } |
|
27 |
+ ], |
|
28 |
+ "require": { |
|
29 |
+ "php": "^8.1", |
|
30 |
+ "contao/core-bundle": "^4.13 || ^5.3" |
|
31 |
+ }, |
|
32 |
+ "require-dev": { |
|
33 |
+ "contao/manager-plugin": "^2.3.1" |
|
34 |
+ }, |
|
35 |
+ "conflict": { |
|
36 |
+ "contao/core": "*" |
|
37 |
+ }, |
|
38 |
+ "autoload": { |
|
39 |
+ "psr-4": { |
|
40 |
+ "Oveleon\\ContaoMemberExtensionBundle\\": "src/" |
|
41 | 41 |
}, |
42 |
- "autoload": { |
|
43 |
- "psr-4": { |
|
44 |
- "Oveleon\\ContaoMemberExtensionBundle\\": "src/" |
|
45 |
- }, |
|
46 |
- "classmap": [ |
|
47 |
- "contao/" |
|
48 |
- ], |
|
49 |
- "exclude-from-classmap": [ |
|
50 |
- "contao/config/", |
|
51 |
- "contao/dca/", |
|
52 |
- "contao/languages/", |
|
53 |
- "contao/templates/" |
|
54 |
- ] |
|
42 |
+ "classmap": [ |
|
43 |
+ "contao/" |
|
44 |
+ ], |
|
45 |
+ "exclude-from-classmap": [ |
|
46 |
+ "contao/config/", |
|
47 |
+ "contao/dca/", |
|
48 |
+ "contao/languages/", |
|
49 |
+ "contao/templates/" |
|
50 |
+ ] |
|
51 |
+ }, |
|
52 |
+ "extra": { |
|
53 |
+ "branch-alias": { |
|
54 |
+ "dev-master": "1.5.x-dev" |
|
55 | 55 |
}, |
56 |
- "support": { |
|
57 |
- "issues": "https://github.com/oveleon/contao-member-extension-bundle/issues", |
|
58 |
- "source": "https://github.com/oveleon/contao-member-extension-bundle" |
|
59 |
- } |
|
56 |
+ "contao-manager-plugin": "Oveleon\\ContaoMemberExtensionBundle\\ContaoManager\\Plugin" |
|
57 |
+ }, |
|
58 |
+ "support": { |
|
59 |
+ "issues": "https://github.com/oveleon/contao-member-extension-bundle/issues", |
|
60 |
+ "source": "https://github.com/oveleon/contao-member-extension-bundle" |
|
61 |
+ } |
|
60 | 62 |
} |
61 | 63 |
deleted file mode 100644 |
... | ... |
@@ -1,8 +0,0 @@ |
1 |
-services: |
|
2 |
- contao_member.listener.insert_tags: |
|
3 |
- class: Oveleon\ContaoMemberExtensionBundle\EventListener\InsertTagsListener |
|
4 |
- arguments: |
|
5 |
- - '@contao.framework' |
|
6 |
- tags: |
|
7 |
- - { name: contao.hook, hook: replaceInsertTags } |
|
8 |
- public: true |
0 | 10 |
deleted file mode 100644 |
... | ... |
@@ -1,332 +0,0 @@ |
1 |
-<?php |
|
2 |
- |
|
3 |
-declare(strict_types=1); |
|
4 |
- |
|
5 |
-/* |
|
6 |
- * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
- * |
|
8 |
- * @package contao-member-extension-bundle |
|
9 |
- * @license MIT |
|
10 |
- * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
- * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
- * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
- * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
- */ |
|
15 |
- |
|
16 |
-namespace Oveleon\ContaoMemberExtensionBundle; |
|
17 |
- |
|
18 |
-use Contao\Config; |
|
19 |
-use Contao\CoreBundle\Monolog\ContaoContext; |
|
20 |
-use Contao\Dbafs; |
|
21 |
-use Contao\File; |
|
22 |
-use Contao\FilesModel; |
|
23 |
-use Contao\FileUpload; |
|
24 |
-use Contao\Frontend; |
|
25 |
-use Contao\FrontendUser; |
|
26 |
-use Contao\MemberModel; |
|
27 |
-use Contao\StringUtil; |
|
28 |
-use Contao\System; |
|
29 |
-use Contao\Validator; |
|
30 |
-use Psr\Log\LogLevel; |
|
31 |
- |
|
32 |
-/** |
|
33 |
- * Class Member |
|
34 |
- * |
|
35 |
- * @property int $avatar UUID of the avatar |
|
36 |
- */ |
|
37 |
-class Member extends Frontend |
|
38 |
-{ |
|
39 |
- const DEFAULT_PICTURE = 'bundles/contaomemberextension/avatar.png'; |
|
40 |
- |
|
41 |
- /** |
|
42 |
- * MemberAvatar file name |
|
43 |
- */ |
|
44 |
- protected string $avatarName = 'memberAvatar'; |
|
45 |
- |
|
46 |
- /** |
|
47 |
- * Create avatar for a member | Registration |
|
48 |
- */ |
|
49 |
- public function createAvatar(int $userId, array $arrData): void |
|
50 |
- { |
|
51 |
- $objMember = MemberModel::findById($userId); |
|
52 |
- $this->processAvatar($objMember, $arrData); |
|
53 |
- } |
|
54 |
- |
|
55 |
- /** |
|
56 |
- * Update avatar of a member | Login |
|
57 |
- */ |
|
58 |
- public function updateAvatar(FrontendUser $objUser, array $arrData): void |
|
59 |
- { |
|
60 |
- $objMember = MemberModel::findById($objUser->id); |
|
61 |
- $this->processAvatar($objMember, $arrData); |
|
62 |
- } |
|
63 |
- |
|
64 |
- /** |
|
65 |
- * Process avatar upload for a member |
|
66 |
- */ |
|
67 |
- protected function processAvatar(MemberModel $objMember, ?array $arrData): void |
|
68 |
- { |
|
69 |
- $objMember = MemberModel::findByPk($objMember->id); |
|
70 |
- |
|
71 |
- if ($objMember === null) |
|
72 |
- { |
|
73 |
- return; |
|
74 |
- } |
|
75 |
- |
|
76 |
- // ToDo: remove $_SESSION when contao 4.13 support ends (Contao ^5.* is not possible with Contao 4.* support) |
|
77 |
- $file = $_SESSION['FILES']['avatar']; |
|
78 |
- $maxlength_kb = $this->getMaximumUploadSize(); |
|
79 |
- $maxlength_kb_readable = $this->getReadableSize($maxlength_kb); |
|
80 |
- |
|
81 |
- // Sanitize the filename |
|
82 |
- try |
|
83 |
- { |
|
84 |
- $file['name'] = StringUtil::sanitizeFileName($file['name']); |
|
85 |
- } |
|
86 |
- catch (\InvalidArgumentException $e) |
|
87 |
- { |
|
88 |
- // ToDo: add error message for invalid characters |
|
89 |
- return; |
|
90 |
- } |
|
91 |
- |
|
92 |
- // Invalid file name |
|
93 |
- if (!Validator::isValidFileName($file['name'])) |
|
94 |
- { |
|
95 |
- // ToDo: add error message for invalid characters |
|
96 |
- return; |
|
97 |
- } |
|
98 |
- |
|
99 |
- // File was not uploaded |
|
100 |
- if (!is_uploaded_file($file['tmp_name'])) |
|
101 |
- { |
|
102 |
- // ToDo: Add error messages |
|
103 |
- /*if ($file['error'] == 1 || $file['error'] == 2) { // Add error message for maximum file size } |
|
104 |
- elseif ($file['error'] == 3) { // Add error message for partial upload } |
|
105 |
- elseif ($file['error'] > 0) { // Add error message for failed upload }*/ |
|
106 |
- |
|
107 |
- unset($_SESSION['FILES']['avatar']); |
|
108 |
- |
|
109 |
- return; |
|
110 |
- } |
|
111 |
- |
|
112 |
- // File is too big |
|
113 |
- if ($file['size'] > $maxlength_kb) |
|
114 |
- { |
|
115 |
- // ToDo: add error message for maximum file size |
|
116 |
- unset($_SESSION['FILES']['avatar']); |
|
117 |
- |
|
118 |
- return; |
|
119 |
- } |
|
120 |
- |
|
121 |
- $objFile = new File($file['name']); |
|
122 |
- $uploadTypes = StringUtil::trimsplit(',', Config::get('validImageTypes')); |
|
123 |
- |
|
124 |
- // File type is not allowed |
|
125 |
- if (!\in_array($objFile->extension, $uploadTypes)) |
|
126 |
- { |
|
127 |
- // ToDo: add error message for not allowed file type |
|
128 |
- unset($_SESSION['FILES']['avatar']); |
|
129 |
- |
|
130 |
- return; |
|
131 |
- } |
|
132 |
- |
|
133 |
- if ($arrImageSize = @getimagesize($file['tmp_name'])) |
|
134 |
- { |
|
135 |
- $intImageWidth = Config::get('imageWidth'); |
|
136 |
- |
|
137 |
- // Image exceeds maximum image width |
|
138 |
- if ($intImageWidth > 0 && $arrImageSize[0] > $intImageWidth) { |
|
139 |
- // ToDo: add error message for exceeding width |
|
140 |
- unset($_SESSION['FILES']['avatar']); |
|
141 |
- |
|
142 |
- return; |
|
143 |
- } |
|
144 |
- |
|
145 |
- $intImageHeight = Config::get('imageHeight'); |
|
146 |
- |
|
147 |
- // Image exceeds maximum image height |
|
148 |
- if ($intImageHeight > 0 && $arrImageSize[1] > $intImageHeight) { |
|
149 |
- // ToDo: add error message for exceeding height |
|
150 |
- unset($_SESSION['FILES']['avatar']); |
|
151 |
- |
|
152 |
- return; |
|
153 |
- } |
|
154 |
- } |
|
155 |
- |
|
156 |
- // Upload valid file type with no width and height -> svg |
|
157 |
- |
|
158 |
- // Don't upload if no homedir is assigned |
|
159 |
- // ToDo: Create homedir? |
|
160 |
- if (!$objMember->assignDir || !$objMember->homeDir) |
|
161 |
- { |
|
162 |
- // ToDo: add error message for no homedir |
|
163 |
- return; |
|
164 |
- } |
|
165 |
- |
|
166 |
- $intUploadFolder = $objMember->homeDir; |
|
167 |
- |
|
168 |
- $objUploadFolder = FilesModel::findByUuid($intUploadFolder); |
|
169 |
- |
|
170 |
- // The upload folder could not be found |
|
171 |
- if ($objUploadFolder === null) |
|
172 |
- { |
|
173 |
- throw new Exception("Invalid upload folder ID $intUploadFolder"); |
|
174 |
- } |
|
175 |
- |
|
176 |
- $strUploadFolder = $objUploadFolder->path; |
|
177 |
- |
|
178 |
- // Store the file if the upload folder exists |
|
179 |
- $projectDir = System::getContainer()->getParameter('kernel.project_dir'); |
|
180 |
- |
|
181 |
- if (!!$strUploadFolder & is_dir($projectDir . '/' . $strUploadFolder)) |
|
182 |
- { |
|
183 |
- // Delete existing avatar if it exists |
|
184 |
- $this->deleteAvatar($objMember); |
|
185 |
- |
|
186 |
- $this->import('Files'); |
|
187 |
- |
|
188 |
- // Rename file |
|
189 |
- $file['name'] = $this->avatarName . '.' . $objFile->extension; |
|
190 |
- |
|
191 |
- // Move the file to its destination |
|
192 |
- $this->Files->move_uploaded_file($file['tmp_name'], $strUploadFolder . '/' . $file['name']); |
|
193 |
- $this->Files->chmod($strUploadFolder . '/' . $file['name'], 0666 & ~umask()); |
|
194 |
- |
|
195 |
- $strUuid = null; |
|
196 |
- $strFile = $strUploadFolder . '/' . $file['name']; |
|
197 |
- |
|
198 |
- |
|
199 |
- // Generate the DB entries |
|
200 |
- if (Dbafs::shouldBeSynchronized($strFile)) |
|
201 |
- { |
|
202 |
- $objModel = FilesModel::findByPath($strFile); |
|
203 |
- |
|
204 |
- if ($objModel === null) |
|
205 |
- { |
|
206 |
- $objModel = Dbafs::addResource($strFile); |
|
207 |
- } |
|
208 |
- |
|
209 |
- $strUuid = StringUtil::binToUuid($objModel->uuid); |
|
210 |
- |
|
211 |
- // Update the hash of the target folder |
|
212 |
- Dbafs::updateFolderHashes($strUploadFolder); |
|
213 |
- |
|
214 |
- // Update member avatar |
|
215 |
- $objMember->avatar = $objModel->uuid; |
|
216 |
- $objMember->save(); |
|
217 |
- } |
|
218 |
- |
|
219 |
- // Add the session entry |
|
220 |
- $_SESSION['FILES']['avatar'] = array |
|
221 |
- ( |
|
222 |
- 'name' => $file['name'], |
|
223 |
- 'type' => $file['type'], |
|
224 |
- 'tmp_name' => $projectDir . '/' . $strFile, |
|
225 |
- 'error' => $file['error'], |
|
226 |
- 'size' => $file['size'], |
|
227 |
- 'uploaded' => true, |
|
228 |
- 'uuid' => $strUuid |
|
229 |
- ); |
|
230 |
- |
|
231 |
- // Add a log entry |
|
232 |
- $logger = System::getContainer()->get('monolog.logger.contao'); |
|
233 |
- $logger->log(LogLevel::INFO, 'File "' . $strUploadFolder . '/' . $file['name'] . '" has been uploaded', ['contao' => new ContaoContext(__METHOD__, TL_FILES)]); |
|
234 |
- } |
|
235 |
- |
|
236 |
- unset($_SESSION['FILES']['avatar']); |
|
237 |
- } |
|
238 |
- |
|
239 |
- /** |
|
240 |
- * Return the maximum upload file size in bytes |
|
241 |
- */ |
|
242 |
- protected function getMaximumUploadSize() |
|
243 |
- { |
|
244 |
- if ($this->maxlength > 0) |
|
245 |
- { |
|
246 |
- return $this->maxlength; |
|
247 |
- } |
|
248 |
- |
|
249 |
- return FileUpload::getMaxUploadSize(); |
|
250 |
- } |
|
251 |
- |
|
252 |
- /** |
|
253 |
- * Parses an avatar to the template |
|
254 |
- */ |
|
255 |
- public static function parseMemberAvatar(?MemberModel $objMember, &$objTemplate, ?string $imgSize): void |
|
256 |
- { |
|
257 |
- $objTemplate->addImage= true; |
|
258 |
- |
|
259 |
- $objTemplate->singleSRC = self::DEFAULT_PICTURE; |
|
260 |
- $objTemplate->addFallbackImage = true; |
|
261 |
- |
|
262 |
- $projectDir = System::getContainer()->getParameter('kernel.project_dir'); |
|
263 |
- |
|
264 |
- // Check if member avatar exists |
|
265 |
- if (null === $objMember || null === $objMember->avatar || null === ($objFile = FilesModel::findByUuid($objMember->avatar)) || !\is_file($projectDir.'/'. $objFile->path)) |
|
266 |
- { |
|
267 |
- $objFile = !!($uuidDefault = Config::get('defaultAvatar')) ? FilesModel::findByUuid($uuidDefault) : null; |
|
268 |
- } |
|
269 |
- |
|
270 |
- // Check if config avatar exists |
|
271 |
- if (null === $objFile || !\is_file($projectDir . '/' . $objFile->path)) |
|
272 |
- { |
|
273 |
- return; |
|
274 |
- } |
|
275 |
- |
|
276 |
- $objTemplate->addFallbackImage = false; |
|
277 |
- $imgSize = $imgSize ?? null; |
|
278 |
- |
|
279 |
- $figureBuilder = System::getContainer() |
|
280 |
- ->get('contao.image.studio') |
|
281 |
- ->createFigureBuilder() |
|
282 |
- ->from($objFile->path) |
|
283 |
- ->setSize($imgSize) |
|
284 |
- ; |
|
285 |
- |
|
286 |
- if (null !== ($figure = $figureBuilder->buildIfResourceExists())) |
|
287 |
- { |
|
288 |
- $figure->applyLegacyTemplateData($objTemplate); |
|
289 |
- } |
|
290 |
- } |
|
291 |
- |
|
292 |
- /** |
|
293 |
- * Gets the url for a member avatar |
|
294 |
- */ |
|
295 |
- public static function getMemberAvatarURL(?MemberModel $objMember): string |
|
296 |
- { |
|
297 |
- // ToDo: Merge logic with parseMemberAvatar |
|
298 |
- $projectDir = System::getContainer()->getParameter('kernel.project_dir'); |
|
299 |
- |
|
300 |
- if (null === $objMember || null === $objMember->avatar || null === ($objFile = FilesModel::findByUuid($objMember->avatar)) || !\is_file($projectDir.'/'. $objFile->path)) |
|
301 |
- { |
|
302 |
- $objFile = !!($uuidDefault = Config::get('defaultAvatar')) ? FilesModel::findByUuid($uuidDefault) : null; |
|
303 |
- } |
|
304 |
- |
|
305 |
- // Check if config avatar exists |
|
306 |
- if (null === $objFile || !\is_file($projectDir . '/' . $objFile->path)) |
|
307 |
- { |
|
308 |
- return self::DEFAULT_PICTURE; |
|
309 |
- } |
|
310 |
- |
|
311 |
- return $objFile->path; |
|
312 |
- } |
|
313 |
- |
|
314 |
- /** |
|
315 |
- * Deletes an avatar |
|
316 |
- */ |
|
317 |
- public static function deleteAvatar(MemberModel $objMember): void |
|
318 |
- { |
|
319 |
- if (!!$objMember->avatar) |
|
320 |
- { |
|
321 |
- $objFile = FilesModel::findByUuid($objMember->avatar) ?: ''; |
|
322 |
- $projectDir = System::getContainer()->getParameter('kernel.project_dir'); |
|
323 |
- |
|
324 |
- // Only delete if file exists |
|
325 |
- if (!!$objFile && file_exists($projectDir . '/' . $objFile->path)) |
|
326 |
- { |
|
327 |
- $file = new File($objFile->path); |
|
328 |
- $file->delete(); |
|
329 |
- } |
|
330 |
- } |
|
331 |
- } |
|
332 |
-} |
... | ... |
@@ -14,21 +14,7 @@ declare(strict_types=1); |
14 | 14 |
*/ |
15 | 15 |
|
16 | 16 |
// Back end modules |
17 |
-use Contao\ArrayUtil; |
|
18 |
- |
|
19 | 17 |
$GLOBALS['BE_MOD']['system']['member_settings'] = [ |
20 |
- 'tables' => ['tl_member_settings'], |
|
21 |
- 'hideInNavigation' => true, |
|
18 |
+ 'tables' => ['tl_member_settings'], |
|
19 |
+ 'hideInNavigation' => true |
|
22 | 20 |
]; |
23 |
- |
|
24 |
-// Front end modules |
|
25 |
-ArrayUtil::arrayInsert($GLOBALS['FE_MOD']['user'], -1, [ |
|
26 |
- 'avatar' => 'Oveleon\ContaoMemberExtensionBundle\ModuleAvatar', |
|
27 |
- 'deleteAvatar' => 'Oveleon\ContaoMemberExtensionBundle\ModuleDeleteAvatar', |
|
28 |
- 'memberList' => 'Oveleon\ContaoMemberExtensionBundle\ModuleMemberList', |
|
29 |
- 'memberReader' => 'Oveleon\ContaoMemberExtensionBundle\ModuleMemberReader' |
|
30 |
-]); |
|
31 |
- |
|
32 |
-// Register hooks |
|
33 |
-$GLOBALS['TL_HOOKS']['createNewUser'][] = ['Oveleon\ContaoMemberExtensionBundle\Member', 'createAvatar']; |
|
34 |
-$GLOBALS['TL_HOOKS']['updatePersonalData'][] = ['Oveleon\ContaoMemberExtensionBundle\Member', 'updateAvatar']; |
... | ... |
@@ -13,28 +13,42 @@ declare(strict_types=1); |
13 | 13 |
* @copyright Oveleon <https://www.oveleon.de/> |
14 | 14 |
*/ |
15 | 15 |
|
16 |
-use Contao\Config; |
|
17 | 16 |
use Contao\CoreBundle\DataContainer\PaletteManipulator; |
17 |
+use Contao\System; |
|
18 | 18 |
|
19 | 19 |
// Extend the default palette |
20 | 20 |
PaletteManipulator::create() |
21 |
- ->addField(['avatar'], 'personal_legend', PaletteManipulator::POSITION_APPEND) |
|
21 |
+ ->addField('avatar', 'personal_legend', PaletteManipulator::POSITION_APPEND) |
|
22 |
+ ->addField('alias', 'avatar') |
|
22 | 23 |
->applyToPalette('default', 'tl_member') |
23 | 24 |
; |
24 | 25 |
|
25 | 26 |
// Add global operations |
26 | 27 |
$GLOBALS['TL_DCA']['tl_member']['list']['global_operations']['settings'] = [ |
27 |
- 'label' => &$GLOBALS['TL_LANG']['tl_member']['settings'], |
|
28 |
- 'href' => 'do=member_settings', |
|
29 |
- 'icon' => 'edit.svg', |
|
30 |
- 'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"' |
|
28 |
+ 'label' => &$GLOBALS['TL_LANG']['tl_member']['settings'], |
|
29 |
+ 'href' => 'do=member_settings', |
|
30 |
+ 'icon' => 'edit.svg', |
|
31 |
+ 'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"' |
|
31 | 32 |
]; |
32 | 33 |
|
33 | 34 |
// Add fields to tl_user |
34 | 35 |
$GLOBALS['TL_DCA']['tl_member']['fields']['avatar'] = [ |
35 |
- 'label' => &$GLOBALS['TL_LANG']['tl_member']['avatar'], |
|
36 |
- 'exclude' => true, |
|
37 |
- 'inputType' => 'fileTree', |
|
38 |
- 'eval' => ['feEditable'=>true, 'feViewable'=>true, 'feGroup'=>'personal', 'fieldType'=>'radio', 'filesOnly'=>true, 'isGallery'=>true, 'extensions'=>Config::get('validImageTypes'), 'tl_class'=>'clr'], |
|
39 |
- 'sql' => "binary(16) NULL" |
|
36 |
+ 'exclude' => true, |
|
37 |
+ 'inputType' => 'fileTree', |
|
38 |
+ 'eval' => [ |
|
39 |
+ 'feEditable' => true, |
|
40 |
+ 'feGroup' => 'personal', |
|
41 |
+ 'fieldType' => 'radio', |
|
42 |
+ 'filesOnly' => true, |
|
43 |
+ 'extensions' => implode(',', System::getContainer()->getParameter('contao.image.valid_extensions')), |
|
44 |
+ 'tl_class' => 'clr' |
|
45 |
+ ], |
|
46 |
+ 'sql' => "binary(16) NULL" |
|
47 |
+]; |
|
48 |
+ |
|
49 |
+$GLOBALS['TL_DCA']['tl_member']['fields']['alias'] = [ |
|
50 |
+ 'search' => true, |
|
51 |
+ 'inputType' => 'text', |
|
52 |
+ 'eval' => ['rgxp'=>'alias', 'doNotCopy'=>true, 'unique'=>true, 'maxlength'=>255, 'tl_class'=>'w50'], |
|
53 |
+ 'sql' => "varchar(255) BINARY NOT NULL default ''" |
|
40 | 54 |
]; |
... | ... |
@@ -2,6 +2,8 @@ |
2 | 2 |
|
3 | 3 |
declare(strict_types=1); |
4 | 4 |
|
5 |
+use Contao\DC_File; |
|
6 |
+ |
|
5 | 7 |
/* |
6 | 8 |
* This file is part of Oveleon ContaoMemberExtension Bundle. |
7 | 9 |
* |
... | ... |
@@ -13,9 +15,6 @@ declare(strict_types=1); |
13 | 15 |
* @copyright Oveleon <https://www.oveleon.de/> |
14 | 16 |
*/ |
15 | 17 |
|
16 |
-use Contao\Config; |
|
17 |
-use Contao\DC_File; |
|
18 |
- |
|
19 | 18 |
$GLOBALS['TL_DCA']['tl_member_settings'] = [ |
20 | 19 |
|
21 | 20 |
'config' => [ |
... | ... |
@@ -23,13 +22,12 @@ $GLOBALS['TL_DCA']['tl_member_settings'] = [ |
23 | 22 |
'closed' => true |
24 | 23 |
], |
25 | 24 |
|
26 |
- 'palettes' => ['default' =>'{avatar_legend},defaultAvatar;'], |
|
25 |
+ 'palettes' => ['default' => '{avatar_legend},defaultAvatar;'], |
|
27 | 26 |
|
28 | 27 |
'fields' => [ |
29 | 28 |
'defaultAvatar' => [ |
30 |
- 'label' => &$GLOBALS['TL_LANG']['tl_member_settings']['defaultAvatar'], |
|
31 | 29 |
'inputType' => 'fileTree', |
32 |
- 'eval' => ['fieldType'=>'radio', 'filesOnly'=>true, 'isGallery'=>true, 'extensions'=>Config::get('validImageTypes'), 'tl_class'=>'clr'] |
|
30 |
+ 'eval' => ['fieldType' => 'radio', 'filesOnly' => true, 'isGallery' => true, 'extensions' => '%contao.image.valid_extensions%', 'tl_class' => 'clr'] |
|
33 | 31 |
] |
34 | 32 |
] |
35 | 33 |
]; |
... | ... |
@@ -13,31 +13,31 @@ declare(strict_types=1); |
13 | 13 |
* @copyright Oveleon <https://www.oveleon.de/> |
14 | 14 |
*/ |
15 | 15 |
|
16 |
-use Contao\Backend; |
|
17 | 16 |
use Contao\Controller; |
18 | 17 |
use Contao\System; |
18 |
+use Oveleon\ContaoMemberExtensionBundle\EventListener\DataContainer\MemberFieldsOptionsListener; |
|
19 | 19 |
|
20 | 20 |
System::loadLanguageFile('tl_member_settings'); |
21 | 21 |
|
22 | 22 |
// Add palettes to tl_module |
23 |
-$GLOBALS['TL_DCA']['tl_module']['palettes']['avatar'] = '{title_legend},name,headline,type;{source_legend},imgSize;{template_legend:hide},customTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; |
|
23 |
+$GLOBALS['TL_DCA']['tl_module']['palettes']['avatar'] = '{title_legend},name,headline,type;{source_legend},imgSize;{template_legend:hide},customTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; |
|
24 | 24 |
$GLOBALS['TL_DCA']['tl_module']['palettes']['deleteAvatar'] = '{title_legend},name,headline,type;{template_legend:hide},customTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; |
25 |
-$GLOBALS['TL_DCA']['tl_module']['palettes']['memberList'] = '{title_legend},name,headline,type;{config_legend},ext_order,ext_orderField,numberOfItems,perPage,ext_groups,memberFields,imgSize;{redirect_legend},jumpTo;{template_legend:hide},customTpl,memberListTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; |
|
26 |
-$GLOBALS['TL_DCA']['tl_module']['palettes']['memberReader'] = '{title_legend},name,headline,type;{config_legend},ext_groups,memberFields,imgSize;{template_legend:hide},customTpl,memberReaderTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; |
|
25 |
+$GLOBALS['TL_DCA']['tl_module']['palettes']['memberList'] = '{title_legend},name,headline,type;{config_legend},ext_order,ext_orderField,numberOfItems,perPage,ext_groups,memberFields,imgSize,ext_activateFilter,ext_parseDetails,ext_memberAlias;{redirect_legend},jumpTo;{template_legend:hide},customTpl,memberListTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; |
|
26 |
+$GLOBALS['TL_DCA']['tl_module']['palettes']['memberReader'] = '{title_legend},name,headline,type;{config_legend},ext_groups,memberFields,imgSize,ext_parseDetails,overviewPage,customLabel;{template_legend:hide},customTpl,memberReaderTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; |
|
27 | 27 |
|
28 | 28 |
$GLOBALS['TL_DCA']['tl_module']['fields']['memberListTpl'] = [ |
29 | 29 |
'exclude' => true, |
30 | 30 |
'inputType' => 'select', |
31 |
- 'options_callback' => static fn () => Controller::getTemplateGroup('memberExtension_list_'), |
|
32 |
- 'eval' => ['includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'], |
|
31 |
+ 'options_callback' => static fn() => Controller::getTemplateGroup('memberExtension_list_'), |
|
32 |
+ 'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50'], |
|
33 | 33 |
'sql' => "varchar(64) NOT NULL default ''" |
34 | 34 |
]; |
35 | 35 |
|
36 | 36 |
$GLOBALS['TL_DCA']['tl_module']['fields']['memberReaderTpl'] = [ |
37 | 37 |
'exclude' => true, |
38 | 38 |
'inputType' => 'select', |
39 |
- 'options_callback' => static fn () => Controller::getTemplateGroup('memberExtension_reader_'), |
|
40 |
- 'eval' => ['includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'], |
|
39 |
+ 'options_callback' => static fn() => Controller::getTemplateGroup('memberExtension_reader_'), |
|
40 |
+ 'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50'], |
|
41 | 41 |
'sql' => "varchar(64) NOT NULL default ''" |
42 | 42 |
]; |
43 | 43 |
|
... | ... |
@@ -46,85 +46,50 @@ $GLOBALS['TL_DCA']['tl_module']['fields']['ext_order'] = [ |
46 | 46 |
'inputType' => 'select', |
47 | 47 |
'options' => ['order_random', 'order_asc', 'order_desc'], |
48 | 48 |
'reference' => &$GLOBALS['TL_LANG']['tl_member_settings'], |
49 |
- 'eval' => ['tl_class'=>'w50 clr', 'includeBlankOption'=>true, 'chosen'=>true,], |
|
49 |
+ 'eval' => ['tl_class' => 'w50 clr', 'includeBlankOption' => true, 'chosen' => true], |
|
50 | 50 |
'sql' => "varchar(32) NOT NULL default ''" |
51 | 51 |
]; |
52 | 52 |
|
53 | 53 |
$GLOBALS['TL_DCA']['tl_module']['fields']['ext_orderField'] = [ |
54 | 54 |
'exclude' => true, |
55 | 55 |
'inputType' => 'select', |
56 |
- 'options_callback' => ['tl_module_extension', 'getViewableMemberFields'], |
|
57 |
- 'eval' => ['tl_class'=>'w50', 'includeBlankOption'=>true, 'chosen'=>true,], |
|
56 |
+ 'eval' => ['tl_class' => 'w50', 'includeBlankOption' => true, 'chosen' => true], |
|
58 | 57 |
'sql' => "varchar(32) NOT NULL default ''" |
59 | 58 |
]; |
60 | 59 |
|
61 | 60 |
$GLOBALS['TL_DCA']['tl_module']['fields']['memberFields'] = [ |
62 | 61 |
'exclude' => true, |
63 | 62 |
'inputType' => 'checkboxWizard', |
64 |
- 'options_callback' => ['tl_module_extension', 'getMemberProperties'], |
|
65 |
- 'eval' => ['multiple'=>true, 'tl_class'=>'clr'], |
|
63 |
+ 'eval' => ['multiple' => true, 'tl_class' => 'clr'], |
|
66 | 64 |
'sql' => "blob NULL" |
67 | 65 |
]; |
68 | 66 |
|
67 |
+$GLOBALS['TL_DCA']['tl_module']['fields']['ext_parseDetails'] = [ |
|
68 |
+ 'exclude' => true, |
|
69 |
+ 'inputType' => 'checkbox', |
|
70 |
+ 'eval' => ['tl_class' => 'w50 clr'], |
|
71 |
+ 'sql' => "char(1) NOT NULL default ''" |
|
72 |
+]; |
|
73 |
+ |
|
74 |
+$GLOBALS['TL_DCA']['tl_module']['fields']['ext_memberAlias'] = [ |
|
75 |
+ 'exclude' => true, |
|
76 |
+ 'inputType' => 'checkbox', |
|
77 |
+ 'eval' => ['tl_class' => 'w50'], |
|
78 |
+ 'sql' => "char(1) NOT NULL default ''" |
|
79 |
+]; |
|
80 |
+ |
|
69 | 81 |
$GLOBALS['TL_DCA']['tl_module']['fields']['ext_groups'] = [ |
70 | 82 |
'exclude' => true, |
71 | 83 |
'inputType' => 'checkbox', |
72 | 84 |
'foreignKey' => 'tl_member_group.name', |
73 |
- 'eval' => ['multiple'=>true, 'tl_class'=>'clr'], |
|
85 |
+ 'eval' => ['multiple' => true, 'tl_class' => 'clr'], |
|
74 | 86 |
'sql' => "blob NULL", |
75 |
- 'relation' => ['type'=>'hasMany', 'load'=>'lazy'] |
|
87 |
+ 'relation' => ['type' => 'hasMany', 'load' => 'lazy'] |
|
76 | 88 |
]; |
77 | 89 |
|
78 |
-class tl_module_extension extends Backend |
|
79 |
-{ |
|
80 |
- /** |
|
81 |
- * Import the back end user object |
|
82 |
- */ |
|
83 |
- public function __construct() |
|
84 |
- { |
|
85 |
- parent::__construct(); |
|
86 |
- $this->import('Contao\BackendUser', 'User'); |
|
87 |
- } |
|
88 |
- |
|
89 |
- /** |
|
90 |
- * Return all fields of table tl_member without account data |
|
91 |
- */ |
|
92 |
- public function getMemberProperties(): array |
|
93 |
- { |
|
94 |
- $return = []; |
|
95 |
- |
|
96 |
- Contao\System::loadLanguageFile('tl_member'); |
|
97 |
- $this->loadDataContainer('tl_member'); |
|
98 |
- |
|
99 |
- foreach ($GLOBALS['TL_DCA']['tl_member']['fields'] as $k=>$v) |
|
100 |
- { |
|
101 |
- if (!empty($v['inputType']) && $v['inputType'] !== 'password') |
|
102 |
- { |
|
103 |
- $return[$k] = $GLOBALS['TL_DCA']['tl_member']['fields'][$k]['label'][0]; |
|
104 |
- } |
|
105 |
- } |
|
106 |
- |
|
107 |
- return $return; |
|
108 |
- } |
|
109 |
- |
|
110 |
- /** |
|
111 |
- * Return all sortable fields of table tl_member |
|
112 |
- */ |
|
113 |
- public function getViewableMemberFields(): array |
|
114 |
- { |
|
115 |
- $return = []; |
|
116 |
- |
|
117 |
- Contao\System::loadLanguageFile('tl_member'); |
|
118 |
- $this->loadDataContainer('tl_member'); |
|
119 |
- |
|
120 |
- foreach ($GLOBALS['TL_DCA']['tl_member']['fields'] as $k=>$v) |
|
121 |
- { |
|
122 |
- if (!empty($v['inputType']) && $k !== 'avatar' && isset($v['eval']['feViewable']) && $v['eval']['feViewable'] === true) |
|
123 |
- { |
|
124 |
- $return[$k] = $GLOBALS['TL_DCA']['tl_member']['fields'][$k]['label'][0] . ' ['.$k.']'; |
|
125 |
- } |
|
126 |
- } |
|
127 |
- |
|
128 |
- return $return; |
|
129 |
- } |
|
130 |
-} |
|
90 |
+$GLOBALS['TL_DCA']['tl_module']['fields']['ext_activateFilter'] = [ |
|
91 |
+ 'exclude' => true, |
|
92 |
+ 'inputType' => 'checkbox', |
|
93 |
+ 'eval' => ['tl_class' => 'w50 m12'], |
|
94 |
+ 'sql' => "char(1) NOT NULL default ''" |
|
95 |
+]; |
... | ... |
@@ -5,6 +5,10 @@ |
5 | 5 |
<source>No members could be found.</source> |
6 | 6 |
<target>Es konnten keine Mitglieder gefunden werden.</target> |
7 | 7 |
</trans-unit> |
8 |
+ <trans-unit id="MSC.memberDetailHeader"> |
|
9 |
+ <source>Details</source> |
|
10 |
+ <target>Details</target> |
|
11 |
+ </trans-unit> |
|
8 | 12 |
<trans-unit id="MSC.memberDetail"> |
9 | 13 |
<source>More</source> |
10 | 14 |
<target>Mehr</target> |
... | ... |
@@ -9,6 +9,14 @@ |
9 | 9 |
<source>Here you can choose a profile picture for the member.</source> |
10 | 10 |
<target>Hier können Sie ein Profilbild für das Mitglied auswählen.</target> |
11 | 11 |
</trans-unit> |
12 |
+ <trans-unit id="tl_member.alias.0"> |
|
13 |
+ <source>Member alias</source> |
|
14 |
+ <target>Mitgliedsalias</target> |
|
15 |
+ </trans-unit> |
|
16 |
+ <trans-unit id="tl_member.alias.1"> |
|
17 |
+ <source>The member alias is a unique reference to the news item which can be called instead of its numeric ID.</source> |
|
18 |
+ <target>Der Mitgliedsalias ist eine eindeutige Referenz, die anstelle der numerischen Mitglieds-ID aufgerufen werden kann.</target> |
|
19 |
+ </trans-unit> |
|
12 | 20 |
<trans-unit id="tl_member.settings.0"> |
13 | 21 |
<source>Settings</source> |
14 | 22 |
<target>Einstellungen</target> |
... | ... |
@@ -19,4 +27,4 @@ |
19 | 27 |
</trans-unit> |
20 | 28 |
</body> |
21 | 29 |
</file> |
22 |
-</xliff> |
|
23 | 30 |
\ No newline at end of file |
31 |
+</xliff> |
... | ... |
@@ -33,14 +33,31 @@ |
33 | 33 |
<source>Here you can select the member fields to be displayed.</source> |
34 | 34 |
<target>Hier können Sie die auszugebenden Mitgliederfelder auswählen.</target> |
35 | 35 |
</trans-unit> |
36 |
- <trans-unit id="tl_module.memberFields.0"> |
|
37 |
- <source>Member fields</source> |
|
38 |
- <target>Mitglieds-Felder</target> |
|
36 |
+ <trans-unit id="tl_module.ext_activateFilter.0"> |
|
37 |
+ <source>Activate filters</source> |
|
38 |
+ <target>Filter aktivieren</target> |
|
39 | 39 |
</trans-unit> |
40 |
- <trans-unit id="tl_module.memberFields.1"> |
|
41 |
- <source>Here you can select the member fields to be output.</source> |
|
42 |
- <target>Hier können Sie die auszugebenden Mitgliederfelder auswählen.</target> |
|
40 |
+ <trans-unit id="tl_module.ext_activateFilter.1"> |
|
41 |
+ <source>Adds filtering. Only works with checkbox fields with the eval flag 'feFilterable'.</source> |
|
42 |
+ <target>Fügt eine Filterung hinzu. Funktioniert nur mit Checkbox-Feldern mit dem eval-flag 'feFilterable'.</target> |
|
43 | 43 |
</trans-unit> |
44 |
+ <trans-unit id="tl_module.ext_parseDetails.0"> |
|
45 |
+ <source>Parse details</source> |
|
46 |
+ <target>Details umwandeln</target> |
|
47 |
+ </trans-unit> |
|
48 |
+ <trans-unit id="tl_module.ext_parseDetails.1"> |
|
49 |
+ <source>Converts all member details based on their rgxp.</source> |
|
50 |
+ <target>Konvertiert alle Mitgliederdaten auf Grundlage der rgxp.</target> |
|
51 |
+ </trans-unit> |
|
52 |
+ <trans-unit id="tl_module.ext_memberAlias.0"> |
|
53 |
+ <source>Use member alias</source> |
|
54 |
+ <target>Mitgliedsalias verwenden</target> |
|
55 |
+ </trans-unit> |
|
56 |
+ <trans-unit id="tl_module.ext_memberAlias.1"> |
|
57 |
+ <source>Uses the member alias for the detail page.</source> |
|
58 |
+ <target>Verwendet den Mitgliedsalias für die Detailseite.</target> |
|
59 |
+ </trans-unit> |
|
60 |
+ |
|
44 | 61 |
<trans-unit id="tl_module.memberListTpl.0"> |
45 | 62 |
<source>List template</source> |
46 | 63 |
<target>Listen-Template</target> |
... | ... |
@@ -57,6 +74,10 @@ |
57 | 74 |
<source>Here you can set your own member reader template.</source> |
58 | 75 |
<target>Hier können Sie ein eigenes Mitglieds-Leser Template einstellen.</target> |
59 | 76 |
</trans-unit> |
77 |
+ <trans-unit id="tl_module.includeMemberListTable"> |
|
78 |
+ <source>For the member-list-table to work, you have to choose <em>%s</em> for your list template and include the <em>%s</em> jQuery template in the page layout.</source> |
|
79 |
+ <target>Damit die Mitglieds-Tabelle funktioniert, müssen Sie das <em>%s</em> als Listen-Template auswählen und das <em>%s</em> jQuery-Template im Seitenlayout einbinden.</target> |
|
80 |
+ </trans-unit> |
|
60 | 81 |
</body> |
61 | 82 |
</file> |
62 | 83 |
</xliff> |
... | ... |
@@ -4,6 +4,9 @@ |
4 | 4 |
<trans-unit id="MSC.emptyMemberList"> |
5 | 5 |
<source>No members could be found.</source> |
6 | 6 |
</trans-unit> |
7 |
+ <trans-unit id="MSC.memberDetailHeader"> |
|
8 |
+ <source>Details</source> |
|
9 |
+ </trans-unit> |
|
7 | 10 |
<trans-unit id="MSC.memberDetail"> |
8 | 11 |
<source>More</source> |
9 | 12 |
</trans-unit> |
... | ... |
@@ -7,6 +7,12 @@ |
7 | 7 |
<trans-unit id="tl_member.avatar.1"> |
8 | 8 |
<source>Here you can choose an avatar for the member.</source> |
9 | 9 |
</trans-unit> |
10 |
+ <trans-unit id="tl_member.alias.0"> |
|
11 |
+ <source>Member alias</source> |
|
12 |
+ </trans-unit> |
|
13 |
+ <trans-unit id="tl_member.alias.1"> |
|
14 |
+ <source>The member alias is a unique reference to the news item which can be called instead of its numeric ID.</source> |
|
15 |
+ </trans-unit> |
|
10 | 16 |
<trans-unit id="tl_member.settings.0"> |
11 | 17 |
<source>Settings</source> |
12 | 18 |
</trans-unit> |
... | ... |
@@ -15,4 +21,4 @@ |
15 | 21 |
</trans-unit> |
16 | 22 |
</body> |
17 | 23 |
</file> |
18 |
-</xliff> |
|
19 | 24 |
\ No newline at end of file |
25 |
+</xliff> |
... | ... |
@@ -25,12 +25,25 @@ |
25 | 25 |
<trans-unit id="tl_module.memberFields.1"> |
26 | 26 |
<source>Here you can select the member fields to be displayed.</source> |
27 | 27 |
</trans-unit> |
28 |
- <trans-unit id="tl_module.memberFields.0"> |
|
29 |
- <source>Member fields</source> |
|
28 |
+ <trans-unit id="tl_module.ext_activateFilter.0"> |
|
29 |
+ <source>Activate filters</source> |
|
30 | 30 |
</trans-unit> |
31 |
- <trans-unit id="tl_module.memberFields.1"> |
|
32 |
- <source>Here you can select the member fields to be output.</source> |
|
31 |
+ <trans-unit id="tl_module.ext_activateFilter.1"> |
|
32 |
+ <source>Adds filtering. Only works with checkbox fields with the eval flag 'feFilterable'.</source> |
|
33 |
+ </trans-unit> |
|
34 |
+ <trans-unit id="tl_module.ext_parseDetails.0"> |
|
35 |
+ <source>Parse details</source> |
|
33 | 36 |
</trans-unit> |
37 |
+ <trans-unit id="tl_module.ext_parseDetails.1"> |
|
38 |
+ <source>Converts all member details based on their rgxp.</source> |
|
39 |
+ </trans-unit> |
|
40 |
+ <trans-unit id="tl_module.ext_memberAlias.0"> |
|
41 |
+ <source>Use member alias</source> |
|
42 |
+ </trans-unit> |
|
43 |
+ <trans-unit id="tl_module.ext_memberAlias.1"> |
|
44 |
+ <source>Uses the member alias for the detail page.</source> |
|
45 |
+ </trans-unit> |
|
46 |
+ |
|
34 | 47 |
<trans-unit id="tl_module.memberListTpl.0"> |
35 | 48 |
<source>List template</source> |
36 | 49 |
</trans-unit> |
... | ... |
@@ -43,6 +56,9 @@ |
43 | 56 |
<trans-unit id="tl_module.memberReaderTpl.1"> |
44 | 57 |
<source>Here you can set your own member reader template.</source> |
45 | 58 |
</trans-unit> |
59 |
+ <trans-unit id="tl_module.includeMemberListTable"> |
|
60 |
+ <source>For the member-list-table to work, you have to choose <em>%s</em> for your list template and include the <em>%s</em> jQuery template in the page layout.</source> |
|
61 |
+ </trans-unit> |
|
46 | 62 |
</body> |
47 | 63 |
</file> |
48 | 64 |
</xliff> |
49 | 65 |
deleted file mode 100644 |
... | ... |
@@ -1,87 +0,0 @@ |
1 |
-<?php |
|
2 |
- |
|
3 |
-declare(strict_types=1); |
|
4 |
- |
|
5 |
-/* |
|
6 |
- * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
- * |
|
8 |
- * @package contao-member-extension-bundle |
|
9 |
- * @license MIT |
|
10 |
- * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
- * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
- * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
- * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
- */ |
|
15 |
- |
|
16 |
-namespace Oveleon\ContaoMemberExtensionBundle; |
|
17 |
- |
|
18 |
-use Contao\BackendTemplate; |
|
19 |
-use Contao\FrontendUser; |
|
20 |
-use Contao\MemberModel; |
|
21 |
-use Contao\StringUtil; |
|
22 |
-use Contao\System; |
|
23 |
- |
|
24 |
-/** |
|
25 |
- * Class ModuleAvatar |
|
26 |
- * |
|
27 |
- * @author Fabian Ekert <fabian@oveleon.de> |
|
28 |
- * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
29 |
- */ |
|
30 |
-class ModuleAvatar extends ModuleMemberExtension |
|
31 |
-{ |
|
32 |
- /** |
|
33 |
- * Template. |
|
34 |
- * |
|
35 |
- * @var string |
|
36 |
- */ |
|
37 |
- protected $strTemplate = 'memberExtension_avatar'; |
|
38 |
- |
|
39 |
- /** |
|
40 |
- * Display a wildcard in the back end |
|
41 |
- * |
|
42 |
- * @return string |
|
43 |
- */ |
|
44 |
- public function generate() |
|
45 |
- { |
|
46 |
- $container = System::getContainer(); |
|
47 |
- $request = System::getContainer()->get('request_stack')->getCurrentRequest(); |
|
48 |
- |
|
49 |
- if ($request && $container->get('contao.routing.scope_matcher')->isBackendRequest($request)) |
|
50 |
- { |
|
51 |
- $objTemplate = new BackendTemplate('be_wildcard'); |
|
52 |
- $objTemplate->wildcard = '### ' . $GLOBALS['TL_LANG']['FMD']['avatar'][0] . ' ###'; |
|
53 |
- $objTemplate->title = $this->headline; |
|
54 |
- $objTemplate->id = $this->id; |
|
55 |
- $objTemplate->link = $this->name; |
|
56 |
- $objTemplate->href = StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', ['do'=>'themes', 'table'=>'tl_module', 'act'=>'edit', 'id'=>$this->id])); |
|
57 |
- |
|
58 |
- return $objTemplate->parse(); |
|
59 |
- } |
|
60 |
- |
|
61 |
- // Return if user is not logged in |
|
62 |
- $tokenChecker = System::getContainer()->get('contao.security.token_checker'); |
|
63 |
- $blnFeUserLoggedIn = $tokenChecker->hasFrontendUser(); |
|
64 |
- |
|
65 |
- if (!$blnFeUserLoggedIn) |
|
66 |
- { |
|
67 |
- return ''; |
|
68 |
- } |
|
69 |
- |
|
70 |
- $this->strTemplate = $this->customTpl ?: 'memberExtension_avatar'; |
|
71 |
- |
|
72 |
- return parent::generate(); |
|
73 |
- } |
|
74 |
- |
|
75 |
- /** |
|
76 |
- * Generate the module |
|
77 |
- */ |
|
78 |
- protected function compile() |
|
79 |
- { |
|
80 |
- $objTemplate = $this->Template; |
|
81 |
- |
|
82 |
- $this->import(FrontendUser::class, 'User'); |
|
83 |
- $objMember = MemberModel::findByPk($this->User->id); |
|
84 |
- |
|
85 |
- Member::parseMemberAvatar($objMember, $objTemplate, $this->imgSize); |
|
86 |
- } |
|
87 |
-} |
88 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,134 +0,0 @@ |
1 |
-<?php |
|
2 |
- |
|
3 |
-declare(strict_types=1); |
|
4 |
- |
|
5 |
-/* |
|
6 |
- * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
- * |
|
8 |
- * @package contao-member-extension-bundle |
|
9 |
- * @license MIT |
|
10 |
- * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
- * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
- * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
- * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
- */ |
|
15 |
- |
|
16 |
-namespace Oveleon\ContaoMemberExtensionBundle; |
|
17 |
- |
|
18 |
-use Contao\BackendTemplate; |
|
19 |
-use Contao\Config; |
|
20 |
-use Contao\FrontendUser; |
|
21 |
-use Contao\Input; |
|
22 |
-use Contao\MemberModel; |
|
23 |
-use Contao\Module; |
|
24 |
-use Contao\StringUtil; |
|
25 |
-use Contao\System; |
|
26 |
- |
|
27 |
-/** |
|
28 |
- * Class ModuleDeleteAvatar |
|
29 |
- * |
|
30 |
- * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
31 |
- */ |
|
32 |
-class ModuleDeleteAvatar extends Module |
|
33 |
-{ |
|
34 |
- /** |
|
35 |
- * Template. |
|
36 |
- * |
|
37 |
- * @var string |
|
38 |
- */ |
|
39 |
- protected $strTemplate = 'memberExtension_deleteAvatar'; |
|
40 |
- |
|
41 |
- /** |
|
42 |
- * Display a wildcard in the back end |
|
43 |
- * |
|
44 |
- * @return string |
|
45 |
- */ |
|
46 |
- public function generate() |
|
47 |
- { |
|
48 |
- $container = System::getContainer(); |
|
49 |
- $request = System::getContainer()->get('request_stack')->getCurrentRequest(); |
|
50 |
- |
|
51 |
- if ($request && $container->get('contao.routing.scope_matcher')->isBackendRequest($request)) |
|
52 |
- { |
|
53 |
- $objTemplate = new BackendTemplate('be_wildcard'); |
|
54 |
- $objTemplate->wildcard = '### ' . $GLOBALS['TL_LANG']['FMD']['deleteAvatar'][0] . ' ###'; |
|
55 |
- $objTemplate->title = $this->headline; |
|
56 |
- $objTemplate->id = $this->id; |
|
57 |
- $objTemplate->link = $this->name; |
|
58 |
- $objTemplate->href = StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', ['do'=>'themes', 'table'=>'tl_module', 'act'=>'edit', 'id'=>$this->id])); |
|
59 |
- |
|
60 |
- return $objTemplate->parse(); |
|
61 |
- } |
|
62 |
- |
|
63 |
- // Set the item from the auto_item parameter |
|
64 |
- if (!isset($_GET['items']) && isset($_GET['auto_item']) && Config::get('useAutoItem')) |
|
65 |
- { |
|
66 |
- Input::setGet('items', Input::get('auto_item')); |
|
67 |
- } |
|
68 |
- |
|
69 |
- // Return if there is no logged-in user |
|
70 |
- if (!$container->get('contao.security.token_checker')->hasFrontendUser()) |
|
71 |
- { |
|
72 |
- return ''; |
|
73 |
- } |
|
74 |
- |
|
75 |
- $this->import(FrontendUser::class, 'User'); |
|
76 |
- $objMember = MemberModel::findByPk($this->User->id); |
|
77 |
- |
|
78 |
- if (null === $objMember) |
|
79 |
- { |
|
80 |
- return ''; |
|
81 |
- } |
|
82 |
- |
|
83 |
- // Confirmation message |
|
84 |
- $session = System::getContainer()->get('session'); |
|
85 |
- $flashBag = $session->getFlashBag(); |
|
86 |
- |
|
87 |
- // Return if there is no flashbag message or an avatar |
|
88 |
- if (!($session->isStarted() && $flashBag->has('mod_avatar_deleted')) && !$objMember->avatar) |
|
89 |
- { |
|
90 |
- return ''; |
|
91 |
- } |
|
92 |
- |
|
93 |
- return parent::generate(); |
|
94 |
- } |
|
95 |
- |
|
96 |
- /** |
|
97 |
- * Generate the module |
|
98 |
- */ |
|
99 |
- protected function compile() |
|
100 |
- { |
|
101 |
- $strFormId = 'deleteAvatar_' . $this->id; |
|
102 |
- $session = System::getContainer()->get('session'); |
|
103 |
- $flashBag = $session->getFlashBag(); |
|
104 |
- |
|
105 |
- // Get form submit |
|
106 |
- if (Input::post('FORM_SUBMIT') == $strFormId) |
|
107 |
- { |
|
108 |
- $this->import(FrontendUser::class, 'User'); |
|
109 |
- $objMember = MemberModel::findByPk($this->User->id); |
|
110 |
- |
|
111 |
- // Delete avatar if it exists |
|
112 |
- if (!!$objMember->avatar) |
|
113 |
- { |
|
114 |
- Member::deleteAvatar($objMember); |
|
115 |
- // Unset avatar |
|
116 |
- $objMember->avatar = null; |
|
117 |
- $objMember->save(); |
|
118 |
- |
|
119 |
- // Set message for deletion feedback |
|
120 |
- $flashBag->set('mod_avatar_deleted', $GLOBALS['TL_LANG']['MSC']['avatarDeleted']); |
|
121 |
- $this->reload(); |
|
122 |
- } |
|
123 |
- } |
|
124 |
- |
|
125 |
- // Confirmation message |
|
126 |
- if ($session->isStarted() && $flashBag->has('mod_avatar_deleted')) { |
|
127 |
- $arrMessages = $flashBag->get('mod_avatar_deleted'); |
|
128 |
- $this->Template->message = $arrMessages[0]; |
|
129 |
- } |
|
130 |
- |
|
131 |
- $this->Template->formId = $strFormId; |
|
132 |
- $this->Template->slabel = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['deleteAvatar']); |
|
133 |
- } |
|
134 |
-} |
135 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,178 +0,0 @@ |
1 |
-<?php |
|
2 |
- |
|
3 |
-declare(strict_types=1); |
|
4 |
- |
|
5 |
-/* |
|
6 |
- * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
- * |
|
8 |
- * @package contao-member-extension-bundle |
|
9 |
- * @license MIT |
|
10 |
- * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
- * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
- * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
- * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
- */ |
|
15 |
- |
|
16 |
-namespace Oveleon\ContaoMemberExtensionBundle; |
|
17 |
- |
|
18 |
-use Contao\Config; |
|
19 |
-use Contao\Date; |
|
20 |
-use Contao\Environment; |
|
21 |
-use Contao\MemberGroupModel; |
|
22 |
-use Contao\MemberModel; |
|
23 |
-use Contao\Module; |
|
24 |
-use Contao\PageModel; |
|
25 |
-use Contao\StringUtil; |
|
26 |
-use Contao\System; |
|
27 |
- |
|
28 |
-/** |
|
29 |
- * Parent class for member modules. |
|
30 |
- * |
|
31 |
- * @author Daniele Sciannimanica <https://github.com/doishub> |
|
32 |
- */ |
|
33 |
-abstract class ModuleMemberExtension extends Module |
|
34 |
-{ |
|
35 |
- /** |
|
36 |
- * Parse member template |
|
37 |
- * |
|
38 |
- * @param $objMember |
|
39 |
- * @param $objTemplate |
|
40 |
- * @param $arrMemberFields |
|
41 |
- * @param $strImgSize |
|
42 |
- * @return string |
|
43 |
- */ |
|
44 |
- protected function parseMemberTemplate($objMember, $objTemplate, $arrMemberFields, $strImgSize): string |
|
45 |
- { |
|
46 |
- System::loadLanguageFile('default'); |
|
47 |
- System::loadLanguageFile('tl_member'); |
|
48 |
- System::loadLanguageFile('countries'); |
|
49 |
- System::loadLanguageFile('languages'); |
|
50 |
- |
|
51 |
- $arrFields = []; |
|
52 |
- |
|
53 |
- foreach ($arrMemberFields as $field) |
|
54 |
- { |
|
55 |
- switch ($field) |
|
56 |
- { |
|
57 |
- /*case 'homeDir': |
|
58 |
- case 'assignDir': |
|
59 |
- break;*/ |
|
60 |
- |
|
61 |
- case 'avatar': |
|
62 |
- Member::parseMemberAvatar($objMember, $objTemplate, $strImgSize); |
|
63 |
- break; |
|
64 |
- |
|
65 |
- default: |
|
66 |
- if ($varValue = $objMember->{$field}) |
|
67 |
- { |
|
68 |
- if (\is_array(($arrValue = StringUtil::deserialize($varValue)))) |
|
69 |
- { |
|
70 |
- $arrFields[$field] = implode(",", $arrValue); |
|
71 |
- } |
|
72 |
- else |
|
73 |
- { |
|
74 |
- $arrFields[$field] = $varValue; |
|
75 |
- } |
|
76 |
- //self::parseMemberDetails($arrFields, $field, $varValue); |
|
77 |
- } |
|
78 |
- } |
|
79 |
- } |
|
80 |
- |
|
81 |
- $objTemplate->fields = $arrFields; |
|
82 |
- |
|
83 |
- if ($this->jumpTo) |
|
84 |
- { |
|
85 |
- $objTemplate->link = $this->generateMemberUrl($objMember); |
|
86 |
- } |
|
87 |
- |
|
88 |
- return $objTemplate->parse(); |
|
89 |
- } |
|
90 |
- |
|
91 |
- /** |
|
92 |
- * Generate a URL and return it as string |
|
93 |
- * |
|
94 |
- * @param MemberModel $objMember |
|
95 |
- * |
|
96 |
- * @return string |
|
97 |
- */ |
|
98 |
- protected function generateMemberUrl(MemberModel $objMember): string |
|
99 |
- { |
|
100 |
- $objPage = PageModel::findPublishedById($this->jumpTo); |
|
101 |
- |
|
102 |
- if (!$objPage instanceof PageModel) |
|
103 |
- { |
|
104 |
- $strLink = StringUtil::ampersand(Environment::get('request')); |
|
105 |
- } |
|
106 |
- else |
|
107 |
- { |
|
108 |
- $params = (Config::get('useAutoItem') ? '/' : '/items/') . ($objMember->alias ?: $objMember->id); |
|
109 |
- $strLink = StringUtil::ampersand($objPage->getFrontendUrl($params)); |
|
110 |
- } |
|
111 |
- |
|
112 |
- return $strLink; |
|
113 |
- } |
|
114 |
- |
|
115 |
- protected function parseMemberDetails(&$arrFields, $field, $value) |
|
116 |
- { |
|
117 |
- $strReturn = sprintf('<span class="label">%s: </span>',$GLOBALS['TL_LANG']['tl_member'][$field][0] ?? null); |
|
118 |
- |
|
119 |
- if (!\is_array(($arrValue = StringUtil::deserialize($value)))) |
|
120 |
- { |
|
121 |
- switch ($field) { |
|
122 |
- case 'gender': |
|
123 |
- $strReturn .= $GLOBALS['TL_LANG']['MSC'][$value] ?? $value; |
|
124 |
- break; |
|
125 |
- |
|
126 |
- case 'email': |
|
127 |
- $strEmail = StringUtil::encodeEmail($value); |
|
128 |
- $strReturn .= '<a href="mailto:' . $strEmail . '" title="' . $strEmail . '">' . preg_replace('/\?.*$/', '', $strEmail) . '</a>'; |
|
129 |
- break; |
|
130 |
- |
|
131 |
- case 'phone': |
|
132 |
- case 'mobile': |
|
133 |
- case 'fax': |
|
134 |
- $strTel = preg_replace('/[^a-z\d+]/i', '', (string)$value); |
|
135 |
- $strReturn .= '<a href="tel:' . $strTel . '" title="' . $value . '">' . $value . '</a>'; |
|
136 |
- break; |
|
137 |
- |
|
138 |
- case 'website': |
|
139 |
- $strUrl = $value; |
|
140 |
- |
|
141 |
- if (strncmp($value, 'http://', 7) !== 0 || strncmp($value, 'https://', 8) !== 0) { |
|
142 |
- $strUrl = 'https://' . $value; |
|
143 |
- } |
|
144 |
- |
|
145 |
- $strReturn .= '<a href="' . $strUrl . '" title="' . $value . '" target="blank noopener" rel="noreferer">' . $value . '</a>'; |
|
146 |
- break; |
|
147 |
- |
|
148 |
- case 'dateOfBirth': |
|
149 |
- $strReturn .= Date::parse(Config::get('dateFormat'), $value) ?? $value; |
|
150 |
- break; |
|
151 |
- |
|
152 |
- case 'country': |
|
153 |
- $strReturn .= $GLOBALS['TL_LANG']['CNT'][$value] ?? $value; |
|
154 |
- break; |
|
155 |
- |
|
156 |
- case 'language': |
|
157 |
- $strReturn .= $GLOBALS['TL_LANG']['LNG'][$value] ?? $value; |
|
158 |
- break; |
|
159 |
- |
|
160 |
- default: |
|
161 |
- $strReturn .= $value; |
|
162 |
- } |
|
163 |
- } |
|
164 |
- else if ('groups' === $field) |
|
165 |
- { |
|
166 |
- $arrReturn = []; |
|
167 |
- |
|
168 |
- foreach ($arrValue as $value) |
|
169 |
- { |
|
170 |
- $arrReturn[] = MemberGroupModel::findById($value)->name; |
|
171 |
- } |
|
172 |
- |
|
173 |
- $strReturn .= implode(", ", $arrReturn); |
|
174 |
- } |
|
175 |
- |
|
176 |
- $arrFields[$field] = $strReturn; |
|
177 |
- } |
|
178 |
-} |
179 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,228 +0,0 @@ |
1 |
-<?php |
|
2 |
- |
|
3 |
-declare(strict_types=1); |
|
4 |
- |
|
5 |
-/* |
|
6 |
- * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
- * |
|
8 |
- * @package contao-member-extension-bundle |
|
9 |
- * @license MIT |
|
10 |
- * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
- * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
- * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
- * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
- */ |
|
15 |
- |
|
16 |
-namespace Oveleon\ContaoMemberExtensionBundle; |
|
17 |
- |
|
18 |
-use Contao\BackendTemplate; |
|
19 |
-use Contao\Config; |
|
20 |
-use Contao\CoreBundle\Exception\PageNotFoundException; |
|
21 |
-use Contao\Date; |
|
22 |
-use Contao\Environment; |
|
23 |
-use Contao\FrontendTemplate; |
|
24 |
-use Contao\Input; |
|
25 |
-use Contao\MemberModel; |
|
26 |
-use Contao\Model\Collection; |
|
27 |
-use Contao\Pagination; |
|
28 |
-use Contao\StringUtil; |
|
29 |
-use Contao\System; |
|
30 |
- |
|
31 |
-/** |
|
32 |
- * Class ModuleMemberList |
|
33 |
- * |
|
34 |
- * @property string $ext_order order of list items |
|
35 |
- * @property string ext_orderField order field for list items |
|
36 |
- * @property string $ext_groups considered member groups |
|
37 |
- * @property string $memberFields Fields to be displayed |
|
38 |
- * @property string $memberListTpl Frontend list template |
|
39 |
- */ |
|
40 |
-class ModuleMemberList extends ModuleMemberExtension |
|
41 |
-{ |
|
42 |
- |
|
43 |
- /** |
|
44 |
- * Template |
|
45 |
- * @var string |
|
46 |
- */ |
|
47 |
- protected $strTemplate = 'mod_memberList'; |
|
48 |
- |
|
49 |
- /** |
|
50 |
- * Template |
|
51 |
- * @var string |
|
52 |
- */ |
|
53 |
- protected $strMemberTemplate = 'memberExtension_list_default'; |
|
54 |
- |
|
55 |
- /** |
|
56 |
- * Display a wildcard in the back end |
|
57 |
- * |
|
58 |
- * @return string |
|
59 |
- */ |
|
60 |
- public function generate() |
|
61 |
- { |
|
62 |
- $container = System::getContainer(); |
|
63 |
- $request = System::getContainer()->get('request_stack')->getCurrentRequest(); |
|
64 |
- |
|
65 |
- if ($request && $container->get('contao.routing.scope_matcher')->isBackendRequest($request)) |
|
66 |
- { |
|
67 |
- $objTemplate = new BackendTemplate('be_wildcard'); |
|
68 |
- $objTemplate->wildcard = '### ' . $GLOBALS['TL_LANG']['FMD']['memberList'][0] . ' ###'; |
|
69 |
- $objTemplate->title = $this->headline; |
|
70 |
- $objTemplate->id = $this->id; |
|
71 |
- $objTemplate->link = $this->name; |
|
72 |
- $objTemplate->href = StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', ['do'=>'themes', 'table'=>'tl_module', 'act'=>'edit', 'id'=>$this->id])); |
|
73 |
- |
|
74 |
- return $objTemplate->parse(); |
|
75 |
- } |
|
76 |
- |
|
77 |
- return parent::generate(); |
|
78 |
- } |
|
79 |
- |
|
80 |
- /** |
|
81 |
- * Generate the module |
|
82 |
- */ |
|
83 |
- protected function compile() |
|
84 |
- { |
|
85 |
- $limit = null; |
|
86 |
- $offset = 0; |
|
87 |
- |
|
88 |
- $arrGroups = StringUtil::deserialize($this->ext_groups); |
|
89 |
- |
|
90 |
- if (empty($arrGroups) || !\is_array($arrGroups)) |
|
91 |
- { |
|
92 |
- $this->Template->empty = $GLOBALS['TL_LANG']['MSC']['emptyMemberList']; |
|
93 |
- return; |
|
94 |
- } |
|
95 |
- |
|
96 |
- $objTemplate = new FrontendTemplate($this->memberListTpl ?: $this->strMemberTemplate); |
|
97 |
- |
|
98 |
- $objMembers = $this->getMembers(); |
|
99 |
- |
|
100 |
- $intTotal = 0; |
|
101 |
- |
|
102 |
- $arrMembers = []; |
|
103 |
- |
|
104 |
- if (null !== $objMembers) |
|
105 |
- { |
|
106 |
- while($objMembers->next()) |
|
107 |
- { |
|
108 |
- $objMember = $objMembers->current(); |
|
109 |
- |
|
110 |
- if (!$this->checkMemberGroups($arrGroups, $objMember)) |
|
111 |
- { |
|
112 |
- continue; |
|
113 |
- } |
|
114 |
- |
|
115 |
- $intTotal += 1; |
|
116 |
- |
|
117 |
- $arrMemberFields = StringUtil::deserialize($this->memberFields, true); |
|
118 |
- $objTemplate->setData($objMember->row()); |
|
119 |
- |
|
120 |
- $arrMembers[] = $this->parseMemberTemplate($objMember, $objTemplate, $arrMemberFields, $this->imgSize); |
|
121 |
- } |
|
122 |
- } |
|
123 |
- |
|
124 |
- $total = $intTotal - $offset; |
|
125 |
- |
|
126 |
- if ($this->numberOfItems > 0) |
|
127 |
- { |
|
128 |
- $limit = $this->numberOfItems; |
|
129 |
- } |
|
130 |
- |
|
131 |
- if ($this->perPage > 0 && (!isset($limit) || $this->numberOfItems >= $this->perPage)) |
|
132 |
- { |
|
133 |
- if (isset($limit)) |
|
134 |
- { |
|
135 |
- $total = min($limit, $total); |
|
136 |
- } |
|
137 |
- |
|
138 |
- $id = 'page_n' . $this->id; |
|
139 |
- $page = Input::get($id) ?? 1; |
|
140 |
- |
|
141 |
- if ($page < 1 || $page > max(ceil($total/$this->perPage), 1)) |
|
142 |
- { |
|
143 |
- throw new PageNotFoundException('Page not found: ' . Environment::get('uri')); |
|
144 |
- } |
|
145 |
- |
|
146 |
- $limit = $this->perPage; |
|
147 |
- $offset += (max($page, 1) - 1) * $this->perPage; |
|
148 |
- $skip = 0; |
|
149 |
- |
|
150 |
- if ($offset + $limit > $total + $skip) |
|
151 |
- { |
|
152 |
- $limit = $total + $skip - $offset; |
|
153 |
- } |
|
154 |
- |
|
155 |
- $arrMembers = \array_slice($arrMembers, $offset, ((int) $limit ?: $intTotal), true); |
|
156 |
- |
|
157 |
- $objPagination = new Pagination($total, $this->perPage, Config::get('maxPaginationLinks'), $id); |
|
158 |
- $this->Template->pagination = $objPagination->generate("\n "); |
|
159 |
- } |
|
160 |
- |
|
161 |
- if (empty($arrMembers)) |
|
162 |
- { |
|
163 |
- $this->Template->empty = $GLOBALS['TL_LANG']['MSC']['emptyMemberList']; |
|
164 |
- } |
|
165 |
- |
|
166 |
- $this->Template->members = $arrMembers; |
|
167 |
- } |
|
168 |
- |
|
169 |
- /** |
|
170 |
- * Checks whether a member is in any given group |
|
171 |
- * |
|
172 |
- * @param array $arrGroups |
|
173 |
- * @param MemberModel $objMember |
|
174 |
- * @return bool |
|
175 |
- */ |
|
176 |
- private function checkMemberGroups(array $arrGroups, MemberModel $objMember): bool |
|
177 |
- { |
|
178 |
- if (empty($arrGroups)) |
|
179 |
- { |
|
180 |
- return false; |
|
181 |
- } |
|
182 |
- |
|
183 |
- $arrMemberGroups = StringUtil::deserialize($objMember->groups); |
|
184 |
- |
|
185 |
- if (!\is_array($arrMemberGroups) || !\count(array_intersect($arrGroups, $arrMemberGroups))) |
|
186 |
- { |
|
187 |
- return false; |
|
188 |
- } |
|
189 |
- |
|
190 |
- return true; |
|
191 |
- } |
|
192 |
- |
|
193 |
- /** |
|
194 |
- * Get members |
|
195 |
- * |
|
196 |
- * @return Collection|MemberModel|null |
|
197 |
- */ |
|
198 |
- private function getMembers() |
|
199 |
- { |
|
200 |
- $t = MemberModel::getTable(); |
|
201 |
- $time = Date::floorToMinute(); |
|
202 |
- |
|
203 |
- $arrColumns = ["$t.disable='' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time') "]; |
|
204 |
- $arrOptions = []; |
|
205 |
- |
|
206 |
- if (!!$this->ext_orderField) |
|
207 |
- { |
|
208 |
- $arrOptions['order'] .= "$t.$this->ext_orderField "; |
|
209 |
- } |
|
210 |
- |
|
211 |
- switch ($this->ext_order) |
|
212 |
- { |
|
213 |
- case 'order_random': |
|
214 |
- $arrOptions['order'] = "RAND()"; |
|
215 |
- break; |
|
216 |
- |
|
217 |
- case 'order_desc': |
|
218 |
- $arrOptions['order'] .= "DESC"; |
|
219 |
- break; |
|
220 |
- |
|
221 |
- case 'order_asc': |
|
222 |
- default: |
|
223 |
- break; |
|
224 |
- } |
|
225 |
- |
|
226 |
- return MemberModel::findBy($arrColumns, null, $arrOptions); |
|
227 |
- } |
|
228 |
-} |
229 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,114 +0,0 @@ |
1 |
-<?php |
|
2 |
- |
|
3 |
-declare(strict_types=1); |
|
4 |
- |
|
5 |
-/* |
|
6 |
- * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
- * |
|
8 |
- * @package contao-member-extension-bundle |
|
9 |
- * @license MIT |
|
10 |
- * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
- * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
- * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
- * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
- */ |
|
15 |
- |
|
16 |
-namespace Oveleon\ContaoMemberExtensionBundle; |
|
17 |
- |
|
18 |
-use Contao\BackendTemplate; |
|
19 |
-use Contao\Config; |
|
20 |
-use Contao\CoreBundle\Exception\PageNotFoundException; |
|
21 |
-use Contao\Environment; |
|
22 |
-use Contao\FrontendTemplate; |
|
23 |
-use Contao\Input; |
|
24 |
-use Contao\MemberModel; |
|
25 |
-use Contao\StringUtil; |
|
26 |
-use Contao\System; |
|
27 |
- |
|
28 |
-/** |
|
29 |
- * Class ModuleMemberList |
|
30 |
- * |
|
31 |
- * @property string $ext_groups considered member groups |
|
32 |
- * @property string $memberFields Fields to be displayed |
|
33 |
- * @property string $memberReaderTpl Frontend reader template |
|
34 |
- */ |
|
35 |
-class ModuleMemberReader extends ModuleMemberExtension |
|
36 |
-{ |
|
37 |
- |
|
38 |
- /** |
|
39 |
- * Template |
|
40 |
- * @var string |
|
41 |
- */ |
|
42 |
- protected $strTemplate = 'mod_memberReader'; |
|
43 |
- |
|
44 |
- /** |
|
45 |
- * Template |
|
46 |
- * @var string |
|
47 |
- */ |
|
48 |
- protected $strMemberTemplate = 'memberExtension_reader_full'; |
|
49 |
- |
|
50 |
- /** |
|
51 |
- * Display a wildcard in the back end |
|
52 |
- * |
|
53 |
- * @return string |
|
54 |
- */ |
|
55 |
- public function generate() |
|
56 |
- { |
|
57 |
- $container = System::getContainer(); |
|
58 |
- $request = System::getContainer()->get('request_stack')->getCurrentRequest(); |
|
59 |
- |
|
60 |
- if ($request && $container->get('contao.routing.scope_matcher')->isBackendRequest($request)) |
|
61 |
- { |
|
62 |
- $objTemplate = new BackendTemplate('be_wildcard'); |
|
63 |
- $objTemplate->wildcard = '### ' . $GLOBALS['TL_LANG']['FMD']['memberList'][0] . ' ###'; |
|
64 |
- $objTemplate->title = $this->headline; |
|
65 |
- $objTemplate->id = $this->id; |
|
66 |
- $objTemplate->link = $this->name; |
|
67 |
- $objTemplate->href = StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', ['do'=>'themes', 'table'=>'tl_module', 'act'=>'edit', 'id'=>$this->id])); |
|
68 |
- |
|
69 |
- return $objTemplate->parse(); |
|
70 |
- } |
|
71 |
- |
|
72 |
- // Set the item from the auto_item parameter |
|
73 |
- if (!isset($_GET['items']) && isset($_GET['auto_item']) && Config::get('useAutoItem')) |
|
74 |
- { |
|
75 |
- Input::setGet('items', Input::get('auto_item')); |
|
76 |
- } |
|
77 |
- |
|
78 |
- return parent::generate(); |
|
79 |
- } |
|
80 |
- |
|
81 |
- /** |
|
82 |
- * Generate the module |
|
83 |
- */ |
|
84 |
- protected function compile() |
|
85 |
- { |
|
86 |
- $this->Template->referer = 'javascript:history.go(-1)'; |
|
87 |
- $this->Template->back = $GLOBALS['TL_LANG']['MSC']['goBack']; |
|
88 |
- |
|
89 |
- // Get the member |
|
90 |
- $objMember = MemberModel::findByIdOrAlias(Input::get('items')); |
|
91 |
- |
|
92 |
- // The member does not exist and is not deactivated |
|
93 |
- if ($objMember === null || $objMember->disable) |
|
94 |
- { |
|
95 |
- throw new PageNotFoundException('Page not found: ' . Environment::get('uri')); |
|
96 |
- } |
|
97 |
- |
|
98 |
- // Check for group intersection |
|
99 |
- $arrGroups = StringUtil::deserialize($this->ext_groups); |
|
100 |
- $memberGroups = StringUtil::deserialize($objMember->groups); |
|
101 |
- |
|
102 |
- if (empty($arrGroups) || !\is_array($arrGroups) || !\count(array_intersect($arrGroups, $memberGroups))) |
|
103 |
- { |
|
104 |
- throw new PageNotFoundException('Page not found: ' . Environment::get('uri')); |
|
105 |
- } |
|
106 |
- |
|
107 |
- $arrMemberFields = StringUtil::deserialize($this->memberFields, true); |
|
108 |
- |
|
109 |
- $objTemplate = new FrontendTemplate($this->memberReaderTpl ?: $this->strMemberTemplate); |
|
110 |
- $objTemplate->setData($objMember->row()); |
|
111 |
- |
|
112 |
- $this->Template->member = $this->parseMemberTemplate($objMember, $objTemplate, $arrMemberFields, $this->imgSize); |
|
113 |
- } |
|
114 |
-} |
115 | 0 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,11 @@ |
1 |
+<?php $GLOBALS['TL_JAVASCRIPT'][] = 'bundles/contaomemberextension/scripts/filter.js|static'; ?> |
|
2 |
+ |
|
3 |
+<form method="POST" class="member-filter-form"> |
|
4 |
+ <div class="formbody"> |
|
5 |
+ <input type="hidden" name="FORM_SUBMIT" value="<?= $this->formId ?>"> |
|
6 |
+ <input type="hidden" name="REQUEST_TOKEN" value="<?= $this->requestToken ?>"> |
|
7 |
+ <?php foreach ($this->filters as $filter): ?> |
|
8 |
+ <?= $filter ?> |
|
9 |
+ <?php endforeach; ?> |
|
10 |
+ </div> |
|
11 |
+</form> |
0 | 12 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,16 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+$GLOBALS['TL_JAVASCRIPT'][] = 'bundles/contaomemberextension/scripts/dataTables.min.js|static'; |
|
4 |
+$GLOBALS['TL_CSS'][] = 'bundles/contaomemberextension/css/dataTables.min.css|static'; |
|
5 |
+$GLOBALS['TL_HEAD'][] = "<script>window.addEventListener('load',()=>{ |
|
6 |
+ document.querySelectorAll('.member-table > table')?.forEach(table => { |
|
7 |
+ new DataTable(table, { |
|
8 |
+ paging: !!table.dataset.pagingTrue, |
|
9 |
+ language: { |
|
10 |
+ 'search': table.dataset.searchLabel, |
|
11 |
+ 'zeroRecords': table.dataset.zeroLabel |
|
12 |
+ }, |
|
13 |
+ info: false |
|
14 |
+ }); |
|
15 |
+ }) |
|
16 |
+})</script>"; |
... | ... |
@@ -10,7 +10,7 @@ |
10 | 10 |
<form id="<?= $this->formId ?>" method="post"> |
11 | 11 |
<div class="formbody"> |
12 | 12 |
<input type="hidden" name="FORM_SUBMIT" value="<?= $this->formId ?>"> |
13 |
- <input type="hidden" name="REQUEST_TOKEN" value="{{request_token}}"> |
|
13 |
+ <input type="hidden" name="REQUEST_TOKEN" value="<?= $this->requestToken ?>"> |
|
14 | 14 |
<div class="widget widget-submit"> |
15 | 15 |
<button type="submit" class="submit"><?= $this->slabel ?></button> |
16 | 16 |
</div> |
17 | 17 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,10 @@ |
1 |
+<tr class="member_list_table_item"> |
|
2 |
+ <?php foreach ($this->fields as $k => $v): ?> |
|
3 |
+ <td class="<?= $k ?>"><?= $v ?></td> |
|
4 |
+ <?php endforeach; ?> |
|
5 |
+ <?php if ($this->link): ?> |
|
6 |
+ <td class="detail-link"> |
|
7 |
+ <a href="<?=$this->link?>"><?= $this->trans('MSC.memberDetail') ?></a> |
|
8 |
+ </td> |
|
9 |
+ <?php endif; ?> |
|
10 |
+</tr> |
... | ... |
@@ -2,13 +2,23 @@ |
2 | 2 |
|
3 | 3 |
<?php $this->block('content'); ?> |
4 | 4 |
|
5 |
+<?php if (!empty($this->filters)): ?> |
|
6 |
+ <div class="member-list-filter"> |
|
7 |
+ <?php $this->insert('memberExtension_filter', [ |
|
8 |
+ 'filters' => $this->filters, |
|
9 |
+ 'requestToken' => $this->requestToken, |
|
10 |
+ 'formId' => $this->filterFormId, |
|
11 |
+ ]) ?> |
|
12 |
+ </div> |
|
13 |
+<?php endif; ?> |
|
14 |
+ |
|
5 | 15 |
<?php if (empty($this->members)): ?> |
6 |
- <p class="empty message"><?=$this->empty?></p> |
|
16 |
+ <p class="empty message"><?=$this->empty?></p> |
|
7 | 17 |
<?php else: ?> |
8 |
- <?php foreach ($this->members as $member): ?> |
|
9 |
- <?=$member?> |
|
10 |
- <?php endforeach; ?> |
|
11 |
- <?= $this->pagination ?> |
|
18 |
+ <?php foreach ($this->members as $member): ?> |
|
19 |
+ <?=$member?> |
|
20 |
+ <?php endforeach; ?> |
|
21 |
+ <?= $this->pagination ?> |
|
12 | 22 |
<?php endif; ?> |
13 | 23 |
|
14 | 24 |
<?php $this->endblock(); ?> |
15 | 25 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,42 @@ |
1 |
+ |
|
2 |
+<?php $this->extend('block_unsearchable'); ?> |
|
3 |
+ |
|
4 |
+<?php $this->block('content'); ?> |
|
5 |
+ |
|
6 |
+<?php if (!empty($this->filters)): ?> |
|
7 |
+ <div class="member-list-filter"> |
|
8 |
+ <?php $this->insert('memberExtension_filter', [ |
|
9 |
+ 'filters' => $this->filters, |
|
10 |
+ 'requestToken' => $this->requestToken, |
|
11 |
+ 'formId' => $this->filterFormId, |
|
12 |
+ ]) ?> |
|
13 |
+ </div> |
|
14 |
+<?php endif; ?> |
|
15 |
+ |
|
16 |
+<div class="member-table"> |
|
17 |
+ <?php if (empty($this->members)): ?> |
|
18 |
+ <p class="empty message"><?=$this->empty?></p> |
|
19 |
+ <?php else: ?> |
|
20 |
+ <table data-zero-label="No matching records found" data-search-label="Search:"<?= (($this->perPage > 0) && ($this->total > 10)) ? ' data-paging-true="1"' : '' ?>> |
|
21 |
+ <thead> |
|
22 |
+ <tr> |
|
23 |
+ <?php foreach ($this->labels as $label): ?> |
|
24 |
+ <th class="head"><?= $label ?></th> |
|
25 |
+ <?php endforeach; ?> |
|
26 |
+ <?php if ($this->hasDetailPage): ?> |
|
27 |
+ <th class="head"><?= $this->trans('MSC.memberDetailHeader') ?></th> |
|
28 |
+ <?php endif; ?> |
|
29 |
+ </tr> |
|
30 |
+ </thead> |
|
31 |
+ <tbody> |
|
32 |
+ <?php foreach ($this->members as $row): ?> |
|
33 |
+ <?= $row ?> |
|
34 |
+ <?php endforeach; ?> |
|
35 |
+ </tbody> |
|
36 |
+ </table> |
|
37 |
+ <?php endif; ?> |
|
38 |
+</div> |
|
39 |
+ |
|
40 |
+<?= $this->pagination ?> |
|
41 |
+ |
|
42 |
+<?php $this->endblock(); ?> |
4 | 47 |
new file mode 100644 |
... | ... |
@@ -0,0 +1 @@ |
1 |
+:root{--dt-row-selected: 13, 110, 253;--dt-row-selected-text: 255, 255, 255;--dt-row-selected-link: 9, 10, 11;--dt-row-stripe: 0, 0, 0;--dt-row-hover: 0, 0, 0;--dt-column-ordering: 0, 0, 0;--dt-html-background: white}:root.dark{--dt-html-background: rgb(33, 37, 41)}table.dataTable td.dt-control{text-align:center;cursor:pointer}table.dataTable td.dt-control:before{display:inline-block;box-sizing:border-box;content:"";border-top:5px solid transparent;border-left:10px solid rgba(0, 0, 0, 0.5);border-bottom:5px solid transparent;border-right:0px solid transparent}table.dataTable tr.dt-hasChild td.dt-control:before{border-top:10px solid rgba(0, 0, 0, 0.5);border-left:5px solid transparent;border-bottom:0px solid transparent;border-right:5px solid transparent}html.dark table.dataTable td.dt-control:before,:root[data-bs-theme=dark] table.dataTable td.dt-control:before{border-left-color:rgba(255, 255, 255, 0.5)}html.dark table.dataTable tr.dt-hasChild td.dt-control:before,:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before{border-top-color:rgba(255, 255, 255, 0.5);border-left-color:transparent}div.dt-scroll-body thead tr,div.dt-scroll-body tfoot tr{height:0}div.dt-scroll-body thead tr th,div.dt-scroll-body thead tr td,div.dt-scroll-body tfoot tr th,div.dt-scroll-body tfoot tr td{height:0 !important;padding-top:0px !important;padding-bottom:0px !important;border-top-width:0px !important;border-bottom-width:0px !important}div.dt-scroll-body thead tr th div.dt-scroll-sizing,div.dt-scroll-body thead tr td div.dt-scroll-sizing,div.dt-scroll-body tfoot tr th div.dt-scroll-sizing,div.dt-scroll-body tfoot tr td div.dt-scroll-sizing{height:0 !important;overflow:hidden !important}table.dataTable thead>tr>th:active,table.dataTable thead>tr>td:active{outline:none}table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:before{position:absolute;display:block;bottom:50%;content:"â–²";content:"â–²"/""}table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:after{position:absolute;display:block;top:50%;content:"â–¼";content:"â–¼"/""}table.dataTable thead>tr>th.dt-orderable-asc,table.dataTable thead>tr>th.dt-orderable-desc,table.dataTable thead>tr>th.dt-ordering-asc,table.dataTable thead>tr>th.dt-ordering-desc,table.dataTable thead>tr>td.dt-orderable-asc,table.dataTable thead>tr>td.dt-orderable-desc,table.dataTable thead>tr>td.dt-ordering-asc,table.dataTable thead>tr>td.dt-ordering-desc{position:relative;padding-right:30px}table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order,table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order{position:absolute;right:12px;top:0;bottom:0;width:12px}table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order:after,table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order:before,table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:after,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order:after,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:after{left:0;opacity:.125;line-height:9px;font-size:.8em}table.dataTable thead>tr>th.dt-orderable-asc,table.dataTable thead>tr>th.dt-orderable-desc,table.dataTable thead>tr>td.dt-orderable-asc,table.dataTable thead>tr>td.dt-orderable-desc{cursor:pointer}table.dataTable thead>tr>th.dt-orderable-asc:hover,table.dataTable thead>tr>th.dt-orderable-desc:hover,table.dataTable thead>tr>td.dt-orderable-asc:hover,table.dataTable thead>tr>td.dt-orderable-desc:hover{outline:2px solid rgba(0, 0, 0, 0.05);outline-offset:-2px}table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:after{opacity:.6}table.dataTable thead>tr>th.sorting_desc_disabled span.dt-column-order:after,table.dataTable thead>tr>th.sorting_asc_disabled span.dt-column-order:before,table.dataTable thead>tr>td.sorting_desc_disabled span.dt-column-order:after,table.dataTable thead>tr>td.sorting_asc_disabled span.dt-column-order:before{display:none}table.dataTable thead>tr>th:active,table.dataTable thead>tr>td:active{outline:none}div.dt-scroll-body>table.dataTable>thead>tr>th,div.dt-scroll-body>table.dataTable>thead>tr>td{overflow:hidden}:root.dark table.dataTable thead>tr>th.dt-orderable-asc:hover,:root.dark table.dataTable thead>tr>th.dt-orderable-desc:hover,:root.dark table.dataTable thead>tr>td.dt-orderable-asc:hover,:root.dark table.dataTable thead>tr>td.dt-orderable-desc:hover,:root[data-bs-theme=dark] table.dataTable thead>tr>th.dt-orderable-asc:hover,:root[data-bs-theme=dark] table.dataTable thead>tr>th.dt-orderable-desc:hover,:root[data-bs-theme=dark] table.dataTable thead>tr>td.dt-orderable-asc:hover,:root[data-bs-theme=dark] table.dataTable thead>tr>td.dt-orderable-desc:hover{outline:2px solid rgba(255, 255, 255, 0.05)}div.dt-processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-22px;text-align:center;padding:2px;z-index:10}div.dt-processing>div:last-child{position:relative;width:80px;height:15px;margin:1em auto}div.dt-processing>div:last-child>div{position:absolute;top:0;width:13px;height:13px;border-radius:50%;background:rgb(13, 110, 253);background:rgb(var(--dt-row-selected));animation-timing-function:cubic-bezier(0, 1, 1, 0)}div.dt-processing>div:last-child>div:nth-child(1){left:8px;animation:datatables-loader-1 .6s infinite}div.dt-processing>div:last-child>div:nth-child(2){left:8px;animation:datatables-loader-2 .6s infinite}div.dt-processing>div:last-child>div:nth-child(3){left:32px;animation:datatables-loader-2 .6s infinite}div.dt-processing>div:last-child>div:nth-child(4){left:56px;animation:datatables-loader-3 .6s infinite}@keyframes datatables-loader-1{0%{transform:scale(0)}100%{transform:scale(1)}}@keyframes datatables-loader-3{0%{transform:scale(1)}100%{transform:scale(0)}}@keyframes datatables-loader-2{0%{transform:translate(0, 0)}100%{transform:translate(24px, 0)}}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable th,table.dataTable td{box-sizing:border-box}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable th.dt-empty,table.dataTable td.dt-empty{text-align:center;vertical-align:top}table.dataTable th.dt-type-numeric,table.dataTable th.dt-type-date,table.dataTable td.dt-type-numeric,table.dataTable td.dt-type-date{text-align:right}table.dataTable thead th,table.dataTable thead td,table.dataTable tfoot th,table.dataTable tfoot td{text-align:left}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable{width:100%;margin:0 auto;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable>thead>tr>th,table.dataTable>thead>tr>td{padding:10px;border-bottom:1px solid rgba(0, 0, 0, 0.3)}table.dataTable>thead>tr>th:active,table.dataTable>thead>tr>td:active{outline:none}table.dataTable>tfoot>tr>th,table.dataTable>tfoot>tr>td{border-top:1px solid rgba(0, 0, 0, 0.3);padding:10px 10px 6px 10px}table.dataTable>tbody>tr{background-color:transparent}table.dataTable>tbody>tr:first-child>*{border-top:none}table.dataTable>tbody>tr:last-child>*{border-bottom:none}table.dataTable>tbody>tr.selected>*{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.9);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.9);color:rgb(255, 255, 255);color:rgb(var(--dt-row-selected-text))}table.dataTable>tbody>tr.selected a{color:rgb(9, 10, 11);color:rgb(var(--dt-row-selected-link))}table.dataTable>tbody>tr>th,table.dataTable>tbody>tr>td{padding:8px 10px}table.dataTable.row-border>tbody>tr>*,table.dataTable.display>tbody>tr>*{border-top:1px solid rgba(0, 0, 0, 0.15)}table.dataTable.row-border>tbody>tr:first-child>*,table.dataTable.display>tbody>tr:first-child>*{border-top:none}table.dataTable.row-border>tbody>tr.selected+tr.selected>td,table.dataTable.display>tbody>tr.selected+tr.selected>td{border-top-color:rgba(13, 110, 253, 0.65);border-top-color:rgba(var(--dt-row-selected), 0.65)}table.dataTable.cell-border>tbody>tr>*{border-top:1px solid rgba(0, 0, 0, 0.15);border-right:1px solid rgba(0, 0, 0, 0.15)}table.dataTable.cell-border>tbody>tr>*:first-child{border-left:1px solid rgba(0, 0, 0, 0.15)}table.dataTable.cell-border>tbody>tr:first-child>*{border-top:1px solid rgba(0, 0, 0, 0.3)}table.dataTable.stripe>tbody>tr:nth-child(odd)>*,table.dataTable.display>tbody>tr:nth-child(odd)>*{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.023);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.023)}table.dataTable.stripe>tbody>tr:nth-child(odd).selected>*,table.dataTable.display>tbody>tr:nth-child(odd).selected>*{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.923);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.923)}table.dataTable.hover>tbody>tr:hover>*,table.dataTable.display>tbody>tr:hover>*{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.035);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.035)}table.dataTable.hover>tbody>tr.selected:hover>*,table.dataTable.display>tbody>tr.selected:hover>*{box-shadow:inset 0 0 0 9999px #0d6efd !important;box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 1) !important}table.dataTable.order-column>tbody tr>.sorting_1,table.dataTable.order-column>tbody tr>.sorting_2,table.dataTable.order-column>tbody tr>.sorting_3,table.dataTable.display>tbody tr>.sorting_1,table.dataTable.display>tbody tr>.sorting_2,table.dataTable.display>tbody tr>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.019);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.019)}table.dataTable.order-column>tbody tr.selected>.sorting_1,table.dataTable.order-column>tbody tr.selected>.sorting_2,table.dataTable.order-column>tbody tr.selected>.sorting_3,table.dataTable.display>tbody tr.selected>.sorting_1,table.dataTable.display>tbody tr.selected>.sorting_2,table.dataTable.display>tbody tr.selected>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.919);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919)}table.dataTable.display>tbody>tr:nth-child(odd)>.sorting_1,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd)>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.054);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.054)}table.dataTable.display>tbody>tr:nth-child(odd)>.sorting_2,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd)>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.047);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.047)}table.dataTable.display>tbody>tr:nth-child(odd)>.sorting_3,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd)>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.039);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.039)}table.dataTable.display>tbody>tr:nth-child(odd).selected>.sorting_1,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd).selected>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.954);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.954)}table.dataTable.display>tbody>tr:nth-child(odd).selected>.sorting_2,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd).selected>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.947);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.947)}table.dataTable.display>tbody>tr:nth-child(odd).selected>.sorting_3,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd).selected>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.939);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.939)}table.dataTable.display>tbody>tr.even>.sorting_1,table.dataTable.order-column.stripe>tbody>tr.even>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.019);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.019)}table.dataTable.display>tbody>tr.even>.sorting_2,table.dataTable.order-column.stripe>tbody>tr.even>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.011);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.011)}table.dataTable.display>tbody>tr.even>.sorting_3,table.dataTable.order-column.stripe>tbody>tr.even>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.003);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.003)}table.dataTable.display>tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe>tbody>tr.even.selected>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.919);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919)}table.dataTable.display>tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe>tbody>tr.even.selected>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.911);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.911)}table.dataTable.display>tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe>tbody>tr.even.selected>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.903);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.903)}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.082);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.082)}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.074);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.074)}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.062);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.062)}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.982);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.982)}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.974);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.974)}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.962);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.962)}table.dataTable.compact thead th,table.dataTable.compact thead td,table.dataTable.compact tfoot th,table.dataTable.compact tfoot td,table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}div.dt-container{position:relative;clear:both}div.dt-container div.dt-layout-row{display:table;clear:both;width:100%}div.dt-container div.dt-layout-row.dt-layout-table{display:block}div.dt-container div.dt-layout-row.dt-layout-table div.dt-layout-cell{display:block}div.dt-container div.dt-layout-cell{display:table-cell;vertical-align:middle;padding:5px 0}div.dt-container div.dt-layout-cell.dt-full{text-align:center}div.dt-container div.dt-layout-cell.dt-start{text-align:left}div.dt-container div.dt-layout-cell.dt-end{text-align:right}div.dt-container div.dt-layout-cell:empty{display:none}div.dt-container .dt-search input{border:1px solid #aaa;border-radius:3px;padding:5px;background-color:transparent;color:inherit;margin-left:3px}div.dt-container .dt-input{border:1px solid #aaa;border-radius:3px;padding:5px;background-color:transparent;color:inherit}div.dt-container select.dt-input{padding:4px}div.dt-container .dt-paging .dt-paging-button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;color:inherit !important;border:1px solid transparent;border-radius:2px;background:transparent}div.dt-container .dt-paging .dt-paging-button.current,div.dt-container .dt-paging .dt-paging-button.current:hover{color:inherit !important;border:1px solid rgba(0, 0, 0, 0.3);background-color:rgba(0, 0, 0, 0.05);background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(230, 230, 230, 0.05)), color-stop(100%, rgba(0, 0, 0, 0.05)));background:-webkit-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:-moz-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:-ms-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:-o-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:linear-gradient(to bottom, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%)}div.dt-container .dt-paging .dt-paging-button.disabled,div.dt-container .dt-paging .dt-paging-button.disabled:hover,div.dt-container .dt-paging .dt-paging-button.disabled:active{cursor:default;color:rgba(0, 0, 0, 0.5) !important;border:1px solid transparent;background:transparent;box-shadow:none}div.dt-container .dt-paging .dt-paging-button:hover{color:white !important;border:1px solid #111;background-color:#111;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));background:-webkit-linear-gradient(top, #585858 0%, #111 100%);background:-moz-linear-gradient(top, #585858 0%, #111 100%);background:-ms-linear-gradient(top, #585858 0%, #111 100%);background:-o-linear-gradient(top, #585858 0%, #111 100%);background:linear-gradient(to bottom, #585858 0%, #111 100%)}div.dt-container .dt-paging .dt-paging-button:active{outline:none;background-color:#0c0c0c;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));background:-webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);box-shadow:inset 0 0 3px #111}div.dt-container .dt-paging .ellipsis{padding:0 1em}div.dt-container .dt-length,div.dt-container .dt-search,div.dt-container .dt-info,div.dt-container .dt-processing,div.dt-container .dt-paging{color:inherit}div.dt-container .dataTables_scroll{clear:both}div.dt-container .dataTables_scroll div.dt-scroll-body{-webkit-overflow-scrolling:touch}div.dt-container .dataTables_scroll div.dt-scroll-body>table>thead>tr>th,div.dt-container .dataTables_scroll div.dt-scroll-body>table>thead>tr>td,div.dt-container .dataTables_scroll div.dt-scroll-body>table>tbody>tr>th,div.dt-container .dataTables_scroll div.dt-scroll-body>table>tbody>tr>td{vertical-align:middle}div.dt-container .dataTables_scroll div.dt-scroll-body>table>thead>tr>th>div.dataTables_sizing,div.dt-container .dataTables_scroll div.dt-scroll-body>table>thead>tr>td>div.dataTables_sizing,div.dt-container .dataTables_scroll div.dt-scroll-body>table>tbody>tr>th>div.dataTables_sizing,div.dt-container .dataTables_scroll div.dt-scroll-body>table>tbody>tr>td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}div.dt-container.dt-empty-footer tbody>tr:last-child>*{border-bottom:1px solid rgba(0, 0, 0, 0.3)}div.dt-container.dt-empty-footer .dt-scroll-body{border-bottom:1px solid rgba(0, 0, 0, 0.3)}div.dt-container.dt-empty-footer .dt-scroll-body tbody>tr:last-child>*{border-bottom:none}@media screen and (max-width: 767px){div.dt-container div.dt-layout-row{display:block}div.dt-container div.dt-layout-cell{display:block}div.dt-container div.dt-layout-cell.dt-full,div.dt-container div.dt-layout-cell.dt-start,div.dt-container div.dt-layout-cell.dt-end{text-align:center}}@media screen and (max-width: 640px){.dt-container .dt-length,.dt-container .dt-search{float:none;text-align:center}.dt-container .dt-search{margin-top:.5em}}html.dark{--dt-row-hover: 255, 255, 255;--dt-row-stripe: 255, 255, 255;--dt-column-ordering: 255, 255, 255}html.dark table.dataTable>thead>tr>th,html.dark table.dataTable>thead>tr>td{border-bottom:1px solid rgb(89, 91, 94)}html.dark table.dataTable>thead>tr>th:active,html.dark table.dataTable>thead>tr>td:active{outline:none}html.dark table.dataTable>tfoot>tr>th,html.dark table.dataTable>tfoot>tr>td{border-top:1px solid rgb(89, 91, 94)}html.dark table.dataTable.row-border>tbody>tr>*,html.dark table.dataTable.display>tbody>tr>*{border-top:1px solid rgb(64, 67, 70)}html.dark table.dataTable.row-border>tbody>tr:first-child>*,html.dark table.dataTable.display>tbody>tr:first-child>*{border-top:none}html.dark table.dataTable.row-border>tbody>tr.selected+tr.selected>td,html.dark table.dataTable.display>tbody>tr.selected+tr.selected>td{border-top-color:rgba(13, 110, 253, 0.65);border-top-color:rgba(var(--dt-row-selected), 0.65)}html.dark table.dataTable.cell-border>tbody>tr>th,html.dark table.dataTable.cell-border>tbody>tr>td{border-top:1px solid rgb(64, 67, 70);border-right:1px solid rgb(64, 67, 70)}html.dark table.dataTable.cell-border>tbody>tr>th:first-child,html.dark table.dataTable.cell-border>tbody>tr>td:first-child{border-left:1px solid rgb(64, 67, 70)}html.dark .dt-container.dt-empty-footer table.dataTable{border-bottom:1px solid rgb(89, 91, 94)}html.dark .dt-container .dt-search input,html.dark .dt-container .dt-length select{border:1px solid rgba(255, 255, 255, 0.2);background-color:var(--dt-html-background)}html.dark .dt-container .dt-paging .dt-paging-button.current,html.dark .dt-container .dt-paging .dt-paging-button.current:hover{border:1px solid rgb(89, 91, 94);background:rgba(255, 255, 255, 0.15)}html.dark .dt-container .dt-paging .dt-paging-button.disabled,html.dark .dt-container .dt-paging .dt-paging-button.disabled:hover,html.dark .dt-container .dt-paging .dt-paging-button.disabled:active{color:#666 !important}html.dark .dt-container .dt-paging .dt-paging-button:hover{border:1px solid rgb(53, 53, 53);background:rgb(53, 53, 53)}html.dark .dt-container .dt-paging .dt-paging-button:active{background:#3a3a3a}*[dir=rtl] table.dataTable thead th,*[dir=rtl] table.dataTable thead td,*[dir=rtl] table.dataTable tfoot th,*[dir=rtl] table.dataTable tfoot td{text-align:right}*[dir=rtl] table.dataTable th.dt-type-numeric,*[dir=rtl] table.dataTable th.dt-type-date,*[dir=rtl] table.dataTable td.dt-type-numeric,*[dir=rtl] table.dataTable td.dt-type-date{text-align:left}*[dir=rtl] div.dt-container div.dt-layout-cell.dt-start{text-align:right}*[dir=rtl] div.dt-container div.dt-layout-cell.dt-end{text-align:left}*[dir=rtl] div.dt-container div.dt-search input{margin:0 3px 0 0} |
|
0 | 2 |
\ No newline at end of file |
1 | 3 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,4 @@ |
1 |
+/*! DataTables 2.0.8 |
|
2 |
+ * © SpryMedia Ltd - datatables.net/license |
|
3 |
+ */ |
|
4 |
+!function(n){"use strict";var a;"function"==typeof define&&define.amd?define(["jquery"],function(t){return n(t,window,document)}):"object"==typeof exports?(a=require("jquery"),"undefined"==typeof window?module.exports=function(t,e){return t=t||window,e=e||a(t),n(e,t,t.document)}:module.exports=n(a,window,window.document)):window.DataTable=n(jQuery,window,document)}(function(V,q,_){"use strict";function g(t){var e=parseInt(t,10);return!isNaN(e)&&isFinite(t)?e:null}function o(t,e,n){var a=typeof t,r="string"==a;return"number"==a||"bigint"==a||!!y(t)||(e&&r&&(t=R(t,e)),n&&r&&(t=t.replace(P,"")),!isNaN(parseFloat(t))&&isFinite(t))}function l(t,e,n){var a;return!!y(t)||("string"!=typeof t||!t.match(/<(input|select)/i))&&(y(a=t)||"string"==typeof a)&&!!o(I(t),e,n)||null}function v(t,e,n,a){var r=[],o=0,i=e.length;if(void 0!==a)for(;o<i;o++)t[e[o]][n]&&r.push(t[e[o]][n][a]);else for(;o<i;o++)t[e[o]]&&r.push(t[e[o]][n]);return r}function h(t,e){var n,a=[];void 0===e?(e=0,n=t):(n=e,e=t);for(var r=e;r<n;r++)a.push(r);return a}function b(t){for(var e=[],n=0,a=t.length;n<a;n++)t[n]&&e.push(t[n]);return e}var C,U,e,t,$=function(t,H){var W,X,B;return $.factory(t,H)?$:this instanceof $?V(t).DataTable(H):(X=void 0===(H=t),B=(W=this).length,X&&(H={}),this.api=function(){return new U(this)},this.each(function(){var n=1<B?Zt({},H,!0):H,a=0,t=this.getAttribute("id"),r=!1,e=$.defaults,o=V(this);if("table"!=this.nodeName.toLowerCase())Z(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{V(this).trigger("options.dt",n),nt(e),at(e.column),z(e,e,!0),z(e.column,e.column,!0),z(e,V.extend(n,o.data()),!0);for(var i=$.settings,a=0,l=i.length;a<l;a++){var s=i[a];if(s.nTable==this||s.nTHead&&s.nTHead.parentNode==this||s.nTFoot&&s.nTFoot.parentNode==this){var E=(void 0!==n.bRetrieve?n:e).bRetrieve,k=(void 0!==n.bDestroy?n:e).bDestroy;if(X||E)return s.oInstance;if(k){new $.Api(s).destroy();break}return void Z(s,0,"Cannot reinitialise DataTable",3)}if(s.sTableId==this.id){i.splice(a,1);break}}null!==t&&""!==t||(t="DataTables_Table_"+$.ext._unique++,this.id=t);var u=V.extend(!0,{},$.models.oSettings,{sDestroyWidth:o[0].style.width,sInstance:t,sTableId:t,colgroup:V("<colgroup>").prependTo(this),fastData:function(t,e,n){return G(u,t,e,n)}}),t=(u.nTable=this,u.oInit=n,i.push(u),u.api=new U(u),u.oInstance=1===W.length?W:o.dataTable(),nt(n),n.aLengthMenu&&!n.iDisplayLength&&(n.iDisplayLength=Array.isArray(n.aLengthMenu[0])?n.aLengthMenu[0][0]:V.isPlainObject(n.aLengthMenu[0])?n.aLengthMenu[0].value:n.aLengthMenu[0]),n=Zt(V.extend(!0,{},e),n),Q(u.oFeatures,n,["bPaginate","bLengthChange","bFilter","bSort","bSortMulti","bInfo","bProcessing","bAutoWidth","bSortClasses","bServerSide","bDeferRender"]),Q(u,n,["ajax","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","iStateDuration","bSortCellsTop","iTabIndex","sDom","fnStateLoadCallback","fnStateSaveCallback","renderer","searchDelay","rowId","caption","layout",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"]]),Q(u.oScroll,n,[["sScrollX","sX"],["sScrollXInner","sXInner"],["sScrollY","sY"],["bScrollCollapse","bCollapse"]]),Q(u.oLanguage,n,"fnInfoCallback"),K(u,"aoDrawCallback",n.fnDrawCallback),K(u,"aoStateSaveParams",n.fnStateSaveParams),K(u,"aoStateLoadParams",n.fnStateLoadParams),K(u,"aoStateLoaded",n.fnStateLoaded),K(u,"aoRowCallback",n.fnRowCallback),K(u,"aoRowCreatedCallback",n.fnCreatedRow),K(u,"aoHeaderCallback",n.fnHeaderCallback),K(u,"aoFooterCallback",n.fnFooterCallback),K(u,"aoInitComplete",n.fnInitComplete),K(u,"aoPreDrawCallback",n.fnPreDrawCallback),u.rowIdFn=J(n.rowId),u),c=($.__browser||(P={},$.__browser=P,j=V("<div/>").css({position:"fixed",top:0,left:-1*q.pageXOffset,height:1,width:1,overflow:"hidden"}).append(V("<div/>").css({position:"absolute",top:1,left:1,width:100,overflow:"scroll"}).append(V("<div/>").css({width:"100%",height:10}))).appendTo("body"),p=j.children(),O=p.children(),P.barWidth=p[0].offsetWidth-p[0].clientWidth,P.bScrollbarLeft=1!==Math.round(O.offset().left),j.remove()),V.extend(t.oBrowser,$.__browser),t.oScroll.iBarWidth=$.__browser.barWidth,u.oClasses),d=(V.extend(c,$.ext.classes,n.oClasses),o.addClass(c.table),u.oFeatures.bPaginate||(n.iDisplayStart=0),void 0===u.iInitDisplayStart&&(u.iInitDisplayStart=n.iDisplayStart,u._iDisplayStart=n.iDisplayStart),u.oLanguage),f=(V.extend(!0,d,n.oLanguage),d.sUrl?(V.ajax({dataType:"json",url:d.sUrl,success:function(t){z(e.oLanguage,t),V.extend(!0,d,t,u.oInit.oLanguage),tt(u,null,"i18n",[u],!0),Et(u)},error:function(){Z(u,0,"i18n file loading error",21),Et(u)}}),r=!0):tt(u,null,"i18n",[u]),[]),h=this.getElementsByTagName("thead"),p=It(u,h[0]);if(n.aoColumns)f=n.aoColumns;else if(p.length)for(l=p[a=0].length;a<l;a++)f.push(null);for(a=0,l=f.length;a<l;a++)rt(u);var g,m,v,b,y,D,x,S=u,T=n.aoColumnDefs,w=f,M=p,_=function(t,e){ot(u,t,e)},C=S.aoColumns;if(w)for(g=0,m=w.length;g<m;g++)w[g]&&w[g].name&&(C[g].sName=w[g].name);if(T)for(g=T.length-1;0<=g;g--){var I=void 0!==(x=T[g]).target?x.target:void 0!==x.targets?x.targets:x.aTargets;for(Array.isArray(I)||(I=[I]),v=0,b=I.length;v<b;v++){var A=I[v];if("number"==typeof A&&0<=A){for(;C.length<=A;)rt(S);_(A,x)}else if("number"==typeof A&&A<0)_(C.length+A,x);else if("string"==typeof A)for(y=0,D=C.length;y<D;y++)"_all"===A?_(y,x):-1!==A.indexOf(":name")?C[y].sName===A.replace(":name","")&&_(y,x):M.forEach(function(t){t[y]&&(t=V(t[y].cell),A.match(/^[a-z][\w-]*$/i)&&(A="."+A),t.is(A))&&_(y,x)})}}if(w)for(g=0,m=w.length;g<m;g++)_(g,w[g]);var L,F,N,j,P=o.children("tbody").find("tr").eq(0),R=(P.length&&(L=function(t,e){return null!==t.getAttribute("data-"+e)?e:null},V(P[0]).children("th, td").each(function(t,e){var n,a=u.aoColumns[t];a||Z(u,0,"Incorrect column count",18),a.mData===t&&(n=L(e,"sort")||L(e,"order"),e=L(e,"filter")||L(e,"search"),null===n&&null===e||(a.mData={_:t+".display",sort:null!==n?t+".@data-"+n:void 0,type:null!==n?t+".@data-"+n:void 0,filter:null!==e?t+".@data-"+e:void 0},a._isArrayHost=!0,ot(u,t)))})),u.oFeatures),O=function(){if(void 0===n.aaSorting){var t=u.aaSorting;for(a=0,l=t.length;a<l;a++)t[a][1]=u.aoColumns[a].asSorting[0]}Yt(u),K(u,"aoDrawCallback",function(){(u.bSorted||"ssp"===et(u)||R.bDeferRender)&&Yt(u)});var e=o.children("caption"),e=(u.caption&&(e=0===e.length?V("<caption/>").appendTo(o):e).html(u.caption),e.length&&(e[0]._captionSide=e.css("caption-side"),u.captionNode=e[0]),0===h.length&&(h=V("<thead/>").appendTo(o)),u.nTHead=h[0],V("tr",h).addClass(c.thead.row),o.children("tbody")),e=(0===e.length&&(e=V("<tbody/>").insertAfter(h)),u.nTBody=e[0],o.children("tfoot"));if(0===e.length&&(e=V("<tfoot/>").appendTo(o)),u.nTFoot=e[0],V("tr",e).addClass(c.tfoot.row),n.aaData)for(a=0;a<n.aaData.length;a++)Y(u,n.aaData[a]);else"dom"==et(u)&&ut(u,V(u.nTBody).children("tr"));u.aiDisplay=u.aiDisplayMaster.slice(),!(u.bInitialised=!0)===r&&Et(u)};K(u,"aoDrawCallback",Gt),n.bStateSave?(R.bStateSave=!0,N=O,(F=u).oFeatures.bStateSave?void 0!==(j=F.fnStateLoadCallback.call(F.oInstance,F,function(t){Jt(F,t,N)}))&&Jt(F,j,N):N()):O()}}),W=null,this)},c=($.ext=C={buttons:{},classes:{},builder:"-source-",errMode:"alert",feature:[],features:{},search:[],selector:{cell:[],column:[],row:[]},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{className:{},detect:[],render:{},search:{},order:{}},_unique:0,fnVersionCheck:$.fnVersionCheck,iApiIndex:0,sVersion:$.version},V.extend(C,{afnFiltering:C.search,aTypes:C.type.detect,ofnSearch:C.type.search,oSort:C.type.order,afnSortData:C.order,aoFeatures:C.feature,oStdClasses:C.classes,oPagination:C.pager}),V.extend($.ext.classes,{container:"dt-container",empty:{row:"dt-empty"},info:{container:"dt-info"},length:{container:"dt-length",select:"dt-input"},order:{canAsc:"dt-orderable-asc",canDesc:"dt-orderable-desc",isAsc:"dt-ordering-asc",isDesc:"dt-ordering-desc",none:"dt-orderable-none",position:"sorting_"},processing:{container:"dt-processing"},scrolling:{body:"dt-scroll-body",container:"dt-scroll",footer:{self:"dt-scroll-foot",inner:"dt-scroll-footInner"},header:{self:"dt-scroll-head",inner:"dt-scroll-headInner"}},search:{container:"dt-search",input:"dt-input"},table:"dataTable",tbody:{cell:"",row:""},thead:{cell:"",row:""},tfoot:{cell:"",row:""},paging:{active:"current",button:"dt-paging-button",container:"dt-paging",disabled:"disabled"}}),{}),d=/[\r\n\u2028]/g,L=/<([^>]*>)/g,F=Math.pow(2,28),N=/^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/,j=new RegExp("(\\"+["/",".","*","+","?","|","(",")","[","]","{","}","\\","$","^","-"].join("|\\")+")","g"),P=/['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi,y=function(t){return!t||!0===t||"-"===t},R=function(t,e){return c[e]||(c[e]=new RegExp(Pt(e),"g")),"string"==typeof t&&"."!==e?t.replace(/\./g,"").replace(c[e],"."):t},f=function(t,e,n){var a=[],r=0,o=t.length;if(void 0!==n)for(;r<o;r++)t[r]&&t[r][e]&&a.push(t[r][e][n]);else for(;r<o;r++)t[r]&&a.push(t[r][e]);return a},I=function(t){if(t.length>F)throw new Error("Exceeded max str len");var e;for(t=t.replace(L,"");(t=(e=t).replace(/<script/i,""))!==e;);return e},u=function(t){return"string"==typeof(t=Array.isArray(t)?t.join(","):t)?t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):t},O=function(t,e){var n;return"string"!=typeof t?t:(n=t.normalize("NFD")).length!==t.length?(!0===e?t+" ":"")+n.replace(/[\u0300-\u036f]/g,""):n},x=function(t){if(Array.from&&Set)return Array.from(new Set(t));if(function(t){if(!(t.length<2))for(var e=t.slice().sort(),n=e[0],a=1,r=e.length;a<r;a++){if(e[a]===n)return!1;n=e[a]}return!0}(t))return t.slice();var e,n,a,r=[],o=t.length,i=0;t:for(n=0;n<o;n++){for(e=t[n],a=0;a<i;a++)if(r[a]===e)continue t;r.push(e),i++}return r},E=function(t,e){if(Array.isArray(e))for(var n=0;n<e.length;n++)E(t,e[n]);else t.push(e);return t};function D(e,t){t&&t.split(" ").forEach(function(t){t&&e.classList.add(t)})}function k(e){var n,a,r={};V.each(e,function(t){(n=t.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(n[1]+" ")&&(a=t.replace(n[0],n[2].toLowerCase()),r[a]=t,"o"===n[1])&&k(e[t])}),e._hungarianMap=r}function z(e,n,a){var r;e._hungarianMap||k(e),V.each(n,function(t){void 0===(r=e._hungarianMap[t])||!a&&void 0!==n[r]||("o"===r.charAt(0)?(n[r]||(n[r]={}),V.extend(!0,n[r],n[t]),z(e[r],n[r],a)):n[r]=n[t])})}$.util={diacritics:function(t,e){if("function"!=typeof t)return O(t,e);O=t},debounce:function(n,a){var r;return function(){var t=this,e=arguments;clearTimeout(r),r=setTimeout(function(){n.apply(t,e)},a||250)}},throttle:function(a,t){var r,o,i=void 0!==t?t:200;return function(){var t=this,e=+new Date,n=arguments;r&&e<r+i?(clearTimeout(o),o=setTimeout(function(){r=void 0,a.apply(t,n)},i)):(r=e,a.apply(t,n))}},escapeRegex:function(t){return t.replace(j,"\\$1")},set:function(a){var f;return V.isPlainObject(a)?$.util.set(a._):null===a?function(){}:"function"==typeof a?function(t,e,n){a(t,"set",e,n)}:"string"!=typeof a||-1===a.indexOf(".")&&-1===a.indexOf("[")&&-1===a.indexOf("(")?function(t,e){t[a]=e}:(f=function(t,e,n){for(var a,r,o,i,l=ft(n),n=l[l.length-1],s=0,u=l.length-1;s<u;s++){if("__proto__"===l[s]||"constructor"===l[s])throw new Error("Cannot set prototype values");if(a=l[s].match(dt),r=l[s].match(p),a){if(l[s]=l[s].replace(dt,""),t[l[s]]=[],(a=l.slice()).splice(0,s+1),i=a.join("."),Array.isArray(e))for(var c=0,d=e.length;c<d;c++)f(o={},e[c],i),t[l[s]].push(o);else t[l[s]]=e;return}r&&(l[s]=l[s].replace(p,""),t=t[l[s]](e)),null!==t[l[s]]&&void 0!==t[l[s]]||(t[l[s]]={}),t=t[l[s]]}n.match(p)?t[n.replace(p,"")](e):t[n.replace(dt,"")]=e},function(t,e){return f(t,e,a)})},get:function(r){var o,f;return V.isPlainObject(r)?(o={},V.each(r,function(t,e){e&&(o[t]=$.util.get(e))}),function(t,e,n,a){var r=o[e]||o._;return void 0!==r?r(t,e,n,a):t}):null===r?function(t){return t}:"function"==typeof r?function(t,e,n,a){return r(t,e,n,a)}:"string"!=typeof r||-1===r.indexOf(".")&&-1===r.indexOf("[")&&-1===r.indexOf("(")?function(t){return t[r]}:(f=function(t,e,n){var a,r,o;if(""!==n)for(var i=ft(n),l=0,s=i.length;l<s;l++){if(d=i[l].match(dt),a=i[l].match(p),d){if(i[l]=i[l].replace(dt,""),""!==i[l]&&(t=t[i[l]]),r=[],i.splice(0,l+1),o=i.join("."),Array.isArray(t))for(var u=0,c=t.length;u<c;u++)r.push(f(t[u],e,o));var d=d[0].substring(1,d[0].length-1);t=""===d?r:r.join(d);break}if(a)i[l]=i[l].replace(p,""),t=t[i[l]]();else{if(null===t||null===t[i[l]])return null;if(void 0===t||void 0===t[i[l]])return;t=t[i[l]]}}return t},function(t,e){return f(t,e,r)})},stripHtml:function(t){var e=typeof t;if("function"!=e)return"string"==e?I(t):t;I=t},escapeHtml:function(t){var e=typeof t;if("function"!=e)return"string"==e||Array.isArray(t)?u(t):t;u=t},unique:x};var r=function(t,e,n){void 0!==t[e]&&(t[n]=t[e])};function nt(t){r(t,"ordering","bSort"),r(t,"orderMulti","bSortMulti"),r(t,"orderClasses","bSortClasses"),r(t,"orderCellsTop","bSortCellsTop"),r(t,"order","aaSorting"),r(t,"orderFixed","aaSortingFixed"),r(t,"paging","bPaginate"),r(t,"pagingType","sPaginationType"),r(t,"pageLength","iDisplayLength"),r(t,"searching","bFilter"),"boolean"==typeof t.sScrollX&&(t.sScrollX=t.sScrollX?"100%":""),"boolean"==typeof t.scrollX&&(t.scrollX=t.scrollX?"100%":"");var e=t.aoSearchCols;if(e)for(var n=0,a=e.length;n<a;n++)e[n]&&z($.models.oSearch,e[n]);t.serverSide&&!t.searchDelay&&(t.searchDelay=400)}function at(t){r(t,"orderable","bSortable"),r(t,"orderData","aDataSort"),r(t,"orderSequence","asSorting"),r(t,"orderDataType","sortDataType");var e=t.aDataSort;"number"!=typeof e||Array.isArray(e)||(t.aDataSort=[e])}function rt(t){var e=$.defaults.column,n=t.aoColumns.length,e=V.extend({},$.models.oColumn,e,{aDataSort:e.aDataSort||[n],mData:e.mData||n,idx:n,searchFixed:{},colEl:V("<col>").attr("data-dt-column",n)}),e=(t.aoColumns.push(e),t.aoPreSearchCols);e[n]=V.extend({},$.models.oSearch,e[n])}function ot(t,e,n){function a(t){return"string"==typeof t&&-1!==t.indexOf("@")}var r=t.aoColumns[e],o=(null!=n&&(at(n),z($.defaults.column,n,!0),void 0===n.mDataProp||n.mData||(n.mData=n.mDataProp),n.sType&&(r._sManualType=n.sType),n.className&&!n.sClass&&(n.sClass=n.className),e=r.sClass,V.extend(r,n),Q(r,n,"sWidth","sWidthOrig"),e!==r.sClass&&(r.sClass=e+" "+r.sClass),void 0!==n.iDataSort&&(r.aDataSort=[n.iDataSort]),Q(r,n,"aDataSort")),r.mData),i=J(o);r.mRender&&Array.isArray(r.mRender)&&(n=(e=r.mRender.slice()).shift(),r.mRender=$.render[n].apply(q,e)),r._render=r.mRender?J(r.mRender):null;r._bAttrSrc=V.isPlainObject(o)&&(a(o.sort)||a(o.type)||a(o.filter)),r._setter=null,r.fnGetData=function(t,e,n){var a=i(t,e,void 0,n);return r._render&&e?r._render(a,e,t,n):a},r.fnSetData=function(t,e,n){return m(o)(t,e,n)},"number"==typeof o||r._isArrayHost||(t._rowReadObject=!0),t.oFeatures.bSort||(r.bSortable=!1)}function M(t){var e=t;if(e.oFeatures.bAutoWidth){var n,a,r=e.nTable,o=e.aoColumns,i=e.oScroll,l=i.sY,s=i.sX,i=i.sXInner,u=X(e,"bVisible"),c=r.getAttribute("width"),d=r.parentNode,f=r.style.width,f=(f&&-1!==f.indexOf("%")&&(c=f),tt(e,null,"column-calc",{visible:u},!1),V(r.cloneNode()).css("visibility","hidden").removeAttr("id")),h=(f.append("<tbody>"),V("<tr/>").appendTo(f.find("tbody")));for(f.append(V(e.nTHead).clone()).append(V(e.nTFoot).clone()),f.find("tfoot th, tfoot td").css("width",""),f.find("thead th, thead td").each(function(){var t=lt(e,this,!0,!1);t?(this.style.width=t,s&&V(this).append(V("<div/>").css({width:t,margin:0,padding:0,border:0,height:1}))):this.style.width=""}),n=0;n<u.length;n++){p=u[n],a=o[p];var p=function(t,e){var n=t.aoColumns[e];if(!n.maxLenString){for(var a,r="",o=-1,i=0,l=t.aiDisplayMaster.length;i<l;i++){var s=t.aiDisplayMaster[i],s=vt(t,s)[e],s=s&&"object"==typeof s&&s.nodeType?s.innerHTML:s+"";s=s.replace(/id=".*?"/g,"").replace(/name=".*?"/g,""),(a=I(s).replace(/ /g," ")).length>o&&(r=s,o=a.length)}n.maxLenString=r}return n.maxLenString}(e,p),g=C.type.className[a.sType],m=p+a.sContentPadding,p=-1===p.indexOf("<")?_.createTextNode(m):m;V("<td/>").addClass(g).addClass(a.sClass).append(p).appendTo(h)}V("[name]",f).removeAttr("name");var v=V("<div/>").css(s||l?{position:"absolute",top:0,left:0,height:1,right:0,overflow:"hidden"}:{}).append(f).appendTo(d),b=(s&&i?f.width(i):s?(f.css("width","auto"),f.removeAttr("width"),f.width()<d.clientWidth&&c&&f.width(d.clientWidth)):l?f.width(d.clientWidth):c&&f.width(c),0),y=f.find("tbody tr").eq(0).children();for(n=0;n<u.length;n++){var D=y[n].getBoundingClientRect().width;b+=D,o[u[n]].sWidth=A(D)}r.style.width=A(b),v.remove(),c&&(r.style.width=A(c)),!c&&!s||e._reszEvt||(V(q).on("resize.DT-"+e.sInstance,$.util.throttle(function(){e.bDestroying||M(e)})),e._reszEvt=!0)}for(var x=t,S=x.aoColumns,T=0;T<S.length;T++){var w=lt(x,[T],!1,!1);S[T].colEl.css("width",w)}i=t.oScroll;""===i.sY&&""===i.sX||Xt(t),tt(t,null,"column-sizing",[t])}function H(t,e){t=X(t,"bVisible");return"number"==typeof t[e]?t[e]:null}function T(t,e){t=X(t,"bVisible").indexOf(e);return-1!==t?t:null}function W(t){var e=t.aoHeader,n=t.aoColumns,a=0;if(e.length)for(var r=0,o=e[0].length;r<o;r++)n[r].bVisible&&"none"!==V(e[0][r].cell).css("display")&&a++;return a}function X(t,n){var a=[];return t.aoColumns.map(function(t,e){t[n]&&a.push(e)}),a}function B(t){for(var e,n,a,r,o,i,l,s=t.aoColumns,u=t.aoData,c=$.ext.type.detect,d=0,f=s.length;d<f;d++){if(l=[],!(o=s[d]).sType&&o._sManualType)o.sType=o._sManualType;else if(!o.sType){for(e=0,n=c.length;e<n;e++){for(a=0,r=u.length;a<r;a++)if(u[a]){if(void 0===l[a]&&(l[a]=G(t,a,d,"type")),!(i=c[e](l[a],t))&&e!==c.length-2)break;if("html"===i&&!y(l[a]))break}if(i){o.sType=i;break}}o.sType||(o.sType="string")}var h=C.type.className[o.sType],h=(h&&(it(t.aoHeader,d,h),it(t.aoFooter,d,h)),C.type.render[o.sType]);if(h&&!o._render){o._render=$.util.get(h),p=b=v=m=g=void 0;for(var p,g=t,m=d,v=g.aoData,b=0;b<v.length;b++)v[b].nTr&&(p=G(g,b,m,"display"),v[b].displayData[m]=p,ct(v[b].anCells[m],p))}}}function it(t,e,n){t.forEach(function(t){t[e]&&t[e].unique&&D(t[e].cell,n)})}function lt(t,e,n,a){Array.isArray(e)||(e=st(e));for(var r,o=0,i=t.aoColumns,l=0,s=e.length;l<s;l++){var u=i[e[l]],c=n?u.sWidthOrig:u.sWidth;if(a||!1!==u.bVisible){if(null==c)return null;"number"==typeof c?(r="px",o+=c):(u=c.match(/([\d\.]+)([^\d]*)/))&&(o+=+u[1],r=3===u.length?u[2]:"px")}}return o+r}function st(t){t=V(t).closest("[data-dt-column]").attr("data-dt-column");return t?t.split(",").map(function(t){return+t}):[]}function Y(t,e,n,a){for(var r=t.aoData.length,o=V.extend(!0,{},$.models.oRow,{src:n?"dom":"data",idx:r}),i=(o._aData=e,t.aoData.push(o),t.aoColumns),l=0,s=i.length;l<s;l++)i[l].sType=null;t.aiDisplayMaster.push(r);e=t.rowIdFn(e);return void 0!==e&&(t.aIds[e]=o),!n&&t.oFeatures.bDeferRender||bt(t,r,n,a),r}function ut(n,t){var a;return(t=t instanceof V?t:V(t)).map(function(t,e){return a=mt(n,e),Y(n,a.data,e,a.cells)})}function G(t,e,n,a){"search"===a?a="filter":"order"===a&&(a="sort");var r=t.aoData[e];if(r){var o=t.iDraw,i=t.aoColumns[n],r=r._aData,l=i.sDefaultContent,s=i.fnGetData(r,a,{settings:t,row:e,col:n});if(void 0===(s="display"!==a&&s&&"object"==typeof s&&s.nodeName?s.innerHTML:s))return t.iDrawError!=o&&null===l&&(Z(t,0,"Requested unknown parameter "+("function"==typeof i.mData?"{function}":"'"+i.mData+"'")+" for row "+e+", column "+n,4),t.iDrawError=o),l;if(s!==r&&null!==s||null===l||void 0===a){if("function"==typeof s)return s.call(r)}else s=l;return null===s&&"display"===a?"":s="filter"===a&&(e=$.ext.type.search)[i.sType]?e[i.sType](s):s}}function ct(t,e){e&&"object"==typeof e&&e.nodeName?V(t).empty().append(e):t.innerHTML=e}var dt=/\[.*?\]$/,p=/\(\)$/;function ft(t){return(t.match(/(\\.|[^.])+/g)||[""]).map(function(t){return t.replace(/\\\./g,".")})}var J=$.util.get,m=$.util.set;function ht(t){return f(t.aoData,"_aData")}function pt(t){t.aoData.length=0,t.aiDisplayMaster.length=0,t.aiDisplay.length=0,t.aIds={}}function gt(t,e,n,a){var r,o,i=t.aoData[e];if(i._aSortData=null,i._aFilterData=null,i.displayData=null,"dom"!==n&&(n&&"auto"!==n||"dom"!==i.src)){var l=i.anCells,s=vt(t,e);if(l)if(void 0!==a)ct(l[a],s[a]);else for(r=0,o=l.length;r<o;r++)ct(l[r],s[r])}else i._aData=mt(t,i,a,void 0===a?void 0:i._aData).data;var u=t.aoColumns;if(void 0!==a)u[a].sType=null,u[a].maxLenString=null;else{for(r=0,o=u.length;r<o;r++)u[r].sType=null,u[r].maxLenString=null;yt(t,i)}}function mt(t,e,n,a){function r(t,e){var n;"string"==typeof t&&-1!==(n=t.indexOf("@"))&&(n=t.substring(n+1),m(t)(a,e.getAttribute(n)))}function o(t){void 0!==n&&n!==d||(l=f[d],s=t.innerHTML.trim(),l&&l._bAttrSrc?(m(l.mData._)(a,s),r(l.mData.sort,t),r(l.mData.type,t),r(l.mData.filter,t)):h?(l._setter||(l._setter=m(l.mData)),l._setter(a,s)):a[d]=s),d++}var i,l,s,u=[],c=e.firstChild,d=0,f=t.aoColumns,h=t._rowReadObject;a=void 0!==a?a:h?{}:[];if(c)for(;c;)"TD"!=(i=c.nodeName.toUpperCase())&&"TH"!=i||(o(c),u.push(c)),c=c.nextSibling;else for(var p=0,g=(u=e.anCells).length;p<g;p++)o(u[p]);var e=e.firstChild?e:e.nTr;return e&&(e=e.getAttribute("id"))&&m(t.rowId)(a,e),{data:a,cells:u}}function vt(t,e){var n=t.aoData[e],a=t.aoColumns;if(!n.displayData){n.displayData=[];for(var r=0,o=a.length;r<o;r++)n.displayData.push(G(t,e,r,"display"))}return n.displayData}function bt(t,e,n,a){var r,o,i,l,s,u,c=t.aoData[e],d=c._aData,f=[],h=t.oClasses.tbody.row;if(null===c.nTr){for(r=n||_.createElement("tr"),c.nTr=r,c.anCells=f,D(r,h),r._DT_RowIndex=e,yt(t,c),l=0,s=t.aoColumns.length;l<s;l++){i=t.aoColumns[l],(o=(u=!n||!a[l])?_.createElement(i.sCellType):a[l])||Z(t,0,"Incorrect column count",18),o._DT_CellIndex={row:e,column:l},f.push(o);var p=vt(t,e);!u&&(!i.mRender&&i.mData===l||V.isPlainObject(i.mData)&&i.mData._===l+".display")||ct(o,p[l]),i.bVisible&&u?r.appendChild(o):i.bVisible||u||o.parentNode.removeChild(o),i.fnCreatedCell&&i.fnCreatedCell.call(t.oInstance,o,G(t,e,l),d,e,l)}tt(t,"aoRowCreatedCallback","row-created",[r,d,e,f])}else D(c.nTr,h)}function yt(t,e){var n=e.nTr,a=e._aData;n&&((t=t.rowIdFn(a))&&(n.id=t),a.DT_RowClass&&(t=a.DT_RowClass.split(" "),e.__rowc=e.__rowc?x(e.__rowc.concat(t)):t,V(n).removeClass(e.__rowc.join(" ")).addClass(a.DT_RowClass)),a.DT_RowAttr&&V(n).attr(a.DT_RowAttr),a.DT_RowData)&&V(n).data(a.DT_RowData)}function Dt(t,e){var n,a=t.oClasses,r=t.aoColumns,o="header"===e?t.nTHead:t.nTFoot,i="header"===e?"sTitle":e;if(o){if(("header"===e||f(t.aoColumns,i).join(""))&&1===(n=(n=V("tr",o)).length?n:V("<tr/>").appendTo(o)).length)for(var l=V("td, th",n).length,s=r.length;l<s;l++)V("<th/>").html(r[l][i]||"").appendTo(n);var u=It(t,o,!0);"header"===e?t.aoHeader=u:t.aoFooter=u,V(o).children("tr").attr("role","row"),V(o).children("tr").children("th, td").each(function(){te(t,e)(t,V(this),a)})}}function xt(t,e,n){var a,r,o,i,l,s=[],u=[],c=t.aoColumns,t=c.length;if(e){for(n=n||h(t).filter(function(t){return c[t].bVisible}),a=0;a<e.length;a++)s[a]=e[a].slice().filter(function(t,e){return n.includes(e)}),u.push([]);for(a=0;a<s.length;a++)for(r=0;r<s[a].length;r++)if(l=i=1,void 0===u[a][r]){for(o=s[a][r].cell;void 0!==s[a+i]&&s[a][r].cell==s[a+i][r].cell;)u[a+i][r]=null,i++;for(;void 0!==s[a][r+l]&&s[a][r].cell==s[a][r+l].cell;){for(var d=0;d<i;d++)u[a+d][r+l]=null;l++}var f=V("span.dt-column-title",o);u[a][r]={cell:o,colspan:l,rowspan:i,title:(f.length?f:V(o)).html()}}return u}}function St(t,e){for(var n,a,r=xt(t,e),o=0;o<e.length;o++){if(n=e[o].row)for(;a=n.firstChild;)n.removeChild(a);for(var i=0;i<r[o].length;i++){var l=r[o][i];l&&V(l.cell).appendTo(n).attr("rowspan",l.rowspan).attr("colspan",l.colspan)}}}function S(t,e){if(r="ssp"==et(s=t),void 0!==(i=s.iInitDisplayStart)&&-1!==i&&(s._iDisplayStart=!r&&i>=s.fnRecordsDisplay()?0:i,s.iInitDisplayStart=-1),-1!==tt(t,"aoPreDrawCallback","preDraw",[t]).indexOf(!1))w(t,!1);else{var l,n=[],a=0,r="ssp"==et(t),o=t.aiDisplay,i=t._iDisplayStart,s=t.fnDisplayEnd(),u=t.aoColumns,c=V(t.nTBody);if(t.bDrawing=!0,r){if(!t.bDestroying&&!e)return 0===t.iDraw&&c.empty().append(Tt(t)),(l=t).iDraw++,w(l,!0),void At(l,function(e){function n(t,e){return"function"==typeof a[t][e]?"function":a[t][e]}var a=e.aoColumns,t=e.oFeatures,r=e.oPreviousSearch,o=e.aoPreSearchCols;return{draw:e.iDraw,columns:a.map(function(e,t){return{data:n(t,"mData"),name:e.sName,searchable:e.bSearchable,orderable:e.bSortable,search:{value:o[t].search,regex:o[t].regex,fixed:Object.keys(e.searchFixed).map(function(t){return{name:t,term:e.searchFixed[t].toString()}})}}}),order:$t(e).map(function(t){return{column:t.col,dir:t.dir,name:n(t.col,"sName")}}),start:e._iDisplayStart,length:t.bPaginate?e._iDisplayLength:-1,search:{value:r.search,regex:r.regex,fixed:Object.keys(e.searchFixed).map(function(t){return{name:t,term:e.searchFixed[t].toString()}})}}}(l),function(t){var e=l,n=Lt(e,t=t),a=Ft(e,"draw",t),r=Ft(e,"recordsTotal",t),t=Ft(e,"recordsFiltered",t);if(void 0!==a){if(+a<e.iDraw)return;e.iDraw=+a}n=n||[],pt(e),e._iRecordsTotal=parseInt(r,10),e._iRecordsDisplay=parseInt(t,10);for(var o=0,i=n.length;o<i;o++)Y(e,n[o]);e.aiDisplay=e.aiDisplayMaster.slice(),S(e,!0),kt(e),w(e,!1)})}else t.iDraw++;if(0!==o.length)for(var d=r?t.aoData.length:s,f=r?0:i;f<d;f++){for(var h=o[f],p=t.aoData[h],g=(null===p.nTr&&bt(t,h),p.nTr),m=0;m<u.length;m++){var v=u[m],b=p.anCells[m];D(b,C.type.className[v.sType]),D(b,v.sClass),D(b,t.oClasses.tbody.cell)}tt(t,"aoRowCallback",null,[g,p._aData,a,f,h]),n.push(g),a++}else n[0]=Tt(t);tt(t,"aoHeaderCallback","header",[V(t.nTHead).children("tr")[0],ht(t),i,s,o]),tt(t,"aoFooterCallback","footer",[V(t.nTFoot).children("tr")[0],ht(t),i,s,o]),c[0].replaceChildren?c[0].replaceChildren.apply(c[0],n):(c.children().detach(),c.append(V(n))),V(t.nTableWrapper).toggleClass("dt-empty-footer",0===V("tr",t.nTFoot).length),tt(t,"aoDrawCallback","draw",[t],!0),t.bSorted=!1,t.bFiltered=!1,t.bDrawing=!1}}function s(t,e,n){var a=t.oFeatures,r=a.bSort,a=a.bFilter;void 0!==n&&!0!==n||(r&&zt(t),a?Nt(t,t.oPreviousSearch):t.aiDisplay=t.aiDisplayMaster.slice()),!0!==e&&(t._iDisplayStart=0),t._drawHold=e,S(t),t._drawHold=!1}function Tt(t){var e=t.oLanguage,n=e.sZeroRecords,a=et(t);return t.iDraw<1&&"ssp"===a||t.iDraw<=1&&"ajax"===a?n=e.sLoadingRecords:e.sEmptyTable&&0===t.fnRecordsTotal()&&(n=e.sEmptyTable),V("<tr/>").append(V("<td />",{colSpan:W(t),class:t.oClasses.empty.row}).html(n))[0]}function wt(t,e,n){for(var i={},a=(V.each(e,function(t,e){if(null!==e){var t=t.replace(/([A-Z])/g," $1").split(" "),n=(i[t[0]]||(i[t[0]]={}),1===t.length?"full":t[1].toLowerCase()),a=i[t[0]],r=function(e,n){V.isPlainObject(n)?Object.keys(n).map(function(t){e.push({feature:t,opts:n[t]})}):e.push(n)};if(a[n]&&a[n].contents||(a[n]={contents:[]}),Array.isArray(e))for(var o=0;o<e.length;o++)r(a[n].contents,e[o]);else r(a[n].contents,e);Array.isArray(a[n].contents)||(a[n].contents=[a[n].contents])}}),Object.keys(i).map(function(t){return 0!==t.indexOf(n)?null:{name:t,val:i[t]}}).filter(function(t){return null!==t})),r=(a.sort(function(t,e){t=+t.name.replace(/[^0-9]/g,"");return+e.name.replace(/[^0-9]/g,"")-t}),"bottom"===n&&a.reverse(),[]),o=0,l=a.length;o<l;o++)a[o].val.full&&(r.push({full:a[o].val.full}),_t(t,r[r.length-1]),delete a[o].val.full),Object.keys(a[o].val).length&&(r.push(a[o].val),_t(t,r[r.length-1]));return r}function _t(o,i){function l(t,e){return C.features[t]||Z(o,0,"Unknown feature: "+t),C.features[t].apply(this,[o,e])}V.each(i,function(t){for(var e,n=i[t].contents,a=0,r=n.length;a<r;a++)n[a]&&("string"==typeof n[a]?n[a]=l(n[a],null):V.isPlainObject(n[a])?n[a]=l(n[a].feature,n[a].opts):"function"==typeof n[a].node?n[a]=n[a].node(o):"function"==typeof n[a]&&(e=n[a](o),n[a]="function"==typeof e.node?e.node():e))})}function Ct(e){var a,t=e.oClasses,n=V(e.nTable),r=V("<div/>").attr({id:e.sTableId+"_wrapper",class:t.container}).insertBefore(n);if(e.nTableWrapper=r[0],e.sDom)for(var o,i,l,s,u,c,d=e,t=e.sDom,f=r,h=t.match(/(".*?")|('.*?')|./g),p=0;p<h.length;p++)o=null,"<"==(i=h[p])?(l=V("<div/>"),"'"!=(s=h[p+1])[0]&&'"'!=s[0]||(s=s.replace(/['"]/g,""),u="",-1!=s.indexOf(".")?(c=s.split("."),u=c[0],c=c[1]):"#"==s[0]?u=s:c=s,l.attr("id",u.substring(1)).addClass(c),p++),f.append(l),f=l):">"==i?f=f.parent():"t"==i?o=Wt(d):$.ext.feature.forEach(function(t){i==t.cFeature&&(o=t.fnInit(d))}),o&&f.append(o);else{var n=wt(e,e.layout,"top"),t=wt(e,e.layout,"bottom"),g=te(e,"layout");n.forEach(function(t){g(e,r,t)}),g(e,r,{full:{table:!0,contents:[Wt(e)]}}),t.forEach(function(t){g(e,r,t)})}var n=e,t=n.nTable,m=""!==n.oScroll.sX||""!==n.oScroll.sY;n.oFeatures.bProcessing&&(a=V("<div/>",{id:n.sTableId+"_processing",class:n.oClasses.processing.container,role:"status"}).html(n.oLanguage.sProcessing).append("<div><div></div><div></div><div></div><div></div></div>"),m?a.prependTo(V("div.dt-scroll",n.nTableWrapper)):a.insertBefore(t),V(t).on("processing.dt.DT",function(t,e,n){a.css("display",n?"block":"none")}))}function It(t,e,n){for(var a,r,o,i,l,s,u=t.aoColumns,c=V(e).children("tr"),d=e&&"thead"===e.nodeName.toLowerCase(),f=[],h=0,p=c.length;h<p;h++)f.push([]);for(h=0,p=c.length;h<p;h++)for(r=(a=c[h]).firstChild;r;){if("TD"==r.nodeName.toUpperCase()||"TH"==r.nodeName.toUpperCase()){var g,m,v,b,y,D=[];for(b=(b=+r.getAttribute("colspan"))&&0!=b&&1!=b?b:1,y=(y=+r.getAttribute("rowspan"))&&0!=y&&1!=y?y:1,l=function(t,e,n){for(var a=t[e];a[n];)n++;return n}(f,h,0),s=1==b,n&&(s&&(ot(t,l,V(r).data()),g=u[l],m=r.getAttribute("width")||null,(v=r.style.width.match(/width:\s*(\d+[pxem%]+)/))&&(m=v[1]),g.sWidthOrig=g.sWidth||m,d?(null===g.sTitle||g.autoTitle||(r.innerHTML=g.sTitle),!g.sTitle&&s&&(g.sTitle=I(r.innerHTML),g.autoTitle=!0)):g.footer&&(r.innerHTML=g.footer),g.ariaTitle||(g.ariaTitle=V(r).attr("aria-label")||g.sTitle),g.className)&&V(r).addClass(g.className),0===V("span.dt-column-title",r).length&&V("<span>").addClass("dt-column-title").append(r.childNodes).appendTo(r),d)&&0===V("span.dt-column-order",r).length&&V("<span>").addClass("dt-column-order").appendTo(r),i=0;i<b;i++){for(o=0;o<y;o++)f[h+o][l+i]={cell:r,unique:s},f[h+o].row=a;D.push(l+i)}r.setAttribute("data-dt-column",x(D).join(","))}r=r.nextSibling}return f}function At(n,t,a){function e(t){var e=n.jqXHR?n.jqXHR.status:null;(null===t||"number"==typeof e&&204==e)&&Lt(n,t={},[]),(e=t.error||t.sError)&&Z(n,0,e),n.json=t,tt(n,null,"xhr",[n,t,n.jqXHR],!0),a(t)}var r,o=n.ajax,i=n.oInstance,l=(V.isPlainObject(o)&&o.data&&(l="function"==typeof(r=o.data)?r(t,n):r,t="function"==typeof r&&l?l:V.extend(!0,t,l),delete o.data),{url:"string"==typeof o?o:"",data:t,success:e,dataType:"json",cache:!1,type:n.sServerMethod,error:function(t,e){-1===tt(n,null,"xhr",[n,null,n.jqXHR],!0).indexOf(!0)&&("parsererror"==e?Z(n,0,"Invalid JSON response",1):4===t.readyState&&Z(n,0,"Ajax error",7)),w(n,!1)}});V.isPlainObject(o)&&V.extend(l,o),n.oAjaxData=t,tt(n,null,"preXhr",[n,t,l],!0),"function"==typeof o?n.jqXHR=o.call(i,t,e,n):""===o.url?(i={},$.util.set(o.dataSrc)(i,[]),e(i)):(n.jqXHR=V.ajax(l),r&&(o.data=r))}function Lt(t,e,n){var a="data";if(V.isPlainObject(t.ajax)&&void 0!==t.ajax.dataSrc&&("string"==typeof(t=t.ajax.dataSrc)||"function"==typeof t?a=t:void 0!==t.data&&(a=t.data)),!n)return"data"===a?e.aaData||e[a]:""!==a?J(a)(e):e;m(a)(e,n)}function Ft(t,e,n){var t=V.isPlainObject(t.ajax)?t.ajax.dataSrc:null;return t&&t[e]?J(t[e])(n):(t="","draw"===e?t="sEcho":"recordsTotal"===e?t="iTotalRecords":"recordsFiltered"===e&&(t="iTotalDisplayRecords"),void 0!==n[t]?n[t]:n[e])}function Nt(n,t){var e=n.aoPreSearchCols;if(B(n),"ssp"!=et(n)){for(var a,r,o,i,l,s=n,u=s.aoColumns,c=s.aoData,d=0;d<c.length;d++)if(c[d]&&!(l=c[d])._aFilterData){for(o=[],a=0,r=u.length;a<r;a++)u[a].bSearchable?"string"!=typeof(i=null===(i=G(s,d,a,"filter"))?"":i)&&i.toString&&(i=i.toString()):i="",i.indexOf&&-1!==i.indexOf("&")&&(Rt.innerHTML=i,i=Ot?Rt.textContent:Rt.innerText),i.replace&&(i=i.replace(/[\r\n\u2028]/g,"")),o.push(i);l._aFilterData=o,l._sFilterRow=o.join(" "),0}n.aiDisplay=n.aiDisplayMaster.slice(),jt(n.aiDisplay,n,t.search,t),V.each(n.searchFixed,function(t,e){jt(n.aiDisplay,n,e,{})});for(var f=0;f<e.length;f++){var h=e[f];jt(n.aiDisplay,n,h.search,h,f),V.each(n.aoColumns[f].searchFixed,function(t,e){jt(n.aiDisplay,n,e,{},f)})}for(var p,g,m=n,v=$.ext.search,b=m.aiDisplay,y=0,D=v.length;y<D;y++){for(var x=[],S=0,T=b.length;S<T;S++)g=b[S],p=m.aoData[g],v[y](m,p._aFilterData,g,p._aData,S)&&x.push(g);b.length=0,b.push.apply(b,x)}}n.bFiltered=!0,tt(n,null,"search",[n])}function jt(t,e,n,a,r){if(""!==n){for(var o=0,i=[],l="function"==typeof n?n:null,s=n instanceof RegExp?n:l?null:function(t,e){var a=[],e=V.extend({},{boundary:!1,caseInsensitive:!0,exact:!1,regex:!1,smart:!0},e);"string"!=typeof t&&(t=t.toString());if(t=O(t),e.exact)return new RegExp("^"+Pt(t)+"$",e.caseInsensitive?"i":"");{var n,r,o;t=e.regex?t:Pt(t),e.smart&&(n=(t.match(/!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g)||[""]).map(function(t){var e,n=!1;return"!"===t.charAt(0)&&(n=!0,t=t.substring(1)),'"'===t.charAt(0)?t=(e=t.match(/^"(.*)"$/))?e[1]:t:"“"===t.charAt(0)&&(t=(e=t.match(/^\u201C(.*)\u201D$/))?e[1]:t),n&&(1<t.length&&a.push("(?!"+t+")"),t=""),t.replace(/"/g,"")}),r=a.length?a.join(""):"",o=e.boundary?"\\b":"",t="^(?=.*?"+o+n.join(")(?=.*?"+o)+")("+r+".)*$")}return new RegExp(t,e.caseInsensitive?"i":"")}(n,a),o=0;o<t.length;o++){var u=e.aoData[t[o]],c=void 0===r?u._sFilterRow:u._aFilterData[r];(l&&l(c,u._aData,t[o],r)||s&&s.test(c))&&i.push(t[o])}for(t.length=i.length,o=0;o<i.length;o++)t[o]=i[o]}}var Pt=$.util.escapeRegex,Rt=V("<div>")[0],Ot=void 0!==Rt.textContent;function Et(n){var a,t,e,r,o,i,l=n.iInitDisplayStart;n.bInitialised?(Dt(n,"header"),Dt(n,"footer"),St(n,n.aoHeader),St(n,n.aoFooter),Ct(n),e=(t=n).nTHead,i=e.querySelectorAll("tr"),r=t.bSortCellsTop,o=':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])',!0===r?e=i[0]:!1===r&&(e=i[i.length-1]),Vt(t,e,e===t.nTHead?"tr"+o+" th"+o+", tr"+o+" td"+o:"th"+o+", td"+o),Ut(t,r=[],t.aaSorting),t.aaSorting=r,Bt(n),w(n,!0),tt(n,null,"preInit",[n],!0),s(n),"ssp"!=(i=et(n))&&("ajax"==i?At(n,{},function(t){var e=Lt(n,t);for(a=0;a<e.length;a++)Y(n,e[a]);n.iInitDisplayStart=l,s(n),w(n,!1),kt(n)}):(kt(n),w(n,!1)))):setTimeout(function(){Et(n)},200)}function kt(t){var e;t._bInitComplete||(e=[t,t.json],t._bInitComplete=!0,M(t),tt(t,null,"plugin-init",e,!0),tt(t,"aoInitComplete","init",e,!0))}function Mt(t,e){e=parseInt(e,10);t._iDisplayLength=e,Kt(t),tt(t,null,"length",[t,e])}function Ht(t,e,n){var a=t._iDisplayStart,r=t._iDisplayLength,o=t.fnRecordsDisplay();if(0===o||-1===r)a=0;else if("number"==typeof e)o<(a=e*r)&&(a=0);else if("first"==e)a=0;else if("previous"==e)(a=0<=r?a-r:0)<0&&(a=0);else if("next"==e)a+r<o&&(a+=r);else if("last"==e)a=Math.floor((o-1)/r)*r;else{if("ellipsis"===e)return;Z(t,0,"Unknown paging action: "+e,5)}o=t._iDisplayStart!==a;t._iDisplayStart=a,tt(t,null,o?"page":"page-nc",[t]),o&&n&&S(t)}function w(t,e){tt(t,null,"processing",[t,e])}function Wt(t){var e,n,a,r,o,i,l,s,u,c,d,f,h,p=V(t.nTable),g=t.oScroll;return""===g.sX&&""===g.sY?t.nTable:(e=g.sX,n=g.sY,a=t.oClasses.scrolling,o=(r=t.captionNode)?r._captionSide:null,u=V(p[0].cloneNode(!1)),i=V(p[0].cloneNode(!1)),c=function(t){return t?A(t):null},(l=p.children("tfoot")).length||(l=null),u=V(s="<div/>",{class:a.container}).append(V(s,{class:a.header.self}).css({overflow:"hidden",position:"relative",border:0,width:e?c(e):"100%"}).append(V(s,{class:a.header.inner}).css({"box-sizing":"content-box",width:g.sXInner||"100%"}).append(u.removeAttr("id").css("margin-left",0).append("top"===o?r:null).append(p.children("thead"))))).append(V(s,{class:a.body}).css({position:"relative",overflow:"auto",width:c(e)}).append(p)),l&&u.append(V(s,{class:a.footer.self}).css({overflow:"hidden",border:0,width:e?c(e):"100%"}).append(V(s,{class:a.footer.inner}).append(i.removeAttr("id").css("margin-left",0).append("bottom"===o?r:null).append(p.children("tfoot"))))),c=u.children(),d=c[0],f=c[1],h=l?c[2]:null,V(f).on("scroll.DT",function(){var t=this.scrollLeft;d.scrollLeft=t,l&&(h.scrollLeft=t)}),V("th, td",d).on("focus",function(){var t=d.scrollLeft;f.scrollLeft=t,l&&(f.scrollLeft=t)}),V(f).css("max-height",n),g.bCollapse||V(f).css("height",n),t.nScrollHead=d,t.nScrollBody=f,t.nScrollFoot=h,t.aoDrawCallback.push(Xt),u[0])}function Xt(e){var t=e.oScroll.iBarWidth,n=V(e.nScrollHead).children("div"),a=n.children("table"),r=e.nScrollBody,o=V(r),i=V(e.nScrollFoot).children("div"),l=i.children("table"),s=V(e.nTHead),u=V(e.nTable),c=e.nTFoot&&V("th, td",e.nTFoot).length?V(e.nTFoot):null,d=e.oBrowser,f=r.scrollHeight>r.clientHeight;if(e.scrollBarVis!==f&&void 0!==e.scrollBarVis)e.scrollBarVis=f,M(e);else{if(e.scrollBarVis=f,u.children("thead, tfoot").remove(),(f=s.clone().prependTo(u)).find("th, td").removeAttr("tabindex"),f.find("[id]").removeAttr("id"),c&&(m=c.clone().prependTo(u)).find("[id]").removeAttr("id"),e.aiDisplay.length)for(var h=u.children("tbody").eq(0).children("tr").eq(0).children("th, td").map(function(t){return{idx:H(e,t),width:V(this).outerWidth()}}),p=0;p<h.length;p++){var g=e.aoColumns[h[p].idx].colEl[0];g.style.width.replace("px","")!==h[p].width&&(g.style.width=h[p].width+"px")}a.find("colgroup").remove(),a.append(e.colgroup.clone()),c&&(l.find("colgroup").remove(),l.append(e.colgroup.clone())),V("th, td",f).each(function(){V(this.childNodes).wrapAll('<div class="dt-scroll-sizing">')}),c&&V("th, td",m).each(function(){V(this.childNodes).wrapAll('<div class="dt-scroll-sizing">')});var s=Math.floor(u.height())>r.clientHeight||"scroll"==o.css("overflow-y"),f="padding"+(d.bScrollbarLeft?"Left":"Right"),m=u.outerWidth();a.css("width",A(m)),n.css("width",A(m)).css(f,s?t+"px":"0px"),c&&(l.css("width",A(m)),i.css("width",A(m)).css(f,s?t+"px":"0px")),u.children("colgroup").prependTo(u),o.trigger("scroll"),!e.bSorted&&!e.bFiltered||e._drawHold||(r.scrollTop=0)}}function A(t){return null===t?"0px":"number"==typeof t?t<0?"0px":t+"px":t.match(/\d$/)?t+"px":t}function Bt(t){var e=t.aoColumns;for(t.colgroup.empty(),a=0;a<e.length;a++)e[a].bVisible&&t.colgroup.append(e[a].colEl)}function Vt(o,t,e,i,l){Qt(t,e,function(t){var e=!1,n=void 0===i?st(t.target):[i];if(n.length){for(var a=0,r=n.length;a<r;a++)if(!1!==function(t,e,n,a){function r(t,e){var n=t._idx;return(n=void 0===n?s.indexOf(t[1]):n)+1<s.length?n+1:e?null:0}var o,i=t.aoColumns[e],l=t.aaSorting,s=i.asSorting;if(!i.bSortable)return!1;"number"==typeof l[0]&&(l=t.aaSorting=[l]);(a||n)&&t.oFeatures.bSortMulti?-1!==(i=f(l,"0").indexOf(e))?null===(o=null===(o=r(l[i],!0))&&1===l.length?0:o)?l.splice(i,1):(l[i][1]=s[o],l[i]._idx=o):(a?l.push([e,s[0],0]):l.push([e,l[0][1],0]),l[l.length-1]._idx=0):l.length&&l[0][0]==e?(o=r(l[0]),l.length=1,l[0][1]=s[o],l[0]._idx=o):(l.length=0,l.push([e,s[0]]),l[0]._idx=0)}(o,n[a],a,t.shiftKey)&&(e=!0),1===o.aaSorting.length&&""===o.aaSorting[0][1])break;e&&(w(o,!0),setTimeout(function(){zt(o),qt(o,o.aiDisplay),w(o,!1),s(o,!1,!1),l&&l()},0))}})}function qt(t,e){if(!(e.length<2)){for(var n=t.aiDisplayMaster,a={},r={},o=0;o<n.length;o++)a[n[o]]=o;for(o=0;o<e.length;o++)r[e[o]]=a[e[o]];e.sort(function(t,e){return r[t]-r[e]})}}function Ut(n,a,t){function e(t){var e;V.isPlainObject(t)?void 0!==t.idx?a.push([t.idx,t.dir]):t.name&&-1!==(e=f(n.aoColumns,"sName").indexOf(t.name))&&a.push([e,t.dir]):a.push(t)}if(V.isPlainObject(t))e(t);else if(t.length&&"number"==typeof t[0])e(t);else if(t.length)for(var r=0;r<t.length;r++)e(t[r])}function $t(t){var e,n,a,r,o,i,l,s=[],u=$.ext.type.order,c=t.aoColumns,d=t.aaSortingFixed,f=V.isPlainObject(d),h=[];if(t.oFeatures.bSort)for(Array.isArray(d)&&Ut(t,h,d),f&&d.pre&&Ut(t,h,d.pre),Ut(t,h,t.aaSorting),f&&d.post&&Ut(t,h,d.post),e=0;e<h.length;e++)if(c[l=h[e][0]])for(n=0,a=(r=c[l].aDataSort).length;n<a;n++)i=c[o=r[n]].sType||"string",void 0===h[e]._idx&&(h[e]._idx=c[o].asSorting.indexOf(h[e][1])),h[e][1]&&s.push({src:l,col:o,dir:h[e][1],index:h[e]._idx,type:i,formatter:u[i+"-pre"],sorter:u[i+"-"+h[e][1]]});return s}function zt(t,e,n){var a,r,o,i,l,c,d=[],s=$.ext.type.order,f=t.aoData,u=t.aiDisplayMaster;for(B(t),void 0!==e?(l=t.aoColumns[e],c=[{src:e,col:e,dir:n,index:0,type:l.sType,formatter:s[l.sType+"-pre"],sorter:s[l.sType+"-"+n]}],u=u.slice()):c=$t(t),a=0,r=c.length;a<r;a++){i=c[a],S=x=D=g=p=h=y=b=v=m=void 0;var h,p,g,m=t,v=i.col,b=m.aoColumns[v],y=$.ext.order[b.sSortDataType];y&&(h=y.call(m.oInstance,m,v,T(m,v)));for(var D=$.ext.type.order[b.sType+"-pre"],x=m.aoData,S=0;S<x.length;S++)x[S]&&((p=x[S])._aSortData||(p._aSortData=[]),p._aSortData[v]&&!y||(g=y?h[S]:G(m,S,v,"sort"),p._aSortData[v]=D?D(g,m):g))}if("ssp"!=et(t)&&0!==c.length){for(a=0,o=u.length;a<o;a++)d[a]=a;c.length&&"desc"===c[0].dir&&d.reverse(),u.sort(function(t,e){for(var n,a,r,o,i=c.length,l=f[t]._aSortData,s=f[e]._aSortData,u=0;u<i;u++)if(n=l[(o=c[u]).col],a=s[o.col],o.sorter){if(0!==(r=o.sorter(n,a)))return r}else if(0!==(r=n<a?-1:a<n?1:0))return"asc"===o.dir?r:-r;return(n=d[t])<(a=d[e])?-1:a<n?1:0})}else 0===c.length&&u.sort(function(t,e){return t<e?-1:e<t?1:0});return void 0===e&&(t.bSorted=!0,tt(t,null,"order",[t,c])),u}function Yt(t){var e,n,a,r=t.aLastSort,o=t.oClasses.order.position,i=$t(t),l=t.oFeatures;if(l.bSort&&l.bSortClasses){for(e=0,n=r.length;e<n;e++)a=r[e].src,V(f(t.aoData,"anCells",a)).removeClass(o+(e<2?e+1:3));for(e=0,n=i.length;e<n;e++)a=i[e].src,V(f(t.aoData,"anCells",a)).addClass(o+(e<2?e+1:3))}t.aLastSort=i}function Gt(n){var t;n._bLoadingState||(t={time:+new Date,start:n._iDisplayStart,length:n._iDisplayLength,order:V.extend(!0,[],n.aaSorting),search:V.extend({},n.oPreviousSearch),columns:n.aoColumns.map(function(t,e){return{visible:t.bVisible,search:V.extend({},n.aoPreSearchCols[e])}})},n.oSavedState=t,tt(n,"aoStateSaveParams","stateSaveParams",[n,t]),n.oFeatures.bStateSave&&!n.bDestroying&&n.fnStateSaveCallback.call(n.oInstance,n,t))}function Jt(n,t,e){var a,r,o=n.aoColumns,i=(n._bLoadingState=!0,n._bInitComplete?new $.Api(n):null);if(t&&t.time){var l=n.iStateDuration;if(0<l&&t.time<+new Date-1e3*l)n._bLoadingState=!1;else if(-1!==tt(n,"aoStateLoadParams","stateLoadParams",[n,t]).indexOf(!1))n._bLoadingState=!1;else if(t.columns&&o.length!==t.columns.length)n._bLoadingState=!1;else{if(n.oLoadedState=V.extend(!0,{},t),tt(n,null,"stateLoadInit",[n,t],!0),void 0!==t.length&&(i?i.page.len(t.length):n._iDisplayLength=t.length),void 0!==t.start&&(null===i?(n._iDisplayStart=t.start,n.iInitDisplayStart=t.start):Ht(n,t.start/n._iDisplayLength)),void 0!==t.order&&(n.aaSorting=[],V.each(t.order,function(t,e){n.aaSorting.push(e[0]>=o.length?[0,e[1]]:e)})),void 0!==t.search&&V.extend(n.oPreviousSearch,t.search),t.columns){for(a=0,r=t.columns.length;a<r;a++){var s=t.columns[a];void 0!==s.visible&&(i?i.column(a).visible(s.visible,!1):o[a].bVisible=s.visible),void 0!==s.search&&V.extend(n.aoPreSearchCols[a],s.search)}i&&i.columns.adjust()}n._bLoadingState=!1,tt(n,"aoStateLoaded","stateLoaded",[n,t])}}else n._bLoadingState=!1;e()}function Z(t,e,n,a){if(n="DataTables warning: "+(t?"table id="+t.sTableId+" - ":"")+n,a&&(n+=". For more information about this error, please see https://datatables.net/tn/"+a),e)q.console&&console.log&&console.log(n);else{e=$.ext,e=e.sErrMode||e.errMode;if(t&&tt(t,null,"dt-error",[t,a,n],!0),"alert"==e)alert(n);else{if("throw"==e)throw new Error(n);"function"==typeof e&&e(t,a,n)}}}function Q(n,a,t,e){Array.isArray(t)?V.each(t,function(t,e){Array.isArray(e)?Q(n,a,e[0],e[1]):Q(n,a,e)}):(void 0===e&&(e=t),void 0!==a[t]&&(n[e]=a[t]))}function Zt(t,e,n){var a,r;for(r in e)Object.prototype.hasOwnProperty.call(e,r)&&(a=e[r],V.isPlainObject(a)?(V.isPlainObject(t[r])||(t[r]={}),V.extend(!0,t[r],a)):n&&"data"!==r&&"aaData"!==r&&Array.isArray(a)?t[r]=a.slice():t[r]=a);return t}function Qt(t,e,n){V(t).on("click.DT",e,function(t){n(t)}).on("keypress.DT",e,function(t){13===t.which&&(t.preventDefault(),n(t))}).on("selectstart.DT",e,function(){return!1})}function K(t,e,n){n&&t[e].push(n)}function tt(e,t,n,a,r){var o=[];return t&&(o=e[t].slice().reverse().map(function(t){return t.apply(e.oInstance,a)})),null!==n&&(t=V.Event(n+".dt"),n=V(e.nTable),t.dt=e.api,n[r?"trigger":"triggerHandler"](t,a),r&&0===n.parents("body").length&&V("body").trigger(t,a),o.push(t.result)),o}function Kt(t){var e=t._iDisplayStart,n=t.fnDisplayEnd(),a=t._iDisplayLength;n<=e&&(e=n-a),e-=e%a,t._iDisplayStart=e=-1===a||e<0?0:e}function te(t,e){var t=t.renderer,n=$.ext.renderer[e];return V.isPlainObject(t)&&t[e]?n[t[e]]||n._:"string"==typeof t&&n[t]||n._}function et(t){return t.oFeatures.bServerSide?"ssp":t.ajax?"ajax":"dom"}function ee(t,e,n){var a=t.fnFormatNumber,r=t._iDisplayStart+1,o=t._iDisplayLength,i=t.fnRecordsDisplay(),l=t.fnRecordsTotal(),s=-1===o;return e.replace(/_START_/g,a.call(t,r)).replace(/_END_/g,a.call(t,t.fnDisplayEnd())).replace(/_MAX_/g,a.call(t,l)).replace(/_TOTAL_/g,a.call(t,i)).replace(/_PAGE_/g,a.call(t,s?1:Math.ceil(r/o))).replace(/_PAGES_/g,a.call(t,s?1:Math.ceil(i/o))).replace(/_ENTRIES_/g,t.api.i18n("entries","",n)).replace(/_ENTRIES-MAX_/g,t.api.i18n("entries","",l)).replace(/_ENTRIES-TOTAL_/g,t.api.i18n("entries","",i))}var ne=[],n=Array.prototype;U=function(t,e){if(!(this instanceof U))return new U(t,e);function n(t){t=t,e=$.settings,a=f(e,"nTable");var n,e,a,r=t?t.nTable&&t.oFeatures?[t]:t.nodeName&&"table"===t.nodeName.toLowerCase()?-1!==(r=a.indexOf(t))?[e[r]]:null:t&&"function"==typeof t.settings?t.settings().toArray():("string"==typeof t?n=V(t).get():t instanceof V&&(n=t.get()),n?e.filter(function(t,e){return n.includes(a[e])}):void 0):[];r&&o.push.apply(o,r)}var o=[];if(Array.isArray(t))for(var a=0,r=t.length;a<r;a++)n(t[a]);else n(t);this.context=1<o.length?x(o):o,e&&this.push.apply(this,e),this.selector={rows:null,cols:null,opts:null},U.extend(this,this,ne)},$.Api=U,V.extend(U.prototype,{any:function(){return 0!==this.count()},context:[],count:function(){return this.flatten().length},each:function(t){for(var e=0,n=this.length;e<n;e++)t.call(this,this[e],e,this);return this},eq:function(t){var e=this.context;return e.length>t?new U(e[t],this[t]):null},filter:function(t){t=n.filter.call(this,t,this);return new U(this.context,t)},flatten:function(){var t=[];return new U(this.context,t.concat.apply(t,this.toArray()))},get:function(t){return this[t]},join:n.join,includes:function(t){return-1!==this.indexOf(t)},indexOf:n.indexOf,iterator:function(t,e,n,a){var r,o,i,l,s,u,c,d,f=[],h=this.context,p=this.selector;for("string"==typeof t&&(a=n,n=e,e=t,t=!1),o=0,i=h.length;o<i;o++){var g=new U(h[o]);if("table"===e)void 0!==(r=n.call(g,h[o],o))&&f.push(r);else if("columns"===e||"rows"===e)void 0!==(r=n.call(g,h[o],this[o],o))&&f.push(r);else if("every"===e||"column"===e||"column-rows"===e||"row"===e||"cell"===e)for(c=this[o],"column-rows"===e&&(u=he(h[o],p.opts)),l=0,s=c.length;l<s;l++)d=c[l],void 0!==(r="cell"===e?n.call(g,h[o],d.row,d.column,o,l):n.call(g,h[o],d,o,l,u))&&f.push(r)}return f.length||a?((t=(a=new U(h,t?f.concat.apply([],f):f)).selector).rows=p.rows,t.cols=p.cols,t.opts=p.opts,a):this},lastIndexOf:n.lastIndexOf,length:0,map:function(t){t=n.map.call(this,t,this);return new U(this.context,t)},pluck:function(t){var e=$.util.get(t);return this.map(function(t){return e(t)})},pop:n.pop,push:n.push,reduce:n.reduce,reduceRight:n.reduceRight,reverse:n.reverse,selector:null,shift:n.shift,slice:function(){return new U(this.context,this)},sort:n.sort,splice:n.splice,toArray:function(){return n.slice.call(this)},to$:function(){return V(this)},toJQuery:function(){return V(this)},unique:function(){return new U(this.context,x(this.toArray()))},unshift:n.unshift}),q.__apiStruct=ne,U.extend=function(t,e,n){if(n.length&&e&&(e instanceof U||e.__dt_wrapper))for(var a,r=0,o=n.length;r<o;r++)"__proto__"!==(a=n[r]).name&&(e[a.name]="function"===a.type?function(e,n,a){return function(){var t=n.apply(e||this,arguments);return U.extend(t,t,a.methodExt),t}}(t,a.val,a):"object"===a.type?{}:a.val,e[a.name].__dt_wrapper=!0,U.extend(t,e[a.name],a.propExt))},U.register=e=function(t,e){if(Array.isArray(t))for(var n=0,a=t.length;n<a;n++)U.register(t[n],e);else for(var r=t.split("."),o=ne,i=0,l=r.length;i<l;i++){var s,u,c=function(t,e){for(var n=0,a=t.length;n<a;n++)if(t[n].name===e)return t[n];return null}(o,u=(s=-1!==r[i].indexOf("()"))?r[i].replace("()",""):r[i]);c||o.push(c={name:u,val:{},methodExt:[],propExt:[],type:"object"}),i===l-1?(c.val=e,c.type="function"==typeof e?"function":V.isPlainObject(e)?"object":"other"):o=s?c.methodExt:c.propExt}},U.registerPlural=t=function(t,e,n){U.register(t,n),U.register(e,function(){var t=n.apply(this,arguments);return t===this?this:t instanceof U?t.length?Array.isArray(t[0])?new U(t.context,t[0]):t[0]:void 0:t})};function ae(t,e){var n,a;return Array.isArray(t)?(n=[],t.forEach(function(t){t=ae(t,e);n.push.apply(n,t)}),n.filter(function(t){return t})):"number"==typeof t?[e[t]]:(a=e.map(function(t){return t.nTable}),V(a).filter(t).map(function(){var t=a.indexOf(this);return e[t]}).toArray())}function re(r,o,t){var e,n;t&&(e=new U(r)).one("draw",function(){t(e.ajax.json())}),"ssp"==et(r)?s(r,o):(w(r,!0),(n=r.jqXHR)&&4!==n.readyState&&n.abort(),At(r,{},function(t){pt(r);for(var e=Lt(r,t),n=0,a=e.length;n<a;n++)Y(r,e[n]);s(r,o),kt(r),w(r,!1)}))}function oe(t,e,n,a,r){for(var o,i,l,s,u=[],c=typeof e,d=0,f=(e=e&&"string"!=c&&"function"!=c&&void 0!==e.length?e:[e]).length;d<f;d++)for(l=0,s=(i=e[d]&&e[d].split&&!e[d].match(/[[(:]/)?e[d].split(","):[e[d]]).length;l<s;l++)(o=(o=n("string"==typeof i[l]?i[l].trim():i[l])).filter(function(t){return null!=t}))&&o.length&&(u=u.concat(o));var h=C.selector[t];if(h.length)for(d=0,f=h.length;d<f;d++)u=h[d](a,r,u);return x(u)}function ie(t){return(t=t||{}).filter&&void 0===t.search&&(t.search=t.filter),V.extend({search:"none",order:"current",page:"all"},t)}function le(t){var e=new U(t.context[0]);return t.length&&e.push(t[0]),e.selector=t.selector,e.length&&1<e[0].length&&e[0].splice(1),e}e("tables()",function(t){return null!=t?new U(ae(t,this.context)):this}),e("table()",function(t){var t=this.tables(t),e=t.context;return e.length?new U(e[0]):t}),[["nodes","node","nTable"],["body","body","nTBody"],["header","header","nTHead"],["footer","footer","nTFoot"]].forEach(function(e){t("tables()."+e[0]+"()","table()."+e[1]+"()",function(){return this.iterator("table",function(t){return t[e[2]]},1)})}),[["header","aoHeader"],["footer","aoFooter"]].forEach(function(n){e("table()."+n[0]+".structure()",function(t){var t=this.columns(t).indexes().flatten(),e=this.context[0];return xt(e,e[n[1]],t)})}),t("tables().containers()","table().container()",function(){return this.iterator("table",function(t){return t.nTableWrapper},1)}),e("tables().every()",function(n){var a=this;return this.iterator("table",function(t,e){n.call(a.table(e),e)})}),e("caption()",function(r,o){var t,e=this.context;return void 0===r?(t=e[0].captionNode)&&e.length?t.innerHTML:null:this.iterator("table",function(t){var e=V(t.nTable),n=V(t.captionNode),a=V(t.nTableWrapper);n.length||(n=V("<caption/>").html(r),t.captionNode=n[0],o)||(e.prepend(n),o=n.css("caption-side")),n.html(r),o&&(n.css("caption-side",o),n[0]._captionSide=o),(a.find("div.dataTables_scroll").length?(t="top"===o?"Head":"Foot",a.find("div.dataTables_scroll"+t+" table")):e).prepend(n)},1)}),e("caption.node()",function(){var t=this.context;return t.length?t[0].captionNode:null}),e("draw()",function(e){return this.iterator("table",function(t){"page"===e?S(t):s(t,!1===(e="string"==typeof e?"full-hold"!==e:e))})}),e("page()",function(e){return void 0===e?this.page.info().page:this.iterator("table",function(t){Ht(t,e)})}),e("page.info()",function(){var t,e,n,a,r;if(0!==this.context.length)return e=(t=this.context[0])._iDisplayStart,n=t.oFeatures.bPaginate?t._iDisplayLength:-1,a=t.fnRecordsDisplay(),{page:(r=-1===n)?0:Math.floor(e/n),pages:r?1:Math.ceil(a/n),start:e,end:t.fnDisplayEnd(),length:n,recordsTotal:t.fnRecordsTotal(),recordsDisplay:a,serverSide:"ssp"===et(t)}}),e("page.len()",function(e){return void 0===e?0!==this.context.length?this.context[0]._iDisplayLength:void 0:this.iterator("table",function(t){Mt(t,e)})}),e("ajax.json()",function(){var t=this.context;if(0<t.length)return t[0].json}),e("ajax.params()",function(){var t=this.context;if(0<t.length)return t[0].oAjaxData}),e("ajax.reload()",function(e,n){return this.iterator("table",function(t){re(t,!1===n,e)})}),e("ajax.url()",function(e){var t=this.context;return void 0===e?0===t.length?void 0:(t=t[0],V.isPlainObject(t.ajax)?t.ajax.url:t.ajax):this.iterator("table",function(t){V.isPlainObject(t.ajax)?t.ajax.url=e:t.ajax=e})}),e("ajax.url().load()",function(e,n){return this.iterator("table",function(t){re(t,!1===n,e)})});function se(o,i,t,e){function l(t,e){var n;if(Array.isArray(t)||t instanceof V)for(var a=0,r=t.length;a<r;a++)l(t[a],e);else t.nodeName&&"tr"===t.nodeName.toLowerCase()?(t.setAttribute("data-dt-row",i.idx),s.push(t)):(n=V("<tr><td></td></tr>").attr("data-dt-row",i.idx).addClass(e),V("td",n).addClass(e).html(t)[0].colSpan=W(o),s.push(n[0]))}var s=[];l(t,e),i._details&&i._details.detach(),i._details=V(s),i._detailsShow&&i._details.insertAfter(i.nTr)}function ue(t,e){var n=t.context;if(n.length&&t.length){var a=n[0].aoData[t[0]];if(a._details){(a._detailsShow=e)?(a._details.insertAfter(a.nTr),V(a.nTr).addClass("dt-hasChild")):(a._details.detach(),V(a.nTr).removeClass("dt-hasChild")),tt(n[0],null,"childRow",[e,t.row(t[0])]);var i=n[0],r=new U(i),a=".dt.DT_details",e="draw"+a,t="column-sizing"+a,a="destroy"+a,l=i.aoData;if(r.off(e+" "+t+" "+a),f(l,"_details").length>0){r.on(e,function(t,e){if(i!==e)return;r.rows({page:"current"}).eq(0).each(function(t){var e=l[t];if(e._detailsShow)e._details.insertAfter(e.nTr)})});r.on(t,function(t,e){if(i!==e)return;var n,a=W(e);for(var r=0,o=l.length;r<o;r++){n=l[r];if(n&&n._details)n._details.each(function(){var t=V(this).children("td");if(t.length==1)t.attr("colspan",a)})}});r.on(a,function(t,e){if(i!==e)return;for(var n=0,a=l.length;n<a;n++)if(l[n]&&l[n]._details)me(r,n)})}ge(n)}}}function ce(t,e,n,a,r,o){for(var i=[],l=0,s=r.length;l<s;l++)i.push(G(t,r[l],e,o));return i}function de(t,e,n){var a=t.aoHeader;return a[void 0!==n?n:t.bSortCellsTop?0:a.length-1][e].cell}function fe(e,n){return function(t){return y(t)||"string"!=typeof t||(t=t.replace(d," "),e&&(t=I(t)),n&&(t=O(t,!1))),t}}var he=function(t,e){var n,a=[],r=t.aiDisplay,o=t.aiDisplayMaster,i=e.search,l=e.order,e=e.page;if("ssp"==et(t))return"removed"===i?[]:h(0,o.length);if("current"==e)for(u=t._iDisplayStart,c=t.fnDisplayEnd();u<c;u++)a.push(r[u]);else if("current"==l||"applied"==l){if("none"==i)a=o.slice();else if("applied"==i)a=r.slice();else if("removed"==i){for(var s={},u=0,c=r.length;u<c;u++)s[r[u]]=null;o.forEach(function(t){Object.prototype.hasOwnProperty.call(s,t)||a.push(t)})}}else if("index"==l||"original"==l)for(u=0,c=t.aoData.length;u<c;u++)t.aoData[u]&&("none"==i||-1===(n=r.indexOf(u))&&"removed"==i||0<=n&&"applied"==i)&&a.push(u);else if("number"==typeof l){var d=zt(t,l,"asc");if("none"===i)a=d;else for(u=0;u<d.length;u++)(-1===(n=r.indexOf(d[u]))&&"removed"==i||0<=n&&"applied"==i)&&a.push(d[u])}return a},pe=(e("rows()",function(n,a){void 0===n?n="":V.isPlainObject(n)&&(a=n,n=""),a=ie(a);var t=this.iterator("table",function(t){return e=oe("row",e=n,function(n){var t=g(n),a=r.aoData;if(null!==t&&!o)return[t];if(i=i||he(r,o),null!==t&&-1!==i.indexOf(t))return[t];if(null==n||""===n)return i;if("function"==typeof n)return i.map(function(t){var e=a[t];return n(t,e._aData,e.nTr)?t:null});if(n.nodeName)return t=n._DT_RowIndex,e=n._DT_CellIndex,void 0!==t?a[t]&&a[t].nTr===n?[t]:[]:e?a[e.row]&&a[e.row].nTr===n.parentNode?[e.row]:[]:(t=V(n).closest("*[data-dt-row]")).length?[t.data("dt-row")]:[];if("string"==typeof n&&"#"===n.charAt(0)){var e=r.aIds[n.replace(/^#/,"")];if(void 0!==e)return[e.idx]}t=b(v(r.aoData,i,"nTr"));return V(t).filter(n).map(function(){return this._DT_RowIndex}).toArray()},r=t,o=a),"current"!==o.order&&"applied"!==o.order||qt(r,e),e;var r,e,o,i},1);return t.selector.rows=n,t.selector.opts=a,t}),e("rows().nodes()",function(){return this.iterator("row",function(t,e){return t.aoData[e].nTr||void 0},1)}),e("rows().data()",function(){return this.iterator(!0,"rows",function(t,e){return v(t.aoData,e,"_aData")},1)}),t("rows().cache()","row().cache()",function(n){return this.iterator("row",function(t,e){t=t.aoData[e];return"search"===n?t._aFilterData:t._aSortData},1)}),t("rows().invalidate()","row().invalidate()",function(n){return this.iterator("row",function(t,e){gt(t,e,n)})}),t("rows().indexes()","row().index()",function(){return this.iterator("row",function(t,e){return e},1)}),t("rows().ids()","row().id()",function(t){for(var e=[],n=this.context,a=0,r=n.length;a<r;a++)for(var o=0,i=this[a].length;o<i;o++){var l=n[a].rowIdFn(n[a].aoData[this[a][o]]._aData);e.push((!0===t?"#":"")+l)}return new U(n,e)}),t("rows().remove()","row().remove()",function(){return this.iterator("row",function(t,e){var n=t.aoData,a=n[e],r=t.aiDisplayMaster.indexOf(e),r=(-1!==r&&t.aiDisplayMaster.splice(r,1),0<t._iRecordsDisplay&&t._iRecordsDisplay--,Kt(t),t.rowIdFn(a._aData));void 0!==r&&delete t.aIds[r],n[e]=null}),this}),e("rows.add()",function(o){var t=this.iterator("table",function(t){for(var e,n=[],a=0,r=o.length;a<r;a++)(e=o[a]).nodeName&&"TR"===e.nodeName.toUpperCase()?n.push(ut(t,e)[0]):n.push(Y(t,e));return n},1),e=this.rows(-1);return e.pop(),e.push.apply(e,t),e}),e("row()",function(t,e){return le(this.rows(t,e))}),e("row().data()",function(t){var e,n=this.context;return void 0===t?n.length&&this.length&&this[0].length?n[0].aoData[this[0]]._aData:void 0:((e=n[0].aoData[this[0]])._aData=t,Array.isArray(t)&&e.nTr&&e.nTr.id&&m(n[0].rowId)(t,e.nTr.id),gt(n[0],this[0],"data"),this)}),e("row().node()",function(){var t=this.context;if(t.length&&this.length&&this[0].length){t=t[0].aoData[this[0]];if(t&&t.nTr)return t.nTr}return null}),e("row.add()",function(e){e instanceof V&&e.length&&(e=e[0]);var t=this.iterator("table",function(t){return e.nodeName&&"TR"===e.nodeName.toUpperCase()?ut(t,e)[0]:Y(t,e)});return this.row(t[0])}),V(_).on("plugin-init.dt",function(t,e){var a=new U(e);a.on("stateSaveParams.DT",function(t,e,n){for(var a=e.rowIdFn,r=e.aiDisplayMaster,o=[],i=0;i<r.length;i++){var l=r[i],l=e.aoData[l];l._detailsShow&&o.push("#"+a(l._aData))}n.childRows=o}),a.on("stateLoaded.DT",function(t,e,n){pe(a,n)}),pe(a,a.state.loaded())}),function(t,e){e&&e.childRows&&t.rows(e.childRows.map(function(t){return t.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g,"$1\\:")})).every(function(){tt(t.settings()[0],null,"requestChild",[this])})}),ge=$.util.throttle(function(t){Gt(t[0])},500),me=function(t,e){var n=t.context;n.length&&(e=n[0].aoData[void 0!==e?e:t[0]])&&e._details&&(e._details.remove(),e._detailsShow=void 0,e._details=void 0,V(e.nTr).removeClass("dt-hasChild"),ge(n))},ve="row().child",be=ve+"()",ye=(e(be,function(t,e){var n=this.context;return void 0===t?n.length&&this.length&&n[0].aoData[this[0]]?n[0].aoData[this[0]]._details:void 0:(!0===t?this.child.show():!1===t?me(this):n.length&&this.length&&se(n[0],n[0].aoData[this[0]],t,e),this)}),e([ve+".show()",be+".show()"],function(){return ue(this,!0),this}),e([ve+".hide()",be+".hide()"],function(){return ue(this,!1),this}),e([ve+".remove()",be+".remove()"],function(){return me(this),this}),e(ve+".isShown()",function(){var t=this.context;return t.length&&this.length&&t[0].aoData[this[0]]&&t[0].aoData[this[0]]._detailsShow||!1}),/^([^:]+)?:(name|title|visIdx|visible)$/),be=(e("columns()",function(n,a){void 0===n?n="":V.isPlainObject(n)&&(a=n,n=""),a=ie(a);var t=this.iterator("table",function(t){return e=n,l=a,s=(i=t).aoColumns,u=f(s,"sName"),c=f(s,"sTitle"),t=$.util.get("[].[].cell")(i.aoHeader),d=x(E([],t)),oe("column",e,function(n){var a,t=g(n);if(""===n)return h(s.length);if(null!==t)return[0<=t?t:s.length+t];if("function"==typeof n)return a=he(i,l),s.map(function(t,e){return n(e,ce(i,e,0,0,a),de(i,e))?e:null});var e,r,o="string"==typeof n?n.match(ye):"";if(o)switch(o[2]){case"visIdx":case"visible":return o[1]?(e=parseInt(o[1],10))<0?[(r=s.map(function(t,e){return t.bVisible?e:null}))[r.length+e]]:[H(i,e)]:s.map(function(t,e){return t.bVisible?e:null});case"name":return u.map(function(t,e){return t===o[1]?e:null});case"title":return c.map(function(t,e){return t===o[1]?e:null});default:return[]}return n.nodeName&&n._DT_CellIndex?[n._DT_CellIndex.column]:(t=V(d).filter(n).map(function(){return st(this)}).toArray()).length||!n.nodeName?t:(t=V(n).closest("*[data-dt-column]")).length?[t.data("dt-column")]:[]},i,l);var i,e,l,s,u,c,d},1);return t.selector.cols=n,t.selector.opts=a,t}),t("columns().header()","column().header()",function(n){return this.iterator("column",function(t,e){return de(t,e,n)},1)}),t("columns().footer()","column().footer()",function(n){return this.iterator("column",function(t,e){return t.aoFooter.length?t.aoFooter[void 0!==n?n:0][e].cell:null},1)}),t("columns().data()","column().data()",function(){return this.iterator("column-rows",ce,1)}),t("columns().render()","column().render()",function(o){return this.iterator("column-rows",function(t,e,n,a,r){return ce(t,e,0,0,r,o)},1)}),t("columns().dataSrc()","column().dataSrc()",function(){return this.iterator("column",function(t,e){return t.aoColumns[e].mData},1)}),t("columns().cache()","column().cache()",function(o){return this.iterator("column-rows",function(t,e,n,a,r){return v(t.aoData,r,"search"===o?"_aFilterData":"_aSortData",e)},1)}),t("columns().init()","column().init()",function(){return this.iterator("column",function(t,e){return t.aoColumns[e]},1)}),t("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(t,e,n,a,r){return v(t.aoData,r,"anCells",e)},1)}),t("columns().titles()","column().title()",function(n,a){return this.iterator("column",function(t,e){"number"==typeof n&&(a=n,n=void 0);e=V("span.dt-column-title",this.column(e).header(a));return void 0!==n?(e.html(n),this):e.html()},1)}),t("columns().types()","column().type()",function(){return this.iterator("column",function(t,e){e=t.aoColumns[e].sType;return e||B(t),e},1)}),t("columns().visible()","column().visible()",function(n,a){var e=this,r=[],t=this.iterator("column",function(t,e){if(void 0===n)return t.aoColumns[e].bVisible;!function(t,e,n){var a,r,o=t.aoColumns,i=o[e],l=t.aoData;if(void 0===n)return i.bVisible;if(i.bVisible===n)return!1;if(n)for(var s=f(o,"bVisible").indexOf(!0,e+1),u=0,c=l.length;u<c;u++)l[u]&&(r=l[u].nTr,a=l[u].anCells,r)&&r.insertBefore(a[e],a[s]||null);else V(f(t.aoData,"anCells",e)).detach();return i.bVisible=n,Bt(t),!0}(t,e,n)||r.push(e)});return void 0!==n&&this.iterator("table",function(t){St(t,t.aoHeader),St(t,t.aoFooter),t.aiDisplay.length||V(t.nTBody).find("td[colspan]").attr("colspan",W(t)),Gt(t),e.iterator("column",function(t,e){r.includes(e)&&tt(t,null,"column-visibility",[t,e,n,a])}),r.length&&(void 0===a||a)&&e.columns.adjust()}),t}),t("columns().widths()","column().width()",function(){var t=this.columns(":visible").count(),t=V("<tr>").html("<td>"+Array(t).join("</td><td>")+"</td>"),n=(V(this.table().body()).append(t),t.children().map(function(){return V(this).outerWidth()}));return t.remove(),this.iterator("column",function(t,e){t=T(t,e);return null!==t?n[t]:0},1)}),t("columns().indexes()","column().index()",function(n){return this.iterator("column",function(t,e){return"visible"===n?T(t,e):e},1)}),e("columns.adjust()",function(){return this.iterator("table",function(t){M(t)},1)}),e("column.index()",function(t,e){var n;if(0!==this.context.length)return n=this.context[0],"fromVisible"===t||"toData"===t?H(n,e):"fromData"===t||"toVisible"===t?T(n,e):void 0}),e("column()",function(t,e){return le(this.columns(t,e))}),e("cells()",function(g,t,m){var a,r,o,i,l,s,e;return V.isPlainObject(g)&&(void 0===g.row?(m=g,g=null):(m=t,t=null)),V.isPlainObject(t)&&(m=t,t=null),null==t?this.iterator("table",function(t){return a=t,t=g,e=ie(m),d=a.aoData,f=he(a,e),n=b(v(d,f,"anCells")),h=V(E([],n)),p=a.aoColumns.length,oe("cell",t,function(t){var e,n="function"==typeof t;if(null==t||n){for(o=[],i=0,l=f.length;i<l;i++)for(r=f[i],s=0;s<p;s++)u={row:r,column:s},(!n||(c=d[r],t(u,G(a,r,s),c.anCells?c.anCells[s]:null)))&&o.push(u);return o}return V.isPlainObject(t)?void 0!==t.column&&void 0!==t.row&&-1!==f.indexOf(t.row)?[t]:[]:(e=h.filter(t).map(function(t,e){return{row:e._DT_CellIndex.row,column:e._DT_CellIndex.column}}).toArray()).length||!t.nodeName?e:(c=V(t).closest("*[data-dt-row]")).length?[{row:c.data("dt-row"),column:c.data("dt-column")}]:[]},a,e);var a,e,r,o,i,l,s,u,c,d,f,n,h,p}):(e=m?{page:m.page,order:m.order,search:m.search}:{},a=this.columns(t,e),r=this.rows(g,e),e=this.iterator("table",function(t,e){var n=[];for(o=0,i=r[e].length;o<i;o++)for(l=0,s=a[e].length;l<s;l++)n.push({row:r[e][o],column:a[e][l]});return n},1),e=m&&m.selected?this.cells(e,m):e,V.extend(e.selector,{cols:t,rows:g,opts:m}),e)}),t("cells().nodes()","cell().node()",function(){return this.iterator("cell",function(t,e,n){t=t.aoData[e];return t&&t.anCells?t.anCells[n]:void 0},1)}),e("cells().data()",function(){return this.iterator("cell",function(t,e,n){return G(t,e,n)},1)}),t("cells().cache()","cell().cache()",function(a){return a="search"===a?"_aFilterData":"_aSortData",this.iterator("cell",function(t,e,n){return t.aoData[e][a][n]},1)}),t("cells().render()","cell().render()",function(a){return this.iterator("cell",function(t,e,n){return G(t,e,n,a)},1)}),t("cells().indexes()","cell().index()",function(){return this.iterator("cell",function(t,e,n){return{row:e,column:n,columnVisible:T(t,n)}},1)}),t("cells().invalidate()","cell().invalidate()",function(a){return this.iterator("cell",function(t,e,n){gt(t,e,a,n)})}),e("cell()",function(t,e,n){return le(this.cells(t,e,n))}),e("cell().data()",function(t){var e,n,a,r,o,i=this.context,l=this[0];return void 0===t?i.length&&l.length?G(i[0],l[0].row,l[0].column):void 0:(e=i[0],n=l[0].row,a=l[0].column,r=e.aoColumns[a],o=e.aoData[n]._aData,r.fnSetData(o,t,{settings:e,row:n,col:a}),gt(i[0],l[0].row,"data",l[0].column),this)}),e("order()",function(e,t){var n=this.context,a=Array.prototype.slice.call(arguments);return void 0===e?0!==n.length?n[0].aaSorting:void 0:("number"==typeof e?e=[[e,t]]:1<a.length&&(e=a),this.iterator("table",function(t){t.aaSorting=Array.isArray(e)?e.slice():e}))}),e("order.listener()",function(e,n,a){return this.iterator("table",function(t){Vt(t,e,{},n,a)})}),e("order.fixed()",function(e){var t;return e?this.iterator("table",function(t){t.aaSortingFixed=V.extend(!0,{},e)}):(t=(t=this.context).length?t[0].aaSortingFixed:void 0,Array.isArray(t)?{pre:t}:t)}),e(["columns().order()","column().order()"],function(n){var a=this;return n?this.iterator("table",function(t,e){t.aaSorting=a[e].map(function(t){return[t,n]})}):this.iterator("column",function(t,e){for(var n=$t(t),a=0,r=n.length;a<r;a++)if(n[a].col===e)return n[a].dir;return null},1)}),t("columns().orderable()","column().orderable()",function(n){return this.iterator("column",function(t,e){t=t.aoColumns[e];return n?t.asSorting:t.bSortable},1)}),e("processing()",function(e){return this.iterator("table",function(t){w(t,e)})}),e("search()",function(e,n,a,r){var t=this.context;return void 0===e?0!==t.length?t[0].oPreviousSearch.search:void 0:this.iterator("table",function(t){t.oFeatures.bFilter&&Nt(t,"object"==typeof n?V.extend(t.oPreviousSearch,n,{search:e}):V.extend(t.oPreviousSearch,{search:e,regex:null!==n&&n,smart:null===a||a,caseInsensitive:null===r||r}))})}),e("search.fixed()",function(e,n){var t=this.iterator(!0,"table",function(t){t=t.searchFixed;return e?void 0===n?t[e]:(null===n?delete t[e]:t[e]=n,this):Object.keys(t)});return void 0!==e&&void 0===n?t[0]:t}),t("columns().search()","column().search()",function(a,r,o,i){return this.iterator("column",function(t,e){var n=t.aoPreSearchCols;if(void 0===a)return n[e].search;t.oFeatures.bFilter&&("object"==typeof r?V.extend(n[e],r,{search:a}):V.extend(n[e],{search:a,regex:null!==r&&r,smart:null===o||o,caseInsensitive:null===i||i}),Nt(t,t.oPreviousSearch))})}),e(["columns().search.fixed()","column().search.fixed()"],function(n,a){var t=this.iterator(!0,"column",function(t,e){t=t.aoColumns[e].searchFixed;return n?void 0===a?t[n]:(null===a?delete t[n]:t[n]=a,this):Object.keys(t)});return void 0!==n&&void 0===a?t[0]:t}),e("state()",function(t,e){var n;return t?(n=V.extend(!0,{},t),this.iterator("table",function(t){!1!==e&&(n.time=+new Date+100),Jt(t,n,function(){})})):this.context.length?this.context[0].oSavedState:null}),e("state.clear()",function(){return this.iterator("table",function(t){t.fnStateSaveCallback.call(t.oInstance,t,{})})}),e("state.loaded()",function(){return this.context.length?this.context[0].oLoadedState:null}),e("state.save()",function(){return this.iterator("table",function(t){Gt(t)})}),$.use=function(t,e){"lib"===e||t.fn?V=t:"win"==e||t.document?_=(q=t).document:"datetime"!==e&&"DateTime"!==t.type||($.DateTime=t)},$.factory=function(t,e){var n=!1;return t&&t.document&&(_=(q=t).document),e&&e.fn&&e.fn.jquery&&(V=e,n=!0),n},$.versionCheck=function(t,e){for(var n,a,r=(e||$.version).split("."),o=t.split("."),i=0,l=o.length;i<l;i++)if((n=parseInt(r[i],10)||0)!==(a=parseInt(o[i],10)||0))return a<n;return!0},$.isDataTable=function(t){var r=V(t).get(0),o=!1;return t instanceof $.Api||(V.each($.settings,function(t,e){var n=e.nScrollHead?V("table",e.nScrollHead)[0]:null,a=e.nScrollFoot?V("table",e.nScrollFoot)[0]:null;e.nTable!==r&&n!==r&&a!==r||(o=!0)}),o)},$.tables=function(e){var t=!1,n=(V.isPlainObject(e)&&(t=e.api,e=e.visible),$.settings.filter(function(t){return!(e&&!V(t.nTable).is(":visible"))}).map(function(t){return t.nTable}));return t?new U(n):n},$.camelToHungarian=z,e("$()",function(t,e){e=this.rows(e).nodes(),e=V(e);return V([].concat(e.filter(t).toArray(),e.find(t).toArray()))}),V.each(["on","one","off"],function(t,n){e(n+"()",function(){var t=Array.prototype.slice.call(arguments),e=(t[0]=t[0].split(/\s/).map(function(t){return t.match(/\.dt\b/)?t:t+".dt"}).join(" "),V(this.tables().nodes()));return e[n].apply(e,t),this})}),e("clear()",function(){return this.iterator("table",function(t){pt(t)})}),e("error()",function(e){return this.iterator("table",function(t){Z(t,0,e)})}),e("settings()",function(){return new U(this.context,this.context)}),e("init()",function(){var t=this.context;return t.length?t[0].oInit:null}),e("data()",function(){return this.iterator("table",function(t){return f(t.aoData,"_aData")}).flatten()}),e("trigger()",function(e,n,a){return this.iterator("table",function(t){return tt(t,null,e,n,a)}).flatten()}),e("ready()",function(t){var e=this.context;return t?this.tables().every(function(){this.context[0]._bInitComplete?t.call(this):this.on("init",function(){t.call(this)})}):e.length?e[0]._bInitComplete||!1:null}),e("destroy()",function(c){return c=c||!1,this.iterator("table",function(t){var e=t.oClasses,n=t.nTable,a=t.nTBody,r=t.nTHead,o=t.nTFoot,i=V(n),a=V(a),l=V(t.nTableWrapper),s=t.aoData.map(function(t){return t?t.nTr:null}),u=e.order,o=(t.bDestroying=!0,tt(t,"aoDestroyCallback","destroy",[t],!0),c||new U(t).columns().visible(!0),l.off(".DT").find(":not(tbody *)").off(".DT"),V(q).off(".DT-"+t.sInstance),n!=r.parentNode&&(i.children("thead").detach(),i.append(r)),o&&n!=o.parentNode&&(i.children("tfoot").detach(),i.append(o)),t.colgroup.remove(),t.aaSorting=[],t.aaSortingFixed=[],Yt(t),V("th, td",r).removeClass(u.canAsc+" "+u.canDesc+" "+u.isAsc+" "+u.isDesc).css("width",""),a.children().detach(),a.append(s),t.nTableWrapper.parentNode),r=t.nTableWrapper.nextSibling,u=c?"remove":"detach",a=(i[u](),l[u](),!c&&o&&(o.insertBefore(n,r),i.css("width",t.sDestroyWidth).removeClass(e.table)),$.settings.indexOf(t));-1!==a&&$.settings.splice(a,1)})}),V.each(["column","row","cell"],function(t,s){e(s+"s().every()",function(a){var r,o=this.selector.opts,i=this,l=0;return this.iterator("every",function(t,e,n){r=i[s](e,o),"cell"===s?a.call(r,r[0][0].row,r[0][0].column,n,l):a.call(r,e,n,l),l++})})}),e("i18n()",function(t,e,n){var a=this.context[0],t=J(t)(a.oLanguage);return"string"==typeof(t=V.isPlainObject(t=void 0===t?e:t)?void 0!==n&&void 0!==t[n]?t[n]:t._:t)?t.replace("%d",n):t}),$.version="2.0.8",$.settings=[],$.models={},$.models.oSearch={caseInsensitive:!0,search:"",regex:!1,smart:!0,return:!1},$.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,src:null,idx:-1,displayData:null},$.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",sSortingClass:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null,maxLenString:null,searchFixed:null},$.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],bAutoWidth:!0,bDeferRender:!0,bDestroy:!1,bFilter:!0,bInfo:!0,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:null,bSortClasses:!0,bStateSave:!1,fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(t){return t.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnStateLoadCallback:function(t){try{return JSON.parse((-1===t.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+t.sInstance+"_"+location.pathname))}catch(t){return{}}},fnStateLoadParams:null,fnStateLoaded:null,fnStateSaveCallback:function(t,e){try{(-1===t.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+t.sInstance+"_"+location.pathname,JSON.stringify(e))}catch(t){}},fnStateSaveParams:null,iStateDuration:7200,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{orderable:": Activate to sort",orderableReverse:": Activate to invert sorting",orderableRemove:": Activate to remove sorting",paginate:{first:"First",last:"Last",next:"Next",previous:"Previous"}},oPaginate:{sFirst:"«",sLast:"»",sNext:"›",sPrevious:"‹"},entries:{_:"entries",1:"entry"},sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_",sInfoEmpty:"Showing 0 to 0 of 0 _ENTRIES-TOTAL_",sInfoFiltered:"(filtered from _MAX_ total _ENTRIES-MAX_)",sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"_MENU_ _ENTRIES_ per page",sLoadingRecords:"Loading...",sProcessing:"",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:V.extend({},$.models.oSearch),layout:{topStart:"pageLength",topEnd:"search",bottomStart:"info",bottomEnd:"paging"},sDom:null,searchDelay:null,sPaginationType:"full_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null,rowId:"DT_RowId",caption:null},k($.defaults),$.defaults.column={aDataSort:null,iDataSort:-1,ariaTitle:"",asSorting:["asc","desc",""],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null},k($.defaults.column),$.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,bInfo:!0,bLengthChange:!0,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollbarLeft:!1,barWidth:0},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aIds:{},aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},searchFixed:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bInitialised:!1,aoOpenRows:[],sDom:null,searchDelay:null,sPaginationType:"two_button",pagingControls:0,iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,bAjaxDataGet:!0,jqXHR:null,json:void 0,oAjaxData:void 0,sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==et(this)?+this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==et(this)?+this._iRecordsDisplay:this.aiDisplay.length},fnDisplayEnd:function(){var t=this._iDisplayLength,e=this._iDisplayStart,n=e+t,a=this.aiDisplay.length,r=this.oFeatures,o=r.bPaginate;return r.bServerSide?!1===o||-1===t?e+a:Math.min(e+t,this._iRecordsDisplay):!o||a<n||-1===t?a:n},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{},rowIdFn:null,rowId:null,caption:"",captionNode:null,colgroup:null},$.ext.pager);V.extend(be,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},numbers:function(){return["numbers"]},simple_numbers:function(){return["previous","numbers","next"]},full_numbers:function(){return["first","previous","numbers","next","last"]},first_last:function(){return["first","last"]},first_last_numbers:function(){return["first","numbers","last"]},_numbers:Ne,numbers_length:7}),V.extend(!0,$.ext.renderer,{pagingButton:{_:function(t,e,n,a,r){var t=t.oClasses.paging,o=[t.button];return a&&o.push(t.active),r&&o.push(t.disabled),{display:a="ellipsis"===e?V('<span class="ellipsis"></span>').html(n)[0]:V("<button>",{class:o.join(" "),role:"link",type:"button"}).html(n),clicker:a}}},pagingContainer:{_:function(t,e){return e}}});function De(t){return t.replace(/[\W]/g,"_")}function xe(t,e,n,a,r){return q.moment?t[e](r):q.luxon?t[n](r):a?t[a](r):t}var Se=!1;function Te(t,e,n){var a;if(q.moment){if(!(a=q.moment.utc(t,e,n,!0)).isValid())return null}else if(q.luxon){if(!(a=e&&"string"==typeof t?q.luxon.DateTime.fromFormat(t,e):q.luxon.DateTime.fromISO(t)).isValid)return null;a.setLocale(n)}else e?(Se||alert("DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17"),Se=!0):a=new Date(t);return a}function we(s){return function(a,r,o,i){0===arguments.length?(o="en",a=r=null):1===arguments.length?(o="en",r=a,a=null):2===arguments.length&&(o=r,r=a,a=null);var l="datetime"+(r?"-"+De(r):"");return $.ext.type.order[l]||$.type(l,{detect:function(t){return t===l&&l},order:{pre:function(t){return t.valueOf()}},className:"dt-right"}),function(t,e){var n;return null==t&&(t="--now"===i?(n=new Date,new Date(Date.UTC(n.getFullYear(),n.getMonth(),n.getDate(),n.getHours(),n.getMinutes(),n.getSeconds()))):""),"type"===e?l:""===t?"sort"!==e?"":Te("0000-01-01 00:00:00",null,o):!(null===r||a!==r||"sort"===e||"type"===e||t instanceof Date)||null===(n=Te(t,a,o))?t:"sort"===e?n:(t=null===r?xe(n,"toDate","toJSDate","")[s]():xe(n,"format","toFormat","toISOString",r),"display"===e?u(t):t)}}}var _e=",",Ce=".";if(void 0!==q.Intl)try{for(var Ie=(new Intl.NumberFormat).formatToParts(100000.1),a=0;a<Ie.length;a++)"group"===Ie[a].type?_e=Ie[a].value:"decimal"===Ie[a].type&&(Ce=Ie[a].value)}catch(t){}$.datetime=function(n,a){var r="datetime-detect-"+De(n);a=a||"en",$.ext.type.order[r]||$.type(r,{detect:function(t){var e=Te(t,n,a);return!(""!==t&&!e)&&r},order:{pre:function(t){return Te(t,n,a)||0}},className:"dt-right"})},$.render={date:we("toLocaleDateString"),datetime:we("toLocaleString"),time:we("toLocaleTimeString"),number:function(r,o,i,l,s){return null==r&&(r=_e),null==o&&(o=Ce),{display:function(t){if("number"!=typeof t&&"string"!=typeof t)return t;if(""===t||null===t)return t;var e=t<0?"-":"",n=parseFloat(t),a=Math.abs(n);if(1e11<=a||a<1e-4&&0!==a)return(a=n.toExponential(i).split(/e\+?/))[0]+" x 10<sup>"+a[1]+"</sup>";if(isNaN(n))return u(t);n=n.toFixed(i),t=Math.abs(n);a=parseInt(t,10),n=i?o+(t-a).toFixed(i).substring(2):"";return(e=0===a&&0===parseFloat(n)?"":e)+(l||"")+a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,r)+n+(s||"")}}},text:function(){return{display:u,filter:u}}};var i=$.ext.type,Ae=($.type=function(a,t,e){if(!t)return{className:i.className[a],detect:i.detect.find(function(t){return t.name===a}),order:{pre:i.order[a+"-pre"],asc:i.order[a+"-asc"],desc:i.order[a+"-desc"]},render:i.render[a],search:i.search[a]};function n(t,e){i[t][a]=e}function r(n){function t(t,e){return!0===(t=n(t,e))?a:t}Object.defineProperty(t,"name",{value:a});var e=i.detect.findIndex(function(t){return t.name===a});-1===e?i.detect.unshift(t):i.detect.splice(e,1,t)}function o(t){i.order[a+"-pre"]=t.pre,i.order[a+"-asc"]=t.asc,i.order[a+"-desc"]=t.desc}void 0===e&&(e=t,t=null),"className"===t?n("className",e):"detect"===t?r(e):"order"===t?o(e):"render"===t?n("render",e):"search"===t?n("search",e):t||(e.className&&n("className",e.className),void 0!==e.detect&&r(e.detect),e.order&&o(e.order),void 0!==e.render&&n("render",e.render),void 0!==e.search&&n("search",e.search))},$.types=function(){return i.detect.map(function(t){return t.name})},$.type("string",{detect:function(){return"string"},order:{pre:function(t){return y(t)?"":"string"==typeof t?t.toLowerCase():t.toString?t.toString():""}},search:fe(!1,!0)}),$.type("html",{detect:function(t){return y(t)||"string"==typeof t&&-1!==t.indexOf("<")?"html":null},order:{pre:function(t){return y(t)?"":t.replace?I(t).trim().toLowerCase():t+""}},search:fe(!0,!0)}),$.type("date",{className:"dt-type-date",detect:function(t){var e;return(!t||t instanceof Date||N.test(t))&&(null!==(e=Date.parse(t))&&!isNaN(e)||y(t))?"date":null},order:{pre:function(t){t=Date.parse(t);return isNaN(t)?-1/0:t}}}),$.type("html-num-fmt",{className:"dt-type-numeric",detect:function(t,e){e=e.oLanguage.sDecimal;return l(t,e,!0)?"html-num-fmt":null},order:{pre:function(t,e){e=e.oLanguage.sDecimal;return Ae(t,e,L,P)}},search:fe(!0,!0)}),$.type("html-num",{className:"dt-type-numeric",detect:function(t,e){e=e.oLanguage.sDecimal;return l(t,e)?"html-num":null},order:{pre:function(t,e){e=e.oLanguage.sDecimal;return Ae(t,e,L)}},search:fe(!0,!0)}),$.type("num-fmt",{className:"dt-type-numeric",detect:function(t,e){e=e.oLanguage.sDecimal;return o(t,e,!0)?"num-fmt":null},order:{pre:function(t,e){e=e.oLanguage.sDecimal;return Ae(t,e,P)}}}),$.type("num",{className:"dt-type-numeric",detect:function(t,e){e=e.oLanguage.sDecimal;return o(t,e)?"num":null},order:{pre:function(t,e){e=e.oLanguage.sDecimal;return Ae(t,e)}}}),function(t,e,n,a){var r;return 0===t||t&&"-"!==t?"number"==(r=typeof t)||"bigint"==r?t:+(t=(t=e?R(t,e):t).replace&&(n&&(t=t.replace(n,"")),a)?t.replace(a,""):t):-1/0});V.extend(!0,$.ext.renderer,{footer:{_:function(t,e,n){e.addClass(n.tfoot.cell)}},header:{_:function(d,f,h){f.addClass(h.thead.cell),d.oFeatures.bSort||f.addClass(h.order.none);var t=d.bSortCellsTop,e=f.closest("thead").find("tr"),n=f.parent().index();"disable"===f.attr("data-dt-order")||"disable"===f.parent().attr("data-dt-order")||!0===t&&0!==n||!1===t&&n!==e.length-1||V(d.nTable).on("order.dt.DT",function(t,e,n){var a,r,o,i,l,s,u,c;d===e&&(a=h.order,c=e.api.columns(f),r=d.aoColumns[c.flatten()[0]],o=c.orderable().includes(!0),i="",u=c.indexes(),l=c.orderable(!0).flatten(),s=","+n.map(function(t){return t.col}).join(",")+",",f.removeClass(a.isAsc+" "+a.isDesc).toggleClass(a.none,!o).toggleClass(a.canAsc,o&&l.includes("asc")).toggleClass(a.canDesc,o&&l.includes("desc")),-1!==(l=s.indexOf(","+u.toArray().join(",")+","))&&(s=c.order(),f.addClass(s.includes("asc")?a.isAsc:""+s.includes("desc")?a.isDesc:"")),0===l?(u=n[0],c=r.asSorting,f.attr("aria-sort","asc"===u.dir?"ascending":"descending"),i=c[u.index+1]?"Reverse":"Remove"):f.removeAttr("aria-sort"),f.attr("aria-label",o?r.ariaTitle+e.api.i18n("oAria.orderable"+i):r.ariaTitle),o)&&(f.find(".dt-column-title").attr("role","button"),f.attr("tabindex",0))})}},layout:{_:function(t,e,n){var a=V("<div/>").addClass("dt-layout-row").appendTo(e);V.each(n,function(t,e){t=e.table?"":"dt-"+t+" ";e.table&&a.addClass("dt-layout-table"),V("<div/>").attr({id:e.id||null,class:"dt-layout-cell "+t+(e.className||"")}).append(e.contents).appendTo(a)})}}}),$.feature={},$.feature.register=function(t,e,n){$.ext.features[t]=e,n&&C.feature.push({cFeature:n,fnInit:e})},$.feature.register("info",function(t,s){var e,n,u;return t.oFeatures.bInfo?(e=t.oLanguage,n=t.sTableId,u=V("<div/>",{class:t.oClasses.info.container}),s=V.extend({callback:e.fnInfoCallback,empty:e.sInfoEmpty,postfix:e.sInfoPostFix,search:e.sInfoFiltered,text:e.sInfo},s),t.aoDrawCallback.push(function(t){var e=s,n=u,a=t._iDisplayStart+1,r=t.fnDisplayEnd(),o=t.fnRecordsTotal(),i=t.fnRecordsDisplay(),l=i?e.text:e.empty;i!==o&&(l+=" "+e.search),l+=e.postfix,l=ee(t,l),e.callback&&(l=e.callback.call(t.oInstance,t,a,r,o,i,l)),n.html(l),tt(t,null,"info",[t,n[0],l])}),t._infoEl||(u.attr({"aria-live":"polite",id:n+"_info",role:"status"}),V(t.nTable).attr("aria-describedby",n+"_info"),t._infoEl=u),u):null},"i");var Le=0;function Fe(t,e,n,a){var r=t.oLanguage.oPaginate,o={display:"",active:!1,disabled:!1};switch(e){case"ellipsis":o.display="…",o.disabled=!0;break;case"first":o.display=r.sFirst,0===n&&(o.disabled=!0);break;case"previous":o.display=r.sPrevious,0===n&&(o.disabled=!0);break;case"next":o.display=r.sNext,0!==a&&n!==a-1||(o.disabled=!0);break;case"last":o.display=r.sLast,0!==a&&n!==a-1||(o.disabled=!0);break;default:"number"==typeof e&&(o.display=t.fnFormatNumber(e+1),n===e)&&(o.active=!0)}return o}function Ne(t,e,n,a){var r=[],o=Math.floor(n/2),i=a?2:1,l=a?1:0;return e<=n?r=h(0,e):1===n?r=[t]:3===n?t<=1?r=[0,1,"ellipsis"]:e-2<=t?(r=h(e-2,e)).unshift("ellipsis"):r=["ellipsis",t,"ellipsis"]:t<=o?((r=h(0,n-i)).push("ellipsis"),a&&r.push(e-1)):e-1-o<=t?((r=h(e-(n-i),e)).unshift("ellipsis"),a&&r.unshift(0)):((r=h(t-o+i,t+o-l)).push("ellipsis"),r.unshift("ellipsis"),a&&(r.push(e-1),r.unshift(0))),r}$.feature.register("search",function(n,t){var e,a,r,o,i,l,s,u,c,d;return n.oFeatures.bFilter?(e=n.oClasses.search,a=n.sTableId,c=n.oLanguage,r=n.oPreviousSearch,o='<input type="search" class="'+e.input+'"/>',-1===(t=V.extend({placeholder:c.sSearchPlaceholder,text:c.sSearch},t)).text.indexOf("_INPUT_")&&(t.text+="_INPUT_"),t.text=ee(n,t.text),c=t.text.match(/_INPUT_$/),s=t.text.match(/^_INPUT_/),i=t.text.replace(/_INPUT_/,""),l="<label>"+t.text+"</label>",s?l="_INPUT_<label>"+i+"</label>":c&&(l="<label>"+i+"</label>_INPUT_"),(s=V("<div>").addClass(e.container).append(l.replace(/_INPUT_/,o))).find("label").attr("for","dt-search-"+Le),s.find("input").attr("id","dt-search-"+Le),Le++,u=function(t){var e=this.value;r.return&&"Enter"!==t.key||e!=r.search&&(r.search=e,Nt(n,r),n._iDisplayStart=0,S(n))},c=null!==n.searchDelay?n.searchDelay:0,d=V("input",s).val(r.search).attr("placeholder",t.placeholder).on("keyup.DT search.DT input.DT paste.DT cut.DT",c?$.util.debounce(u,c):u).on("mouseup.DT",function(t){setTimeout(function(){u.call(d[0],t)},10)}).on("keypress.DT",function(t){if(13==t.keyCode)return!1}).attr("aria-controls",a),V(n.nTable).on("search.dt.DT",function(t,e){n===e&&d[0]!==_.activeElement&&d.val("function"!=typeof r.search?r.search:"")}),s):null},"f"),$.feature.register("paging",function(t,e){if(!t.oFeatures.bPaginate)return null;(e=V.extend({buttons:$.ext.pager.numbers_length,type:t.sPaginationType,boundaryNumbers:!0},e)).numbers&&(e.buttons=e.numbers);function n(){!function t(e,n,a){if(!e._bInitComplete)return;var r=$.ext.pager[a.type],o=e.oLanguage.oAria.paginate||{},i=e._iDisplayStart,l=e._iDisplayLength,s=e.fnRecordsDisplay(),u=-1===l,c=u?0:Math.ceil(i/l),d=u?1:Math.ceil(s/l),f=r().map(function(t){return"numbers"===t?Ne(c,d,a.buttons,a.boundaryNumbers):t}).flat();var h=[];for(var p=0;p<f.length;p++){var g=f[p],m=Fe(e,g,c,d),v=te(e,"pagingButton")(e,g,m.display,m.active,m.disabled);V(v.clicker).attr({"aria-controls":e.sTableId,"aria-disabled":m.disabled?"true":null,"aria-current":m.active?"page":null,"aria-label":o[g],"data-dt-idx":g,tabIndex:m.disabled?-1:e.iTabIndex}),"number"!=typeof g&&V(v.clicker).addClass(g),Qt(v.clicker,{action:g},function(t){t.preventDefault(),Ht(e,t.data.action,!0)}),h.push(v.display)}i=te(e,"pagingContainer")(e,h);u=n.find(_.activeElement).data("dt-idx");n.empty().append(i);void 0!==u&&n.find("[data-dt-idx="+u+"]").trigger("focus");h.length&&1<a.numbers&&V(n).height()>=2*V(h[0]).outerHeight()-10&&t(e,n,V.extend({},a,{numbers:a.numbers-2}))}(t,a,e)}var a=V("<div/>").addClass(t.oClasses.paging.container+" paging_"+e.type);return t.aoDrawCallback.push(n),V(t.nTable).on("column-sizing.dt.DT",n),a},"p");var je=0;return $.feature.register("pageLength",function(a,t){var e=a.oFeatures;if(!e.bPaginate||!e.bLengthChange)return null;t=V.extend({menu:a.aLengthMenu,text:a.oLanguage.sLengthMenu},t);var e=a.oClasses.length,n=a.sTableId,r=t.menu,o=[],i=[];if(Array.isArray(r[0]))o=r[0],i=r[1];else for(p=0;p<r.length;p++)V.isPlainObject(r[p])?(o.push(r[p].value),i.push(r[p].label)):(o.push(r[p]),i.push(r[p]));for(var l=t.text.match(/_MENU_$/),s=t.text.match(/^_MENU_/),u=t.text.replace(/_MENU_/,""),t="<label>"+t.text+"</label>",c=(s?t="_MENU_<label>"+u+"</label>":l&&(t="<label>"+u+"</label>_MENU_"),V("<div/>").addClass(e.container).append(t.replace("_MENU_","<span></span>"))),d=[],f=(c.find("label")[0].childNodes.forEach(function(t){t.nodeType===Node.TEXT_NODE&&d.push({el:t,text:t.textContent})}),function(e){d.forEach(function(t){t.el.textContent=ee(a,t.text,e)})}),h=V("<select/>",{name:n+"_length","aria-controls":n,class:e.select}),p=0;p<o.length;p++)h[0][p]=new Option("number"==typeof i[p]?a.fnFormatNumber(i[p]):i[p],o[p]);return c.find("label").attr("for","dt-length-"+je),h.attr("id","dt-length-"+je),je++,c.find("span").replaceWith(h),V("select",c).val(a._iDisplayLength).on("change.DT",function(){Mt(a,V(this).val()),S(a)}),V(a.nTable).on("length.dt.DT",function(t,e,n){a===e&&(V("select",c).val(n),f(n))}),f(a._iDisplayLength),c},"l"),((V.fn.dataTable=$).$=V).fn.dataTableSettings=$.settings,V.fn.dataTableExt=$.ext,V.fn.DataTable=function(t){return V(this).dataTable(t).api()},V.each($,function(t,e){V.fn.DataTable[t]=e}),$}); |
|
0 | 5 |
\ No newline at end of file |
0 | 8 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,47 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+declare(strict_types=1); |
|
4 |
+ |
|
5 |
+/* |
|
6 |
+ * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
+ * |
|
8 |
+ * @package contao-member-extension-bundle |
|
9 |
+ * @license MIT |
|
10 |
+ * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
+ * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
+ * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
+ * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
+ */ |
|
15 |
+ |
|
16 |
+namespace Oveleon\ContaoMemberExtensionBundle\Controller\FrontendModule; |
|
17 |
+ |
|
18 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule; |
|
19 |
+use Contao\FrontendUser; |
|
20 |
+use Contao\MemberModel; |
|
21 |
+use Contao\ModuleModel; |
|
22 |
+use Contao\System; |
|
23 |
+use Contao\Template; |
|
24 |
+use Oveleon\ContaoMemberExtensionBundle\Member; |
|
25 |
+use Symfony\Component\HttpFoundation\Request; |
|
26 |
+use Symfony\Component\HttpFoundation\Response; |
|
27 |
+ |
|
28 |
+#[AsFrontendModule(category: 'user', template: 'memberExtension_avatar')] |
|
29 |
+class AvatarController extends MemberExtensionController |
|
30 |
+{ |
|
31 |
+ protected function getResponse(Template $template, ModuleModel $model, Request $request): Response |
|
32 |
+ { |
|
33 |
+ $container = System::getContainer(); |
|
34 |
+ |
|
35 |
+ // Return if there is no logged-in user |
|
36 |
+ if ( |
|
37 |
+ !$container->get('contao.security.token_checker')->hasFrontendUser() || |
|
38 |
+ null === ($member = MemberModel::findByPk(FrontendUser::getInstance()->id)) |
|
39 |
+ ) { |
|
40 |
+ return new Response(); |
|
41 |
+ } |
|
42 |
+ |
|
43 |
+ Member::parseMemberAvatar($member, $template, $model->imgSize); |
|
44 |
+ |
|
45 |
+ return $template->getResponse(); |
|
46 |
+ } |
|
47 |
+} |
0 | 48 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,94 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+declare(strict_types=1); |
|
4 |
+ |
|
5 |
+/* |
|
6 |
+ * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
+ * |
|
8 |
+ * @package contao-member-extension-bundle |
|
9 |
+ * @license MIT |
|
10 |
+ * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
+ * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
+ * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
+ * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
+ */ |
|
15 |
+ |
|
16 |
+namespace Oveleon\ContaoMemberExtensionBundle\Controller\FrontendModule; |
|
17 |
+ |
|
18 |
+use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController; |
|
19 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule; |
|
20 |
+use Contao\CoreBundle\Exception\RedirectResponseException; |
|
21 |
+use Contao\FrontendUser; |
|
22 |
+use Contao\Input; |
|
23 |
+use Contao\MemberModel; |
|
24 |
+use Contao\ModuleModel; |
|
25 |
+use Contao\StringUtil; |
|
26 |
+use Contao\System; |
|
27 |
+use Contao\Template; |
|
28 |
+use Exception; |
|
29 |
+use Oveleon\ContaoMemberExtensionBundle\Member; |
|
30 |
+use Symfony\Component\HttpFoundation\Request; |
|
31 |
+use Symfony\Component\HttpFoundation\Response; |
|
32 |
+ |
|
33 |
+#[AsFrontendModule(DeleteAvatarController::TYPE, category:'user', template:'memberExtension_deleteAvatar')] |
|
34 |
+class DeleteAvatarController extends AbstractFrontendModuleController |
|
35 |
+{ |
|
36 |
+ const TYPE = 'deleteAvatar'; |
|
37 |
+ |
|
38 |
+ /** |
|
39 |
+ * @throws Exception |
|
40 |
+ */ |
|
41 |
+ protected function getResponse(Template $template, ModuleModel $model, Request $request): Response |
|
42 |
+ { |
|
43 |
+ $container = System::getContainer(); |
|
44 |
+ |
|
45 |
+ // Return if there is no logged-in user |
|
46 |
+ if ( |
|
47 |
+ !$container->get('contao.security.token_checker')->hasFrontendUser() || |
|
48 |
+ null === ($member = MemberModel::findByPk(FrontendUser::getInstance()->id)) |
|
49 |
+ ) { |
|
50 |
+ return new Response(); |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ // Confirmation message |
|
54 |
+ $session = $container->get('request_stack')->getSession(); |
|
55 |
+ $flashBag = $session->getFlashBag(); |
|
56 |
+ |
|
57 |
+ if (!($session->isStarted() && $flashBag->has('mod_avatar_deleted')) && !$member->avatar) |
|
58 |
+ { |
|
59 |
+ return new Response(); |
|
60 |
+ } |
|
61 |
+ |
|
62 |
+ $strFormId = 'deleteAvatar_' . $model->id; |
|
63 |
+ |
|
64 |
+ // Get form submit |
|
65 |
+ if (Input::post('FORM_SUBMIT') == $strFormId) |
|
66 |
+ { |
|
67 |
+ // Delete avatar if it exists |
|
68 |
+ if (!!$member->avatar) |
|
69 |
+ { |
|
70 |
+ Member::deleteAvatar($member); |
|
71 |
+ // Unset avatar |
|
72 |
+ $member->avatar = null; |
|
73 |
+ $member->save(); |
|
74 |
+ |
|
75 |
+ // Set message for deletion feedback |
|
76 |
+ $flashBag->set('mod_avatar_deleted', $GLOBALS['TL_LANG']['MSC']['avatarDeleted'] ?? ''); |
|
77 |
+ |
|
78 |
+ throw new RedirectResponseException($request->getRequestUri()); |
|
79 |
+ } |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ // Confirmation message |
|
83 |
+ if ($session->isStarted() && $flashBag->has('mod_avatar_deleted')) { |
|
84 |
+ $arrMessages = $flashBag->get('mod_avatar_deleted'); |
|
85 |
+ $template->message = $arrMessages[0]; |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ $template->formId = $strFormId; |
|
89 |
+ $template->slabel = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['deleteAvatar'] ?? ''); |
|
90 |
+ $template->requestToken = System::getContainer()->get('contao.csrf.token_manager')->getDefaultTokenValue(); |
|
91 |
+ |
|
92 |
+ return $template->getResponse(); |
|
93 |
+ } |
|
94 |
+} |
0 | 95 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,203 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+declare(strict_types=1); |
|
4 |
+ |
|
5 |
+/* |
|
6 |
+ * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
+ * |
|
8 |
+ * @package contao-member-extension-bundle |
|
9 |
+ * @license MIT |
|
10 |
+ * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
+ * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
+ * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
+ * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
+ */ |
|
15 |
+ |
|
16 |
+namespace Oveleon\ContaoMemberExtensionBundle\Controller\FrontendModule; |
|
17 |
+ |
|
18 |
+use Contao\Config; |
|
19 |
+use Contao\Controller; |
|
20 |
+use Contao\CoreBundle\ContaoCoreBundle; |
|
21 |
+use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController; |
|
22 |
+use Contao\CoreBundle\EventListener\Widget\HttpUrlListener; |
|
23 |
+use Contao\Date; |
|
24 |
+use Contao\Environment; |
|
25 |
+use Contao\FrontendTemplate; |
|
26 |
+use Contao\MemberGroupModel; |
|
27 |
+use Contao\MemberModel; |
|
28 |
+use Contao\Model; |
|
29 |
+use Contao\ModuleModel; |
|
30 |
+use Contao\PageModel; |
|
31 |
+use Contao\StringUtil; |
|
32 |
+use Contao\System; |
|
33 |
+use Oveleon\ContaoMemberExtensionBundle\Member; |
|
34 |
+ |
|
35 |
+abstract class MemberExtensionController extends AbstractFrontendModuleController |
|
36 |
+{ |
|
37 |
+ private ModuleModel $model; |
|
38 |
+ |
|
39 |
+ protected bool $isTable = false; |
|
40 |
+ |
|
41 |
+ protected array $memberFields = []; |
|
42 |
+ |
|
43 |
+ protected array $labels = []; |
|
44 |
+ |
|
45 |
+ protected function parseMemberTemplate(MemberModel|Model $objMember, FrontendTemplate $objTemplate, ModuleModel $model): string |
|
46 |
+ { |
|
47 |
+ System::loadLanguageFile('default'); |
|
48 |
+ System::loadLanguageFile('tl_member'); |
|
49 |
+ System::loadLanguageFile('countries'); |
|
50 |
+ System::loadLanguageFile('languages'); |
|
51 |
+ |
|
52 |
+ $this->model = $model; |
|
53 |
+ |
|
54 |
+ $arrFields = []; |
|
55 |
+ |
|
56 |
+ // HOOK: modify the member details |
|
57 |
+ if (isset($GLOBALS['TL_HOOKS']['parseMemberTemplate']) && \is_array($GLOBALS['TL_HOOKS']['parseMemberTemplate'])) |
|
58 |
+ { |
|
59 |
+ foreach ($GLOBALS['TL_HOOKS']['parseMemberTemplate'] as $callback) |
|
60 |
+ { |
|
61 |
+ System::importStatic($callback[0])->{$callback[1]}($objMember, $this->memberFields, $objTemplate, $model, $this); |
|
62 |
+ } |
|
63 |
+ } |
|
64 |
+ |
|
65 |
+ foreach ($this->memberFields as $field) |
|
66 |
+ { |
|
67 |
+ switch ($field) |
|
68 |
+ { |
|
69 |
+ /*case 'homeDir': |
|
70 |
+ case 'assignDir': |
|
71 |
+ break;*/ |
|
72 |
+ |
|
73 |
+ case 'avatar': |
|
74 |
+ Member::parseMemberAvatar($objMember, $objTemplate, $model->imgSize); |
|
75 |
+ break; |
|
76 |
+ |
|
77 |
+ default: |
|
78 |
+ if ($varValue = $objMember->{$field}) |
|
79 |
+ { |
|
80 |
+ if (\is_array(($arrValue = StringUtil::deserialize($varValue)))) |
|
81 |
+ { |
|
82 |
+ $arrFields[$field] = implode(",", $arrValue); |
|
83 |
+ } |
|
84 |
+ else |
|
85 |
+ { |
|
86 |
+ $arrFields[$field] = $varValue; |
|
87 |
+ } |
|
88 |
+ |
|
89 |
+ if ($model->ext_parseDetails) |
|
90 |
+ { |
|
91 |
+ self::parseMemberDetails($arrFields, $field, $varValue); |
|
92 |
+ } |
|
93 |
+ } |
|
94 |
+ } |
|
95 |
+ } |
|
96 |
+ |
|
97 |
+ $returnFields = []; |
|
98 |
+ |
|
99 |
+ foreach ($this->memberFields as $value) |
|
100 |
+ { |
|
101 |
+ $returnFields[$value] = $arrFields[$value] ?? ''; |
|
102 |
+ } |
|
103 |
+ |
|
104 |
+ $labels = array_keys($returnFields); |
|
105 |
+ |
|
106 |
+ $this->parsedLabels = true; |
|
107 |
+ $this->labels = array_map(fn($field) => $GLOBALS['TL_LANG']['tl_member'][$field][0] ?? $field, $labels);; |
|
108 |
+ |
|
109 |
+ $objTemplate->fields = $returnFields; |
|
110 |
+ |
|
111 |
+ if ($model->jumpTo) |
|
112 |
+ { |
|
113 |
+ $objTemplate->link = $this->generateMemberUrl($objMember); |
|
114 |
+ } |
|
115 |
+ |
|
116 |
+ return $objTemplate->parse(); |
|
117 |
+ } |
|
118 |
+ |
|
119 |
+ protected function generateMemberUrl(MemberModel $objMember): string |
|
120 |
+ { |
|
121 |
+ $objPage = PageModel::findPublishedById($this->model->jumpTo); |
|
122 |
+ |
|
123 |
+ if (!$objPage instanceof PageModel) |
|
124 |
+ { |
|
125 |
+ $strLink = StringUtil::ampersand(Environment::get('request')); |
|
126 |
+ } |
|
127 |
+ else |
|
128 |
+ { |
|
129 |
+ $params = ($this->useAutoItem() ? '/' : '/items/') . ($this->model->ext_memberAlias ? ($objMember->alias ?: $objMember->id) : $objMember->id); |
|
130 |
+ $strLink = StringUtil::ampersand($objPage->getFrontendUrl($params)); |
|
131 |
+ } |
|
132 |
+ |
|
133 |
+ return $strLink; |
|
134 |
+ } |
|
135 |
+ |
|
136 |
+ protected function parseMemberDetails(&$arrFields, $field, $value): void |
|
137 |
+ { |
|
138 |
+ $strReturn = !$this->isTable ? sprintf('<span class="label">%s: </span>',$GLOBALS['TL_LANG']['tl_member'][$field][0] ?? null) : ''; |
|
139 |
+ |
|
140 |
+ if (!\is_array(($arrValue = StringUtil::deserialize($value)))) |
|
141 |
+ { |
|
142 |
+ Controller::loadDataContainer('tl_member'); |
|
143 |
+ |
|
144 |
+ if (!empty($rgxp = $GLOBALS['TL_DCA']['tl_member']['fields'][$field]['eval']['rgxp'] ?? [])) |
|
145 |
+ { |
|
146 |
+ switch ($rgxp) { |
|
147 |
+ case HttpUrlListener::RGXP_NAME: |
|
148 |
+ $strReturn .= '<a href="' . $value . '" title="' . $value . '" target="blank noopener" rel="noreferer">' . preg_replace('/https?:\/\/|www.|\/$/', '', $value) . '</a>'; |
|
149 |
+ break; |
|
150 |
+ |
|
151 |
+ case 'phone': |
|
152 |
+ $strTel = preg_replace('/[^a-z\d+]/i', '', (string)$value); |
|
153 |
+ $strReturn .= '<a href="tel:' . $strTel . '" title="' . $value . '">' . $value . '</a>'; |
|
154 |
+ break; |
|
155 |
+ |
|
156 |
+ case 'email': |
|
157 |
+ $strEmail = StringUtil::encodeEmail($value); |
|
158 |
+ $strReturn .= '<a href="mailto:' . $strEmail . '" title="' . $strEmail . '">' . preg_replace('/\?.*$/', '', $strEmail) . '</a>'; |
|
159 |
+ break; |
|
160 |
+ |
|
161 |
+ case 'date': |
|
162 |
+ $strReturn .= Date::parse(Config::get('dateFormat'), $value) ?? $value; |
|
163 |
+ break; |
|
164 |
+ |
|
165 |
+ default: |
|
166 |
+ $strReturn .= $value; |
|
167 |
+ } |
|
168 |
+ } |
|
169 |
+ else { |
|
170 |
+ $strReturn .= match ($field) { |
|
171 |
+ 'gender' => $GLOBALS['TL_LANG']['MSC'][$value] ?? $value, |
|
172 |
+ 'country' => $GLOBALS['TL_LANG']['CNT'][$value] ?? $value, |
|
173 |
+ 'language' => $GLOBALS['TL_LANG']['LNG'][$value] ?? $value, |
|
174 |
+ default => $value |
|
175 |
+ }; |
|
176 |
+ } |
|
177 |
+ } |
|
178 |
+ else if ('groups' === $field) |
|
179 |
+ { |
|
180 |
+ $arrReturn = []; |
|
181 |
+ |
|
182 |
+ foreach ($arrValue as $value) |
|
183 |
+ { |
|
184 |
+ $arrReturn[] = MemberGroupModel::findById($value)->name; |
|
185 |
+ } |
|
186 |
+ |
|
187 |
+ $strReturn .= implode(", ", $arrReturn); |
|
188 |
+ } |
|
189 |
+ |
|
190 |
+ $arrFields[$field] = $strReturn; |
|
191 |
+ } |
|
192 |
+ |
|
193 |
+ /** |
|
194 |
+ * Checks weather auto_item should be used to provide BC |
|
195 |
+ * |
|
196 |
+ * @deprecated - To be removed when contao 4.13 support ends |
|
197 |
+ * @internal |
|
198 |
+ */ |
|
199 |
+ protected function useAutoItem(): bool |
|
200 |
+ { |
|
201 |
+ return version_compare(ContaoCoreBundle::getVersion(), '5', '<') ? Config::get('useAutoItem') : true; |
|
202 |
+ } |
|
203 |
+} |
0 | 204 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,284 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+declare(strict_types=1); |
|
4 |
+ |
|
5 |
+/* |
|
6 |
+ * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
+ * |
|
8 |
+ * @package contao-member-extension-bundle |
|
9 |
+ * @license MIT |
|
10 |
+ * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
+ * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
+ * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
+ * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
+ */ |
|
15 |
+ |
|
16 |
+namespace Oveleon\ContaoMemberExtensionBundle\Controller\FrontendModule; |
|
17 |
+ |
|
18 |
+use Contao\Config; |
|
19 |
+use Contao\Controller; |
|
20 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule; |
|
21 |
+use Contao\CoreBundle\Exception\PageNotFoundException; |
|
22 |
+use Contao\Date; |
|
23 |
+use Contao\Environment; |
|
24 |
+use Contao\FrontendTemplate; |
|
25 |
+use Contao\Input; |
|
26 |
+use Contao\MemberModel; |
|
27 |
+use Contao\Model\Collection; |
|
28 |
+use Contao\ModuleModel; |
|
29 |
+use Contao\Pagination; |
|
30 |
+use Contao\StringUtil; |
|
31 |
+use Contao\System; |
|
32 |
+use Contao\Template; |
|
33 |
+use Contao\Widget; |
|
34 |
+use Symfony\Component\HttpFoundation\Request; |
|
35 |
+use Symfony\Component\HttpFoundation\Response; |
|
36 |
+ |
|
37 |
+#[AsFrontendModule(MemberListController::TYPE, category: 'user', template: 'mod_memberList')] |
|
38 |
+class MemberListController extends MemberExtensionController |
|
39 |
+{ |
|
40 |
+ const TYPE = 'memberList'; |
|
41 |
+ private ModuleModel $model; |
|
42 |
+ private Template $template; |
|
43 |
+ |
|
44 |
+ private array $memberFilter = []; |
|
45 |
+ |
|
46 |
+ protected function getResponse(Template $template, ModuleModel $model, Request $request): Response |
|
47 |
+ { |
|
48 |
+ $this->model = $model; |
|
49 |
+ $this->template = $template; |
|
50 |
+ |
|
51 |
+ $limit = null; |
|
52 |
+ $offset = 0; |
|
53 |
+ |
|
54 |
+ $arrGroups = StringUtil::deserialize($model->ext_groups); |
|
55 |
+ |
|
56 |
+ if (empty($arrGroups) || !\is_array($arrGroups)) |
|
57 |
+ { |
|
58 |
+ $template->empty = $GLOBALS['TL_LANG']['MSC']['emptyMemberList']; |
|
59 |
+ $template->getResponse(); |
|
60 |
+ } |
|
61 |
+ |
|
62 |
+ if ($this->model->ext_activateFilter) |
|
63 |
+ { |
|
64 |
+ $this->parseFilters(); |
|
65 |
+ } |
|
66 |
+ |
|
67 |
+ $memberTemplate = new FrontendTemplate($model->memberListTpl ?: 'memberExtension_list_default'); |
|
68 |
+ |
|
69 |
+ if ( |
|
70 |
+ str_starts_with($this->template->getName(), 'mod_' . self::TYPE . '_table') && |
|
71 |
+ str_starts_with($memberTemplate->getName(), 'memberExtension_list_row') |
|
72 |
+ ) { |
|
73 |
+ $this->isTable = true; |
|
74 |
+ } |
|
75 |
+ |
|
76 |
+ $intTotal = 0; |
|
77 |
+ $arrMembers = []; |
|
78 |
+ |
|
79 |
+ if (null !== ($objMembers = $this->getMembers())) |
|
80 |
+ { |
|
81 |
+ foreach ($objMembers as $objMember) |
|
82 |
+ { |
|
83 |
+ if ( |
|
84 |
+ !$this->checkMemberGroups($arrGroups, $objMember) || |
|
85 |
+ ($this->model->ext_activateFilter && $this->excludeMember($objMember)) |
|
86 |
+ ) { |
|
87 |
+ continue; |
|
88 |
+ } |
|
89 |
+ |
|
90 |
+ $intTotal += 1; |
|
91 |
+ |
|
92 |
+ $this->memberFields = StringUtil::deserialize($model->memberFields, true); |
|
93 |
+ $memberTemplate->setData($objMember->row()); |
|
94 |
+ |
|
95 |
+ $arrMembers[] = $this->parseMemberTemplate($objMember, $memberTemplate, $model); |
|
96 |
+ } |
|
97 |
+ } |
|
98 |
+ |
|
99 |
+ $total = $intTotal - $offset; |
|
100 |
+ |
|
101 |
+ if ($model->numberOfItems > 0) |
|
102 |
+ { |
|
103 |
+ $limit = $model->numberOfItems; |
|
104 |
+ } |
|
105 |
+ |
|
106 |
+ if ($model->perPage > 0 && (!isset($limit) || $model->numberOfItems > $model->perPage) && !$this->isTable) |
|
107 |
+ { |
|
108 |
+ if (isset($limit)) |
|
109 |
+ { |
|
110 |
+ $total = min($limit, $total); |
|
111 |
+ } |
|
112 |
+ |
|
113 |
+ $id = 'page_n' . $model->id; |
|
114 |
+ $page = Input::get($id) ?? 1; |
|
115 |
+ |
|
116 |
+ if ($page < 1 || $page > max(ceil($total/$model->perPage), 1)) |
|
117 |
+ { |
|
118 |
+ throw new PageNotFoundException('Page not found: ' . Environment::get('uri')); |
|
119 |
+ } |
|
120 |
+ |
|
121 |
+ $limit = $model->perPage; |
|
122 |
+ $offset += (max($page, 1) - 1) * $model->perPage; |
|
123 |
+ $skip = 0; |
|
124 |
+ |
|
125 |
+ if ($offset + $limit > $total + $skip) |
|
126 |
+ { |
|
127 |
+ $limit = $total + $skip - $offset; |
|
128 |
+ } |
|
129 |
+ |
|
130 |
+ $arrMembers = \array_slice($arrMembers, $offset, ((int) $limit ?: $intTotal), true); |
|
131 |
+ |
|
132 |
+ $objPagination = new Pagination($total, $model->perPage, Config::get('maxPaginationLinks'), $id); |
|
133 |
+ $template->pagination = $objPagination->generate("\n "); |
|
134 |
+ } |
|
135 |
+ |
|
136 |
+ if (empty($arrMembers)) |
|
137 |
+ { |
|
138 |
+ $template->empty = $GLOBALS['TL_LANG']['MSC']['emptyMemberList']; |
|
139 |
+ } |
|
140 |
+ |
|
141 |
+ $template->hasDetailPage = !!$model->jumpTo; |
|
142 |
+ |
|
143 |
+ $template->total = $total; |
|
144 |
+ $template->labels = $this->labels; |
|
145 |
+ $template->members = $arrMembers; |
|
146 |
+ |
|
147 |
+ return $template->getResponse(); |
|
148 |
+ } |
|
149 |
+ |
|
150 |
+ private function checkMemberGroups(array $arrGroups, MemberModel $objMember): bool |
|
151 |
+ { |
|
152 |
+ if (empty($arrGroups)) |
|
153 |
+ { |
|
154 |
+ return false; |
|
155 |
+ } |
|
156 |
+ |
|
157 |
+ $arrMemberGroups = StringUtil::deserialize($objMember->groups); |
|
158 |
+ |
|
159 |
+ if (!\is_array($arrMemberGroups) || !\count(array_intersect($arrGroups, $arrMemberGroups))) |
|
160 |
+ { |
|
161 |
+ return false; |
|
162 |
+ } |
|
163 |
+ |
|
164 |
+ return true; |
|
165 |
+ } |
|
166 |
+ |
|
167 |
+ private function getMembers(): Collection|MemberModel|null |
|
168 |
+ { |
|
169 |
+ $t = MemberModel::getTable(); |
|
170 |
+ $time = Date::floorToMinute(); |
|
171 |
+ |
|
172 |
+ $arrColumns = ["$t.disable='' AND ($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'$time') "]; |
|
173 |
+ $arrOptions = []; |
|
174 |
+ |
|
175 |
+ if (!!$orderField = $this->model->ext_orderField) |
|
176 |
+ { |
|
177 |
+ $arrOptions['order'] = "$t.$orderField "; |
|
178 |
+ } |
|
179 |
+ |
|
180 |
+ switch ($this->model->ext_order) |
|
181 |
+ { |
|
182 |
+ case 'order_random': |
|
183 |
+ $arrOptions['order'] = "RAND()"; |
|
184 |
+ |
|
185 |
+ break; |
|
186 |
+ |
|
187 |
+ case 'order_desc': |
|
188 |
+ if (isset($arrOptions['order'])) { |
|
189 |
+ $arrOptions['order'] .= "DESC "; |
|
190 |
+ } |
|
191 |
+ |
|
192 |
+ break; |
|
193 |
+ |
|
194 |
+ case 'order_asc': |
|
195 |
+ default: |
|
196 |
+ break; |
|
197 |
+ } |
|
198 |
+ |
|
199 |
+ // Hook modify the member results |
|
200 |
+ if (isset($GLOBALS['TL_HOOKS']['getMembers']) && \is_array($GLOBALS['TL_HOOKS']['getMembers'])) |
|
201 |
+ { |
|
202 |
+ foreach ($GLOBALS['TL_HOOKS']['getMembers'] as $callback) |
|
203 |
+ { |
|
204 |
+ System::importStatic($callback[0])->{$callback[1]}($arrColumns, $arrOptions, $this); |
|
205 |
+ } |
|
206 |
+ } |
|
207 |
+ |
|
208 |
+ return MemberModel::findBy($arrColumns, null, $arrOptions); |
|
209 |
+ } |
|
210 |
+ |
|
211 |
+ private function excludeMember(MemberModel $member): bool |
|
212 |
+ { |
|
213 |
+ foreach ($this->memberFilter as $condition) |
|
214 |
+ { |
|
215 |
+ if ($member->$condition !== '1') |
|
216 |
+ { |
|
217 |
+ return true; |
|
218 |
+ } |
|
219 |
+ } |
|
220 |
+ |
|
221 |
+ return false; |
|
222 |
+ } |
|
223 |
+ |
|
224 |
+ private function parseFilters(): void |
|
225 |
+ { |
|
226 |
+ Controller::loadDataContainer('tl_member'); |
|
227 |
+ System::loadLanguageFile('tl_member'); |
|
228 |
+ |
|
229 |
+ $filters = []; |
|
230 |
+ |
|
231 |
+ foreach ($GLOBALS['TL_DCA']['tl_member']['fields'] ?? [] as $fieldName => $fieldConfig) |
|
232 |
+ { |
|
233 |
+ $type = $fieldConfig['inputType'] ?? null; |
|
234 |
+ $filterable = $fieldConfig['eval']['feFilterable'] ?? null; |
|
235 |
+ |
|
236 |
+ if ('checkbox' === $type && $filterable) |
|
237 |
+ { |
|
238 |
+ $filters[] = $fieldName; |
|
239 |
+ } |
|
240 |
+ } |
|
241 |
+ |
|
242 |
+ if (!empty($filters)) |
|
243 |
+ { |
|
244 |
+ /** @var Widget $strClass */ |
|
245 |
+ if (null === ($strClass = $GLOBALS['TL_FFL']['checkbox'] ?? null)) |
|
246 |
+ { |
|
247 |
+ return; |
|
248 |
+ } |
|
249 |
+ |
|
250 |
+ $formId = 'memberListFilter_' . $this->model->id; |
|
251 |
+ |
|
252 |
+ $this->template->requestToken = System::getContainer()->get('contao.csrf.token_manager')->getDefaultTokenValue(); |
|
253 |
+ $this->template->filterFormId = $formId; |
|
254 |
+ |
|
255 |
+ foreach ($filters as $key => $filter) |
|
256 |
+ { |
|
257 |
+ $objWidget = new $strClass([ |
|
258 |
+ 'type' => 'checkbox', |
|
259 |
+ 'name' => $filter, |
|
260 |
+ 'id' => $filter . '_'. $this->model->id, |
|
261 |
+ 'options' => [[ |
|
262 |
+ 'default'=> '', |
|
263 |
+ 'value' => '1', |
|
264 |
+ 'label' => $GLOBALS['TL_LANG']['tl_member'][$filter][0] ?? $filters |
|
265 |
+ ]] |
|
266 |
+ ]); |
|
267 |
+ |
|
268 |
+ if (Input::post('FORM_SUBMIT') === $formId) |
|
269 |
+ { |
|
270 |
+ $objWidget->validate(); |
|
271 |
+ |
|
272 |
+ if (!!$objWidget->value) |
|
273 |
+ { |
|
274 |
+ $this->memberFilter[] = $objWidget->name; |
|
275 |
+ } |
|
276 |
+ } |
|
277 |
+ |
|
278 |
+ $filters[$key] = $objWidget->parse(); |
|
279 |
+ } |
|
280 |
+ } |
|
281 |
+ |
|
282 |
+ $this->template->filters = $filters; |
|
283 |
+ } |
|
284 |
+} |
0 | 285 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,104 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+declare(strict_types=1); |
|
4 |
+ |
|
5 |
+/* |
|
6 |
+ * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
+ * |
|
8 |
+ * @package contao-member-extension-bundle |
|
9 |
+ * @license MIT |
|
10 |
+ * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
+ * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
+ * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
+ * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
+ */ |
|
15 |
+ |
|
16 |
+namespace Oveleon\ContaoMemberExtensionBundle\Controller\FrontendModule; |
|
17 |
+ |
|
18 |
+use Contao\CoreBundle\ContaoCoreBundle; |
|
19 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule; |
|
20 |
+use Contao\CoreBundle\Exception\PageNotFoundException; |
|
21 |
+use Contao\Environment; |
|
22 |
+use Contao\FrontendTemplate; |
|
23 |
+use Contao\Input; |
|
24 |
+use Contao\MemberModel; |
|
25 |
+use Contao\ModuleModel; |
|
26 |
+use Contao\PageModel; |
|
27 |
+use Contao\StringUtil; |
|
28 |
+use Contao\System; |
|
29 |
+use Contao\Template; |
|
30 |
+use Symfony\Component\HttpFoundation\Request; |
|
31 |
+use Symfony\Component\HttpFoundation\Response; |
|
32 |
+ |
|
33 |
+#[AsFrontendModule(MemberReaderController::TYPE, category: 'user', template: 'mod_memberReader')] |
|
34 |
+class MemberReaderController extends MemberExtensionController |
|
35 |
+{ |
|
36 |
+ const TYPE = 'memberReader'; |
|
37 |
+ |
|
38 |
+ protected function getResponse(Template $template, ModuleModel $model, Request $request): Response |
|
39 |
+ { |
|
40 |
+ $auto_item = Input::get('auto_item'); |
|
41 |
+ |
|
42 |
+ if ( |
|
43 |
+ version_compare(ContaoCoreBundle::getVersion(), '5', '<') && |
|
44 |
+ !isset($_GET['items']) && |
|
45 |
+ isset($_GET['auto_item']) && |
|
46 |
+ $this->useAutoItem() |
|
47 |
+ ) { |
|
48 |
+ Input::setGet('member', Input::get('auto_item')); |
|
49 |
+ $auto_item = Input::get('member'); |
|
50 |
+ |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ if (null === $auto_item) |
|
54 |
+ { |
|
55 |
+ return new Response(); |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ $member = MemberModel::findByIdOrAlias($auto_item); |
|
59 |
+ |
|
60 |
+ // The member does not exist and is not deactivated |
|
61 |
+ if (null === $member || $member->disable) |
|
62 |
+ { |
|
63 |
+ throw new PageNotFoundException('Page not found: ' . Environment::get('uri')); |
|
64 |
+ } |
|
65 |
+ |
|
66 |
+ // Check for group intersection |
|
67 |
+ $arrGroups = StringUtil::deserialize($model->ext_groups); |
|
68 |
+ $memberGroups = StringUtil::deserialize($member->groups); |
|
69 |
+ |
|
70 |
+ if (empty($arrGroups) || !\is_array($arrGroups) || !\count(array_intersect($arrGroups, $memberGroups))) |
|
71 |
+ { |
|
72 |
+ throw new PageNotFoundException('Page not found: ' . Environment::get('uri')); |
|
73 |
+ } |
|
74 |
+ |
|
75 |
+ // Hook modify the member detail page |
|
76 |
+ if (isset($GLOBALS['TL_HOOKS']['parseMemberReader']) && \is_array($GLOBALS['TL_HOOKS']['parseMemberReader'])) |
|
77 |
+ { |
|
78 |
+ foreach ($GLOBALS['TL_HOOKS']['parseMemberReader'] as $callback) |
|
79 |
+ { |
|
80 |
+ System::importStatic($callback[0])->{$callback[1]}($member, $template, $model, $this); |
|
81 |
+ } |
|
82 |
+ } |
|
83 |
+ |
|
84 |
+ $this->memberFields = StringUtil::deserialize($model->memberFields, true); |
|
85 |
+ |
|
86 |
+ $memberTemplate = new FrontendTemplate($model->memberReaderTpl ?: 'memberExtension_reader_full'); |
|
87 |
+ $memberTemplate->setData($member->row()); |
|
88 |
+ |
|
89 |
+ if ($model->overviewPage) |
|
90 |
+ { |
|
91 |
+ $template->referer = PageModel::findById($model->overviewPage)->getFrontendUrl(); |
|
92 |
+ $template->back = $model->customLabel ?: $GLOBALS['TL_LANG']['MSC']['goBack']; |
|
93 |
+ } |
|
94 |
+ else |
|
95 |
+ { |
|
96 |
+ $template->referer = 'javascript:history.go(-1)'; |
|
97 |
+ $template->back = $GLOBALS['TL_LANG']['MSC']['goBack']; |
|
98 |
+ } |
|
99 |
+ |
|
100 |
+ $template->member = $this->parseMemberTemplate($member, $memberTemplate, $model); |
|
101 |
+ |
|
102 |
+ return $template->getResponse(); |
|
103 |
+ } |
|
104 |
+} |
... | ... |
@@ -24,9 +24,9 @@ class ContaoMemberExtensionExtension extends Extension |
24 | 24 |
{ |
25 | 25 |
$loader = new YamlFileLoader( |
26 | 26 |
$container, |
27 |
- new FileLocator(__DIR__.'/../../config') |
|
27 |
+ new FileLocator(__DIR__ . '/../../config') |
|
28 | 28 |
); |
29 | 29 |
|
30 |
- $loader->load('listener.yml'); |
|
30 |
+ $loader->load('services.yaml'); |
|
31 | 31 |
} |
32 | 32 |
} |
33 | 33 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,23 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Oveleon\ContaoMemberExtensionBundle\EventListener; |
|
4 |
+ |
|
5 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; |
|
6 |
+use Contao\MemberModel; |
|
7 |
+use Contao\Module; |
|
8 |
+use Exception; |
|
9 |
+use Oveleon\ContaoMemberExtensionBundle\Member; |
|
10 |
+ |
|
11 |
+#[AsHook('createNewUser')] |
|
12 |
+class CreateNewUserListener |
|
13 |
+{ |
|
14 |
+ /** |
|
15 |
+ * @throws Exception |
|
16 |
+ */ |
|
17 |
+ public function __invoke(int $userId, array $userData, Module $module): void |
|
18 |
+ { |
|
19 |
+ // Create avatar |
|
20 |
+ $objMember = MemberModel::findById($userId); |
|
21 |
+ Member::processAvatar($objMember, $userData); |
|
22 |
+ } |
|
23 |
+} |
0 | 24 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,44 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Oveleon\ContaoMemberExtensionBundle\EventListener\DataContainer; |
|
4 |
+ |
|
5 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsCallback; |
|
6 |
+use Contao\Database; |
|
7 |
+use Contao\DataContainer; |
|
8 |
+use Contao\MemberModel; |
|
9 |
+use Contao\System; |
|
10 |
+use Exception; |
|
11 |
+ |
|
12 |
+class MemberFieldsListener |
|
13 |
+{ |
|
14 |
+ /** |
|
15 |
+ * @throws Exception |
|
16 |
+ */ |
|
17 |
+ #[AsCallback(table: 'tl_member', target: 'fields.alias.save')] |
|
18 |
+ public function generateAlias($varValue, DataContainer $dc): string |
|
19 |
+ { |
|
20 |
+ $aliasExists = static function (string $alias) use ($dc): bool { |
|
21 |
+ $result = Database::getInstance() |
|
22 |
+ ->prepare("SELECT id FROM tl_member WHERE alias=? AND id!=?") |
|
23 |
+ ->execute($alias, $dc->id); |
|
24 |
+ |
|
25 |
+ return $result->numRows > 0; |
|
26 |
+ }; |
|
27 |
+ |
|
28 |
+ if (!$varValue) |
|
29 |
+ { |
|
30 |
+ // ToDo - use slug generator for aliases |
|
31 |
+ $varValue = str_replace(' ','-', $dc->activeRecord->firstname) . '_' . str_replace(' ','-', $dc->activeRecord->lastname) . ($aliasExists ? '_' . $dc->activeRecord->id : ''); |
|
32 |
+ } |
|
33 |
+ if (preg_match('/^[1-9]\d*$/', $varValue)) |
|
34 |
+ { |
|
35 |
+ throw new Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasNumeric'], $varValue)); |
|
36 |
+ } |
|
37 |
+ elseif ($aliasExists($varValue)) |
|
38 |
+ { |
|
39 |
+ throw new Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasExists'], $varValue)); |
|
40 |
+ } |
|
41 |
+ |
|
42 |
+ return $varValue; |
|
43 |
+ } |
|
44 |
+} |
0 | 45 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,51 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Oveleon\ContaoMemberExtensionBundle\EventListener\DataContainer; |
|
4 |
+ |
|
5 |
+use Contao\Controller; |
|
6 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsCallback; |
|
7 |
+use Contao\System; |
|
8 |
+ |
|
9 |
+class MemberFieldsOptionsListener |
|
10 |
+{ |
|
11 |
+ public function __construct() { |
|
12 |
+ Controller::loadDataContainer('tl_member'); |
|
13 |
+ System::loadLanguageFile('tl_member'); |
|
14 |
+ } |
|
15 |
+ |
|
16 |
+ #[AsCallback(table: 'tl_module', target: 'fields.ext_orderField.options')] |
|
17 |
+ public function getEditableMemberFields(): array |
|
18 |
+ { |
|
19 |
+ $fields = []; |
|
20 |
+ |
|
21 |
+ foreach ($GLOBALS['TL_DCA']['tl_member']['fields'] as $k => $v) |
|
22 |
+ { |
|
23 |
+ if ( |
|
24 |
+ !empty($v['inputType']) && |
|
25 |
+ $k !== 'avatar' && |
|
26 |
+ isset($v['eval']['feEditable']) && |
|
27 |
+ $v['eval']['feEditable'] === true |
|
28 |
+ ) { |
|
29 |
+ $fields[$k] = ($GLOBALS['TL_DCA']['tl_member']['fields'][$k]['label'][0] ?? $k) . ' ['.$k.']'; |
|
30 |
+ } |
|
31 |
+ } |
|
32 |
+ |
|
33 |
+ return $fields; |
|
34 |
+ } |
|
35 |
+ |
|
36 |
+ #[AsCallback(table: 'tl_module', target: 'fields.memberFields.options')] |
|
37 |
+ public function getMemberProperties(): array |
|
38 |
+ { |
|
39 |
+ $properties = []; |
|
40 |
+ |
|
41 |
+ foreach ($GLOBALS['TL_DCA']['tl_member']['fields'] as $k => $v) |
|
42 |
+ { |
|
43 |
+ if (!empty($v['inputType']) && $v['inputType'] !== 'password') |
|
44 |
+ { |
|
45 |
+ $properties[$k] = $GLOBALS['TL_DCA']['tl_member']['fields'][$k]['label'][0] ?? $k; |
|
46 |
+ } |
|
47 |
+ } |
|
48 |
+ |
|
49 |
+ return $properties; |
|
50 |
+ } |
|
51 |
+} |
0 | 52 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,39 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Oveleon\ContaoMemberExtensionBundle\EventListener\DataContainer; |
|
4 |
+ |
|
5 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsCallback; |
|
6 |
+use Contao\CoreBundle\Security\ContaoCorePermissions; |
|
7 |
+use Contao\DataContainer; |
|
8 |
+use Contao\Input; |
|
9 |
+use Contao\Message; |
|
10 |
+use Contao\ModuleModel; |
|
11 |
+use Contao\System; |
|
12 |
+ |
|
13 |
+class ModuleFieldsListener |
|
14 |
+{ |
|
15 |
+ #[AsCallback(table: 'tl_module', target: 'config.onload')] |
|
16 |
+ public function showJsLibraryHint(DataContainer $dc): void |
|
17 |
+ { |
|
18 |
+ if ($_POST || Input::get('act') != 'edit') |
|
19 |
+ { |
|
20 |
+ return; |
|
21 |
+ } |
|
22 |
+ |
|
23 |
+ $security = System::getContainer()->get('security.helper'); |
|
24 |
+ |
|
25 |
+ if ( |
|
26 |
+ !$security->isGranted(ContaoCorePermissions::USER_CAN_ACCESS_MODULE, 'themes') || |
|
27 |
+ !$security->isGranted(ContaoCorePermissions::USER_CAN_ACCESS_LAYOUTS) |
|
28 |
+ ) { |
|
29 |
+ return; |
|
30 |
+ } |
|
31 |
+ |
|
32 |
+ $objModule = ModuleModel::findByPk($dc->id); |
|
33 |
+ |
|
34 |
+ if (null !== $objModule && 'memberList' === $objModule->type && str_starts_with($objModule->customTpl, 'mod_memberList_table')) |
|
35 |
+ { |
|
36 |
+ Message::addInfo(sprintf(($GLOBALS['TL_LANG']['tl_module']['includeMemberListTable'] ?? null), 'memberExtension_list_row', 'j_datatables')); |
|
37 |
+ } |
|
38 |
+ } |
|
39 |
+} |
... | ... |
@@ -15,14 +15,15 @@ declare(strict_types=1); |
15 | 15 |
|
16 | 16 |
namespace Oveleon\ContaoMemberExtensionBundle\EventListener; |
17 | 17 |
|
18 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; |
|
19 |
+use Contao\CoreBundle\Security\Authentication\Token\TokenChecker; |
|
18 | 20 |
use Contao\FrontendTemplate; |
19 | 21 |
use Contao\FrontendUser; |
20 | 22 |
use Contao\Image\ResizeConfiguration; |
21 | 23 |
use Contao\MemberModel; |
22 |
-use Contao\System; |
|
23 |
-use Contao\CoreBundle\Framework\ContaoFramework; |
|
24 | 24 |
use Oveleon\ContaoMemberExtensionBundle\Member; |
25 | 25 |
|
26 |
+#[AsHook('replaceInsertTags')] |
|
26 | 27 |
class InsertTagsListener |
27 | 28 |
{ |
28 | 29 |
private const SUPPORTED_TAGS = [ |
... | ... |
@@ -30,62 +31,35 @@ class InsertTagsListener |
30 | 31 |
'avatar_url' |
31 | 32 |
]; |
32 | 33 |
|
33 |
- /** |
|
34 |
- * @var ContaoFramework |
|
35 |
- */ |
|
36 |
- private $framework; |
|
34 |
+ public function __construct(private readonly TokenChecker $tokenChecker) |
|
35 |
+ {} |
|
37 | 36 |
|
38 |
- public function __construct(ContaoFramework $framework) |
|
39 |
- { |
|
40 |
- $this->framework = $framework; |
|
41 |
- } |
|
42 |
- |
|
43 |
- /** |
|
44 |
- * @return string|false |
|
45 |
- */ |
|
46 |
- public function __invoke(string $tag, bool $useCache, $cacheValue, array $flags) |
|
37 |
+ public function __invoke(string $tag, bool $useCache, $cacheValue): string|false |
|
47 | 38 |
{ |
48 | 39 |
$elements = explode('::', $tag); |
49 | 40 |
$key = strtolower($elements[0]); |
50 | 41 |
|
51 |
- if (\in_array($key, self::SUPPORTED_TAGS, true)) { |
|
52 |
- return $this->replaceMemberInsertTag($key, $elements, $flags); |
|
42 |
+ if (in_array($key, self::SUPPORTED_TAGS, true)) |
|
43 |
+ { |
|
44 |
+ return $this->replaceMemberInsertTag($key, $elements); |
|
53 | 45 |
} |
54 | 46 |
|
55 | 47 |
return false; |
56 | 48 |
} |
57 | 49 |
|
58 |
- private function replaceMemberInsertTag(string $insertTag, array $elements, array $flags): string |
|
50 |
+ private function replaceMemberInsertTag(string $insertTag, array $elements): string |
|
59 | 51 |
{ |
60 |
- $this->framework->initialize(); |
|
61 |
- $tokenChecker = System::getContainer()->get('contao.security.token_checker'); |
|
52 |
+ $memberID = match ($elements[2]) { |
|
53 |
+ 'current' => $this->tokenChecker->hasFrontendUser() ? FrontendUser::getInstance()->id : '', |
|
54 |
+ default => is_numeric($elements[2]) ? $elements[2] : '', |
|
55 |
+ }; |
|
62 | 56 |
|
63 |
- if ($elements[1] !== 'member') |
|
57 |
+ if (!\is_numeric($memberID)) |
|
64 | 58 |
{ |
65 | 59 |
return ''; |
66 | 60 |
} |
67 | 61 |
|
68 |
- switch ($elements[2]) |
|
69 |
- { |
|
70 |
- |
|
71 |
- case 'current': |
|
72 |
- if (!$tokenChecker->hasFrontendUser()) |
|
73 |
- { |
|
74 |
- return ''; |
|
75 |
- } |
|
76 |
- $memberID = FrontendUser::getInstance()->id; |
|
77 |
- break; |
|
78 |
- |
|
79 |
- default: |
|
80 |
- if (!\is_numeric($elements[2])) |
|
81 |
- { |
|
82 |
- return ''; |
|
83 |
- } |
|
84 |
- $memberID = $elements[2]; |
|
85 |
- break; |
|
86 |
- } |
|
87 |
- |
|
88 |
- $objMember = MemberModel::findByPk($memberID); |
|
62 |
+ $member = MemberModel::findByPk($memberID); |
|
89 | 63 |
|
90 | 64 |
switch ($insertTag) |
91 | 65 |
{ |
... | ... |
@@ -93,19 +67,19 @@ class InsertTagsListener |
93 | 67 |
{ |
94 | 68 |
if (isset($elements[3])) |
95 | 69 |
{ |
96 |
- $strImgSize = $this->convertImgSize($elements[3]); |
|
70 |
+ $size = $this->convertImgSize($elements[3]); |
|
97 | 71 |
} |
98 | 72 |
|
99 |
- $objTemplate = new FrontendTemplate('memberExtension_image'); |
|
73 |
+ $memberTemplate = new FrontendTemplate('memberExtension_image'); |
|
100 | 74 |
|
101 |
- Member::parseMemberAvatar($objMember, $objTemplate, $strImgSize ?? null); |
|
75 |
+ Member::parseMemberAvatar($member, $memberTemplate, $size ?? null); |
|
102 | 76 |
|
103 |
- return $objTemplate->parse(); |
|
77 |
+ return $memberTemplate->parse(); |
|
104 | 78 |
} |
105 | 79 |
|
106 | 80 |
case 'avatar_url': |
107 | 81 |
{ |
108 |
- return Member::getMemberAvatarURL($objMember); |
|
82 |
+ return Member::getMemberAvatarURL($member); |
|
109 | 83 |
} |
110 | 84 |
} |
111 | 85 |
|
... | ... |
@@ -119,22 +93,17 @@ class InsertTagsListener |
119 | 93 |
return null; |
120 | 94 |
} |
121 | 95 |
|
122 |
- $mode = ''; |
|
96 |
+ list($intWidth, $intHeight, $mode) = array_pad(explode('x', $strSize),3, null); |
|
123 | 97 |
|
124 |
- $arrSizes = explode('x', $strSize); |
|
125 |
- if (isset($arrSizes[2])) |
|
126 |
- { |
|
127 |
- $mode = $arrSizes[2]; |
|
128 |
- array_splice($arrSizes,2); |
|
129 |
- } |
|
98 |
+ $arrSizes = [$intWidth, $intHeight]; |
|
130 | 99 |
|
131 | 100 |
$arrValidModes = [ |
132 | 101 |
ResizeConfiguration::MODE_BOX, |
133 |
- ResizeConfiguration::MODE_PROPORTIONAL, |
|
102 |
+ ResizeConfiguration::MODE_PROPORTIONAL, // To be removed when simultaneous C4/5 support ends |
|
134 | 103 |
ResizeConfiguration::MODE_CROP, |
135 | 104 |
]; |
136 | 105 |
|
137 |
- if (!empty($mode) && in_array($mode, $arrValidModes, true)) |
|
106 |
+ if (!!$mode && in_array($mode, $arrValidModes, true)) |
|
138 | 107 |
{ |
139 | 108 |
$arrSizes[] = $mode; |
140 | 109 |
} |
141 | 110 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,24 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+namespace Oveleon\ContaoMemberExtensionBundle\EventListener; |
|
4 |
+ |
|
5 |
+use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; |
|
6 |
+use Contao\FrontendUser; |
|
7 |
+use Contao\MemberModel; |
|
8 |
+use Contao\Module; |
|
9 |
+use Exception; |
|
10 |
+use Oveleon\ContaoMemberExtensionBundle\Member; |
|
11 |
+ |
|
12 |
+#[AsHook('updatePersonalData')] |
|
13 |
+class UpdatePersonalDataListener |
|
14 |
+{ |
|
15 |
+ /** |
|
16 |
+ * @throws Exception |
|
17 |
+ */ |
|
18 |
+ public function __invoke(FrontendUser $member, array $data, Module $module): void |
|
19 |
+ { |
|
20 |
+ // Update avatar of a member | Login |
|
21 |
+ $objMember = MemberModel::findById($member->id); |
|
22 |
+ Member::processAvatar($objMember, $data); |
|
23 |
+ } |
|
24 |
+} |
0 | 25 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,252 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+declare(strict_types=1); |
|
4 |
+ |
|
5 |
+/* |
|
6 |
+ * This file is part of Oveleon ContaoMemberExtension Bundle. |
|
7 |
+ * |
|
8 |
+ * @package contao-member-extension-bundle |
|
9 |
+ * @license MIT |
|
10 |
+ * @author Sebastian Zoglowek <https://github.com/zoglo> |
|
11 |
+ * @author Daniele Sciannimanica <https://github.com/doishub> |
|
12 |
+ * @author Fabian Ekert <https://github.com/eki89> |
|
13 |
+ * @copyright Oveleon <https://www.oveleon.de/> |
|
14 |
+ */ |
|
15 |
+ |
|
16 |
+namespace Oveleon\ContaoMemberExtensionBundle; |
|
17 |
+ |
|
18 |
+use Contao\Config; |
|
19 |
+use Contao\Dbafs; |
|
20 |
+use Contao\File; |
|
21 |
+use Contao\Files; |
|
22 |
+use Contao\FilesModel; |
|
23 |
+use Contao\FileUpload; |
|
24 |
+use Contao\MemberModel; |
|
25 |
+use Contao\StringUtil; |
|
26 |
+use Contao\System; |
|
27 |
+use Contao\Validator; |
|
28 |
+use Exception; |
|
29 |
+ |
|
30 |
+/** |
|
31 |
+ * Class Member |
|
32 |
+ * |
|
33 |
+ * @property int $avatar UUID of the avatar |
|
34 |
+ */ |
|
35 |
+class Member |
|
36 |
+{ |
|
37 |
+ const DEFAULT_PICTURE = 'bundles/contaomemberextension/assets/avatar.png'; |
|
38 |
+ const AVATAR_NAME = 'memberAvatar'; |
|
39 |
+ |
|
40 |
+ /** |
|
41 |
+ * Process avatar upload for a member |
|
42 |
+ * @throws Exception |
|
43 |
+ */ |
|
44 |
+ public static function processAvatar(?MemberModel $objMember, ?array $arrData): void |
|
45 |
+ { |
|
46 |
+ if (null === $objMember) |
|
47 |
+ { |
|
48 |
+ return; |
|
49 |
+ } |
|
50 |
+ |
|
51 |
+ $container = System::getContainer(); |
|
52 |
+ $request = $container->get('request_stack')->getCurrentRequest(); |
|
53 |
+ |
|
54 |
+ if (null === ($file = $request->files->get('avatar'))) |
|
55 |
+ { |
|
56 |
+ return; |
|
57 |
+ } |
|
58 |
+ |
|
59 |
+ $maxlength_kb = FileUpload::getMaxUploadSize(); |
|
60 |
+ //$maxlength_kb_readable = System::getReadableSize($maxlength_kb); |
|
61 |
+ |
|
62 |
+ // Sanitize the filename |
|
63 |
+ try { |
|
64 |
+ $fileName = StringUtil::sanitizeFileName($file->getClientOriginalName()); |
|
65 |
+ } catch (\InvalidArgumentException $e) { |
|
66 |
+ return; // ToDo: add error message for invalid characters |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ // Invalid file name |
|
70 |
+ if (!Validator::isValidFileName($fileName)) |
|
71 |
+ { |
|
72 |
+ return; // ToDo: add error message for invalid characters |
|
73 |
+ } |
|
74 |
+ |
|
75 |
+ // File was not uploaded |
|
76 |
+ if (!$path = $file->getRealPath()) |
|
77 |
+ { |
|
78 |
+ // ToDo: Add error messages |
|
79 |
+ /*if ($file['error'] == 1 || $file['error'] == 2) { // Add error message for maximum file size } |
|
80 |
+ elseif ($file['error'] == 3) { // Add error message for partial upload } |
|
81 |
+ elseif ($file['error'] > 0) { // Add error message for failed upload }*/ |
|
82 |
+ |
|
83 |
+ return; |
|
84 |
+ } |
|
85 |
+ |
|
86 |
+ // File is too big |
|
87 |
+ if ($file->getSize() > $maxlength_kb) |
|
88 |
+ { |
|
89 |
+ return; // ToDo: add error message for maximum file size |
|
90 |
+ } |
|
91 |
+ |
|
92 |
+ $objFile = new File($fileName); |
|
93 |
+ |
|
94 |
+ // File type is not allowed |
|
95 |
+ if (!\in_array($objFile->extension, $container->getParameter('contao.image.valid_extensions'))) |
|
96 |
+ { |
|
97 |
+ return; // ToDo: add error message for not allowed file type |
|
98 |
+ } |
|
99 |
+ |
|
100 |
+ if ( |
|
101 |
+ ($arrImageSize = getimagesize($path)) && |
|
102 |
+ ($arrImageSize[0] > Config::get('imageWidth') || $arrImageSize[1] > Config::get('imageHeight')) |
|
103 |
+ ) { |
|
104 |
+ return; |
|
105 |
+ } |
|
106 |
+ |
|
107 |
+ // Upload valid file type with no width and height -> svg |
|
108 |
+ |
|
109 |
+ // Don't upload if no homedir is assigned |
|
110 |
+ // ToDo: Create homedir? |
|
111 |
+ if (!$objMember->assignDir || !$objMember->homeDir) |
|
112 |
+ { |
|
113 |
+ return; // ToDo: add error message for no homedir |
|
114 |
+ } |
|
115 |
+ |
|
116 |
+ $intUploadFolder = $objMember->homeDir; |
|
117 |
+ |
|
118 |
+ $objUploadFolder = FilesModel::findByUuid($intUploadFolder); |
|
119 |
+ |
|
120 |
+ // The upload folder could not be found |
|
121 |
+ if ($objUploadFolder === null) |
|
122 |
+ { |
|
123 |
+ throw new Exception("Invalid upload folder ID $intUploadFolder"); |
|
124 |
+ } |
|
125 |
+ |
|
126 |
+ $strUploadFolder = $objUploadFolder->path; |
|
127 |
+ |
|
128 |
+ // Store the file if the upload folder exists |
|
129 |
+ $projectDir = $container->getParameter('kernel.project_dir'); |
|
130 |
+ |
|
131 |
+ if (!!$strUploadFolder & is_dir($projectDir . '/' . $strUploadFolder)) |
|
132 |
+ { |
|
133 |
+ // Delete existing avatar if it exists |
|
134 |
+ static::deleteAvatar($objMember); |
|
135 |
+ |
|
136 |
+ // Rename file |
|
137 |
+ $fileName = self::AVATAR_NAME . '.' . $objFile->extension; |
|
138 |
+ |
|
139 |
+ // Move the file to its destination |
|
140 |
+ $filesObj = Files::getInstance(); |
|
141 |
+ $filesObj->move_uploaded_file($path, $strUploadFolder . '/' . $fileName); |
|
142 |
+ $filesObj->chmod($strUploadFolder . '/' . $fileName, 0666 & ~umask()); |
|
143 |
+ |
|
144 |
+ $strFile = $strUploadFolder . '/' . $fileName; |
|
145 |
+ |
|
146 |
+ |
|
147 |
+ // Generate the DB entries |
|
148 |
+ if (Dbafs::shouldBeSynchronized($strFile)) |
|
149 |
+ { |
|
150 |
+ $objModel = FilesModel::findByPath($strFile); |
|
151 |
+ |
|
152 |
+ if ($objModel === null) |
|
153 |
+ { |
|
154 |
+ $objModel = Dbafs::addResource($strFile); |
|
155 |
+ } |
|
156 |
+ |
|
157 |
+ // Update the hash of the target folder |
|
158 |
+ Dbafs::updateFolderHashes($strUploadFolder); |
|
159 |
+ |
|
160 |
+ // Update member avatar |
|
161 |
+ $objMember->avatar = $objModel->uuid; |
|
162 |
+ $objMember->save(); |
|
163 |
+ } |
|
164 |
+ |
|
165 |
+ $container->get('monolog.logger.contao.files')->info('File "' . $strUploadFolder . '/' . $fileName . '" has been uploaded'); |
|
166 |
+ } |
|
167 |
+ } |
|
168 |
+ |
|
169 |
+ /** |
|
170 |
+ * Parses an avatar to the template |
|
171 |
+ */ |
|
172 |
+ public static function parseMemberAvatar(?MemberModel $objMember, &$objTemplate, ?string $imgSize): void |
|
173 |
+ { |
|
174 |
+ $container = System::getContainer(); |
|
175 |
+ |
|
176 |
+ $objTemplate->addImage= true; |
|
177 |
+ |
|
178 |
+ $objTemplate->singleSRC = self::DEFAULT_PICTURE; |
|
179 |
+ $objTemplate->addFallbackImage = true; |
|
180 |
+ |
|
181 |
+ $projectDir = $container->getParameter('kernel.project_dir'); |
|
182 |
+ |
|
183 |
+ // Check if member avatar exists |
|
184 |
+ if (null === $objMember || null === $objMember->avatar || null === ($objFile = FilesModel::findByUuid($objMember->avatar)) || !\is_file($projectDir.'/'. $objFile->path)) |
|
185 |
+ { |
|
186 |
+ $objFile = !!($uuidDefault = Config::get('defaultAvatar')) ? FilesModel::findByUuid($uuidDefault) : null; |
|
187 |
+ } |
|
188 |
+ |
|
189 |
+ // Check if config avatar exists |
|
190 |
+ if (null === $objFile || !\is_file($projectDir . '/' . $objFile->path)) |
|
191 |
+ { |
|
192 |
+ return; |
|
193 |
+ } |
|
194 |
+ |
|
195 |
+ $objTemplate->addFallbackImage = false; |
|
196 |
+ $imgSize = $imgSize ?? null; |
|
197 |
+ |
|
198 |
+ $figureBuilder = $container |
|
199 |
+ ->get('contao.image.studio') |
|
200 |
+ ->createFigureBuilder() |
|
201 |
+ ->from($objFile->path) |
|
202 |
+ ->setSize($imgSize) |
|
203 |
+ ; |
|
204 |
+ |
|
205 |
+ if (null !== ($figure = $figureBuilder->buildIfResourceExists())) |
|
206 |
+ { |
|
207 |
+ $figure->applyLegacyTemplateData($objTemplate); |
|
208 |
+ } |
|
209 |
+ } |
|
210 |
+ |
|
211 |
+ /** |
|
212 |
+ * Gets the url for a member avatar |
|
213 |
+ */ |
|
214 |
+ public static function getMemberAvatarURL(?MemberModel $objMember): string |
|
215 |
+ { |
|
216 |
+ // ToDo: Merge logic with parseMemberAvatar |
|
217 |
+ $projectDir = System::getContainer()->getParameter('kernel.project_dir'); |
|
218 |
+ |
|
219 |
+ if (null === $objMember || null === $objMember->avatar || null === ($objFile = FilesModel::findByUuid($objMember->avatar)) || !\is_file($projectDir.'/'. $objFile->path)) |
|
220 |
+ { |
|
221 |
+ $objFile = !!($uuidDefault = Config::get('defaultAvatar')) ? FilesModel::findByUuid($uuidDefault) : null; |
|
222 |
+ } |
|
223 |
+ |
|
224 |
+ // Check if config avatar exists |
|
225 |
+ if (null === $objFile || !\is_file($projectDir . '/' . $objFile->path)) |
|
226 |
+ { |
|
227 |
+ return self::DEFAULT_PICTURE; |
|
228 |
+ } |
|
229 |
+ |
|
230 |
+ return $objFile->path; |
|
231 |
+ } |
|
232 |
+ |
|
233 |
+ /** |
|
234 |
+ * Deletes an avatar |
|
235 |
+ * @throws Exception |
|
236 |
+ */ |
|
237 |
+ public static function deleteAvatar(MemberModel $objMember): void |
|
238 |
+ { |
|
239 |
+ if (!!$objMember->avatar) |
|
240 |
+ { |
|
241 |
+ $objFile = FilesModel::findByUuid($objMember->avatar) ?: ''; |
|
242 |
+ $projectDir = System::getContainer()->getParameter('kernel.project_dir'); |
|
243 |
+ |
|
244 |
+ // Only delete if file exists |
|
245 |
+ if (!!$objFile && file_exists($projectDir . '/' . $objFile->path)) |
|
246 |
+ { |
|
247 |
+ $file = new File($objFile->path); |
|
248 |
+ $file->delete(); |
|
249 |
+ } |
|
250 |
+ } |
|
251 |
+ } |
|
252 |
+} |