Browse code

Merge pull request #7 from oveleon/develop

Update 1.2.0

Sebastian Zoglowek authored on25/05/2022 10:48:28 • GitHub committed on25/05/2022 10:48:28
Showing35 changed files
... ...
@@ -1,20 +1,204 @@
1
-# Contao Member Extension Bundle
2
-Member Extension for Contao 4 Open Source CMS
3
-
4
-The following functions are added by this bundle:
5
-- Avatar (BE/FE)
6
-    - Fallback / Default Image
7
-- Memberlist
8
-- Memberreader
9
-
10
-### Avatar
11
-For the representation of the avatar, a separate module is provided. To prepare the avatar upload for the frontend, you only have to select the option "Avatar" in the module "Personal data".
12
-
13
-##### Member
14
-![Admin View: List](https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/mebmer-extension-bundle-be.png)
15
-
16
-##### How to configurate the default avatar:
17
-###### Member list
18
-![Admin View: List](https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/mebmer-extension-bundle-be-settings-1.png)
19
-###### Member settings
20
-![Admin View: List](https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/mebmer-extension-bundle-be-settings-2.png)
1
+<p align="center"><img src="https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/cme-logo.svg" width="200"></p>
2
+<h1 align="center">Contao Member Extension Bundle</h1>
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
+<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>
8
+</p>
9
+<br/>
10
+
11
+---
12
+
13
+> Working with **Contao 4.9** and up to **Contao 4.13** (PHP ^7.4 and PHP 8)
14
+
15
+---
16
+
17
+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
+
21
++ [Features](#features)
22
++ [Installation](#installation)
23
+   + [Upgrading](#upgrading-to-version-12)
24
+   + [Composer](#via-composer)
25
+   + [Contao Manager](#via-contao-manager)
26
++ [Initial Setup](#initial-setup)
27
++ [Insert tags](#insert-tags)
28
+   + [Avatar insert tags](#avatar-insert-tags)
29
++ [Front end modules](#front-end-modules)
30
+   + [Memberlist](#memberlist)
31
+   + [Memberreader](#memberreader)
32
+   + [Avatar / Profile picture](#avatar--profile-picture)
33
+   + [Delete avatar](#delete-avatar)
34
++ [Support](#support)
35
++ [Sponsoring](#sponsoring)
36
+
37
+## Features
38
+
39
+- Compatible with Contao 4.9 and higher versions (PHP 8 Support)
40
+- Extends members with avatars
41
+- Paginated member lists
42
+- Member detail pages
43
+- Insert tags for member avatars
44
+
45
+---
46
+
47
+## Installation
48
+
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
+#### Via composer
53
+```
54
+composer require oveleon/contao-member-extension-bundle
55
+```
56
+
57
+#### Via contao-manager
58
+```
59
+Search for contao member extension bundle and add it to your extensions.
60
+```
61
+
62
+After installing the contao-member-extension-bundle, you need to run a **contao install**.
63
+
64
+---
65
+
66
+## Initial setup
67
+This bundle extends contao with the possibiity to extend members with an avatar and displaying members in a list with
68
+detail pages.
69
+
70
+1. Go into members and set up a default avatar in the newly added settings
71
+
72
+    ![Admin View: Member overview](https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/default_avatar.jpg)
73
+    ![Admin View: Member settings](https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/default_avatar_setup.jpg)
74
+
75
+2. To display your members, you need to setup 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
80
+
81
+    ![Admin View: Memberlist](https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/module_memberlist.jpg)
82
+
83
+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
87
+
88
+    ![Admin View: MemberAvatar](https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/module_memberavatar.jpg)
89
+
90
+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
96
+
97
+    ![Admin View: MemberDeleteAvatar](https://www.oveleon.de/share/github-assets/contao-member-extension-bundle/module_memberdeleteavatar.jpg)
98
+
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
102
+
103
+---
104
+
105
+## Insert tags
106
+Member avatars can be shown using following *insert-tags*
107
+
108
+> 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
+
110
+**Example**
111
+```
112
+{{avatar::member::current}}
113
+{{avatar::member::current::200x200xproportional}}
114
+{{avatar::member::4}}
115
+{{avatar::member::4::300x300xcrop}}
116
+```
117
+
118
+The allowed image size parameters are:
119
+"<strong>width</strong> x <strong>height</strong> x <strong>mode</strong>"
120
+
121
+Size mode (See: [Size Array](https://docs.contao.org/dev/framework/image-processing/image-sizes/#size-array))
122
+- crop
123
+- proportional
124
+- box
125
+
126
+The standard mode vor avatar insert tags is *crop*
127
+
128
+### Avatar insert tags
129
+
130
+<table>
131
+  <tr>
132
+    <th colspan="2"><strong>Member extension avatar</strong></th>
133
+  </tr>
134
+  <tr>
135
+    <td><strong>Insert tag</strong></td>
136
+    <td><strong>Description</strong></td>
137
+  </tr>
138
+  <tr>
139
+    <td>{{avatar::member::current}}</td>
140
+    <td>Displays the avatar of the logged in member</td>
141
+  </tr>
142
+  <tr>
143
+    <td>{{avatar::member::current::200x300}}</td>
144
+    <td>Displays the avatar of the logged in member with a width of 200 and a height of 300</td>
145
+  </tr>
146
+  <tr>
147
+    <td>{{avatar::member::current::300x400xbox}}</td>
148
+    <td>Displays the avatar of the logged in member with a width of 300, a height of 400 and the mode: box</td>
149
+  </tr>
150
+  <tr>
151
+    <td>{{avatar::member::123}}</td>
152
+    <td>Displays the avatar of member id 123</td>
153
+  </tr>
154
+  <tr>
155
+    <td>{{avatar::member::123::200x300}}</td>
156
+    <td>Displays the avatar of member id 123 with a width of 200 and a height of 300</td>
157
+  </tr>
158
+  <tr>
159
+    <td>{{avatar::member::123::300x400xbox}}</td>
160
+    <td>Displays the avatar of member id 123 with a width of 300, a height of 400 and the mode: box</td>
161
+  </tr>
162
+</table>
163
+
164
+---
165
+
166
+## Front end modules
167
+
168
+### Memberlist
169
+
170
+Displays activated members in a list
171
+
172
+### Memberreader
173
+
174
+Displays a detail page of a member
175
+
176
+### Avatar / Profile picture
177
+
178
+Displays an avatar of a member. If no avatar has been uploaded, the default avatar (or the fallback avatar from the bundle)
179
+will be shown
180
+
181
+### Delete Avatar
182
+
183
+A module that can be embedded into a page that adds the possibility to delete the avatar of a member.
184
+
185
+---
186
+
187
+## Support
188
+> We **only provide support** for **bugs, and feature requests**; please only post issues about these two topics.
189
+>
190
+> If you need help implementing Contao Member Extension Bundle or you are just starting out 
191
+> with Contao/CSS or HTML, please contact us on our [website](https://www.oveleon.de/kontakt.html#kontaktformular),
192
+> visit the [Contao Community](https://community.contao.org/) 
193
+> or the [Contao Slack](https://join.slack.com/t/contao/shared_invite/enQtNjUzMjY4MDU0ODM3LWVjYWMzODVkZjM5NjdlNDRiZjk2OTI3OWVkMmQ1YjA0MTQ3YTljMjFjODkwYTllN2NkMDcxMThiNzMzZjZlOGU),
194
+> you will be able to find more help there.
195
+>
196
+> This will help us to keep the issues related to this plugin and solve them faster.
197
+
198
+---
199
+
200
+## Sponsoring
201
+
202
+If you find this plugin useful, please consider [sponsoring us](https://github.com/sponsors/oveleon) 
203
+to help contribute to our time invested and to further development of this and other open source projects. 
204
+Thank you for your support! - [Oveleon](https://www.oveleon.de).
... ...
@@ -1,44 +1,70 @@
1 1
 {
2
-  "name":"oveleon/contao-member-extension-bundle",
3
-  "type":"contao-bundle",
4
-  "description":"Member feature extension for Contao.",
5
-  "keywords":["contao","member-extension-bundle"],
6
-  "homepage":"https://oveleon.de/",
7
-  "license":"MIT",
8
-  "authors":[
9
-    {
10
-      "name":"Oveleon",
11
-      "homepage":"https://oveleon.de/",
12
-      "role":"Developer"
13
-    }
14
-  ],
15
-  "require":{
16
-    "php":">=7.1",
17
-    "contao/core-bundle":"^4.4"
18
-  },
19
-  "require-dev": {
20
-    "contao/manager-plugin": "^2.0"
21
-  },
22
-  "conflict": {
23
-    "contao/core": "*",
24
-    "contao/core-bundle": "4.4.1",
25
-    "contao/manager-plugin": "<2.0 || >=3.0"
26
-  },
27
-  "autoload":{
28
-    "psr-4": {
29
-      "Oveleon\\ContaoMemberExtensionBundle\\": "src/"
30
-    },
31
-    "classmap": [
32
-      "src/Resources/contao/"
2
+    "name": "oveleon/contao-member-extension-bundle",
3
+    "type": "contao-bundle",
4
+    "description": "Member feature extension for Contao.",
5
+    "keywords": ["contao","member-extension-bundle"],
6
+    "homepage": "https://oveleon.de/",
7
+    "license": "MIT",
8
+    "authors": [
9
+        {
10
+            "name": "Oveleon",
11
+            "homepage": "https://oveleon.de/",
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
+            "name": "Sebastian Zoglowek",
26
+            "homepage": "https://github.com/zoglo",
27
+            "role": "Developer"
28
+        }
33 29
     ],
34
-    "exclude-from-classmap": [
35
-      "src/Resources/contao/config/",
36
-      "src/Resources/contao/dca/",
37
-      "src/Resources/contao/languages/",
38
-      "src/Resources/contao/templates/"
39
-    ]
40
-  },
41
-  "extra":{
42
-    "contao-manager-plugin": "Oveleon\\ContaoMemberExtensionBundle\\ContaoManager\\Plugin"
43
-  }
30
+    "require": {
31
+        "php": "^7.4 || ^8.0",
32
+        "contao/core-bundle":"^4.9"
33
+    },
34
+    "require-dev": {
35
+        "contao/manager-plugin": "^2.3.1"
36
+    },
37
+    "conflict": {
38
+        "contao/core": "*",
39
+        "contao/manager-plugin": "<2.0 || >=3.0"
40
+    },
41
+    "autoload": {
42
+        "psr-4": {
43
+            "Oveleon\\ContaoMemberExtensionBundle\\": "src/"
44
+        },
45
+        "classmap": [
46
+            "src/Resources/contao/"
47
+        ],
48
+        "exclude-from-classmap": [
49
+            "src/Resources/contao/config/",
50
+            "src/Resources/contao/dca/",
51
+            "src/Resources/contao/languages/",
52
+            "src/Resources/contao/templates/"
53
+        ]
54
+    },
55
+    "extra": {
56
+        "branch-alias": {
57
+            "dev-master": "1.2.x-dev"
58
+        },
59
+        "contao-manager-plugin": "Oveleon\\ContaoMemberExtensionBundle\\ContaoManager\\Plugin"
60
+    },
61
+    "config": {
62
+        "allow-plugins": {
63
+            "contao-components/installer": true,
64
+            "contao/manager-plugin": true
65
+        }
66
+    },
67
+    "support": {
68
+        "issues": "https://github.com/oveleon/contao-member-extension-bundle/issues"
69
+    }
44 70
 }
45 71
\ No newline at end of file
... ...
@@ -5,7 +5,12 @@ declare(strict_types=1);
5 5
 /*
6 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
7 7
  *
8
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
9 14
  */
10 15
 
11 16
 namespace Oveleon\ContaoMemberExtensionBundle\ContaoManager;
... ...
@@ -15,7 +20,6 @@ use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
15 20
 use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
16 21
 use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
17 22
 use Oveleon\ContaoMemberExtensionBundle\ContaoMemberExtensionBundle;
18
-use Symfony\Component\HttpKernel\KernelInterface;
19 23
 
20 24
 class Plugin implements BundlePluginInterface
21 25
 {
... ...
@@ -5,7 +5,12 @@ declare(strict_types=1);
5 5
 /*
6 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
7 7
  *
8
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
9 14
  */
10 15
 
11 16
 namespace Oveleon\ContaoMemberExtensionBundle;
12 17
new file mode 100644
... ...
@@ -0,0 +1,29 @@
1
+<?php
2
+
3
+/**
4
+ * Procuna
5
+ *
6
+ * @link      https://www.oveleon.de/
7
+ * @copyright Copyright (c) 2021  Oveleon GbR (https://www.oveleon.de)
8
+ */
9
+
10
+
11
+namespace Oveleon\ContaoMemberExtensionBundle\DependencyInjection;
12
+
13
+use Symfony\Component\Config\FileLocator;
14
+use Symfony\Component\DependencyInjection\ContainerBuilder;
15
+use Symfony\Component\DependencyInjection\Extension\Extension;
16
+use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
17
+
18
+class ContaoMemberExtensionExtension extends Extension
19
+{
20
+    public function load(array $configs, ContainerBuilder $container): void
21
+    {
22
+        $loader = new YamlFileLoader(
23
+            $container,
24
+            new FileLocator(__DIR__.'/../Resources/config')
25
+        );
26
+
27
+        $loader->load('listener.yml');
28
+    }
29
+}
0 30
new file mode 100644
... ...
@@ -0,0 +1,123 @@
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      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
14
+ */
15
+
16
+namespace Oveleon\ContaoMemberExtensionBundle\EventListener;
17
+
18
+use Contao\FrontendTemplate;
19
+use Contao\FrontendUser;
20
+use Contao\Image\ResizeConfiguration;
21
+use Contao\MemberModel;
22
+use Contao\System;
23
+use Contao\CoreBundle\Framework\ContaoFramework;
24
+use Oveleon\ContaoMemberExtensionBundle\Member;
25
+
26
+class InsertTagsListener
27
+{
28
+    private const SUPPORTED_TAGS = [
29
+        'avatar'
30
+    ];
31
+
32
+    /**
33
+     * @var ContaoFramework
34
+     */
35
+    private $framework;
36
+
37
+    public function __construct(ContaoFramework $framework)
38
+    {
39
+        $this->framework = $framework;
40
+    }
41
+
42
+    /**
43
+     * @return string|false
44
+     */
45
+    public function __invoke(string $tag, bool $useCache, $cacheValue, array $flags)
46
+    {
47
+        $elements = explode('::', $tag);
48
+        $key = strtolower($elements[0]);
49
+
50
+        if (\in_array($key, self::SUPPORTED_TAGS, true)) {
51
+            return $this->replaceEventInsertTag($key, $elements, $flags);
52
+        }
53
+
54
+        return false;
55
+    }
56
+
57
+    private function replaceEventInsertTag(string $insertTag, array $elements, array $flags): string
58
+    {
59
+        $this->framework->initialize();
60
+        $tokenChecker = System::getContainer()->get('contao.security.token_checker');
61
+
62
+        if($elements[1] !== 'member')
63
+        {
64
+            return '';
65
+        }
66
+
67
+        switch ($elements[2]) {
68
+
69
+            case 'current':
70
+                if(!$tokenChecker->hasFrontendUser())
71
+                {
72
+                    return '';
73
+                }
74
+                $memberID = FrontendUser::getInstance()->id;
75
+                break;
76
+
77
+            default:
78
+                if(!\is_numeric($elements[2]))
79
+                {
80
+                    return '';
81
+                }
82
+                $memberID = $elements[2];
83
+                break;
84
+        }
85
+
86
+        if(!!$objMember = MemberModel::findByPk($memberID))
87
+        {
88
+            $strImgSize = $this->convertImgSize($elements[3]);
89
+            $objTemplate = new FrontendTemplate('memberExtension_image');
90
+
91
+            Member::parseMemberAvatar($objMember, $objTemplate, $strImgSize);
92
+
93
+            return $objTemplate->parse();
94
+        }
95
+
96
+        return '';
97
+    }
98
+
99
+    private function convertImgSize($strSize): ?string
100
+    {
101
+        if (!$strSize)
102
+        {
103
+            return null;
104
+        }
105
+
106
+        list($intWidth, $intHeight, $mode) = explode('x', $strSize);
107
+
108
+        $arrSizes = [$intWidth, $intHeight];
109
+
110
+        $arrValidModes = [
111
+            ResizeConfiguration::MODE_BOX,
112
+            ResizeConfiguration::MODE_PROPORTIONAL,
113
+            ResizeConfiguration::MODE_CROP,
114
+        ];
115
+
116
+        if(!!$mode && in_array($mode, $arrValidModes, true))
117
+        {
118
+            $arrSizes[] = $mode;
119
+        }
120
+
121
+        return serialize($arrSizes);
122
+    }
123
+}
0 124
new file mode 100644
... ...
@@ -0,0 +1,8 @@
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
... ...
@@ -1,38 +1,90 @@
1 1
 <?php
2
+
3
+declare(strict_types=1);
4
+
2 5
 /*
3 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
4 7
  *
5
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
6 14
  */
15
+
7 16
 namespace Oveleon\ContaoMemberExtensionBundle;
8 17
 
9 18
 use Contao\Config;
19
+use Contao\CoreBundle\Monolog\ContaoContext;
10 20
 use Contao\Dbafs;
11 21
 use Contao\File;
12 22
 use Contao\FilesModel;
23
+use Contao\FileUpload;
13 24
 use Contao\Frontend;
14 25
 use Contao\FrontendUser;
15 26
 use Contao\MemberModel;
16 27
 use Contao\StringUtil;
28
+use Contao\System;
17 29
 use Contao\Validator;
30
+use Psr\Log\LogLevel;
31
+
18 32
 
19 33
 /**
20 34
  * Class Member
21
- *
22
- * @author Fabian Ekert <fabian@oveleon.de>
23
- * @author Daniele Sciannimanica <https://github.com/doishub>
35
+ * 
36
+ * @property int $avatar UUID of the avatar
24 37
  */
25 38
 class Member extends Frontend
26 39
 {
40
+    const DEFAULT_PICTURE = 'bundles/contaomemberextension/avatar.png';
41
+
42
+    /**
43
+     * MemberAvatar file name
44
+     *
45
+     * @var string
46
+     */
47
+    protected $avatarName = 'memberAvatar';
48
+
49
+    /**
50
+     * Create avatar for a member | Registration
51
+     *
52
+     * @param int   $userId
53
+     * @param array $arrData
54
+     *
55
+     * @return void
56
+     */
57
+    public function createAvatar(int $userId, array $arrData): void
58
+    {
59
+        $objMember = MemberModel::findById($userId);
60
+        $this->processAvatar($objMember, $arrData);
61
+    }
62
+
63
+    /**
64
+     * Update avatar of a member | Login
65
+     *
66
+     * @param FrontendUser  $objUser
67
+     * @param array         $arrData
68
+     *
69
+     * @return void
70
+     */
71
+    public function updateAvatar(FrontendUser $objUser, $arrData): void
72
+    {
73
+        $objMember = MemberModel::findById($objUser->id);
74
+        $this->processAvatar($objMember, $arrData);
75
+    }
76
+
27 77
     /**
28
-     * Update avatar of member
78
+     * Process avatar upload for a member
29 79
      *
30
-     * @param FrontendUser $objUser
31
-     * @param array        $arrData
80
+     * @param MemberModel   $objMember
81
+     * @param array         $arrData
82
+     *
83
+     * @return void
32 84
      */
33
-    public function updateAvatar($objUser, $arrData)
85
+    protected function processAvatar(MemberModel $objMember, ?array $arrData): void
34 86
     {
35
-        $objMember = MemberModel::findByPk($objUser->id);
87
+        $objMember = MemberModel::findByPk($objMember->id);
36 88
 
37 89
         if ($objMember === null)
38 90
         {
... ...
@@ -41,6 +93,7 @@ class Member extends Frontend
41 93
 
42 94
         $file = $_SESSION['FILES']['avatar'];
43 95
         $maxlength_kb = $this->getMaximumUploadSize();
96
+        $maxlength_kb_readable = $this->getReadableSize($maxlength_kb);
44 97
 
45 98
         // Sanitize the filename
46 99
         try
... ...
@@ -49,26 +102,34 @@ class Member extends Frontend
49 102
         }
50 103
         catch (\InvalidArgumentException $e)
51 104
         {
52
-            // ToDo: Fehler: Dateiname beinhaltet unzulässige Zeichen
53
-
105
+            // ToDo: add error message for invalid characters
54 106
             return;
55 107
         }
56 108
 
57 109
         // Invalid file name
58 110
         if (!Validator::isValidFileName($file['name']))
59 111
         {
60
-            // ToDo: Fehler: Dateiname beinhaltet unzulässige Zeichen
61
-
112
+            // ToDo: add error message for invalid characters
62 113
             return;
63 114
         }
64 115
 
65 116
         // File was not uploaded
66
-        // ToDo
117
+        if (!is_uploaded_file($file['tmp_name']))
118
+        {
119
+            // ToDo: Add error messages
120
+            /*if ($file['error'] == 1 || $file['error'] == 2) { // Add error message for maximum file size }
121
+            elseif ($file['error'] == 3) { // Add error message for partial upload }
122
+            elseif ($file['error'] > 0) { // Add error message for failed upload }*/
123
+
124
+            unset($_SESSION['FILES']['avatar']);
125
+
126
+            return;
127
+        }
67 128
 
68 129
         // File is too big
69 130
         if ($file['size'] > $maxlength_kb)
70 131
         {
71
-            // ToDo: Fehler: Datei zu groß
132
+            // ToDo: add error message for maximum file size
72 133
             unset($_SESSION['FILES']['avatar']);
73 134
 
74 135
             return;
... ...
@@ -80,7 +141,7 @@ class Member extends Frontend
80 141
         // File type is not allowed
81 142
         if (!\in_array($objFile->extension, $uploadTypes))
82 143
         {
83
-            // ToDo: Fehler: Dateityp nicht erlaubt
144
+            // ToDo: add error message for not allowed file type
84 145
             unset($_SESSION['FILES']['avatar']);
85 146
 
86 147
             return;
... ...
@@ -91,9 +152,8 @@ class Member extends Frontend
91 152
             $intImageWidth = Config::get('imageWidth');
92 153
 
93 154
             // Image exceeds maximum image width
94
-            if ($intImageWidth > 0 && $arrImageSize[0] > $intImageWidth)
95
-            {
96
-                // ToDo: Fehler: Bild ist zu groß in der breite
155
+            if ($intImageWidth > 0 && $arrImageSize[0] > $intImageWidth) {
156
+                // ToDo: add error message for exceeding width
97 157
                 unset($_SESSION['FILES']['avatar']);
98 158
 
99 159
                 return;
... ...
@@ -102,81 +162,91 @@ class Member extends Frontend
102 162
             $intImageHeight = Config::get('imageHeight');
103 163
 
104 164
             // Image exceeds maximum image height
105
-            if ($intImageHeight > 0 && $arrImageSize[1] > $intImageHeight)
106
-            {
107
-                // ToDo: Fehler: Bild ist zu groß in der höhe
165
+            if ($intImageHeight > 0 && $arrImageSize[1] > $intImageHeight) {
166
+                // ToDo: add error message for exceeding height
108 167
                 unset($_SESSION['FILES']['avatar']);
109 168
 
110 169
                 return;
111 170
             }
171
+        }
112 172
 
113
-            $_SESSION['FILES']['avatar'] = $_SESSION['FILES']['avatar'];
173
+        // Upload valid file type with no width and height -> svg
114 174
 
115
-            // Overwrite the upload folder with user's home directory
116
-            if (!$objMember->assignDir || !$objMember->homeDir)
117
-            {
118
-                return;
119
-            }
175
+        // Don't upload if no homedir is assigned
176
+        if (!$objMember->assignDir || !$objMember->homeDir)
177
+        {
178
+            // ToDo: add error message for no homedir
179
+            return;
180
+        }
120 181
 
121
-            $intUploadFolder = $objMember->homeDir;
182
+        $intUploadFolder = $objMember->homeDir;
122 183
 
123
-            $objUploadFolder = FilesModel::findByUuid($intUploadFolder);
184
+        $objUploadFolder = FilesModel::findByUuid($intUploadFolder);
124 185
 
125
-            // The upload folder could not be found
126
-            if ($objUploadFolder === null)
127
-            {
128
-                throw new \Exception("Invalid upload folder ID $intUploadFolder");
129
-            }
186
+        // The upload folder could not be found
187
+        if ($objUploadFolder === null)
188
+        {
189
+            throw new \Exception("Invalid upload folder ID $intUploadFolder");
190
+        }
130 191
 
131
-            $strUploadFolder = $objUploadFolder->path;
192
+        $strUploadFolder = $objUploadFolder->path;
132 193
 
133
-            // Store the file if the upload folder exists
134
-            if ($strUploadFolder != '' && is_dir(TL_ROOT . '/' . $strUploadFolder))
135
-            {
136
-                $this->import('Files');
194
+        // Store the file if the upload folder exists
195
+        $projectDir = System::getContainer()->getParameter('kernel.project_dir');
137 196
 
138
-                // Move the file to its destination
139
-                $this->Files->move_uploaded_file($file['tmp_name'], $strUploadFolder . '/' . $file['name']);
140
-                $this->Files->chmod($strUploadFolder . '/' . $file['name'], Config::get('defaultFileChmod'));
197
+        if (!!$strUploadFolder & is_dir($projectDir . '/' . $strUploadFolder))
198
+        {
199
+            // Delete existing avatar if it exists
200
+            $this->deleteAvatar($objMember);
141 201
 
142
-                $strUuid = null;
143
-                $strFile = $strUploadFolder . '/' . $file['name'];
202
+            $this->import('Files');
144 203
 
145
-                // Generate the DB entries
146
-                if (Dbafs::shouldBeSynchronized($strFile))
147
-                {
148
-                    $objModel = FilesModel::findByPath($strFile);
204
+            // Rename file
205
+            $file['name'] =  $this->avatarName . '.' . $objFile->extension;
206
+
207
+            // Move the file to its destination
208
+            $this->Files->move_uploaded_file($file['tmp_name'], $strUploadFolder . '/' . $file['name']);
209
+            $this->Files->chmod($strUploadFolder . '/' . $file['name'], 0666 & ~umask());
149 210
 
150
-                    if ($objModel === null)
151
-                    {
152
-                        $objModel = Dbafs::addResource($strFile);
153
-                    }
211
+            $strUuid = null;
212
+            $strFile = $strUploadFolder . '/' . $file['name'];
154 213
 
155
-                    $strUuid = StringUtil::binToUuid($objModel->uuid);
156 214
 
157
-                    // Update the hash of the target folder
158
-                    Dbafs::updateFolderHashes($strUploadFolder);
215
+            // Generate the DB entries
216
+            if (Dbafs::shouldBeSynchronized($strFile))
217
+            {
218
+                $objModel = FilesModel::findByPath($strFile);
159 219
 
160
-                    // Update member avatar
161
-                    $objMember->avatar = $objModel->uuid;
162
-                    $objMember->save();
220
+                if ($objModel === null)
221
+                {
222
+                    $objModel = Dbafs::addResource($strFile);
163 223
                 }
164 224
 
165
-                // Add the session entry (see #6986)
166
-                $_SESSION['FILES']['avatar'] = array
167
-                (
168
-                    'name'     => $file['name'],
169
-                    'type'     => $file['type'],
170
-                    'tmp_name' => TL_ROOT . '/' . $strFile,
171
-                    'error'    => $file['error'],
172
-                    'size'     => $file['size'],
173
-                    'uploaded' => true,
174
-                    'uuid'     => $strUuid
175
-                );
176
-
177
-                // Add a log entry
178
-                $this->log('File "' . $strUploadFolder . '/' . $file['name'] . '" has been uploaded', __METHOD__, TL_FILES);
225
+                $strUuid = StringUtil::binToUuid($objModel->uuid);
226
+
227
+                // Update the hash of the target folder
228
+                Dbafs::updateFolderHashes($strUploadFolder);
229
+
230
+                // Update member avatar
231
+                $objMember->avatar = $objModel->uuid;
232
+                $objMember->save();
179 233
             }
234
+
235
+            // Add the session entry
236
+            $_SESSION['FILES']['avatar'] = array
237
+            (
238
+                'name'     => $file['name'],
239
+                'type'     => $file['type'],
240
+                'tmp_name' => $projectDir . '/' . $strFile,
241
+                'error'    => $file['error'],
242
+                'size'     => $file['size'],
243
+                'uploaded' => true,
244
+                'uuid'     => $strUuid
245
+            );
246
+
247
+            // Add a log entry
248
+            $logger = System::getContainer()->get('monolog.logger.contao');
249
+            $logger->log(LogLevel::INFO, 'File "' . $strUploadFolder . '/' . $file['name'] . '" has been uploaded', array('contao' => new ContaoContext(__METHOD__, TL_FILES)));
180 250
         }
181 251
 
182 252
         unset($_SESSION['FILES']['avatar']);
... ...
@@ -189,23 +259,75 @@ class Member extends Frontend
189 259
      */
190 260
     protected function getMaximumUploadSize()
191 261
     {
192
-        // Get the upload_max_filesize from the php.ini
193
-        $upload_max_filesize = ini_get('upload_max_filesize');
262
+        if ($this->maxlength > 0)
263
+        {
264
+            return $this->maxlength;
265
+        }
266
+
267
+        return FileUpload::getMaxUploadSize();
268
+    }
194 269
 
195
-        // Convert the value to bytes
196
-        if (stripos($upload_max_filesize, 'K') !== false)
270
+    /**
271
+     * Parses an avatar to the template
272
+     *
273
+     * @param MemberModel $objMember
274
+     * @param $objTemplate
275
+     * @param $strImgSize
276
+     * @return void
277
+     */
278
+    public static function parseMemberAvatar(MemberModel $objMember, &$objTemplate, $strImgSize)
279
+    {
280
+        $objTemplate->singleSRC = self::DEFAULT_PICTURE;
281
+        $objTemplate->addImage = false;
282
+
283
+        $uuidDefault = Config::get('defaultAvatar');
284
+
285
+        if(!!$objMember->avatar)
197 286
         {
198
-            $upload_max_filesize = round($upload_max_filesize * 1024);
287
+            $objFile = FilesModel::findByUuid($objMember->avatar);
199 288
         }
200
-        elseif (stripos($upload_max_filesize, 'M') !== false)
289
+        else if(!!$uuidDefault)
201 290
         {
202
-            $upload_max_filesize = round($upload_max_filesize * 1024 * 1024);
291
+            $objFile = FilesModel::findByUuid($uuidDefault);
292
+        }
293
+        else
294
+        {
295
+            return;
203 296
         }
204
-        elseif (stripos($upload_max_filesize, 'G') !== false)
297
+
298
+        $projectDir = System::getContainer()->getParameter('kernel.project_dir');
299
+
300
+        // If file does not exist use default image
301
+        if (null === $objFile || !\is_file($projectDir . '/' . $objFile->path))
205 302
         {
206
-            $upload_max_filesize = round($upload_max_filesize * 1024 * 1024 * 1024);
303
+            return;
207 304
         }
208 305
 
209
-        return min($upload_max_filesize, Config::get('maxFileSize'));
306
+        $objTemplate->addImage = true;
307
+        $arrData = ['singleSRC'=>$objFile->path, 'size'=>$strImgSize];
308
+
309
+        //ToDo: Change to FigureBuilder in the future
310
+        $objTemplate->addImageToTemplate($objTemplate, $arrData, null, null, $objFile);
311
+    }
312
+
313
+    /**
314
+     * @param MemberModel $objMember
315
+     *
316
+     * @return void
317
+     */
318
+    public static function deleteAvatar(MemberModel $objMember): void
319
+    {
320
+        if(!!$objMember->avatar)
321
+        {
322
+            $objFile = FilesModel::findByUuid($objMember->avatar) ?: '';
323
+            $projectDir = System::getContainer()->getParameter('kernel.project_dir');
324
+
325
+            // Only delete if file exists
326
+            if (!!$objFile && file_exists($projectDir . '/' . $objFile->path))
327
+            {
328
+                $file = new File($objFile->path);
329
+                $file->delete();
330
+            }
331
+        }
210 332
     }
211 333
 }
... ...
@@ -1,31 +1,44 @@
1 1
 <?php
2 2
 
3
+declare(strict_types=1);
4
+
3 5
 /*
4 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
5 7
  *
6
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
7 14
  */
8 15
 
9 16
 // Back end modules
17
+use Contao\System;
18
+
10 19
 $GLOBALS['BE_MOD']['system']['member_settings'] = array
11 20
 (
12
-    'tables'            => array('tl_member_settings'),
21
+    'tables'            => ['tl_member_settings'],
13 22
     'hideInNavigation'  => true,
14 23
 );
15 24
 
16 25
 // Front end modules
17
-array_insert($GLOBALS['FE_MOD']['user'], -1, array
18
-(
26
+// ToDo: Change to ArrayUtil::arrayInsert in the future
27
+array_insert($GLOBALS['FE_MOD']['user'], -1, [
19 28
     'avatar'       => 'Oveleon\ContaoMemberExtensionBundle\ModuleAvatar',
29
+    'deleteAvatar' => 'Oveleon\ContaoMemberExtensionBundle\ModuleDeleteAvatar',
20 30
     'memberList'   => 'Oveleon\ContaoMemberExtensionBundle\ModuleMemberList',
21 31
     'memberReader' => 'Oveleon\ContaoMemberExtensionBundle\ModuleMemberReader'
22
-));
32
+]);
23 33
 
24 34
 // Register hooks
25
-$GLOBALS['TL_HOOKS']['updatePersonalData'][] = array('Oveleon\ContaoMemberExtensionBundle\Member', 'updateAvatar');
35
+$GLOBALS['TL_HOOKS']['createNewUser'][] = ['Oveleon\ContaoMemberExtensionBundle\Member', 'createAvatar'];
36
+$GLOBALS['TL_HOOKS']['updatePersonalData'][] = ['Oveleon\ContaoMemberExtensionBundle\Member', 'updateAvatar'];
26 37
 
27 38
 // Style sheet
28
-if (TL_MODE == 'BE')
39
+$request = System::getContainer()->get('request_stack')->getCurrentRequest();
40
+
41
+if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request))
29 42
 {
30 43
     $GLOBALS['TL_CSS'][] = 'bundles/contaomemberextension/style.css|static';
31 44
 }
... ...
@@ -1,35 +1,39 @@
1 1
 <?php
2 2
 
3
+declare(strict_types=1);
4
+
3 5
 /*
4 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
5 7
  *
6
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
7 14
  */
8 15
 
9 16
 // Extend the default palette
10 17
 Contao\CoreBundle\DataContainer\PaletteManipulator::create()
11
-    ->addField(array('avatar'), 'personal_legend', Contao\CoreBundle\DataContainer\PaletteManipulator::POSITION_APPEND)
18
+    ->addField(['avatar'], 'personal_legend', Contao\CoreBundle\DataContainer\PaletteManipulator::POSITION_APPEND)
12 19
     ->applyToPalette('default', 'tl_member')
13 20
 ;
14 21
 
15 22
 // Add global operations
16
-array_insert($GLOBALS['TL_DCA']['tl_member']['list']['global_operations'], 0, array
17
-(
18
-    'settings' => array
19
-    (
20
-        'label'               => &$GLOBALS['TL_LANG']['tl_member']['settings'],
21
-        'href'                => 'do=member_settings',
22
-        'icon'                => 'edit.svg',
23
-        'attributes'          => 'onclick="Backend.getScrollOffset()" accesskey="e"'
24
-    )
25
-));
23
+array_insert($GLOBALS['TL_DCA']['tl_member']['list']['global_operations'], 0, [
24
+    'settings' => [
25
+        'label' => &$GLOBALS['TL_LANG']['tl_member']['settings'],
26
+        'href' => 'do=member_settings',
27
+        'icon' => 'edit.svg',
28
+        'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"'
29
+    ]
30
+]);
26 31
 
27 32
 // Add fields to tl_user
28
-$GLOBALS['TL_DCA']['tl_member']['fields']['avatar'] = array
29
-(
30
-    'label'                   => &$GLOBALS['TL_LANG']['tl_member']['avatar'],
31
-    'exclude'                 => true,
32
-    'inputType'               => 'fileTree',
33
-    'eval'                    => array('feEditable'=>true, 'feViewable'=>true, 'feGroup'=>'personal', 'fieldType'=>'radio', 'filesOnly'=>true, 'isGallery'=>true, 'extensions'=>Config::get('validImageTypes'), 'tl_class'=>'clr'),
34
-    'sql'                     => "binary(16) NULL"
35
-);
36 33
\ No newline at end of file
34
+$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"
40
+];
... ...
@@ -1,35 +1,34 @@
1 1
 <?php
2 2
 
3
+declare(strict_types=1);
4
+
3 5
 /*
4 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
5 7
  *
6
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
7 14
  */
8
-
9
-$GLOBALS['TL_DCA']['tl_member_settings'] = array
10
-(
15
+$GLOBALS['TL_DCA']['tl_member_settings'] = [
11 16
 
12 17
 	// Config
13
-	'config' => array
14
-	(
15
-		'dataContainer'               => 'File',
16
-		'closed'                      => true
17
-	),
18
+	'config' => [
19
+		'dataContainer' => 'File',
20
+		'closed' => true
21
+	],
18 22
 
19 23
 	// Palettes
20
-	'palettes' => array
21
-	(
22
-		'default'                     => '{avatar_legend},defaultAvatar;'
23
-	),
24
+	'palettes' => ['default' =>'{avatar_legend},defaultAvatar;'],
24 25
 
25 26
 	// Fields
26
-	'fields' => array
27
-	(
28
-		'defaultAvatar' => array
29
-		(
30
-            'label'                   => &$GLOBALS['TL_LANG']['tl_member_settings']['defaultAvatar'],
31
-            'inputType'               => 'fileTree',
32
-            'eval'                    => array('fieldType'=>'radio', 'filesOnly'=>true, 'isGallery'=>true, 'extensions'=>Config::get('validImageTypes'), 'tl_class'=>'clr')
33
-		)
34
-	)
35
-);
27
+	'fields' => [
28
+		'defaultAvatar' => [
29
+            'label' => &$GLOBALS['TL_LANG']['tl_member_settings']['defaultAvatar'],
30
+            'inputType' => 'fileTree',
31
+            'eval' => array('fieldType'=>'radio', 'filesOnly'=>true, 'isGallery'=>true, 'extensions'=>Config::get('validImageTypes'), 'tl_class'=>'clr')
32
+		]
33
+	]
34
+];
... ...
@@ -1,58 +1,84 @@
1 1
 <?php
2 2
 
3
+declare(strict_types=1);
4
+
3 5
 /*
4 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
5 7
  *
6
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
7 14
  */
8 15
 
9
-array_insert($GLOBALS['TL_DCA']['tl_module']['palettes'], 0, array
10
-(
11
-    'avatar'       => '{title_legend},name,headline,type;{source_legend},imgSize;{template_legend:hide},memberTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID',
12
-    'memberList'   => '{title_legend},name,headline,type;{config_legend},groups,memberFields,imgSize;{redirect_legend},jumpTo;{template_legend:hide},customTpl,memberListTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID',
13
-    'memberReader' => '{title_legend},name,headline,type;{config_legend},groups,memberFields,imgSize;{template_legend:hide},customTpl,memberReaderTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'
14
-));
15
-
16
-$GLOBALS['TL_DCA']['tl_module']['fields']['memberListTpl'] = array
17
-(
18
-    'exclude'                 => true,
19
-    'inputType'               => 'select',
20
-    'options_callback' => static function ()
21
-    {
22
-        return Contao\Controller::getTemplateGroup('member_list_');
23
-    },
24
-    'eval'                    => array('includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'),
25
-    'sql'                     => "varchar(64) NOT NULL default ''"
26
-);
27
-
28
-$GLOBALS['TL_DCA']['tl_module']['fields']['memberReaderTpl'] = array
29
-(
30
-    'exclude'                 => true,
31
-    'inputType'               => 'select',
32
-    'options_callback' => static function ()
33
-    {
34
-        return Contao\Controller::getTemplateGroup('member_reader_');
35
-    },
36
-    'eval'                    => array('includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'),
37
-    'sql'                     => "varchar(64) NOT NULL default ''"
38
-);
39
-
40
-$GLOBALS['TL_DCA']['tl_module']['fields']['memberFields'] = array
41
-(
42
-    'exclude'                 => true,
43
-    'inputType'               => 'checkboxWizard',
44
-    'options_callback'        => array('tl_module_extension', 'getMemberProperties'),
45
-    'eval'                    => array('multiple'=>true),
46
-    'sql'                     => "blob NULL"
47
-);
48
-
49
-
50
-/**
51
- * Provide miscellaneous methods that are used by the data configuration array.
52
- *
53
- * @author Daniele Sciannimanica <https://github.com/doishub>
54
- */
55
-class tl_module_extension extends Contao\Backend
16
+use Contao\Backend;
17
+use Contao\Controller;
18
+use Contao\System;
19
+
20
+System::loadLanguageFile('tl_member_settings');
21
+
22
+// Add palettes to tl_module
23
+// ToDo: Change to ArrayUtil::arrayInsert in the future
24
+array_insert($GLOBALS['TL_DCA']['tl_module']['palettes'], 0, [
25
+    'avatar' => '{title_legend},name,headline,type;{source_legend},imgSize;{template_legend:hide},customTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID',
26
+    'deleteAvatar' => '{title_legend},name,headline,type;{template_legend:hide},customTpl;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID',
27
+    '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',
28
+    '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'
29
+]);
30
+
31
+$GLOBALS['TL_DCA']['tl_module']['fields']['memberListTpl'] = [
32
+    'exclude' => true,
33
+    'inputType' => 'select',
34
+    'options_callback' => static fn () => Controller::getTemplateGroup('memberExtension_list_'),
35
+    'eval' => ['includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'],
36
+    'sql' => "varchar(64) NOT NULL default ''"
37
+];
38
+
39
+$GLOBALS['TL_DCA']['tl_module']['fields']['memberReaderTpl'] = [
40
+    'exclude' => true,
41
+    'inputType' => 'select',
42
+    'options_callback' => static fn () => Controller::getTemplateGroup('memberExtension_reader_'),
43
+    'eval' => ['includeBlankOption'=>true, 'chosen'=>true, 'tl_class'=>'w50'],
44
+    'sql' => "varchar(64) NOT NULL default ''"
45
+];
46
+
47
+$GLOBALS['TL_DCA']['tl_module']['fields']['ext_order'] = [
48
+    'exclude' => true,
49
+    'inputType' => 'select',
50
+    'options' => ['order_random', 'order_asc', 'order_desc'],
51
+    'reference' => &$GLOBALS['TL_LANG']['tl_member_settings'],
52
+    'eval' => ['tl_class'=>'w50 clr', 'includeBlankOption'=>true, 'chosen'=>true,],
53
+    'sql' => "varchar(32) NOT NULL default ''"
54
+];
55
+
56
+$GLOBALS['TL_DCA']['tl_module']['fields']['ext_orderField'] = [
57
+    'exclude' => true,
58
+    'inputType' => 'select',
59
+    'options_callback' => ['tl_module_extension', 'getViewableMemberFields'],
60
+    'eval' => ['tl_class'=>'w50', 'includeBlankOption'=>true, 'chosen'=>true,],
61
+    'sql' => "varchar(32) NOT NULL default ''"
62
+];
63
+
64
+$GLOBALS['TL_DCA']['tl_module']['fields']['memberFields'] = [
65
+    'exclude' => true,
66
+    'inputType' => 'checkboxWizard',
67
+    'options_callback' => ['tl_module_extension', 'getMemberProperties'],
68
+    'eval' => ['multiple'=>true, 'tl_class'=>'clr'],
69
+    'sql' => "blob NULL"
70
+];
71
+
72
+$GLOBALS['TL_DCA']['tl_module']['fields']['ext_groups'] = [
73
+    'exclude' => true,
74
+    'inputType' => 'checkbox',
75
+    'foreignKey' => 'tl_member_group.name',
76
+    'eval' => ['multiple'=>true, 'tl_class'=>'clr'],
77
+    'sql' => "blob NULL",
78
+    'relation' => ['type'=>'hasMany', 'load'=>'lazy']
79
+];
80
+
81
+class tl_module_extension extends Backend
56 82
 {
57 83
     /**
58 84
      * Import the back end user object
... ...
@@ -70,7 +96,8 @@ class tl_module_extension extends Contao\Backend
70 96
      */
71 97
     public function checkPermission()
72 98
     {
73
-        if ($this->User->isAdmin) {
99
+        if ($this->User->isAdmin)
100
+        {
74 101
             return;
75 102
         }
76 103
 
... ...
@@ -86,7 +113,7 @@ class tl_module_extension extends Contao\Backend
86 113
      */
87 114
     public function getMemberProperties()
88 115
     {
89
-        $return = array();
116
+        $return = [];
90 117
 
91 118
         Contao\System::loadLanguageFile('tl_member');
92 119
         $this->loadDataContainer('tl_member');
... ...
@@ -101,4 +128,27 @@ class tl_module_extension extends Contao\Backend
101 128
 
102 129
         return $return;
103 130
     }
131
+
132
+    /**
133
+     * Return all sortable fields of table tl_member
134
+     *
135
+     * @return array
136
+     */
137
+    public function getViewableMemberFields()
138
+    {
139
+        $return = [];
140
+
141
+        Contao\System::loadLanguageFile('tl_member');
142
+        $this->loadDataContainer('tl_member');
143
+
144
+        foreach ($GLOBALS['TL_DCA']['tl_member']['fields'] as $k=>$v)
145
+        {
146
+            if (!empty($v['inputType']) && $v['eval']['feViewable'] === true)
147
+            {
148
+                $return[$k] = $GLOBALS['TL_DCA']['tl_member']['fields'][$k]['label'][0] . ' ['.$k.']';
149
+            }
150
+        }
151
+
152
+        return $return;
153
+    }
104 154
 }
... ...
@@ -9,6 +9,14 @@
9 9
         <source>More</source>
10 10
         <target>Mehr</target>
11 11
       </trans-unit>
12
+      <trans-unit id="MSC.deleteAvatar">
13
+        <source>Delete avatar</source>
14
+        <target>Profilbild löschen</target>
15
+      </trans-unit>
16
+      <trans-unit id="MSC.avatarDeleted">
17
+        <source>The avatar was successfully deleted</source>
18
+        <target>Das Profilbild wurde erfolgreich gelöscht</target>
19
+      </trans-unit>
12 20
     </body>
13 21
   </file>
14 22
 </xliff>
... ...
@@ -17,6 +17,14 @@
17 17
         <source>Displays the profile picture of the member.</source>
18 18
         <target>Zeigt das Profilbild des Mitgliedes an.</target>
19 19
       </trans-unit>
20
+      <trans-unit id="FMD.deleteAvatar.0">
21
+        <source>Delete avatar</source>
22
+        <target>Profilbild löschen</target>
23
+      </trans-unit>
24
+      <trans-unit id="FMD.deleteAvatar.1">
25
+        <source>Adds a button to delete the avatar of the member.</source>
26
+        <target>Fügt eine Schaltfläche zum Löschen des Profilbildes hinzu.</target>
27
+      </trans-unit>
20 28
       <trans-unit id="FMD.memberList.0">
21 29
         <source>Memberlist</source>
22 30
         <target>Mitgliederliste</target>
... ...
@@ -1,17 +1,29 @@
1 1
 <?xml version="1.0" ?><xliff version="1.1">
2 2
   <file datatype="php" original="src/Resources/contao/languages/en/tl_member.php" source-language="en" target-language="de">
3 3
     <body>
4
+      <trans-unit id="tl_member_settings.avatar_legend">
5
+        <source>Extended member settings</source>
6
+        <target>Erweiterte Mitgliedseinstellungen</target>
7
+      </trans-unit>
4 8
       <trans-unit id="tl_member_settings.defaultAvatar.0">
5
-        <source>Default profile pricutre</source>
9
+        <source>Default avatar</source>
6 10
         <target>Standard-Profilbild</target>
7 11
       </trans-unit>
8 12
       <trans-unit id="tl_member_settings.defaultAvatar.1">
9
-        <source>The default profile picture is displayed for members who have not set their own profile picture.</source>
10
-        <target>Das Standard-Profilbild wird bei Mitgliedern angezeigt, die keinen eigens Profilbild gesetzt haben.</target>
13
+        <source>The default avatar is displayed for members who have not uploaded their own profile picture.</source>
14
+        <target>Das Standard-Profilbild wird bei Mitgliedern angezeigt, welches kein eigenes Profilbild hochgeladen haben.</target>
11 15
       </trans-unit>
12
-      <trans-unit id="tl_member_settings.avatar_legend">
13
-        <source>Profile picture</source>
14
-        <target>Profilbild</target>
16
+      <trans-unit id="tl_member_settings.order_random">
17
+        <source>Random order</source>
18
+        <target>Zufällige Reihenfolge</target>
19
+      </trans-unit>
20
+      <trans-unit id="tl_member_settings.order_asc">
21
+        <source>Ascending</source>
22
+        <target>Aufsteigend</target>
23
+      </trans-unit>
24
+      <trans-unit id="tl_member_settings.order_desc">
25
+        <source>Descending</source>
26
+        <target>Absteigend</target>
15 27
       </trans-unit>
16 28
     </body>
17 29
   </file>
... ...
@@ -1,29 +1,61 @@
1 1
 <?xml version="1.0" ?><xliff version="1.1">
2 2
   <file datatype="php" original="src/Resources/contao/languages/en/tl_module.php" source-language="en" target-language="de">
3 3
     <body>
4
+      <trans-unit id="tl_module.ext_order.0">
5
+        <source>Sort order</source>
6
+        <target>Sortierreihenfolge</target>
7
+      </trans-unit>
8
+      <trans-unit id="tl_module.ext_order.1">
9
+        <source>Here you can choose the sort order.</source>
10
+        <target>Hier können Sie die Sortierreihenfolge festlegen.</target>
11
+      </trans-unit>
12
+      <trans-unit id="tl_module.ext_orderField.0">
13
+        <source>Sorting field</source>
14
+        <target>Sortierfeld</target>
15
+      </trans-unit>
16
+      <trans-unit id="tl_module.ext_orderField.1">
17
+        <source>Here you can select the field to be sorted by.</source>
18
+        <target>Hier können Sie das Feld auswählen, nach dem sortiert werden soll.</target>
19
+      </trans-unit>
20
+      <trans-unit id="tl_module.ext_groups.0">
21
+        <source>Groups to show</source>
22
+        <target>Anzuzeigende Gruppen</target>
23
+      </trans-unit>
24
+      <trans-unit id="tl_module.ext_groups.1">
25
+        <source>Here you can select the member groups to be displayed.</source>
26
+        <target>Hier können Sie die Mitgliedergruppen auswählen, die angezeigt werden sollen.</target>
27
+      </trans-unit>
28
+      <trans-unit id="tl_module.memberFields.0">
29
+        <source>Member fields</source>
30
+        <target>Mitglieds-Felder</target>
31
+      </trans-unit>
32
+      <trans-unit id="tl_module.memberFields.1">
33
+        <source>Here you can select the member fields to be displayed.</source>
34
+        <target>Hier können Sie die auszugebenden Mitgliederfelder auswählen.</target>
35
+      </trans-unit>
4 36
       <trans-unit id="tl_module.memberFields.0">
5 37
         <source>Member fields</source>
6
-        <target>Mitglied-Felder</target>
38
+        <target>Mitglieds-Felder</target>
7 39
       </trans-unit>
8 40
       <trans-unit id="tl_module.memberFields.1">
9
-        <source>Here you can make a selection of the fields that will be output.</source>
10
-        <target>Hier können Sie eine Auswhal der Felder treffen, welche ausgegeben werden.</target>
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>
11 43
       </trans-unit>
12 44
       <trans-unit id="tl_module.memberListTpl.0">
13
-        <source>List-Template</source>
45
+        <source>List template</source>
14 46
         <target>Listen-Template</target>
15 47
       </trans-unit>
16 48
       <trans-unit id="tl_module.memberListTpl.1">
17
-        <source>Here you can choose your own member list template.</source>
18
-        <target>Hier können Sie ein eigenes Mitglieder-Listen Template wählen.</target>
49
+        <source>Here you can set your own member list template.</source>
50
+        <target>Hier können Sie ein eigenes Mitglieder-Listen Template einstellen.</target>
19 51
       </trans-unit>
20 52
       <trans-unit id="tl_module.memberReaderTpl.0">
21
-        <source>Reader-Template</source>
53
+        <source>Reader template</source>
22 54
         <target>Leser-Template</target>
23 55
       </trans-unit>
24 56
       <trans-unit id="tl_module.memberReaderTpl.1">
25
-        <source>Here you can choose your own member reader template.</source>
26
-        <target>Hier können Sie ein eigenes Mitglied-Leser Template wählen.</target>
57
+        <source>Here you can set your own member reader template.</source>
58
+        <target>Hier können Sie ein eigenes Mitglieds-Leser Template einstellen.</target>
27 59
       </trans-unit>
28 60
     </body>
29 61
   </file>
... ...
@@ -7,6 +7,12 @@
7 7
       <trans-unit id="MSC.memberDetail">
8 8
         <source>More</source>
9 9
       </trans-unit>
10
+      <trans-unit id="MSC.deleteAvatar">
11
+        <source>Delete avatar</source>
12
+      </trans-unit>
13
+      <trans-unit id="MSC.avatarDeleted">
14
+        <source>The avatar was successfully deleted</source>
15
+      </trans-unit>
10 16
     </body>
11 17
   </file>
12 18
 </xliff>
... ...
@@ -8,10 +8,16 @@
8 8
         <source>Configure member settings</source>
9 9
       </trans-unit>
10 10
       <trans-unit id="FMD.avatar.0">
11
-        <source>Profile picture / Avatar</source>
11
+        <source>Avatar / profile picture</source>
12 12
       </trans-unit>
13 13
       <trans-unit id="FMD.avatar.1">
14
-        <source>Displays the profile picture of the member.</source>
14
+        <source>Displays the avatar of the member.</source>
15
+      </trans-unit>
16
+      <trans-unit id="FMD.deleteAvatar.0">
17
+        <source>Delete avatar</source>
18
+      </trans-unit>
19
+      <trans-unit id="FMD.deleteAvatar.1">
20
+        <source>Adds a button to delete the avatar of the member.</source>
15 21
       </trans-unit>
16 22
       <trans-unit id="FMD.memberList.0">
17 23
         <source>Memberlist</source>
... ...
@@ -2,10 +2,10 @@
2 2
   <file datatype="php" original="src/Resources/contao/languages/en/tl_member.php" source-language="en">
3 3
     <body>
4 4
       <trans-unit id="tl_member.avatar.0">
5
-        <source>Profile picture</source>
5
+        <source>Avatar</source>
6 6
       </trans-unit>
7 7
       <trans-unit id="tl_member.avatar.1">
8
-        <source>Here you can choose a profile picture for the member.</source>
8
+        <source>Here you can choose an avatar for the member.</source>
9 9
       </trans-unit>
10 10
       <trans-unit id="tl_member.settings.0">
11 11
         <source>Settings</source>
... ...
@@ -15,4 +15,4 @@
15 15
       </trans-unit>
16 16
     </body>
17 17
   </file>
18
-</xliff>
18
+</xliff>
19 19
\ No newline at end of file
... ...
@@ -1,14 +1,23 @@
1 1
 <?xml version="1.0" ?><xliff version="1.1">
2 2
   <file datatype="php" original="src/Resources/contao/languages/en/tl_member.php" source-language="en">
3 3
     <body>
4
+      <trans-unit id="tl_member_settings.avatar_legend">
5
+        <source>Extended member settings</source>
6
+      </trans-unit>
4 7
       <trans-unit id="tl_member_settings.defaultAvatar.0">
5
-        <source>Default profile pricutre</source>
8
+        <source>Default avatar</source>
6 9
       </trans-unit>
7 10
       <trans-unit id="tl_member_settings.defaultAvatar.1">
8
-        <source>The default profile picture is displayed for members who have not set their own profile picture.</source>
11
+        <source>The default avatar is displayed for members who have not uploaded their own profile picture.</source>
9 12
       </trans-unit>
10
-      <trans-unit id="tl_member_settings.avatar_legend">
11
-        <source>Profile picture</source>
13
+      <trans-unit id="tl_member_settings.order_random">
14
+        <source>Random order</source>
15
+      </trans-unit>
16
+      <trans-unit id="tl_member_settings.order_asc">
17
+        <source>Ascending</source>
18
+      </trans-unit>
19
+      <trans-unit id="tl_member_settings.order_desc">
20
+        <source>Descending</source>
12 21
       </trans-unit>
13 22
     </body>
14 23
   </file>
... ...
@@ -1,23 +1,47 @@
1 1
 <?xml version="1.0" ?><xliff version="1.1">
2 2
   <file datatype="php" original="src/Resources/contao/languages/en/tl_module.php" source-language="en">
3 3
     <body>
4
+      <trans-unit id="tl_module.ext_order.0">
5
+        <source>Sort order</source>
6
+      </trans-unit>
7
+      <trans-unit id="tl_module.ext_order.1">
8
+        <source>Here you can choose the sort order.</source>
9
+      </trans-unit>
10
+      <trans-unit id="tl_module.ext_orderField.0">
11
+        <source>Sorting field</source>
12
+      </trans-unit>
13
+      <trans-unit id="tl_module.ext_orderField.1">
14
+        <source>Here you can select the field to be sorted by.</source>
15
+      </trans-unit>
16
+      <trans-unit id="tl_module.ext_groups.0">
17
+        <source>Groups to show</source>
18
+      </trans-unit>
19
+      <trans-unit id="tl_module.ext_groups.1">
20
+        <source>Here you can select the member groups to be displayed.</source>
21
+      </trans-unit>
22
+      <trans-unit id="tl_module.memberFields.0">
23
+        <source>Member fields</source>
24
+      </trans-unit>
25
+      <trans-unit id="tl_module.memberFields.1">
26
+        <source>Here you can select the member fields to be displayed.</source>
27
+      </trans-unit>
4 28
       <trans-unit id="tl_module.memberFields.0">
5 29
         <source>Member fields</source>
6 30
       </trans-unit>
7 31
       <trans-unit id="tl_module.memberFields.1">
8
-        <source>Here you can make a selection of the fields that will be output.</source>
32
+        <source>Here you can select the member fields to be output.</source>
9 33
       </trans-unit>
10 34
       <trans-unit id="tl_module.memberListTpl.0">
11
-        <source>List-Template</source>
35
+        <source>List template</source>
12 36
       </trans-unit>
13 37
       <trans-unit id="tl_module.memberListTpl.1">
14
-        <source>Here you can choose your own member list template.</source>
38
+        <source>Here you can set your own member list template.</source>
15 39
       </trans-unit>
16 40
       <trans-unit id="tl_module.memberReaderTpl.0">
17
-        <source>Reader-Template</source>
41
+        <source>Reader template</source>
18 42
       </trans-unit>
19 43
       <trans-unit id="tl_module.memberReaderTpl.1">
20
-        <source>Here you can choose your own member reader template.</source>
44
+        <source>Here you can set your own member reader template.</source>
21 45
       </trans-unit>
22 46
     </body>
23 47
   </file>
... ...
@@ -1,105 +1,88 @@
1 1
 <?php
2 2
 
3
+declare(strict_types=1);
4
+
3 5
 /*
4 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
5 7
  *
6
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
7 14
  */
8 15
 
9 16
 namespace Oveleon\ContaoMemberExtensionBundle;
10 17
 
11
-use Patchwork\Utf8;
18
+use Contao\BackendTemplate;
19
+use Contao\Config;
20
+use Contao\FilesModel;
21
+use Contao\FrontendUser;
22
+use Contao\MemberModel;
23
+use Contao\Module;
24
+use Contao\System;
12 25
 
13 26
 /**
14 27
  * Class ModuleAvatar
15 28
  *
16 29
  * @author Fabian Ekert <fabian@oveleon.de>
30
+ * @author Sebastian Zoglowek <https://github.com/zoglo>
17 31
  */
18
-class ModuleAvatar extends \Module
32
+class ModuleAvatar extends ModuleMemberExtension
19 33
 {
20
-
21
-	/**
22
-	 * Template
23
-	 * @var string
24
-	 */
25
-	protected $strTemplate = 'member_avatar';
26
-
27
-	/**
28
-	 * Return a wildcard in the back end
29
-	 *
30
-	 * @return string
31
-	 */
32
-	public function generate()
33
-	{
34
-		if (TL_MODE == 'BE')
35
-		{
36
-			/** @var BackendTemplate|object $objTemplate */
37
-			$objTemplate = new \BackendTemplate('be_wildcard');
38
-
39
-			$objTemplate->wildcard = '### ' . Utf8::strtoupper($GLOBALS['TL_LANG']['FMD']['avatar'][0]) . ' ###';
40
-			$objTemplate->title = $this->headline;
41
-			$objTemplate->id = $this->id;
42
-			$objTemplate->link = $this->name;
43
-			$objTemplate->href = 'contao/main.php?do=themes&amp;table=tl_module&amp;act=edit&amp;id=' . $this->id;
44
-
45
-			return $objTemplate->parse();
46
-		}
47
-
48
-		// Return if user is not logged in
49
-		if (!FE_USER_LOGGED_IN)
50
-		{
51
-			return '';
52
-		}
53
-
54
-		if ($this->memberTpl != '')
55
-		{
56
-			$this->strTemplate = $this->memberTpl;
57
-		}
58
-
59
-		return parent::generate();
60
-	}
61
-
62
-	/**
63
-	 * Generate the module
64
-	 */
65
-	protected function compile()
66
-	{
67
-        $this->size = $this->imgSize;
68
-
69
-        $this->import('FrontendUser', 'User');
70
-
71
-        if ($this->User->avatar == '' && \Config::get('defaultAvatar') == '')
34
+    /**
35
+     * Template.
36
+     *
37
+     * @var string
38
+     */
39
+    protected $strTemplate = 'memberExtension_avatar';
40
+
41
+    /**
42
+     * Return a wildcard in the back end
43
+     *
44
+     * @return string
45
+     */
46
+    public function generate()
47
+    {
48
+        $request = System::getContainer()->get('request_stack')->getCurrentRequest();
49
+
50
+        if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request))
72 51
         {
73
-            return '';
52
+            $objTemplate = new BackendTemplate('be_wildcard');
53
+            $objTemplate->wildcard = '### ' . mb_strtoupper($GLOBALS['TL_LANG']['FMD']['avatar'][0], 'UTF-8') . ' ###';
54
+            $objTemplate->title = $this->headline;
55
+            $objTemplate->id = $this->id;
56
+            $objTemplate->link = $this->name;
57
+            $objTemplate->href = 'contao/main.php?do=themes&amp;table=tl_module&amp;act=edit&amp;id=' . $this->id;
58
+
59
+            return $objTemplate->parse();
74 60
         }
75 61
 
76
-        if ($this->User->avatar == '')
77
-        {
78
-            $objFile = \FilesModel::findByUuid(\Config::get('defaultAvatar'));
79
-
80
-            if ($objFile === null || !is_file(TL_ROOT . '/' . $objFile->path))
81
-            {
82
-                return '';
83
-            }
84
-
85
-            $this->singleSRC = $objFile->path;
62
+        // Return if user is not logged in
63
+        $tokenChecker = System::getContainer()->get('contao.security.token_checker');
64
+        $blnFeUserLoggedIn = $tokenChecker->hasFrontendUser();
86 65
 
87
-            $this->addImageToTemplate($this->Template, $this->arrData);
88
-            return;
66
+        if (!$blnFeUserLoggedIn)
67
+        {
68
+            return '';
89 69
         }
90 70
 
91
-        $objFile = \FilesModel::findByUuid($this->User->avatar);
71
+        $this->strTemplate = $this->customTpl ?: 'memberExtension_avatar';
92 72
 
93
-        if ($objFile === null || !is_file(TL_ROOT . '/' . $objFile->path))
94
-        {
95
-            $this->singleSRC = \FilesModel::findByUuid(\Config::get('defaultAvatar'))->path;
73
+        return parent::generate();
74
+    }
96 75
 
97
-            $this->addImageToTemplate($this->Template, $this->arrData);
98
-            return;
99
-        }
76
+    /**
77
+     * Generate the module
78
+     */
79
+    protected function compile()
80
+    {
81
+        $objTemplate = $this->Template;
100 82
 
101
-        $this->singleSRC = $objFile->path;
83
+        $this->import(FrontendUser::class, 'User');
84
+        $objMember = MemberModel::findByPk($this->User->id);
102 85
 
103
-        $this->addImageToTemplate($this->Template, $this->arrData, null, null, $objFile);
104
-	}
86
+        Member::parseMemberAvatar($objMember, $objTemplate, $this->imgSize);
87
+    }
105 88
 }
106 89
new file mode 100644
... ...
@@ -0,0 +1,135 @@
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      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
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
+     * Return a wildcard in the back end
43
+     *
44
+     * @return string
45
+     */
46
+    public function generate()
47
+    {
48
+        $container = System::getContainer();
49
+
50
+        $request = System::getContainer()->get('request_stack')->getCurrentRequest();
51
+
52
+        if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request))
53
+        {
54
+            $objTemplate = new BackendTemplate('be_wildcard');
55
+            $objTemplate->wildcard = '### ' . mb_strtoupper($GLOBALS['TL_LANG']['FMD']['deleteAvatar'][0] ?? '', 'UTF-8') . ' ###';
56
+            $objTemplate->title = $this->headline;
57
+            $objTemplate->id = $this->id;
58
+            $objTemplate->link = $this->name;
59
+            $objTemplate->href = 'contao/main.php?do=themes&amp;table=tl_module&amp;act=edit&amp;id=' . $this->id;
60
+
61
+            return $objTemplate->parse();
62
+        }
63
+
64
+        // Set the item from the auto_item parameter
65
+        if (!isset($_GET['items']) && isset($_GET['auto_item']) && Config::get('useAutoItem'))
66
+        {
67
+            Input::setGet('items', Input::get('auto_item'));
68
+        }
69
+
70
+        // Return if there is no logged-in user
71
+        if (!$container->get('contao.security.token_checker')->hasFrontendUser())
72
+        {
73
+            return '';
74
+        }
75
+
76
+        $this->import(FrontendUser::class, 'User');
77
+        $objMember = MemberModel::findByPk($this->User->id);
78
+
79
+        if(null === $objMember)
80
+        {
81
+            return '';
82
+        }
83
+
84
+        // Confirmation message
85
+        $session = System::getContainer()->get('session');
86
+        $flashBag = $session->getFlashBag();
87
+
88
+        // Return if there is no flashbag message or an avatar
89
+        if (!($session->isStarted() && $flashBag->has('mod_avatar_deleted')) && !$objMember->avatar)
90
+        {
91
+            return '';
92
+        }
93
+
94
+        return parent::generate();
95
+    }
96
+
97
+    /**
98
+     * Generate the module
99
+     */
100
+    protected function compile()
101
+    {
102
+        $strFormId = 'deleteAvatar_' . $this->id;
103
+        $session = System::getContainer()->get('session');
104
+        $flashBag = $session->getFlashBag();
105
+
106
+        // Get form submit
107
+        if (Input::post('FORM_SUBMIT') == $strFormId)
108
+        {
109
+            $this->import(FrontendUser::class, 'User');
110
+            $objMember = MemberModel::findByPk($this->User->id);
111
+
112
+            // Delete avatar if it exists
113
+            if(!!$objMember->avatar)
114
+            {
115
+                Member::deleteAvatar($objMember);
116
+                // Unset avatar
117
+                $objMember->avatar = null;
118
+                $objMember->save();
119
+
120
+                // Set message for deletion feedback
121
+                $flashBag->set('mod_avatar_deleted', $GLOBALS['TL_LANG']['MSC']['avatarDeleted']);
122
+                $this->reload();
123
+            }
124
+        }
125
+
126
+        // Confirmation message
127
+        if($session->isStarted() && $flashBag->has('mod_avatar_deleted')) {
128
+            $arrMessages = $flashBag->get('mod_avatar_deleted');
129
+            $this->Template->message = $arrMessages[0];
130
+        }
131
+
132
+        $this->Template->formId = $strFormId;
133
+        $this->Template->slabel = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['deleteAvatar']);
134
+    }
135
+}
... ...
@@ -1,8 +1,16 @@
1 1
 <?php
2
+
3
+declare(strict_types=1);
4
+
2 5
 /*
3 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
4 7
  *
5
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
6 14
  */
7 15
 
8 16
 namespace Oveleon\ContaoMemberExtensionBundle;
... ...
@@ -14,6 +22,7 @@ use Contao\MemberModel;
14 22
 use Contao\Module;
15 23
 use Contao\PageModel;
16 24
 use Contao\StringUtil;
25
+use Contao\System;
17 26
 
18 27
 /**
19 28
  * Parent class for member modules.
... ...
@@ -28,11 +37,10 @@ abstract class ModuleMemberExtension extends Module
28 37
      * @param $objMember
29 38
      * @param $objTemplate
30 39
      * @param $arrMemberFields
31
-     * @param $varImgSize
32
-     *
40
+     * @param $strImgSize
33 41
      * @return string
34 42
      */
35
-    protected function parseMemberTemplate($objMember, $objTemplate, $arrMemberFields, $varImgSize)
43
+    protected function parseMemberTemplate($objMember, $objTemplate, $arrMemberFields, $strImgSize)
36 44
     {
37 45
         $arrFields = [];
38 46
 
... ...
@@ -41,7 +49,7 @@ abstract class ModuleMemberExtension extends Module
41 49
             switch($field)
42 50
             {
43 51
                 case 'avatar':
44
-                    $this->addAvatarToTemplate($objMember, $objTemplate, $varImgSize);
52
+                    Member::parseMemberAvatar($objMember, $objTemplate, $strImgSize);
45 53
                     break;
46 54
 
47 55
                 default:
... ...
@@ -49,11 +57,11 @@ abstract class ModuleMemberExtension extends Module
49 57
                     {
50 58
                         if (\is_array(($arrValue = StringUtil::deserialize($varValue))))
51 59
                         {
52
-                            $arrFields[] = implode(",", $arrValue);
60
+                            $arrFields[$field] = implode(",", $arrValue);
53 61
                         }
54 62
                         else
55 63
                         {
56
-                            $arrFields[] = $varValue;
64
+                            $arrFields[$field] = $varValue;
57 65
                         }
58 66
                     }
59 67
             }
... ...
@@ -70,54 +78,6 @@ abstract class ModuleMemberExtension extends Module
70 78
     }
71 79
 
72 80
     /**
73
-     * Add avatar to template
74
-     *
75
-     * @param $objMember
76
-     * @param $objTemplate
77
-     * @param $varImgSize
78
-     */
79
-    protected function addAvatarToTemplate($objMember, $objTemplate, $varImgSize)
80
-    {
81
-        $objTemplate->addImage = false;
82
-
83
-        $arrData = array(
84
-            'size' => $varImgSize
85
-        );
86
-
87
-        if ($objMember->avatar == '' && Config::get('defaultAvatar') == '')
88
-        {
89
-            return;
90
-        }
91
-
92
-        if ($objMember->avatar == '')
93
-        {
94
-            $objFile = FilesModel::findByUuid( Config::get('defaultAvatar') );
95
-
96
-            if ($objFile === null || !is_file(TL_ROOT . '/' . $objFile->path))
97
-            {
98
-                return;
99
-            }
100
-
101
-            $arrData['singleSRC'] = $objFile->path;
102
-            $objTemplate->addImage = true;
103
-            $this->addImageToTemplate($objTemplate, $arrData);
104
-        }
105
-
106
-        $objFile = FilesModel::findByUuid($objMember->avatar);
107
-
108
-        if ($objFile === null || !is_file(TL_ROOT . '/' . $objFile->path))
109
-        {
110
-            $arrData['singleSRC'] = FilesModel::findByUuid(Config::get('defaultAvatar'))->path;
111
-            $objTemplate->addImage = true;
112
-            $this->addImageToTemplate($objTemplate, $arrData);
113
-        }
114
-
115
-        $arrData['singleSRC'] = $objFile->path;
116
-        $objTemplate->addImage = true;
117
-        $this->addImageToTemplate($objTemplate, $arrData, null, null, $objFile);
118
-    }
119
-
120
-    /**
121 81
      * Generate a URL and return it as string
122 82
      *
123 83
      * @param MemberModel $objMember
... ...
@@ -1,23 +1,40 @@
1 1
 <?php
2 2
 
3
+declare(strict_types=1);
4
+
3 5
 /*
4 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
5 7
  *
6
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
7 14
  */
8 15
 
9 16
 namespace Oveleon\ContaoMemberExtensionBundle;
10 17
 
11 18
 use Contao\BackendTemplate;
19
+use Contao\Config;
20
+use Contao\CoreBundle\Exception\PageNotFoundException;
21
+use Contao\Environment;
12 22
 use Contao\FrontendTemplate;
23
+use Contao\Input;
13 24
 use Contao\MemberModel;
25
+use Contao\Model\Collection;
26
+use Contao\Pagination;
14 27
 use Contao\StringUtil;
15
-use Patchwork\Utf8;
28
+use Contao\System;
16 29
 
17 30
 /**
18 31
  * Class ModuleMemberList
19 32
  *
20
- * @author Daniele Sciannimanica <https://github.com/doishub>
33
+ * @property string $ext_order order of list items
34
+ * @property string ext_orderField order field for list items
35
+ * @property string $ext_groups considered member groups
36
+ * @property string $memberFields Fields to be displayed
37
+ * @property string $memberListTpl Frontend list template
21 38
  */
22 39
 class ModuleMemberList extends ModuleMemberExtension
23 40
 {
... ...
@@ -32,7 +49,7 @@ class ModuleMemberList extends ModuleMemberExtension
32 49
 	 * Template
33 50
 	 * @var string
34 51
 	 */
35
-	protected $strMemberTemplate = 'member_list_default';
52
+	protected $strMemberTemplate = 'memberExtension_list_default';
36 53
 
37 54
 	/**
38 55
 	 * Return a wildcard in the back end
... ...
@@ -41,12 +58,12 @@ class ModuleMemberList extends ModuleMemberExtension
41 58
 	 */
42 59
 	public function generate()
43 60
 	{
44
-		if (TL_MODE == 'BE')
45
-		{
46
-			/** @var BackendTemplate|object $objTemplate */
47
-			$objTemplate = new BackendTemplate('be_wildcard');
61
+        $request = System::getContainer()->get('request_stack')->getCurrentRequest();
48 62
 
49
-			$objTemplate->wildcard = '### ' . Utf8::strtoupper($GLOBALS['TL_LANG']['FMD']['memberList'][0]) . ' ###';
63
+        if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request))
64
+        {
65
+            $objTemplate = new BackendTemplate('be_wildcard');
66
+			$objTemplate->wildcard = '### ' . mb_strtoupper($GLOBALS['TL_LANG']['FMD']['memberList'][0], 'UTF-8') . ' ###';
50 67
 			$objTemplate->title = $this->headline;
51 68
 			$objTemplate->id = $this->id;
52 69
 			$objTemplate->link = $this->name;
... ...
@@ -63,35 +80,144 @@ class ModuleMemberList extends ModuleMemberExtension
63 80
 	 */
64 81
 	protected function compile()
65 82
 	{
66
-        $objGroups = MemberModel::findAll();
67
-        $arrGroups = StringUtil::deserialize($this->groups);
68
-        $arrMembers = null;
83
+        $limit = null;
84
+        $offset = 0;
85
+
86
+        $arrGroups = StringUtil::deserialize($this->ext_groups);
87
+
88
+        if(empty($arrGroups) || !\is_array($arrGroups))
89
+        {
90
+            $this->Template->empty = $GLOBALS['TL_LANG']['MSC']['emptyMemberList'];
91
+            return;
92
+        }
93
+
94
+        $objTemplate = new FrontendTemplate($this->memberListTpl ?: $this->strMemberTemplate);
95
+
96
+        $objMembers = $this->getMembers();
69 97
 
70
-        if($objGroups->count())
98
+        $intTotal = 0;
99
+
100
+        $arrMembers = [];
101
+
102
+        if(null !== $objMembers)
71 103
         {
72
-            while($objGroups->next())
104
+            while($objMembers->next())
73 105
             {
74
-                $memberGroups = StringUtil::deserialize($objGroups->groups);
106
+                $objMember = $objMembers->current();
75 107
 
76
-                if($objGroups->disable || empty($arrGroups) || !\is_array($arrGroups) || !\count(array_intersect($arrGroups, $memberGroups)))
108
+                if(!$this->checkMemberGroups($arrGroups, $objMember))
77 109
                 {
78 110
                     continue;
79 111
                 }
80 112
 
113
+                $intTotal += 1;
114
+
81 115
                 $arrMemberFields = StringUtil::deserialize($this->memberFields, true);
116
+                $objTemplate->setData($objMember->row());
117
+
118
+                $arrMembers[] = $this->parseMemberTemplate($objMember, $objTemplate, $arrMemberFields, $this->imgSize);
119
+            }
120
+        }
121
+
122
+        $total = $intTotal - $offset;
123
+
124
+        if ($this->numberOfItems > 0)
125
+        {
126
+            $limit = $this->numberOfItems;
127
+        }
128
+
129
+        if ($this->perPage > 0 && (!isset($limit) || $this->numberOfItems > $this->perPage))
130
+        {
131
+            if (isset($limit))
132
+            {
133
+                $total = min($limit, $total);
134
+            }
135
+
136
+            $id = 'page_n' . $this->id;
137
+            $page = Input::get($id) ?? 1;
82 138
 
83
-                $objTemplate = new FrontendTemplate($this->memberListTpl ?: $this->strMemberTemplate);
84
-                $objTemplate->setData($objGroups->current()->row());
139
+            if ($page < 1 || $page > max(ceil($total/$this->perPage), 1))
140
+            {
141
+                throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
142
+            }
143
+
144
+            $limit = $this->perPage;
145
+            $offset += (max($page, 1) - 1) * $this->perPage;
146
+            $skip = 0;
85 147
 
86
-                $arrMembers[] = $this->parseMemberTemplate($objGroups->current(), $objTemplate, $arrMemberFields, $this->imgSize);
148
+            if ($offset + $limit > $total + $skip)
149
+            {
150
+                $limit = $total + $skip - $offset;
87 151
             }
152
+
153
+            $arrMembers = \array_slice($arrMembers, $offset, ($limit ?: $intTotal), true);
154
+
155
+            $objPagination = new Pagination($total, $this->perPage, Config::get('maxPaginationLinks'), $id);
156
+            $this->Template->pagination = $objPagination->generate("\n  ");
88 157
         }
89 158
 
90
-        if(null === $arrMembers)
159
+        if(empty($arrMembers))
91 160
         {
92 161
             $this->Template->empty = $GLOBALS['TL_LANG']['MSC']['emptyMemberList'];
93 162
         }
94 163
 
95 164
         $this->Template->members = $arrMembers;
96 165
 	}
166
+
167
+    /**
168
+     * Checks whether a member is in any given group
169
+     *
170
+     * @param array $arrGroups
171
+     * @param MemberModel $objMember
172
+     * @return bool
173
+     */
174
+    private function checkMemberGroups(array $arrGroups, MemberModel $objMember): bool
175
+    {
176
+        if(empty($arrGroups))
177
+        {
178
+            return false;
179
+        }
180
+
181
+        $arrMemberGroups = StringUtil::deserialize($objMember->groups);
182
+
183
+        if(!\is_array($arrMemberGroups) || !\count(array_intersect($arrGroups, $arrMemberGroups)))
184
+        {
185
+            return false;
186
+        }
187
+
188
+        return true;
189
+    }
190
+
191
+    /**
192
+     * Get members
193
+     *
194
+     * @return Collection|MemberModel|null
195
+     */
196
+    private function getMembers()
197
+    {
198
+        $arrOptions = [];
199
+        $t = MemberModel::getTable();
200
+
201
+        if (!!$this->ext_orderField)
202
+        {
203
+            $arrOptions['order'] .= "$t.$this->ext_orderField ";
204
+        }
205
+
206
+        switch ($this->ext_order)
207
+        {
208
+            case 'order_random':
209
+                $arrOptions['order'] = "RAND()";
210
+                break;
211
+
212
+            case 'order_desc':
213
+                $arrOptions['order'] .= "DESC";
214
+                break;
215
+
216
+            case 'order_asc':
217
+            default:
218
+                break;
219
+        }
220
+
221
+        return MemberModel::findBy(["$t.disable=''"], null, $arrOptions);
222
+    }
97 223
 }
... ...
@@ -1,9 +1,16 @@
1 1
 <?php
2 2
 
3
+declare(strict_types=1);
4
+
3 5
 /*
4 6
  * This file is part of Oveleon ContaoMemberExtension Bundle.
5 7
  *
6
- * (c) https://www.oveleon.de/
8
+ * @package     contao-member-extension-bundle
9
+ * @license     MIT
10
+ * @author      Daniele Sciannimanica   <https://github.com/doishub>
11
+ * @author      Fabian Ekert            <https://github.com/eki89>
12
+ * @author      Sebastian Zoglowek      <https://github.com/zoglo>
13
+ * @copyright   Oveleon                 <https://www.oveleon.de/>
7 14
  */
8 15
 
9 16
 namespace Oveleon\ContaoMemberExtensionBundle;
... ...
@@ -16,12 +23,14 @@ use Contao\FrontendTemplate;
16 23
 use Contao\Input;
17 24
 use Contao\MemberModel;
18 25
 use Contao\StringUtil;
19
-use Patchwork\Utf8;
26
+use Contao\System;
20 27
 
21 28
 /**
22 29
  * Class ModuleMemberList
23
- *
24
- * @author Daniele Sciannimanica <https://github.com/doishub>
30
+ * 
31
+ * @property string $ext_groups considered member groups
32
+ * @property string $memberFields Fields to be displayed
33
+ * @property string $memberReaderTpl Frontend reader template
25 34
  */
26 35
 class ModuleMemberReader extends ModuleMemberExtension
27 36
 {
... ...
@@ -36,7 +45,7 @@ class ModuleMemberReader extends ModuleMemberExtension
36 45
 	 * Template
37 46
 	 * @var string
38 47
 	 */
39
-	protected $strMemberTemplate = 'member_reader_full';
48
+	protected $strMemberTemplate = 'memberExtension_reader_full';
40 49
 
41 50
 	/**
42 51
 	 * Return a wildcard in the back end
... ...
@@ -45,12 +54,12 @@ class ModuleMemberReader extends ModuleMemberExtension
45 54
 	 */
46 55
 	public function generate()
47 56
 	{
48
-		if (TL_MODE == 'BE')
49
-		{
50
-			/** @var BackendTemplate|object $objTemplate */
51
-			$objTemplate = new BackendTemplate('be_wildcard');
57
+        $request = System::getContainer()->get('request_stack')->getCurrentRequest();
52 58
 
53
-			$objTemplate->wildcard = '### ' . Utf8::strtoupper($GLOBALS['TL_LANG']['FMD']['memberList'][0]) . ' ###';
59
+        if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request))
60
+        {
61
+            $objTemplate = new BackendTemplate('be_wildcard');
62
+			$objTemplate->wildcard = '### ' . mb_strtoupper($GLOBALS['TL_LANG']['FMD']['memberList'][0], 'UTF-8') . ' ###';
54 63
 			$objTemplate->title = $this->headline;
55 64
 			$objTemplate->id = $this->id;
56 65
 			$objTemplate->link = $this->name;
... ...
@@ -79,14 +88,14 @@ class ModuleMemberReader extends ModuleMemberExtension
79 88
         // Get the member
80 89
         $objMember = MemberModel::findByIdOrAlias(Input::get('items'));
81 90
 
82
-        // The member does not exist
91
+        // The member does not exist and is not deactivated
83 92
         if ($objMember === null || $objMember->disable)
84 93
         {
85 94
             throw new PageNotFoundException('Page not found: ' . Environment::get('uri'));
86 95
         }
87 96
 
88
-        // Check groups
89
-        $arrGroups = StringUtil::deserialize($this->groups);
97
+        // Check for group intersection
98
+        $arrGroups = StringUtil::deserialize($this->ext_groups);
90 99
         $memberGroups = StringUtil::deserialize($objMember->groups);
91 100
 
92 101
         if (empty($arrGroups) || !\is_array($arrGroups) || !\count(array_intersect($arrGroups, $memberGroups)))
93 102
new file mode 100644
... ...
@@ -0,0 +1,8 @@
1
+<figure class="image_container">
2
+
3
+  <?php if($this->addImage): ?>
4
+    <?php $this->insert('picture_default', $this->picture); ?>
5
+  <?php else: ?>
6
+    <img src="<?= $this->singleSRC; ?>" width="200" height="200" itemprop="image">
7
+  <?php endif; ?>
8
+</figure>
0 9
new file mode 100644
... ...
@@ -0,0 +1,7 @@
1
+<?php $this->extend('block_searchable'); ?>
2
+
3
+<?php $this->block('content'); ?>
4
+
5
+<?php $this->insert('memberExtension_image', $this->arrData); ?>
6
+
7
+<?php $this->endblock(); ?>
0 8
new file mode 100644
... ...
@@ -0,0 +1,23 @@
1
+<?php $this->extend('block_unsearchable'); ?>
2
+
3
+<?php $this->block('content'); ?>
4
+
5
+<!-- indexer::stop -->
6
+
7
+  <?php if ($this->message): ?>
8
+    <p class="tl_confirm"><?= $this->message ?></p>
9
+  <?php else: ?>
10
+    <form id="<?= $this->formId ?>" method="post">
11
+      <div class="formbody">
12
+        <input type="hidden" name="FORM_SUBMIT" value="<?= $this->formId ?>">
13
+        <input type="hidden" name="REQUEST_TOKEN" value="{{request_token}}">
14
+        <div class="widget widget-submit">
15
+          <button type="submit" class="submit"><?= $this->slabel ?></button>
16
+        </div>
17
+      </div>
18
+    </form>
19
+  <?php endif; ?>
20
+
21
+<!-- indexer::continue -->
22
+
23
+<?php $this->endblock(); ?>
0 24
new file mode 100644
... ...
@@ -0,0 +1,12 @@
1
+<div class="member_list_default">
2
+  <?php $this->insert('memberExtension_image', $this->arrData); ?>
3
+
4
+  <ul>
5
+    <?php foreach ($this->fields as $k => $v): ?>
6
+      <li class="<?= $k ?>"><?= $v ?></li>
7
+    <?php endforeach; ?>
8
+  </ul>
9
+  <?php if($this->link): ?>
10
+    <a href="<?=$this->link?>"><?=$GLOBALS['TL_LANG']['MSC']['memberDetail']?></a>
11
+  <?php endif; ?>
12
+</div>
0 13
new file mode 100644
... ...
@@ -0,0 +1,9 @@
1
+<div class="member_reader_full">
2
+  <?php $this->insert('memberExtension_image', $this->arrData); ?>
3
+
4
+  <ul>
5
+    <?php foreach ($this->fields as $k => $v): ?>
6
+      <li class="<?= $k ?>"><?= $v ?></li>
7
+    <?php endforeach; ?>
8
+  </ul>
9
+</div>
0 10
deleted file mode 100644
... ...
@@ -1,9 +0,0 @@
1
-<?php $this->extend('block_searchable'); ?>
2
-
3
-<?php $this->block('content'); ?>
4
-
5
-  <figure class="image_container">
6
-      <?php $this->insert('picture_default', $this->picture); ?>
7
-  </figure>
8
-
9
-<?php $this->endblock(); ?>
10 0
deleted file mode 100644
... ...
@@ -1,15 +0,0 @@
1
-<div class="member_list_default">
2
-    <?php if($this->addImage): ?>
3
-        <figure class="image_container">
4
-          <?php $this->insert('picture_default', $this->picture); ?>
5
-        </figure>
6
-    <?php endif; ?>
7
-    <ul>
8
-        <?php foreach ($this->fields as $field): ?>
9
-            <li><?=$field?></li>
10
-        <?php endforeach; ?>
11
-    </ul>
12
-    <?php if($this->link): ?>
13
-        <a href="<?=$this->link?>"><?=$GLOBALS['TL_LANG']['MSC']['memberDetail']?></a>
14
-    <?php endif; ?>
15
-</div>
16 0
deleted file mode 100644
... ...
@@ -1,12 +0,0 @@
1
-<div class="member_reader_full">
2
-    <?php if($this->addImage): ?>
3
-        <figure class="image_container">
4
-          <?php $this->insert('picture_default', $this->picture); ?>
5
-        </figure>
6
-    <?php endif; ?>
7
-    <ul>
8
-        <?php foreach ($this->fields as $field): ?>
9
-            <li><?=$field?></li>
10
-        <?php endforeach; ?>
11
-    </ul>
12
-</div>
... ...
@@ -2,12 +2,13 @@
2 2
 
3 3
 <?php $this->block('content'); ?>
4 4
 
5
-<?php if ($this->members): ?>
5
+<?php if (empty($this->members)): ?>
6
+    <p class="empty message"><?=$this->empty?></p>
7
+<?php else: ?>
6 8
     <?php foreach ($this->members as $member): ?>
7
-        <?=$member?>
9
+      <?=$member?>
8 10
     <?php endforeach; ?>
9
-<?php else: ?>
10
-    <p class="empty message"><?=$this->empty?></p>
11
+    <?= $this->pagination ?>
11 12
 <?php endif; ?>
12 13
 
13 14
 <?php $this->endblock(); ?>